278 lines
6.4 KiB
Rust
278 lines
6.4 KiB
Rust
|
use rand::prelude::*;
|
||
|
|
||
|
|
||
|
use std::fmt::{Display, Formatter, write};
|
||
|
use std::mem;
|
||
|
use crate::uno::UnoState::NotStarted;
|
||
|
|
||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||
|
pub enum Color {
|
||
|
Wild,
|
||
|
Blue,
|
||
|
Green,
|
||
|
Red,
|
||
|
Yellow,
|
||
|
}
|
||
|
|
||
|
|
||
|
impl Display for Color {
|
||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||
|
match self {
|
||
|
Color::Wild => { write!(f, "Wild") }
|
||
|
Color::Blue => { write!(f, "Blue") }
|
||
|
Color::Green => { write!(f, "Green") }
|
||
|
Color::Red => { write!(f, "Red") }
|
||
|
Color::Yellow => { write!(f, "Yellow") }
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||
|
pub enum Type {
|
||
|
Number(usize),
|
||
|
DrawTwo,
|
||
|
DrawFour,
|
||
|
ChooseColor,
|
||
|
Reverse,
|
||
|
Skip,
|
||
|
}
|
||
|
|
||
|
|
||
|
impl Display for Type {
|
||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||
|
match self {
|
||
|
Type::Number(n) => { write!(f, "{}", n) }
|
||
|
Type::DrawTwo => { write!(f, "+2") }
|
||
|
Type::DrawFour => { write!(f, "+4") }
|
||
|
Type::ChooseColor => { write!(f, "Choose Color") }
|
||
|
Type::Reverse => { write!(f, "Reverse") }
|
||
|
Type::Skip => { write!(f, "Skip") }
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
#[derive(Debug, Eq, PartialEq)]
|
||
|
pub struct UnoCard {
|
||
|
typ: Type,
|
||
|
col: Color,
|
||
|
}
|
||
|
|
||
|
|
||
|
impl UnoCard {
|
||
|
pub fn new(typ: Type, col: Color) -> UnoCard {
|
||
|
UnoCard { typ, col }
|
||
|
}
|
||
|
|
||
|
pub fn can_stack(&self, other: &UnoCard) -> bool {
|
||
|
return if self.typ == other.typ || self.col == other.col || self.col == Color::Wild || other.col == Color::Wild {
|
||
|
true
|
||
|
} else {
|
||
|
false
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Default for UnoCard {
|
||
|
fn default() -> Self {
|
||
|
UnoCard::new(Type::Number(0), Color::Wild)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Display for UnoCard {
|
||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||
|
write!(f, "{} {}", self.col, self.typ)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[derive(Debug)]
|
||
|
pub struct UnoPlayer {
|
||
|
pub name: String,
|
||
|
pub hand: Vec<UnoCard>,
|
||
|
}
|
||
|
|
||
|
|
||
|
impl UnoPlayer {
|
||
|
pub fn new(name: String) -> Self {
|
||
|
UnoPlayer { name, hand: vec![] }
|
||
|
}
|
||
|
|
||
|
pub fn take_hand(&mut self) -> Vec<UnoCard> {
|
||
|
mem::take(&mut self.hand)
|
||
|
}
|
||
|
|
||
|
pub fn give_hand(&mut self, hand: Vec<UnoCard>) {
|
||
|
self.hand = hand;
|
||
|
}
|
||
|
|
||
|
pub fn take_card(&mut self, idx: usize) -> UnoCard {
|
||
|
let mut hand = self.take_hand();
|
||
|
let result = hand.remove(idx);
|
||
|
self.give_hand(hand);
|
||
|
|
||
|
result
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
#[derive(Debug, Eq, PartialEq)]
|
||
|
pub enum UnoState {
|
||
|
NotStarted,
|
||
|
Started,
|
||
|
Ended,
|
||
|
}
|
||
|
|
||
|
|
||
|
fn new_uno_stack() -> Vec<UnoCard> {
|
||
|
let mut deck: Vec<UnoCard> = vec![];
|
||
|
|
||
|
for col in [Color::Blue, Color::Red, Color::Green, Color::Yellow] {
|
||
|
deck.push(UnoCard::new(Type::Number(0), col));
|
||
|
|
||
|
for n in 1..=9 {
|
||
|
deck.push(UnoCard::new(Type::Number(n), col));
|
||
|
deck.push(UnoCard::new(Type::Number(n), col));
|
||
|
}
|
||
|
|
||
|
for typ in [Type::Reverse, Type::Skip, Type::DrawTwo] {
|
||
|
deck.push(UnoCard::new(typ, col));
|
||
|
}
|
||
|
|
||
|
for _ in 0..4 {
|
||
|
deck.push(UnoCard::new(Type::DrawFour, Color::Wild));
|
||
|
deck.push(UnoCard::new(Type::ChooseColor, Color::Wild));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
deck
|
||
|
}
|
||
|
|
||
|
|
||
|
#[derive(Debug)]
|
||
|
pub struct Uno {
|
||
|
card_stack: Vec<UnoCard>,
|
||
|
pub players: Vec<UnoPlayer>,
|
||
|
pub admin: String,
|
||
|
pub active_player: usize,
|
||
|
pub direction: i32,
|
||
|
pub draw_amount: u32,
|
||
|
pub state: UnoState,
|
||
|
pub top: UnoCard,
|
||
|
}
|
||
|
|
||
|
impl Uno {
|
||
|
pub fn new(admin: String) -> Self {
|
||
|
Uno {
|
||
|
card_stack: new_uno_stack(),
|
||
|
players: vec![],
|
||
|
admin,
|
||
|
active_player: 0,
|
||
|
direction: 1,
|
||
|
draw_amount: 0,
|
||
|
state: UnoState::Ended,
|
||
|
top: UnoCard::default(),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub fn draw(&mut self) -> UnoCard {
|
||
|
self.card_stack.pop().unwrap()
|
||
|
}
|
||
|
|
||
|
pub fn end(&mut self) {
|
||
|
self.state = UnoState::Ended;
|
||
|
}
|
||
|
|
||
|
pub fn init(&mut self) {
|
||
|
self.state = UnoState::Started;
|
||
|
self.shuffle();
|
||
|
|
||
|
let mut card_buf: Vec<UnoCard> = vec![];
|
||
|
|
||
|
for _ in 0..(7 * self.players.len()) {
|
||
|
card_buf.push(self.draw());
|
||
|
}
|
||
|
|
||
|
for player in self.players.iter_mut() {
|
||
|
for _ in 0..7 {
|
||
|
player.hand.push(card_buf.pop().unwrap());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
self.top = self.draw();
|
||
|
}
|
||
|
|
||
|
pub fn join(&mut self, name: String) {
|
||
|
if self.state == UnoState::NotStarted {
|
||
|
self.players.push(UnoPlayer::new(name));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub fn kick(&mut self, name: String) {
|
||
|
let mut kick_idx: usize = usize::MAX;
|
||
|
|
||
|
for (i, p) in self.players.iter_mut().enumerate() {
|
||
|
if p.name == name {
|
||
|
let mut hand = p.take_hand();
|
||
|
self.card_stack.append(&mut hand);
|
||
|
kick_idx = i;
|
||
|
|
||
|
if i < self.active_player {
|
||
|
self.active_player -= 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if kick_idx < self.players.len() {
|
||
|
self.players.remove(kick_idx);
|
||
|
}
|
||
|
self.shuffle();
|
||
|
}
|
||
|
|
||
|
pub fn next_player(&mut self) {
|
||
|
let mult: i32 = match self.top.typ {
|
||
|
Type::Skip => { 2 }
|
||
|
_ => { 1 }
|
||
|
};
|
||
|
|
||
|
self.active_player = (self.active_player + self.players.len() + (self.direction * mult) as usize) % self.players.len();
|
||
|
}
|
||
|
|
||
|
pub fn place(&mut self, card: UnoCard) {
|
||
|
self.card_stack.push(mem::take(&mut self.top));
|
||
|
self.top = card;
|
||
|
self.shuffle();
|
||
|
|
||
|
match self.top.typ {
|
||
|
Type::Number(_) => { self.draw_amount = 0; }
|
||
|
Type::DrawTwo => { self.draw_amount += 2; }
|
||
|
Type::DrawFour => { self.draw_amount += 4; }
|
||
|
Type::ChooseColor => { self.draw_amount = 0; }
|
||
|
Type::Reverse => {
|
||
|
self.direction *= -1;
|
||
|
self.draw_amount = 0;
|
||
|
}
|
||
|
Type::Skip => { self.draw_amount = 0; }
|
||
|
}
|
||
|
|
||
|
self.next_player();
|
||
|
}
|
||
|
|
||
|
pub fn shuffle(&mut self) {
|
||
|
let mut rng = thread_rng();
|
||
|
self.card_stack.shuffle(&mut rng);
|
||
|
}
|
||
|
|
||
|
pub fn reset(&mut self, admin: String) {
|
||
|
self.admin = admin;
|
||
|
self.players = vec![];
|
||
|
self.card_stack = new_uno_stack();
|
||
|
self.active_player = 0;
|
||
|
self.direction = 1;
|
||
|
self.draw_amount = 0;
|
||
|
self.state = NotStarted;
|
||
|
self.top = UnoCard::default();
|
||
|
}
|
||
|
}
|
||
|
|