diff --git a/src/lib.rs b/src/lib.rs index 6ff602b..094d8fe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -258,22 +258,18 @@ impl Game { } } - pub fn action_challenge(&mut self, action_challenge: phase::ActionChallenge, agents: &[&dyn Agent]) -> CoupResult { - let block = Phase::Block(phase::Block { - action: action_challenge.action, - target: action_challenge.target, - }); - match action_challenge.action.challenger_mode() { - ResMode::None => Ok(block), + pub fn action_challenge(&mut self, move_: Move, agents: &[&dyn Agent]) -> CoupResult { + match move_.action.challenger_mode() { + ResMode::None => Ok(Phase::Block(move_)), ResMode::Target => unreachable!(), ResMode::Anyone => { - let challenger = self.turn_iterator().find(|&id| agents[id].should_action_challenge(self, &action_challenge)); + let challenger = self.turn_iterator().find(|&id| agents[id].should_action_challenge(self, move_)); if let Some(challenger) = challenger{ - let current_player_wins = self.players[self.turn].wins_challenge(action_challenge.action); + let current_player_wins = self.players[self.turn].wins_challenge(move_.action); if current_player_wins { // Challenger loses influence self.player_lose_influence(challenger, agents)?; - Ok(block) + Ok(Phase::Block(move_)) } else { // Player loses influence self.player_lose_influence(self.turn, agents)?; @@ -281,73 +277,57 @@ impl Game { Ok(Phase::Done) } } else { - Ok(block) + Ok(Phase::Block(move_)) } } } } - pub fn block(&mut self, block: phase::Block, agents: &[&dyn Agent]) -> CoupResult { - match block.action.blocker_mode() { - ResMode::None => Ok(Phase::Resolution(phase::Resolution { - action: block.action, - target: block.target, - })), - ResMode::Target => match agents[block.target.unwrap()].choose_block_card(self, &block) { + pub fn block(&mut self, move_: Move, agents: &[&dyn Agent]) -> CoupResult { + match move_.action.blocker_mode() { + ResMode::None => Ok(Phase::Resolution(move_)), + ResMode::Target => match agents[move_.target.unwrap()].choose_block_card(self, move_) { Some(card) => { - if card.blocks_action(block.action) { - Ok(Phase::BlockChallenge(phase::BlockChallenge { - blocker: block.target.unwrap(), - block_card: card, - action: block.action, - target: block.target, + if card.blocks_action(move_.action) { + Ok(Phase::BlockChallenge(move_, Block { + blocker: move_.target.unwrap(), + card, })) } else { Err("Card does not block action") } }, - None => Ok(Phase::Resolution(phase::Resolution { - action: block.action, - target: block.target, - })), + None => Ok(Phase::Resolution(move_)), } ResMode::Anyone => { for id in self.turn_iterator() { - if let Some(card) = agents[id].choose_block_card(self, &block) { - if card.blocks_action(block.action) { - return Ok(Phase::BlockChallenge(phase::BlockChallenge { + if let Some(card) = agents[id].choose_block_card(self, move_) { + if card.blocks_action(move_.action) { + return Ok(Phase::BlockChallenge(move_, Block { blocker: id, - block_card: card, - action: block.action, - target: block.target, + card, })) } else { return Err("Card does not block action") } } } - Ok(Phase::Resolution(phase::Resolution { - action: block.action, - target: block.target, - })) + Ok(Phase::Resolution(move_)) } } } - pub fn block_challenge(&mut self, block_challenge: phase::BlockChallenge, agents: &[&dyn Agent]) -> CoupResult { - if agents[self.turn].should_block_challenge(self, &block_challenge) { - if self.players[block_challenge.blocker].holds(block_challenge.block_card) { + pub fn block_challenge(&mut self, move_: Move, block: Block, agents: &[&dyn Agent]) -> CoupResult { + if agents[self.turn].should_block_challenge(self, move_, block) { + if self.players[block.blocker].holds(block.card) { // Player challenged incorrectly, loses influence and turn is forfeit self.player_lose_influence(self.turn, agents)?; Ok(Phase::Done) } else { // Player challenged correctly, blocker loses a card - self.player_lose_influence(block_challenge.blocker, agents)?; + self.player_lose_influence(block.blocker, agents)?; // Game continues - Ok(Phase::Resolution(phase::Resolution { - action: block_challenge.action, - target: block_challenge.target, - })) + Ok(Phase::Resolution(move_)) } } else { // Player chose not to challenge the block, turn is forfeit @@ -356,11 +336,11 @@ impl Game { } } - pub fn resolution(&mut self, resolution: phase::Resolution, agents: &[&dyn Agent]) -> CoupResult { - match resolution.action { + pub fn resolution(&mut self, move_: Move, agents: &[&dyn Agent]) -> CoupResult { + match move_.action { Income => self.players[self.turn].coins += 1, ForeignAid => self.players[self.turn].coins += 2, - Coup | Assassinate => match resolution.target { + Coup | Assassinate => match move_.target { Some(target) => { // Target may have died from challenge let target_alive = self.players[target].is_alive(); @@ -386,7 +366,7 @@ impl Game { self.players[self.turn].cards = choices; self.deck.shuffle(); } - Steal => match resolution.target { + Steal => match move_.target { Some(target) => { let val = self.players[target].coins.min(2); self.players[self.turn].coins += val; @@ -401,61 +381,41 @@ impl Game { } } -pub mod phase { - //! Structures relating to game phases. - //! - //! Coup turns have 5 phases, depending on what actions are taken each phase - //! some phases might be skipped. These structures marshal information - //! between the phases. - use super::{ Card, Action }; - #[derive(PartialEq, Debug)] - pub enum Phase { - Action(Action), - ActionChallenge(ActionChallenge), - Block(Block), - BlockChallenge(BlockChallenge), - Resolution(Resolution), - Done, - } - - #[derive(PartialEq, Debug)] - pub struct ActionChallenge { - pub action: Action, - pub target: Option, - } - - #[derive(PartialEq, Debug)] - pub struct Block { - pub action: Action, - pub target: Option, - } - - #[derive(PartialEq, Debug)] - pub struct BlockChallenge { - pub blocker: usize, - pub block_card: Card, - pub action: Action, - pub target: Option, - } - - #[derive(PartialEq, Debug)] - pub struct Resolution { - pub action: Action, - pub target: Option, - } +#[derive(PartialEq, Debug, Clone, Copy)] +pub struct Move { + pub action: Action, + pub target: Option, } -use phase::Phase; +#[derive(PartialEq, Debug, Clone, Copy)] +pub struct Block { + pub blocker: usize, + pub card: Card, +} + +/// Phase we should move to. +/// +/// Coup turns have 5 phases, depending on what actions are taken each phase +/// some phases might be skipped. +#[derive(PartialEq, Debug)] +pub enum Phase { + Action(Move), + ActionChallenge(Move), + Block(Move), + BlockChallenge(Move, Block), + Resolution(Move), + Done, +} /// An interface to a game to make strategic decisions. pub trait Agent : fmt::Debug { /// Should the agent challenge the action? - fn should_action_challenge(&self, game: &Game, action_challenge: &phase::ActionChallenge) -> bool; + fn should_action_challenge(&self, game: &Game, move_: Move) -> bool; /// Which [card](Card) the agent wishes to use to block the current action. /// If the agent does not wish to block, it should return [None] - fn choose_block_card(&self, game: &Game, block: &phase::Block) -> Option; + fn choose_block_card(&self, game: &Game, move_: Move) -> Option; /// Should the agent challenge the block? - fn should_block_challenge(&self, game: &Game, block_challenge: &phase::BlockChallenge) -> bool; + fn should_block_challenge(&self, game: &Game, move_: Move, block: Block) -> bool; /// The [Ambassador]'s exchange. /// Given 3 or 4 [Cards](Card) the agent must return two cards to the deck. fn exchange(&self, game: &Game, cards: &[Card]) -> [Card; 2]; diff --git a/src/test.rs b/src/test.rs index 3582cca..dd2e1f9 100644 --- a/src/test.rs +++ b/src/test.rs @@ -3,13 +3,13 @@ use super::*; #[derive(Debug)] struct DummyAgent(Card, Option, bool); impl Agent for DummyAgent { - fn should_action_challenge(&self, _game: &Game, _action_challenge: &phase::ActionChallenge) -> bool { + fn should_action_challenge(&self, _game: &Game, _move: Move) -> bool { self.2 } - fn choose_block_card(&self, _game: &Game, _block: &phase::Block) -> Option { + fn choose_block_card(&self, _game: &Game, _move: Move) -> Option { self.1 } - fn should_block_challenge(&self, _game: &Game, _block_challenge: &phase::BlockChallenge) -> bool { + fn should_block_challenge(&self, _game: &Game, _move: Move, _block: Block) -> bool { self.2 } fn exchange(&self, _game: &Game, _cards: &[Card]) -> [Card; 2] { @@ -42,11 +42,11 @@ fn test_action_challenge() { { let mut game = game.clone(); assert_eq!( - game.action_challenge(phase::ActionChallenge { + game.action_challenge(Move { action: Income, target: None, }, &[&challenge_agent, &challenge_agent, &challenge_agent]), - Ok(Phase::Block(phase::Block { + Ok(Phase::Block(Move { action: Income, target: None })) @@ -57,11 +57,11 @@ fn test_action_challenge() { { let mut game = game.clone(); assert_eq!( - game.action_challenge(phase::ActionChallenge { + game.action_challenge(Move { action: Steal, target: Some(2), }, &[&challenge_agent, &challenge_agent, &challenge_agent]), - Ok(Phase::Block(phase::Block { + Ok(Phase::Block(Move { action: Steal, target: Some(2) })) @@ -72,7 +72,7 @@ fn test_action_challenge() { { let mut game = game.clone(); assert_eq!( - game.action_challenge(phase::ActionChallenge { + game.action_challenge(Move { action: Assassinate, target: Some(2), }, &[&non_challenge_agent, &challenge_agent, &challenge_agent]), @@ -106,11 +106,11 @@ fn test_block() { { let mut game = game.clone(); assert_eq!( - game.block(phase::Block { + game.block(Move { action: Income, target: None, }, &[&non_blocking_agent, &ambassador_block_agent, &ambassador_block_agent]), - Ok(Phase::Resolution(phase::Resolution { + Ok(Phase::Resolution(Move { action: Income, target: None })) @@ -121,16 +121,20 @@ fn test_block() { { let mut game = game.clone(); assert_eq!( - game.block(phase::Block { + game.block(Move { action: Steal, target: Some(2), }, &[&non_blocking_agent, &ambassador_block_agent, &ambassador_block_agent]), - Ok(Phase::BlockChallenge(phase::BlockChallenge { - blocker: 2, - block_card: Ambassador, - action: Steal, - target: Some(2), - })) + Ok(Phase::BlockChallenge( + Move { + action: Steal, + target: Some(2), + }, + Block { + blocker: 2, + card: Ambassador, + }, + )), ); } @@ -138,11 +142,11 @@ fn test_block() { { let mut game = game.clone(); assert_eq!( - game.block(phase::Block { + game.block(Move { action: Steal, target: Some(2), }, &[&non_blocking_agent, &ambassador_block_agent, &non_blocking_agent]), - Ok(Phase::Resolution(phase::Resolution { + Ok(Phase::Resolution(Move { action: Steal, target: Some(2), })) @@ -153,16 +157,20 @@ fn test_block() { { let mut game = game.clone(); assert_eq!( - game.block(phase::Block { + game.block(Move { action: ForeignAid, target: None, }, &[&non_blocking_agent, &non_blocking_agent, &duke_block_agent]), - Ok(Phase::BlockChallenge(phase::BlockChallenge { - blocker: 2, - block_card: Duke, - action: ForeignAid, - target: None, - })) + Ok(Phase::BlockChallenge( + Move { + action: ForeignAid, + target: None, + }, + Block { + blocker: 2, + card: Duke, + }, + )), ); } @@ -170,11 +178,11 @@ fn test_block() { { let mut game = game.clone(); assert_eq!( - game.block(phase::Block { + game.block(Move { action: ForeignAid, target: None, }, &[&non_blocking_agent, &non_blocking_agent, &non_blocking_agent]), - Ok(Phase::Resolution(phase::Resolution { + Ok(Phase::Resolution(Move { action: ForeignAid, target: None, })) @@ -185,14 +193,13 @@ fn test_block() { { let mut game = game.clone(); assert!( - game.block(phase::Block { + game.block(Move { action: ForeignAid, target: None, }, &[&non_blocking_agent, &ambassador_block_agent, &non_blocking_agent]) .is_err() ); } - } #[test] @@ -217,12 +224,17 @@ fn test_block_challenge() { { let mut game = game.clone(); assert_eq!( - game.block_challenge( phase::BlockChallenge { - blocker: 1, - block_card: Contessa, - action: Assassinate, - target: Some(1), - }, &[&non_challenge_agent, &block_agent]), + game.block_challenge( + Move { + action: Assassinate, + target: Some(1), + }, + Block { + blocker: 1, + card: Contessa, + }, + &[&non_challenge_agent, &block_agent] + ), Ok(Phase::Done) ); assert!(game.discard.is_empty()); @@ -232,12 +244,17 @@ fn test_block_challenge() { { let mut game = game.clone(); assert_eq!( - game.block_challenge( phase::BlockChallenge { - blocker: 1, - block_card: Contessa, - action: Assassinate, - target: Some(1), - }, &[&challenge_agent, &block_agent]), + game.block_challenge( + Move { + action: Assassinate, + target: Some(1), + }, + Block { + blocker: 1, + card: Contessa, + }, + &[&challenge_agent, &block_agent] + ), Ok(Phase::Done) ); assert!(!game.discard.is_empty()); @@ -247,20 +264,24 @@ fn test_block_challenge() { { let mut game = game.clone(); assert_eq!( - game.block_challenge( phase::BlockChallenge { - blocker: 1, - block_card: Duke, - action: ForeignAid, - target: None, - }, &[&challenge_agent, &block_agent]), - Ok(Phase::Resolution(phase::Resolution { + game.block_challenge( + Move { + action: ForeignAid, + target: None, + }, + Block { + blocker: 1, + card: Duke, + }, + &[&challenge_agent, &block_agent] + ), + Ok(Phase::Resolution(Move { action: ForeignAid, target: None, })) ); assert!(!game.discard.is_empty()); } - } #[test] @@ -283,7 +304,7 @@ fn test_resolution() { // Test income { let mut game = game.clone(); - game.resolution(phase::Resolution { + game.resolution(Move { action: Income, target: None, }, &[&dummy_agent, &dummy_agent]).unwrap(); @@ -293,7 +314,7 @@ fn test_resolution() { // Test foreign aid { let mut game = game.clone(); - game.resolution(phase::Resolution { + game.resolution(Move { action: ForeignAid, target: None, }, &[&dummy_agent, &dummy_agent]).unwrap(); @@ -303,7 +324,7 @@ fn test_resolution() { // Test coup / assassinate { let mut game = game.clone(); - game.resolution(phase::Resolution { + game.resolution(Move { action: Coup, target: Some(1), }, &[&dummy_agent, &loser_agent]).unwrap(); @@ -314,7 +335,7 @@ fn test_resolution() { // Test steal { let mut game = game.clone(); - game.resolution(phase::Resolution { + game.resolution(Move { action: Steal, target: Some(1), }, &[&dummy_agent, &dummy_agent]).unwrap(); @@ -325,7 +346,7 @@ fn test_resolution() { // Test exchange { let mut game = game.clone(); - game.resolution(phase::Resolution { + game.resolution(Move { action: Exchange, target: Some(1), }, &[&dummy_agent, &dummy_agent]).unwrap();