wip board

This commit is contained in:
Henri Bourcereau 2024-01-09 17:58:10 +01:00
parent ec16e5f3df
commit 23ab966602
3 changed files with 227 additions and 131 deletions

View file

@ -30,9 +30,10 @@ cf. https://blessed.rs/crates
## Backgammon existing projects ## Backgammon existing projects
* go : https://bgammon.org/blog/20240101-hello-world/ * go : https://bgammon.org/blog/20240101-hello-world/
- protocole de communication : https://code.rocket9labs.com/tslocum/bgammon/src/branch/main/PROTOCOL.md
* lib rust backgammon * lib rust backgammon
- https://docs.rs/crate/backgammon/0.4.0 - https://github.com/carlostrub/backgammon
- https://github.com/marktani/backgammon - https://github.com/marktani/backgammon
* network webtarot * network webtarot
* front ? * front ?

View file

@ -9,3 +9,13 @@ cargo add pico-args
Organisation store / server / client selon https://herluf-ba.github.io/making-a-turn-based-multiplayer-game-in-rust-01-whats-a-turn-based-game-anyway Organisation store / server / client selon https://herluf-ba.github.io/making-a-turn-based-multiplayer-game-in-rust-01-whats-a-turn-based-game-anyway
_store_ est la bibliothèque contenant le _reducer_ qui transforme l'état du jeu en fonction les évènements. Elle est utilisée par le _server_ et le _client_. Seuls les évènements sont transmis entre clients et serveur. _store_ est la bibliothèque contenant le _reducer_ qui transforme l'état du jeu en fonction les évènements. Elle est utilisée par le _server_ et le _client_. Seuls les évènements sont transmis entre clients et serveur.
## Organisation du store
lib
- game::GameState
- error
- dice
- board
- user
- user

View file

@ -1,42 +1,21 @@
use crate::player::{Player, Color}; use crate::player::{Color, Player};
use crate::Error; use crate::Error;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// Represents the Tric Trac board /// Represents the Tric Trac board
/// #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
/// A Tric-Trac board consists of 24 fields, each of which can hold 0 or more checkers. struct Board {
/// board: [i8; 24],
/// ```
/// # fn foo() {}
/// // +12-11-10--9--8--7-------6--5--4--3--2--1-+
/// // | X O | | O X | +-------+
/// // | X O | | O X | | OFF O |
/// // | X O | | O | +-------+
/// // | X | | O |
/// // | X | | O |
/// // | | | |
/// // | O | | X |
/// // | O | | X |
/// // | O X | | X | +-------+
/// // | O X | | X O | | OFF X |
/// // | O X | | X O | +-------+
/// // +13-14-15-16-17-18------19-20-21-22-23-24-+
/// ```
#[derive(Debug, Clone, Serialize, PartialEq, Deserialize, Default)]
pub struct Board {
raw_board: (PlayerBoard, PlayerBoard),
} }
/// Represents the Backgammon board for both players (to be used for graphical representation). impl Default for Board {
#[derive(Debug, Serialize, PartialEq, Deserialize)] fn default() -> Self {
pub struct BoardDisplay { Board {
/// The board represented as an array of 24 fields, each of which can hold 0 or more checkers. board: [
/// Positive amounts represent checkers of player 0, negative amounts represent checkers of 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -15,
/// player 1. ],
pub board: [i8; 24], }
/// The off for both players }
pub off: (u8, u8),
} }
impl Board { impl Board {
@ -45,43 +24,17 @@ impl Board {
Board::default() Board::default()
} }
/// Get the board for both players. Use for graphical representation of the board.
///
/// This method outputs a tuple with three values:
///
/// 1. the board represented as an array of 24 fields, each of which can hold 0 or more
/// checkers. Positive amounts represent checkers of player 0, negative amounts represent
/// checkers of player 1.
/// 3. the off for both players
pub fn get(&self) -> BoardDisplay {
let mut board: [i8; 24] = [0; 24];
for (i, val) in board.iter_mut().enumerate() {
*val = self.raw_board.0.board[i] as i8 - self.raw_board.1.board[23 - i] as i8;
}
BoardDisplay {
board,
off: self.get_off(),
}
}
/// Get the off for both players
fn get_off(&self) -> (u8, u8) {
(self.raw_board.0.off, self.raw_board.1.off)
}
/// Set checkers for a player on a field /// Set checkers for a player on a field
/// ///
/// This method adds the amount of checkers for a player on a field. The field is numbered from /// This method adds the amount of checkers for a player on a field. The field is numbered from
/// 0 to 23, starting from the last field of each player in the home board, the most far away /// 1 to 24, starting from the first field of each player in the home board, the most far away
/// field for each player is number 23. /// field for each player is number 24.
/// ///
/// If the field is blocked for the player, an error is returned. If the field is not blocked, /// If the field is blocked for the player, an error is returned. If the field is not blocked,
/// but there is already one checker from the other player on the field, that checker is hit and /// but there is already one checker from the other player on the field, that checker is hit and
/// moved to the bar. /// moved to the bar.
pub fn set(&mut self, player: &Player, field: usize, amount: i8) -> Result<(), Error> { pub fn set(&mut self, player: &Player, field: usize, amount: i8) -> Result<(), Error> {
if field > 23 { if field > 24 {
return Err(Error::FieldInvalid); return Err(Error::FieldInvalid);
} }
@ -91,20 +44,20 @@ impl Board {
match player.color { match player.color {
Color::White => { Color::White => {
let new = self.raw_board.0.board[field] as i8 + amount; let new = self.board[field - 1] + amount;
if new < 0 { if new < 0 {
return Err(Error::MoveInvalid); return Err(Error::MoveInvalid);
} }
self.raw_board.0.board[field] = new as u8; self.board[field - 1] = new;
Ok(()) Ok(())
} }
Color::Black => { Color::Black => {
let new = self.raw_board.1.board[field] as i8 + amount; let new = self.board[24 - field] - amount;
if new < 0 { if new > 0 {
return Err(Error::MoveInvalid); return Err(Error::MoveInvalid);
} }
self.raw_board.1.board[field] = new as u8; self.board[24 - field] = new;
Ok(()) Ok(())
} }
@ -113,13 +66,13 @@ impl Board {
/// Check if a field is blocked for a player /// Check if a field is blocked for a player
pub fn blocked(&self, player: &Player, field: usize) -> Result<bool, Error> { pub fn blocked(&self, player: &Player, field: usize) -> Result<bool, Error> {
if field > 23 { if field > 24 {
return Err(Error::FieldInvalid); return Err(Error::FieldInvalid);
} }
match player.color { match player.color {
Color::White => { Color::White => {
if self.raw_board.1.board[23 - field] > 1 { if self.board[field - 1] < 0 {
Ok(true) Ok(true)
} else { } else {
Ok(false) Ok(false)
@ -134,41 +87,6 @@ impl Board {
} }
} }
} }
/// Set checkers for a player off the board. This method adds amount to the already existing
/// checkers there.
pub fn set_off(&mut self, player: &Player, amount: u8) -> Result<(), Error> {
match player.color {
Color::White => {
let new = self.raw_board.0.off + amount;
self.raw_board.0.off = new;
Ok(())
}
Color::Black => {
let new = self.raw_board.1.off + amount;
self.raw_board.1.off = new;
Ok(())
}
}
}
}
/// Represents the Backgammon board for one player
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
struct PlayerBoard {
board: [u8; 24],
off: u8,
}
impl Default for PlayerBoard {
fn default() -> Self {
PlayerBoard {
board: [
0, 0, 0, 0, 0, 5, 0, 3, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
],
off: 0,
}
}
} }
/// Trait to move checkers /// Trait to move checkers
@ -228,7 +146,10 @@ mod tests {
#[test] #[test]
fn set_player0() -> Result<(), Error> { fn set_player0() -> Result<(), Error> {
let mut board = Board::new(); let mut board = Board::new();
let player = Player {name: "".into(), color: Color::White}; let player = Player {
name: "".into(),
color: Color::White,
};
board.set(&player, 1, 1)?; board.set(&player, 1, 1)?;
assert_eq!(board.get().board[1], 1); assert_eq!(board.get().board[1], 1);
Ok(()) Ok(())
@ -237,7 +158,10 @@ mod tests {
#[test] #[test]
fn set_player1() -> Result<(), Error> { fn set_player1() -> Result<(), Error> {
let mut board = Board::new(); let mut board = Board::new();
let player = Player {name: "".into(), color: Color::Black}; let player = Player {
name: "".into(),
color: Color::Black,
};
board.set(&player, 2, 1)?; board.set(&player, 2, 1)?;
assert_eq!(board.get().board[21], -1); assert_eq!(board.get().board[21], -1);
Ok(()) Ok(())
@ -246,7 +170,10 @@ mod tests {
#[test] #[test]
fn set_player0_off() -> Result<(), Error> { fn set_player0_off() -> Result<(), Error> {
let mut board = Board::new(); let mut board = Board::new();
let player = Player {name: "".into(), color: Color::White}; let player = Player {
name: "".into(),
color: Color::White,
};
board.set_off(player, 1)?; board.set_off(player, 1)?;
assert_eq!(board.get().off.0, 1); assert_eq!(board.get().off.0, 1);
Ok(()) Ok(())
@ -255,7 +182,10 @@ mod tests {
#[test] #[test]
fn set_player1_off() -> Result<(), Error> { fn set_player1_off() -> Result<(), Error> {
let mut board = Board::new(); let mut board = Board::new();
let player = Player {name: "".into(), color: Color::Black}; let player = Player {
name: "".into(),
color: Color::Black,
};
board.set_off(player, 1)?; board.set_off(player, 1)?;
assert_eq!(board.get().off.1, 1); assert_eq!(board.get().off.1, 1);
Ok(()) Ok(())
@ -264,7 +194,10 @@ mod tests {
#[test] #[test]
fn set_player1_off1() -> Result<(), Error> { fn set_player1_off1() -> Result<(), Error> {
let mut board = Board::new(); let mut board = Board::new();
let player = Player {name: "".into(), color: Color::Black}; let player = Player {
name: "".into(),
color: Color::Black,
};
board.set_off(player, 1)?; board.set_off(player, 1)?;
board.set_off(player, 1)?; board.set_off(player, 1)?;
assert_eq!(board.get().off.1, 2); assert_eq!(board.get().off.1, 2);
@ -274,44 +207,104 @@ mod tests {
#[test] #[test]
fn blocked_player0() -> Result<(), Error> { fn blocked_player0() -> Result<(), Error> {
let board = Board::new(); let board = Board::new();
assert!(board.blocked(&Player { name:"".into(), color: Color::White }, 0)?); assert!(board.blocked(
&Player {
name: "".into(),
color: Color::White
},
0
)?);
Ok(()) Ok(())
} }
#[test] #[test]
fn blocked_player1() -> Result<(), Error> { fn blocked_player1() -> Result<(), Error> {
let board = Board::new(); let board = Board::new();
assert!(board.blocked(&Player { name:"".into(), color: Color::Black }, 0)?); assert!(board.blocked(
&Player {
name: "".into(),
color: Color::Black
},
0
)?);
Ok(()) Ok(())
} }
#[test] #[test]
fn blocked_player0_a() -> Result<(), Error> { fn blocked_player0_a() -> Result<(), Error> {
let mut board = Board::new(); let mut board = Board::new();
board.set(&Player { name:"".into(), color: Color::Black }, 1, 2)?; board.set(
assert!(board.blocked(&Player { name:"".into(), color: Color::White }, 22)?); &Player {
name: "".into(),
color: Color::Black,
},
1,
2,
)?;
assert!(board.blocked(
&Player {
name: "".into(),
color: Color::White
},
22
)?);
Ok(()) Ok(())
} }
#[test] #[test]
fn blocked_player1_a() -> Result<(), Error> { fn blocked_player1_a() -> Result<(), Error> {
let mut board = Board::new(); let mut board = Board::new();
board.set(&Player { name:"".into(), color: Color::White }, 1, 2)?; board.set(
assert!(board.blocked(&Player { name:"".into(), color: Color::Black }, 22)?); &Player {
name: "".into(),
color: Color::White,
},
1,
2,
)?;
assert!(board.blocked(
&Player {
name: "".into(),
color: Color::Black
},
22
)?);
Ok(()) Ok(())
} }
#[test] #[test]
fn blocked_invalid_field() { fn blocked_invalid_field() {
let board = Board::new(); let board = Board::new();
assert!(board.blocked(&Player { name:"".into(), color: Color::White }, 24).is_err()); assert!(board
.blocked(
&Player {
name: "".into(),
color: Color::White
},
24
)
.is_err());
} }
#[test] #[test]
fn set_field_with_1_checker_player0_a() -> Result<(), Error> { fn set_field_with_1_checker_player0_a() -> Result<(), Error> {
let mut board = Board::new(); let mut board = Board::new();
board.set(&Player { name:"".into(), color: Color::White }, 1, 1)?; board.set(
board.set(&Player { name:"".into(), color: Color::Black }, 22, 1)?; &Player {
name: "".into(),
color: Color::White,
},
1,
1,
)?;
board.set(
&Player {
name: "".into(),
color: Color::Black,
},
22,
1,
)?;
assert_eq!(board.get().board[1], -1); assert_eq!(board.get().board[1], -1);
Ok(()) Ok(())
} }
@ -319,8 +312,22 @@ mod tests {
#[test] #[test]
fn set_field_with_1_checker_player0_b() -> Result<(), Error> { fn set_field_with_1_checker_player0_b() -> Result<(), Error> {
let mut board = Board::new(); let mut board = Board::new();
board.set(&Player { name:"".into(), color: Color::White }, 1, 1)?; board.set(
board.set(&Player { name:"".into(), color: Color::Black }, 22, 1)?; &Player {
name: "".into(),
color: Color::White,
},
1,
1,
)?;
board.set(
&Player {
name: "".into(),
color: Color::Black,
},
22,
1,
)?;
assert_eq!(board.get().board[1], -1); assert_eq!(board.get().board[1], -1);
Ok(()) Ok(())
} }
@ -328,8 +335,22 @@ mod tests {
#[test] #[test]
fn set_field_with_1_checker_player1_a() -> Result<(), Error> { fn set_field_with_1_checker_player1_a() -> Result<(), Error> {
let mut board = Board::new(); let mut board = Board::new();
board.set(&Player { name:"".into(), color: Color::Black }, 1, 1)?; board.set(
board.set(&Player { name:"".into(), color: Color::White }, 22, 1)?; &Player {
name: "".into(),
color: Color::Black,
},
1,
1,
)?;
board.set(
&Player {
name: "".into(),
color: Color::White,
},
22,
1,
)?;
assert_eq!(board.get().board[22], 1); assert_eq!(board.get().board[22], 1);
Ok(()) Ok(())
} }
@ -337,8 +358,22 @@ mod tests {
#[test] #[test]
fn set_field_with_1_checker_player1_b() -> Result<(), Error> { fn set_field_with_1_checker_player1_b() -> Result<(), Error> {
let mut board = Board::new(); let mut board = Board::new();
board.set(&Player { name:"".into(), color: Color::Black }, 1, 1)?; board.set(
board.set(&Player { name:"".into(), color: Color::White }, 22, 1)?; &Player {
name: "".into(),
color: Color::Black,
},
1,
1,
)?;
board.set(
&Player {
name: "".into(),
color: Color::White,
},
22,
1,
)?;
assert_eq!(board.get().board[22], 1); assert_eq!(board.get().board[22], 1);
Ok(()) Ok(())
} }
@ -346,7 +381,14 @@ mod tests {
#[test] #[test]
fn set_field_with_2_checkers_player0_a() -> Result<(), Error> { fn set_field_with_2_checkers_player0_a() -> Result<(), Error> {
let mut board = Board::new(); let mut board = Board::new();
board.set(&Player { name:"".into(), color: Color::White }, 23, 2)?; board.set(
&Player {
name: "".into(),
color: Color::White,
},
23,
2,
)?;
assert_eq!(board.get().board[23], 4); assert_eq!(board.get().board[23], 4);
Ok(()) Ok(())
} }
@ -354,7 +396,14 @@ mod tests {
#[test] #[test]
fn set_field_with_2_checkers_player0_b() -> Result<(), Error> { fn set_field_with_2_checkers_player0_b() -> Result<(), Error> {
let mut board = Board::new(); let mut board = Board::new();
board.set(&Player { name:"".into(), color: Color::White }, 23, -1)?; board.set(
&Player {
name: "".into(),
color: Color::White,
},
23,
-1,
)?;
assert_eq!(board.get().board[23], 1); assert_eq!(board.get().board[23], 1);
Ok(()) Ok(())
} }
@ -362,24 +411,60 @@ mod tests {
#[test] #[test]
fn set_field_blocked() { fn set_field_blocked() {
let mut board = Board::new(); let mut board = Board::new();
assert!(board.set(&Player { name:"".into(), color: Color::White }, 0, 2).is_err()); assert!(board
.set(
&Player {
name: "".into(),
color: Color::White
},
0,
2
)
.is_err());
} }
#[test] #[test]
fn set_wrong_field1() { fn set_wrong_field1() {
let mut board = Board::new(); let mut board = Board::new();
assert!(board.set(&Player { name:"".into(), color: Color::White }, 50, 2).is_err()); assert!(board
.set(
&Player {
name: "".into(),
color: Color::White
},
50,
2
)
.is_err());
} }
#[test] #[test]
fn set_wrong_amount0() { fn set_wrong_amount0() {
let mut board = Board::new(); let mut board = Board::new();
assert!(board.set(&Player { name:"".into(), color: Color::White }, 23, -3).is_err()); assert!(board
.set(
&Player {
name: "".into(),
color: Color::White
},
23,
-3
)
.is_err());
} }
#[test] #[test]
fn set_wrong_amount1() { fn set_wrong_amount1() {
let mut board = Board::new(); let mut board = Board::new();
assert!(board.set(&Player { name:"".into(), color: Color::Black }, 23, -3).is_err()); assert!(board
.set(
&Player {
name: "".into(),
color: Color::Black
},
23,
-3
)
.is_err());
} }
} }