move to retained mode complete
This commit is contained in:
Binary file not shown.
+2
-276
@@ -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
|
|
||||||
}
|
|
||||||
|
|||||||
+49
-39
@@ -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(
|
||||||
|
Rect::new(
|
||||||
board_x + (square_width - btn_w) / 2.,
|
board_x + (square_width - btn_w) / 2.,
|
||||||
bottom_row_y,
|
bottom_row_y,
|
||||||
btn_w,
|
btn_w,
|
||||||
btn_h,
|
btn_h,
|
||||||
));
|
),
|
||||||
|
FontSize::Fixed(btn_font_size),
|
||||||
|
);
|
||||||
|
|
||||||
w_button!(self, btn_reset).place(Rect::new(
|
w_button!(self, btn_reset).place(
|
||||||
|
Rect::new(
|
||||||
board_x + square_width + (square_width - btn_w) / 2.,
|
board_x + square_width + (square_width - btn_w) / 2.,
|
||||||
bottom_row_y,
|
bottom_row_y,
|
||||||
btn_w,
|
btn_w,
|
||||||
btn_h,
|
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(
|
||||||
|
Rect::new(
|
||||||
right_column_x,
|
right_column_x,
|
||||||
board_y + (square_width - btn_h) / 2.,
|
board_y + (square_width - btn_h) / 2.,
|
||||||
btn_w,
|
btn_w,
|
||||||
btn_h,
|
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(
|
||||||
|
Rect::new(
|
||||||
right_column_x,
|
right_column_x,
|
||||||
board_y + 2. * square_width + (square_width - btn_h) / 2.,
|
board_y + 2. * 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::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(
|
||||||
|
Rect::new(
|
||||||
right_column_2_x,
|
right_column_2_x,
|
||||||
board_y + board_width - square_width + (square_width - btn_h) / 2.,
|
board_y + board_width - square_width + (square_width - btn_h) / 2.,
|
||||||
2. * btn_w,
|
2. * btn_w,
|
||||||
btn_h,
|
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
@@ -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
@@ -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
@@ -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
@@ -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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+27
-15
@@ -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 font_size = match self.font_size {
|
||||||
|
FontSize::Fixed(s) => s as u16,
|
||||||
|
FontSize::Fill => {
|
||||||
let target_w = self.rect.w * 0.9;
|
let target_w = self.rect.w * 0.9;
|
||||||
let target_h = self.rect.h;
|
let target_h = self.rect.h;
|
||||||
let mut font_size = 20;
|
let mut font_size = 22;
|
||||||
let mut dims = measure_text(&text, Some(&font), font_size, 1.0);
|
|
||||||
loop {
|
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 {
|
if dims.width <= target_w && dims.height <= target_h {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
font_size -= 2;
|
|
||||||
dims = measure_text(&text, Some(&font), font_size as u16, 1.0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
font_size
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let dims = measure_text(&text, Some(&font), font_size, 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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
-30
@@ -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,27 +107,29 @@ 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 interaction.clicked {
|
||||||
if self.current < self.max {
|
if self.current < self.max {
|
||||||
self.current += 1;
|
self.current += 1;
|
||||||
return CounterWidgetInteraction::increment(self.current);
|
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 interaction.clicked {
|
||||||
if self.current > self.min {
|
if self.current > self.min {
|
||||||
self.current -= 1;
|
self.current -= 1;
|
||||||
return CounterWidgetInteraction::decrement(self.current);
|
return CounterWidgetInteraction::decrement(self.current);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return CounterWidgetInteraction::not_interacted(self.current);
|
return CounterWidgetInteraction::not_interacted(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.,
|
||||||
|
|||||||
@@ -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
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user