#[cfg(test)]
mod tests;
pub mod deck;
pub mod card_collection;
pub mod hand;
use crate::card_collection::{CardCollection, CardCollectionContains};
use crate::hand::{Hand, Turn};
use crate::deck::Card;
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Player {
pub name: String,
pub hand: Hand,
pub cards_known_to_opponents: CardCollection,
} impl Player {
pub fn new(name: String, hand: Hand) -> Self {
Player {
name: name,
hand: hand,
cards_known_to_opponents: CardCollection::new()
}
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Game {
pub current_turn: usize,
pub discard_pile: CardCollection,
pub deck: CardCollection,
pub players: Vec<Player>,
pub over: bool,
} impl Game {
pub fn new(players: Vec<Player>) -> Result<Self, String> {
if players.len() < 3 || players.len() > 6 {
return Err("Number of players must be between 3 and 6.".to_string());
}
let unique_hand_cards: CardCollection = players
.iter()
.fold(CardCollection::new(),
|mut acc, player| {
for card in &player.hand {
acc += *card;
}
acc
});
if unique_hand_cards.len() != 7 * players.len() as u32 {
return Err("Same card found in multiple hands.".to_string());
}
Ok(Game {
current_turn: 0,
players: players,
discard_pile: CardCollection::new(),
deck: (!CardCollection::new()) - unique_hand_cards,
over: false,
})
}
pub fn play_turn(&mut self, turn: Turn) -> Result<(), String> {
if self.discard_pile.len() >= 10 {
return Err("Game is over.".to_string());
}
if self.players.iter().any(|player| player.hand.contains(&turn.draw)) {
return Err(format!("{:?} cannot be drawn.", turn.draw.name))
}
if turn.discard == turn.draw {
if !self.discard_pile.contains(turn.draw) {
self.discard_pile += turn.discard;
self.deck -= turn.draw;
}
if self.discard_pile.len() == 10 {
self.over = true;
} else {
self.current_turn = (self.current_turn + 1) % self.players.len();
}
return Ok(());
}
if !self.players[self.current_turn].hand.contains(&turn.discard) {
return Err(format!("{:?} not found in hand.", turn.discard.name));
}
self.discard_pile += turn.discard;
self.players[self.current_turn].hand.play(&turn);
self.players[self.current_turn].cards_known_to_opponents -= turn.discard;
if self.discard_pile.contains(turn.draw) {
self.discard_pile -= turn.draw;
self.players[self.current_turn].cards_known_to_opponents += turn.draw;
} else {
self.deck -= turn.draw;
}
if self.discard_pile.len() == 10 {
self.over = true;
} else {
self.current_turn = (self.current_turn + 1) % self.players.len();
}
Ok(())
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct PartialPlayer {
pub name: String,
pub cards_known_to_opponents: CardCollection,
} impl PartialPlayer {
pub fn new(name: String) -> Self {
PartialPlayer {
name: name,
cards_known_to_opponents: CardCollection::new()
}
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum PartialGamePlayer {
Human (PartialPlayer),
Bot (Player),
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum PartialGameTurn {
Human (PartialTurn),
Bot (Turn),
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum DrawCard {
Deck,
Discard (Card),
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct PartialTurn {
pub draw: DrawCard,
pub discard: Card,
} impl PartialTurn {
pub fn new(draw: DrawCard, discard: Card) -> Self {
PartialTurn {draw: draw, discard: discard}
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct PartialGame {
pub current_turn: usize,
pub discard_pile: CardCollection,
pub players: Vec<PartialGamePlayer>,
pub over: bool,
} impl PartialGame {
pub fn new(players: Vec<PartialGamePlayer>) -> Result<Self, String> {
if players.len() < 3 || players.len() > 6 {
return Err("Number of players must be between 3 and 6.".to_string());
}
Ok(PartialGame {
current_turn: 0,
players: players,
discard_pile: CardCollection::new(),
over: false,
})
}
pub fn play_turn(&mut self, turn: PartialGameTurn) -> Result<(), String> {
if self.discard_pile.len() >= 10 {
return Err("Game is over.".to_string());
}
match turn {
PartialGameTurn::Human (turn) => self.play_human_turn(turn),
PartialGameTurn::Bot (turn) => self.play_bot_turn(turn),
}
}
fn play_human_turn(&mut self, turn: PartialTurn) -> Result<(), String> {
let player = match &mut self.players[self.current_turn] {
PartialGamePlayer::Human (player) => player,
PartialGamePlayer::Bot (_) => {
return Err("Received a bot turn on a human player's turn".to_string());
}
};
if let DrawCard::Discard (card) = turn.draw {
player.cards_known_to_opponents += card;
self.discard_pile -= card;
}
player.cards_known_to_opponents -= turn.discard;
self.discard_pile += turn.discard;
if self.discard_pile.len() == 10 {
self.over = true;
} else {
self.current_turn = (self.current_turn + 1) % self.players.len();
}
Ok(())
}
fn play_bot_turn(&mut self, turn: Turn) -> Result<(), String> {
let player = match &mut self.players[self.current_turn] {
PartialGamePlayer::Human (_) => {
return Err("Received a human turn on a bot player's turn".to_string());
}
PartialGamePlayer::Bot (player) => player,
};
if self.discard_pile.contains(turn.draw){
player.cards_known_to_opponents += turn.draw;
}
if turn.discard == turn.draw {
if !self.discard_pile.contains(turn.draw) {
self.discard_pile += turn.discard;
}
if self.discard_pile.len() == 10 {
self.over = true;
} else {
self.current_turn = (self.current_turn + 1) % self.players.len();
}
return Ok(());
}
if !player.hand.contains(&turn.discard) {
return Err(format!("{:?} not found in hand.", turn.discard.name));
}
player.hand.play(&turn);
player.cards_known_to_opponents -= turn.discard;
self.discard_pile += turn.discard;
if self.discard_pile.contains(turn.draw) {
self.discard_pile -= turn.draw;
player.cards_known_to_opponents += turn.draw;
}
if self.discard_pile.len() == 10 {
self.over = true;
} else {
self.current_turn = (self.current_turn + 1) % self.players.len();
}
Ok(())
}
}