mill_game/src/game.rs

643 lines
20 KiB
Rust
Raw Normal View History

2023-05-09 14:39:53 +00:00
use std::sync::{
atomic::{AtomicU32, Ordering::SeqCst},
2023-05-10 05:47:10 +00:00
Arc, Mutex, MutexGuard,
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-09 14:39:53 +00:00
use engine::prelude::{cgmath::vec3, *};
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 {
Placing,
2023-05-10 05:47:10 +00:00
Removing,
2023-05-09 16:58:07 +00:00
Main,
}
#[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-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 => {
MillGame::log("swapped to black player");
PlayerColor::Black
}
PlayerColor::Black => {
MillGame::log("swapped to white player");
PlayerColor::White
}
2023-05-09 16:58:07 +00:00
}
}
}
2023-05-08 18:56:50 +00:00
pub struct MillGame {
2023-05-09 16:58:07 +00:00
board: Arc<Mutex<Board>>,
white_stones: Mutex<[Stone; 9]>,
black_stones: Mutex<[Stone; 9]>,
state: Mutex<GameState>,
current_player: Mutex<PlayerColor>,
2023-05-09 14:39:53 +00:00
scene: Mutex<SceneHandle>,
camera_controls: Mutex<CameraControl>,
mouse_x: AtomicU32,
mouse_y: AtomicU32,
2023-05-09 16:58:07 +00:00
simple_ai: SimpleAI,
white_player_label: Arc<Label>,
black_player_label: Arc<Label>,
start_button: Arc<Button>,
grid: Arc<Grid>,
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-09 14:39:53 +00:00
2023-05-08 18:56:50 +00:00
pub fn new(engine: Arc<Engine>) -> Result<Arc<Self>> {
let mut scene = SceneHandle::new(&engine)?;
2023-05-09 14:39:53 +00:00
let board = Board::new(&engine, &mut scene)?;
let mut white_stones = None;
let mut black_stones = None;
scene.on_scene(|scene| {
let view = scene.view_mut();
view.camera_mut().set_center(board.center());
view.update_buffer()?;
// add light
let mut sun_light = engine.new_directional_light()?;
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)?;
scene.add_light(sun_light)?;
white_stones = Some(
Self::init_nine_stones(&engine, 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));
2023-05-09 16:58:07 +00:00
Ok(Stone {
stone: scene.add_entity(e)?,
state: StoneState::ReadyToBePlaced,
})
2023-05-09 14:39:53 +00:00
})
2023-05-09 16:58:07 +00:00
.collect::<Result<Vec<Stone>>>()?
2023-05-09 14:39:53 +00:00
.try_into()
2023-05-09 16:58:07 +00:00
.unwrap_or_else(|_: Vec<Stone>| {
2023-05-09 14:39:53 +00:00
unreachable!("create array from vec from an array")
}),
);
black_stones = Some(
2023-05-09 16:58:07 +00:00
Self::init_nine_stones(&engine, Color::try_from("#2c2c2c")?)?
2023-05-09 14:39:53 +00:00
.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));
2023-05-09 16:58:07 +00:00
Ok(Stone {
stone: scene.add_entity(e)?,
state: StoneState::ReadyToBePlaced,
})
2023-05-09 14:39:53 +00:00
})
2023-05-09 16:58:07 +00:00
.collect::<Result<Vec<Stone>>>()?
2023-05-09 14:39:53 +00:00
.try_into()
2023-05-09 16:58:07 +00:00
.unwrap_or_else(|_: Vec<Stone>| {
2023-05-09 14:39:53 +00:00
unreachable!("create array from vec from an array")
}),
);
Ok(())
})?;
2023-05-09 16:58:07 +00:00
let mut camera_control = CameraControl::new(&mut scene)?;
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
scene.activate()?;
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")?;
gui.enable()?;
let me = Arc::new(Self {
board: Arc::new(Mutex::new(board)),
white_stones: Mutex::new(white_stones.unwrap()),
black_stones: Mutex::new(black_stones.unwrap()),
state: Mutex::new(GameState::Placing),
current_player: Mutex::new(PlayerColor::White),
2023-05-09 14:39:53 +00:00
scene: Mutex::new(scene),
camera_controls: Mutex::new(camera_control),
mouse_x: AtomicU32::new(0),
mouse_y: AtomicU32::new(0),
2023-05-09 16:58:07 +00:00
simple_ai: SimpleAI::new(PlayerColor::White),
grid: grid.clone(),
white_player_label,
black_player_label,
start_button: start_button.clone(),
gui,
});
start_button.set_callback({
let weak_grid = Arc::downgrade(&grid);
let weak_self = Arc::downgrade(&me);
move || {
if let Some(grid) = weak_grid.upgrade() {
grid.detach(1, 0)?;
}
if let Some(me) = weak_self.upgrade() {
2023-05-10 05:47:10 +00:00
Self::log("start game");
2023-05-09 16:58:07 +00:00
*me.state.lock().unwrap() = GameState::Placing;
2023-05-10 05:47:10 +00:00
*me.current_player.lock().unwrap() = PlayerColor::Black;
2023-05-09 16:58:07 +00:00
me.next_game_step()?;
}
Ok(())
}
});
Ok(me)
}
fn next_game_step(&self) -> Result<()> {
2023-05-10 05:47:10 +00:00
Self::log("next_game_step");
2023-05-09 16:58:07 +00:00
{
let mut state = self.state.lock().unwrap();
2023-05-10 05:47:10 +00:00
Self::log(&format!("\tstate: {:?}", state));
2023-05-09 16:58:07 +00:00
match *state {
GameState::Placing => {
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-10 05:47:10 +00:00
Self::log("change state to Main");
2023-05-09 16:58:07 +00:00
*state = GameState::Main;
}
2023-05-10 05:47:10 +00:00
if self.check_for_mill() != MillState::None {
Self::log("change state to Removing");
*state = GameState::Removing;
} else {
self.current_player.lock().unwrap().swap();
}
}
GameState::Removing => {
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)
{
Self::log("change state to Main");
*state = GameState::Main;
} else {
Self::log("change state to Placing");
*state = GameState::Placing;
}
self.current_player.lock().unwrap().swap();
}
GameState::Main => {
if self.check_for_mill() != MillState::None {
Self::log("change state to Removing");
*state = GameState::Removing;
} else {
self.current_player.lock().unwrap().swap();
}
2023-05-09 16:58:07 +00:00
}
}
}
2023-05-10 05:47:10 +00:00
let player = *self.current_player.lock().unwrap();
Self::log(&format!("current player {:?}", player));
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 {
Self::log("current player is AI");
self.simple_ai.step(
&mut self.stones(self.simple_ai.player_color),
self.board.lock().unwrap().slots(),
&mut *self.scene.lock().unwrap(),
*self.state.lock().unwrap(),
)?;
2023-05-09 16:58:07 +00:00
self.next_game_step()?;
}
Ok(())
2023-05-08 18:56:50 +00:00
}
2023-05-09 14:39:53 +00:00
2023-05-10 05:47:10 +00:00
fn stones(&self, player_color: PlayerColor) -> MutexGuard<'_, [Stone; 9]> {
match player_color {
PlayerColor::White => self.white_stones.lock().unwrap(),
PlayerColor::Black => self.black_stones.lock().unwrap(),
}
}
2023-05-09 14:39:53 +00:00
fn init_nine_stones(engine: &Arc<Engine>, color: Color) -> Result<[EntityObject; 9]> {
Ok((0..9)
.map(|_| Self::init_stone(engine, color))
.collect::<Result<Vec<EntityObject>>>()?
.try_into()
.unwrap_or_else(|_: Vec<EntityObject>| {
unreachable!("create array from vec from an array")
}))
}
fn init_stone(engine: &Arc<Engine>, color: Color) -> Result<EntityObject> {
let mut marker = engine.assets().empty_entity();
let draw = Draw::new(vec![{
let mut mesh = AssetMesh::new(
engine.device(),
engine.settings().graphics_info()?.render_type,
)?;
let vertex_buffer = Buffer::builder()
.set_data(&Objects::create_cylinder(1.0, 0.25, 12))
.set_usage(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT)
.set_memory_usage(MemoryUsage::CpuOnly)
.build(engine.device().clone())?;
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,
slot: &mut BoardSlot,
player_color: PlayerColor,
scene: &mut Scene,
) -> Result<()> {
2023-05-10 05:47:10 +00:00
Self::log("place_stone");
2023-05-09 16:58:07 +00:00
match player_color {
2023-05-10 05:47:10 +00:00
PlayerColor::White => {
Self::log(&format!("place white stone"));
slot.state = BoardSlotState::White(stone.stone);
}
PlayerColor::Black => {
Self::log(&format!("place black stone"));
slot.state = BoardSlotState::Black(stone.stone);
}
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));
Ok(())
}
2023-05-10 05:47:10 +00:00
pub fn remove_stone(slot: &mut BoardSlot, scene: &mut Scene) -> Result<()> {
Self::log("remove_stone");
let entity = match slot.state {
BoardSlotState::Black(e) => {
Self::log(&format!("\tremove black stone"));
e
}
BoardSlotState::White(e) => {
Self::log(&format!("\tremove white stone"));
e
}
_ => unreachable!(),
};
scene.remove_entity(entity)?;
slot.state = BoardSlotState::Empty;
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
}
pub fn check_for_mill(&self) -> MillState {
Self::log("check for mill");
let mut board = self.board.lock().unwrap();
let slots = board.slots();
2023-05-09 16:58:07 +00:00
for x in 0..3 {
for y in 0..3 {
2023-05-10 05:47:10 +00:00
let state = Self::check_mill(&slots[x][y][0], &slots[x][y][1], &slots[x][y][2]);
if state != MillState::None {
Self::log(&format!("mill found {:?}", state));
return state;
2023-05-09 16:58:07 +00:00
}
}
}
2023-05-10 05:47:10 +00:00
for x in 0..3 {
for z in 0..3 {
let state = Self::check_mill(&slots[x][0][z], &slots[x][1][z], &slots[x][2][z]);
if state != MillState::None {
Self::log(&format!("mill found {:?}", state));
return state;
}
}
}
for y in 0..3 {
for z in 0..3 {
let state = Self::check_mill(&slots[0][y][z], &slots[1][y][z], &slots[2][y][z]);
if state != MillState::None {
Self::log(&format!("mill found {:?}", state));
return state;
}
}
}
MillState::None
}
fn check_mouse_click<F>(&self, f: F) -> Result<()>
where
F: FnOnce(&Self, &mut BoardSlot, &mut Scene) -> Result<()>,
{
Self::log("check_mouse_click");
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))?
{
if let Some(slot) = self
.board
.lock()
.unwrap()
.close_to_marker(world_space.truncate())
{
Self::log("click is close to marker");
f(self, slot, scene)?;
}
}
Ok(())
})
}
pub fn log(s: &str) {
println!("{}", s);
2023-05-09 16:58:07 +00:00
}
2023-05-08 18:56:50 +00:00
}
impl EngineObject for MillGame {
fn name(&self) -> &str {
"MillGame"
}
fn update(&self) -> Result<()> {
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(),
)?;
}
EngineEvent::MouseButtonDown(button) => match button {
MouseButton::Left => {
2023-05-10 05:47:10 +00:00
let state = *self.state.lock().unwrap();
Self::log(&format!("User click at state {:?}", state));
match state {
GameState::Placing => {
let mut placed = false;
self.check_mouse_click(|me, slot, scene| {
let current_player = *self.current_player.lock().unwrap();
2023-05-09 16:58:07 +00:00
if let Some(placable) = self
2023-05-10 05:47:10 +00:00
.stones(current_player)
2023-05-09 16:58:07 +00:00
.iter_mut()
.find(|stone| stone.state == StoneState::ReadyToBePlaced)
{
2023-05-10 05:47:10 +00:00
Self::place_stone(placable, slot, current_player, scene)?;
2023-05-09 16:58:07 +00:00
placed = true;
}
2023-05-10 05:47:10 +00:00
Ok(())
})?;
if placed {
self.next_game_step()?;
2023-05-09 14:39:53 +00:00
}
}
2023-05-10 05:47:10 +00:00
GameState::Removing => {
let mut removed = false;
self.check_mouse_click(|me, slot, scene| {
Self::log(&format!(
"state {:?} - player {:?}",
slot.state,
*self.current_player.lock().unwrap()
));
if match (slot.state, *self.current_player.lock().unwrap()) {
(BoardSlotState::Black(_), PlayerColor::White) => true,
(BoardSlotState::White(_), PlayerColor::Black) => true,
_ => false,
} {
removed = true;
Self::remove_stone(slot, scene)?;
}
2023-05-09 14:39:53 +00:00
2023-05-10 05:47:10 +00:00
Ok(())
})?;
2023-05-09 16:58:07 +00:00
2023-05-10 05:47:10 +00:00
if removed {
self.next_game_step()?;
}
}
GameState::Main => todo!(),
2023-05-09 16:58:07 +00:00
}
2023-05-09 14:39:53 +00:00
}
MouseButton::Middle => self.camera_controls.lock().unwrap().hold(),
MouseButton::Right => (),
MouseButton::Forward => (),
MouseButton::Backward => (),
},
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(())
}
}