add age
This commit is contained in:
+2
-1
@@ -72,7 +72,8 @@ fn generate_puzzle(num_pieces: Option<u32>, num_solutions: Option<u32>) -> Optio
|
|||||||
"Generating a puzzle with {} pieces with a maximum of {} solutions",
|
"Generating a puzzle with {} pieces with a maximum of {} solutions",
|
||||||
num_pieces, num_solutions
|
num_pieces, num_solutions
|
||||||
);
|
);
|
||||||
let gen_result = generator::generate_weighted_random(num_pieces, num_solutions, &RandRngImpl);
|
let gen_result =
|
||||||
|
generator::generate_weighted_random(num_pieces, num_solutions, num_pieces, &RandRngImpl);
|
||||||
gen_result.print_stats();
|
gen_result.print_stats();
|
||||||
|
|
||||||
let Some(puzzle) = gen_result.puzzle() else {
|
let Some(puzzle) = gen_result.puzzle() else {
|
||||||
|
|||||||
+17
-8
@@ -2,6 +2,8 @@ pub mod constants;
|
|||||||
mod draw;
|
mod draw;
|
||||||
mod logic;
|
mod logic;
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{resources::Resources, widgets::*};
|
use crate::{resources::Resources, widgets::*};
|
||||||
use macroquad::prelude::*;
|
use macroquad::prelude::*;
|
||||||
use sol_lib::generator::Puzzle;
|
use sol_lib::generator::Puzzle;
|
||||||
@@ -15,30 +17,36 @@ pub struct Game {
|
|||||||
window_width: f32,
|
window_width: f32,
|
||||||
|
|
||||||
puzzle: Puzzle,
|
puzzle: Puzzle,
|
||||||
id_text: IdTextInput,
|
id_text_btn: ButtonWidget,
|
||||||
reset_btn: ButtonWidget,
|
reset_btn: ButtonWidget,
|
||||||
next_btn: ButtonWidget,
|
next_btn: ButtonWidget,
|
||||||
|
|
||||||
|
show_rules: bool,
|
||||||
|
rules_font_size: f32,
|
||||||
board: BoardWidget,
|
board: BoardWidget,
|
||||||
|
|
||||||
heading_rect: Rect,
|
|
||||||
heading_font_size: f32,
|
|
||||||
heading_text: String,
|
heading_text: String,
|
||||||
|
heading: LabelWidget,
|
||||||
|
|
||||||
show_rules: bool,
|
rules_btn_text: String,
|
||||||
rules_text: String,
|
|
||||||
rules_btn: ButtonWidget,
|
rules_btn: ButtonWidget,
|
||||||
|
|
||||||
game_mode: GameMode,
|
game_mode: GameMode,
|
||||||
easy_btn: ButtonWidget,
|
game_mode_btns: HashMap<GameMode, ButtonWidget>,
|
||||||
medium_btn: ButtonWidget,
|
|
||||||
hard_btn: ButtonWidget,
|
pieces_label: LabelWidget,
|
||||||
|
max_age_label: LabelWidget,
|
||||||
|
pieces_counter: CounterWidget,
|
||||||
|
max_age_counter: CounterWidget,
|
||||||
|
generate_btn: ButtonWidget,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct GameSettings {
|
pub struct GameSettings {
|
||||||
pub volume: f32,
|
pub volume: f32,
|
||||||
|
pub max_moves_per_piece: u32,
|
||||||
pub debug: bool,
|
pub debug: bool,
|
||||||
|
pub num_pieces: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||||
@@ -46,6 +54,7 @@ pub enum GameMode {
|
|||||||
Easy,
|
Easy,
|
||||||
Medium,
|
Medium,
|
||||||
Hard,
|
Hard,
|
||||||
|
Custom,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for GameMode {
|
impl Default for GameMode {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ pub const SCREEN_WIDTH_MIN: f32 = 200.0;
|
|||||||
pub const SCREEN_WIDTH_MAX: f32 = 10000.0;
|
pub const SCREEN_WIDTH_MAX: f32 = 10000.0;
|
||||||
|
|
||||||
// How big the square should be relative to the screen size, between 0 and 1
|
// How big the square should be relative to the screen size, between 0 and 1
|
||||||
pub const BOARD_SQUARE_WIDTH_MULTIPLIER: f32 = 0.15;
|
pub const BOARD_SQUARE_WIDTH_MULTIPLIER: f32 = 0.11;
|
||||||
|
|
||||||
pub const BOARD_SHADOW_MULTIPLIER: f32 = 0.1;
|
pub const BOARD_SHADOW_MULTIPLIER: f32 = 0.1;
|
||||||
|
|
||||||
@@ -22,19 +22,24 @@ pub const BUTTON_HEIGHT_MULTIPLIER: f32 = 0.08;
|
|||||||
pub const BUTTON_WIDTH_MULTIPLIER: f32 = 0.20;
|
pub const BUTTON_WIDTH_MULTIPLIER: f32 = 0.20;
|
||||||
|
|
||||||
// Gap between the bottom of the board and the first row of buttons
|
// Gap between the bottom of the board and the first row of buttons
|
||||||
pub const BOTTOM_BUTTON_ROW_OFFSET_MULTIPLIER: f32 = 0.3;
|
pub const BOTTOM_ROW_OFFSET_MULTIPLIER: f32 = 0.3;
|
||||||
|
|
||||||
// Gap between the right of the board and the first row of buttons
|
// Gap between the right of the board and the first row of buttons
|
||||||
pub const BOTTOM_RIGHT_ROW_OFFSET_MULTIPLIER: f32 = 0.3;
|
pub const RIGHT_COLUMN_OFFSET_MULTIPLIER: f32 = 0.3;
|
||||||
|
|
||||||
// Gap between the top of the board and the heading
|
// Gap between the top of the board and the heading
|
||||||
pub const TOP_HEADING_OFFSET_MULTIPLIER: f32 = 0.5;
|
pub const TOP_HEADING_OFFSET_MULTIPLIER: f32 = 0.5;
|
||||||
|
|
||||||
pub const HEADING_TEXT: &str = "Solitaire Chess";
|
pub const HEADING_TEXT: &str = "Solitaire Chess";
|
||||||
pub const RESET_BUTTON_TEXT: &str = "R";
|
pub const RESET_BUTTON_TEXT: &str = "RESET";
|
||||||
pub const NEXT_BUTTON_TEXT: &str = "N";
|
pub const NEXT_BUTTON_TEXT: &str = "NEXT";
|
||||||
pub const RULES_BUTTON_TEXT: &str = "RULES";
|
pub const RULES_BUTTON_TEXT: &str = "RULES";
|
||||||
pub const RULES_BUTTON_ALT_TEXT: &str = "CLOSE";
|
pub const RULES_BUTTON_ALT_TEXT: &str = "CLOSE";
|
||||||
pub const EASY_BUTTON_TEXT: &str = "EASY";
|
pub const EASY_BUTTON_TEXT: &str = "EASY";
|
||||||
pub const MEDIUM_BUTTON_TEXT: &str = "MEDIUM";
|
pub const MEDIUM_BUTTON_TEXT: &str = "MEDIUM";
|
||||||
pub const HARD_BUTTON_TEXT: &str = "HARD";
|
pub const HARD_BUTTON_TEXT: &str = "HARD";
|
||||||
|
pub const CUSTOM_BUTTON_TEXT: &str = "CUSTOM";
|
||||||
|
pub const GENERATE_BUTTON_TEXT: &str = "GENERATE";
|
||||||
|
pub const PIECES_LABEL_TEXT: &str = "Pieces";
|
||||||
|
pub const AGE_LABEL_TEXT: &str = " Age";
|
||||||
|
|
||||||
|
|||||||
+159
-70
@@ -1,53 +1,66 @@
|
|||||||
use super::{Game, constants};
|
use super::{Game, constants};
|
||||||
use crate::widgets::*;
|
use crate::{game::GameMode, widgets::*};
|
||||||
use macroquad::{math, prelude::*};
|
use macroquad::{math, prelude::*};
|
||||||
|
|
||||||
impl Game {
|
impl Game {
|
||||||
fn initialize_drawables(&mut self) {
|
fn initialize_drawables(&mut self, new_width: f32, new_height: f32) {
|
||||||
|
self.window_height = new_height;
|
||||||
|
self.window_width = new_width;
|
||||||
|
|
||||||
let min_dimension = f32::min(self.window_height, self.window_width);
|
let min_dimension = f32::min(self.window_height, self.window_width);
|
||||||
let square_width = constants::BOARD_SQUARE_WIDTH_MULTIPLIER * min_dimension;
|
let square_width = constants::BOARD_SQUARE_WIDTH_MULTIPLIER * min_dimension;
|
||||||
let num_squares = self.board.num_squares;
|
let num_squares = self.board.num_squares;
|
||||||
let board_width = square_width * num_squares as f32;
|
let board_width = square_width * num_squares as f32;
|
||||||
let board_x = (self.window_width - board_width) / 2.0;
|
let board_x = (self.window_width * 0.6 - board_width) / 2.0;
|
||||||
let board_y = (self.window_height - board_width) / 2.0;
|
let board_y = (self.window_height - board_width) / 2.0;
|
||||||
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);
|
||||||
self.board.initialize_drawables(square_width, board_rect);
|
self.board.initialize_drawables(square_width, board_rect);
|
||||||
|
|
||||||
self.heading_font_size = constants::HEADING_FONT_SIZE_MULTIPLIER * min_dimension;
|
let heading_font_size = constants::HEADING_FONT_SIZE_MULTIPLIER * min_dimension;
|
||||||
|
let f = heading_font_size.floor() as u16;
|
||||||
let f = self.heading_font_size.floor() as u16;
|
let heading_text_dims = measure_text(
|
||||||
let dims = measure_text(
|
|
||||||
self.heading_text.as_str(),
|
self.heading_text.as_str(),
|
||||||
Some(&self.resources.font()),
|
Some(&self.resources.font()),
|
||||||
f,
|
f,
|
||||||
1.0,
|
1.0,
|
||||||
);
|
);
|
||||||
self.heading_rect = Rect::new(
|
let heading_rect = Rect::new(
|
||||||
board_x + (board_width - dims.width) / 2.0,
|
(self.window_width - heading_text_dims.width) / 2.0,
|
||||||
board_y - dims.height - constants::TOP_HEADING_OFFSET_MULTIPLIER * square_width,
|
board_y
|
||||||
dims.width,
|
- heading_text_dims.height
|
||||||
dims.height,
|
- constants::TOP_HEADING_OFFSET_MULTIPLIER * square_width,
|
||||||
|
heading_text_dims.width,
|
||||||
|
heading_text_dims.height,
|
||||||
);
|
);
|
||||||
|
self.heading
|
||||||
|
.initialize_drawables(heading_rect, heading_font_size, &self.resources);
|
||||||
|
|
||||||
// Buttons
|
// Buttons
|
||||||
let btn_h = constants::BUTTON_HEIGHT_MULTIPLIER * min_dimension;
|
let btn_h = constants::BUTTON_HEIGHT_MULTIPLIER * min_dimension;
|
||||||
let btn_w = board_width * constants::BUTTON_WIDTH_MULTIPLIER;
|
let btn_w = constants::BUTTON_WIDTH_MULTIPLIER * board_width;
|
||||||
let btn_w_sq = btn_h;
|
|
||||||
|
|
||||||
// Bottom row
|
// Bottom row
|
||||||
let bottom_row_y =
|
let bottom_row_y =
|
||||||
board_width + board_y + constants::BOTTOM_BUTTON_ROW_OFFSET_MULTIPLIER * square_width;
|
board_width + board_y + constants::BOTTOM_ROW_OFFSET_MULTIPLIER * square_width;
|
||||||
self.reset_btn.initialize_drawables(Rect::new(
|
self.rules_btn.initialize_drawables(Rect::new(
|
||||||
board_x + (square_width - btn_w_sq) / 2.,
|
board_x + (square_width - btn_w) / 2.,
|
||||||
bottom_row_y,
|
bottom_row_y,
|
||||||
btn_w_sq,
|
btn_w,
|
||||||
btn_h,
|
btn_h,
|
||||||
));
|
));
|
||||||
let btn_next_x_offset = (board_width - square_width) + (square_width - btn_w_sq) / 2.;
|
|
||||||
|
self.reset_btn.initialize_drawables(Rect::new(
|
||||||
|
board_x + square_width + (square_width - btn_w) / 2.,
|
||||||
|
bottom_row_y,
|
||||||
|
btn_w,
|
||||||
|
btn_h,
|
||||||
|
));
|
||||||
|
|
||||||
|
let btn_next_x_offset = (board_width - square_width) + (square_width - btn_w) / 2.;
|
||||||
self.next_btn.initialize_drawables(Rect::new(
|
self.next_btn.initialize_drawables(Rect::new(
|
||||||
board_x + btn_next_x_offset,
|
board_x + btn_next_x_offset,
|
||||||
bottom_row_y,
|
bottom_row_y,
|
||||||
btn_w_sq,
|
btn_w,
|
||||||
btn_h,
|
btn_h,
|
||||||
));
|
));
|
||||||
|
|
||||||
@@ -57,77 +70,131 @@ impl Game {
|
|||||||
btn_w * 2.,
|
btn_w * 2.,
|
||||||
btn_h,
|
btn_h,
|
||||||
);
|
);
|
||||||
self.id_text = IdTextInput::new(id_text_rect);
|
self.id_text_btn.initialize_drawables(id_text_rect);
|
||||||
|
|
||||||
// Left column
|
self.rules_font_size = heading_font_size;
|
||||||
let left_column_x =
|
|
||||||
board_x - constants::BOTTOM_RIGHT_ROW_OFFSET_MULTIPLIER * square_width - btn_w;
|
// Right column
|
||||||
self.rules_btn.initialize_drawables(Rect::new(
|
let right_column_x =
|
||||||
left_column_x,
|
board_x + board_width + constants::RIGHT_COLUMN_OFFSET_MULTIPLIER * square_width;
|
||||||
board_y + board_rect.h - square_width + (square_width - btn_h) / 2.,
|
self.get_game_mode_button(&GameMode::Easy)
|
||||||
|
.initialize_drawables(Rect::new(
|
||||||
|
right_column_x,
|
||||||
|
board_y + (square_width - btn_h) / 2.,
|
||||||
btn_w,
|
btn_w,
|
||||||
btn_h,
|
btn_h,
|
||||||
));
|
));
|
||||||
|
|
||||||
// Right column
|
self.get_game_mode_button(&GameMode::Medium)
|
||||||
let right_column_x =
|
.initialize_drawables(Rect::new(
|
||||||
board_x + board_width + constants::BOTTOM_RIGHT_ROW_OFFSET_MULTIPLIER * square_width;
|
|
||||||
self.easy_btn.initialize_drawables(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,
|
||||||
));
|
));
|
||||||
|
|
||||||
self.medium_btn.initialize_drawables(Rect::new(
|
self.get_game_mode_button(&GameMode::Hard)
|
||||||
|
.initialize_drawables(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,
|
||||||
));
|
));
|
||||||
|
|
||||||
self.hard_btn.initialize_drawables(Rect::new(
|
self.get_game_mode_button(&GameMode::Custom)
|
||||||
|
.initialize_drawables(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,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
// Right column 2
|
||||||
|
let right_column_2_x =
|
||||||
|
right_column_x + btn_w + constants::RIGHT_COLUMN_OFFSET_MULTIPLIER * square_width;
|
||||||
|
self.generate_btn.initialize_drawables(Rect::new(
|
||||||
|
right_column_2_x,
|
||||||
|
board_y + board_width - square_width + (square_width - btn_h) / 2.,
|
||||||
|
2. * btn_w,
|
||||||
|
btn_h,
|
||||||
|
));
|
||||||
|
|
||||||
|
self.pieces_label.initialize_drawables(
|
||||||
|
Rect::new(
|
||||||
|
right_column_2_x,
|
||||||
|
board_y + (square_width + btn_h) / 2.,
|
||||||
|
btn_w,
|
||||||
|
btn_h,
|
||||||
|
),
|
||||||
|
0.30 * btn_h,
|
||||||
|
&self.resources,
|
||||||
|
);
|
||||||
|
self.pieces_counter.initialize_drawables(
|
||||||
|
Rect::new(
|
||||||
|
right_column_2_x,
|
||||||
|
board_y + square_width + (2. * square_width - 2. * btn_h) / 2.,
|
||||||
|
btn_w,
|
||||||
|
2. * btn_h,
|
||||||
|
),
|
||||||
|
30.,
|
||||||
|
&self.resources,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Right column 3
|
||||||
|
let right_column_3_x = right_column_2_x + btn_w;
|
||||||
|
self.max_age_label.initialize_drawables(
|
||||||
|
Rect::new(
|
||||||
|
right_column_2_x + btn_w,
|
||||||
|
board_y + (square_width + btn_h) / 2.,
|
||||||
|
btn_w,
|
||||||
|
btn_h,
|
||||||
|
),
|
||||||
|
0.30 * btn_h,
|
||||||
|
&self.resources,
|
||||||
|
);
|
||||||
|
self.max_age_counter.initialize_drawables(
|
||||||
|
Rect::new(
|
||||||
|
right_column_3_x,
|
||||||
|
board_y + square_width + (2. * square_width - 2. * btn_h) / 2.,
|
||||||
|
btn_w,
|
||||||
|
2. * btn_h,
|
||||||
|
),
|
||||||
|
30.,
|
||||||
|
&self.resources,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(&mut self) {
|
pub fn draw(&mut self) {
|
||||||
self.update_window_size();
|
self.update_window_size();
|
||||||
|
|
||||||
self.draw_heading();
|
self.draw_heading();
|
||||||
self.board.draw(
|
self.draw_board();
|
||||||
self.show_rules,
|
self.draw_id_text_button();
|
||||||
self.heading_font_size,
|
|
||||||
&self.resources,
|
|
||||||
&self.settings,
|
|
||||||
);
|
|
||||||
self.id_text.draw(&self.resources, &self.puzzle.board.id);
|
|
||||||
self.draw_buttons();
|
self.draw_buttons();
|
||||||
|
self.draw_setting_controls();
|
||||||
self.draw_debug();
|
self.draw_debug();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_heading(&self) {
|
fn draw_heading(&self) {
|
||||||
let f = self.heading_font_size.floor() as u16;
|
self.heading.draw(&self.resources);
|
||||||
let dims = measure_text(
|
}
|
||||||
self.heading_text.as_str(),
|
|
||||||
Some(&self.resources.font()),
|
fn draw_board(&mut self) {
|
||||||
f,
|
let params = BoardDrawParams {
|
||||||
1.0,
|
show_rules: self.show_rules,
|
||||||
);
|
rules_font_size: self.rules_font_size,
|
||||||
let draw_text_params = TextParams {
|
|
||||||
font_size: f,
|
|
||||||
font: Some(&self.resources.font()),
|
|
||||||
color: BLACK,
|
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
draw_text_ex(
|
|
||||||
self.heading_text.as_str(),
|
self.board.draw(¶ms, &self.resources, &self.settings);
|
||||||
self.heading_rect.x,
|
}
|
||||||
self.heading_rect.y + dims.offset_y,
|
|
||||||
draw_text_params,
|
fn get_game_mode_button<'a>(&'a mut self, mode: &GameMode) -> &'a mut ButtonWidget {
|
||||||
);
|
self.game_mode_btns.get_mut(mode).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_game_mode_button(&mut self, mode: &GameMode, color: &UiColor, text: &str) {
|
||||||
|
let btn = self.game_mode_btns.get_mut(mode).unwrap();
|
||||||
|
btn.draw(text, color, &self.resources);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_buttons(&mut self) {
|
fn draw_buttons(&mut self) {
|
||||||
@@ -143,26 +210,50 @@ impl Game {
|
|||||||
&self.resources,
|
&self.resources,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.easy_btn.draw(
|
self.draw_game_mode_button(
|
||||||
|
&GameMode::Easy,
|
||||||
|
&UiColor::Yellow,
|
||||||
constants::EASY_BUTTON_TEXT,
|
constants::EASY_BUTTON_TEXT,
|
||||||
&UiColor::Yellow,
|
|
||||||
&self.resources,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
self.medium_btn.draw(
|
self.draw_game_mode_button(
|
||||||
|
&GameMode::Medium,
|
||||||
|
&UiColor::Yellow,
|
||||||
constants::MEDIUM_BUTTON_TEXT,
|
constants::MEDIUM_BUTTON_TEXT,
|
||||||
&UiColor::Yellow,
|
|
||||||
&self.resources,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
self.hard_btn.draw(
|
self.draw_game_mode_button(
|
||||||
constants::HARD_BUTTON_TEXT,
|
&GameMode::Hard,
|
||||||
&UiColor::Yellow,
|
&UiColor::Yellow,
|
||||||
|
constants::HARD_BUTTON_TEXT,
|
||||||
|
);
|
||||||
|
|
||||||
|
self.draw_game_mode_button(
|
||||||
|
&GameMode::Custom,
|
||||||
|
&UiColor::Blue,
|
||||||
|
constants::CUSTOM_BUTTON_TEXT,
|
||||||
|
);
|
||||||
|
|
||||||
|
self.generate_btn.draw(
|
||||||
|
constants::GENERATE_BUTTON_TEXT,
|
||||||
|
&UiColor::Pink,
|
||||||
&self.resources,
|
&self.resources,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.rules_btn
|
self.rules_btn
|
||||||
.draw(&self.rules_text, &UiColor::Brown, &self.resources);
|
.draw(&self.rules_btn_text, &UiColor::Brown, &self.resources);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_setting_controls(&mut self) {
|
||||||
|
self.pieces_label.draw(&self.resources);
|
||||||
|
self.pieces_counter.draw(&self.resources);
|
||||||
|
self.max_age_label.draw(&self.resources);
|
||||||
|
self.max_age_counter.draw(&self.resources);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_id_text_button(&self) {
|
||||||
|
// self.id_text_btn
|
||||||
|
// .draw(&self.puzzle.board.id, &UiColor::Yellow, &self.resources);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_debug(&self) {
|
fn draw_debug(&self) {
|
||||||
@@ -198,8 +289,6 @@ impl Game {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.window_height = new_height;
|
self.initialize_drawables(new_width, new_height);
|
||||||
self.window_width = new_width;
|
|
||||||
self.initialize_drawables();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+123
-48
@@ -1,3 +1,5 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use super::{Game, GameMode, GameSettings, constants};
|
use super::{Game, GameMode, GameSettings, constants};
|
||||||
use crate::{
|
use crate::{
|
||||||
resources::{self, *},
|
resources::{self, *},
|
||||||
@@ -15,26 +17,61 @@ impl Game {
|
|||||||
let game_mode = GameMode::Medium;
|
let game_mode = GameMode::Medium;
|
||||||
let settings = GameSettings {
|
let settings = GameSettings {
|
||||||
volume: constants::VOLUME,
|
volume: constants::VOLUME,
|
||||||
|
max_moves_per_piece: 3,
|
||||||
|
num_pieces: 2,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let puzzle = Game::generate_puzzle(game_mode);
|
let puzzle = Game::generate_puzzle(game_mode, &settings);
|
||||||
let board = BoardWidget::initialize_state(puzzle.board.size, puzzle.board.clone());
|
let board = BoardWidget::initialize_state(puzzle.board.size, puzzle.board.clone());
|
||||||
let medium_btn = ButtonWidget::initialize_state(false);
|
let mut game_mode_btns = HashMap::new();
|
||||||
|
let game_mode = GameMode::Medium;
|
||||||
|
game_mode_btns.insert(GameMode::Easy, ButtonWidget::initialize_state(true));
|
||||||
|
game_mode_btns.insert(GameMode::Medium, ButtonWidget::initialize_state(true));
|
||||||
|
game_mode_btns.insert(GameMode::Hard, ButtonWidget::initialize_state(true));
|
||||||
|
game_mode_btns.insert(GameMode::Custom, ButtonWidget::initialize_state(true));
|
||||||
|
let reset_btn = ButtonWidget::initialize_state(true);
|
||||||
let next_btn = ButtonWidget::initialize_state(false);
|
let next_btn = ButtonWidget::initialize_state(false);
|
||||||
|
let rules_btn = ButtonWidget::initialize_state(true);
|
||||||
|
let generate_btn = ButtonWidget::initialize_state(false);
|
||||||
|
let id_text_btn = ButtonWidget::initialize_state(true);
|
||||||
|
let heading = LabelWidget::initialize_state(constants::HEADING_TEXT);
|
||||||
|
let pieces_label = LabelWidget::initialize_state(constants::PIECES_LABEL_TEXT);
|
||||||
|
let max_age_label = LabelWidget::initialize_state(constants::AGE_LABEL_TEXT);
|
||||||
|
let show_rules = false;
|
||||||
|
let pieces_counter = CounterWidget::initialize_state(2, 7, settings.num_pieces, false);
|
||||||
|
let moves_counter = CounterWidget::initialize_state(1, 7, settings.max_moves_per_piece, false);
|
||||||
|
game_mode_btns.get_mut(&game_mode).unwrap().is_active = false;
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
puzzle,
|
puzzle,
|
||||||
board,
|
board,
|
||||||
resources,
|
resources,
|
||||||
game_mode,
|
|
||||||
settings,
|
settings,
|
||||||
heading_text: constants::HEADING_TEXT.to_string(),
|
game_mode,
|
||||||
rules_text: constants::RULES_BUTTON_TEXT.to_string(),
|
game_mode_btns,
|
||||||
medium_btn,
|
rules_btn_text: constants::RULES_BUTTON_TEXT.to_string(),
|
||||||
|
reset_btn,
|
||||||
next_btn,
|
next_btn,
|
||||||
..Self::default()
|
rules_btn,
|
||||||
|
id_text_btn,
|
||||||
|
show_rules,
|
||||||
|
heading_text: constants::HEADING_TEXT.to_string(),
|
||||||
|
heading,
|
||||||
|
pieces_counter,
|
||||||
|
max_age_counter: moves_counter,
|
||||||
|
generate_btn,
|
||||||
|
pieces_label,
|
||||||
|
max_age_label,
|
||||||
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_input(&mut self) {
|
pub fn handle_input(&mut self) {
|
||||||
|
let (mx, my) = mouse_position();
|
||||||
|
let winput = WidgetInput {
|
||||||
|
mouse_pos: Circle::new(mx, my, 0.0),
|
||||||
|
};
|
||||||
|
|
||||||
// CORE GAMEPLAY
|
// CORE GAMEPLAY
|
||||||
let (mouse_x, mouse_y) = mouse_position();
|
let (mouse_x, mouse_y) = mouse_position();
|
||||||
let mouse_pos = Circle::new(mouse_x, mouse_y, 0.0);
|
let mouse_pos = Circle::new(mouse_x, mouse_y, 0.0);
|
||||||
@@ -56,6 +93,11 @@ impl Game {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.id_text_btn.handle_input(
|
||||||
|
&self.resources.sound(&SoundKind::Click),
|
||||||
|
self.settings.volume,
|
||||||
|
);
|
||||||
|
|
||||||
if self
|
if self
|
||||||
.reset_btn
|
.reset_btn
|
||||||
.handle_input(
|
.handle_input(
|
||||||
@@ -81,50 +123,41 @@ impl Game {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PRESETS
|
// PRESETS
|
||||||
let mut clicked_game_mode_btn: Option<&ButtonWidget> = None;
|
let mut game_mode_changed = false;
|
||||||
if self
|
for (mode, btn) in &mut self.game_mode_btns {
|
||||||
.easy_btn
|
if btn
|
||||||
.handle_input(
|
.handle_input(
|
||||||
&self.resources.sound(&SoundKind::Mode),
|
&self.resources.sound(&SoundKind::Mode),
|
||||||
self.settings.volume,
|
self.settings.volume,
|
||||||
)
|
)
|
||||||
.is_clicked
|
.is_clicked
|
||||||
{
|
{
|
||||||
self.game_mode = GameMode::Easy;
|
self.game_mode = *mode;
|
||||||
self.easy_btn.is_active = false;
|
game_mode_changed = true;
|
||||||
self.medium_btn.is_active = true;
|
break;
|
||||||
self.hard_btn.is_active = true;
|
}
|
||||||
clicked_game_mode_btn = Some(&self.easy_btn);
|
|
||||||
} else if self
|
|
||||||
.medium_btn
|
|
||||||
.handle_input(
|
|
||||||
&self.resources.sound(&SoundKind::Mode),
|
|
||||||
self.settings.volume,
|
|
||||||
)
|
|
||||||
.is_clicked
|
|
||||||
{
|
|
||||||
self.game_mode = GameMode::Medium;
|
|
||||||
self.easy_btn.is_active = true;
|
|
||||||
self.medium_btn.is_active = false;
|
|
||||||
self.hard_btn.is_active = true;
|
|
||||||
clicked_game_mode_btn = Some(&self.medium_btn);
|
|
||||||
} else if self
|
|
||||||
.hard_btn
|
|
||||||
.handle_input(
|
|
||||||
&self.resources.sound(&SoundKind::Mode),
|
|
||||||
self.settings.volume,
|
|
||||||
)
|
|
||||||
.is_clicked
|
|
||||||
{
|
|
||||||
self.game_mode = GameMode::Hard;
|
|
||||||
self.easy_btn.is_active = true;
|
|
||||||
self.medium_btn.is_active = true;
|
|
||||||
self.hard_btn.is_active = false;
|
|
||||||
clicked_game_mode_btn = Some(&self.hard_btn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(_) = clicked_game_mode_btn {
|
if game_mode_changed {
|
||||||
|
for (mode, btn) in &mut self.game_mode_btns {
|
||||||
|
if *mode == self.game_mode {
|
||||||
|
btn.is_active = false;
|
||||||
|
} else {
|
||||||
|
btn.is_active = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.game_mode != GameMode::Custom {
|
||||||
|
self.generate_btn.is_active = false;
|
||||||
|
self.pieces_counter.set_active(false);
|
||||||
|
self.max_age_counter.set_active(false);
|
||||||
self.next_puzzle();
|
self.next_puzzle();
|
||||||
|
} else {
|
||||||
|
self.generate_btn.is_active = true;
|
||||||
|
self.pieces_counter.set_active(true);
|
||||||
|
self.max_age_counter.set_active(true);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +170,7 @@ impl Game {
|
|||||||
.is_clicked
|
.is_clicked
|
||||||
{
|
{
|
||||||
self.show_rules = !self.show_rules;
|
self.show_rules = !self.show_rules;
|
||||||
self.rules_text = match self.show_rules {
|
self.rules_btn_text = match self.show_rules {
|
||||||
true => constants::RULES_BUTTON_ALT_TEXT.to_string(),
|
true => constants::RULES_BUTTON_ALT_TEXT.to_string(),
|
||||||
false => constants::RULES_BUTTON_TEXT.to_string(),
|
false => constants::RULES_BUTTON_TEXT.to_string(),
|
||||||
};
|
};
|
||||||
@@ -145,6 +178,36 @@ impl Game {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let pieces_counter_interaction =
|
||||||
|
self.pieces_counter
|
||||||
|
.handle_input(&winput, &self.resources, &self.settings);
|
||||||
|
if pieces_counter_interaction.interacted {
|
||||||
|
self.settings.num_pieces = pieces_counter_interaction.current;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let moves_counter_interaction =
|
||||||
|
self.max_age_counter
|
||||||
|
.handle_input(&winput, &self.resources, &self.settings);
|
||||||
|
if moves_counter_interaction.interacted {
|
||||||
|
self.settings.max_moves_per_piece = moves_counter_interaction.current;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self
|
||||||
|
.generate_btn
|
||||||
|
.handle_input(
|
||||||
|
&self.resources.sound(&SoundKind::Button),
|
||||||
|
self.settings.volume,
|
||||||
|
)
|
||||||
|
.is_clicked
|
||||||
|
{
|
||||||
|
if self.game_mode == GameMode::Custom {
|
||||||
|
self.next_puzzle();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// KEY INPUTS
|
// KEY INPUTS
|
||||||
if is_key_released(KeyCode::Escape) {
|
if is_key_released(KeyCode::Escape) {
|
||||||
if self.show_rules {
|
if self.show_rules {
|
||||||
@@ -153,7 +216,7 @@ impl Game {
|
|||||||
self.settings.volume,
|
self.settings.volume,
|
||||||
);
|
);
|
||||||
self.show_rules = false;
|
self.show_rules = false;
|
||||||
self.rules_text = constants::RULES_BUTTON_TEXT.to_string();
|
self.rules_btn_text = constants::RULES_BUTTON_TEXT.to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -175,7 +238,7 @@ impl Game {
|
|||||||
|
|
||||||
fn reset_game(&mut self, options: ResetOptions) {
|
fn reset_game(&mut self, options: ResetOptions) {
|
||||||
if options.create_new_puzzle {
|
if options.create_new_puzzle {
|
||||||
self.puzzle = Game::generate_puzzle(self.game_mode);
|
self.puzzle = Game::generate_puzzle(self.game_mode, &self.settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.next_btn.is_active = false;
|
self.next_btn.is_active = false;
|
||||||
@@ -189,15 +252,27 @@ impl Game {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_puzzle(mode: GameMode) -> Puzzle {
|
fn generate_puzzle(mode: GameMode, settings: &GameSettings) -> Puzzle {
|
||||||
let piece_count = match mode {
|
let piece_count = match mode {
|
||||||
GameMode::Easy => 3,
|
GameMode::Easy => 3,
|
||||||
GameMode::Medium => 5,
|
GameMode::Medium => 5,
|
||||||
GameMode::Hard => 7,
|
GameMode::Hard => 7,
|
||||||
|
GameMode::Custom => settings.num_pieces,
|
||||||
};
|
};
|
||||||
|
|
||||||
let generated =
|
let max_moves_per_piece = match mode {
|
||||||
generator::generate_weighted_random(piece_count, 100, &MacroquadRandAdapter);
|
GameMode::Easy => 2,
|
||||||
|
GameMode::Medium => 2,
|
||||||
|
GameMode::Hard => 2,
|
||||||
|
GameMode::Custom => settings.max_moves_per_piece,
|
||||||
|
};
|
||||||
|
|
||||||
|
let generated = generator::generate_weighted_random(
|
||||||
|
piece_count,
|
||||||
|
100,
|
||||||
|
max_moves_per_piece,
|
||||||
|
&MacroquadRandAdapter,
|
||||||
|
);
|
||||||
let puzzle = generated.puzzle();
|
let puzzle = generated.puzzle();
|
||||||
puzzle.expect("No puzzle was generated")
|
puzzle.expect("No puzzle was generated")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,3 +36,4 @@ async fn main() {
|
|||||||
next_frame().await
|
next_frame().await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+36
-18
@@ -2,13 +2,14 @@ use macroquad::{
|
|||||||
audio::{self, Sound},
|
audio::{self, Sound},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
use sol_lib::board::piece::Piece;
|
use sol_lib::board::piece::{Piece, PieceKind};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Resources {
|
pub struct Resources {
|
||||||
texture_pieces: Option<Texture2D>,
|
texture_pieces: Option<Texture2D>,
|
||||||
texture_rects_pieces: HashMap<Piece, Rect>,
|
texture_rects_pieces: HashMap<PieceKind, Rect>,
|
||||||
|
texture_rects_inactive_pieces: HashMap<PieceKind, Rect>,
|
||||||
|
|
||||||
sounds: HashMap<SoundKind, Sound>,
|
sounds: HashMap<SoundKind, Sound>,
|
||||||
|
|
||||||
@@ -52,32 +53,43 @@ pub async fn init() -> Resources {
|
|||||||
texture_pieces.set_filter(FilterMode::Nearest);
|
texture_pieces.set_filter(FilterMode::Nearest);
|
||||||
build_textures_atlas();
|
build_textures_atlas();
|
||||||
|
|
||||||
let mut texture_rects_pieces = HashMap::new();
|
let texture_rects_pieces = generate_texture_rects_pieces(true);
|
||||||
texture_rects_pieces.insert(Piece::Pawn, piece_texture_rect(Piece::Pawn));
|
let texture_rects_inactive_pieces = generate_texture_rects_pieces(false);
|
||||||
texture_rects_pieces.insert(Piece::Knight, piece_texture_rect(Piece::Knight));
|
|
||||||
texture_rects_pieces.insert(Piece::Bishop, piece_texture_rect(Piece::Bishop));
|
|
||||||
texture_rects_pieces.insert(Piece::Rook, piece_texture_rect(Piece::Rook));
|
|
||||||
texture_rects_pieces.insert(Piece::Queen, piece_texture_rect(Piece::Queen));
|
|
||||||
texture_rects_pieces.insert(Piece::King, piece_texture_rect(Piece::King));
|
|
||||||
|
|
||||||
Resources {
|
Resources {
|
||||||
texture_pieces: Some(texture_pieces),
|
texture_pieces: Some(texture_pieces),
|
||||||
texture_rects_pieces,
|
texture_rects_pieces,
|
||||||
|
texture_rects_inactive_pieces,
|
||||||
sounds,
|
sounds,
|
||||||
font: Some(font),
|
font: Some(font),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn piece_texture_rect(piece: Piece) -> Rect {
|
fn generate_texture_rects_pieces(active: bool) -> HashMap<PieceKind, Rect> {
|
||||||
|
let mut texture_rects_pieces = HashMap::new();
|
||||||
|
texture_rects_pieces.insert(PieceKind::Pawn, piece_texture_rect(PieceKind::Pawn, active));
|
||||||
|
texture_rects_pieces.insert(PieceKind::Knight, piece_texture_rect(PieceKind::Knight, active));
|
||||||
|
texture_rects_pieces.insert(PieceKind::Bishop, piece_texture_rect(PieceKind::Bishop, active));
|
||||||
|
texture_rects_pieces.insert(PieceKind::Rook, piece_texture_rect(PieceKind::Rook, active));
|
||||||
|
texture_rects_pieces.insert(PieceKind::Queen, piece_texture_rect(PieceKind::Queen, active));
|
||||||
|
texture_rects_pieces.insert(PieceKind::King, piece_texture_rect(PieceKind::King, active));
|
||||||
|
|
||||||
|
texture_rects_pieces
|
||||||
|
}
|
||||||
|
|
||||||
|
fn piece_texture_rect(piece: PieceKind, active: bool) -> Rect {
|
||||||
let index = match piece {
|
let index = match piece {
|
||||||
Piece::Pawn => 0,
|
PieceKind::Pawn => 0,
|
||||||
Piece::Knight => 1,
|
PieceKind::Knight => 1,
|
||||||
Piece::Bishop => 2,
|
PieceKind::Bishop => 2,
|
||||||
Piece::Rook => 3,
|
PieceKind::Rook => 3,
|
||||||
Piece::Queen => 4,
|
PieceKind::Queen => 4,
|
||||||
Piece::King => 5,
|
PieceKind::King => 5,
|
||||||
|
};
|
||||||
|
let color = match active {
|
||||||
|
true => 1,
|
||||||
|
false => 0,
|
||||||
};
|
};
|
||||||
let color = 0;
|
|
||||||
Rect::new(index as f32 * 128.0, color as f32 * 128.0, 128.0, 128.0)
|
Rect::new(index as f32 * 128.0, color as f32 * 128.0, 128.0, 128.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,7 +105,13 @@ pub enum SoundKind {
|
|||||||
impl Resources {
|
impl Resources {
|
||||||
pub fn get_piece_texture<'a>(&'a self, piece: &Piece) -> Texture<'a> {
|
pub fn get_piece_texture<'a>(&'a self, piece: &Piece) -> Texture<'a> {
|
||||||
let texture = self.texture_pieces.as_ref().unwrap();
|
let texture = self.texture_pieces.as_ref().unwrap();
|
||||||
let texture_rect = self.texture_rects_pieces.get(piece).unwrap();
|
let texture_rects_lookup = if piece.active {
|
||||||
|
&self.texture_rects_pieces
|
||||||
|
} else {
|
||||||
|
&self.texture_rects_inactive_pieces
|
||||||
|
};
|
||||||
|
|
||||||
|
let texture_rect = texture_rects_lookup.get(&piece.kind).unwrap();
|
||||||
Texture {
|
Texture {
|
||||||
texture: texture,
|
texture: texture,
|
||||||
texture_rect: *texture_rect,
|
texture_rect: *texture_rect,
|
||||||
|
|||||||
+10
-1
@@ -5,7 +5,12 @@ mod button;
|
|||||||
pub use button::*;
|
pub use button::*;
|
||||||
|
|
||||||
mod id_text_input;
|
mod id_text_input;
|
||||||
pub use id_text_input::*;
|
|
||||||
|
mod label;
|
||||||
|
pub use label::*;
|
||||||
|
|
||||||
|
mod counter;
|
||||||
|
pub use counter::*;
|
||||||
|
|
||||||
use macroquad::{
|
use macroquad::{
|
||||||
audio::{self, Sound},
|
audio::{self, Sound},
|
||||||
@@ -13,6 +18,10 @@ use macroquad::{
|
|||||||
};
|
};
|
||||||
use quad_snd::PlaySoundParams;
|
use quad_snd::PlaySoundParams;
|
||||||
|
|
||||||
|
pub struct WidgetInput {
|
||||||
|
pub mouse_pos: Circle,
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum UiColor {
|
pub enum UiColor {
|
||||||
|
|||||||
@@ -28,10 +28,6 @@ pub struct BoardInteraction<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BoardWidget {
|
impl BoardWidget {
|
||||||
fn get(&mut self, i: usize, j: usize) -> &mut GameSquare {
|
|
||||||
&mut self.squares[i * self.num_squares + j]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn initialize_state(num_squares: usize, current_board: Board) -> BoardWidget {
|
pub fn initialize_state(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;
|
||||||
@@ -71,11 +67,11 @@ impl BoardWidget {
|
|||||||
self.squares = rects;
|
self.squares = rects;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(&self, show_rules: bool, rules_font_size: f32, resources: &Resources, settings: &GameSettings) {
|
pub fn draw(&self, params: &BoardDrawParams, resources: &Resources, settings: &GameSettings) {
|
||||||
let board_shadow_width = constants::BOARD_SHADOW_MULTIPLIER * self.square_width;
|
let board_shadow_width = constants::BOARD_SHADOW_MULTIPLIER * self.square_width;
|
||||||
draw_shadow(&self.board_rect, board_shadow_width);
|
draw_shadow(&self.board_rect, board_shadow_width);
|
||||||
|
|
||||||
if show_rules {
|
if params.show_rules {
|
||||||
draw_rectangle(
|
draw_rectangle(
|
||||||
self.board_rect.x,
|
self.board_rect.x,
|
||||||
self.board_rect.y,
|
self.board_rect.y,
|
||||||
@@ -84,11 +80,13 @@ impl BoardWidget {
|
|||||||
UiColor::Yellow.to_bg_color(),
|
UiColor::Yellow.to_bg_color(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let font_size = rules_font_size * 0.6;
|
let font_size = params.rules_font_size * 0.4;
|
||||||
let rules = "\
|
let rules = "\
|
||||||
Every move should be a \n\
|
Every move should be a \n\
|
||||||
capture. Win when only \n\
|
capture. Win when only \n\
|
||||||
one piece is left.\n";
|
one piece is left.\n\
|
||||||
|
Age: Each piece can only \n\
|
||||||
|
move 'age' times";
|
||||||
let measurement = measure_text(rules, Some(resources.font()), font_size as u16, 1.0);
|
let measurement = measure_text(rules, Some(resources.font()), font_size as u16, 1.0);
|
||||||
let draw_text_params = TextParams {
|
let draw_text_params = TextParams {
|
||||||
font_size: font_size as u16,
|
font_size: font_size as u16,
|
||||||
@@ -99,7 +97,7 @@ impl BoardWidget {
|
|||||||
draw_multiline_text_ex(
|
draw_multiline_text_ex(
|
||||||
rules,
|
rules,
|
||||||
self.board_rect.x + 0.5 * self.square_width,
|
self.board_rect.x + 0.5 * self.square_width,
|
||||||
self.board_rect.y + 0.5 * (self.board_rect.h - measurement.height)
|
self.board_rect.y + 0.4 * (self.board_rect.h - measurement.height)
|
||||||
- 2. * measurement.offset_y,
|
- 2. * measurement.offset_y,
|
||||||
Some(2.),
|
Some(2.),
|
||||||
draw_text_params,
|
draw_text_params,
|
||||||
@@ -161,6 +159,10 @@ impl BoardWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get(&mut self, i: usize, j: usize) -> &mut GameSquare {
|
||||||
|
&mut self.squares[i * self.num_squares + j]
|
||||||
|
}
|
||||||
|
|
||||||
fn draw_debug(&self) {
|
fn draw_debug(&self) {
|
||||||
let mut debug_lines = vec![];
|
let mut debug_lines = vec![];
|
||||||
let (mx, my) = mouse_position();
|
let (mx, my) = mouse_position();
|
||||||
@@ -420,3 +422,8 @@ struct PieceDrawTextureParams<'a> {
|
|||||||
texture: &'a Texture2D,
|
texture: &'a Texture2D,
|
||||||
draw_text_params: DrawTextureParams,
|
draw_text_params: DrawTextureParams,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct BoardDrawParams {
|
||||||
|
pub show_rules: bool,
|
||||||
|
pub rules_font_size: f32,
|
||||||
|
}
|
||||||
|
|||||||
@@ -66,8 +66,19 @@ impl ButtonWidget {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let font = resources.font();
|
let font = resources.font();
|
||||||
let font_size = (0.2 * self.rect.w) as u16;
|
let mut dim_width = self.rect.w;
|
||||||
let dims = measure_text(&text, Some(&font), font_size, 1.0);
|
let mut font_size = 30.0;
|
||||||
|
let mut dims = measure_text(&text, Some(&font), font_size as u16, 1.0);
|
||||||
|
loop {
|
||||||
|
if dim_width <= self.rect.w * 0.8 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
dims = measure_text(&text, Some(&font), font_size as u16, 1.0);
|
||||||
|
dim_width = dims.width;
|
||||||
|
font_size -= 2.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 {
|
||||||
|
|||||||
@@ -0,0 +1,127 @@
|
|||||||
|
use crate::{
|
||||||
|
game::GameSettings,
|
||||||
|
resources::{Resources, SoundKind},
|
||||||
|
widgets::*,
|
||||||
|
};
|
||||||
|
use macroquad::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct CounterWidget {
|
||||||
|
visual_rect: Rect,
|
||||||
|
increment: ButtonWidget,
|
||||||
|
decrement: ButtonWidget,
|
||||||
|
min: u32,
|
||||||
|
max: u32,
|
||||||
|
current: u32,
|
||||||
|
font_size: f32,
|
||||||
|
visual_dims: TextDimensions,
|
||||||
|
increment_dims: TextDimensions,
|
||||||
|
decrement_dims: TextDimensions,
|
||||||
|
is_active: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CounterWidgetInteraction {
|
||||||
|
pub current: u32,
|
||||||
|
pub interacted: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CounterWidget {
|
||||||
|
pub fn initialize_state(min: u32, max: u32, current: u32, is_active: bool) -> Self {
|
||||||
|
Self {
|
||||||
|
min,
|
||||||
|
max,
|
||||||
|
current,
|
||||||
|
is_active,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_active(&mut self, is_active: bool) {
|
||||||
|
self.is_active = is_active;
|
||||||
|
self.increment.is_active = is_active;
|
||||||
|
self.decrement.is_active = is_active;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn initialize_drawables(
|
||||||
|
&mut self,
|
||||||
|
total_area: Rect,
|
||||||
|
font_size: f32,
|
||||||
|
resources: &Resources,
|
||||||
|
) {
|
||||||
|
let margin_y = 0.05 * total_area.h;
|
||||||
|
let btn_h = total_area.h * 0.3;
|
||||||
|
let btn_w = total_area.w * 0.9;
|
||||||
|
let btn_x = total_area.x + (total_area.w - btn_w) / 2.;
|
||||||
|
let increment_y = total_area.y + margin_y;
|
||||||
|
let visual_y = increment_y + btn_h + margin_y;
|
||||||
|
let decrement_y = visual_y + btn_h + margin_y;
|
||||||
|
|
||||||
|
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);
|
||||||
|
self.increment = ButtonWidget::initialize_state(self.is_active);
|
||||||
|
self.increment.initialize_drawables(increment_rect);
|
||||||
|
|
||||||
|
let decrement_rect = Rect::new(btn_x, decrement_y, btn_w, btn_h);
|
||||||
|
self.decrement = ButtonWidget::initialize_state(self.is_active);
|
||||||
|
self.decrement.initialize_drawables(decrement_rect);
|
||||||
|
|
||||||
|
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.visual_dims = measure_text("0", Some(resources.font()), font_size as u16, 1.0);
|
||||||
|
self.font_size = font_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_input(
|
||||||
|
&mut self,
|
||||||
|
_: &WidgetInput,
|
||||||
|
resources: &Resources,
|
||||||
|
settings: &GameSettings,
|
||||||
|
) -> CounterWidgetInteraction {
|
||||||
|
let mut interacted = false;
|
||||||
|
if self
|
||||||
|
.increment
|
||||||
|
.handle_input(resources.sound(&SoundKind::Click), settings.volume)
|
||||||
|
.is_clicked
|
||||||
|
{
|
||||||
|
if self.current < self.max {
|
||||||
|
self.current += 1;
|
||||||
|
interacted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self
|
||||||
|
.decrement
|
||||||
|
.handle_input(resources.sound(&SoundKind::Click), settings.volume)
|
||||||
|
.is_clicked
|
||||||
|
{
|
||||||
|
if self.current > self.min {
|
||||||
|
self.current -= 1;
|
||||||
|
interacted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return CounterWidgetInteraction {
|
||||||
|
current: self.current,
|
||||||
|
interacted,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw(&mut self, resources: &Resources) {
|
||||||
|
self.increment.draw("+", &UiColor::Yellow, resources);
|
||||||
|
self.decrement.draw("-", &UiColor::Yellow, resources);
|
||||||
|
|
||||||
|
draw_text_ex(
|
||||||
|
&self.current.to_string(),
|
||||||
|
self.visual_rect.x + (self.visual_rect.w - self.visual_dims.width) / 2.,
|
||||||
|
self.visual_rect.y + (self.visual_rect.h + self.font_size) / 2.,
|
||||||
|
TextParams {
|
||||||
|
font: Some(resources.font()),
|
||||||
|
font_size: self.font_size as u16,
|
||||||
|
color: UiColor::Yellow.to_fg_color(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,77 +1,73 @@
|
|||||||
|
#[allow(dead_code)]
|
||||||
|
|
||||||
use crate::{resources::Resources, widgets::*};
|
use crate::{resources::Resources, widgets::*};
|
||||||
use macroquad::prelude::*;
|
use macroquad::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
pub struct IdTextInput {
|
pub struct IdTextInput {
|
||||||
rect: Rect,
|
rect: Rect,
|
||||||
text_rect: Rect,
|
text_rect: Rect,
|
||||||
copy_box_rect: Rect,
|
copy_box_rect: Rect,
|
||||||
paste_box_rect: Rect,
|
paste_box_rect: Rect,
|
||||||
shadow_width: f32,
|
}
|
||||||
color: UiColor,
|
|
||||||
copy_color: UiColor,
|
pub struct IdTextInputDrawParams<'a> {
|
||||||
paste_color: UiColor,
|
pub text: &'a str,
|
||||||
|
pub color: UiColor,
|
||||||
|
pub copy_color: UiColor,
|
||||||
|
pub paste_color: UiColor,
|
||||||
|
pub shadow_width: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IdTextInput {
|
impl IdTextInput {
|
||||||
pub fn new(rect: Rect) -> Self {
|
pub fn initialize_state() -> Self {
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn initialize_drawables(&mut self, rect: Rect) {
|
||||||
let copy_button_w = 0.25 * rect.w;
|
let copy_button_w = 0.25 * rect.w;
|
||||||
let paste_button_h = 0.5 * rect.h;
|
let paste_button_h = 0.5 * rect.h;
|
||||||
Self {
|
self.rect = rect;
|
||||||
rect,
|
self.text_rect = Rect::new(rect.x, rect.y, rect.w - copy_button_w, rect.h);
|
||||||
text_rect: Rect::new(rect.x, rect.y, rect.w - copy_button_w, rect.h),
|
self.copy_box_rect = Rect::new(
|
||||||
copy_box_rect: Rect::new(
|
|
||||||
rect.x + (rect.w - copy_button_w),
|
rect.x + (rect.w - copy_button_w),
|
||||||
rect.y,
|
rect.y,
|
||||||
copy_button_w,
|
copy_button_w,
|
||||||
paste_button_h,
|
paste_button_h,
|
||||||
),
|
);
|
||||||
paste_box_rect: Rect::new(
|
self.paste_box_rect = Rect::new(
|
||||||
rect.x + (rect.w - copy_button_w),
|
rect.x + (rect.w - copy_button_w),
|
||||||
rect.y + paste_button_h,
|
rect.y + paste_button_h,
|
||||||
copy_button_w,
|
copy_button_w,
|
||||||
paste_button_h,
|
paste_button_h,
|
||||||
),
|
);
|
||||||
shadow_width: 3.0,
|
|
||||||
color: UiColor::Yellow,
|
|
||||||
copy_color: UiColor::Blue,
|
|
||||||
paste_color: UiColor::Pink,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(&self, resources: &Resources, text: &str) {
|
pub fn draw(&self, params: &IdTextInputDrawParams, resources: &Resources) {
|
||||||
draw_rect(&self.text_rect, self.color);
|
draw_rect(&self.text_rect, params.color);
|
||||||
draw_rect(&self.copy_box_rect, self.copy_color);
|
draw_rect(&self.copy_box_rect, params.copy_color);
|
||||||
draw_rect(&self.paste_box_rect, self.paste_color);
|
draw_rect(&self.paste_box_rect, params.paste_color);
|
||||||
draw_shadow(&self.rect, self.shadow_width);
|
draw_shadow(&self.rect, params.shadow_width);
|
||||||
|
|
||||||
let font = resources.font();
|
let font = resources.font();
|
||||||
let font_size = (0.15 * self.text_rect.w) as u16;
|
let font_size = (0.15 * self.text_rect.w) as u16;
|
||||||
let text_dims = measure_text(text, Some(font), font_size, 1.0);
|
let text_dims = measure_text(params.text, Some(font), font_size, 1.0);
|
||||||
draw_text_ex(
|
draw_text_ex(
|
||||||
&text,
|
params.text,
|
||||||
self.rect.x + (self.text_rect.w - text_dims.width) / 2.,
|
self.rect.x + (self.text_rect.w - text_dims.width) / 2.,
|
||||||
self.rect.y + (self.text_rect.h - text_dims.height) / 2. + text_dims.offset_y,
|
self.rect.y + (self.text_rect.h - text_dims.height) / 2. + text_dims.offset_y,
|
||||||
TextParams {
|
TextParams {
|
||||||
font: Some(font),
|
font: Some(font),
|
||||||
font_size: font_size,
|
font_size: font_size,
|
||||||
color: self.color.to_fg_color(),
|
color: params.color.to_fg_color(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn handle_input(&self, _: &WidgetInput) -> IdTextInputInteraction {
|
||||||
|
IdTextInputInteraction {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for IdTextInput {
|
pub struct IdTextInputInteraction {}
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
rect: Default::default(),
|
|
||||||
text_rect: Default::default(),
|
|
||||||
copy_box_rect: Default::default(),
|
|
||||||
paste_box_rect: Default::default(),
|
|
||||||
shadow_width: Default::default(),
|
|
||||||
color: UiColor::Grey,
|
|
||||||
copy_color: UiColor::Grey,
|
|
||||||
paste_color: UiColor::Grey,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
use macroquad::prelude::*;
|
||||||
|
|
||||||
|
use crate::resources::Resources;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct LabelWidget {
|
||||||
|
rect: Rect,
|
||||||
|
font_size: f32,
|
||||||
|
text_dimensions: TextDimensions,
|
||||||
|
text: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LabelWidget {
|
||||||
|
pub fn initialize_state(text: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
text: text.to_string(),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn initialize_drawables(&mut self, rect: Rect, font_size: f32, resources: &Resources) {
|
||||||
|
self.rect = rect;
|
||||||
|
self.font_size = font_size;
|
||||||
|
self.text_dimensions = measure_text(&self.text, Some(resources.font()), font_size as u16, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw(&self, resources: &Resources) {
|
||||||
|
let draw_text_params = TextParams {
|
||||||
|
font: Some(&resources.font()),
|
||||||
|
font_size: self.font_size as u16,
|
||||||
|
color: BLACK,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
draw_text_ex(
|
||||||
|
&self.text,
|
||||||
|
self.rect.x,
|
||||||
|
self.rect.y + self.text_dimensions.offset_y,
|
||||||
|
draw_text_params,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
+62
-38
@@ -14,10 +14,10 @@ use std::{
|
|||||||
use cmove::CMove;
|
use cmove::CMove;
|
||||||
use constants::BOARD_SIZE;
|
use constants::BOARD_SIZE;
|
||||||
use errors::SError;
|
use errors::SError;
|
||||||
use piece::Piece;
|
use piece::PieceKind;
|
||||||
use square::{Square, SquarePair};
|
use square::{Square, SquarePair};
|
||||||
|
|
||||||
use crate::generator::Puzzle;
|
use crate::{board::piece::Piece, generator::Puzzle};
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct Board {
|
pub struct Board {
|
||||||
@@ -27,6 +27,7 @@ pub struct Board {
|
|||||||
pub id: String,
|
pub id: String,
|
||||||
pub size: usize,
|
pub size: usize,
|
||||||
pieces_remaining: u8,
|
pieces_remaining: u8,
|
||||||
|
max_moves_per_piece: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||||
@@ -43,8 +44,19 @@ impl Default for BoardState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct BoardOptions {
|
||||||
|
pub max_moves_per_piece: u32,
|
||||||
|
}
|
||||||
|
|
||||||
impl Board {
|
impl Board {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
|
let options = BoardOptions {
|
||||||
|
max_moves_per_piece: 2,
|
||||||
|
};
|
||||||
|
Board::create(options)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create(options: BoardOptions) -> Self {
|
||||||
let cells = [[None; BOARD_SIZE]; BOARD_SIZE];
|
let cells = [[None; BOARD_SIZE]; BOARD_SIZE];
|
||||||
let id = Board::encode(cells);
|
let id = Board::encode(cells);
|
||||||
Board {
|
Board {
|
||||||
@@ -54,6 +66,7 @@ impl Board {
|
|||||||
pieces_remaining: 0,
|
pieces_remaining: 0,
|
||||||
game_state: BoardState::NotStarted,
|
game_state: BoardState::NotStarted,
|
||||||
size: BOARD_SIZE,
|
size: BOARD_SIZE,
|
||||||
|
max_moves_per_piece: options.max_moves_per_piece,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,10 +85,10 @@ impl Board {
|
|||||||
let mask = 0b111;
|
let mask = 0b111;
|
||||||
for i in (0..BOARD_SIZE).rev() {
|
for i in (0..BOARD_SIZE).rev() {
|
||||||
for j in (0..BOARD_SIZE).rev() {
|
for j in (0..BOARD_SIZE).rev() {
|
||||||
let piece = Board::get_piece_from_encoding((working & mask) as u8);
|
let piece_kind = Board::get_piece_from_encoding((working & mask) as u8);
|
||||||
working = working >> 3;
|
working = working >> 3;
|
||||||
let piece = piece?;
|
let piece_kind = piece_kind?;
|
||||||
board.set(Square::new(i, j, piece));
|
board.set(Square::new(i, j, Piece::from_kind(piece_kind)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(board)
|
Ok(board)
|
||||||
@@ -91,18 +104,18 @@ impl Board {
|
|||||||
for r in 0..BOARD_SIZE {
|
for r in 0..BOARD_SIZE {
|
||||||
for f in 0..BOARD_SIZE {
|
for f in 0..BOARD_SIZE {
|
||||||
let c = chars.next().unwrap();
|
let c = chars.next().unwrap();
|
||||||
let piece = match c {
|
let piece_kind = match c {
|
||||||
'K' | 'k' => Piece::King,
|
'K' | 'k' => PieceKind::King,
|
||||||
'Q' | 'q' => Piece::Queen,
|
'Q' | 'q' => PieceKind::Queen,
|
||||||
'B' | 'b' => Piece::Bishop,
|
'B' | 'b' => PieceKind::Bishop,
|
||||||
'N' | 'n' => Piece::Knight,
|
'N' | 'n' => PieceKind::Knight,
|
||||||
'R' | 'r' => Piece::Rook,
|
'R' | 'r' => PieceKind::Rook,
|
||||||
'P' | 'p' => Piece::Pawn,
|
'P' | 'p' => PieceKind::Pawn,
|
||||||
'.' => continue,
|
'.' => continue,
|
||||||
_ => return Err(SError::InvalidBoard),
|
_ => return Err(SError::InvalidBoard),
|
||||||
};
|
};
|
||||||
|
|
||||||
let square = Square::new(f, r, Some(piece));
|
let square = Square::new(f, r, Some(Piece::new(piece_kind)));
|
||||||
board.set(square);
|
board.set(square);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,7 +150,14 @@ impl Board {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let from_piece = mem::replace(&mut self.cells[mv.from.file][mv.from.rank], None);
|
let mut from_piece = mem::replace(&mut self.cells[mv.from.file][mv.from.rank], None);
|
||||||
|
if let Some(p) = &mut from_piece {
|
||||||
|
p.moves_made += 1;
|
||||||
|
if p.moves_made >= self.max_moves_per_piece {
|
||||||
|
p.active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.cells[mv.to.file][mv.to.rank] = from_piece;
|
self.cells[mv.to.file][mv.to.rank] = from_piece;
|
||||||
|
|
||||||
self.pieces_remaining -= 1;
|
self.pieces_remaining -= 1;
|
||||||
@@ -278,13 +298,17 @@ impl Board {
|
|||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
let legal = match piece {
|
if piece.moves_made >= self.max_moves_per_piece {
|
||||||
Piece::King => self.is_king_legal(&pair),
|
return None;
|
||||||
Piece::Queen => self.is_queen_legal(&pair),
|
}
|
||||||
Piece::Bishop => self.is_bishop_legal(&pair),
|
|
||||||
Piece::Knight => self.is_knight_legal(&pair),
|
let legal = match piece.kind {
|
||||||
Piece::Rook => self.is_rook_legal(&pair),
|
PieceKind::King => self.is_king_legal(&pair),
|
||||||
Piece::Pawn => self.is_pawn_legal(&pair),
|
PieceKind::Queen => self.is_queen_legal(&pair),
|
||||||
|
PieceKind::Bishop => self.is_bishop_legal(&pair),
|
||||||
|
PieceKind::Knight => self.is_knight_legal(&pair),
|
||||||
|
PieceKind::Rook => self.is_rook_legal(&pair),
|
||||||
|
PieceKind::Pawn => self.is_pawn_legal(&pair),
|
||||||
};
|
};
|
||||||
|
|
||||||
if legal {
|
if legal {
|
||||||
@@ -400,26 +424,26 @@ impl Board {
|
|||||||
|
|
||||||
fn get_piece_encoding(piece: Option<Piece>) -> u8 {
|
fn get_piece_encoding(piece: Option<Piece>) -> u8 {
|
||||||
match piece {
|
match piece {
|
||||||
Some(p) => match p {
|
Some(p) => match p.kind {
|
||||||
Piece::King => 0b001,
|
PieceKind::King => 0b001,
|
||||||
Piece::Queen => 0b010,
|
PieceKind::Queen => 0b010,
|
||||||
Piece::Rook => 0b011,
|
PieceKind::Rook => 0b011,
|
||||||
Piece::Bishop => 0b100,
|
PieceKind::Bishop => 0b100,
|
||||||
Piece::Knight => 0b101,
|
PieceKind::Knight => 0b101,
|
||||||
Piece::Pawn => 0b110,
|
PieceKind::Pawn => 0b110,
|
||||||
},
|
},
|
||||||
None => 0b000,
|
None => 0b000,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_piece_from_encoding(encoding: u8) -> Result<Option<Piece>, SError> {
|
fn get_piece_from_encoding(encoding: u8) -> Result<Option<PieceKind>, SError> {
|
||||||
match encoding {
|
match encoding {
|
||||||
0b001 => Ok(Some(Piece::King)),
|
0b001 => Ok(Some(PieceKind::King)),
|
||||||
0b010 => Ok(Some(Piece::Queen)),
|
0b010 => Ok(Some(PieceKind::Queen)),
|
||||||
0b011 => Ok(Some(Piece::Rook)),
|
0b011 => Ok(Some(PieceKind::Rook)),
|
||||||
0b100 => Ok(Some(Piece::Bishop)),
|
0b100 => Ok(Some(PieceKind::Bishop)),
|
||||||
0b101 => Ok(Some(Piece::Knight)),
|
0b101 => Ok(Some(PieceKind::Knight)),
|
||||||
0b110 => Ok(Some(Piece::Pawn)),
|
0b110 => Ok(Some(PieceKind::Pawn)),
|
||||||
0b000 => Ok(None),
|
0b000 => Ok(None),
|
||||||
_ => Err(SError::InvalidBoard),
|
_ => Err(SError::InvalidBoard),
|
||||||
}
|
}
|
||||||
@@ -433,9 +457,9 @@ impl Board {
|
|||||||
fn get_square_for_display(piece: &Option<Piece>, pretty: bool) -> String {
|
fn get_square_for_display(piece: &Option<Piece>, pretty: bool) -> String {
|
||||||
let contents = if let Some(piece) = piece {
|
let contents = if let Some(piece) = piece {
|
||||||
if pretty {
|
if pretty {
|
||||||
piece.pretty()
|
piece.kind.pretty()
|
||||||
} else {
|
} else {
|
||||||
piece.notation()
|
piece.kind.notation()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
".".to_string()
|
".".to_string()
|
||||||
@@ -557,7 +581,7 @@ mod tests {
|
|||||||
assert!(board.set(sq!("Nb2")).is_none());
|
assert!(board.set(sq!("Nb2")).is_none());
|
||||||
let existing = board.set(sq!("Pc4"));
|
let existing = board.set(sq!("Pc4"));
|
||||||
assert!(existing.is_some());
|
assert!(existing.is_some());
|
||||||
assert_eq!(Piece::Knight, existing.unwrap());
|
assert_eq!(PieceKind::Knight, existing.unwrap().kind);
|
||||||
validate_board!(board, "..PP", "..B.", "QN..", "K..R");
|
validate_board!(board, "..PP", "..B.", "QN..", "K..R");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
use super::{piece::Piece, square::Square};
|
use crate::board::piece::Piece;
|
||||||
|
|
||||||
|
use super::{piece::PieceKind, square::Square};
|
||||||
|
|
||||||
#[derive(PartialEq, Hash, Eq, Clone)]
|
#[derive(PartialEq, Hash, Eq, Clone)]
|
||||||
pub struct CMove {
|
pub struct CMove {
|
||||||
@@ -26,8 +28,8 @@ impl CMove {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn notation(&self) -> String {
|
pub fn notation(&self) -> String {
|
||||||
let piece_qualifier = match &self.from_piece {
|
let piece_qualifier = match &self.from_piece.kind {
|
||||||
Piece::Pawn => self.from.file_notation(),
|
PieceKind::Pawn => self.from.file_notation(),
|
||||||
p => p.notation(),
|
p => p.notation(),
|
||||||
};
|
};
|
||||||
format!(
|
format!(
|
||||||
|
|||||||
+47
-27
@@ -1,5 +1,25 @@
|
|||||||
#[derive(Clone, Eq, Hash, Copy, Debug, PartialEq)]
|
#[derive(Clone, Eq, Hash, Copy, Debug, PartialEq)]
|
||||||
pub enum Piece {
|
pub struct Piece {
|
||||||
|
pub kind: PieceKind,
|
||||||
|
pub moves_made: u32,
|
||||||
|
pub active: bool,
|
||||||
|
}
|
||||||
|
impl Piece {
|
||||||
|
pub fn new(kind: PieceKind) -> Self {
|
||||||
|
Self {
|
||||||
|
kind,
|
||||||
|
moves_made: 0,
|
||||||
|
active: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_kind(kind: Option<PieceKind>) -> Option<Self> {
|
||||||
|
kind.map(|k| Piece::new(k))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Eq, Hash, Copy, Debug, PartialEq)]
|
||||||
|
pub enum PieceKind {
|
||||||
King,
|
King,
|
||||||
Queen,
|
Queen,
|
||||||
Bishop,
|
Bishop,
|
||||||
@@ -8,15 +28,15 @@ pub enum Piece {
|
|||||||
Pawn,
|
Pawn,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Piece {
|
impl PieceKind {
|
||||||
pub fn parse(piece: &str) -> Option<Self> {
|
pub fn parse(piece: &str) -> Option<Self> {
|
||||||
match piece {
|
match piece {
|
||||||
"K" => Some(Piece::King),
|
"K" => Some(PieceKind::King),
|
||||||
"Q" => Some(Piece::Queen),
|
"Q" => Some(PieceKind::Queen),
|
||||||
"B" => Some(Piece::Bishop),
|
"B" => Some(PieceKind::Bishop),
|
||||||
"N" => Some(Piece::Knight),
|
"N" => Some(PieceKind::Knight),
|
||||||
"R" => Some(Piece::Rook),
|
"R" => Some(PieceKind::Rook),
|
||||||
"P" => Some(Piece::Pawn),
|
"P" => Some(PieceKind::Pawn),
|
||||||
"." => None,
|
"." => None,
|
||||||
p => panic!("Invalid piece {}", p),
|
p => panic!("Invalid piece {}", p),
|
||||||
}
|
}
|
||||||
@@ -24,12 +44,12 @@ impl Piece {
|
|||||||
|
|
||||||
pub fn notation(&self) -> String {
|
pub fn notation(&self) -> String {
|
||||||
let n = match self {
|
let n = match self {
|
||||||
Piece::King => "K",
|
PieceKind::King => "K",
|
||||||
Piece::Queen => "Q",
|
PieceKind::Queen => "Q",
|
||||||
Piece::Bishop => "B",
|
PieceKind::Bishop => "B",
|
||||||
Piece::Knight => "N",
|
PieceKind::Knight => "N",
|
||||||
Piece::Rook => "R",
|
PieceKind::Rook => "R",
|
||||||
Piece::Pawn => "P",
|
PieceKind::Pawn => "P",
|
||||||
};
|
};
|
||||||
|
|
||||||
n.to_string()
|
n.to_string()
|
||||||
@@ -37,12 +57,12 @@ impl Piece {
|
|||||||
|
|
||||||
pub fn pretty(&self) -> String {
|
pub fn pretty(&self) -> String {
|
||||||
let n = match self {
|
let n = match self {
|
||||||
Piece::King => "♔",
|
PieceKind::King => "♔",
|
||||||
Piece::Queen => "♕",
|
PieceKind::Queen => "♕",
|
||||||
Piece::Bishop => "♗",
|
PieceKind::Bishop => "♗",
|
||||||
Piece::Knight => "♘",
|
PieceKind::Knight => "♘",
|
||||||
Piece::Rook => "♖",
|
PieceKind::Rook => "♖",
|
||||||
Piece::Pawn => "♙",
|
PieceKind::Pawn => "♙",
|
||||||
};
|
};
|
||||||
|
|
||||||
n.to_string()
|
n.to_string()
|
||||||
@@ -55,17 +75,17 @@ mod tests {
|
|||||||
|
|
||||||
macro_rules! p {
|
macro_rules! p {
|
||||||
($piece:literal) => {
|
($piece:literal) => {
|
||||||
Piece::parse($piece)
|
PieceKind::parse($piece)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_piece_parse() {
|
fn test_piece_parse() {
|
||||||
assert_eq!(p!("K"), Some(Piece::King));
|
assert_eq!(p!("K"), Some(PieceKind::King));
|
||||||
assert_eq!(p!("Q"), Some(Piece::Queen));
|
assert_eq!(p!("Q"), Some(PieceKind::Queen));
|
||||||
assert_eq!(p!("B"), Some(Piece::Bishop));
|
assert_eq!(p!("B"), Some(PieceKind::Bishop));
|
||||||
assert_eq!(p!("N"), Some(Piece::Knight));
|
assert_eq!(p!("N"), Some(PieceKind::Knight));
|
||||||
assert_eq!(p!("R"), Some(Piece::Rook));
|
assert_eq!(p!("R"), Some(PieceKind::Rook));
|
||||||
assert_eq!(p!("P"), Some(Piece::Pawn));
|
assert_eq!(p!("P"), Some(PieceKind::Pawn));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
|
use crate::board::piece::Piece;
|
||||||
|
|
||||||
use super::constants::BOARD_SIZE;
|
use super::constants::BOARD_SIZE;
|
||||||
use super::piece::Piece;
|
use super::piece::PieceKind;
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
|
|
||||||
#[derive(Clone, Eq, Hash, PartialEq)]
|
#[derive(Clone, Eq, Hash, PartialEq)]
|
||||||
@@ -29,8 +31,8 @@ impl Square {
|
|||||||
|
|
||||||
pub fn parse(notation: &str) -> Self {
|
pub fn parse(notation: &str) -> Self {
|
||||||
let mut chars = notation.chars();
|
let mut chars = notation.chars();
|
||||||
let piece = chars.next().expect("Piece missing");
|
let piece_kind = chars.next().expect("Piece missing");
|
||||||
let piece = Piece::parse(&piece.to_string());
|
let piece_kind = PieceKind::parse(&piece_kind.to_string());
|
||||||
let file = chars.next().expect("File missing");
|
let file = chars.next().expect("File missing");
|
||||||
let file = match file {
|
let file = match file {
|
||||||
'a' => 0,
|
'a' => 0,
|
||||||
@@ -45,6 +47,7 @@ impl Square {
|
|||||||
panic!("rank should be between 1-{}", BOARD_SIZE);
|
panic!("rank should be between 1-{}", BOARD_SIZE);
|
||||||
}
|
}
|
||||||
let rank = BOARD_SIZE - rank;
|
let rank = BOARD_SIZE - rank;
|
||||||
|
let piece = piece_kind.map(|kind| Piece::new(kind));
|
||||||
Square::new(file, rank, piece)
|
Square::new(file, rank, piece)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,7 +76,7 @@ impl Square {
|
|||||||
if self.piece.is_none() {
|
if self.piece.is_none() {
|
||||||
"".to_string()
|
"".to_string()
|
||||||
} else {
|
} else {
|
||||||
self.piece.unwrap().notation()
|
self.piece.unwrap().kind.notation()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -131,7 +134,7 @@ mod tests {
|
|||||||
let square = Square::parse(¬ation);
|
let square = Square::parse(¬ation);
|
||||||
assert_eq!(square.file, $file);
|
assert_eq!(square.file, $file);
|
||||||
assert_eq!(square.rank, $rank);
|
assert_eq!(square.rank, $rank);
|
||||||
assert_eq!(square.piece, Some(Piece::King));
|
assert_eq!(square.piece.unwrap().kind, PieceKind::King);
|
||||||
assert_eq!(square.notation(), notation);
|
assert_eq!(square.notation(), notation);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-21
@@ -1,6 +1,10 @@
|
|||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
use crate::board::{Board, cmove::CMove, piece::Piece};
|
use crate::board::{
|
||||||
|
Board, BoardOptions,
|
||||||
|
cmove::CMove,
|
||||||
|
piece::{Piece, PieceKind},
|
||||||
|
};
|
||||||
|
|
||||||
pub trait RandomRange {
|
pub trait RandomRange {
|
||||||
fn gen_range(&self, min: usize, max: usize) -> usize;
|
fn gen_range(&self, min: usize, max: usize) -> usize;
|
||||||
@@ -24,23 +28,24 @@ pub struct GenerateStats {
|
|||||||
pub fn generate_weighted_random(
|
pub fn generate_weighted_random(
|
||||||
num_pieces: u32,
|
num_pieces: u32,
|
||||||
num_solutions: u32,
|
num_solutions: u32,
|
||||||
|
max_moves_per_piece: u32,
|
||||||
rand: &impl RandomRange,
|
rand: &impl RandomRange,
|
||||||
) -> GenerateStats {
|
) -> GenerateStats {
|
||||||
let candidate_pieces = vec![
|
let candidate_pieces = vec![
|
||||||
Piece::Pawn,
|
PieceKind::Pawn,
|
||||||
Piece::Pawn,
|
PieceKind::Pawn,
|
||||||
Piece::Pawn,
|
PieceKind::Pawn,
|
||||||
Piece::Pawn,
|
PieceKind::Pawn,
|
||||||
Piece::Bishop,
|
PieceKind::Bishop,
|
||||||
Piece::Bishop,
|
PieceKind::Bishop,
|
||||||
Piece::Bishop,
|
PieceKind::Bishop,
|
||||||
Piece::Bishop,
|
PieceKind::Bishop,
|
||||||
Piece::Knight,
|
PieceKind::Knight,
|
||||||
Piece::Knight,
|
PieceKind::Knight,
|
||||||
Piece::Knight,
|
PieceKind::Knight,
|
||||||
Piece::Queen,
|
PieceKind::Queen,
|
||||||
Piece::Rook,
|
PieceKind::Rook,
|
||||||
Piece::Rook,
|
PieceKind::Rook,
|
||||||
];
|
];
|
||||||
|
|
||||||
if num_pieces > candidate_pieces.len().try_into().unwrap() {
|
if num_pieces > candidate_pieces.len().try_into().unwrap() {
|
||||||
@@ -53,7 +58,13 @@ pub fn generate_weighted_random(
|
|||||||
let attempts: u32 = 1000;
|
let attempts: u32 = 1000;
|
||||||
let mut overall_stats = GenerateStats::new(0, 0, 0, None, vec![]);
|
let mut overall_stats = GenerateStats::new(0, 0, 0, None, vec![]);
|
||||||
for _ in 0..attempts {
|
for _ in 0..attempts {
|
||||||
let stats = try_generate(num_pieces, num_solutions, rand, candidate_pieces.clone());
|
let stats = try_generate(
|
||||||
|
num_pieces,
|
||||||
|
num_solutions,
|
||||||
|
max_moves_per_piece,
|
||||||
|
rand,
|
||||||
|
candidate_pieces.clone(),
|
||||||
|
);
|
||||||
overall_stats.piece_total += stats.piece_total;
|
overall_stats.piece_total += stats.piece_total;
|
||||||
overall_stats.piece_success += stats.piece_success;
|
overall_stats.piece_success += stats.piece_success;
|
||||||
overall_stats.total += stats.total;
|
overall_stats.total += stats.total;
|
||||||
@@ -117,10 +128,14 @@ where
|
|||||||
fn try_generate(
|
fn try_generate(
|
||||||
num_pieces: u32,
|
num_pieces: u32,
|
||||||
num_solutions: u32,
|
num_solutions: u32,
|
||||||
|
max_moves_per_piece: u32,
|
||||||
rand: &impl RandomRange,
|
rand: &impl RandomRange,
|
||||||
mut candidate_pieces: Vec<Piece>,
|
mut candidate_pieces: Vec<PieceKind>,
|
||||||
) -> GenerateStats {
|
) -> GenerateStats {
|
||||||
let mut board = Board::new();
|
let mut board = Board::create(BoardOptions {
|
||||||
|
max_moves_per_piece,
|
||||||
|
});
|
||||||
|
|
||||||
let mut piece_total = 0;
|
let mut piece_total = 0;
|
||||||
let mut piece_success = 0;
|
let mut piece_success = 0;
|
||||||
for _ in 0..num_pieces {
|
for _ in 0..num_pieces {
|
||||||
@@ -136,10 +151,10 @@ fn try_generate(
|
|||||||
piece_total += 1;
|
piece_total += 1;
|
||||||
|
|
||||||
let index = rand.gen_range(0, candidate_pieces.len());
|
let index = rand.gen_range(0, candidate_pieces.len());
|
||||||
let piece = candidate_pieces[index];
|
let piece_kind = candidate_pieces[index];
|
||||||
let square_index = rand.gen_range(0, empty_squares.len());
|
let square_index = rand.gen_range(0, empty_squares.len());
|
||||||
let mut random_square = empty_squares[square_index].clone();
|
let mut random_square = empty_squares[square_index].clone();
|
||||||
random_square.piece = Some(piece);
|
random_square.piece = Some(Piece::new(piece_kind));
|
||||||
board.set(random_square.clone());
|
board.set(random_square.clone());
|
||||||
let puzzle = board.solve();
|
let puzzle = board.solve();
|
||||||
if puzzle.solutions.len() > 0 {
|
if puzzle.solutions.len() > 0 {
|
||||||
@@ -186,7 +201,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn generator_smoke() {
|
fn generator_smoke() {
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
let gen_stats = generate_weighted_random(5, 5, &TestRandom);
|
let gen_stats = generate_weighted_random(5, 5, 10, &TestRandom);
|
||||||
let board = gen_stats.board.expect("No puzzle was generated");
|
let board = gen_stats.board.expect("No puzzle was generated");
|
||||||
assert_eq!(board.game_state, BoardState::InProgress);
|
assert_eq!(board.game_state, BoardState::InProgress);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user