mill_game/src/game.rs

1120 lines
37 KiB
Rust
Raw Normal View History

2023-05-11 15:00:42 +00:00
use std::{
2023-05-12 12:53:01 +00:00
fs::OpenOptions,
io::Write,
2023-05-11 15:00:42 +00:00
sync::{
atomic::{AtomicBool, AtomicU32, Ordering::SeqCst},
Arc, Mutex, MutexGuard,
},
time::Duration,
2023-05-09 14:39:53 +00:00
};
2023-05-08 18:56:50 +00:00
use anyhow::Result;
2023-05-09 16:58:07 +00:00
use assetpath::AssetPath;
2023-05-12 12:53:01 +00:00
use engine::prelude::{
cgmath::{vec3, Vector2},
*,
};
2023-05-08 18:56:50 +00:00
2023-05-09 16:58:07 +00:00
use crate::{
board::{Board, BoardSlot, BoardSlotState},
objects::Objects,
simple_ai::SimpleAI,
};
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
pub enum StoneState {
ReadyToBePlaced,
Placed,
Dead,
}
pub struct Stone {
pub stone: Entity,
pub state: StoneState,
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
pub enum GameState {
2023-05-11 12:00:24 +00:00
Waiting,
2023-05-09 16:58:07 +00:00
Placing,
2023-05-10 05:47:10 +00:00
Removing,
2023-05-09 16:58:07 +00:00
Main,
2023-05-16 08:00:24 +00:00
Won(PlayerColor),
2023-05-09 16:58:07 +00:00
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
pub enum PlayerColor {
White,
Black,
}
2023-05-10 05:47:10 +00:00
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
pub enum MillState {
White,
Black,
None,
}
2023-05-12 12:53:01 +00:00
#[derive(Debug, Clone, Copy)]
2023-05-11 15:00:42 +00:00
pub enum LogSeverity {
Basic,
Debug,
}
2023-05-12 12:53:01 +00:00
const LOG_SEVERITY: LogSeverity = LogSeverity::Basic;
const LOG_FILE: &str = "millgame.log";
2023-05-11 15:00:42 +00:00
2023-05-09 16:58:07 +00:00
impl PlayerColor {
pub fn swap(&mut self) {
*self = match *self {
2023-05-10 05:47:10 +00:00
PlayerColor::White => {
2023-05-11 15:00:42 +00:00
MillGame::log("swapped to black player", LogSeverity::Debug);
2023-05-10 05:47:10 +00:00
PlayerColor::Black
}
PlayerColor::Black => {
2023-05-11 15:00:42 +00:00
MillGame::log("swapped to white player", LogSeverity::Debug);
2023-05-10 05:47:10 +00:00
PlayerColor::White
}
2023-05-09 16:58:07 +00:00
}
}
2023-05-11 12:00:24 +00:00
pub fn other(&self) -> Self {
match self {
PlayerColor::White => PlayerColor::Black,
PlayerColor::Black => PlayerColor::White,
}
}
2023-05-09 16:58:07 +00:00
}
2023-05-08 18:56:50 +00:00
2023-05-12 12:53:01 +00:00
#[derive(Default, Debug)]
struct TurnState {
white_infos: Vec<(StoneState, Entity, Option<Vector2<f32>>)>,
black_infos: Vec<(StoneState, Entity, Option<Vector2<f32>>)>,
board_infos: Vec<((usize, usize, usize), Vector2<f32>, BoardSlotState)>,
}
impl TurnState {
pub fn new(game: &MillGame) -> Result<Self> {
let mut white_infos = Vec::new();
let mut black_infos = Vec::new();
game.scene.lock().unwrap().on_scene(|scene| {
for white_stone in game.white_stones.lock().unwrap().iter() {
let pos = match white_stone.state {
StoneState::ReadyToBePlaced => None,
StoneState::Placed => Some(
scene
.entity(white_stone.stone)?
.get_component::<Location>()?
.position()
.truncate(),
),
StoneState::Dead => None,
};
white_infos.push((white_stone.state, white_stone.stone, pos));
}
for black_stone in game.black_stones.lock().unwrap().iter() {
let pos = match black_stone.state {
StoneState::ReadyToBePlaced => None,
StoneState::Placed => Some(
scene
.entity(black_stone.stone)?
.get_component::<Location>()?
.position()
.truncate(),
),
StoneState::Dead => None,
};
black_infos.push((black_stone.state, black_stone.stone, pos));
}
Ok(())
})?;
let board_infos: Vec<((usize, usize, usize), Vector2<f32>, BoardSlotState)> = game
.board
.lock()
.unwrap()
.slots()
.iter()
.flatten()
.flatten()
.map(|slot| ((slot.x, slot.y, slot.z), slot.position, slot.state()))
.collect();
Ok(Self {
white_infos,
black_infos,
board_infos,
})
}
pub fn diff(&self, other: &Self) -> (Self, Self) {
let mut my_diffs = Self::default();
let mut other_diffs = Self::default();
for my_info in self.white_infos.iter() {
let other_info = other.white_infos.iter().find(|o| my_info.1 == o.1).unwrap();
if my_info.0 != other_info.0 || my_info.2 != other_info.2 {
my_diffs.white_infos.push(*my_info);
other_diffs.white_infos.push(*other_info);
}
}
for my_info in self.black_infos.iter() {
let other_info = other.black_infos.iter().find(|o| my_info.1 == o.1).unwrap();
if my_info.0 != other_info.0 || my_info.2 != other_info.2 {
my_diffs.black_infos.push(*my_info);
other_diffs.black_infos.push(*other_info);
}
}
for my_slot in self.board_infos.iter() {
let other_slot = other.board_infos.iter().find(|o| my_slot.0 == o.0).unwrap();
if my_slot.2 != other_slot.2 {
my_diffs.board_infos.push(*my_slot);
other_diffs.board_infos.push(*other_slot);
}
}
(my_diffs, other_diffs)
}
pub fn log(&self, severity: LogSeverity) {
if !self.white_infos.is_empty() {
MillGame::log(" == WHITE STONES ==", severity);
for white_info in self.white_infos.iter() {
MillGame::log(&format!("{:?}", white_info), severity);
}
}
if !self.black_infos.is_empty() {
MillGame::log(" == BLACK STONES ==", severity);
for black_info in self.black_infos.iter() {
MillGame::log(&format!("{:?}", black_info), severity);
}
}
MillGame::log(" == BOARD ==", severity);
for board_info in self.board_infos.iter() {
MillGame::log(&format!("{:?}", board_info), severity);
}
}
pub fn is_empty(&self) -> bool {
self.white_infos.is_empty() && self.black_infos.is_empty() && self.board_infos.is_empty()
}
}
2023-05-08 18:56:50 +00:00
pub struct MillGame {
2025-03-25 11:07:45 +00:00
last_turn_timing: Duration,
2023-05-11 15:00:42 +00:00
2025-03-25 11:07:45 +00:00
board: Board,
white_stones: [Stone; 9],
black_stones: [Stone; 9],
2023-05-09 16:58:07 +00:00
2025-03-25 11:07:45 +00:00
state: GameState,
current_player: PlayerColor,
2023-05-09 14:39:53 +00:00
2025-03-25 11:07:45 +00:00
mouse_x: u32,
mouse_y: u32,
2023-05-09 14:39:53 +00:00
2025-03-25 11:07:45 +00:00
turn_finished: bool,
turn_states: Vec<TurnState>,
2023-05-09 16:58:07 +00:00
2025-03-25 11:07:45 +00:00
selected_field: Option<(usize, usize, usize)>,
2023-05-09 16:58:07 +00:00
white_player_label: Arc<Label>,
black_player_label: Arc<Label>,
2023-05-12 12:53:01 +00:00
_start_button: Arc<Button>,
_grid: Arc<Grid>,
2023-05-09 16:58:07 +00:00
2023-05-12 12:53:01 +00:00
_gui: Arc<GuiBuilder>,
2023-05-08 18:56:50 +00:00
}
impl MillGame {
2023-05-09 16:58:07 +00:00
pub const OFFSET_TO_BOARD: f32 = 0.02;
2023-05-11 15:00:42 +00:00
pub const TURN_WAIT_TIME: Duration = Duration::from_millis(500);
2023-05-09 14:39:53 +00:00
2025-03-25 11:07:45 +00:00
pub fn new(world: &mut World) -> Result<()> {
let board = Board::new(world)?;
// add light
let mut sun_light = Light::directional_light(world.resources.get::<Context>().device());
sun_light.set_direction(vec3(10.0, -4.0, -20.0))?;
sun_light.set_color(vec3(1.0, 1.0, 1.0))?;
sun_light.set_position(vec3(0.0, 0.0, 100.0))?;
sun_light.set_power(10000000000.0)?;
let scene = world.resources.get_mut::<Scene>();
scene.add_light(sun_light)?;
let view = scene.view_mut();
view.camera_mut().set_center(board.center());
view.update_buffer()?;
let white_stones = Self::init_nine_stones(world, Color::White)?
.into_iter()
.enumerate()
.map(|(index, mut e)| {
let location = e.get_component_mut::<Location>()?;
location.set_position(board.white_start_slots()[index].extend(0.0));
Ok(Stone {
stone: world.add_entity(e)?,
state: StoneState::ReadyToBePlaced,
})
})
.collect::<Result<Vec<Stone>>>()?
.try_into()
.unwrap_or_else(|_: Vec<Stone>| unreachable!("create array from vec from an array"));
let black_stones = Self::init_nine_stones(world, Color::try_from("#2c2c2c")?)?
.into_iter()
.enumerate()
.map(|(index, mut e)| {
let location = e.get_component_mut::<Location>()?;
location.set_position(board.black_start_slots()[index].extend(0.0));
Ok(Stone {
stone: world.add_entity(e)?,
state: StoneState::ReadyToBePlaced,
})
})
.collect::<Result<Vec<Stone>>>()?
.try_into()
.unwrap_or_else(|_: Vec<Stone>| unreachable!("create array from vec from an array"));
2023-05-09 14:39:53 +00:00
2025-03-25 11:07:45 +00:00
let mut camera_control = TopDownCameraControl::new(&mut scene)?;
2023-05-09 16:58:07 +00:00
camera_control.set_zoom_levels(
(3..60).into_iter().rev().map(|z| z as f32).collect(),
5,
&mut scene,
)?;
2023-05-08 18:56:50 +00:00
2025-03-25 11:07:45 +00:00
world.resources.insert(camera_control);
2023-05-08 18:56:50 +00:00
2023-05-09 16:58:07 +00:00
let gui = GuiBuilder::new(
engine.gui_handler(),
&AssetPath::from((
engine.settings().resource_base_path.as_str(),
"mainmenu.xml",
)),
)?;
let grid: Arc<Grid> = gui.element("grid")?;
let white_player_label: Arc<Label> = gui.element("white_player_label")?;
let black_player_label: Arc<Label> = gui.element("black_player_label")?;
let start_button: Arc<Button> = gui.element("start")?;
2025-03-25 11:07:45 +00:00
gui.enable(world.resources.get_mut::<GuiHandler>())?;
2023-05-09 16:58:07 +00:00
2025-03-25 11:07:45 +00:00
start_button.set_callback({
let weak_grid = Arc::downgrade(&grid);
2023-05-11 15:00:42 +00:00
2025-03-25 11:07:45 +00:00
move |world| {
if let Some(grid) = weak_grid.upgrade() {
grid.detach(world, 1, 0)?;
}
2023-05-09 16:58:07 +00:00
2025-03-25 11:07:45 +00:00
let now = world.now();
let me = world.resources.get_mut::<Self>();
2023-05-09 14:39:53 +00:00
2025-03-25 11:07:45 +00:00
Self::log(" ===== START GAME =====", LogSeverity::Basic);
2023-05-09 14:39:53 +00:00
2025-03-25 11:07:45 +00:00
me.state = GameState::Placing;
me.current_player = PlayerColor::Black;
2023-05-09 16:58:07 +00:00
2025-03-25 11:07:45 +00:00
me.finish_turn();
me.last_turn_timing = now;
2023-05-10 08:22:46 +00:00
2025-03-25 11:07:45 +00:00
Ok(())
}
});
2023-05-11 12:00:24 +00:00
2025-03-25 11:07:45 +00:00
world.resources.insert(SimpleAI::new(PlayerColor::White));
2023-05-09 16:58:07 +00:00
2025-03-25 11:07:45 +00:00
let me = Self {
last_turn_timing: Duration::default(),
2023-05-09 16:58:07 +00:00
2025-03-25 11:07:45 +00:00
board,
white_stones,
black_stones,
2023-05-09 16:58:07 +00:00
2025-03-25 11:07:45 +00:00
state: GameState::Waiting,
current_player: PlayerColor::White,
2023-05-09 16:58:07 +00:00
2025-03-25 11:07:45 +00:00
mouse_x: 0,
mouse_y: 0,
2023-05-09 16:58:07 +00:00
2025-03-25 11:07:45 +00:00
turn_finished: false,
turn_states: TurnState::default(),
2023-05-10 05:47:10 +00:00
2025-03-25 11:07:45 +00:00
selected_field: None,
2023-05-09 16:58:07 +00:00
2025-03-25 11:07:45 +00:00
_grid: grid.clone(),
white_player_label,
black_player_label,
_start_button: start_button.clone(),
2023-05-09 16:58:07 +00:00
2025-03-25 11:07:45 +00:00
_gui: gui,
};
2023-05-09 16:58:07 +00:00
2025-03-25 11:07:45 +00:00
world.resources.insert(me);
Ok(())
2023-05-09 16:58:07 +00:00
}
2023-05-10 08:22:46 +00:00
pub fn finish_turn(&self) {
2023-05-11 15:00:42 +00:00
Self::log(
&format!("{:?} finished turn", *self.current_player.lock().unwrap()),
LogSeverity::Debug,
);
2023-05-10 08:22:46 +00:00
2023-05-11 15:00:42 +00:00
*self.last_turn_timing.lock().unwrap() = self.engine.time();
2023-05-10 08:22:46 +00:00
self.turn_finished.store(true, SeqCst);
2023-05-16 07:05:04 +00:00
*self.selected_field.lock().unwrap() = None;
2023-05-10 08:22:46 +00:00
}
2023-05-12 12:53:01 +00:00
fn print_game_state(&self, severity: LogSeverity, log_state: bool) -> Result<()> {
let current_turn_state = TurnState::new(self)?;
if log_state {
current_turn_state.log(severity);
}
let mut turn_states = self.turn_states.lock().unwrap();
if !turn_states.is_empty() {
let (new_diff, old_diff) = current_turn_state.diff(turn_states.last().unwrap());
2023-05-16 07:05:04 +00:00
// verity that last turn actually something happened
debug_assert!(!new_diff.is_empty());
debug_assert!(!old_diff.is_empty());
2023-05-12 12:53:01 +00:00
Self::log(" ===== OLD DIFF =====", severity);
old_diff.log(severity);
Self::log(" ===== NEW DIFF =====", severity);
new_diff.log(severity);
}
turn_states.push(current_turn_state);
Ok(())
}
2023-05-09 16:58:07 +00:00
fn next_game_step(&self) -> Result<()> {
{
let mut state = self.state.lock().unwrap();
2023-05-11 15:00:42 +00:00
Self::log(
2023-05-12 12:53:01 +00:00
&format!(
" =========================== NEW TURN ({:?}) ===========================",
state
),
2023-05-11 15:00:42 +00:00
LogSeverity::Basic,
);
2023-05-10 05:47:10 +00:00
2023-05-12 12:53:01 +00:00
self.print_game_state(LogSeverity::Basic, false)?;
2023-05-09 16:58:07 +00:00
match *state {
GameState::Placing => {
2023-05-16 08:00:24 +00:00
// check if all stones are placed
2023-05-09 16:58:07 +00:00
if !self
.black_stones
.lock()
.unwrap()
.iter()
.any(|stones| stones.state == StoneState::ReadyToBePlaced)
&& !self
.white_stones
.lock()
.unwrap()
.iter()
.any(|stones| stones.state == StoneState::ReadyToBePlaced)
{
2023-05-11 15:00:42 +00:00
Self::log("change state to Main", LogSeverity::Debug);
2023-05-09 16:58:07 +00:00
*state = GameState::Main;
}
2023-05-10 05:47:10 +00:00
2023-05-10 08:22:46 +00:00
self.current_player.lock().unwrap().swap();
2023-05-10 05:47:10 +00:00
}
GameState::Removing => {
2023-05-16 08:00:24 +00:00
// check if all stones are placed, then decide whether main or placing is next state
2023-05-10 05:47:10 +00:00
if !self
.black_stones
.lock()
.unwrap()
.iter()
.any(|stones| stones.state == StoneState::ReadyToBePlaced)
&& !self
.white_stones
.lock()
.unwrap()
.iter()
.any(|stones| stones.state == StoneState::ReadyToBePlaced)
{
2023-05-11 15:00:42 +00:00
Self::log("change state to Main", LogSeverity::Debug);
2023-05-10 05:47:10 +00:00
*state = GameState::Main;
} else {
2023-05-11 15:00:42 +00:00
Self::log("change state to Placing", LogSeverity::Debug);
2023-05-10 05:47:10 +00:00
*state = GameState::Placing;
}
self.current_player.lock().unwrap().swap();
}
GameState::Main => {
2023-05-16 08:00:24 +00:00
// check if each player has enough stones to play
if self
.white_stones
.lock()
.unwrap()
.iter()
.filter(|stone| stone.state != StoneState::Dead)
.collect::<Vec<&Stone>>()
.len()
<= 3
{
*state = GameState::Won(PlayerColor::Black);
return Ok(());
}
if self
.black_stones
.lock()
.unwrap()
.iter()
.filter(|stone| stone.state != StoneState::Dead)
.collect::<Vec<&Stone>>()
.len()
<= 3
{
*state = GameState::Won(PlayerColor::White);
return Ok(());
}
2023-05-10 08:22:46 +00:00
self.current_player.lock().unwrap().swap();
2023-05-09 16:58:07 +00:00
}
2023-05-11 12:00:24 +00:00
GameState::Waiting => unreachable!(),
2023-05-16 08:00:24 +00:00
GameState::Won(_) => (),
2023-05-09 16:58:07 +00:00
}
}
2023-05-10 05:47:10 +00:00
let player = *self.current_player.lock().unwrap();
2023-05-11 15:00:42 +00:00
Self::log(&format!("current player {:?}", player), LogSeverity::Debug);
2023-05-10 05:47:10 +00:00
match player {
PlayerColor::White => {
self.black_player_label
.set_background(Color::try_from("#9b9292")?)?;
self.white_player_label.set_background(Color::Yellow)?;
2023-05-09 16:58:07 +00:00
}
2023-05-10 05:47:10 +00:00
PlayerColor::Black => {
self.white_player_label
.set_background(Color::try_from("#9b9292")?)?;
self.black_player_label.set_background(Color::Yellow)?;
}
}
if player == self.simple_ai.player_color {
2023-05-11 15:00:42 +00:00
Self::log("current player is AI", LogSeverity::Debug);
2023-05-10 05:47:10 +00:00
2023-05-16 07:05:04 +00:00
self.simple_ai.step(
2023-05-10 05:47:10 +00:00
&mut self.stones(self.simple_ai.player_color),
self.board.lock().unwrap().slots(),
&mut *self.scene.lock().unwrap(),
2023-05-11 12:00:24 +00:00
&mut *self.state.lock().unwrap(),
2023-05-16 07:05:04 +00:00
)?;
self.finish_turn();
2023-05-09 16:58:07 +00:00
}
2023-05-11 15:00:42 +00:00
Self::log("leave next_game_step", LogSeverity::Debug);
2023-05-10 08:22:46 +00:00
2023-05-09 16:58:07 +00:00
Ok(())
2023-05-08 18:56:50 +00:00
}
2023-05-09 14:39:53 +00:00
2025-03-25 11:07:45 +00:00
fn stones(&self, player_color: PlayerColor) -> &[Stone; 9] {
2023-05-10 05:47:10 +00:00
match player_color {
2025-03-25 11:07:45 +00:00
PlayerColor::White => &self.white_stones,
PlayerColor::Black => &self.black_stones,
2023-05-10 05:47:10 +00:00
}
}
2025-03-25 11:07:45 +00:00
fn init_nine_stones(world: &mut World, color: Color) -> Result<[EntityObject; 9]> {
2023-05-09 14:39:53 +00:00
Ok((0..9)
2025-03-25 11:07:45 +00:00
.map(|_| Self::init_stone(world, color))
2023-05-09 14:39:53 +00:00
.collect::<Result<Vec<EntityObject>>>()?
.try_into()
.unwrap_or_else(|_: Vec<EntityObject>| {
unreachable!("create array from vec from an array")
}))
}
2025-03-25 11:07:45 +00:00
fn init_stone(world: &mut World, color: Color) -> Result<EntityObject> {
let mut marker = AssetHandler::create(world).empty_entity();
2023-05-09 14:39:53 +00:00
let draw = Draw::new(vec![{
let mut mesh = AssetMesh::new(
2025-03-25 11:07:45 +00:00
world.resources.get::<Context>().device(),
world
.resources
.get::<EngineSettings>()
.graphics_info()?
.render_type,
2023-05-09 14:39:53 +00:00
)?;
let vertex_buffer = Buffer::builder()
2023-05-11 12:00:24 +00:00
.set_data(&Objects::create_cylinder(1.1, 0.25, 30))
2023-05-09 14:39:53 +00:00
.set_usage(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT)
.set_memory_usage(MemoryUsage::CpuOnly)
2025-03-25 11:07:45 +00:00
.build(world.resources.get::<Context>().device().clone())?;
2023-05-09 14:39:53 +00:00
let a: [f32; 3] = color.into();
mesh.add_primitive(
vertex_buffer,
None,
None,
PrimitiveMaterial {
color: [a[0], a[1], a[2], 1.0],
metallic_factor: 0.2,
emissive_factor: [0.2, 0.2, 0.2],
roughness_factor: 0.8,
alpha_mode: AlphaMode::Opaque,
alpha_cut_off: 0.5,
},
true,
)?;
mesh
}]);
marker.insert_component(draw);
let location = Location::from_entity(&mut marker);
marker.insert_component(location);
Ok(marker)
}
2023-05-09 16:58:07 +00:00
pub fn place_stone(
stone: &mut Stone,
2023-05-11 12:00:24 +00:00
(x, y, z): (usize, usize, usize),
board: &mut [[[BoardSlot; 3]; 3]; 3],
2023-05-09 16:58:07 +00:00
player_color: PlayerColor,
scene: &mut Scene,
2023-05-11 12:00:24 +00:00
state: &mut GameState,
) -> Result<MillState> {
2023-05-11 15:00:42 +00:00
Self::log("place_stone", LogSeverity::Debug);
2023-05-10 05:47:10 +00:00
2023-05-11 12:00:24 +00:00
let slot = &board[x][y][z];
2023-05-09 16:58:07 +00:00
match player_color {
2023-05-10 05:47:10 +00:00
PlayerColor::White => {
2023-05-11 15:00:42 +00:00
Self::log(
2023-05-12 12:53:01 +00:00
&format!(
" ==> white stone ({:?}) placed at {:?}",
stone.stone,
(x, y, z)
),
2023-05-11 15:00:42 +00:00
LogSeverity::Basic,
);
2023-05-10 05:47:10 +00:00
2023-05-11 12:00:24 +00:00
slot.set_state(BoardSlotState::White(stone.stone));
2023-05-10 05:47:10 +00:00
}
PlayerColor::Black => {
2023-05-11 15:00:42 +00:00
Self::log(
2023-05-12 12:53:01 +00:00
&format!(
" ==> black stone ({:?}) placed at {:?}",
stone.stone,
(x, y, z)
),
2023-05-11 15:00:42 +00:00
LogSeverity::Basic,
);
2023-05-10 05:47:10 +00:00
2023-05-11 12:00:24 +00:00
slot.set_state(BoardSlotState::Black(stone.stone));
2023-05-10 05:47:10 +00:00
}
2023-05-09 16:58:07 +00:00
}
stone.state = StoneState::Placed;
let entity = scene.entity_mut(stone.stone)?;
let location = entity.get_component_mut::<Location>()?;
location.set_position(slot.position.extend(0.0));
2023-05-11 12:00:24 +00:00
let millstate = Self::check_for_mill(&board[x][y][z], board);
if millstate != MillState::None {
*state = GameState::Removing;
}
2023-05-11 15:00:42 +00:00
Self::log(
&format!("return place stone with {:?}", millstate),
LogSeverity::Debug,
);
2023-05-11 12:00:24 +00:00
Ok(millstate)
}
pub fn move_stone(
(x, y, z): (usize, usize, usize),
(tx, ty, tz): (usize, usize, usize),
scene: &mut Scene,
board: &mut [[[BoardSlot; 3]; 3]; 3],
state: &mut GameState,
2023-05-12 07:04:48 +00:00
) -> Result<Option<MillState>> {
2023-05-11 15:00:42 +00:00
Self::log(
&format!("move stone ({:?}) to ({:?})", (x, y, z), (tx, ty, tz)),
LogSeverity::Debug,
);
2023-05-11 12:00:24 +00:00
let slot = &board[x][y][z];
let neighbour = &board[tx][ty][tz];
2023-05-12 07:04:48 +00:00
if neighbour.state() != BoardSlotState::Empty
|| !Self::is_neighbour((x, y, z), (tx, ty, tz))
{
2023-05-11 15:00:42 +00:00
Self::log("neighbour not empty", LogSeverity::Debug);
2023-05-12 07:04:48 +00:00
return Ok(None);
2023-05-11 12:00:24 +00:00
}
neighbour.set_state(slot.state());
slot.set_state(BoardSlotState::Empty);
scene
.entity_mut(match neighbour.state() {
2023-05-11 15:00:42 +00:00
BoardSlotState::Black(e) => {
Self::log(
&format!(
2023-05-12 12:53:01 +00:00
" ==> black stone ({:?}) moved from {:?} to {:?}",
e,
2023-05-11 15:00:42 +00:00
(x, y, z),
(tx, ty, tz)
),
LogSeverity::Basic,
);
e
}
BoardSlotState::White(e) => {
Self::log(
&format!(
2023-05-12 12:53:01 +00:00
" ==> white stone ({:?}) moved from {:?} to {:?}",
e,
2023-05-11 15:00:42 +00:00
(x, y, z),
(tx, ty, tz)
),
LogSeverity::Basic,
);
e
}
2023-05-11 12:00:24 +00:00
_ => unreachable!(),
})?
.get_component_mut::<Location>()?
.set_position(neighbour.position.extend(0.0));
let millstate = Self::check_for_mill(&board[tx][ty][tz], board);
if millstate != MillState::None {
2023-05-11 15:00:42 +00:00
Self::log("mill found!", LogSeverity::Debug);
2023-05-11 12:00:24 +00:00
*state = GameState::Removing;
}
2023-05-11 15:00:42 +00:00
Self::log(
&format!("return move stone with {:?}", millstate),
LogSeverity::Debug,
);
2023-05-11 12:00:24 +00:00
2023-05-12 07:04:48 +00:00
Ok(Some(millstate))
2023-05-09 16:58:07 +00:00
}
2023-05-11 12:00:24 +00:00
pub fn remove_stone(stone: &mut Stone, slot: &mut BoardSlot, scene: &mut Scene) -> Result<()> {
2023-05-11 15:00:42 +00:00
Self::log("remove_stone", LogSeverity::Debug);
2023-05-10 05:47:10 +00:00
2023-05-11 12:00:24 +00:00
let entity = match slot.state() {
2023-05-10 05:47:10 +00:00
BoardSlotState::Black(e) => {
2023-05-11 15:00:42 +00:00
Self::log(
&format!(
2023-05-12 12:53:01 +00:00
" ==> white player removes black stone ({:?}) at {:?}",
e,
2023-05-11 15:00:42 +00:00
(slot.x, slot.y, slot.z)
),
LogSeverity::Basic,
);
2023-05-10 05:47:10 +00:00
e
}
BoardSlotState::White(e) => {
2023-05-11 15:00:42 +00:00
Self::log(
&format!(
2023-05-12 12:53:01 +00:00
" ==> black player removes white stone ({:?}) at {:?}",
e,
2023-05-11 15:00:42 +00:00
(slot.x, slot.y, slot.z)
),
LogSeverity::Basic,
);
2023-05-10 05:47:10 +00:00
e
}
_ => unreachable!(),
};
scene.remove_entity(entity)?;
2023-05-11 12:00:24 +00:00
assert_ne!(stone.state, StoneState::Dead);
stone.state = StoneState::Dead;
slot.set_state(BoardSlotState::Empty);
2023-05-10 05:47:10 +00:00
Ok(())
}
fn check_mill(s1: &BoardSlot, s2: &BoardSlot, s3: &BoardSlot) -> MillState {
if s1.valid() && s2.valid() && s3.valid() {
if s1.white() && s2.white() && s3.white() {
return MillState::White;
} else if s1.black() && s2.black() && s3.black() {
return MillState::Black;
}
}
MillState::None
}
2023-05-11 12:00:24 +00:00
pub fn check_for_mill(slot: &BoardSlot, board: &[[[BoardSlot; 3]; 3]; 3]) -> MillState {
2023-05-11 15:00:42 +00:00
Self::log("check for mill", LogSeverity::Debug);
2023-05-10 05:47:10 +00:00
2023-05-11 12:00:24 +00:00
if !(slot.x == 0 && slot.y == 0)
&& !(slot.x == 2 && slot.y == 0)
&& !(slot.x == 0 && slot.y == 2)
&& !(slot.x == 2 && slot.y == 2)
{
let state = Self::check_mill(
&board[slot.x][slot.y][0],
&board[slot.x][slot.y][1],
&board[slot.x][slot.y][2],
);
2023-05-09 16:58:07 +00:00
2023-05-11 12:00:24 +00:00
if state != MillState::None {
2023-05-11 15:00:42 +00:00
Self::log(&format!("mill found {:?}", state), LogSeverity::Debug);
2023-05-10 05:47:10 +00:00
2023-05-11 12:00:24 +00:00
return state;
}
2023-05-09 16:58:07 +00:00
}
2023-05-10 05:47:10 +00:00
2023-05-10 08:22:46 +00:00
let state = Self::check_mill(
&board[slot.x][0][slot.z],
&board[slot.x][1][slot.z],
&board[slot.x][2][slot.z],
);
2023-05-10 05:47:10 +00:00
2023-05-10 08:22:46 +00:00
if state != MillState::None {
2023-05-11 15:00:42 +00:00
Self::log(&format!("mill found {:?}", state), LogSeverity::Debug);
2023-05-10 05:47:10 +00:00
2023-05-10 08:22:46 +00:00
return state;
2023-05-10 05:47:10 +00:00
}
2023-05-10 08:22:46 +00:00
let state = Self::check_mill(
&board[0][slot.y][slot.z],
&board[1][slot.y][slot.z],
&board[2][slot.y][slot.z],
);
2023-05-10 05:47:10 +00:00
2023-05-10 08:22:46 +00:00
if state != MillState::None {
2023-05-11 15:00:42 +00:00
Self::log(&format!("mill found {:?}", state), LogSeverity::Debug);
2023-05-10 05:47:10 +00:00
2023-05-10 08:22:46 +00:00
return state;
2023-05-10 05:47:10 +00:00
}
MillState::None
}
fn check_mouse_click<F>(&self, f: F) -> Result<()>
where
2023-05-10 08:22:46 +00:00
F: FnOnce(
&Self,
(usize, usize, usize),
&mut [[[BoardSlot; 3]; 3]; 3],
&mut Scene,
) -> Result<()>,
2023-05-10 05:47:10 +00:00
{
2023-05-11 15:00:42 +00:00
Self::log("check_mouse_click", LogSeverity::Debug);
2023-05-10 05:47:10 +00:00
self.scene.lock().unwrap().on_scene(|scene| {
if let Some(world_space) =
scene.screen_space_to_world(self.mouse_x.load(SeqCst), self.mouse_y.load(SeqCst))?
{
2023-05-10 08:22:46 +00:00
let mut board = self.board.lock().unwrap();
if let Some(slot) = board.close_to_marker(world_space.truncate()) {
2023-05-11 15:00:42 +00:00
Self::log("click is close to marker", LogSeverity::Debug);
2023-05-10 05:47:10 +00:00
2023-05-10 08:22:46 +00:00
f(self, (slot.x, slot.y, slot.z), board.slots(), scene)?;
2023-05-10 05:47:10 +00:00
}
}
Ok(())
})
}
2023-05-11 12:00:24 +00:00
fn is_neighbour((x, y, z): (usize, usize, usize), (tx, ty, tz): (usize, usize, usize)) -> bool {
if (x == tx && y == ty)
&& ((x == 0 && y == 0)
|| (x == 2 && y == 0)
|| (x == 0 && y == 2)
|| (x == 2 && y == 2))
{
return false;
}
(x as i32 - tx as i32).abs() == 1
|| (y as i32 - ty as i32).abs() == 1
|| (z as i32 - tz as i32).abs() == 1
}
2023-05-11 15:00:42 +00:00
pub fn log(s: &str, log_severity: LogSeverity) {
match (LOG_SEVERITY, log_severity) {
2023-05-12 12:53:01 +00:00
(LogSeverity::Basic, LogSeverity::Basic) => Self::write(s),
2023-05-16 07:05:04 +00:00
(LogSeverity::Basic, LogSeverity::Debug) => Self::write_extra(s),
2023-05-12 12:53:01 +00:00
(LogSeverity::Debug, LogSeverity::Basic) => Self::write(s),
(LogSeverity::Debug, LogSeverity::Debug) => Self::write(s),
}
}
fn write(s: &str) {
if let Ok(mut file) = OpenOptions::new().append(true).create(true).open(&LOG_FILE) {
if let Err(_) = file.write_all(format!("{}\n", s.to_string()).as_bytes()) {}
2023-05-11 15:00:42 +00:00
}
2023-05-09 16:58:07 +00:00
}
2023-05-16 07:05:04 +00:00
fn write_extra(s: &str) {
if let Ok(mut file) = OpenOptions::new()
.append(true)
.create(true)
.open("millgame_extra.log")
{
if let Err(_) = file.write_all(format!("{}\n", s.to_string()).as_bytes()) {}
}
}
2023-05-08 18:56:50 +00:00
}
impl EngineObject for MillGame {
fn name(&self) -> &str {
"MillGame"
}
fn update(&self) -> Result<()> {
2023-05-10 08:22:46 +00:00
if self.turn_finished.load(SeqCst) {
2023-05-11 15:00:42 +00:00
if (*self.last_turn_timing.lock().unwrap() + Self::TURN_WAIT_TIME) < self.engine.time()
{
self.turn_finished.store(false, SeqCst);
self.next_game_step()?;
}
2023-05-10 08:22:46 +00:00
}
2023-05-08 18:56:50 +00:00
Ok(())
}
fn event(&self, event: EngineEvent) -> Result<()> {
match event {
2023-05-09 14:39:53 +00:00
EngineEvent::MouseMotion(x, y) => {
self.mouse_x.store(x, SeqCst);
self.mouse_y.store(y, SeqCst);
self.camera_controls.lock().unwrap().mouse_move(
x,
y,
&mut *self.scene.lock().unwrap(),
)?;
}
2023-05-11 12:00:24 +00:00
EngineEvent::MouseButtonDown(button) => {
match button {
MouseButton::Left => {
let mut state = self.state.lock().unwrap();
2023-05-11 15:00:42 +00:00
Self::log(
&format!("User click at state {:?}", state),
LogSeverity::Debug,
);
2023-05-11 12:00:24 +00:00
match *state {
GameState::Placing => {
self.check_mouse_click(|me, (x, y, z), board, scene| {
2023-05-11 15:00:42 +00:00
let current_player = *me.current_player.lock().unwrap();
2023-05-11 12:00:24 +00:00
2023-05-11 15:00:42 +00:00
if let Some(placable) = me
2023-05-11 12:00:24 +00:00
.stones(current_player)
.iter_mut()
.find(|stone| stone.state == StoneState::ReadyToBePlaced)
2023-05-10 08:22:46 +00:00
{
2023-05-11 12:00:24 +00:00
if board[x][y][z].is_empty() {
if Self::place_stone(
placable,
(x, y, z),
board,
current_player,
scene,
&mut *state,
2023-05-11 15:00:42 +00:00
)? == MillState::None
2023-05-11 12:00:24 +00:00
{
2023-05-11 15:00:42 +00:00
me.finish_turn();
2023-05-11 12:00:24 +00:00
}
} else {
2023-05-11 15:00:42 +00:00
Self::log(
&format!(
"slot ({:?}), not empty ({:?})",
(x, y, z),
board[x][y][z].state()
),
LogSeverity::Debug,
);
2023-05-11 12:00:24 +00:00
}
2023-05-10 08:22:46 +00:00
}
2023-05-09 16:58:07 +00:00
2023-05-11 12:00:24 +00:00
Ok(())
})?;
}
GameState::Removing => {
self.check_mouse_click(|me, (x, y, z), board, scene| {
let slot = &mut board[x][y][z];
2023-05-11 15:00:42 +00:00
Self::log(
&format!(
"state {:?} - player {:?}",
slot.state(),
*me.current_player.lock().unwrap()
),
LogSeverity::Debug,
);
2023-05-11 12:00:24 +00:00
2023-05-11 15:00:42 +00:00
if match (slot.state(), *me.current_player.lock().unwrap()) {
2023-05-11 12:00:24 +00:00
(BoardSlotState::Black(_), PlayerColor::White) => true,
(BoardSlotState::White(_), PlayerColor::Black) => true,
_ => false,
} {
2023-05-11 15:00:42 +00:00
let mut stones =
self.stones(me.current_player.lock().unwrap().other());
2023-05-11 12:00:24 +00:00
let stone = stones
.iter_mut()
.find(|s| {
s.stone
== match slot.state() {
BoardSlotState::Black(e) => e,
BoardSlotState::White(e) => e,
_ => unreachable!(),
}
})
.unwrap();
Self::remove_stone(stone, slot, scene)?;
2023-05-11 15:00:42 +00:00
me.finish_turn();
2023-05-11 12:00:24 +00:00
}
2023-05-10 05:47:10 +00:00
2023-05-11 12:00:24 +00:00
Ok(())
})?;
}
GameState::Main => {
self.check_mouse_click(|me, (tx, ty, tz), board, scene| {
let mut selected_slot = me.selected_field.lock().unwrap();
match *selected_slot {
Some((x, y, z)) => {
if Self::is_neighbour((x, y, z), (tx, ty, tz)) {
2023-05-12 07:04:48 +00:00
if let Some(millstate)
= Self::move_stone((x,y,z), (tx,ty,tz), scene, board, &mut state)? {
if let MillState::None = millstate {
*selected_slot = None;
2023-05-16 07:05:04 +00:00
drop(selected_slot);
2023-05-12 07:04:48 +00:00
self.finish_turn();
}
2023-05-11 12:00:24 +00:00
}
}
}
None => {
let slot = &board[tx][ty][tz];
match (
slot.state(),
*me.current_player.lock().unwrap(),
) {
(BoardSlotState::Black(_), PlayerColor::Black)
| (BoardSlotState::White(_), PlayerColor::White) => {
2023-05-12 12:53:01 +00:00
Self::log(
&format!("Selected ({:?})", (tx, ty, tz)),
LogSeverity::Basic,
);
2023-05-11 12:00:24 +00:00
*selected_slot = Some((tx, ty, tz));
}
_ => (),
}
}
}
2023-05-09 14:39:53 +00:00
2023-05-11 12:00:24 +00:00
Ok(())
})?;
2023-05-10 05:47:10 +00:00
}
2023-05-16 08:00:24 +00:00
GameState::Waiting | GameState::Won(_) => (),
2023-05-10 05:47:10 +00:00
}
2023-05-09 16:58:07 +00:00
}
2023-05-11 12:00:24 +00:00
MouseButton::Middle => self.camera_controls.lock().unwrap().hold(),
2023-05-12 12:53:01 +00:00
MouseButton::Right => {
let mut selected_slot = self.selected_field.lock().unwrap();
if selected_slot.is_some() {
Self::log(
&format!("Released selection ({:?})", selected_slot.unwrap()),
LogSeverity::Basic,
);
*selected_slot = None;
}
}
2023-05-11 12:00:24 +00:00
MouseButton::Forward => (),
MouseButton::Backward => (),
2023-05-09 14:39:53 +00:00
}
2023-05-11 12:00:24 +00:00
}
2023-05-09 14:39:53 +00:00
EngineEvent::MouseButtonUp(button) => match button {
MouseButton::Left => (),
MouseButton::Middle => self.camera_controls.lock().unwrap().release(),
MouseButton::Right => (),
MouseButton::Forward => (),
MouseButton::Backward => (),
},
EngineEvent::MouseWheel(_x, y, direction) => self
.camera_controls
.lock()
.unwrap()
.scroll_wheel(y, direction, &mut *self.scene.lock().unwrap())?,
2023-05-08 19:24:12 +00:00
EngineEvent::KeyDown(_) => (),
EngineEvent::KeyUp(_) => (),
EngineEvent::ButtonDown(_) => (),
EngineEvent::ButtonUp(_) => (),
EngineEvent::ControllerAxis(_) => (),
EngineEvent::ControllerAdded(_) => (),
EngineEvent::ControllerRemoved(_) => (),
EngineEvent::FileDrop(_) => (),
2023-05-08 18:56:50 +00:00
}
Ok(())
}
}