diff --git a/benches/reversals.rs b/benches/reversals.rs index 4739682..30c9bd2 100644 --- a/benches/reversals.rs +++ b/benches/reversals.rs @@ -1,24 +1,46 @@ use std::hint::black_box; -use criterion::{Criterion, criterion_group, criterion_main}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use othello::{ board::{Board, squares::*}, game::Game, }; -const PLAYS: [Board; 15] = [F5, F4, E3, F3, G4, G5, H4, H5, E6, D3, C4, C5, D6, C6, B5]; +const PLAYS: [(&str, [Board; 10]); 4] = [ + ("game1", [F5, F6, E6, F4, E3, D6, G4, D3, C3, H3]), + ("game2", [F5, D6, C3, D3, C4, F4, F6, F3, E6, E7]), + ("game3", [F5, D6, C4, D3, C3, F4, C5, B3, C2, E3]), + ("game4", [F5, F6, E6, F4, E3, C5, C4, D6, B6, D3]), +]; -fn bench_available_all_positions_sequential(c: &mut Criterion) { - c.bench_function("play_four_move_opening", |b| { - b.iter(|| { - let mut game = Game::default(); - for play in PLAYS { - black_box(game.play(play)); - } +fn bench_perfect_games(c: &mut Criterion) { + let mut group = c.benchmark_group("perfect games"); + for (id, play) in PLAYS { + group.bench_with_input(BenchmarkId::new("perfect game", id), &play, |b, p| { + b.iter(|| { + let mut game = Game::default(); + for m in p { + black_box(game.play(*m)); + } + }); }); - }); + } } -criterion_group!(benches, bench_available_all_positions_sequential,); +fn bench_perfect_games_safe_play(c: &mut Criterion) { + let mut group = c.benchmark_group("perfect games with safe play"); + for (id, play) in PLAYS { + group.bench_with_input(BenchmarkId::new("perfect game", id), &play, |b, p| { + b.iter(|| { + let mut game = Game::default(); + for m in p { + black_box(game.safe_play(*m).unwrap()); + } + }); + }); + } +} + +criterion_group!(benches, bench_perfect_games, bench_perfect_games_safe_play); criterion_main!(benches); diff --git a/src/board/mod.rs b/src/board/mod.rs index 630d9d5..92917c3 100644 --- a/src/board/mod.rs +++ b/src/board/mod.rs @@ -685,7 +685,7 @@ mod tests { } #[allow(dead_code)] - fn render_dbg(board: BitBoard) -> BitBoard { + pub fn render_dbg(board: BitBoard) -> BitBoard { println!("dbg:\n{}", board.render(View::RankAsc, vec![])); board } diff --git a/src/game.rs b/src/game.rs index 7f537a9..7d4e8a3 100644 --- a/src/game.rs +++ b/src/game.rs @@ -34,9 +34,12 @@ impl Game { /// Play a move but first verify that the move is legal. /// Automatically transitions state to next player. - pub fn safe_play(&mut self, player_move: Board) { - // TODO: validate that move is legal + pub fn safe_play(&mut self, player_move: Board) -> Result<(), &'static str> { + if self.board.available(self.current_team) & player_move != player_move { + return Err("Illegal move"); + } self.play(player_move); + Ok(()) } pub fn board(&self) -> BitBoard { @@ -47,7 +50,7 @@ impl Game { #[cfg(test)] mod tests { use super::*; - use crate::board::squares::*; + use crate::board::{squares::*, view::View}; #[test] fn play_switches_team() { let mut game = Game::default(); @@ -64,4 +67,25 @@ mod tests { assert_ne!(og_b, game.board().boards[0]); assert_ne!(og_w, game.board().boards[1]); } + + pub fn render_dbg(board: BitBoard) -> BitBoard { + println!("dbg:\n{}", board.render(View::RankAsc, vec![])); + board + } + + #[test] + fn ten_move_opening() { + let plays: Vec> = vec![ + vec![F5, F6, E6, F4, E3, D6, G4, D3, C3, H3], + vec![F5, D6, C3, D3, C4, F4, F6, F3, E6, E7], + vec![F5, D6, C4, D3, C3, F4, C5, B3, C2, E3], + vec![F5, F6, E6, F4, E3, C5, C4, D6, B6, D3], + ]; + for play in plays { + let mut game = Game::default(); + for m in play { + game.safe_play(m).expect("Move should be valid"); + } + } + } }