move to retained mode complete

This commit is contained in:
cool-mist
2026-03-15 17:24:41 +05:30
parent 194136dfc7
commit 82d13cfd66
12 changed files with 544 additions and 540 deletions
Binary file not shown.
+2 -276
View File
@@ -4,11 +4,7 @@ mod logic;
use std::collections::HashMap; use std::collections::HashMap;
use crate::{ use crate::{resources::Resources, widgets::*};
game,
resources::{self, Resources, SoundKind},
widgets::*,
};
use macroquad::prelude::*; use macroquad::prelude::*;
use sol_lib::{board::BoardState, generator::Puzzle}; use sol_lib::{board::BoardState, generator::Puzzle};
@@ -16,14 +12,12 @@ use sol_lib::{board::BoardState, generator::Puzzle};
pub struct Game { pub struct Game {
resources: Resources, resources: Resources,
// Game state
settings: Settings, settings: Settings,
puzzle: Puzzle, puzzle: Puzzle,
show_rules: bool, show_rules: bool,
game_mode: GameMode, game_mode: GameMode,
game_mode_btns: HashMap<GameMode, WidgetId>, game_mode_btns: HashMap<GameMode, WidgetId>,
// Render state
window_height: f32, window_height: f32,
window_width: f32, window_width: f32,
@@ -44,109 +38,6 @@ pub struct Game {
btn_generate: WidgetId, btn_generate: WidgetId,
} }
#[derive(Eq, PartialEq, Copy, Clone, Hash, Default)]
pub struct WidgetId(u64);
pub enum Widget {
Label(LabelWidget),
Counter(CounterWidget),
Button(ButtonWidget),
Board(BoardWidget),
}
impl Widget {
fn draw(&mut self, resources: &Resources, settings: &Settings) {
match self {
Widget::Label(label) => label.draw(&resources),
Widget::Counter(counter) => counter.draw(&resources),
Widget::Button(btn) => btn.draw(&resources),
Widget::Board(board) => board.draw(&resources, &settings),
}
}
fn update(
&mut self,
interaction: &Interaction,
resources: &Resources,
settings: &Settings,
) -> Command {
match self {
Widget::Label(_) => Command::NoInteraction,
Widget::Counter(c) => handle_counter_input(interaction, c, resources, settings),
Widget::Button(b) => handle_button_input(interaction, b, resources, settings),
Widget::Board(_) => Command::NoInteraction,
}
}
}
fn handle_counter_input(
interaction: &Interaction,
counter: &mut CounterWidget,
resources: &Resources,
settings: &Settings,
) -> Command {
let counter_interaction = counter.handle_input(interaction, resources, settings);
let Some(interaction_type) = counter_interaction.interaction_type else {
return Command::NoInteraction;
};
match interaction_type {
CounterInteractionType::Increment => (counter.on_increment)(counter_interaction),
CounterInteractionType::Decrement => (counter.on_decrement)(counter_interaction),
}
}
fn handle_button_input(
interaction: &Interaction,
button: &mut ButtonWidget,
resources: &Resources,
settings: &Settings,
) -> Command {
let clicked = button
.handle_input(interaction, resources, settings)
.clicked;
if clicked {
return (button.on_click)(button);
}
Command::NoInteraction
}
macro_rules! get_widget_fn {
($name:ident, $ret:ty, $variant:path) => {
fn $name(widgets: &mut HashMap<WidgetId, Widget>, id: WidgetId) -> &mut $ret {
match widgets.get_mut(&id) {
Some(w) => match w {
$variant(v) => return v,
_ => panic!("Widget with id {} not found", id.0),
},
None => panic!("Widget with id {} not found", id.0),
}
}
};
}
get_widget_fn!(label, LabelWidget, Widget::Label);
get_widget_fn!(button, ButtonWidget, Widget::Button);
get_widget_fn!(counter, CounterWidget, Widget::Counter);
get_widget_fn!(board, BoardWidget, Widget::Board);
macro_rules! widget_accessor {
($name:ident, $fn:ident) => {
macro_rules! $name {
($this:expr, $widget_id:ident) => {
game::$fn(&mut $this.widgets, $this.$widget_id)
};
}
pub(crate) use $name;
};
}
widget_accessor!(w_button, button);
widget_accessor!(w_label, label);
widget_accessor!(w_counter, counter);
widget_accessor!(w_board, board);
#[derive(Default)] #[derive(Default)]
pub struct Settings { pub struct Settings {
pub volume: f32, pub volume: f32,
@@ -169,6 +60,7 @@ impl Default for GameMode {
} }
} }
#[derive(Copy, Clone)]
pub enum Command { pub enum Command {
NoInteraction, NoInteraction,
ResetBoard, ResetBoard,
@@ -183,169 +75,3 @@ pub enum Command {
HideRules, HideRules,
Quit, Quit,
} }
pub async fn new_game() -> Game {
let resources = resources::init().await;
let game_mode = GameMode::Medium;
let settings = Settings {
volume: constants::VOLUME,
max_age: 3,
max_pieces: 2,
..Default::default()
};
let puzzle = logic::generate_new_puzzle(game_mode, &settings);
let game_mode = GameMode::Medium;
let label_heading = LabelWidget::initialize_state(constants::HEADING_TEXT);
let label_pieces = LabelWidget::initialize_state(constants::PIECES_LABEL_TEXT);
let label_max_age = LabelWidget::initialize_state(constants::AGE_LABEL_TEXT);
let show_rules = false;
let mut widgets = HashMap::new();
let label_pieces = add_widget(&mut widgets, Widget::Label(label_pieces));
let label_max_age = add_widget(&mut widgets, Widget::Label(label_max_age));
let label_heading = add_widget(&mut widgets, Widget::Label(label_heading));
let mut w_counter_pieces =
CounterWidget::new(2, 7, settings.max_pieces, false, UiColor::Yellow);
w_counter_pieces.on_increment = |c| Command::UpdateMaxPieceCount(c.current);
w_counter_pieces.on_decrement = |c| Command::UpdateMaxPieceCount(c.current);
let counter_pieces = add_widget(&mut widgets, Widget::Counter(w_counter_pieces));
let mut w_counter_moves = CounterWidget::new(1, 7, settings.max_age, false, UiColor::Yellow);
w_counter_moves.on_increment = |c| Command::UpdateMaxAge(c.current);
w_counter_moves.on_decrement = |c| Command::UpdateMaxAge(c.current);
let counter_moves = add_widget(&mut widgets, Widget::Counter(w_counter_moves));
let btn_reset = add_widget(
&mut widgets,
Widget::Button(ButtonWidget::new(
true,
UiColor::Yellow,
constants::RESET_BUTTON_TEXT,
SoundKind::Button,
)),
);
let btn_next = add_widget(
&mut widgets,
Widget::Button(ButtonWidget::new(
false,
UiColor::Green,
constants::NEXT_BUTTON_TEXT,
SoundKind::Button,
)),
);
let btn_rules = add_widget(
&mut widgets,
Widget::Button(ButtonWidget::new(
true,
UiColor::Brown,
constants::RULES_BUTTON_TEXT,
SoundKind::Button,
)),
);
let btn_generate = add_widget(
&mut widgets,
Widget::Button(ButtonWidget::new(
false,
UiColor::Pink,
constants::GENERATE_BUTTON_TEXT,
SoundKind::Button,
)),
);
let mut game_mode_btns = HashMap::new();
game_mode_btns.insert(
GameMode::Easy,
add_widget(
&mut widgets,
Widget::Button(ButtonWidget::new(
true,
UiColor::Yellow,
constants::EASY_BUTTON_TEXT,
SoundKind::Mode,
)),
),
);
game_mode_btns.insert(
GameMode::Medium,
add_widget(
&mut widgets,
Widget::Button(ButtonWidget::new(
true,
UiColor::Yellow,
constants::MEDIUM_BUTTON_TEXT,
SoundKind::Mode,
)),
),
);
game_mode_btns.insert(
GameMode::Hard,
add_widget(
&mut widgets,
Widget::Button(ButtonWidget::new(
true,
UiColor::Yellow,
constants::HARD_BUTTON_TEXT,
SoundKind::Mode,
)),
),
);
game_mode_btns.insert(
GameMode::Custom,
add_widget(
&mut widgets,
Widget::Button(ButtonWidget::new(
true,
UiColor::Blue,
constants::CUSTOM_BUTTON_TEXT,
SoundKind::Mode,
)),
),
);
let active_game_mode_btn = game_mode_btns.get(&game_mode).unwrap();
game::button(&mut widgets, *active_game_mode_btn).is_active = false;
let board = add_widget(
&mut widgets,
Widget::Board(BoardWidget::initialize_state(
puzzle.board.size,
puzzle.board.clone(),
)),
);
Game {
puzzle,
board,
resources,
settings,
game_mode,
game_mode_btns,
btn_reset,
btn_next,
btn_rules,
show_rules,
widgets,
counter_pieces,
counter_moves,
btn_generate,
label_pieces,
label_max_age,
label_heading,
..Default::default()
}
}
fn add_widget(widgets: &mut HashMap<WidgetId, Widget>, w: Widget) -> WidgetId {
let id = rand::rand() as u64;
let widget_id = WidgetId(id);
widgets.insert(widget_id, w);
widget_id
}
+69 -59
View File
@@ -1,6 +1,6 @@
use super::{Game, constants}; use super::{Game, constants};
use crate::{ use crate::{
game::{self, GameMode, w_board, w_button, w_counter, w_label}, game::{self, GameMode},
widgets::*, widgets::*,
}; };
use macroquad::{math, prelude::*}; use macroquad::{math, prelude::*};
@@ -33,92 +33,102 @@ impl Game {
heading_text_dims.width, heading_text_dims.width,
heading_text_dims.height, heading_text_dims.height,
); );
w_label!(self, label_heading).initialize_drawables( w_label!(self, label_heading).place(heading_rect, heading_font_size, &self.resources);
heading_rect,
heading_font_size,
&self.resources,
);
let board_rect = Rect::new(board_x, board_y, board_width, board_width); let board_rect = Rect::new(board_x, board_y, board_width, board_width);
w_board!(self, board).initialize_drawables( w_board!(self, board).place(square_width, board_rect, heading_font_size * 0.4);
square_width,
board_rect,
heading_font_size * 0.4,
);
// Buttons // Buttons
let btn_h = constants::BUTTON_HEIGHT_MULTIPLIER * min_dimension; let btn_h = constants::BUTTON_HEIGHT_MULTIPLIER * min_dimension;
let btn_w = constants::BUTTON_WIDTH_MULTIPLIER * board_width; let btn_w = constants::BUTTON_WIDTH_MULTIPLIER * board_width;
let btn_font_size = heading_font_size * 0.3;
// Bottom row // Bottom row
let bottom_row_y = let bottom_row_y =
board_width + board_y + constants::BOTTOM_ROW_OFFSET_MULTIPLIER * square_width; board_width + board_y + constants::BOTTOM_ROW_OFFSET_MULTIPLIER * square_width;
w_button!(self, btn_rules).place(Rect::new( w_button!(self, btn_rules).place(
board_x + (square_width - btn_w) / 2., Rect::new(
bottom_row_y, board_x + (square_width - btn_w) / 2.,
btn_w, bottom_row_y,
btn_h, btn_w,
)); btn_h,
),
FontSize::Fixed(btn_font_size),
);
w_button!(self, btn_reset).place(Rect::new( w_button!(self, btn_reset).place(
board_x + square_width + (square_width - btn_w) / 2., Rect::new(
bottom_row_y, board_x + square_width + (square_width - btn_w) / 2.,
btn_w, bottom_row_y,
btn_h, btn_w,
)); btn_h,
),
FontSize::Fixed(btn_font_size),
);
let btn_next_x_offset = (board_width - square_width) + (square_width - btn_w) / 2.; let btn_next_x_offset = (board_width - square_width) + (square_width - btn_w) / 2.;
w_button!(self, btn_next).place(Rect::new( w_button!(self, btn_next).place(
board_x + btn_next_x_offset, Rect::new(board_x + btn_next_x_offset, bottom_row_y, btn_w, btn_h),
bottom_row_y, FontSize::Fixed(btn_font_size),
btn_w, );
btn_h,
));
// Right column // Right column
let right_column_x = let right_column_x =
board_x + board_width + constants::RIGHT_COLUMN_OFFSET_MULTIPLIER * square_width; board_x + board_width + constants::RIGHT_COLUMN_OFFSET_MULTIPLIER * square_width;
self.get_game_mode_button(&GameMode::Easy).place(Rect::new( self.get_game_mode_button(&GameMode::Easy).place(
right_column_x, Rect::new(
board_y + (square_width - btn_h) / 2., right_column_x,
btn_w, board_y + (square_width - btn_h) / 2.,
btn_h, btn_w,
)); btn_h,
),
FontSize::Fixed(btn_font_size),
);
self.get_game_mode_button(&GameMode::Medium) self.get_game_mode_button(&GameMode::Medium).place(
.place(Rect::new( Rect::new(
right_column_x, right_column_x,
board_y + square_width + (square_width - btn_h) / 2., board_y + square_width + (square_width - btn_h) / 2.,
btn_w, btn_w,
btn_h, btn_h,
)); ),
FontSize::Fixed(btn_font_size),
);
self.get_game_mode_button(&GameMode::Hard).place(Rect::new( self.get_game_mode_button(&GameMode::Hard).place(
right_column_x, Rect::new(
board_y + 2. * square_width + (square_width - btn_h) / 2., right_column_x,
btn_w, board_y + 2. * square_width + (square_width - btn_h) / 2.,
btn_h, btn_w,
)); btn_h,
),
FontSize::Fixed(btn_font_size),
);
self.get_game_mode_button(&GameMode::Custom) self.get_game_mode_button(&GameMode::Custom).place(
.place(Rect::new( Rect::new(
right_column_x, right_column_x,
board_y + 3. * square_width + (square_width - btn_h) / 2., board_y + 3. * square_width + (square_width - btn_h) / 2.,
btn_w, btn_w,
btn_h, btn_h,
)); ),
FontSize::Fixed(btn_font_size),
);
// Right column 2 // Right column 2
let right_column_2_x = let right_column_2_x =
right_column_x + btn_w + constants::RIGHT_COLUMN_OFFSET_MULTIPLIER * square_width; right_column_x + btn_w + constants::RIGHT_COLUMN_OFFSET_MULTIPLIER * square_width;
w_button!(self, btn_generate).place(Rect::new( w_button!(self, btn_generate).place(
right_column_2_x, Rect::new(
board_y + board_width - square_width + (square_width - btn_h) / 2., right_column_2_x,
2. * btn_w, board_y + board_width - square_width + (square_width - btn_h) / 2.,
btn_h, 2. * btn_w,
)); btn_h,
),
FontSize::Fixed(btn_font_size),
);
w_label!(self, label_pieces).initialize_drawables( w_label!(self, label_pieces).place(
Rect::new( Rect::new(
right_column_2_x, right_column_2_x,
board_y + (square_width + btn_h) / 2., board_y + (square_width + btn_h) / 2.,
@@ -129,20 +139,20 @@ impl Game {
&self.resources, &self.resources,
); );
w_counter!(self, counter_pieces).initialize_drawables( w_counter!(self, counter_pieces).place(
Rect::new( Rect::new(
right_column_2_x, right_column_2_x,
board_y + square_width + (2. * square_width - 2. * btn_h) / 2., board_y + square_width + (2. * square_width - 2. * btn_h) / 2.,
btn_w, btn_w,
2. * btn_h, 2. * btn_h,
), ),
30., 0.50 * btn_h,
&self.resources, &self.resources,
); );
// Right column 3 // Right column 3
let right_column_3_x = right_column_2_x + btn_w; let right_column_3_x = right_column_2_x + btn_w;
w_label!(self, label_max_age).initialize_drawables( w_label!(self, label_max_age).place(
Rect::new( Rect::new(
right_column_2_x + btn_w, right_column_2_x + btn_w,
board_y + (square_width + btn_h) / 2., board_y + (square_width + btn_h) / 2.,
@@ -152,14 +162,14 @@ impl Game {
0.30 * btn_h, 0.30 * btn_h,
&self.resources, &self.resources,
); );
w_counter!(self, counter_moves).initialize_drawables( w_counter!(self, counter_moves).place(
Rect::new( Rect::new(
right_column_3_x, right_column_3_x,
board_y + square_width + (2. * square_width - 2. * btn_h) / 2., board_y + square_width + (2. * square_width - 2. * btn_h) / 2.,
btn_w, btn_w,
2. * btn_h, 2. * btn_h,
), ),
30., 0.50 * btn_h,
&self.resources, &self.resources,
); );
} }
+174 -69
View File
@@ -1,7 +1,9 @@
use std::collections::HashMap;
use super::{Game, GameMode, Settings, constants}; use super::{Game, GameMode, Settings, constants};
use crate::{ use crate::{
game::{self, Command, w_board, w_button, w_counter}, game::{self, Command},
resources::*, resources::{self, *},
widgets::*, widgets::*,
}; };
use macroquad::prelude::*; use macroquad::prelude::*;
@@ -11,80 +13,175 @@ use sol_lib::{
}; };
impl Game { impl Game {
pub async fn new() -> Self {
let resources = resources::init().await;
let game_mode = GameMode::Medium;
let settings = Settings {
volume: constants::VOLUME,
max_age: 3,
max_pieces: 2,
..Default::default()
};
let puzzle = generate_new_puzzle(game_mode, &settings);
let game_mode = GameMode::Medium;
let show_rules = false;
let mut widgets = HashMap::new();
let w_label_heading = LabelWidget::new(constants::HEADING_TEXT);
let label_heading = add_widget(&mut widgets, Widget::Label(w_label_heading));
let w_label_pieces = LabelWidget::new(constants::PIECES_LABEL_TEXT);
let label_pieces = add_widget(&mut widgets, Widget::Label(w_label_pieces));
let w_label_max_age = LabelWidget::new(constants::AGE_LABEL_TEXT);
let label_max_age = add_widget(&mut widgets, Widget::Label(w_label_max_age));
let mut w_counter_pieces =
CounterWidget::new(2, 7, settings.max_pieces, false, UiColor::Yellow);
w_counter_pieces.on_increment = |c| Command::UpdateMaxPieceCount(c.current);
w_counter_pieces.on_decrement = |c| Command::UpdateMaxPieceCount(c.current);
let counter_pieces = add_widget(&mut widgets, Widget::Counter(w_counter_pieces));
let mut w_counter_moves =
CounterWidget::new(1, 7, settings.max_age, false, UiColor::Yellow);
w_counter_moves.on_increment = |c| Command::UpdateMaxAge(c.current);
w_counter_moves.on_decrement = |c| Command::UpdateMaxAge(c.current);
let counter_moves = add_widget(&mut widgets, Widget::Counter(w_counter_moves));
let mut w_btn_reset = ButtonWidget::new(
true,
UiColor::Yellow,
constants::RESET_BUTTON_TEXT,
SoundKind::Button,
);
w_btn_reset.on_click = |_| Command::ResetBoard;
let btn_reset = add_widget(&mut widgets, Widget::Button(w_btn_reset));
let mut w_btn_next = ButtonWidget::new(
false,
UiColor::Green,
constants::NEXT_BUTTON_TEXT,
SoundKind::Button,
);
w_btn_next.on_click = |_| Command::NextPuzzle;
let btn_next = add_widget(&mut widgets, Widget::Button(w_btn_next));
let mut w_btn_rules = ButtonWidget::new(
true,
UiColor::Brown,
constants::RULES_BUTTON_TEXT,
SoundKind::Button,
);
w_btn_rules.on_click = |_| Command::ToggleRules;
let btn_rules = add_widget(&mut widgets, Widget::Button(w_btn_rules));
let mut w_btn_generate = ButtonWidget::new(
false,
UiColor::Pink,
constants::GENERATE_BUTTON_TEXT,
SoundKind::Button,
);
w_btn_generate.on_click = |_| Command::NewCustomPuzzle;
let btn_generate = add_widget(&mut widgets, Widget::Button(w_btn_generate));
let mut game_mode_btns = HashMap::new();
let mut w_game_mode_easy_btn = ButtonWidget::new(
true,
UiColor::Yellow,
constants::EASY_BUTTON_TEXT,
SoundKind::Mode,
);
w_game_mode_easy_btn.on_click = |_| Command::SwitchGameMode(GameMode::Easy);
game_mode_btns.insert(
GameMode::Easy,
add_widget(&mut widgets, Widget::Button(w_game_mode_easy_btn)),
);
let mut w_game_mode_medium_btn = ButtonWidget::new(
true,
UiColor::Yellow,
constants::MEDIUM_BUTTON_TEXT,
SoundKind::Mode,
);
w_game_mode_medium_btn.on_click = |_| Command::SwitchGameMode(GameMode::Medium);
game_mode_btns.insert(
GameMode::Medium,
add_widget(&mut widgets, Widget::Button(w_game_mode_medium_btn)),
);
let mut w_game_mode_hard_btn = ButtonWidget::new(
true,
UiColor::Yellow,
constants::HARD_BUTTON_TEXT,
SoundKind::Mode,
);
w_game_mode_hard_btn.on_click = |_| Command::SwitchGameMode(GameMode::Hard);
game_mode_btns.insert(
GameMode::Hard,
add_widget(&mut widgets, Widget::Button(w_game_mode_hard_btn)),
);
let mut w_game_mode_custom_btn = ButtonWidget::new(
true,
UiColor::Blue,
constants::CUSTOM_BUTTON_TEXT,
SoundKind::Mode,
);
w_game_mode_custom_btn.on_click = |_| Command::SwitchGameMode(GameMode::Custom);
game_mode_btns.insert(
GameMode::Custom,
add_widget(&mut widgets, Widget::Button(w_game_mode_custom_btn)),
);
let active_game_mode_btn = game_mode_btns.get(&game_mode).unwrap();
game::button(&mut widgets, *active_game_mode_btn).is_active = false;
let mut w_board = BoardWidget::new(puzzle.board.size, puzzle.board.clone());
w_board.on_board_state_changed = |s| Command::UpdateBoard(s.new_board_state);
let board = add_widget(&mut widgets, Widget::Board(w_board));
let mut w_controls = ControlsWidget::new();
w_controls.add(KeyCode::Q, Command::Quit);
w_controls.add(KeyCode::Escape, Command::HideRules);
w_controls.add(KeyCode::D, Command::ToggleDebug);
add_widget(&mut widgets, Widget::Controls(w_controls));
Game {
puzzle,
board,
resources,
settings,
game_mode,
game_mode_btns,
btn_reset,
btn_next,
btn_rules,
show_rules,
widgets,
counter_pieces,
counter_moves,
btn_generate,
label_pieces,
label_max_age,
label_heading,
..Default::default()
}
}
pub fn update(&mut self, interaction: Interaction) { pub fn update(&mut self, interaction: Interaction) {
let command = self.get_command(interaction); let command = self.map_to_command(interaction);
self.update_state(command); self.update_state(command);
} }
fn get_command(&mut self, interaction: Interaction) -> Command { fn map_to_command(&mut self, interaction: Interaction) -> Command {
for (_, widget) in &mut self.widgets { for (_, widget) in &mut self.widgets {
let cmd = widget.update(&interaction, &self.resources, &self.settings); if let Some(command) = widget.update(&interaction, &self.resources, &self.settings) {
let ret_cmd = match cmd { return command;
Command::NoInteraction => false,
_ => true,
};
if ret_cmd {
return cmd;
} }
} }
let board_interaction =
w_board!(self, board).handle_input(&interaction, &self.resources, &self.settings);
if let Some(new_board_state) = board_interaction.new_board_state {
return Command::UpdateBoard(new_board_state);
}
if w_button!(self, btn_reset)
.handle_input(&interaction, &self.resources, &self.settings)
.clicked
{
return Command::ResetBoard;
}
if w_button!(self, btn_next)
.handle_input(&interaction, &self.resources, &self.settings)
.clicked
{
return Command::NextPuzzle;
}
for (mode, btn) in &mut self.game_mode_btns {
if game::button(&mut self.widgets, *btn)
.handle_input(&interaction, &self.resources, &self.settings)
.clicked
{
return Command::SwitchGameMode(*mode);
}
}
if w_button!(self, btn_rules)
.handle_input(&interaction, &self.resources, &self.settings)
.clicked
{
return Command::ToggleRules;
}
if w_button!(self, btn_generate)
.handle_input(&interaction, &self.resources, &self.settings)
.clicked
{
return Command::NewCustomPuzzle;
}
if interaction.keys_released.contains(&KeyCode::Escape) {
return Command::HideRules;
}
if interaction.keys_released.contains(&KeyCode::D) {
return Command::ToggleDebug;
}
if interaction.keys_released.contains(&KeyCode::Q) {
return Command::Quit;
}
return Command::NoInteraction; return Command::NoInteraction;
} }
@@ -177,7 +274,15 @@ impl Game {
} }
} }
pub fn generate_new_puzzle(mode: GameMode, settings: &Settings) -> Puzzle { fn add_widget(widgets: &mut HashMap<WidgetId, Widget>, w: Widget) -> WidgetId {
let id = rand::rand() as u64;
let widget_id = WidgetId(id);
widgets.insert(widget_id, w);
widget_id
}
fn generate_new_puzzle(mode: GameMode, settings: &Settings) -> Puzzle {
let piece_count = match mode { let piece_count = match mode {
GameMode::Easy => 3, GameMode::Easy => 3,
GameMode::Medium => 5, GameMode::Medium => 5,
+2 -2
View File
@@ -9,7 +9,7 @@ use miniquad::date;
use game::constants; use game::constants;
use crate::widgets::Interaction; use crate::{game::Game, widgets::Interaction};
fn window_conf() -> Conf { fn window_conf() -> Conf {
let window_title = { let window_title = {
@@ -31,7 +31,7 @@ fn window_conf() -> Conf {
async fn main() { async fn main() {
rand::srand(date::now() as u64); rand::srand(date::now() as u64);
let background_color = Color::from_rgba(196, 195, 208, 255); let background_color = Color::from_rgba(196, 195, 208, 255);
let mut game = game::new_game().await; let mut game = Game::new().await;
loop { loop {
clear_background(background_color); clear_background(background_color);
+134 -8
View File
@@ -1,16 +1,16 @@
use std::collections::{HashMap, HashSet};
mod board; mod board;
use std::collections::HashSet; mod button;
mod controls;
mod counter;
mod label;
pub use board::*; pub use board::*;
mod button;
pub use button::*; pub use button::*;
pub use controls::*;
mod label;
pub use label::*;
mod counter;
pub use counter::*; pub use counter::*;
pub use label::*;
use macroquad::{ use macroquad::{
audio::{self, Sound}, audio::{self, Sound},
@@ -18,6 +18,11 @@ use macroquad::{
}; };
use quad_snd::PlaySoundParams; use quad_snd::PlaySoundParams;
use crate::{
game::{Command, Settings},
resources::Resources,
};
pub struct Interaction { pub struct Interaction {
pub mouse_pos: Circle, pub mouse_pos: Circle,
pub keys_released: HashSet<KeyCode>, pub keys_released: HashSet<KeyCode>,
@@ -25,6 +30,127 @@ pub struct Interaction {
pub mouse_released: HashSet<MouseButton>, pub mouse_released: HashSet<MouseButton>,
} }
#[derive(Eq, PartialEq, Copy, Clone, Hash, Default)]
pub struct WidgetId(pub u64);
pub enum Widget {
Label(LabelWidget),
Counter(CounterWidget),
Button(ButtonWidget),
Board(BoardWidget),
Controls(ControlsWidget),
}
impl Widget {
pub fn draw(&mut self, resources: &Resources, settings: &Settings) {
match self {
Widget::Label(label) => label.draw(&resources),
Widget::Counter(counter) => counter.draw(&resources),
Widget::Button(btn) => btn.draw(&resources),
Widget::Board(board) => board.draw(&resources, &settings),
Widget::Controls(_) => (),
}
}
pub fn update(
&mut self,
interaction: &Interaction,
resources: &Resources,
settings: &Settings,
) -> Option<Command> {
match self {
Widget::Label(_) => None,
Widget::Counter(c) => handle_counter_input(interaction, c, resources, settings),
Widget::Button(b) => handle_button_input(interaction, b, resources, settings),
Widget::Board(b) => handle_board_input(interaction, b, resources, settings),
Widget::Controls(c) => c.handle_input(interaction),
}
}
}
fn handle_board_input(
interaction: &Interaction,
b: &mut BoardWidget,
resources: &Resources,
settings: &Settings,
) -> Option<Command> {
let board_interaction = b.handle_input(interaction, resources, settings);
let Some(board_interaction) = board_interaction else {
return None;
};
Some((b.on_board_state_changed)(board_interaction))
}
fn handle_counter_input(
interaction: &Interaction,
counter: &mut CounterWidget,
resources: &Resources,
settings: &Settings,
) -> Option<Command> {
let counter_interaction = counter.handle_input(interaction, resources, settings);
let Some(interaction_type) = counter_interaction.interaction_type else {
return None;
};
let command = match interaction_type {
CounterInteractionType::Increment => (counter.on_increment)(counter_interaction),
CounterInteractionType::Decrement => (counter.on_decrement)(counter_interaction),
};
Some(command)
}
fn handle_button_input(
interaction: &Interaction,
button: &mut ButtonWidget,
resources: &Resources,
settings: &Settings,
) -> Option<Command> {
let Some(ButtonInteraction { clicked: true }) =
button.handle_input(interaction, resources, settings)
else {
return None;
};
return Some((button.on_click)(button));
}
macro_rules! get_widget_fn {
($name:ident, $ret:ty, $variant:path) => {
pub fn $name(widgets: &mut HashMap<WidgetId, Widget>, id: WidgetId) -> &mut $ret {
match widgets.get_mut(&id) {
Some(w) => match w {
$variant(v) => return v,
_ => panic!("Widget with id {} not found", id.0),
},
None => panic!("Widget with id {} not found", id.0),
}
}
};
}
get_widget_fn!(label, LabelWidget, Widget::Label);
get_widget_fn!(button, ButtonWidget, Widget::Button);
get_widget_fn!(counter, CounterWidget, Widget::Counter);
get_widget_fn!(board, BoardWidget, Widget::Board);
macro_rules! widget_accessor {
($name:ident, $fn:ident) => {
macro_rules! $name {
($this:expr, $widget_id:ident) => {
game::$fn(&mut $this.widgets, $this.$widget_id)
};
}
pub(crate) use $name;
};
}
widget_accessor!(w_button, button);
widget_accessor!(w_label, label);
widget_accessor!(w_counter, counter);
widget_accessor!(w_board, board);
#[allow(unused)] #[allow(unused)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UiColor { pub enum UiColor {
+67 -53
View File
@@ -2,7 +2,7 @@ use core::fmt;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use crate::{ use crate::{
game::{Settings, constants}, game::{Command, Settings, constants},
resources::{Resources, SoundKind}, resources::{Resources, SoundKind},
widgets::*, widgets::*,
}; };
@@ -11,12 +11,12 @@ use sol_lib::{
generator::Puzzle, generator::Puzzle,
}; };
#[derive(Default)]
pub struct BoardWidget { pub struct BoardWidget {
current_board: Board,
square_width: f32,
pub num_squares: usize, // this is a constant = 4 for now. pub num_squares: usize, // this is a constant = 4 for now.
pub show_rules: bool, pub show_rules: bool,
pub on_board_state_changed: fn(BoardInteraction) -> Command,
current_board: Board,
square_width: f32,
rules_font_size: f32, rules_font_size: f32,
board_rect: Rect, board_rect: Rect,
squares: Vec<GameSquare>, squares: Vec<GameSquare>,
@@ -24,23 +24,18 @@ pub struct BoardWidget {
} }
pub struct BoardInteraction { pub struct BoardInteraction {
pub new_board_state: Option<BoardState>, pub new_board_state: BoardState,
} }
impl BoardWidget { impl BoardWidget {
pub fn initialize_state(num_squares: usize, current_board: Board) -> BoardWidget { pub fn new(num_squares: usize, current_board: Board) -> BoardWidget {
let mut board = BoardWidget::default(); let mut board = BoardWidget::default();
board.num_squares = num_squares; board.num_squares = num_squares;
board.current_board = current_board; board.current_board = current_board;
board board
} }
pub fn initialize_drawables( pub fn place(&mut self, square_width: f32, board_rect: Rect, rules_font_size: f32) {
&mut self,
square_width: f32,
board_rect: Rect,
rules_font_size: f32,
) {
self.square_width = square_width; self.square_width = square_width;
self.board_rect = board_rect; self.board_rect = board_rect;
self.rules_font_size = rules_font_size; self.rules_font_size = rules_font_size;
@@ -169,49 +164,18 @@ impl BoardWidget {
} }
} }
fn get(&mut self, i: usize, j: usize) -> &mut GameSquare {
&mut self.squares[i * self.num_squares + j]
}
fn draw_debug(&self) {
let mut debug_lines = vec![];
let (mx, my) = mouse_position();
let hover_square = self.squares.iter().find(|s| {
let c = Circle::new(mx, my, 0.0);
if c.overlaps_rect(&s.rect) {
return true;
}
return false;
});
debug_lines.push(format!("Game State: {}", self.state));
debug_lines.push(format!("Board State: {}", self.current_board.game_state));
if let Some(hover_square) = hover_square {
debug_lines.push(format!("Hover: [ {}, {} ]", hover_square.i, hover_square.j));
}
self.add_debug_info(debug_lines);
}
fn add_debug_info(&self, lines: Vec<String>) {
let mut y = 20.0;
for line in lines {
draw_text(&line, 10.0, y, 20.0, BLACK);
y += 25.0;
}
}
pub fn handle_input( pub fn handle_input(
&mut self, &mut self,
interaction: &Interaction, interaction: &Interaction,
resources: &Resources, resources: &Resources,
settings: &Settings, settings: &Settings,
) -> BoardInteraction { ) -> Option<BoardInteraction> {
let mouse_pos = interaction.mouse_pos; let mouse_pos = interaction.mouse_pos;
if mouse_pos.overlaps_rect(&self.board_rect) == false { if mouse_pos.overlaps_rect(&self.board_rect) == false {
return BoardInteraction { return None;
new_board_state: None,
};
} }
let old_board_state = self.current_board.state;
if is_mouse_button_released(MouseButton::Left) { if is_mouse_button_released(MouseButton::Left) {
let current_state = self.state.clone(); let current_state = self.state.clone();
let new_state = match current_state { let new_state = match current_state {
@@ -247,10 +211,44 @@ impl BoardWidget {
self.state = new_state; self.state = new_state;
} }
let board_state = &self.current_board.game_state; if old_board_state == self.current_board.state {
return BoardInteraction { return None;
new_board_state: Some(*board_state), }
};
let board_state = self.current_board.state;
return Some(BoardInteraction {
new_board_state: board_state,
});
}
fn get(&mut self, i: usize, j: usize) -> &mut GameSquare {
&mut self.squares[i * self.num_squares + j]
}
fn draw_debug(&self) {
let mut debug_lines = vec![];
let (mx, my) = mouse_position();
let hover_square = self.squares.iter().find(|s| {
let c = Circle::new(mx, my, 0.0);
if c.overlaps_rect(&s.rect) {
return true;
}
return false;
});
debug_lines.push(format!("Game State: {}", self.state));
debug_lines.push(format!("Board State: {}", self.current_board.state));
if let Some(hover_square) = hover_square {
debug_lines.push(format!("Hover: [ {}, {} ]", hover_square.i, hover_square.j));
}
self.add_debug_info(debug_lines);
}
fn add_debug_info(&self, lines: Vec<String>) {
let mut y = 20.0;
for line in lines {
draw_text(&line, 10.0, y, 20.0, BLACK);
y += 25.0;
}
} }
pub fn reset(&mut self, puzzle: &Puzzle) { pub fn reset(&mut self, puzzle: &Puzzle) {
@@ -337,11 +335,11 @@ impl BoardWidget {
let m = m.expect("legal move should be found"); let m = m.expect("legal move should be found");
self.current_board.make_move(m.clone()); self.current_board.make_move(m.clone());
if self.current_board.game_state == BoardState::Won if self.current_board.state == BoardState::Won
|| self.current_board.game_state == BoardState::Lost || self.current_board.state == BoardState::Lost
{ {
self.reset_squares(); self.reset_squares();
if self.current_board.game_state == BoardState::Won { if self.current_board.state == BoardState::Won {
play_sound_once(resources.sound(&SoundKind::Win), settings.volume); play_sound_once(resources.sound(&SoundKind::Win), settings.volume);
} else { } else {
play_sound_once(resources.sound(&SoundKind::Loss), settings.volume); play_sound_once(resources.sound(&SoundKind::Loss), settings.volume);
@@ -431,3 +429,19 @@ struct PieceDrawTextureParams<'a> {
texture: &'a Texture2D, texture: &'a Texture2D,
draw_text_params: DrawTextureParams, draw_text_params: DrawTextureParams,
} }
impl Default for BoardWidget {
fn default() -> Self {
Self {
on_board_state_changed: |_| Command::NoInteraction,
num_squares: Default::default(),
show_rules: Default::default(),
current_board: Default::default(),
square_width: Default::default(),
rules_font_size: Default::default(),
board_rect: Default::default(),
squares: Default::default(),
state: Default::default(),
}
}
}
+33 -21
View File
@@ -12,6 +12,7 @@ pub struct ButtonWidget {
rect: Rect, rect: Rect,
shadow_width: f32, shadow_width: f32,
sound: SoundKind, sound: SoundKind,
font_size: FontSize,
pub on_click: fn(&mut ButtonWidget) -> Command, pub on_click: fn(&mut ButtonWidget) -> Command,
} }
@@ -20,15 +21,17 @@ pub struct ButtonInteraction {
} }
impl ButtonInteraction { impl ButtonInteraction {
fn not_clicked() -> Self {
Self { clicked: false }
}
fn clicked() -> Self { fn clicked() -> Self {
Self { clicked: true } Self { clicked: true }
} }
} }
#[derive(Copy, Clone)]
pub enum FontSize {
Fixed(f32),
Fill,
}
impl ButtonWidget { impl ButtonWidget {
pub fn new(is_active: bool, color: UiColor, text: impl Into<String>, sound: SoundKind) -> Self { pub fn new(is_active: bool, color: UiColor, text: impl Into<String>, sound: SoundKind) -> Self {
Self { Self {
@@ -41,8 +44,9 @@ impl ButtonWidget {
} }
} }
pub fn place(&mut self, rect: Rect) { pub fn place(&mut self, rect: Rect, font_size: FontSize) {
self.rect = rect; self.rect = rect;
self.font_size = font_size;
} }
pub fn draw(&self, resources: &Resources) { pub fn draw(&self, resources: &Resources) {
@@ -86,19 +90,26 @@ impl ButtonWidget {
}; };
let font = resources.font(); let font = resources.font();
let target_w = self.rect.w * 0.9; let font_size = match self.font_size {
let target_h = self.rect.h; FontSize::Fixed(s) => s as u16,
let mut font_size = 20; FontSize::Fill => {
let mut dims = measure_text(&text, Some(&font), font_size, 1.0); let target_w = self.rect.w * 0.9;
loop { let target_h = self.rect.h;
if dims.width <= target_w && dims.height <= target_h { let mut font_size = 22;
break; loop {
font_size -= 2;
let dims = measure_text(&text, Some(&font), font_size as u16, 1.0);
if dims.width <= target_w && dims.height <= target_h {
break;
}
}
font_size
} }
};
font_size -= 2; let dims = measure_text(&text, Some(&font), font_size, 1.0);
dims = measure_text(&text, Some(&font), font_size as u16, 1.0);
}
let button_draw_offset = self.get_button_draw_offset(); let button_draw_offset = self.get_button_draw_offset();
let text_params = TextParams { let text_params = TextParams {
font_size, font_size,
@@ -130,17 +141,17 @@ impl ButtonWidget {
interaction: &Interaction, interaction: &Interaction,
resources: &Resources, resources: &Resources,
settings: &Settings, settings: &Settings,
) -> ButtonInteraction { ) -> Option<ButtonInteraction> {
if !self.is_active { if !self.is_active {
self.is_down = false; self.is_down = false;
return ButtonInteraction::not_clicked(); return None;
} }
let mouse = interaction.mouse_pos; let mouse = interaction.mouse_pos;
if interaction.mouse_pressed.contains(&MouseButton::Left) { if interaction.mouse_pressed.contains(&MouseButton::Left) {
if mouse.overlaps_rect(&self.rect) { if mouse.overlaps_rect(&self.rect) {
self.is_down = true; self.is_down = true;
return ButtonInteraction::not_clicked(); return None;
} }
} }
@@ -150,13 +161,13 @@ impl ButtonWidget {
let volume = settings.volume; let volume = settings.volume;
play_sound_once(&click_sound, volume); play_sound_once(&click_sound, volume);
self.is_down = false; self.is_down = false;
return ButtonInteraction::clicked(); return Some(ButtonInteraction::clicked());
} }
self.is_down = false; self.is_down = false;
} }
ButtonInteraction::not_clicked() None
} }
} }
@@ -170,6 +181,7 @@ impl Default for ButtonWidget {
color: UiColor::Grey, color: UiColor::Grey,
text: String::new(), text: String::new(),
sound: SoundKind::Click, sound: SoundKind::Click,
font_size: FontSize::Fill,
on_click: |_| Command::NoInteraction, on_click: |_| Command::NoInteraction,
} }
} }
+31
View File
@@ -0,0 +1,31 @@
use std::collections::HashMap;
use macroquad::input::KeyCode;
use crate::{game::Command, widgets::Interaction};
pub struct ControlsWidget {
key_release_bindings: HashMap<KeyCode, Command>,
}
impl ControlsWidget {
pub fn new() -> Self {
Self {
key_release_bindings: HashMap::new(),
}
}
pub fn add(&mut self, key: KeyCode, command: Command) {
self.key_release_bindings.insert(key, command);
}
pub fn handle_input(&self, interaction: &Interaction) -> Option<Command> {
for (key, command) in &self.key_release_bindings {
if interaction.keys_released.contains(&key) {
return Some(*command);
}
}
None
}
}
+16 -36
View File
@@ -77,12 +77,7 @@ impl CounterWidget {
self.decrement.is_active = is_active; self.decrement.is_active = is_active;
} }
pub fn initialize_drawables( pub fn place(&mut self, total_area: Rect, font_size: f32, resources: &Resources) {
&mut self,
total_area: Rect,
font_size: f32,
resources: &Resources,
) {
let margin_y = 0.05 * total_area.h; let margin_y = 0.05 * total_area.h;
let btn_h = total_area.h * 0.3; let btn_h = total_area.h * 0.3;
let btn_w = total_area.w * 0.9; let btn_w = total_area.w * 0.9;
@@ -94,10 +89,10 @@ impl CounterWidget {
self.visual_rect = Rect::new(btn_x, visual_y, btn_w, btn_h); self.visual_rect = Rect::new(btn_x, visual_y, btn_w, btn_h);
let increment_rect = Rect::new(btn_x, increment_y, btn_w, btn_h); let increment_rect = Rect::new(btn_x, increment_y, btn_w, btn_h);
self.increment.place(increment_rect); self.increment.place(increment_rect, FontSize::Fill);
let decrement_rect = Rect::new(btn_x, decrement_y, btn_w, btn_h); let decrement_rect = Rect::new(btn_x, decrement_y, btn_w, btn_h);
self.decrement.place(decrement_rect); self.decrement.place(decrement_rect, FontSize::Fill);
self.increment_dims = measure_text("+", Some(resources.font()), font_size as u16, 1.0); self.increment_dims = measure_text("+", Some(resources.font()), font_size as u16, 1.0);
self.decrement_dims = measure_text("-", Some(resources.font()), font_size as u16, 1.0); self.decrement_dims = measure_text("-", Some(resources.font()), font_size as u16, 1.0);
@@ -112,25 +107,27 @@ impl CounterWidget {
resources: &Resources, resources: &Resources,
settings: &Settings, settings: &Settings,
) -> CounterWidgetInteraction { ) -> CounterWidgetInteraction {
if self if let Some(interaction) = self
.increment .increment
.handle_input(&interaction, &resources, &settings) .handle_input(&interaction, &resources, &settings)
.clicked
{ {
if self.current < self.max { if interaction.clicked {
self.current += 1; if self.current < self.max {
return CounterWidgetInteraction::increment(self.current); self.current += 1;
return CounterWidgetInteraction::increment(self.current);
}
} }
} }
if self if let Some(interaction) = self
.decrement .decrement
.handle_input(&interaction, &resources, &settings) .handle_input(&interaction, &resources, &settings)
.clicked
{ {
if self.current > self.min { if interaction.clicked {
self.current -= 1; if self.current > self.min {
return CounterWidgetInteraction::decrement(self.current); self.current -= 1;
return CounterWidgetInteraction::decrement(self.current);
}
} }
} }
@@ -141,24 +138,7 @@ impl CounterWidget {
self.increment.draw(resources); self.increment.draw(resources);
self.decrement.draw(resources); self.decrement.draw(resources);
let font = resources.font(); let font_size = self.font_size as u16;
let mut min_dim = self.visual_rect.w.min(self.visual_rect.h);
let mut font_size = self.font_size as u16;
loop {
if min_dim <= self.visual_rect.w * 0.5 {
break;
}
let dims = measure_text(
&self.current.to_string(),
Some(&font),
font_size as u16,
1.0,
);
min_dim = dims.width.min(dims.height);
font_size -= 1;
}
draw_text_ex( draw_text_ex(
&self.current.to_string(), &self.current.to_string(),
self.visual_rect.x + (self.visual_rect.w - self.visual_dims.width) / 2., self.visual_rect.x + (self.visual_rect.w - self.visual_dims.width) / 2.,
+2 -2
View File
@@ -11,14 +11,14 @@ pub struct LabelWidget {
} }
impl LabelWidget { impl LabelWidget {
pub fn initialize_state(text: &str) -> Self { pub fn new(text: &str) -> Self {
Self { Self {
text: text.to_string(), text: text.to_string(),
..Default::default() ..Default::default()
} }
} }
pub fn initialize_drawables(&mut self, rect: Rect, font_size: f32, resources: &Resources) { pub fn place(&mut self, rect: Rect, font_size: f32, resources: &Resources) {
self.rect = rect; self.rect = rect;
self.font_size = font_size; self.font_size = font_size;
self.text_dimensions = self.text_dimensions =
+14 -14
View File
@@ -23,7 +23,7 @@ use crate::{board::piece::Piece, generator::Puzzle};
pub struct Board { pub struct Board {
pub cells: [[Option<Piece>; BOARD_SIZE]; BOARD_SIZE], pub cells: [[Option<Piece>; BOARD_SIZE]; BOARD_SIZE],
pub legal_moves: HashSet<CMove>, pub legal_moves: HashSet<CMove>,
pub game_state: BoardState, pub state: BoardState,
pub id: String, pub id: String,
pub size: usize, pub size: usize,
pieces_remaining: u8, pieces_remaining: u8,
@@ -64,7 +64,7 @@ impl Board {
legal_moves: HashSet::new(), legal_moves: HashSet::new(),
id, id,
pieces_remaining: 0, pieces_remaining: 0,
game_state: BoardState::NotStarted, state: BoardState::NotStarted,
size: BOARD_SIZE, size: BOARD_SIZE,
max_moves_per_piece: options.max_moves_per_piece, max_moves_per_piece: options.max_moves_per_piece,
} }
@@ -190,7 +190,7 @@ impl Board {
next_move: CMove, next_move: CMove,
} }
if let BoardState::Won = self.game_state { if let BoardState::Won = self.state {
return Puzzle { return Puzzle {
board: self.clone(), board: self.clone(),
solutions: vec![vec![]], solutions: vec![vec![]],
@@ -223,7 +223,7 @@ impl Board {
let (mut board, mut moves_so_far, next) = (top.board, top.moves_so_far, top.next_move); let (mut board, mut moves_so_far, next) = (top.board, top.moves_so_far, top.next_move);
board.make_move(next.clone()); board.make_move(next.clone());
match board.game_state { match board.state {
BoardState::Won => { BoardState::Won => {
moves_so_far.push(next); moves_so_far.push(next);
solutions.push(moves_so_far); solutions.push(moves_so_far);
@@ -374,7 +374,7 @@ impl Board {
} }
fn calc_game_state(&mut self) { fn calc_game_state(&mut self) {
self.game_state = if self.pieces_remaining == 0 { self.state = if self.pieces_remaining == 0 {
BoardState::NotStarted BoardState::NotStarted
} else if self.pieces_remaining == 1 { } else if self.pieces_remaining == 1 {
BoardState::Won BoardState::Won
@@ -674,7 +674,7 @@ mod tests {
#[test] #[test]
fn test_smoke_puzzle() { fn test_smoke_puzzle() {
let mut board = Board::new(); let mut board = Board::new();
assert_eq!(BoardState::NotStarted, board.game_state); assert_eq!(BoardState::NotStarted, board.state);
assert_eq!(0, board.pieces_remaining); assert_eq!(0, board.pieces_remaining);
// K . . . // K . . .
@@ -682,22 +682,22 @@ mod tests {
// . . R . // . . R .
// N . . . // N . . .
board.set(sq!("Ka4")); board.set(sq!("Ka4"));
assert_eq!(BoardState::Won, board.game_state); assert_eq!(BoardState::Won, board.state);
board.set(sq!("Pb3")); board.set(sq!("Pb3"));
board.set(sq!("Rc2")); board.set(sq!("Rc2"));
board.set(sq!("Na1")); board.set(sq!("Na1"));
assert_eq!(BoardState::InProgress, board.game_state); assert_eq!(BoardState::InProgress, board.state);
assert_eq!(4, board.pieces_remaining); assert_eq!(4, board.pieces_remaining);
assert!(board.make_move(mv!("Na1", "Rc2")).is_some()); assert!(board.make_move(mv!("Na1", "Rc2")).is_some());
assert_eq!(3, board.pieces_remaining); assert_eq!(3, board.pieces_remaining);
assert_eq!(BoardState::InProgress, board.game_state); assert_eq!(BoardState::InProgress, board.state);
assert!(board.make_move(mv!("Pb3", "Ka4")).is_some()); assert!(board.make_move(mv!("Pb3", "Ka4")).is_some());
assert_eq!(2, board.pieces_remaining); assert_eq!(2, board.pieces_remaining);
assert_eq!(BoardState::Lost, board.game_state); assert_eq!(BoardState::Lost, board.state);
// P . . . // P . . .
// . . . . // . . . .
@@ -712,12 +712,12 @@ mod tests {
// . . N . // . . N .
// P . . . // P . . .
assert_eq!(4, board.pieces_remaining); assert_eq!(4, board.pieces_remaining);
assert_eq!(BoardState::InProgress, board.game_state); assert_eq!(BoardState::InProgress, board.state);
board.make_move(mv!("Qa3", "Pa4")); board.make_move(mv!("Qa3", "Pa4"));
board.make_move(mv!("Nc2", "Pa1")); board.make_move(mv!("Nc2", "Pa1"));
assert_eq!(2, board.pieces_remaining); assert_eq!(2, board.pieces_remaining);
assert_eq!(BoardState::InProgress, board.game_state); assert_eq!(BoardState::InProgress, board.state);
// Q . . . // Q . . .
// . . . . // . . . .
@@ -725,7 +725,7 @@ mod tests {
// N . . . // N . . .
board.make_move(mv!("Qa4", "Na1")); board.make_move(mv!("Qa4", "Na1"));
assert_eq!(1, board.pieces_remaining); assert_eq!(1, board.pieces_remaining);
assert_eq!(BoardState::Won, board.game_state); assert_eq!(BoardState::Won, board.state);
} }
#[test] #[test]
@@ -775,7 +775,7 @@ mod tests {
solution solution
.into_iter() .into_iter()
.for_each(|m| assert!(board.make_move(m).is_some())); .for_each(|m| assert!(board.make_move(m).is_some()));
assert_eq!(BoardState::Won, board.game_state); assert_eq!(BoardState::Won, board.state);
} }
} }