diff --git a/src/game.rs b/src/game.rs index 86475e7..44c531b 100644 --- a/src/game.rs +++ b/src/game.rs @@ -1,6 +1,9 @@ -use std::sync::{ - atomic::{AtomicBool, AtomicU32, Ordering::SeqCst}, - Arc, Mutex, MutexGuard, +use std::{ + sync::{ + atomic::{AtomicBool, AtomicU32, Ordering::SeqCst}, + Arc, Mutex, MutexGuard, + }, + time::Duration, }; use anyhow::Result; @@ -46,16 +49,23 @@ pub enum MillState { None, } +pub enum LogSeverity { + Basic, + Debug, +} + +const LOG_SEVERITY: LogSeverity = LogSeverity::Basic; + impl PlayerColor { pub fn swap(&mut self) { *self = match *self { PlayerColor::White => { - MillGame::log("swapped to black player"); + MillGame::log("swapped to black player", LogSeverity::Debug); PlayerColor::Black } PlayerColor::Black => { - MillGame::log("swapped to white player"); + MillGame::log("swapped to white player", LogSeverity::Debug); PlayerColor::White } @@ -71,6 +81,9 @@ impl PlayerColor { } pub struct MillGame { + engine: Arc, + last_turn_timing: Mutex, + board: Arc>, white_stones: Mutex<[Stone; 9]>, black_stones: Mutex<[Stone; 9]>, @@ -100,6 +113,7 @@ pub struct MillGame { impl MillGame { pub const OFFSET_TO_BOARD: f32 = 0.02; + pub const TURN_WAIT_TIME: Duration = Duration::from_millis(500); pub fn new(engine: Arc) -> Result> { let mut scene = SceneHandle::new(&engine)?; @@ -191,6 +205,9 @@ impl MillGame { gui.enable()?; let me = Arc::new(Self { + engine: engine.clone(), + last_turn_timing: Mutex::default(), + board: Arc::new(Mutex::new(board)), white_stones: Mutex::new(white_stones.unwrap()), black_stones: Mutex::new(black_stones.unwrap()), @@ -228,12 +245,13 @@ impl MillGame { } if let Some(me) = weak_self.upgrade() { - Self::log("start game"); + Self::log("start game", LogSeverity::Debug); *me.state.lock().unwrap() = GameState::Placing; *me.current_player.lock().unwrap() = PlayerColor::Black; - me.turn_finished.store(true, SeqCst); + me.finish_turn(); + *me.last_turn_timing.lock().unwrap() = me.engine.time(); } Ok(()) @@ -244,11 +262,12 @@ impl MillGame { } pub fn finish_turn(&self) { - Self::log(&format!( - "{:?} finished turn", - *self.current_player.lock().unwrap() - )); + Self::log( + &format!("{:?} finished turn", *self.current_player.lock().unwrap()), + LogSeverity::Debug, + ); + *self.last_turn_timing.lock().unwrap() = self.engine.time(); self.turn_finished.store(true, SeqCst); } @@ -256,7 +275,10 @@ impl MillGame { { let mut state = self.state.lock().unwrap(); - Self::log(&format!(" ===== NEW TURN ({:?}) =====", state)); + Self::log( + &format!(" ===== NEW TURN ({:?}) =====", state), + LogSeverity::Basic, + ); match *state { GameState::Placing => { @@ -273,7 +295,7 @@ impl MillGame { .iter() .any(|stones| stones.state == StoneState::ReadyToBePlaced) { - Self::log("change state to Main"); + Self::log("change state to Main", LogSeverity::Debug); *state = GameState::Main; } @@ -293,10 +315,10 @@ impl MillGame { .iter() .any(|stones| stones.state == StoneState::ReadyToBePlaced) { - Self::log("change state to Main"); + Self::log("change state to Main", LogSeverity::Debug); *state = GameState::Main; } else { - Self::log("change state to Placing"); + Self::log("change state to Placing", LogSeverity::Debug); *state = GameState::Placing; } @@ -311,7 +333,7 @@ impl MillGame { let player = *self.current_player.lock().unwrap(); - Self::log(&format!("current player {:?}", player)); + Self::log(&format!("current player {:?}", player), LogSeverity::Debug); match player { PlayerColor::White => { @@ -327,7 +349,7 @@ impl MillGame { } if player == self.simple_ai.player_color { - Self::log("current player is AI"); + Self::log("current player is AI", LogSeverity::Debug); if !self.simple_ai.step( &mut self.stones(self.simple_ai.player_color), @@ -339,7 +361,7 @@ impl MillGame { } } - Self::log("leave next_game_step"); + Self::log("leave next_game_step", LogSeverity::Debug); Ok(()) } @@ -411,18 +433,24 @@ impl MillGame { scene: &mut Scene, state: &mut GameState, ) -> Result { - Self::log("place_stone"); + Self::log("place_stone", LogSeverity::Debug); let slot = &board[x][y][z]; match player_color { PlayerColor::White => { - Self::log(&format!("place white stone")); + Self::log( + &format!(" ==> white stone placed at {:?}", (x, y, z)), + LogSeverity::Basic, + ); slot.set_state(BoardSlotState::White(stone.stone)); } PlayerColor::Black => { - Self::log(&format!("place black stone")); + Self::log( + &format!(" ==> black stone placed at {:?}", (x, y, z)), + LogSeverity::Basic, + ); slot.set_state(BoardSlotState::Black(stone.stone)); } @@ -441,7 +469,10 @@ impl MillGame { *state = GameState::Removing; } - Self::log(&format!("return place stone with {:?}", millstate)); + Self::log( + &format!("return place stone with {:?}", millstate), + LogSeverity::Debug, + ); Ok(millstate) } @@ -453,17 +484,16 @@ impl MillGame { board: &mut [[[BoardSlot; 3]; 3]; 3], state: &mut GameState, ) -> Result { - Self::log(&format!( - "move stone ({:?}) to ({:?})", - (x, y, z), - (tx, ty, tz) - )); + Self::log( + &format!("move stone ({:?}) to ({:?})", (x, y, z), (tx, ty, tz)), + LogSeverity::Debug, + ); let slot = &board[x][y][z]; let neighbour = &board[tx][ty][tz]; if neighbour.state() != BoardSlotState::Empty { - Self::log("neighbour not empty"); + Self::log("neighbour not empty", LogSeverity::Debug); return Ok(MillState::None); } @@ -472,8 +502,30 @@ impl MillGame { scene .entity_mut(match neighbour.state() { - BoardSlotState::Black(e) => e, - BoardSlotState::White(e) => e, + BoardSlotState::Black(e) => { + Self::log( + &format!( + " ==> black stone moved from {:?} to {:?}", + (x, y, z), + (tx, ty, tz) + ), + LogSeverity::Basic, + ); + + e + } + BoardSlotState::White(e) => { + Self::log( + &format!( + " ==> white stone moved from {:?} to {:?}", + (x, y, z), + (tx, ty, tz) + ), + LogSeverity::Basic, + ); + + e + } _ => unreachable!(), })? @@ -483,26 +535,41 @@ impl MillGame { let millstate = Self::check_for_mill(&board[tx][ty][tz], board); if millstate != MillState::None { - Self::log("mill found!"); + Self::log("mill found!", LogSeverity::Debug); *state = GameState::Removing; } - Self::log(&format!("return move stone with {:?}", millstate)); + Self::log( + &format!("return move stone with {:?}", millstate), + LogSeverity::Debug, + ); Ok(millstate) } pub fn remove_stone(stone: &mut Stone, slot: &mut BoardSlot, scene: &mut Scene) -> Result<()> { - Self::log("remove_stone"); + Self::log("remove_stone", LogSeverity::Debug); let entity = match slot.state() { BoardSlotState::Black(e) => { - Self::log(&format!("\tremove black stone")); + Self::log( + &format!( + " ==> white player removes black stone at {:?}", + (slot.x, slot.y, slot.z) + ), + LogSeverity::Basic, + ); e } BoardSlotState::White(e) => { - Self::log(&format!("\tremove white stone")); + Self::log( + &format!( + " ==> black player removes white stone at {:?}", + (slot.x, slot.y, slot.z) + ), + LogSeverity::Basic, + ); e } @@ -532,7 +599,7 @@ impl MillGame { } pub fn check_for_mill(slot: &BoardSlot, board: &[[[BoardSlot; 3]; 3]; 3]) -> MillState { - Self::log("check for mill"); + Self::log("check for mill", LogSeverity::Debug); if !(slot.x == 0 && slot.y == 0) && !(slot.x == 2 && slot.y == 0) @@ -546,7 +613,7 @@ impl MillGame { ); if state != MillState::None { - Self::log(&format!("mill found {:?}", state)); + Self::log(&format!("mill found {:?}", state), LogSeverity::Debug); return state; } @@ -559,7 +626,7 @@ impl MillGame { ); if state != MillState::None { - Self::log(&format!("mill found {:?}", state)); + Self::log(&format!("mill found {:?}", state), LogSeverity::Debug); return state; } @@ -571,7 +638,7 @@ impl MillGame { ); if state != MillState::None { - Self::log(&format!("mill found {:?}", state)); + Self::log(&format!("mill found {:?}", state), LogSeverity::Debug); return state; } @@ -588,7 +655,7 @@ impl MillGame { &mut Scene, ) -> Result<()>, { - Self::log("check_mouse_click"); + Self::log("check_mouse_click", LogSeverity::Debug); self.scene.lock().unwrap().on_scene(|scene| { if let Some(world_space) = @@ -597,7 +664,7 @@ impl MillGame { let mut board = self.board.lock().unwrap(); if let Some(slot) = board.close_to_marker(world_space.truncate()) { - Self::log("click is close to marker"); + Self::log("click is close to marker", LogSeverity::Debug); f(self, (slot.x, slot.y, slot.z), board.slots(), scene)?; } @@ -622,8 +689,13 @@ impl MillGame { || (z as i32 - tz as i32).abs() == 1 } - pub fn log(s: &str) { - println!("{}", s); + pub fn log(s: &str, log_severity: LogSeverity) { + match (LOG_SEVERITY, log_severity) { + (LogSeverity::Basic, LogSeverity::Basic) => println!("{}", s), + (LogSeverity::Basic, LogSeverity::Debug) => (), + (LogSeverity::Debug, LogSeverity::Basic) => println!("{}", s), + (LogSeverity::Debug, LogSeverity::Debug) => println!("{}", s), + } } } @@ -634,8 +706,11 @@ impl EngineObject for MillGame { fn update(&self) -> Result<()> { if self.turn_finished.load(SeqCst) { - self.turn_finished.store(false, SeqCst); - self.next_game_step()?; + if (*self.last_turn_timing.lock().unwrap() + Self::TURN_WAIT_TIME) < self.engine.time() + { + self.turn_finished.store(false, SeqCst); + self.next_game_step()?; + } } Ok(()) @@ -657,17 +732,17 @@ impl EngineObject for MillGame { match button { MouseButton::Left => { let mut state = self.state.lock().unwrap(); - Self::log(&format!("User click at state {:?}", state)); + Self::log( + &format!("User click at state {:?}", state), + LogSeverity::Debug, + ); match *state { GameState::Placing => { - let mut placed = false; - let mut mill_found = false; - self.check_mouse_click(|me, (x, y, z), board, scene| { - let current_player = *self.current_player.lock().unwrap(); + let current_player = *me.current_player.lock().unwrap(); - if let Some(placable) = self + if let Some(placable) = me .stones(current_player) .iter_mut() .find(|stone| stone.state == StoneState::ReadyToBePlaced) @@ -680,47 +755,45 @@ impl EngineObject for MillGame { current_player, scene, &mut *state, - )? != MillState::None + )? == MillState::None { - mill_found = true; + me.finish_turn(); } - - placed = true; } else { - Self::log(&format!( - "slot ({:?}), not empty ({:?})", - (x, y, z), - board[x][y][z].state() - )); + Self::log( + &format!( + "slot ({:?}), not empty ({:?})", + (x, y, z), + board[x][y][z].state() + ), + LogSeverity::Debug, + ); } } Ok(()) })?; - - if placed && !mill_found { - self.finish_turn(); - } } GameState::Removing => { - let mut removed = false; - self.check_mouse_click(|me, (x, y, z), board, scene| { let slot = &mut board[x][y][z]; - Self::log(&format!( - "state {:?} - player {:?}", - slot.state(), - *self.current_player.lock().unwrap() - )); + Self::log( + &format!( + "state {:?} - player {:?}", + slot.state(), + *me.current_player.lock().unwrap() + ), + LogSeverity::Debug, + ); - if match (slot.state(), *self.current_player.lock().unwrap()) { + if match (slot.state(), *me.current_player.lock().unwrap()) { (BoardSlotState::Black(_), PlayerColor::White) => true, (BoardSlotState::White(_), PlayerColor::Black) => true, _ => false, } { - let mut stones = self - .stones(self.current_player.lock().unwrap().other()); + let mut stones = + self.stones(me.current_player.lock().unwrap().other()); let stone = stones .iter_mut() @@ -736,15 +809,11 @@ impl EngineObject for MillGame { Self::remove_stone(stone, slot, scene)?; - removed = true; + me.finish_turn(); } Ok(()) })?; - - if removed { - self.finish_turn(); - } } GameState::Main => { self.check_mouse_click(|me, (tx, ty, tz), board, scene| { diff --git a/src/simple_ai.rs b/src/simple_ai.rs index 3edbffc..38c0717 100644 --- a/src/simple_ai.rs +++ b/src/simple_ai.rs @@ -1,6 +1,6 @@ use crate::{ board::{Board, BoardSlot, BoardSlotState}, - game::{GameState, MillGame, MillState, PlayerColor, Stone, StoneState}, + game::{GameState, LogSeverity, MillGame, MillState, PlayerColor, Stone, StoneState}, }; use anyhow::Result; @@ -22,7 +22,7 @@ impl SimpleAI { scene: &mut SceneHandle, state: &mut GameState, ) -> Result { - MillGame::log(&format!("AI at state {:?}", *state)); + MillGame::log(&format!("AI at state {:?}", *state), LogSeverity::Debug); let mut found_a_mill = false; @@ -151,7 +151,7 @@ impl SimpleAI { GameState::Waiting => unreachable!(), } - MillGame::log("finish AI"); + MillGame::log("finish AI", LogSeverity::Debug); Ok(found_a_mill) }