Finish, mudinji pochi

This commit is contained in:
cool-mist 2025-05-25 23:21:47 +05:30
parent d758d03f2d
commit c551715a52
6 changed files with 184 additions and 93 deletions

View File

@ -4,6 +4,8 @@ Goal: Generate 'hard' puzzles.
### Play a demo of the game [here](https://games.neophyte.me/sol_chess/) ### Play a demo of the game [here](https://games.neophyte.me/sol_chess/)
![img](./img/sol_chess.png)
## Install ## Install
- Install Rust from [here](https://www.rust-lang.org/tools/install). - Install Rust from [here](https://www.rust-lang.org/tools/install).

BIN
img/sol_chess.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 KiB

View File

@ -3,8 +3,10 @@ use std::{
fmt::{self, Display, Formatter}, fmt::{self, Display, Formatter},
}; };
use button::{Button, ButtonColor}; use button::Button;
use color::UiColor;
use macroquad::{math, prelude::*, rand}; use macroquad::{math, prelude::*, rand};
use shadow::draw_shadow;
use sol_chess::{ use sol_chess::{
board::{Board, BoardState}, board::{Board, BoardState},
generator::{self, RandomRange}, generator::{self, RandomRange},
@ -12,6 +14,8 @@ use sol_chess::{
use texture::PieceTexture; use texture::PieceTexture;
pub mod button; pub mod button;
pub mod color;
pub mod shadow;
pub mod texture; pub mod texture;
pub struct MacroquadRandAdapter; pub struct MacroquadRandAdapter;
@ -43,11 +47,14 @@ pub struct Game {
square_width: f32, square_width: f32,
window_height: f32, window_height: f32,
window_width: f32, window_width: f32,
board_rect: Rect,
squares: Vec<GameSquare>, squares: Vec<GameSquare>,
heading_rect: Rect, heading_rect: Rect,
heading_font_size: f32, heading_font_size: f32,
gp_btns: HashMap<ButtonAction, Button>, gp_btns: HashMap<ButtonAction, Button>,
mode_btns: HashMap<GameMode, Button>, mode_btns: HashMap<GameMode, Button>,
rules: bool,
rules_btn: Vec<Button>,
} }
struct GameSquare { struct GameSquare {
@ -89,6 +96,7 @@ impl Game {
Self { Self {
original_board: board.clone(), original_board: board.clone(),
board, board,
board_rect: Rect::new(0., 0., 0., 0.),
squares: Vec::new(), squares: Vec::new(),
heading_rect: Rect::new(0., 0., 0., 0.), heading_rect: Rect::new(0., 0., 0., 0.),
heading_text: "Solitaire Chess".to_string(), heading_text: "Solitaire Chess".to_string(),
@ -100,6 +108,8 @@ impl Game {
debug: false, debug: false,
gp_btns: HashMap::new(), gp_btns: HashMap::new(),
mode_btns: HashMap::new(), mode_btns: HashMap::new(),
rules: false,
rules_btn: Vec::new(),
window_height: 0., window_height: 0.,
window_width: 0., window_width: 0.,
square_width: 0., square_width: 0.,
@ -127,29 +137,54 @@ impl Game {
} }
pub fn handle_input(&mut self) { pub fn handle_input(&mut self) {
let mut btn_clicked = None; let mut gp_btn_clicked = None;
for btn in &mut self.gp_btns { for btn in &mut self.gp_btns {
btn.1.handle_input(); btn.1.handle_input();
if btn.1.is_clicked() { if btn.1.is_clicked() {
btn_clicked = Some(btn.0.clone()); gp_btn_clicked = Some(btn.0.clone());
break; break;
} }
} }
if let Some(action) = btn_clicked { if let Some(action) = gp_btn_clicked {
match action { match action {
ButtonAction::Reset => self.reset(), ButtonAction::Reset => self.reset(),
ButtonAction::Next => self.next_puzzle(), ButtonAction::Next => self.next_puzzle(),
} }
} else { } else {
let mut mode_btn_clicked = None;
for btn in &mut self.mode_btns { for btn in &mut self.mode_btns {
btn.1.handle_input(); btn.1.handle_input();
if btn.1.is_clicked() { if btn.1.is_clicked() {
self.game_mode = *btn.0; mode_btn_clicked = Some(btn);
self.next_puzzle();
break; break;
} }
} }
if let Some(btn) = mode_btn_clicked {
self.game_mode = *btn.0;
self.next_puzzle();
} else {
let mut rules_btn_clicked = false;
for btn in &mut self.rules_btn {
btn.handle_input();
if btn.is_clicked() {
rules_btn_clicked = true;
break;
}
}
if rules_btn_clicked {
self.rules = !self.rules;
if self.rules {
self.rules_btn[0].text = "Close".to_string();
self.rules_btn[0].color = UiColor::Green;
} else {
self.rules_btn[0].text = "Rules".to_string();
self.rules_btn[0].color = UiColor::Brown;
}
}
}
} }
for btn in &mut self.mode_btns { for btn in &mut self.mode_btns {
@ -160,6 +195,11 @@ impl Game {
} }
} }
if is_key_released(KeyCode::Escape) {
self.rules = false;
return;
}
if is_key_released(KeyCode::D) { if is_key_released(KeyCode::D) {
self.debug = !self.debug; self.debug = !self.debug;
return; return;
@ -218,15 +258,45 @@ impl Game {
} }
fn draw_board(&self) { fn draw_board(&self) {
let board_shadow_width = 0.1 * self.square_width;
draw_shadow(self.board_rect, board_shadow_width);
if self.rules {
draw_rectangle(
self.board_rect.x,
self.board_rect.y,
self.board_rect.w,
self.board_rect.h,
UiColor::Brown.to_bg_color(),
);
let font_size = self.heading_font_size * 0.8;
let rules = "\
Every move should be a \n\
capture. Win when only \n\
one piece is left.\n";
let measurement = measure_text(rules, None, font_size as u16, 1.0);
draw_multiline_text(
rules,
self.board_rect.x + 0.05 * self.square_width,
self.board_rect.y + 0.5 * (self.board_rect.h - measurement.height)
- 2. * measurement.offset_y,
font_size,
Some(2.),
UiColor::Brown.to_fg_color(),
);
return;
}
let sprite_size = 0.8 * self.square_width; let sprite_size = 0.8 * self.square_width;
let mut selected_square = None; let mut selected_square = None;
self.squares.iter().for_each(|square| { self.squares.iter().for_each(|square| {
let color = if square.is_source { let color = match square.is_source {
Color::from_rgba(112, 105, 141, 255) true => square.color,
} else if square.is_target { false => match square.is_target {
Color::from_rgba(112, 150, 141, 255) true => UiColor::Pink.to_shadow_color(),
} else { false => square.color,
square.color },
}; };
draw_rectangle( draw_rectangle(
@ -276,6 +346,10 @@ impl Game {
for btn in &self.mode_btns { for btn in &self.mode_btns {
btn.1.draw(); btn.1.draw();
} }
for btn in &self.rules_btn {
btn.draw();
}
} }
fn draw_debug(&self) { fn draw_debug(&self) {
@ -310,6 +384,7 @@ impl Game {
let board_width = self.square_width * self.num_squares as f32; let board_width = self.square_width * self.num_squares as f32;
let board_x = (self.window_width - board_width) / 2.0; let board_x = (self.window_width - board_width) / 2.0;
let board_y = (self.window_height - board_width) / 2.0; let board_y = (self.window_height - board_width) / 2.0;
self.board_rect = Rect::new(board_x, board_y, board_width, board_width);
self.heading_font_size = 0.07 * min_dimension; self.heading_font_size = 0.07 * min_dimension;
let f = self.heading_font_size.floor() as u16; let f = self.heading_font_size.floor() as u16;
@ -321,8 +396,8 @@ impl Game {
dims.height, dims.height,
); );
let dark = Color::from_rgba(83, 104, 120, 255); let dark = UiColor::Brown.to_bg_color();
let light = Color::from_rgba(190, 190, 190, 255); let light = UiColor::Yellow.to_bg_color();
let mut rects = Vec::new(); let mut rects = Vec::new();
for i in 0..self.num_squares { for i in 0..self.num_squares {
for j in 0..self.num_squares { for j in 0..self.num_squares {
@ -356,7 +431,7 @@ impl Game {
let reset_btn = Button::new( let reset_btn = Button::new(
"Reset", "Reset",
Rect::new(board_x + btn_x_offset, btn_y, btn_w, btn_h), Rect::new(board_x + btn_x_offset, btn_y, btn_w, btn_h),
ButtonColor::Red, UiColor::Yellow,
); );
let mut next_btn = Button::new( let mut next_btn = Button::new(
"Next", "Next",
@ -366,11 +441,22 @@ impl Game {
btn_w, btn_w,
btn_h, btn_h,
), ),
ButtonColor::Green, UiColor::Green,
); );
next_btn.is_active = false; next_btn.is_active = false;
let rules_button = Button::new(
"Rules",
Rect::new(
(board_x - btn_w) / 2.,
board_y + (self.square_width - btn_h) / 2.,
btn_w,
btn_h,
),
UiColor::Brown,
);
self.rules_btn = vec![rules_button];
self.gp_btns = HashMap::new(); self.gp_btns = HashMap::new();
self.gp_btns.insert(ButtonAction::Next, next_btn); self.gp_btns.insert(ButtonAction::Next, next_btn);
self.gp_btns.insert(ButtonAction::Reset, reset_btn); self.gp_btns.insert(ButtonAction::Reset, reset_btn);
@ -379,33 +465,33 @@ impl Game {
"Easy", "Easy",
Rect::new( Rect::new(
(board_x - btn_w) / 2., (board_x - btn_w) / 2.,
board_y + (self.square_width - btn_h) / 2., board_y + self.square_width + (self.square_width - btn_h) / 2.,
btn_w, btn_w,
btn_h, btn_h,
), ),
ButtonColor::Blue, UiColor::Yellow,
); );
let medium_btn = Button::new( let medium_btn = Button::new(
"Medium", "Medium",
Rect::new( Rect::new(
(board_x - btn_w) / 2., (board_x - btn_w) / 2.,
board_y + self.square_width + (self.square_width - btn_h) / 2., board_y + 2. * self.square_width + (self.square_width - btn_h) / 2.,
btn_w, btn_w,
btn_h, btn_h,
), ),
ButtonColor::Blue, UiColor::Yellow,
); );
let hard_button = Button::new( let hard_button = Button::new(
"Hard", "Hard",
Rect::new( Rect::new(
(board_x - btn_w) / 2., (board_x - btn_w) / 2.,
board_y + 2. * self.square_width + (self.square_width - btn_h) / 2., board_y + 3. * self.square_width + (self.square_width - btn_h) / 2.,
btn_w, btn_w,
btn_h, btn_h,
), ),
ButtonColor::Blue, UiColor::Yellow,
); );
self.mode_btns = HashMap::new(); self.mode_btns = HashMap::new();

View File

@ -1,45 +1,19 @@
use macroquad::prelude::*; use macroquad::prelude::*;
use super::{color::UiColor, shadow::draw_shadow};
pub struct Button { pub struct Button {
pub is_active: bool, pub is_active: bool,
text: String, pub text: String,
is_down: bool, is_down: bool,
is_clicked: bool, is_clicked: bool,
rect: Rect, rect: Rect,
shadow_width: f32, shadow_width: f32,
color: ButtonColor, pub color: UiColor,
}
pub enum ButtonColor {
Grey,
Green,
Red,
Blue,
}
impl ButtonColor {
fn to_bg_color(&self) -> Color {
match self {
ButtonColor::Grey => Color::from_rgba(140, 140, 140, 200),
ButtonColor::Green => Color::from_rgba(112, 140, 141, 200),
ButtonColor::Red => Color::from_rgba(123, 70, 85, 200),
ButtonColor::Blue => Color::from_rgba(140, 120, 250, 200),
}
}
fn to_shadow_color(&self) -> Color {
let bg_color = self.to_bg_color();
Color::from_rgba(
(bg_color.r * 255.) as u8,
(bg_color.g * 255.) as u8,
(bg_color.b * 255.) as u8,
100,
)
}
} }
impl Button { impl Button {
pub fn new(text: &str, rect: Rect, color: ButtonColor) -> Self { pub fn new(text: &str, rect: Rect, color: UiColor) -> Self {
Self { Self {
text: text.to_string(), text: text.to_string(),
is_down: false, is_down: false,
@ -68,9 +42,8 @@ impl Button {
fn draw_button(&self) { fn draw_button(&self) {
let bg_color = match self.is_active { let bg_color = match self.is_active {
true => self.color.to_bg_color(), true => self.color.to_bg_color(),
false => ButtonColor::Grey.to_bg_color(), false => self.color.to_shadow_color(),
}; };
let button_draw_offset = self.get_button_draw_offset(); let button_draw_offset = self.get_button_draw_offset();
draw_rectangle( draw_rectangle(
self.rect.x + button_draw_offset, self.rect.x + button_draw_offset,
@ -92,47 +65,12 @@ impl Button {
return; return;
} }
let color = self.color.to_shadow_color(); draw_shadow(self.rect, self.shadow_width);
draw_rectangle(
self.rect.x + self.rect.w,
self.rect.y + self.shadow_width,
self.shadow_width,
self.rect.h,
color,
);
draw_rectangle(
self.rect.x + self.shadow_width,
self.rect.y + self.rect.h,
self.rect.w - self.shadow_width,
self.shadow_width,
color,
);
draw_triangle(
vec2(self.rect.x + self.rect.w, self.rect.y),
vec2(
self.rect.x + self.rect.w + self.shadow_width,
self.rect.y + self.shadow_width,
),
vec2(self.rect.x + self.rect.w, self.rect.y + self.shadow_width),
color,
);
draw_triangle(
vec2(self.rect.x, self.rect.y + self.rect.h),
vec2(
self.rect.x + self.shadow_width,
self.rect.y + self.rect.h + self.shadow_width,
),
vec2(self.rect.x + self.shadow_width, self.rect.y + self.rect.h),
color,
);
} }
fn draw_label(&self) { fn draw_label(&self) {
let font_color = match self.is_active { let font_color = match self.is_active {
true => Color::from_rgba(0, 0, 0, 255), true => self.color.to_fg_color(),
false => Color::from_rgba(100, 100, 100, 255), false => Color::from_rgba(100, 100, 100, 255),
}; };

45
src/game/color.rs Normal file
View File

@ -0,0 +1,45 @@
use macroquad::prelude::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UiColor {
Grey,
Green,
Pink,
Brown,
Yellow,
Blue,
}
impl UiColor {
pub fn to_bg_color(&self) -> Color {
match self {
UiColor::Grey => Color::from_rgba(140, 140, 140, 200),
UiColor::Green => Color::from_rgba(16, 60, 50, 200),
UiColor::Pink => Color::from_rgba(234, 128, 71, 200),
UiColor::Brown => Color::from_rgba(123, 61, 35, 200),
UiColor::Yellow => Color::from_rgba(242, 230, 190, 200),
UiColor::Blue => Color::from_rgba(47, 85, 172, 200),
}
}
pub fn to_fg_color(&self) -> Color {
match self {
UiColor::Grey => Color::from_rgba(255, 255, 255, 200),
UiColor::Green => Color::from_rgba(255, 255, 255, 200),
UiColor::Pink => Color::from_rgba(255, 255, 255, 200),
UiColor::Brown => Color::from_rgba(255, 255, 255, 200),
UiColor::Yellow => Color::from_rgba(0, 0, 0, 200),
UiColor::Blue => Color::from_rgba(255, 255, 255, 200),
}
}
pub fn to_shadow_color(&self) -> Color {
let bg_color = self.to_bg_color();
Color::from_rgba(
(bg_color.r * 255.) as u8,
(bg_color.g * 255.) as u8,
(bg_color.b * 255.) as u8,
100,
)
}
}

20
src/game/shadow.rs Normal file
View File

@ -0,0 +1,20 @@
use macroquad::prelude::*;
pub fn draw_shadow(rect: Rect, shadow_width: f32) {
let shadow_color = Color::new(0., 0., 0., 0.8);
draw_rectangle(
rect.x + rect.w,
rect.y + shadow_width,
shadow_width,
rect.h,
shadow_color,
);
draw_rectangle(
rect.x + shadow_width,
rect.y + rect.h,
rect.w - shadow_width,
shadow_width,
shadow_color,
);
}