use open_ttt_lib::{ai, game};
#[test]
#[ignore]
fn unbeatable_opponent_should_never_lose() {
const NUM_GAMES: usize = 100;
let mut game = game::Game::new();
let random_ai = ai::Opponent::new(ai::Difficulty::None);
let unbeatable_ai = ai::Opponent::new(ai::Difficulty::Unbeatable);
for _ in 0..NUM_GAMES {
let mut move_log = Vec::new();
loop {
match game.state() {
game::State::PlayerXMove => {
let position = random_ai.get_move(&game).unwrap();
move_log.push(format!(" Random AI as X: {:?}", position));
game.do_move(position).unwrap();
}
game::State::PlayerOMove => {
let position = unbeatable_ai.get_move(&game).unwrap();
move_log.push(format!(" Unbeatable AI as O: {:?}", position));
game.do_move(position).unwrap();
}
game::State::PlayerXWin(_) => {
panic!(
"\nThe random AI has won over the unbeatable AI. \
\n\nList of moves: \n{}\n \
\nThe final game board: \n{}\n",
move_log.join("\n"),
game.board()
);
}
game::State::PlayerOWin(_) => {
break;
}
game::State::CatsGame => {
break;
}
};
}
game.start_next_game();
}
}
#[test]
#[ignore]
fn easy_medium_hard_difficulties_should_increasingly_win_vs_none_difficulty() {
let easy_scores = battle(ai::Difficulty::Easy, ai::Difficulty::None);
let medium_scores = battle(ai::Difficulty::Medium, ai::Difficulty::None);
let hard_scores = battle(ai::Difficulty::Hard, ai::Difficulty::None);
assert!(
easy_scores.wins < medium_scores.wins,
"The Easy difficulty with {} wins has unexpectedly won more than the \
Medium difficulty with {} wins.",
easy_scores.wins,
medium_scores.wins
);
assert!(
medium_scores.wins < hard_scores.wins,
"The Medium difficulty with {} wins has unexpectedly won more than the \
Hard difficulty with {} wins.",
medium_scores.wins,
hard_scores.wins
);
}
#[test]
#[ignore]
fn easy_medium_hard_difficulties_should_increasingly_tie_vs_unbeatable_difficulty() {
let easy_scores = battle(ai::Difficulty::Easy, ai::Difficulty::Unbeatable);
let medium_scores = battle(ai::Difficulty::Medium, ai::Difficulty::Unbeatable);
let hard_scores = battle(ai::Difficulty::Hard, ai::Difficulty::Unbeatable);
assert!(
easy_scores.cats_games < medium_scores.cats_games,
"The Easy difficulty with {} cat's games has unexpectedly tied more \
than the Medium difficulty with {} cat's games.",
easy_scores.cats_games,
medium_scores.cats_games
);
assert!(
medium_scores.cats_games < hard_scores.cats_games,
"The Medium difficulty with {} cat's games has unexpectedly tied more \
than the Hard difficulty with {} cat's games.",
medium_scores.cats_games,
hard_scores.cats_games
);
assert_eq!(
easy_scores.wins, 0,
"The Easy opponent with {} wins has unexpectedly won over the \
unbeatable opponent.",
easy_scores.wins
);
assert_eq!(
medium_scores.wins, 0,
"The Medium opponent with {} wins has unexpectedly won over the \
Unbeatable opponent.",
medium_scores.wins
);
assert_eq!(
hard_scores.wins, 0,
"The Hard opponent with {} wins has unexpectedly won over the \
Unbeatable opponent.",
hard_scores.wins
);
}
fn battle(difficulty: ai::Difficulty, reference_difficulty: ai::Difficulty) -> BattleScores {
const NUM_GAMES: i32 = 100;
let mut game = game::Game::new();
let player_x = ai::Opponent::new(difficulty);
let player_o = ai::Opponent::new(reference_difficulty);
let mut scores = BattleScores::new();
while scores.total_games() < NUM_GAMES {
match game.state() {
game::State::PlayerXMove => {
let position = player_x.get_move(&game).unwrap();
game.do_move(position).unwrap();
}
game::State::PlayerOMove => {
let position = player_o.get_move(&game).unwrap();
game.do_move(position).unwrap();
}
game::State::PlayerXWin(_) => {
scores.wins += 1;
game.start_next_game();
}
game::State::PlayerOWin(_) => {
scores.losses += 1;
game.start_next_game();
}
game::State::CatsGame => {
scores.cats_games += 1;
game.start_next_game();
}
};
}
scores
}
struct BattleScores {
wins: i32,
losses: i32,
cats_games: i32,
}
impl BattleScores {
fn new() -> Self {
BattleScores {
wins: 0,
losses: 0,
cats_games: 0,
}
}
fn total_games(&self) -> i32 {
self.wins + self.losses + self.cats_games
}
}