Start placing phse

This commit is contained in:
hodasemi 2023-05-09 18:58:07 +02:00
parent 139f7d92cc
commit 686dae95bf
5 changed files with 324 additions and 35 deletions

8
resources/mainmenu.xml Normal file
View file

@ -0,0 +1,8 @@
<root reference_width="1280" reference_height="720">
<grid id="grid" x_dim="3" y_dim="1" x_offset="-400" y_offset="10" width="800" height="80"
vert_align="top" hori_align="middle" margin="0" padding="20">
<label id="white_player_label" x_slot="0" y_slot="0" text_color="white">White Player (AI)</label>
<button id="start" x_slot="1" y_slot="0" text_color="white" select="true">Start</button>
<label id="black_player_label" x_slot="2" y_slot="0" text_color="white">Black Player</label>
</grid>
</root>

View file

@ -98,10 +98,10 @@ impl Default for BoardSlotState {
} }
pub struct Board { pub struct Board {
board: Entity, _board: Entity,
center: Vector3<f32>, center: Vector3<f32>,
connection_lines: Vec<Entity>, _connection_lines: Vec<Entity>,
slots: [[[BoardSlot; 3]; 3]; 3], slots: [[[BoardSlot; 3]; 3]; 3],
white_start_slots: [Vector2<f32>; 9], white_start_slots: [Vector2<f32>; 9],
@ -109,7 +109,7 @@ pub struct Board {
} }
impl Board { impl Board {
pub fn new(engine: &Arc<Engine>, scene: &mut SceneHandle) -> Result<Arc<Self>> { pub fn new(engine: &Arc<Engine>, scene: &mut SceneHandle) -> Result<Self> {
let mut board_entity = engine.assets().empty_entity(); let mut board_entity = engine.assets().empty_entity();
let meshes = vec![Self::create_board_base(&engine)?]; let meshes = vec![Self::create_board_base(&engine)?];
@ -209,16 +209,16 @@ impl Board {
vec2(16.0, -distance), vec2(16.0, -distance),
]; ];
Ok(Arc::new(Self { Ok(Self {
board: board.unwrap(), _board: board.unwrap(),
center: vec3(0.0, 0.0, 0.0), center: vec3(0.0, 0.0, 0.0),
connection_lines: Self::create_connection_lines(scene, Color::Black)?, _connection_lines: Self::create_connection_lines(scene, Color::Black)?,
slots: slots.unwrap(), slots: slots.unwrap(),
white_start_slots, white_start_slots,
black_start_slots, black_start_slots,
})) })
} }
pub fn center(&self) -> Vector3<f32> { pub fn center(&self) -> Vector3<f32> {
@ -233,22 +233,26 @@ impl Board {
&self.black_start_slots &self.black_start_slots
} }
pub fn close_to_marker(&self, position: Vector2<f32>) -> bool { pub fn slots(&mut self) -> &mut [[[BoardSlot; 3]; 3]; 3] {
&mut self.slots
}
pub fn close_to_marker(&mut self, position: Vector2<f32>) -> Option<&mut BoardSlot> {
const DISTANCE: f32 = 1.25; const DISTANCE: f32 = 1.25;
for outer in self.slots.iter() { for outer in self.slots.iter_mut() {
for inner in outer.iter() { for inner in outer.iter_mut() {
for slot in inner.iter() { for slot in inner.iter_mut() {
if slot.state != BoardSlotState::Invalid { if slot.state != BoardSlotState::Invalid {
if (slot.position - position).magnitude() <= DISTANCE { if (slot.position - position).magnitude() <= DISTANCE {
return true; return Some(slot);
} }
} }
} }
} }
} }
false None
} }
} }

View file

@ -4,24 +4,74 @@ use std::sync::{
}; };
use anyhow::Result; use anyhow::Result;
use assetpath::AssetPath;
use engine::prelude::{cgmath::vec3, *}; use engine::prelude::{cgmath::vec3, *};
use crate::{board::Board, objects::Objects}; 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,
Main,
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
pub enum PlayerColor {
White,
Black,
}
impl PlayerColor {
pub fn swap(&mut self) {
*self = match *self {
PlayerColor::White => PlayerColor::Black,
PlayerColor::Black => PlayerColor::White,
}
}
}
pub struct MillGame { pub struct MillGame {
board: Arc<Board>, board: Arc<Mutex<Board>>,
white_stones: [Entity; 9], white_stones: Mutex<[Stone; 9]>,
black_stones: [Entity; 9], black_stones: Mutex<[Stone; 9]>,
state: Mutex<GameState>,
current_player: Mutex<PlayerColor>,
scene: Mutex<SceneHandle>, scene: Mutex<SceneHandle>,
camera_controls: Mutex<CameraControl>, camera_controls: Mutex<CameraControl>,
mouse_x: AtomicU32, mouse_x: AtomicU32,
mouse_y: AtomicU32, mouse_y: AtomicU32,
simple_ai: SimpleAI,
white_player_label: Arc<Label>,
black_player_label: Arc<Label>,
start_button: Arc<Button>,
grid: Arc<Grid>,
gui: Arc<GuiBuilder>,
} }
impl MillGame { impl MillGame {
pub const OFFSET_TO_BOARD: f32 = 0.01; pub const OFFSET_TO_BOARD: f32 = 0.02;
pub fn new(engine: Arc<Engine>) -> Result<Arc<Self>> { pub fn new(engine: Arc<Engine>) -> Result<Arc<Self>> {
let mut scene = SceneHandle::new(&engine)?; let mut scene = SceneHandle::new(&engine)?;
@ -53,28 +103,34 @@ impl MillGame {
let location = e.get_component_mut::<Location>()?; let location = e.get_component_mut::<Location>()?;
location.set_position(board.white_start_slots()[index].extend(0.0)); location.set_position(board.white_start_slots()[index].extend(0.0));
scene.add_entity(e) Ok(Stone {
stone: scene.add_entity(e)?,
state: StoneState::ReadyToBePlaced,
})
}) })
.collect::<Result<Vec<Entity>>>()? .collect::<Result<Vec<Stone>>>()?
.try_into() .try_into()
.unwrap_or_else(|_: Vec<Entity>| { .unwrap_or_else(|_: Vec<Stone>| {
unreachable!("create array from vec from an array") unreachable!("create array from vec from an array")
}), }),
); );
black_stones = Some( black_stones = Some(
Self::init_nine_stones(&engine, Color::Black)? Self::init_nine_stones(&engine, Color::try_from("#2c2c2c")?)?
.into_iter() .into_iter()
.enumerate() .enumerate()
.map(|(index, mut e)| { .map(|(index, mut e)| {
let location = e.get_component_mut::<Location>()?; let location = e.get_component_mut::<Location>()?;
location.set_position(board.black_start_slots()[index].extend(0.0)); location.set_position(board.black_start_slots()[index].extend(0.0));
scene.add_entity(e) Ok(Stone {
stone: scene.add_entity(e)?,
state: StoneState::ReadyToBePlaced,
})
}) })
.collect::<Result<Vec<Entity>>>()? .collect::<Result<Vec<Stone>>>()?
.try_into() .try_into()
.unwrap_or_else(|_: Vec<Entity>| { .unwrap_or_else(|_: Vec<Stone>| {
unreachable!("create array from vec from an array") unreachable!("create array from vec from an array")
}), }),
); );
@ -82,21 +138,135 @@ impl MillGame {
Ok(()) Ok(())
})?; })?;
let camera_control = CameraControl::new(&mut scene)?; 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,
)?;
scene.activate()?; scene.activate()?;
Ok(Arc::new(Self { let gui = GuiBuilder::new(
board, engine.gui_handler(),
white_stones: white_stones.unwrap(), &AssetPath::from((
black_stones: black_stones.unwrap(), 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),
scene: Mutex::new(scene), scene: Mutex::new(scene),
camera_controls: Mutex::new(camera_control), camera_controls: Mutex::new(camera_control),
mouse_x: AtomicU32::new(0), mouse_x: AtomicU32::new(0),
mouse_y: AtomicU32::new(0), mouse_y: AtomicU32::new(0),
}))
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() {
*me.state.lock().unwrap() = GameState::Placing;
*me.current_player.lock().unwrap() = PlayerColor::White;
me.next_game_step()?;
}
Ok(())
}
});
Ok(me)
}
fn next_game_step(&self) -> Result<()> {
match *self.current_player.lock().unwrap() {
PlayerColor::White => {
self.black_player_label
.set_background(Color::try_from("#9b9292")?)?;
self.white_player_label.set_background(Color::Yellow)?;
}
PlayerColor::Black => {
self.white_player_label
.set_background(Color::try_from("#9b9292")?)?;
self.black_player_label.set_background(Color::Yellow)?;
}
}
{
let mut state = self.state.lock().unwrap();
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)
{
*state = GameState::Main;
}
}
GameState::Main => {}
}
}
if *self.current_player.lock().unwrap() == self.simple_ai.player_color {
match self.simple_ai.player_color {
PlayerColor::White => self.simple_ai.step(
&mut *self.white_stones.lock().unwrap(),
self.board.lock().unwrap().slots(),
&mut *self.scene.lock().unwrap(),
)?,
PlayerColor::Black => self.simple_ai.step(
&mut *self.black_stones.lock().unwrap(),
self.board.lock().unwrap().slots(),
&mut *self.scene.lock().unwrap(),
)?,
}
self.current_player.lock().unwrap().swap();
self.next_game_step()?;
}
Ok(())
} }
fn init_nine_stones(engine: &Arc<Engine>, color: Color) -> Result<[EntityObject; 9]> { fn init_nine_stones(engine: &Arc<Engine>, color: Color) -> Result<[EntityObject; 9]> {
@ -150,6 +320,39 @@ impl MillGame {
Ok(marker) Ok(marker)
} }
pub fn place_stone(
stone: &mut Stone,
slot: &mut BoardSlot,
player_color: PlayerColor,
scene: &mut Scene,
) -> Result<()> {
match player_color {
PlayerColor::White => slot.state = BoardSlotState::White(stone.stone),
PlayerColor::Black => slot.state = BoardSlotState::Black(stone.stone),
}
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(())
}
pub fn check_for_mill(&self) -> Result<bool> {
let slots = self.board.lock().unwrap().slots();
for x in 0..3 {
for y in 0..3 {
for z in 0..3 {
//
}
}
}
}
} }
impl EngineObject for MillGame { impl EngineObject for MillGame {
@ -175,20 +378,40 @@ impl EngineObject for MillGame {
} }
EngineEvent::MouseButtonDown(button) => match button { EngineEvent::MouseButtonDown(button) => match button {
MouseButton::Left => { MouseButton::Left => {
let mut placed = false;
self.scene.lock().unwrap().on_scene(|scene| { self.scene.lock().unwrap().on_scene(|scene| {
if let Some(world_space) = scene.screen_space_to_world( if let Some(world_space) = scene.screen_space_to_world(
self.mouse_x.load(SeqCst), self.mouse_x.load(SeqCst),
self.mouse_y.load(SeqCst), self.mouse_y.load(SeqCst),
)? { )? {
if self.board.close_to_marker(world_space.truncate()) { if let Some(slot) = self
println!("close to marker"); .board
} else { .lock()
println!("not close to a marker"); .unwrap()
.close_to_marker(world_space.truncate())
{
if let Some(placable) = self
.black_stones
.lock()
.unwrap()
.iter_mut()
.find(|stone| stone.state == StoneState::ReadyToBePlaced)
{
Self::place_stone(placable, slot, PlayerColor::Black, scene)?;
placed = true;
}
} }
} }
Ok(()) Ok(())
})?; })?;
if placed {
self.current_player.lock().unwrap().swap();
self.next_game_step()?;
}
} }
MouseButton::Middle => self.camera_controls.lock().unwrap().hold(), MouseButton::Middle => self.camera_controls.lock().unwrap().hold(),
MouseButton::Right => (), MouseButton::Right => (),

View file

@ -1,6 +1,7 @@
mod board; mod board;
mod game; mod game;
mod objects; mod objects;
mod simple_ai;
use anyhow::Result; use anyhow::Result;
use assetpath::AssetPath; use assetpath::AssetPath;
@ -19,6 +20,11 @@ fn main() -> Result<()> {
AssetPath::from((create_info.resource_base_path.as_str(), "")); AssetPath::from((create_info.resource_base_path.as_str(), ""));
create_info.graphics_info.render_scale = 1.0; create_info.graphics_info.render_scale = 1.0;
create_info.graphics_info.vsync = true;
create_info.graphics_info.sample_count = VK_SAMPLE_COUNT_4_BIT;
create_info.rasterizer_info.enable_lighting = true;
create_info.rasterizer_info.shadow_image_size = 512;
create_info.window_info.height = 600; create_info.window_info.height = 600;
create_info.window_info.width = 800; create_info.window_info.width = 800;

48
src/simple_ai.rs Normal file
View file

@ -0,0 +1,48 @@
use crate::{
board::{BoardSlot, BoardSlotState},
game::{MillGame, PlayerColor, Stone, StoneState},
};
use anyhow::Result;
use engine::prelude::*;
pub struct SimpleAI {
pub player_color: PlayerColor,
}
impl SimpleAI {
pub fn new(player_color: PlayerColor) -> Self {
Self { player_color }
}
pub fn step(
&self,
stones: &mut [Stone; 9],
board: &mut [[[BoardSlot; 3]; 3]; 3],
scene: &mut SceneHandle,
) -> Result<()> {
if let Some(placable) = stones
.iter_mut()
.find(|stone| stone.state == StoneState::ReadyToBePlaced)
{
let mut free_slots: Vec<&mut BoardSlot> = board
.iter_mut()
.flatten()
.flatten()
.filter(|slot| slot.state == BoardSlotState::Empty)
.collect();
let len = free_slots.len();
let slot = &mut free_slots[Random::range(0, len as u32) as usize];
scene.on_scene(|scene| {
MillGame::place_stone(placable, slot, self.player_color, scene)?;
Ok(())
})?;
}
Ok(())
}
}