temporary commit

This commit is contained in:
2025-07-01 22:16:32 +08:00
parent 1348d8369f
commit 6d415468e0
15 changed files with 713 additions and 663 deletions

View File

@@ -1,5 +1,5 @@
use gtk4::{AlertDialog, Window, prelude::IsA};
use rdraught::draughts::Player;
use rdraught::Player;
pub(crate) async fn create_dialog<W: IsA<Window>>(window: W, winner: Player) {
let msg = match winner {

View File

@@ -1,7 +1,7 @@
use gtk4::{Align, Application, Box, CheckButton, Label, Orientation, Window, prelude::*};
use crate::types::SharedMutable;
use rdraught::draughts::Player;
use rdraught::Player;
pub(crate) fn create(application: &Application, current_player: SharedMutable<Player>) -> Window {
let label = Label::builder().label("Main player:").build();

View File

@@ -1,13 +1,6 @@
use glib::ExitCode;
use rdraught;
use rdraught::draughts::DraughtsGame;
use rdraught_ui;
mod geo2d;
mod final_dialog;
mod greeting_dialog;
mod rdraught_application;
mod types;
fn main() -> ExitCode {
let game = DraughtsGame::default();

View File

@@ -5,8 +5,10 @@ use glib::ExitCode;
use gtk4::glib::{MainContext, Propagation};
use gtk4::{self as gtk, gdk::ffi::GDK_BUTTON_PRIMARY};
use gtk4::{Application, DrawingArea, prelude::*};
use rdraught::draughts::{DraughtsBoard, DraughtsGame, Error, Move, Piece, Player};
use rdraught::position::Position;
use rdraught::{
DraughtsBoard, DraughtsGame, Error, Move, Piece, Player, Position, RDraughtApplication,
RectangularBoard,
};
use rsvg::SvgHandle;
use std::thread;
const SQUARE_SIZE: f64 = 1.0;
@@ -169,7 +171,7 @@ fn draw_score_bar(
) {
fn modulate_score(relative_score: f64) -> f64 {
let x = relative_score;
f64::atan(8.0 * x - 4.0) / f64::atan(4.0) / 2.0 + 0.5
1.0 - (f64::atan(8.0 * x - 4.0) / f64::atan(4.0) / 2.0 + 0.5)
}
let score_bar = Rectangle::new(
board.tl().x() - board.width() / 10.0,
@@ -177,31 +179,56 @@ fn draw_score_bar(
board.width() / 16.0,
board.height(),
);
let num_rects = 40usize;
let spacing = board.height() / 200.0;
let rect_height = (board.height() - spacing * (num_rects - 1) as f64) / (num_rects as f64);
let score_percentage = modulate_score(draughts_game.relative_score(current_player) as f64);
let threshold = (score_percentage * num_rects as f64) as usize;
let tl = score_bar.tl();
cr.save().unwrap();
match current_player {
Player::White => cr.set_source_rgb(1.0, 0.0, 0.0),
Player::Red => cr.set_source_rgb(1.0, 1.0, 1.0),
cr.set_line_width(spacing / 4.0);
for i in 0..threshold {
match current_player {
Player::White => cr.set_source_rgb(1.0, 0.0, 0.0),
Player::Red => cr.set_source_rgb(1.0, 1.0, 1.0),
}
cr.rectangle(
tl.x(),
tl.y() + i as f64 * (rect_height + spacing),
score_bar.width(),
rect_height,
);
cr.fill().unwrap();
cr.set_source_rgb(0.0, 0.0, 0.0);
cr.rectangle(
tl.x(),
tl.y() + i as f64 * (rect_height + spacing),
score_bar.width(),
rect_height,
);
cr.stroke().unwrap();
}
cr.rectangle(
score_bar.tl().x(),
score_bar.tl().y(),
score_bar.width(),
score_bar.height() * (1.0 - score_percentage),
);
cr.fill().unwrap();
match current_player {
Player::White => cr.set_source_rgb(1.0, 1.0, 1.0),
Player::Red => cr.set_source_rgb(1.0, 0.0, 0.0),
for i in threshold..num_rects {
match current_player {
Player::White => cr.set_source_rgb(1.0, 1.0, 1.0),
Player::Red => cr.set_source_rgb(1.0, 0.0, 0.0),
}
cr.rectangle(
tl.x(),
tl.y() + i as f64 * (rect_height + spacing),
score_bar.width(),
rect_height,
);
cr.fill().unwrap();
cr.set_source_rgb(0.0, 0.0, 0.0);
cr.rectangle(
tl.x(),
tl.y() + i as f64 * (rect_height + spacing),
score_bar.width(),
rect_height,
);
cr.stroke().unwrap();
}
cr.rectangle(
tl.x(),
tl.y() + score_bar.height() * (1.0 - score_percentage),
score_bar.width(),
score_bar.height() * score_percentage,
);
cr.fill().unwrap();
cr.restore().unwrap();
}
@@ -316,7 +343,7 @@ fn create_game_window(
draw_piece(
cr,
&square,
rd.borrow().game.piece_at(position),
position.map_or(Piece::NoPiece, |p| rd.borrow().game().piece_at(p)),
&crown_red_handle,
&crown_white_handle,
)
@@ -326,16 +353,12 @@ fn create_game_window(
}
if let Some(selected_position) = selected_piece.get() {
let screen_position = match current_player {
Player::White => {
Position::new(8 - 1 - selected_position.row(), selected_position.col())
}
Player::Red => {
Position::new(selected_position.row(), 8 - 1 - selected_position.col())
}
Player::White => (8 - 1 - selected_position.row(), selected_position.col()),
Player::Red => (selected_position.row(), 8 - 1 - selected_position.col()),
};
let square = Rectangle::new(
screen_position.col() as f64 * SQUARE_SIZE,
screen_position.row() as f64 * SQUARE_SIZE,
screen_position.1 as f64 * SQUARE_SIZE,
screen_position.0 as f64 * SQUARE_SIZE,
SQUARE_SIZE,
SQUARE_SIZE,
);
@@ -349,7 +372,7 @@ fn create_game_window(
cr.clip();
cr.new_path();
cr.set_source_rgb(0.0, 0.0, 1.0);
cr.set_line_width((square.width() + square.height()) * 0.05);
cr.set_line_width((square.width() + square.height()) * 0.035);
cr.move_to(square.tl().x(), square.tl().y());
cr.line_to(square.tl().x(), square.br().y());
cr.line_to(square.br().x(), square.br().y());
@@ -364,12 +387,12 @@ fn create_game_window(
for mv in am.iter() {
let end_pos = mv.get_end_position();
let screen_position = match current_player {
Player::White => Position::new(8 - 1 - end_pos.row(), end_pos.col()),
Player::Red => Position::new(end_pos.row(), 8 - 1 - end_pos.col()),
Player::White => (8 - 1 - end_pos.row(), end_pos.col()),
Player::Red => (end_pos.row(), 8 - 1 - end_pos.col()),
};
let square = Rectangle::new(
screen_position.col() as f64 * SQUARE_SIZE,
screen_position.row() as f64 * SQUARE_SIZE,
screen_position.1 as f64 * SQUARE_SIZE,
screen_position.0 as f64 * SQUARE_SIZE,
SQUARE_SIZE,
SQUARE_SIZE,
);
@@ -383,7 +406,7 @@ fn create_game_window(
cr.clip();
cr.new_path();
cr.set_source_rgb(0.0, 1.0, 0.0);
cr.set_line_width((square.width() + square.height()) * 0.05);
cr.set_line_width((square.width() + square.height()) * 0.035);
cr.move_to(square.tl().x(), square.tl().y());
cr.line_to(square.tl().x(), square.br().y());
cr.line_to(square.br().x(), square.br().y());
@@ -393,7 +416,7 @@ fn create_game_window(
cr.restore().unwrap();
}
}
draw_score_bar(cr, &board, &rd.borrow().game, current_player);
draw_score_bar(cr, &board, rd.borrow().game(), current_player);
});
}
let gesture = gtk::GestureClick::new();
@@ -408,7 +431,7 @@ fn create_game_window(
let window = window.clone();
gesture.connect_pressed(move |gesture, _, x, y| {
gesture.set_state(gtk::EventSequenceState::Claimed);
if let Some(winner) = rd.borrow().game.winner() {
if let Some(winner) = rd.borrow().game().winner() {
MainContext::default()
.spawn_local(final_dialog::create_dialog(window.clone(), winner));
} else {
@@ -432,32 +455,38 @@ fn create_game_window(
(8.0 - p.x() / SQUARE_SIZE) as u8,
),
};
// println!("Selected position: {:?}", position);
println!("Selected position: {:?}", position);
let piece = {
let draughts_game = &rd.borrow().game;
draughts_game.piece_at(position)
let rd = rd.borrow();
let draughts_game = rd.game();
position
.clone()
.map_or(Piece::NoPiece, |it| draughts_game.piece_at(it))
// println!("Selected piece: {:?}", piece);
};
let am = available_moves.replace(Vec::new());
let mut move_applied = false;
if !am.is_empty() {
for mv in am.into_iter() {
if mv.get_end_position() == position {
let mut rd_app = rd.borrow_mut();
println!("Applied move: {:?}", mv);
rd_app.apply_move(mv).unwrap();
let game_copy = rd_app.game.clone();
thread::spawn(move || {
if let (Some(mv), analyzed_moves) = game_copy.get_best_move(10)
{
println!(
"Next best move: {:?}, analyzed moves: {}",
mv, analyzed_moves
);
}
});
move_applied = true;
break;
if let Ok(pos) = position {
for mv in am.into_iter() {
if mv.get_end_position() == pos {
let mut rd_app = rd.borrow_mut();
println!("Applied move: {:?}", mv);
rd_app.apply_move(mv).unwrap();
let game_copy = rd_app.game().clone();
thread::spawn(move || {
if let (Some(mv), analyzed_moves) =
game_copy.get_best_move(10)
{
println!(
"Next best move: {:?}, analyzed moves: {}",
mv, analyzed_moves
);
}
});
move_applied = true;
break;
}
}
}
if move_applied {
@@ -465,18 +494,21 @@ fn create_game_window(
}
}
if !move_applied {
let position = position.ok();
match piece.player() {
Some(Player::Red) => selected_piece.set(Some(position)),
Some(Player::White) => selected_piece.set(Some(position)),
Some(Player::Red) => selected_piece.set(position),
Some(Player::White) => selected_piece.set(position),
None => selected_piece.set(None),
}
if piece.player().is_none() {
selected_piece.set(None)
} else {
let mut am = available_moves.borrow_mut();
selected_piece.set(Some(position));
for mv in rd.borrow().game.moves_for_piece(position) {
am.push(mv);
selected_piece.set(position);
if let Some(position) = position {
for mv in rd.borrow().game().moves_for_piece(position) {
am.push(mv);
}
}
}
}
@@ -491,12 +523,6 @@ fn create_game_window(
drawing_area.add_controller(gesture);
window.present();
}
struct RDraughtApplication {
initial_state: DraughtsGame,
game: DraughtsGame,
moves: Vec<Move>,
cursor: usize,
}
fn on_activate(application: &Application, game: DraughtsGame) {
// Initialize GTK before using any GTK functions.
@@ -515,53 +541,6 @@ fn on_activate(application: &Application, game: DraughtsGame) {
});
}
impl RDraughtApplication {
fn new(game: DraughtsGame) -> RDraughtApplication {
RDraughtApplication {
initial_state: game.clone(),
game,
moves: Vec::<Move>::new(),
cursor: 0usize,
}
}
fn undo(&mut self) -> Result<(), Error> {
let mut new_state = self.initial_state.clone();
if self.cursor > 0 && !self.moves.is_empty() {
self.cursor -= 1;
for mv in &self.moves[0..self.cursor] {
new_state.apply_move(mv)?;
}
self.game = new_state;
Ok(())
} else {
Err(Error::InvalidMove)
}
}
fn redo(&mut self) -> Result<(), Error> {
let mut new_state = self.initial_state.clone();
if self.cursor < self.moves.len() {
for mv in &self.moves[0..self.cursor] {
new_state.apply_move(mv)?;
}
self.game = new_state;
self.cursor += 1;
Ok(())
} else {
Err(Error::InvalidMove)
}
}
fn apply_move(&mut self, mv: Move) -> Result<(), Error> {
self.game.apply_move(&mv)?;
self.moves.truncate(self.cursor);
self.moves.push(mv);
self.cursor += 1;
Ok(())
}
}
pub fn run(game: DraughtsGame) -> ExitCode {
// Create a new application with the builder pattern
let app = Application::builder()