twitchbot-rs/tests/uno.rs
2024-03-23 19:50:45 +01:00

277 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();
}
}