Start placing phse
This commit is contained in:
parent
139f7d92cc
commit
686dae95bf
5 changed files with 324 additions and 35 deletions
8
resources/mainmenu.xml
Normal file
8
resources/mainmenu.xml
Normal 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>
|
30
src/board.rs
30
src/board.rs
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
267
src/game.rs
267
src/game.rs
|
@ -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 => (),
|
||||||
|
|
|
@ -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
48
src/simple_ai.rs
Normal 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(())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue