#[cfg(test)]
mod tests {
use std::str::FromStr;
use super::super::action::Action;
use super::super::{take_actions, value::Value};
use crate::game_state::GameState;
macro_rules! take_and_verify_actions {
($($action: expr),* $(,)?) => {{
take_and_verify_actions![GameState::initial() => $($action),*]
}};
($game_state: expr => $($action: expr),*) => {{
let mut game_state = $game_state;
$({
game_state = verify_and_take_action(game_state, stringify!($action));
})*
game_state
}};
}
macro_rules! assert_valid_actions {
($game_state: expr => $($action: expr),*) => {{
let mut valid_actions = $game_state.valid_actions();
$({
let action: Action = stringify!($action).parse().unwrap();
let position = valid_actions.iter().position(|a| a == &action);
assert!(position.is_some(), "Expected {} to be in the set of valid actions.", action);
valid_actions.remove(position.unwrap());
})*
assert!(valid_actions.is_empty(), "Unmatched valid actions. The following actions were found but not expected: {:?}", valid_actions);
}};
}
macro_rules! has_action {
($game_state: expr => $action: expr) => {{
assert!(
has_action(&$game_state, stringify!($action)),
"Expected action: {}, Actual actions: {:?}",
stringify!($action),
$game_state.valid_actions()
);
}};
}
macro_rules! does_not_have_action {
($game_state: expr => $action: expr) => {{
assert!(does_not_have_action(&$game_state, stringify!($action)));
}};
}
macro_rules! game_state_eq {
($game_state: expr => $board: expr) => {{
let board = stringify!($board).replace("\"", "");
let provided_game_state = GameState::from_str(&board).unwrap();
assert_eq!($game_state.to_string(), provided_game_state.to_string());
}};
}
#[must_use]
fn place_gold_pieces(game_state: GameState) -> GameState {
take_actions!(game_state => e1, d1, a1, h1, c1, f1, b1, g1)
}
#[must_use]
fn place_silver_pieces(game_state: GameState) -> GameState {
take_actions!(game_state => e8, d8, a8, h8, c8, f8, b8, g8)
}
fn initial_play_state() -> GameState {
let game_state = GameState::initial();
let game_state = place_gold_pieces(game_state);
place_silver_pieces(game_state)
}
#[must_use]
fn has_action(game_state: &GameState, action: &str) -> bool {
game_state.valid_actions().iter().any(|a| *a == action.parse::<Action>().unwrap())
}
#[must_use]
fn does_not_have_action(game_state: &GameState, action: &str) -> bool {
!has_action(game_state, action)
}
fn verify_and_take_action(game_state: GameState, a: &str) -> GameState {
let action = a.parse().unwrap();
assert!(has_action(&game_state, a), "The action {:?} does not exist in {:?}", a, game_state.valid_actions());
game_state.take_action(&action)
}
fn can_pass(game_state: &GameState) -> bool {
has_action(game_state, "p")
}
#[test]
fn test_action_placing_pieces() {
let game_state = initial_play_state();
game_state_eq!(
game_state =>
"2g
+-----------------+
8| h c d m e d c h |
7| r r r r r r r r |
6| x x |
5| |
4| |
3| x x |
2| R R R R R R R R |
1| H C D M E D C H |
+-----------------+
a b c d e f g h
"
);
assert!(game_state.is_p1_turn_to_move());
assert_eq!(game_state.get_move_number(), 2);
assert_eq!(game_state.step(), 0);
}
#[test]
fn test_action_move_up() {
let game_state = initial_play_state();
let game_state = take_actions!(game_state => a2n);
game_state_eq!(
game_state =>
"2g
+-----------------+
8| h c d m e d c h |
7| r r r r r r r r |
6| x x |
5| |
4| |
3| R x x |
2| R R R R R R R |
1| H C D M E D C H |
+-----------------+
a b c d e f g h
"
);
assert_eq!(game_state.step(), 1);
assert!(game_state.is_p1_turn_to_move());
let game_state = take_actions!(game_state => a3n);
game_state_eq!(
game_state =>
"2g
+-----------------+
8| h c d m e d c h |
7| r r r r r r r r |
6| x x |
5| |
4| R |
3| x x |
2| R R R R R R R |
1| H C D M E D C H |
+-----------------+
a b c d e f g h
"
);
assert_eq!(game_state.step(), 2);
assert!(game_state.is_p1_turn_to_move());
let game_state = take_actions!(game_state => a4n);
game_state_eq!(
game_state =>
"2g
+-----------------+
8| h c d m e d c h |
7| r r r r r r r r |
6| x x |
5| R |
4| |
3| x x |
2| R R R R R R R |
1| H C D M E D C H |
+-----------------+
a b c d e f g h
"
);
assert_eq!(game_state.step(), 3);
assert!(game_state.is_p1_turn_to_move());
let game_state = take_actions!(game_state => b2n);
game_state_eq!(
game_state =>
"2s
+-----------------+
8| h c d m e d c h |
7| r r r r r r r r |
6| x x |
5| R |
4| |
3| R x x |
2| R R R R R R |
1| H C D M E D C H |
+-----------------+
a b c d e f g h
"
);
assert_eq!(game_state.step(), 0);
assert!(!game_state.is_p1_turn_to_move());
}
#[test]
fn test_action_place_initial() {
let game_state = GameState::initial();
assert_valid_actions!(game_state => a1, b1, c1, d1, e1, f1, g1, h1, a2, b2, c2, d2, e2, f2, g2, h2);
}
#[test]
fn test_action_place_initial_silver() {
let game_state = GameState::initial();
let game_state = place_gold_pieces(game_state);
assert!(!game_state.is_p1_turn_to_move());
assert_valid_actions!(game_state => a7, b7, c7, d7, e7, f7, g7, h7, a8, b8, c8, d8, e8, f8, g8, h8);
}
#[test]
fn test_action_place_elephant() {
let game_state = GameState::initial();
let game_state = take_actions!(game_state => e2);
assert_valid_actions!(game_state => a1, b1, c1, d1, e1, f1, g1, h1, a2, b2, c2, d2, f2, g2, h2);
}
#[test]
fn test_action_place_camel() {
let game_state = GameState::initial();
let game_state = take_actions!(game_state => g2, h2);
assert_valid_actions!(game_state => a1, b1, c1, d1, e1, f1, g1, h1, a2, b2, c2, d2, e2, f2);
}
#[test]
fn test_action_place_rabbits() {
let game_state = GameState::initial();
let game_state = take_actions!(game_state => h2, a1, e2, d2, f1, b1, d1, c2);
game_state_eq!(
game_state =>
"1s
+-----------------+
8| |
7| |
6| x x |
5| |
4| |
3| x x |
2| R R C H H R R E |
1| M D R C R D R R |
+-----------------+
a b c d e f g h
"
);
}
#[test]
fn test_action_place_p2_initial() {
let game_state = GameState::initial();
let game_state = place_gold_pieces(game_state);
assert!(!game_state.is_p1_turn_to_move());
assert_valid_actions!(game_state => a7, b7, c7, d7, e7, f7, g7, h7, a8, b8, c8, d8, e8, f8, g8, h8);
}
#[test]
fn test_action_place_silver_rabbits() {
let game_state = GameState::initial();
let game_state = take_actions!(game_state => h2, a1, e2, d2, f1, b1, d1, c2);
let game_state = take_actions!(game_state => c7, f7, d7, e8, a7, b8, a8, h8);
game_state_eq!(
game_state =>
"2g
+-----------------+
8| c d r r h r r c |
7| d r e h r m r r |
6| x x |
5| |
4| |
3| x x |
2| R R C H H R R E |
1| M D R C R D R R |
+-----------------+
a b c d e f g h
"
);
}
#[test]
fn test_action_correct_state_after_4_steps() {
let game_state: GameState = "
2g
+-----------------+
8| r r r r |
7| |
6| x x |
5| E r |
4| |
3| x x |
2| |
1| R |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
let game_state = take_actions!(game_state => a1nnnn, d8s);
game_state_eq!(
game_state =>
"2s
+-----------------+
8| r r r |
7| r |
6| x x |
5| R E r |
4| |
3| x x |
2| |
1| |
+-----------------+
a b c d e f g h
"
);
}
#[test]
fn test_action_correct_state_after_pull_with_trap() {
let game_state: GameState = "
1g
+-----------------+
8| r r r r |
7| |
6| x x |
5| E r |
4| |
3| x x |
2| |
1| R |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
let game_state = take_actions!(game_state => pc5nw);
assert_valid_actions!(game_state => a1n, a1e, a1nn, a1ne, a1ee, p);
game_state_eq!(
game_state =>
"1g
+-----------------+
8| r r r r |
7| |
6| x x |
5| r |
4| |
3| x x |
2| |
1| R |
+-----------------+
a b c d e f g h
"
);
let game_state = take_actions!(game_state => a1n);
assert_valid_actions!(game_state => p);
game_state_eq!(
game_state =>
"1g
+-----------------+
8| r r r r |
7| |
6| x x |
5| r |
4| |
3| x x |
2| R |
1| |
+-----------------+
a b c d e f g h
"
);
let game_state = take_actions!(game_state => p);
assert_valid_actions!(game_state => b8e, e8e, g8e, c5e, b8s, d8s, e8s, g8s, c5s, b8w, d8w, g8w, c5w, c5ee, b8es, e8es, g8es, c5es, d8se, b8ss, d8ss, e8ss, g8ss, c5ss, b8sw, d8sw, e8sw, g8sw, c5sw, c5ww, c5eee, c5ees, b8ese, e8ese, d8see, b8ess, e8ess, g8ess, c5ess, d8ses, b8sss, d8sss, e8sss, g8sss, b8ssw, d8ssw, e8ssw, g8ssw, c5sws, d8sww, e8sww, g8sww, c5sww, c5eeee, c5eees, b8esee, e8esee, d8seee, c5eess, b8eses, e8eses, d8sees, g8esss, c5esss, d8sess, e8ssse, b8ssss, d8ssss, e8ssss, g8ssss, b8sssw, e8sssw, g8sssw, c5swss, d8ssww, e8ssww, c5swsw, g8swws, d8swww, e8swww, g8swww);
game_state_eq!(
game_state =>
"1s
+-----------------+
8| r r r r |
7| |
6| x x |
5| r |
4| |
3| x x |
2| R |
1| |
+-----------------+
a b c d e f g h
"
);
}
#[test]
fn test_action_cant_push_on_last_step() {
let game_state: GameState = "
1g
+-----------------+
8| r r r r |
7| |
6| x x |
5| E r |
4| |
3| x x |
2| r D |
1| D |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
let game_state = take_actions!(game_state => f2n);
let game_state = take_actions!(game_state => pc5nw);
assert_valid_actions!(game_state => a1e, p);
game_state_eq!(
game_state =>
"1g
+-----------------+
8| r r r r |
7| |
6| x x |
5| r |
4| |
3| x x |
2| r |
1| D |
+-----------------+
a b c d e f g h
"
);
let game_state = take_actions!(game_state => a1e);
assert_valid_actions!(game_state => b8e, e8e, g8e, c5e, a2e, b8s, d8s, e8s, g8s, c5s, a2s, b8w, d8w, g8w, c5w, c5ee, b8es, e8es, g8es, c5es, d8se, b8ss, d8ss, e8ss, g8ss, c5ss, b8sw, d8sw, e8sw, g8sw, c5sw, c5ww, c5eee, c5ees, b8ese, e8ese, d8see, b8ess, e8ess, g8ess, c5ess, d8ses, b8sss, d8sss, e8sss, g8sss, b8ssw, d8ssw, e8ssw, g8ssw, c5sws, d8sww, e8sww, g8sww, c5sww, c5eeee, c5eees, b8esee, e8esee, d8seee, c5eess, b8eses, e8eses, d8sees, g8esss, c5esss, d8sess, e8ssse, b8ssss, d8ssss, e8ssss, g8ssss, b8sssw, e8sssw, g8sssw, c5swss, d8ssww, e8ssww, c5swsw, g8swws, d8swww, e8swww, g8swww);
game_state_eq!(
game_state =>
"1s
+-----------------+
8| r r r r |
7| |
6| x x |
5| r |
4| |
3| x x |
2| r |
1| D |
+-----------------+
a b c d e f g h
"
);
let game_state = take_actions!(game_state => a2s);
assert_valid_actions!(game_state => b8e, e8e, g8e, c5e, b8s, d8s, e8s, g8s, c5s, b8w, d8w, g8w, c5w, c5ee, b8es, e8es, g8es, c5es, d8se, b8ss, d8ss, e8ss, g8ss, c5ss, b8sw, d8sw, e8sw, g8sw, c5sw, c5ww, c5eee, c5ees, b8ese, e8ese, d8see, b8ess, e8ess, g8ess, c5ess, d8ses, b8sss, d8sss, e8sss, g8sss, b8ssw, d8ssw, e8ssw, g8ssw, c5sws, d8sww, e8sww, g8sww, c5sww, p);
game_state_eq!(
game_state =>
"1s
+-----------------+
8| r r r r |
7| |
6| x x |
5| r |
4| |
3| x x |
2| |
1| r D |
+-----------------+
a b c d e f g h
"
);
assert_eq!(game_state.is_terminal(), None);
let game_state = take_actions!(game_state => p);
assert_eq!(game_state.is_terminal(), Some(Value([0.0, 1.0])));
}
#[test]
fn test_action_cant_push_and_pull_simultaneously() {
let game_state: GameState = "
1s
+-----------------+
8| |
7| |
6| x x |
5| |
4| R |
3| c x x |
2| R |
1| |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
assert_valid_actions!(game_state => pa4nn, pa4en, pa3en, pa3es, pa2es, pa2ss, a3e, a3ennw, a3en, a3ee, a3es, a3essw, a3enn, a3ene, a3ese, a3ess, a3ennn, a3enne, a3enee, a3esee, a3eses);
let game_state = verify_and_take_action(game_state, "pa2es");
assert_valid_actions!(game_state => pb2ne, pb2ee, pb2se, pa2sw, a2n, a2s, a2ne, a2se, p);
game_state_eq!(
game_state =>
"1s
+-----------------+
8| |
7| |
6| x x |
5| |
4| R |
3| x x |
2| c R |
1| |
+-----------------+
a b c d e f g h
"
);
let game_state = take_actions!(game_state => a2s);
assert_valid_actions!(game_state => p);
game_state_eq!(
game_state =>
"1s
+-----------------+
8| |
7| |
6| x x |
5| |
4| R |
3| x x |
2| R |
1| c |
+-----------------+
a b c d e f g h
"
);
let game_state = take_actions!(game_state => p);
assert_valid_actions!(game_state => a4n, b2n, a4e, b2e, b2w, a4nn, b2nn, a4ne, b2ne, b2nw, a4ee, b2ee, a4nnn, b2nnn, a4nne, b2nne, a4nee, b2een, a4eee, b2eee, a4nnnn, b2nnnn, a4nnne, b2nnne, b2nnnw, a4nnee, b2nnee, a4neee, b2eene, a4eeee, b2eeee);
game_state_eq!(
game_state =>
"2g
+-----------------+
8| |
7| |
6| x x |
5| |
4| R |
3| x x |
2| R |
1| c |
+-----------------+
a b c d e f g h
"
);
}
#[test]
fn test_can_pass_first_move() {
let game_state: GameState = "
1g
+-----------------+
8| r r r r |
7| |
6| x x |
5| E r |
4| |
3| x x |
2| |
1| R |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
assert!(!can_pass(&game_state));
}
#[test]
fn test_can_pass_during_place_phase() {
let game_state = GameState::initial();
assert!(!can_pass(&game_state));
let game_state = take_actions!(game_state => a1);
assert!(!can_pass(&game_state));
}
#[test]
fn test_can_pass_false_if_same_as_start_of_move_state() {
let game_state: GameState = "
1g
+-----------------+
8| r r r r |
7| |
6| x x |
5| E r |
4| |
3| x x |
2| |
1| R |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
assert!(!can_pass(&game_state));
let game_state = take_actions!(game_state => c5s);
assert!(can_pass(&game_state));
let game_state = take_actions!(game_state => c4n);
assert!(!can_pass(&game_state));
let game_state = take_actions!(game_state => c5s);
assert!(can_pass(&game_state));
}
#[test]
fn test_is_terminal_no_winner() {
let game_state: GameState = "
1g
+-----------------+
8| M r r r r |
7| |
6| x x |
5| |
4| |
3| x x |
2| |
1| m R |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
assert_eq!(game_state.is_terminal(), None);
}
#[test]
fn test_is_terminal_mid_turn() {
let game_state: GameState = "
1g
+-----------------+
8| r r r r |
7| R |
6| x x |
5| |
4| |
3| x x |
2| |
1| m |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
let game_state = take_actions!(game_state => a7n);
assert_eq!(game_state.is_terminal(), None);
let game_state = take_actions!(game_state => a8ss);
assert_eq!(game_state.is_terminal(), None);
let game_state = take_actions!(game_state => p);
assert_eq!(game_state.is_terminal(), None);
}
#[test]
fn test_is_terminal_p1_winner_as_p1() {
let game_state: GameState = "
1s
+-----------------+
8| R r r r r |
7| |
6| x x |
5| |
4| |
3| x x |
2| |
1| R |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
assert_eq!(game_state.is_terminal(), Some(Value([1.0, 0.0])));
}
#[test]
fn test_is_terminal_p2_winner_as_p1() {
let game_state: GameState = "
1s
+-----------------+
8| r r r r |
7| |
6| x x |
5| |
4| |
3| x x |
2| |
1| r R |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
assert_eq!(game_state.is_terminal(), Some(Value([0.0, 1.0])));
}
#[test]
fn test_is_terminal_p1_winner_as_p2() {
let game_state: GameState = "
1g
+-----------------+
8| R r r r r |
7| |
6| x x |
5| |
4| |
3| x x |
2| |
1| R |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
assert_eq!(game_state.is_terminal(), Some(Value([1.0, 0.0])));
}
#[test]
fn test_is_terminal_p2_winner_as_p2() {
let game_state: GameState = "
1g
+-----------------+
8| r r r r |
7| |
6| x x |
5| |
4| |
3| x x |
2| |
1| r R |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
assert_eq!(game_state.is_terminal(), Some(Value([0.0, 1.0])));
}
#[test]
fn test_is_terminal_p1_and_p2_met_as_p1() {
let game_state: GameState = "
1s
+-----------------+
8| R r r r r |
7| |
6| x x |
5| |
4| |
3| x x |
2| |
1| r R |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
assert_eq!(game_state.is_terminal(), Some(Value([1.0, 0.0])));
}
#[test]
fn test_is_terminal_p1_and_p2_met_as_p2() {
let game_state: GameState = "
1g
+-----------------+
8| R r r r r |
7| |
6| x x |
5| |
4| |
3| x x |
2| |
1| r R |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
assert_eq!(game_state.is_terminal(), Some(Value([0.0, 1.0])));
}
#[test]
fn test_is_terminal_p2_lost_rabbits_as_p1() {
let game_state: GameState = "
1s
+-----------------+
8| |
7| e |
6| x x |
5| |
4| |
3| x x |
2| E R |
1| |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
assert_eq!(game_state.is_terminal(), Some(Value([1.0, 0.0])));
}
#[test]
fn test_is_terminal_p1_lost_rabbits_as_p2() {
let game_state: GameState = "
1g
+-----------------+
8| r |
7| e |
6| x x |
5| |
4| |
3| x x |
2| E |
1| |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
assert_eq!(game_state.is_terminal(), Some(Value([0.0, 1.0])));
}
#[test]
fn test_is_terminal_p1_lost_rabbits_as_p1() {
let game_state: GameState = "
1s
+-----------------+
8| r |
7| e |
6| x x |
5| |
4| |
3| x x |
2| E |
1| |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
assert_eq!(game_state.is_terminal(), Some(Value([0.0, 1.0])));
}
#[test]
fn test_is_terminal_p2_lost_rabbits_as_p2() {
let game_state: GameState = "
1g
+-----------------+
8| |
7| e |
6| x x |
5| |
4| |
3| x x |
2| E R |
1| |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
assert_eq!(game_state.is_terminal(), Some(Value([1.0, 0.0])));
}
#[test]
fn test_is_terminal_p1_and_p2_lost_rabbits_as_p1() {
let game_state: GameState = "
1s
+-----------------+
8| |
7| e |
6| x x |
5| |
4| |
3| x x |
2| E |
1| |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
assert_eq!(game_state.is_terminal(), Some(Value([1.0, 0.0])));
}
#[test]
fn test_is_terminal_p1_and_p2_lost_rabbits_as_p2() {
let game_state: GameState = "
1g
+-----------------+
8| |
7| e |
6| x x |
5| |
4| |
3| x x |
2| E |
1| |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
assert_eq!(game_state.is_terminal(), Some(Value([0.0, 1.0])));
}
#[test]
fn test_action_cant_push_while_frozen() {
let game_state: GameState = "
+-----------------+
8| |
7| |
6| x x |
5| e |
4| M r |
3| x x |
2| |
1| R |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
assert_valid_actions!(game_state => a1n, a1e, a1nn, a1ne, a1ee, a1nnn, a1nne, a1nee, a1eee, a1nnnn, a1nnne, a1nnee, a1neee, a1eeee);
}
#[test]
fn test_action_cant_push_while_frozen_2() {
let game_state: GameState = "
35s
+-----------------+
8| d |
7| r e r r h c r r |
6| H R m r r X c R |
5| d R E R R h R r |
4| R M C |
3| X X |
2| D D H |
1| C |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
let game_state = take_actions!(game_state => c8w);
has_action!(game_state => pg5ss);
does_not_have_action!(game_state => pg5se);
}
#[test]
fn test_action_move_possible_pull_with_valid_pull() {
let game_state: GameState = "
+-----------------+
8| r r r r |
7| |
6| x x |
5| |
4| E m |
3| x x |
2| |
1| R R R R |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
has_action!(game_state => pc4nw);
let game_state = take_actions!(game_state => c4n);
does_not_have_action!(game_state => pc4nw);
}
#[test]
fn test_action_move_pull_into_trap() {
let game_state: GameState = "
+-----------------+
8| r r r r |
7| |
6| x x |
5| |
4| E m |
3| x x |
2| |
1| R R R R |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
has_action!(game_state => pc4sw);
}
#[test]
fn test_action_move_possible_pull_with_invalid_pull() {
let game_state: GameState = "
+-----------------+
8| r r r r |
7| |
6| x x |
5| |
4| E e |
3| x x |
2| |
1| R R R R |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
does_not_have_action!(game_state => pc4nw);
}
#[test]
fn test_action_move_possible_pull_with_invalid_pull_2() {
let game_state: GameState = "
+-----------------+
8| r r r r |
7| |
6| x x |
5| |
4| M e |
3| x x |
2| |
1| R R R R |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
does_not_have_action!(game_state => pc4nw);
}
#[test]
fn test_action_pass() {
let game_state = initial_play_state();
let game_state = take_actions!(game_state => a2n);
assert_eq!(game_state.step(), 1);
assert!(game_state.is_p1_turn_to_move());
has_action!(game_state => p);
let game_state = take_actions!(game_state => p);
assert_eq!(game_state.step(), 0);
assert!(!game_state.is_p1_turn_to_move());
}
#[test]
fn test_valid_actions() {
let game_state = initial_play_state();
assert_valid_actions!(game_state => a2n, b2n, c2n, d2n, e2n, f2n, g2n, h2n, a2nn, b2nn, d2nn, e2nn, g2nn, h2nn, a2ne, b2ne, d2ne, e2ne, g2ne, b2nw, d2nw, e2nw, g2nw, h2nw, a2nnn, b2nnn, d2nnn, e2nnn, g2nnn, h2nnn, a2nne, b2nne, d2nne, e2nne, g2nne, b2nnw, d2nnw, e2nnw, g2nnw, h2nnw, a2nee, b2nee, d2nee, e2nee, d2nww, e2nww, g2nww, h2nww, a2nnnn, b2nnnn, d2nnnn, e2nnnn, g2nnnn, h2nnnn, a2nnne, b2nnne, d2nnne, e2nnne, g2nnne, b2nnnw, d2nnnw, e2nnnw, g2nnnw, h2nnnw, a2nnee, b2nnee, d2nnee, e2nnee, d2nnww, e2nnww, g2nnww, h2nnww, a2neee, b2neee, d2neee, e2neee, d2nwww, e2nwww, g2nwww, h2nwww);
}
#[test]
fn test_valid_actions_p2() {
let game_state: GameState = "
1s
+-----------------+
8| h c d m e d c h |
7| r r r r r r r r |
6| x x |
5| |
4| |
3| x x |
2| R R R R R R R R |
1| H C D M E D C H |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
assert_valid_actions!(game_state => a7s, b7s, c7s, d7s, e7s, f7s, g7s, h7s, a7se, b7se, d7se, e7se, g7se, a7ss, b7ss, d7ss, e7ss, g7ss, h7ss, b7sw, d7sw, e7sw, g7sw, h7sw, a7see, b7see, d7see, e7see, a7ses, b7ses, d7ses, e7ses, g7ses, a7sss, b7sss, d7sss, e7sss, g7sss, h7sss, b7ssw, d7ssw, e7ssw, g7ssw, h7ssw, d7sww, e7sww, g7sww, h7sww, a7seee, b7seee, d7seee, e7seee, a7sees, b7sees, d7sees, e7sees, a7sess, b7sess, d7sess, e7sess, g7sess, a7ssss, b7ssss, d7ssss, e7ssss, g7ssss, h7ssss, b7sssw, d7sssw, e7sssw, g7sssw, h7sssw, d7ssww, e7ssww, g7ssww, h7ssww, d7swww, e7swww, g7swww, h7swww);
}
#[test]
fn test_valid_actions_2() {
let game_state: GameState = "
+-----------------+
8| h c d m e d c h |
7| r r r r r r r r |
6| x x |
5| |
4| |
3| x R x |
2| R R R R R R R |
1| H C D M E D C H |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
assert_valid_actions!(game_state => e3n, a2n, b2n, c2n, d2n, f2n, g2n, h2n, e3e, d2e, e3w, f2w, e3nn, a2nn, b2nn, d2nn, f2nn, g2nn, h2nn, e3ne, a2ne, b2ne, f2ne, g2ne, e3nw, b2nw, d2nw, g2nw, h2nw, e3ee, e3ww, e3nnn, a2nnn, b2nnn, d2nnn, f2nnn, g2nnn, h2nnn, e3nne, a2nne, b2nne, d2nne, f2nne, g2nne, e3nnw, b2nnw, d2nnw, f2nnw, g2nnw, h2nnw, e3nee, a2nee, b2nee, f2nee, e3nww, d2nww, h2nww, e3eee, e3www, a2nnnn, b2nnnn, d2nnnn, f2nnnn, g2nnnn, h2nnnn, e3nnne, a2nnne, b2nnne, d2nnne, f2nnne, g2nnne, e3nnnw, b2nnnw, d2nnnw, f2nnnw, g2nnnw, h2nnnw, e3nnee, a2nnee, b2nnee, d2nnee, f2nnee, e3nnww, d2nnww, f2nnww, g2nnww, h2nnww, e3neee, a2neee, e3nwww, d2nwww, e3wwww, e1n);
}
#[test]
fn test_valid_actions_3() {
let game_state: GameState = "
1s
+-----------------+
8| h c d m e d c h |
7| r r r r r r |
6| x r x r |
5| |
4| |
3| x x |
2| R R R R R R R R |
1| H C D M E D C H |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
assert_valid_actions!(game_state => d7e, g7e, e6e, a7s, b7s, c7s, d7s, f7s, g7s, e6s, h6s, f7w, e6w, h6w, e6ee, e6es, a7se, b7se, f7se, a7ss, b7ss, d7ss, f7ss, g7ss, e6ss, h6ss, b7sw, d7sw, g7sw, e6sw, h6sw, e6ww, h6ww, e6ees, a7see, b7see, e6ess, a7ses, b7ses, f7ses, d7sse, g7sse, a7sss, b7sss, d7sss, f7sss, g7sss, e6sss, h6sss, b7ssw, d7ssw, f7ssw, g7ssw, e6ssw, h6ssw, d7sww, e6sww, h6sww, e6www, e6eese, a7seee, e6eess, a7sees, b7sees, f7sese, d7ssee, e6esss, a7sess, b7sess, f7sess, d7sses, g7sses, a7ssss, b7ssss, d7ssss, f7ssss, g7ssss, b7sssw, d7sssw, f7sssw, g7sssw, e6sssw, h6sssw, d7ssww, f7ssww, g7ssww, e6ssww, h6ssww, d7swww, e6swww, h6swww, e6wwww, h8s, e8s);
}
#[test]
fn test_valid_actions_4() {
let game_state: GameState = "
1g
+-----------------+
8| h c d m e d c h |
7| r r r r r r r r |
6| x x |
5| |
4| |
3| x E x |
2| R R R R R R R |
1| H C D M R D C H |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
assert_valid_actions!(game_state => a2n, b2n, c2n, d2n, f2n, g2n, h2n, e1n, d2e, f2w, a2nn, b2nn, d2nn, f2nn, g2nn, h2nn, a2ne, b2ne, f2ne, g2ne, b2nw, d2nw, g2nw, h2nw, a2nnn, b2nnn, d2nnn, f2nnn, g2nnn, h2nnn, a2nne, b2nne, d2nne, f2nne, g2nne, b2nnw, d2nnw, f2nnw, g2nnw, h2nnw, a2nee, b2nee, f2nee, d2nww, h2nww, a2nnnn, b2nnnn, d2nnnn, f2nnnn, g2nnnn, h2nnnn, a2nnne, b2nnne, d2nnne, f2nnne, g2nnne, b2nnnw, d2nnnw, f2nnnw, g2nnnw, h2nnnw, a2nnee, b2nnee, d2nnee, f2nnee, d2nnww, f2nnww, g2nnww, h2nnww, a2neee, d2nwww, e3n, e3e, e3s, e3w, e3nn, e3ne, e3nw, e3ee, e3ww, e3nnn, e3nne, e3nnw, e3nee, e3nww, e3eee, e3www, e3nnne, e3nnnw, e3nnee, e3nnww, e3neee, e3nwww, e3wwww);
}
#[test]
fn test_valid_actions_5() {
let game_state: GameState = "
1s
+-----------------+
8| h c d m r d c h |
7| r r r r r r r |
6| x e x |
5| |
4| |
3| x x |
2| R R R R E R R R |
1| H C D M R D C H |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
assert_valid_actions!(game_state => d7e, e8s, a7s, b7s, c7s, d7s, f7s, g7s, h7s, f7w, a7se, b7se, f7se, g7se, a7ss, b7ss, d7ss, f7ss, g7ss, h7ss, b7sw, d7sw, g7sw, h7sw, a7see, b7see, f7see, a7ses, b7ses, f7ses, g7ses, d7sse, a7sss, b7sss, d7sss, f7sss, g7sss, h7sss, b7ssw, d7ssw, f7ssw, g7ssw, h7ssw, d7sww, h7sww, a7seee, a7sees, b7sees, f7sees, d7ssee, a7sess, b7sess, f7sess, g7sess, d7sses, a7ssss, b7ssss, d7ssss, f7ssss, g7ssss, h7ssss, b7sssw, d7sssw, f7sssw, g7sssw, h7sssw, d7ssww, f7ssww, g7ssww, h7ssww, d7swww, e6n, e6e, e6s, e6w, e6ee, e6es, e6ss, e6sw, e6ww, e6eee, e6ees, e6ess, e6sss, e6ssw, e6sww, e6www, e6eees, e6eess, e6esss, e6sssw, e6ssww, e6swww, e6wwww);
}
#[test]
fn test_valid_actions_6() {
let game_state: GameState = "
+-----------------+
8| r r r r |
7| m h e c |
6| r x r r x r |
5| h d c d |
4| E H M |
3| R x R R H R |
2| D C C D |
1| R R R R |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
assert_valid_actions!(game_state => pa5nn, pc5nn, ph5nn, pa5en, pc5en, pc5wn, ph5wn, pa4es, pc4es, pa4ss, pc4ss, ph4ss, pc4ws, ph4ws, b3n, d3n, e3n, g3n, b1n, d1n, e1n, g1n, b3e, g3e, b1e, e1e, g1e, b3w, d3w, b1w, d1w, g1w, b3nn, d3nn, e3nn, g3nn, d3ne, e3ne, d1ne, e3nw, g3nw, e1nw, d3nen, e3nwn, d3nee, e3nee, g3nww, e3neen, g3nwwn, d3neee, g3nwww, c2n, c2e, f2e, c2s, f2s, c2w, f2w, c2ee, f2ww, a2n, h2n, a2e, a2s, h2s, h2w, f3n, c4e, c4s, c4w, f3ne, c4en, f3nw, c4wn, c4ee, f3nen, f3nwn, c4ene, f3nww, c4eee, f3nwnw, c4eeee, h4s, h4w, h4wn, h4ww, h4www, h4wwwn, h4wwww, a4e, a4s, a4en);
}
#[test]
fn test_valid_actions_7() {
let game_state: GameState = "
1s
+-----------------+
8| r r r r |
7| m h e c |
6| r x r r x r |
5| h d c d |
4| E H M |
3| R x R R H R |
2| D C C D |
1| R R R R |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
assert_valid_actions!(game_state => b8e, e8e, g8e, b6e, e6e, g6e, b8s, d8s, e8s, g8s, b6s, d6s, e6s, g6s, b8w, d8w, g8w, b6w, d6w, g6w, d8se, d6se, b6ss, d6ss, e6ss, g6ss, e8sw, e6sw, d6ses, e6sse, e6ssw, d6sese, e6ssee, h7n, f5n, f5e, h7s, f5s, h7w, f5w, f5es, f5ws, f5ww, f5wsw, c7n, c7e, c7s, c7w, c7ee, a7n, a7e, a7s, f7n, f7e, f7s, f7w, f7ww);
}
#[test]
fn test_valid_actions_frozen_piece_p1() {
let game_state: GameState = "
1g
+-----------------+
8| |
7| |
6| x x |
5| e |
4| M |
3| x x |
2| |
1| R |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
assert_valid_actions!(game_state => a1n, a1e, a1nn, a1ne, a1ee, a1nnn, a1nne, a1nee, a1eee, a1nnnn, a1nnne, a1nnee, a1neee, a1eeee);
}
#[test]
fn test_valid_actions_frozen_piece_p1_2() {
let game_state: GameState = "
1g
+-----------------+
8| |
7| |
6| x x |
5| m |
4| M |
3| x x |
2| |
1| |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
assert_valid_actions!(game_state => d4e, d4s, d4w, d4ennw, d4en, d4wn, d4ee, d4es, d4ss, d4sw, d4ww, d4enn, d4wnn, d4ene, d4wnw, d4eee, d4ees, d4ess, d4sss, d4ssw, d4wws, d4www, d4ennn, d4enne, d4wnwn, d4enee, d4wnww, d4eeee, d4eees, d4esse, d4esss, d4sssw, d4ssww, d4wwsw);
}
#[test]
fn test_valid_actions_frozen_piece_p2() {
let game_state: GameState = "
1s
+-----------------+
8| |
7| |
6| x x |
5| m |
4| M |
3| x x |
2| |
1| R |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
assert_valid_actions!(game_state => d5n, d5e, d5w, d5nn, d5ne, d5nw, d5ee, d5es, d5essw, d5ws, d5ww, d5nnn, d5nne, d5nnw, d5nee, d5wwn, d5eee, d5ees, d5ess, d5wss, d5wsw, d5www, d5nnne, d5nnnw, d5nnee, d5nnww, d5eeen, d5wwnw, d5eeee, d5eees, d5eess, d5esss, d5wsws, d5wsww);
}
#[test]
fn test_valid_actions_frozen_piece_p2_2() {
let game_state: GameState = "
1s
+-----------------+
8| r |
7| |
6| x x |
5| h |
4| M |
3| x x |
2| |
1| R |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
assert_valid_actions!(game_state => a8e, a8s, a8ee, a8es, a8ss, a8eee, a8ees, a8ess, a8sss, a8eeee, a8eees, a8eess, a8esss, a8ssss);
}
#[test]
fn test_valid_actions_frozen_piece_p2_3() {
let game_state: GameState = "
1s
+-----------------+
8| r |
7| |
6| x x |
5| r |
4| M |
3| x x |
2| |
1| R |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
assert_valid_actions!(game_state => a8e, a8s, a8ee, a8es, a8ss, a8eee, a8ees, a8ess, a8sss, a8eeee, a8eees, a8eess, a8esss, a8ssss);
}
#[test]
fn test_valid_actions_frozen_piece_p2_4() {
let game_state: GameState = "
1s
+-----------------+
8| |
7| |
6| x x |
5| e |
4| E |
3| x x |
2| |
1| R |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
does_not_have_action!(game_state => pd4es);
does_not_have_action!(game_state => pd4ws);
does_not_have_action!(game_state => pd4ss);
}
#[test]
fn test_valid_actions_frozen_push_p2_1() {
let game_state: GameState = "
1s
+-----------------+
8| |
7| |
6| x x |
5| e |
4| M |
3| x x |
2| |
1| R |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
assert_valid_actions!(game_state => pd5nn, pd5en, pd5wn, pd4es, pd4ss, pd4ws, d5n, d5e, d5w, d5nn, d5ne, d5nw, d5ee, d5es, d5essw, d5ws, d5ww, d5nnn, d5nne, d5nnw, d5nee, d5wwn, d5eee, d5ees, d5ess, d5wss, d5wsw, d5www, d5nnne, d5nnnw, d5nnee, d5nnww, d5eeen, d5wwnw, d5eeee, d5eees, d5eess, d5esss, d5wsws, d5wsww);
}
#[test]
fn test_valid_actions_frozen_push_p2_2() {
let game_state: GameState = "
1s
+-----------------+
8| r |
7| |
6| x x |
5| e |
4| M |
3| x x |
2| |
1| R |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
assert_valid_actions!(game_state => pd5nn, pd5en, pd5wn, pd4es, pd4ss, pd4ws, a8e, a8s, a8ee, a8es, a8ss, a8eee, a8ees, a8ess, a8sss, a8eeee, a8eees, a8eess, a8esss, a8ssss, d5n, d5e, d5w, d5nn, d5ne, d5nw, d5ee, d5es, d5essw, d5ws, d5ww, d5nnn, d5nne, d5nnw, d5nee, d5wwn, d5eee, d5ees, d5ess, d5wss, d5wsw, d5www, d5nnne, d5nnnw, d5nnee, d5nnww, d5eeen, d5wwnw, d5eeee, d5eees, d5eess, d5esss, d5wsws, d5wsww);
let game_state = take_actions!(game_state => pd4es);
assert_valid_actions!(game_state => pe4ne, pe4ee, pe4se, pd4sw, pd4ww, a8e, a8s, a8ee, a8es, a8ss, d4n, d4s, d4w, d4nn, d4ne, d4nw, d4se, d4ss, d4sw, d4ww, p);
}
#[test]
fn test_valid_actions_frozen_push_p2_3() {
let game_state: GameState = "
1s
+-----------------+
8| r |
7| |
6| x x |
5| e |
4| R |
3| x c x |
2| |
1| R |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
has_action!(game_state => pd4es);
has_action!(game_state => pd4en);
}
#[test]
fn test_valid_actions_frozen_push_p2_4() {
let game_state: GameState = "
1s
+-----------------+
8| r |
7| |
6| x x |
5| e |
4| E R |
3| x c x |
2| |
1| R |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
assert_valid_actions!(game_state => pd5nn, pd5en, pd4en, pd5wn, pd4es, pd3es, pd3ss, pd3ws, a8e, a8s, a8ee, a8es, a8ss, a8eee, a8ees, a8ess, a8sss, a8eeee, a8eees, a8eess, a8esss, a8ssss, d3e, d3s, d3w, d3en, d3ee, d3es, d3ss, d3sw, d3swwn, d3enn, d3ene, d3ese, d3ess, d3ssw, d3sww, d3ennn, d3enne, d3enee, d3esee, d3eses, d3ssww, d3swww, d5n, d5e, d5w, d5nn, d5ne, d5nw, d5ee, d5es, d5ww, d5nnn, d5nne, d5nnw, d5nee, d5wwn, d5eee, d5ees, d5ess, d5wws, d5www, d5nnne, d5nnnw, d5nnee, d5nnww, d5eeen, d5wwnw, d5eeee, d5eees, d5eess, d5esss, d5wwss, d5wwsw);
let game_state = take_actions!(game_state => pd4es);
assert_valid_actions!(game_state => pe4ne, pe4ee, pe4se, a8e, a8s, a8ee, a8es, a8ss, d3e, d3s, d3w, d3ee, d3es, d3ss, d3sw, d4n, d4nn, d4ne, d4nw, p);
}
#[test]
fn test_valid_actions_supported_piece_p2_1() {
let game_state: GameState = "
1s
+-----------------+
8| |
7| |
6| x x |
5| r m |
4| C E |
3| x x |
2| |
1| R |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
assert_valid_actions!(game_state => c5w, c5ws, c5ww, c5wws, c5wwss, d5n, d5e, d5nn, d5ne, d5nw, d5ee, d5es, d5nwws, d5nnn, d5nne, d5nnw, d5nee, d5nww, d5eee, d5ees, d5nnne, d5nnnw, d5nnee, d5nnww, d5eeen, d5nwww, d5eeee, d5eees, d5eess);
}
#[test]
fn test_valid_actions_matches_first_step_2() {
let game_state: GameState = "
1g
+-----------------+
8| r |
7| d C |
6| x x |
5| |
4| |
3| x x |
2| E h |
1| e R |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
let game_state = take_actions!(game_state => a2s);
let game_state = take_actions!(game_state => a1n);
let game_state = take_actions!(game_state => a2s);
assert_valid_actions!(game_state => p);
}
#[test]
fn test_valid_actions_matches_is_terminal_if_only_passing() {
let game_state: GameState = "
1g
+-----------------+
8| |
7| |
6| x x |
5| m |
4| E |
3| x x |
2| |
1| |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
let game_state = verify_and_take_action(game_state, "pd4es");
does_not_have_action!(game_state => pd4nw);
}
#[test]
fn test_valid_actions_repeated_positions() {
let game_state: GameState = "
1g
+-----------------+
8| r |
7| d |
6| x x |
5| m |
4| |
3| x E x |
2| |
1| R |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
let game_state = verify_and_take_action(game_state, "d3n");
let game_state = verify_and_take_action(game_state, "p");
// First occurance of position
let game_state = verify_and_take_action(game_state, "h7s");
let game_state = verify_and_take_action(game_state, "p");
let game_state = verify_and_take_action(game_state, "d4s");
let game_state = verify_and_take_action(game_state, "p");
let game_state = verify_and_take_action(game_state, "h6n");
let game_state = verify_and_take_action(game_state, "p");
let game_state = verify_and_take_action(game_state, "d3n");
let game_state = verify_and_take_action(game_state, "p");
// Second occurance of position
let game_state = verify_and_take_action(game_state, "h7s");
let game_state = verify_and_take_action(game_state, "p");
let game_state = verify_and_take_action(game_state, "d4ss");
let game_state = verify_and_take_action(game_state, "p");
let game_state = verify_and_take_action(game_state, "h6n");
let game_state = verify_and_take_action(game_state, "p");
// Would be third occurance of position
let game_state = verify_and_take_action(game_state, "d2nn");
// Should not allow pass here
does_not_have_action!(game_state => p);
let game_state = take_actions!(game_state => d4s);
// Should not allow last move to be the final repeat
does_not_have_action!(game_state => d3n);
assert_valid_actions!(game_state => h1n, h1w, p);
}
#[test]
fn test_valid_actions_does_not_duplicate_piece_that_can_be_both_pushed_and_pull() {
let game_state: GameState = "
1g
+-----------------+
8| |
7| |
6| x x |
5| |
4| E c H |
3| x x |
2| |
1| e |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
let game_state = take_actions!(game_state => f4n);
assert_valid_actions!(game_state => pe4ne, pe4ee, pe4se, pd4nw, pd4sw, pd4ww, d4n, d4s, d4w, d4nn, d4ne, d4nw, d4se, d4ss, d4sw, d4ww, d4nnn, d4nne, d4nnw, d4nww, d4see, d4ses, d4sss, d4ssw, d4wws, d4www, p);
}
#[test]
fn test_hash_initial_play_state() {
let game_state: GameState = "
1g
+-----------------+
8| h c d m e d c h |
7| r r r r r r r r |
6| x x |
5| |
4| |
3| x x |
2| R R R R R R R R |
1| H C D M E D C H |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
let game_state_2 = initial_play_state();
assert_eq!(game_state, game_state_2);
assert_eq!(game_state.hash(), game_state_2.hash());
}
#[test]
fn test_hash_should_account_for_player_not_equal() {
let game_state: GameState = "
1s
+-----------------+
8| h c d m e d c h |
7| r r r r r r r r |
6| x x |
5| |
4| |
3| x x |
2| R R R R R R R R |
1| H C D M E D C H |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
let game_state_2 = initial_play_state();
assert_ne!(game_state.hash(), game_state_2.hash());
}
#[test]
fn test_hash_should_account_for_step_not_equal() {
let game_state: GameState = "
1g
+-----------------+
8| h c d m e d c h |
7| r r r r r r r r |
6| x x |
5| |
4| |
3| x R x |
2| R R R R R R R |
1| H C D M E D C H |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
let game_state_2 = initial_play_state();
let game_state_2 = game_state_2.take_action(&"e2n".parse().unwrap());
assert_ne!(game_state.hash(), game_state_2.hash());
}
#[test]
fn test_hash_should_account_for_step_equal() {
let game_state: GameState = "
1g
+-----------------+
8| h c d m e d c h |
7| r r r r r r r r |
6| x x |
5| |
4| |
3| x R x |
2| R R R R R R R |
1| H C D M E D C H |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
let game_state_2 = initial_play_state();
let game_state_2 = game_state_2.take_action(&"e2n".parse().unwrap());
assert_ne!(game_state.hash(), game_state_2.hash());
let game_state_2 = game_state_2.take_action(&Action::Pass);
let game_state_2 = game_state_2.take_action(&Action::Pass);
assert_eq!(game_state.hash(), game_state_2.hash());
}
#[test]
fn test_hash_should_allow_for_pass_to_equal() {
let game_state: GameState = "
1g
+-----------------+
8| h c d m e d c h |
7| r r r r r r r r |
6| x x |
5| |
4| |
3| x x |
2| R R R R R R R R |
1| H C D M E D C H |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
let game_state_2 = initial_play_state();
let game_state_2 = game_state_2.take_action(&Action::Pass);
assert_ne!(game_state.hash(), game_state_2.hash());
let game_state_2 = game_state_2.take_action(&Action::Pass);
assert_eq!(game_state.hash(), game_state_2.hash());
}
#[test]
fn test_hash_should_switch_player_on_pass_for_step() {
let game_state: GameState = "
1g
+-----------------+
8| h c d m e d c h |
7| r r r r r r r r |
6| x x |
5| |
4| |
3| x R x |
2| R R R R R R R |
1| H C D M E D C H |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
let game_state_2 = initial_play_state();
let game_state_2 = game_state_2.take_action(&"e2n".parse().unwrap());
assert_ne!(game_state.hash(), game_state_2.hash());
}
#[test]
fn test_hash_should_account_for_trapped_pieces() {
let game_state: GameState = "
1g
+-----------------+
8| h c d m e d c h |
7| r r r r r r |
6| x E r x |
5| |
4| |
3| x x |
2| R R R R R R R |
1| H C D M D C H |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
let game_state_final: GameState = "
1s
+-----------------+
8| h c d m e d c h |
7| r r r r r r |
6| x E x |
5| |
4| |
3| x x |
2| R R R R R R R |
1| H C D M D C H |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
let game_state = take_actions!(game_state => e6e);
assert_ne!(game_state.hash(), game_state_final.hash());
let game_state = take_actions!(game_state => d6e);
assert_ne!(game_state.hash(), game_state_final.hash());
let game_state = take_actions!(game_state => p);
assert_eq!(game_state.hash(), game_state_final.hash());
}
#[test]
fn test_hash_should_account_for_four_actions() {
let game_state: GameState = "
1g
+-----------------+
8| h c d m e d c h |
7| r r r r r r |
6| x E r x |
5| r |
4| |
3| x x |
2| R R R R R R R |
1| H C D M D C H |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
let game_state_final: GameState = "
1s
+-----------------+
8| h c d m e d c h |
7| r r r r E r r |
6| x r x |
5| |
4| |
3| x x |
2| R R R R R R R |
1| H C D M D C H |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
let game_state = take_actions!(game_state => e6e);
assert_ne!(game_state.hash(), game_state_final.hash());
let game_state = take_actions!(game_state => d6e);
assert_ne!(game_state.hash(), game_state_final.hash());
let game_state = take_actions!(game_state => e6n);
assert_ne!(game_state.hash(), game_state_final.hash());
let game_state = take_actions!(game_state => e5n);
assert_eq!(game_state.hash(), game_state_final.hash());
}
#[test]
fn test_double_move_should_not_prevent_another_piece_mid_move() {
let game_state: GameState = "
1g
+-----------------+
8| r |
7| |
6| x x |
5| |
4| |
3| x m x |
2| C H |
1| R |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
let game_state = verify_and_take_action(game_state, "b2n");
let game_state = verify_and_take_action(game_state, "c2nn");
verify_and_take_action(game_state, "b3w");
}
#[test]
fn test_double_move_should_allow_support_of_path() {
let game_state: GameState = "
1g
+-----------------+
8| r |
7| |
6| x x |
5| C h |
4| C R m |
3| x x |
2| |
1| R |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
let game_state = verify_and_take_action(game_state, "b4e");
does_not_have_action!(game_state => c4w);
let game_state = verify_and_take_action(game_state, "d4n");
has_action!(game_state => c4w);
}
#[test]
fn test_double_move_should_allow_support_of_path_trap() {
let game_state: GameState = "
1g
+-----------------+
8| r |
7| |
6| x x |
5| h |
4| R m |
3| C x x |
2| R |
1| R |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
let game_state = verify_and_take_action(game_state, "a3e");
does_not_have_action!(game_state => b3n);
let game_state = verify_and_take_action(game_state, "c2nn");
has_action!(game_state => b3n);
}
#[test]
fn test_double_move_should_allow_support_of_path_trap_already_supported() {
let game_state: GameState = "
1g
+-----------------+
8| r |
7| |
6| x x |
5| h |
4| m |
3| C x R x |
2| R |
1| R |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
let game_state = verify_and_take_action(game_state, "a3e");
does_not_have_action!(game_state => b3n);
let game_state = verify_and_take_action(game_state, "c2nn");
does_not_have_action!(game_state => b3n);
}
#[test]
fn test_double_move_should_not_prevent_another_frozen_piece() {
let game_state: GameState = "
1g
+-----------------+
8| r |
7| |
6| x x |
5| |
4| |
3| x m x |
2| C H |
1| R |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
let game_state = verify_and_take_action(game_state, "b2e");
let game_state = verify_and_take_action(game_state, "d2e");
verify_and_take_action(game_state, "c2s");
}
#[test]
fn test_double_move_should_not_prevent_another_trapped_piece() {
let game_state: GameState = "
1g
+-----------------+
8| r |
7| |
6| x x |
5| |
4| c |
3| m H x |
2| E |
1| R |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
let game_state = verify_and_take_action(game_state, "b2n");
let game_state = verify_and_take_action(game_state, "pb3ww");
verify_and_take_action(game_state, "a3n");
}
#[test]
fn test_double_move_should_not_prevent_a_sequence_of_required_events() {
let game_state: GameState = "
2g
+-----------------+
8| r |
7| |
6| x x |
5| |
4| c |
3| h M e x |
2| R |
1| R R |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
let game_state = verify_and_take_action(game_state, "d1n");
let game_state = verify_and_take_action(game_state, "pd3ne");
verify_and_take_action(game_state, "d2w");
}
#[test]
fn test_double_move_should_prevent_a_piece_from_moving_twice() {
let game_state: GameState = "
1g
+-----------------+
8| r |
7| |
6| x x |
5| |
4| c |
3| m H x |
2| E R |
1| R |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
let game_state = verify_and_take_action(game_state, "e2n");
assert!(does_not_have_action(&game_state, "e3n"), "Should not be able to move the elephant again");
does_not_have_action!(game_state => e3e);
does_not_have_action!(game_state => e3nn);
does_not_have_action!(game_state => e3ne);
does_not_have_action!(game_state => e3nw);
}
#[test]
fn test_double_move_unfreezing_piece() {
let game_state: GameState = "
1g
+-----------------+
8| r |
7| |
6| x x |
5| |
4| |
3| x x |
2| E h C |
1| R R |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
let game_state = verify_and_take_action(game_state, "e1n");
let game_state = verify_and_take_action(game_state, "d2n");
let game_state = verify_and_take_action(game_state, "e2n");
assert!(does_not_have_action(&game_state, "d3n"), "Should not be able to move the cat again");
assert!(does_not_have_action(&game_state, "e3n"), "Should not be able to move the rabbit again");
}
#[test]
fn test_double_move_after_push_pull() {
let game_state: GameState = "
1g
+-----------------+
8| r |
7| |
6| x x |
5| |
4| |
3| x x |
2| E r |
1| C R R |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
let game_state = verify_and_take_action(game_state, "d1n");
let game_state = verify_and_take_action(game_state, "pd2nw");
verify_and_take_action(game_state, "d3n");
}
#[test]
fn test_double_move_after_push_pull_2() {
let game_state: GameState = "
1g
+-----------------+
8| r |
7| |
6| x x |
5| |
4| |
3| x x |
2| E C r |
1| R R |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
let game_state = verify_and_take_action(game_state, "pd2nw");
let game_state = verify_and_take_action(game_state, "d3n");
assert!(does_not_have_action(&game_state, "d4n"), "Should not be able to move the cat again");
}
#[test]
fn test_double_move_after_piece_trapped() {
let game_state: GameState = "
1g
+-----------------+
8| r |
7| |
6| x x |
5| |
4| |
3| x R |
2| E C R |
1| R R |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
let game_state = verify_and_take_action(game_state, "f2w");
assert!(does_not_have_action(&game_state, "f2e"), "Should not be able to move the rabbit again");
}
#[test]
fn test_double_move_vacate_path() {
let game_state: GameState = "
1g
+-----------------+
8| r |
7| |
6| x x |
5| |
4| |
3| x x |
2| E C |
1| R R |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
let game_state = verify_and_take_action(game_state, "e2w");
does_not_have_action!(game_state => d2e);
let game_state = verify_and_take_action(game_state, "e1nn");
has_action!(game_state => d2e);
}
#[test]
fn test_double_move_multiple_duplicates() {
let game_state: GameState = "
1g
+-----------------+
8| r |
7| |
6| x x |
5| |
4| |
3| x x |
2| E R R R |
1| |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
let game_state = verify_and_take_action(game_state, "d2n");
does_not_have_action!(game_state => d3n);
let game_state = verify_and_take_action(game_state, "e2n");
does_not_have_action!(game_state => d3n);
does_not_have_action!(game_state => e3n);
let game_state = verify_and_take_action(game_state, "g2n");
does_not_have_action!(game_state => d3n);
does_not_have_action!(game_state => e3n);
does_not_have_action!(game_state => g3n);
has_action!(game_state => b2n);
}
#[test]
fn test_double_move_multiple_duplicates_2() {
let game_state: GameState = "
1g
+-----------------+
8| r |
7| |
6| x x |
5| |
4| |
3| x x |
2| E R R R h |
1| |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
let game_state = verify_and_take_action(game_state, "d2n");
does_not_have_action!(game_state => d3n);
let game_state = verify_and_take_action(game_state, "e2e");
does_not_have_action!(game_state => d3n);
let game_state = verify_and_take_action(game_state, "g2n");
does_not_have_action!(game_state => d3n);
has_action!(game_state => f2e);
has_action!(game_state => b2n);
}
#[test]
fn test_double_move_swap_positions() {
let game_state: GameState = "
1g
+-----------------+
8| r |
7| |
6| x x |
5| |
4| |
3| x x |
2| E C R R h |
1| |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
let game_state = verify_and_take_action(game_state, "d2ne");
does_not_have_action!(game_state => e3n);
does_not_have_action!(game_state => e3s);
let game_state = verify_and_take_action(game_state, "e2w");
has_action!(game_state => e3n);
has_action!(game_state => e3e);
has_action!(game_state => e3s);
has_action!(game_state => e3w);
verify_and_take_action(game_state, "e3s");
}
#[test]
fn test_double_move_swap_positions_2() {
let game_state: GameState = "
1g
+-----------------+
8| r |
7| |
6| x x |
5| |
4| |
3| x x |
2| E C R R h |
1| |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
let game_state = verify_and_take_action(game_state, "d2ne");
does_not_have_action!(game_state => d3e);
let game_state = verify_and_take_action(game_state, "e2w");
has_action!(game_state => e3n);
has_action!(game_state => e3e);
has_action!(game_state => e3s);
does_not_have_action!(game_state => d2n);
verify_and_take_action(game_state, "e3s");
}
#[test]
fn test_double_protect_trap() {
let game_state: GameState = "
1g
+-----------------+
8| r |
7| |
6| x x |
5| |
4| |
3| x x |
2| E C R R |
1| |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
let game_state = verify_and_take_action(game_state, "g2n");
does_not_have_action!(game_state => g3e);
let game_state = verify_and_take_action(game_state, "f2nn");
has_action!(game_state => g3e);
}
#[test]
fn test_double_frozen_walk() {
let game_state: GameState = "
1g
+-----------------+
8| r |
7| |
6| x x |
5| |
4| |
3| x x |
2| E h |
1| h R R |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
let game_state = verify_and_take_action(game_state, "e1n");
let game_state = verify_and_take_action(game_state, "f1n");
let game_state = verify_and_take_action(game_state, "e2n");
has_action!(game_state => f2n);
has_action!(game_state => f2e);
has_action!(game_state => f2w);
does_not_have_action!(game_state => e3n);
does_not_have_action!(game_state => e3e);
does_not_have_action!(game_state => e3w);
}
#[test]
fn test_double_can_move_on_double_move() {
let game_state: GameState = "
1g
+-----------------+
8| r |
7| |
6| x x |
5| |
4| |
3| x x |
2| e R C |
1| |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
// Force occurance of position to happen twice.
let game_state = take_actions!(game_state => p);
let game_state = take_actions!(game_state => p);
let game_state = take_actions!(game_state => p);
let game_state = take_actions!(game_state => p);
// Move out of the current forced repition so that the next set of actions can't finish in this position.
let game_state = take_actions!(game_state => f2w);
let game_state = take_actions!(game_state => p);
let game_state = take_actions!(game_state => p);
// Move the cat to the original position forcing both a double_move and can't pass simultaneously.
let game_state = verify_and_take_action(game_state, "e2e");
does_not_have_action!(game_state => p);
assert_eq!(format!("{:?}", game_state.valid_actions()), "[]");
assert!(game_state.is_terminal().is_some());
}
#[test]
fn test_if_set_of_actions_is_valid_move_piece_twice_to_drop() {
let game_state = take_and_verify_actions![e2, h2, a1, b1, f2, b2, a2, c2, a7, f8, a8, b8, d8, c7, c8, g8, b2ne, c2w];
has_action!(game_state => b2e);
}
#[test]
fn test_if_set_of_actions_is_valid_move_piece_thrice_to_drop() {
let game_state = take_actions![
e2, h2, a1, b1, f2, b2, a2, c2, a7, f8, a8, b8, d8, c7, c8, g8, d2nnnn, a7sess, e2nnne, e7seee, h2n, e1nnw, d7e, b4sw, d8s, h3nwww, a3ne, pd6ss, a2nee, e4w, b4n, h6s, pd5es, h1nnn, g2e,
b5es, e7sw, f5esse, g7sw, b7se, f1wnn, b2n, c4n, f7es
];
has_action!(game_state => g6w);
}
#[test]
fn test_if_set_of_actions_is_valid() {
let game_state = take_actions![
e2, h2, a1, b1, f2, b2, a2, c2, a7, f8, a8, b8, d8, c7, c8, g8, g2nwww, d7seee, f2ene, d3w, a7ses, g6w, d2ne, h3ww, d8sssw, f1nene, b5sees, e3n
];
let game_state = take_actions![game_state => c2e];
has_action!(game_state => d2wn);
}
#[test]
fn test_if_set_of_actions_is_valid_piece_support_swap() {
let game_state = take_actions![e2, h2, a1, b1, f2, b2, a2, c2, a7, f8, a8, b8, d8, c7, c8, g8, b2n, c2n, c1n];
has_action!(game_state => b3n);
}
#[test]
fn test_if_set_of_actions_is_valid_vacate_and_swap_with_pull() {
let game_state = take_actions![g2, d2, a2, b2, f2, b1, e1, a1, e8, a8, f8, g8, c8, d8, a7, d7, e2nnnw, g7ssss, h2n, pg2es];
has_action!(game_state => h3w);
}
#[test]
fn test_if_set_of_actions_vacate_with_trap_support() {
let game_state = take_actions![g2, d2, a2, b2, f2, b1, e1, a1, e8, a8, f8, g8, c8, d8, a7, d7, b2n, c2w, d2w];
has_action!(game_state => b3e);
}
#[test]
fn test_valid_actions_double_vacate_trap_support() {
let game_state: GameState = "
4g
+-----------------+
8| d d h r c h m r |
7| c e r r r r |
6| r x r x |
5| R |
4| |
3| x R H |
2| H D E M C C |
1| R R D R R R R |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
let game_state = verify_and_take_action(game_state, "e3w");
does_not_have_action!(game_state => d3e);
let game_state = verify_and_take_action(game_state, "f2e");
has_action!(game_state => g2w);
has_action!(game_state => d3e);
let game_state = verify_and_take_action(game_state, "g2w");
has_action!(game_state => d3e);
}
#[test]
fn test_swap_trap_protection() {
let game_state: GameState = "
3s
+-----------------+
8| d c m h c e h d |
7| r r r r r r r |
6| x x |
5| r |
4| |
3| R R E x |
2| R H D R C H |
1| R R R C D R M |
+-----------------+
a b c d e f g h"
.parse()
.unwrap();
let game_state = verify_and_take_action(game_state, "c7s");
does_not_have_action!(game_state => c7n);
does_not_have_action!(game_state => c6e);
does_not_have_action!(game_state => c6s);
does_not_have_action!(game_state => c6w);
let game_state = verify_and_take_action(game_state, "c8s");
let game_state = verify_and_take_action(game_state, "c5s");
does_not_have_action!(game_state => c7n);
has_action!(game_state => c6e);
has_action!(game_state => c6w);
has_action!(game_state => c6s);
}
#[test]
fn test_if_set_of_actions_is_valid_triple_swap() {
let game_state: GameState = "
1g
+-----------------+
8| r |
7| |
6| x x |
5| |
4| |
3| x C x |
2| R D |
1| |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
let game_state = verify_and_take_action(game_state, "d3e");
let game_state = verify_and_take_action(game_state, "d2n");
does_not_have_action!(game_state => e3n);
does_not_have_action!(game_state => d3n);
let game_state = verify_and_take_action(game_state, "e2w");
has_action!(game_state => e3s);
}
#[test]
fn test_dropped_supporting_piece() {
let game_state: GameState = "
1g
+-----------------+
8| r |
7| |
6| x x |
5| d |
4| |
3| R C x |
2| |
1| |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
has_action!(game_state => d3nw);
has_action!(game_state => d3nn);
does_not_have_action!(game_state => d3nww);
}
#[test]
fn test_dropped_supporting_piece_2() {
let game_state: GameState = "
1g
+-----------------+
8| r |
7| |
6| x x |
5| R |
4| |
3| R C x |
2| |
1| |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
verify_and_take_action(game_state, "d3nww");
}
#[test]
fn test_dropped_supporting_piece_3() {
let game_state: GameState = "
1g
+-----------------+
8| r |
7| |
6| x x |
5| d |
4| |
3| R C x |
2| R |
1| |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
verify_and_take_action(game_state, "d3nww");
}
#[test]
fn test_dropped_supporting_piece_4() {
let game_state: GameState = "
1g
+-----------------+
8| r |
7| |
6| x x |
5| d |
4| |
3| R R C x |
2| R |
1| |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
verify_and_take_action(game_state, "d3nww");
}
#[test]
fn test_dropped_supporting_piece_5() {
let game_state: GameState = "
1g
+-----------------+
8| r |
7| |
6| x x |
5| d |
4| R |
3| R R C x |
2| R |
1| |
+-----------------+
a b c d e f g h
"
.parse()
.unwrap();
does_not_have_action!(game_state => d3nww);
}
// -----------------------------------------------------------------------
// Third-repetition terminal tests
//
// Board used for all third-rep tests. Gold elephant (E) at d3, silver dog
// (d) at h7, silver camel (m) at d5. The elephant oscillates d3↔d4 and
// the dog oscillates h7↔h6.
//
// Hash-history positions (abbreviated):
// hash_0g = {ele@d3, dog@h7, p1_turn} — the initial 1g position
// hash_1s = {ele@d4, dog@h7, p2_turn} — after gold moves ele up
// hash_2g = {ele@d4, dog@h6, p1_turn} — after silver moves dog down
// hash_2s = {ele@d3, dog@h6, p2_turn} — after gold moves ele down
//
// One full cycle (4 turns) returns to the initial position.
// After 1 cycle hash_0g is in second_occurance_list; third = false.
// After 2 cycles hash_0g is appended a 3rd time → third = true (p2 caused it).
//
// To test p1 causing the 3rd rep we use the same board but with silver to
// move first (1s). There the "p2-closing" hash is hash_0s = {ele@d3,
// dog@h7, p2_turn}. After 2 cycles hash_0s is in second_occurance_list,
// and when gold executes the third d4s+p it appends hash_0s a 3rd time
// → third = true (p1 caused it).
// -----------------------------------------------------------------------
fn third_rep_1g() -> GameState {
"
1g
+-----------------+
8| r |
7| d |
6| x x |
5| m |
4| |
3| x E x |
2| |
1| R |
+-----------------+
a b c d e f g h"
.parse()
.unwrap()
}
fn third_rep_1s() -> GameState {
"
1s
+-----------------+
8| r |
7| d |
6| x x |
5| m |
4| |
3| x E x |
2| |
1| R |
+-----------------+
a b c d e f g h"
.parse()
.unwrap()
}
// One full cycle from a 1g starting board (4 turns, returns to a 1g-equivalent).
fn gold_first_cycle(gs: GameState) -> GameState {
let gs = take_actions!(gs => d3n, p); // gold: ele d3→d4
let gs = take_actions!(gs => h7s, p); // silver: dog h7→h6
let gs = take_actions!(gs => d4s, p); // gold: ele d4→d3
take_actions!(gs => h6n, p) // silver: dog h6→h7
}
// One full cycle from a 1s starting board (4 turns, returns to a 1s-equivalent).
fn silver_first_cycle(gs: GameState) -> GameState {
let gs = take_actions!(gs => h7s, p); // silver: dog h7→h6
let gs = take_actions!(gs => d3n, p); // gold: ele d3→d4
let gs = take_actions!(gs => h6n, p); // silver: dog h6→h7
take_actions!(gs => d4s, p) // gold: ele d4→d3
}
#[test]
fn test_is_terminal_not_terminal_after_first_repetition() {
// After 1 full cycle the initial position has appeared twice
// (once on construction, once after the cycle). Not yet terminal.
let gs = third_rep_1g();
let gs = gold_first_cycle(gs);
assert_eq!(gs.is_terminal(), None);
}
#[test]
fn test_is_terminal_not_terminal_one_step_before_third_repetition() {
// After 1 cycle + 3 more turns (gold up, silver down, gold down) we
// are one silver move away from the 3rd repetition. Still not terminal.
let gs = third_rep_1g();
let gs = gold_first_cycle(gs);
let gs = take_actions!(gs => d3n, p); // gold up (2nd occurrence of hash_1s into second)
let gs = take_actions!(gs => h7s, p); // silver down
let gs = take_actions!(gs => d4s, p); // gold down — hash_2s now in second; no third yet
assert_eq!(gs.is_terminal(), None);
}
#[test]
fn test_is_terminal_third_repetition_p2_causes_p1_wins() {
// Silver (p2) plays h6n+p, returning the board to the initial position
// for the third time. Silver caused the repetition → silver loses (p1 wins).
let gs = third_rep_1g();
let gs = gold_first_cycle(gs); // 2nd occurrence of hash_0g → second_occur
let gs = take_actions!(gs => d3n, p); // gold up
let gs = take_actions!(gs => h7s, p); // silver down
let gs = take_actions!(gs => d4s, p); // gold down
assert_eq!(gs.is_terminal(), None); // not yet — third not triggered
let gs = take_actions!(gs => h6n, p); // silver up → 3rd occurrence of hash_0g
assert_eq!(gs.is_terminal(), Some(Value([1.0, 0.0]))); // p1 wins
}
#[test]
fn test_is_terminal_third_repetition_p1_causes_p2_wins() {
// Starting with silver to move first. After 2 full cycles the initial
// p2-turn position {ele@d3, dog@h7, p2_turn} is in second_occurance_list.
// Gold (p1) then plays d4s+p, appending that hash a third time.
// Gold caused the repetition → gold loses (p2 wins).
let gs = third_rep_1s();
let gs = silver_first_cycle(gs); // 2nd occurrence of hash_0s → second_occur
let gs = take_actions!(gs => h7s, p); // silver down
let gs = take_actions!(gs => d3n, p); // gold up
let gs = take_actions!(gs => h6n, p); // silver up
assert_eq!(gs.is_terminal(), None); // not yet
let gs = take_actions!(gs => d4s, p); // gold down → 3rd occurrence of hash_0s
assert_eq!(gs.is_terminal(), Some(Value([0.0, 1.0]))); // p2 wins
}
#[test]
fn test_is_terminal_mid_turn_not_triggered_during_third_repetition_turn() {
// Even after third_repetition_occurred is set, a mid-turn state (step > 0)
// must not return a terminal from the repetition path.
let gs = third_rep_1g();
let gs = gold_first_cycle(gs);
let gs = take_actions!(gs => d3n, p);
let gs = take_actions!(gs => h7s, p);
let gs = take_actions!(gs => d4s, p);
let gs = take_actions!(gs => h6n, p); // 3rd rep → terminal at step 0
assert_eq!(gs.is_terminal(), Some(Value([1.0, 0.0])));
// Mid-turn from the (already terminal) state: step > 0 suppresses rep check.
let mid_turn = take_actions!(gs => d3n);
assert_eq!(mid_turn.is_terminal(), None);
}
}