Compare commits

...

2 commits

Author SHA1 Message Date
Henri Bourcereau 73cc6ee67e doc 2025-08-30 13:28:00 +02:00
Henri Bourcereau f2a89f60bc feat: Karel Peeters board game implementation 2025-08-28 19:20:06 +02:00
11 changed files with 561 additions and 150 deletions

251
Cargo.lock generated
View file

@ -2,6 +2,15 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "addr2line"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
dependencies = [
"gimli",
]
[[package]]
name = "adler2"
version = "2.0.1"
@ -158,6 +167,24 @@ dependencies = [
"syn 2.0.104",
]
[[package]]
name = "arimaa_engine_step"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1c6726d7896a539a62e157b05fa4b7308ffb7872f2b4a2a592d5adb19837861"
dependencies = [
"anyhow",
"itertools 0.10.5",
"log",
"regex",
]
[[package]]
name = "arrayvec"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
[[package]]
name = "arrayvec"
version = "0.7.6"
@ -204,7 +231,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f3efb2ca85bc610acfa917b5aaa36f3fcbebed5b3182d7f877b02531c4b80c8"
dependencies = [
"anyhow",
"arrayvec",
"arrayvec 0.7.6",
"log",
"nom",
"num-rational",
@ -217,7 +244,22 @@ version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98922d6a4cfbcb08820c69d8eeccc05bb1f29bfa06b4f5b1dbfe9a868bd7608e"
dependencies = [
"arrayvec",
"arrayvec 0.7.6",
]
[[package]]
name = "backtrace"
version = "0.3.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
dependencies = [
"addr2line",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
"windows-targets 0.52.6",
]
[[package]]
@ -314,14 +356,39 @@ dependencies = [
"generic-array",
]
[[package]]
name = "board-game"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "647fc8459363368aae04df3d21da37094430c57dd993d09be2792133d5365e3e"
dependencies = [
"arimaa_engine_step",
"cast_trait",
"chess",
"decorum",
"internal-iterator",
"itertools 0.10.5",
"lazy_static",
"nohash-hasher",
"nom",
"num-traits",
"once_cell",
"rand 0.8.5",
"rand_xoshiro",
"rayon",
"static_assertions",
]
[[package]]
name = "bot"
version = "0.1.0"
dependencies = [
"board-game",
"burn",
"burn-rl",
"confy",
"env_logger 0.10.0",
"internal-iterator",
"log",
"pretty_assertions",
"rand 0.8.5",
@ -797,6 +864,12 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
[[package]]
name = "cast_trait"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4f8d981c476baadf74cd52897866a1d279d3e14e2d5e2d9af045210e0ae6128"
[[package]]
name = "castaway"
version = "0.2.3"
@ -863,6 +936,18 @@ dependencies = [
"zeroize",
]
[[package]]
name = "chess"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ed299b171ec34f372945ad6726f7bc1d2afd5f59fb8380f64f48e2bab2f0ec8"
dependencies = [
"arrayvec 0.5.2",
"failure",
"nodrop",
"rand 0.7.3",
]
[[package]]
name = "cipher"
version = "0.4.4"
@ -1446,6 +1531,15 @@ version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476"
[[package]]
name = "decorum"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "281759d3c8a14f5c3f0c49363be56810fcd7f910422f97f2db850c2920fde5cf"
dependencies = [
"num-traits",
]
[[package]]
name = "deranged"
version = "0.4.0"
@ -1759,6 +1853,28 @@ dependencies = [
"zune-inflate",
]
[[package]]
name = "failure"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86"
dependencies = [
"backtrace",
"failure_derive",
]
[[package]]
name = "failure_derive"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
"synstructure 0.12.6",
]
[[package]]
name = "fallible-iterator"
version = "0.3.0"
@ -2192,6 +2308,12 @@ dependencies = [
"weezl",
]
[[package]]
name = "gimli"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "gix-features"
version = "0.42.1"
@ -2374,7 +2496,7 @@ dependencies = [
"num-traits",
"ordered-float 5.0.0",
"rand 0.8.5",
"rand_pcg",
"rand_pcg 0.3.1",
"sdl2",
"serde",
]
@ -2573,6 +2695,12 @@ dependencies = [
"syn 2.0.104",
]
[[package]]
name = "internal-iterator"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "969ee3fc68ec2e88eb21434ce4d9b7e1600d1ce92ff974560a6c4a304f5124b9"
[[package]]
name = "interpolate_name"
version = "0.2.4"
@ -2601,6 +2729,15 @@ version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itertools"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]]
name = "itertools"
version = "0.12.1"
@ -2959,7 +3096,7 @@ version = "25.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b977c445f26e49757f9aca3631c3b8b836942cb278d69a92e7b80d3b24da632"
dependencies = [
"arrayvec",
"arrayvec 0.7.6",
"bit-set",
"bitflags 2.9.1",
"cfg_aliases",
@ -3036,6 +3173,18 @@ version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
[[package]]
name = "nodrop"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
[[package]]
name = "nohash-hasher"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
[[package]]
name = "nom"
version = "7.1.3"
@ -3235,6 +3384,15 @@ dependencies = [
"malloc_buf",
]
[[package]]
name = "object"
version = "0.36.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
dependencies = [
"memchr",
]
[[package]]
name = "octets"
version = "0.2.0"
@ -3592,6 +3750,18 @@ dependencies = [
"uuid",
]
[[package]]
name = "rand"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
"rand_chacha 0.2.2",
"rand_core 0.5.1",
"rand_hc",
"rand_pcg 0.2.1",
]
[[package]]
name = "rand"
version = "0.8.5"
@ -3614,6 +3784,16 @@ dependencies = [
"rand_core 0.9.3",
]
[[package]]
name = "rand_chacha"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
dependencies = [
"ppv-lite86",
"rand_core 0.5.1",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
@ -3634,6 +3814,12 @@ dependencies = [
"rand_core 0.9.3",
]
[[package]]
name = "rand_core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
[[package]]
name = "rand_core"
version = "0.6.4"
@ -3663,6 +3849,24 @@ dependencies = [
"rand 0.9.1",
]
[[package]]
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [
"rand_core 0.5.1",
]
[[package]]
name = "rand_pcg"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
dependencies = [
"rand_core 0.5.1",
]
[[package]]
name = "rand_pcg"
version = "0.3.1"
@ -3672,6 +3876,15 @@ dependencies = [
"rand_core 0.6.4",
]
[[package]]
name = "rand_xoshiro"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa"
dependencies = [
"rand_core 0.6.4",
]
[[package]]
name = "range-alloc"
version = "0.1.4"
@ -3729,7 +3942,7 @@ checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9"
dependencies = [
"arbitrary",
"arg_enum_proc_macro",
"arrayvec",
"arrayvec 0.7.6",
"av1-grain",
"bitstream-io",
"built",
@ -4013,6 +4226,12 @@ dependencies = [
"smallvec",
]
[[package]]
name = "rustc-demangle"
version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
[[package]]
name = "rustc-hash"
version = "1.1.0"
@ -4522,6 +4741,18 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "synstructure"
version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
"unicode-xid",
]
[[package]]
name = "synstructure"
version = "0.13.2"
@ -5273,7 +5504,7 @@ version = "25.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec8fb398f119472be4d80bc3647339f56eb63b2a331f6a3d16e25d8144197dd9"
dependencies = [
"arrayvec",
"arrayvec 0.7.6",
"bitflags 2.9.1",
"cfg_aliases",
"document-features",
@ -5301,7 +5532,7 @@ version = "25.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7b882196f8368511d613c6aeec80655160db6646aebddf8328879a88d54e500"
dependencies = [
"arrayvec",
"arrayvec 0.7.6",
"bit-set",
"bit-vec",
"bitflags 2.9.1",
@ -5360,7 +5591,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f968767fe4d3d33747bbd1473ccd55bf0f6451f55d733b5597e67b5deab4ad17"
dependencies = [
"android_system_properties",
"arrayvec",
"arrayvec 0.7.6",
"ash",
"bit-set",
"bitflags 2.9.1",
@ -5783,7 +6014,7 @@ dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
"synstructure",
"synstructure 0.13.2",
]
[[package]]
@ -5824,7 +6055,7 @@ dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
"synstructure",
"synstructure 0.13.2",
]
[[package]]

View file

@ -24,3 +24,5 @@ burn = { version = "0.17", features = ["ndarray", "autodiff"] }
burn-rl = { git = "https://github.com/yunjhongwu/burn-rl-examples.git", package = "burn-rl" }
log = "0.4.20"
confy = "1.0.0"
board-game = "0.8.2"
internal-iterator = "0.2.3"

View file

@ -281,79 +281,8 @@ impl TrictracEnvironment {
let mut reward = 0.0;
let mut is_rollpoint = false;
let event = match action {
TrictracAction::Roll => {
// Lancer les dés
Some(GameEvent::Roll {
player_id: self.active_player_id,
})
}
// TrictracAction::Mark => {
// // Marquer des points
// let points = self.game.
// Some(GameEvent::Mark {
// player_id: self.active_player_id,
// points,
// })
// }
TrictracAction::Go => {
// Continuer après avoir gagné un trou
Some(GameEvent::Go {
player_id: self.active_player_id,
})
}
TrictracAction::Move {
dice_order,
checker1,
checker2,
} => {
// Effectuer un mouvement
let (dice1, dice2) = if dice_order {
(self.game.dice.values.0, self.game.dice.values.1)
} else {
(self.game.dice.values.1, self.game.dice.values.0)
};
let color = &store::Color::White;
let from1 = self
.game
.board
.get_checker_field(color, checker1 as u8)
.unwrap_or(0);
let mut to1 = from1 + dice1 as usize;
let checker_move1 = store::CheckerMove::new(from1, to1).unwrap_or_default();
let mut tmp_board = self.game.board.clone();
let move_result = tmp_board.move_checker(color, checker_move1);
if move_result.is_err() {
None
// panic!("Error while moving checker {move_result:?}")
} else {
let from2 = tmp_board
.get_checker_field(color, checker2 as u8)
.unwrap_or(0);
let mut to2 = from2 + dice2 as usize;
// Gestion prise de coin par puissance
let opp_rest_field = 13;
if to1 == opp_rest_field && to2 == opp_rest_field {
to1 -= 1;
to2 -= 1;
}
let checker_move1 = store::CheckerMove::new(from1, to1).unwrap_or_default();
let checker_move2 = store::CheckerMove::new(from2, to2).unwrap_or_default();
Some(GameEvent::Move {
player_id: self.active_player_id,
moves: (checker_move1, checker_move2),
})
}
}
};
// Appliquer l'événement si valide
if let Some(event) = event {
if let Some(event) = action.to_event(&self.game) {
if self.game.validate(&event) {
self.game.consume(&event);
reward += REWARD_VALID_MOVE;

View file

@ -3,6 +3,7 @@ pub mod dqn_simple;
pub mod strategy;
pub mod training_common;
pub mod training_common_big;
pub mod trictrac_board;
use log::debug;
use store::{CheckerMove, Color, GameEvent, GameState, PlayerId, PointsRules, Stage, TurnStage};

View file

@ -1,10 +1,11 @@
use std::cmp::{max, min};
use std::fmt::{Debug, Display, Formatter};
use serde::{Deserialize, Serialize};
use store::CheckerMove;
use store::{CheckerMove, GameEvent, GameState};
/// Types d'actions possibles dans le jeu
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[derive(Debug, Copy, Clone, Eq, Serialize, Deserialize, PartialEq)]
pub enum TrictracAction {
/// Lancer les dés
Roll,
@ -20,6 +21,14 @@ pub enum TrictracAction {
// Mark,
}
impl Display for TrictracAction {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let s = format!("{self:?}");
writeln!(f, "{}", s.chars().rev().collect::<String>())?;
Ok(())
}
}
impl TrictracAction {
/// Encode une action en index pour le réseau de neurones
pub fn to_action_index(&self) -> usize {
@ -44,6 +53,78 @@ impl TrictracAction {
}
}
pub fn to_event(&self, state: &GameState) -> Option<GameEvent> {
match self {
TrictracAction::Roll => {
// Lancer les dés
Some(GameEvent::Roll {
player_id: state.active_player_id,
})
}
// TrictracAction::Mark => {
// // Marquer des points
// let points = self.game.
// Some(GameEvent::Mark {
// player_id: self.active_player_id,
// points,
// })
// }
TrictracAction::Go => {
// Continuer après avoir gagné un trou
Some(GameEvent::Go {
player_id: state.active_player_id,
})
}
TrictracAction::Move {
dice_order,
checker1,
checker2,
} => {
// Effectuer un mouvement
let (dice1, dice2) = if *dice_order {
(state.dice.values.0, state.dice.values.1)
} else {
(state.dice.values.1, state.dice.values.0)
};
let color = &store::Color::White;
let from1 = state
.board
.get_checker_field(color, *checker1 as u8)
.unwrap_or(0);
let mut to1 = from1 + dice1 as usize;
let checker_move1 = store::CheckerMove::new(from1, to1).unwrap_or_default();
let mut tmp_board = state.board.clone();
let move_result = tmp_board.move_checker(color, checker_move1);
if move_result.is_err() {
None
// panic!("Error while moving checker {move_result:?}")
} else {
let from2 = tmp_board
.get_checker_field(color, *checker2 as u8)
.unwrap_or(0);
let mut to2 = from2 + dice2 as usize;
// Gestion prise de coin par puissance
let opp_rest_field = 13;
if to1 == opp_rest_field && to2 == opp_rest_field {
to1 -= 1;
to2 -= 1;
}
let checker_move1 = store::CheckerMove::new(from1, to1).unwrap_or_default();
let checker_move2 = store::CheckerMove::new(from2, to2).unwrap_or_default();
Some(GameEvent::Move {
player_id: state.active_player_id,
moves: (checker_move1, checker_move2),
})
}
}
}
}
/// Décode un index d'action en TrictracAction
pub fn from_action_index(index: usize) -> Option<TrictracAction> {
match index {

149
bot/src/trictrac_board.rs Normal file
View file

@ -0,0 +1,149 @@
// https://docs.rs/board-game/ implementation
use crate::training_common::{get_valid_actions, TrictracAction};
use board_game::board::{
Board as BoardGameBoard, BoardDone, BoardMoves, Outcome, PlayError, Player as BoardGamePlayer,
};
use board_game::impl_unit_symmetry_board;
use internal_iterator::InternalIterator;
use std::fmt;
use std::ops::ControlFlow;
use store::Color;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TrictracBoard(crate::GameState);
impl Default for TrictracBoard {
fn default() -> Self {
TrictracBoard(crate::GameState::new_with_players("white", "black"))
}
}
impl fmt::Display for TrictracBoard {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl_unit_symmetry_board!(TrictracBoard);
impl BoardGameBoard for TrictracBoard {
// impl TrictracBoard {
type Move = TrictracAction;
fn next_player(&self) -> BoardGamePlayer {
self.0
.who_plays()
.map(|p| {
if p.color == Color::Black {
BoardGamePlayer::B
} else {
BoardGamePlayer::A
}
})
.unwrap_or(BoardGamePlayer::A)
}
fn is_available_move(&self, mv: Self::Move) -> Result<bool, BoardDone> {
self.check_done()?;
let is_valid = mv
.to_event(&self.0)
.map(|evt| self.0.validate(&evt))
.unwrap_or(false);
Ok(is_valid)
}
fn play(&mut self, mv: Self::Move) -> Result<(), PlayError> {
self.check_can_play(mv)?;
self.0.consume(&mv.to_event(&self.0).unwrap());
Ok(())
}
fn outcome(&self) -> Option<Outcome> {
if self.0.stage == crate::Stage::Ended {
self.0.determine_winner().map(|player_id| {
Outcome::WonBy(if player_id == 1 {
BoardGamePlayer::A
} else {
BoardGamePlayer::B
})
})
} else {
None
}
}
fn can_lose_after_move() -> bool {
true
}
}
impl<'a> BoardMoves<'a, TrictracBoard> for TrictracBoard {
type AllMovesIterator = TrictracAllMovesIterator;
type AvailableMovesIterator = TrictracAvailableMovesIterator<'a>;
fn all_possible_moves() -> Self::AllMovesIterator {
TrictracAllMovesIterator::default()
}
fn available_moves(&'a self) -> Result<Self::AvailableMovesIterator, BoardDone> {
TrictracAvailableMovesIterator::new(self)
}
}
#[derive(Debug, Clone)]
pub struct TrictracAllMovesIterator;
impl Default for TrictracAllMovesIterator {
fn default() -> Self {
TrictracAllMovesIterator
}
}
impl InternalIterator for TrictracAllMovesIterator {
type Item = TrictracAction;
fn try_for_each<R, F: FnMut(Self::Item) -> ControlFlow<R>>(self, mut f: F) -> ControlFlow<R> {
f(TrictracAction::Roll)?;
f(TrictracAction::Go)?;
for dice_order in [false, true] {
for checker1 in 0..16 {
for checker2 in 0..16 {
f(TrictracAction::Move {
dice_order,
checker1,
checker2,
})?;
}
}
}
ControlFlow::Continue(())
}
}
#[derive(Debug, Clone)]
pub struct TrictracAvailableMovesIterator<'a> {
board: &'a TrictracBoard,
}
impl<'a> TrictracAvailableMovesIterator<'a> {
pub fn new(board: &'a TrictracBoard) -> Result<Self, BoardDone> {
board.check_done()?;
Ok(TrictracAvailableMovesIterator { board })
}
pub fn board(&self) -> &'a TrictracBoard {
self.board
}
}
impl InternalIterator for TrictracAvailableMovesIterator<'_> {
type Item = TrictracAction;
fn try_for_each<R, F>(self, f: F) -> ControlFlow<R>
where
F: FnMut(Self::Item) -> ControlFlow<R>,
{
get_valid_actions(&self.board.0).into_iter().try_for_each(f)
}
}

View file

@ -1,46 +1,52 @@
# Inspirations
tools
- config clippy ?
- bacon : tests runner (ou loom ?)
- config clippy ?
- bacon : tests runner (ou loom ?)
## Rust libs
cf. https://blessed.rs/crates
cf. <https://blessed.rs/crates>
nombres aléatoires avec seed : https://richard.dallaway.com/posts/2021-01-04-repeat-resume/
nombres aléatoires avec seed : <https://richard.dallaway.com/posts/2021-01-04-repeat-resume/>
- cli : https://lib.rs/crates/pico-args ( ou clap )
- cli : <https://lib.rs/crates/pico-args> ( ou clap )
- reseau async : tokio
- web serveur : axum (uses tokio)
- https://fasterthanli.me/series/updating-fasterthanli-me-for-2022/part-2#the-opinions-of-axum-also-nice-error-handling
- <https://fasterthanli.me/series/updating-fasterthanli-me-for-2022/part-2#the-opinions-of-axum-also-nice-error-handling>
- db : sqlx
- eyre, color-eyre (Results)
- tracing (logging)
- rayon ( sync <-> parallel )
- front : yew + tauri
- front : yew + tauri
- egui
- https://docs.rs/board-game/latest/board_game/
- <https://docs.rs/board-game/latest/board_game/>
## network games
- <https://www.mattkeeter.com/projects/pont/>
- <https://github.com/jackadamson/onitama> (wasm, rooms)
- <https://github.com/UkoeHB/renet2>
## Others
- plugins avec https://github.com/extism/extism
- plugins avec <https://github.com/extism/extism>
## Backgammon existing projects
* go : https://bgammon.org/blog/20240101-hello-world/
- protocole de communication : https://code.rocket9labs.com/tslocum/bgammon/src/branch/main/PROTOCOL.md
* ocaml : https://github.com/jacobhilton/backgammon?tab=readme-ov-file
cli example : https://www.jacobh.co.uk/backgammon/
* lib rust backgammon
- https://github.com/carlostrub/backgammon
- https://github.com/marktani/backgammon
* network webtarot
* front ?
- go : <https://bgammon.org/blog/20240101-hello-world/>
- protocole de communication : <https://code.rocket9labs.com/tslocum/bgammon/src/branch/main/PROTOCOL.md>
- ocaml : <https://github.com/jacobhilton/backgammon?tab=readme-ov-file>
cli example : <https://www.jacobh.co.uk/backgammon/>
- lib rust backgammon
- <https://github.com/carlostrub/backgammon>
- <https://github.com/marktani/backgammon>
- network webtarot
- front ?
## cli examples
@ -48,7 +54,7 @@ nombres aléatoires avec seed : https://richard.dallaway.com/posts/2021-01-04-re
(No game) new game
gnubg rolls 3, anthon rolls 1.
GNU Backgammon Positions ID: 4HPwATDgc/ABMA
Match ID : MIEFAAAAAAAA
+12-11-10--9--8--7-------6--5--4--3--2--1-+ O: gnubg
@ -64,7 +70,7 @@ nombres aléatoires avec seed : https://richard.dallaway.com/posts/2021-01-04-re
| O X | | X O |
| O X | | X O | 0 points
+13-14-15-16-17-18------19-20-21-22-23-24-+ X: anthon
gnubg moves 8/5 6/5.
### jacobh
@ -72,33 +78,37 @@ nombres aléatoires avec seed : https://richard.dallaway.com/posts/2021-01-04-re
Move 11: player O rolls a 6-2.
Player O estimates that they have a 90.6111% chance of winning.
Os borne off: none
24 23 22 21 20 19 18 17 16 15 14 13
-------------------------------------------------------------------
| v v v v v v | | v v v v v v |
| | | |
| X O O O | | O O O |
| X O O O | | O O |
| O | | |
| | X | |
| | | |
| | | |
| | | |
| | | |
|------------------------------| |------------------------------|
| | | |
| | | |
| | | |
| | | |
| X | | |
| X X | | X |
| X X X | | X O |
| X X X | | X O O |
| | | |
| ^ ^ ^ ^ ^ ^ | | ^ ^ ^ ^ ^ ^ |
-------------------------------------------------------------------
1 2 3 4 5 6 7 8 9 10 11 12
Xs borne off: none
Os borne off: none
24 23 22 21 20 19 18 17 16 15 14 13
---
| v v v v v v | | v v v v v v |
| | | |
| X O O O | | O O O |
| X O O O | | O O |
| O | | |
| | X | |
| | | |
| | | |
| | | |
| | | |
|------------------------------| |------------------------------|
| | | |
| | | |
| | | |
| | | |
| X | | |
| X X | | X |
| X X X | | X O |
| X X X | | X O O |
| | | |
| ^ ^ ^ ^ ^ ^ | | ^ ^ ^ ^ ^ ^ |
---
1 2 3 4 5 6 7 8 9 10 11 12
Xs borne off: none
Move 12: player X rolls a 6-3.
Your move (? for help): bar/22
@ -107,13 +117,12 @@ Your move (? for help): ?
Enter the start and end positions, separated by a forward slash (or any non-numeric character), of each counter you want to move.
Each position should be number from 1 to 24, "bar" or "off".
Unlike in standard notation, you should enter each counter movement individually. For example:
24/18 18/13
bar/3 13/10 13/10 8/5
2/off 1/off
24/18 18/13
bar/3 13/10 13/10 8/5
2/off 1/off
You can also enter these commands:
p - show the previous move
n - show the next move
<enter> - toggle between showing the current and last moves
help - show this help text
quit - abandon game
p - show the previous move
n - show the next move
<enter> - toggle between showing the current and last moves
help - show this help text
quit - abandon game

View file

@ -8,7 +8,7 @@ use std::fmt;
pub type Field = usize;
pub type FieldWithCount = (Field, i8);
#[derive(Debug, Copy, Clone, Serialize, PartialEq, Deserialize)]
#[derive(Debug, Copy, Clone, Serialize, PartialEq, Eq, Deserialize)]
pub struct CheckerMove {
from: Field,
to: Field,
@ -94,7 +94,7 @@ impl CheckerMove {
}
/// Represents the Tric Trac board
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Board {
positions: [i8; 24],
}

View file

@ -44,7 +44,7 @@ impl DiceRoller {
/// Represents the two dice
///
/// Trictrac is always played with two dice.
#[derive(Debug, Clone, Copy, Serialize, PartialEq, Deserialize, Default)]
#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq, Deserialize, Default)]
pub struct Dice {
/// The two dice values
pub values: (u8, u8),

View file

@ -60,7 +60,7 @@ impl From<TurnStage> for u8 {
}
/// Represents a TricTrac game
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct GameState {
pub stage: Stage,
pub turn_stage: TurnStage,
@ -123,6 +123,15 @@ impl GameState {
gs
}
pub fn new_with_players(p1_name: &str, p2_name: &str) -> Self {
let mut game = Self::default();
if let Some(p1) = game.init_player(p1_name) {
game.init_player(p2_name);
game.consume(&GameEvent::BeginGame { goes_first: p1 });
}
game
}
fn set_schools_enabled(&mut self, schools_enabled: bool) {
self.schools_enabled = schools_enabled;
}
@ -707,14 +716,14 @@ impl GameState {
}
/// The reasons why a game could end
#[derive(Debug, Clone, Copy, Serialize, PartialEq, Deserialize)]
#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq, Deserialize)]
pub enum EndGameReason {
PlayerLeft { player_id: PlayerId },
PlayerWon { winner: PlayerId },
}
/// An event that progresses the GameState forward
#[derive(Debug, Clone, Serialize, PartialEq, Deserialize)]
#[derive(Debug, Clone, Serialize, PartialEq, Eq, Deserialize)]
pub enum GameEvent {
BeginGame {
goes_first: PlayerId,

View file

@ -4,7 +4,7 @@ use std::fmt;
// This just makes it easier to dissern between a player id and any ol' u64
pub type PlayerId = u64;
#[derive(Copy, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[derive(Copy, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum Color {
White,
Black,
@ -20,7 +20,7 @@ impl Color {
}
/// Struct for storing player related data.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Player {
pub name: String,
pub color: Color,