trictrac/store/src/board.rs

471 lines
11 KiB
Rust
Raw Normal View History

2024-01-09 17:58:10 +01:00
use crate::player::{Color, Player};
2023-10-07 20:46:24 +02:00
use crate::Error;
use serde::{Deserialize, Serialize};
2023-11-05 17:14:58 +01:00
/// Represents the Tric Trac board
2024-01-09 17:58:10 +01:00
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
struct Board {
board: [i8; 24],
2023-10-07 20:46:24 +02:00
}
2024-01-09 17:58:10 +01:00
impl Default for Board {
fn default() -> Self {
Board {
board: [
15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -15,
],
}
}
2023-10-07 20:46:24 +02:00
}
impl Board {
/// Create a new board
pub fn new() -> Self {
Board::default()
}
/// 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
2024-01-09 17:58:10 +01:00
/// 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 24.
2023-10-07 20:46:24 +02:00
///
/// 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
/// moved to the bar.
2023-10-29 20:48:53 +01:00
pub fn set(&mut self, player: &Player, field: usize, amount: i8) -> Result<(), Error> {
2024-01-09 17:58:10 +01:00
if field > 24 {
2023-10-07 20:46:24 +02:00
return Err(Error::FieldInvalid);
}
if self.blocked(player, field)? {
return Err(Error::FieldBlocked);
}
2023-10-28 15:12:04 +02:00
match player.color {
Color::White => {
2024-01-09 17:58:10 +01:00
let new = self.board[field - 1] + amount;
2023-10-07 20:46:24 +02:00
if new < 0 {
return Err(Error::MoveInvalid);
}
2024-01-09 17:58:10 +01:00
self.board[field - 1] = new;
2023-10-07 20:46:24 +02:00
Ok(())
}
2023-10-28 15:12:04 +02:00
Color::Black => {
2024-01-09 17:58:10 +01:00
let new = self.board[24 - field] - amount;
if new > 0 {
2023-10-07 20:46:24 +02:00
return Err(Error::MoveInvalid);
}
2024-01-09 17:58:10 +01:00
self.board[24 - field] = new;
2023-10-07 20:46:24 +02:00
Ok(())
}
}
}
/// Check if a field is blocked for a player
2023-10-29 20:48:53 +01:00
pub fn blocked(&self, player: &Player, field: usize) -> Result<bool, Error> {
2024-01-09 17:58:10 +01:00
if field > 24 {
2023-10-07 20:46:24 +02:00
return Err(Error::FieldInvalid);
}
2023-10-28 15:12:04 +02:00
match player.color {
Color::White => {
2024-01-09 17:58:10 +01:00
if self.board[field - 1] < 0 {
2023-10-07 20:46:24 +02:00
Ok(true)
} else {
Ok(false)
}
}
2023-10-28 15:12:04 +02:00
Color::Black => {
2023-10-07 20:46:24 +02:00
if self.raw_board.0.board[23 - field] > 1 {
Ok(true)
} else {
Ok(false)
}
}
}
}
}
/// Trait to move checkers
pub trait Move {
/// Move a checker
2023-10-29 20:48:53 +01:00
fn move_checker(&mut self, player: &Player, dice: u8, from: usize) -> Result<&mut Self, Error>
2023-10-07 20:46:24 +02:00
where
Self: Sized;
/// Move permitted
2023-10-29 20:48:53 +01:00
fn move_permitted(&mut self, player: &Player, dice: u8) -> Result<&mut Self, Error>
2023-10-07 20:46:24 +02:00
where
Self: Sized;
}
// Unit Tests
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_board() {
assert_eq!(Board::new(), Board::default());
}
#[test]
fn default_player_board() {
assert_eq!(
PlayerBoard::default(),
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
}
);
}
#[test]
fn get_board() {
let board = Board::new();
assert_eq!(
board.get(),
BoardDisplay {
board: [
-2, 0, 0, 0, 0, 5, 0, 3, 0, 0, 0, -5, 5, 0, 0, 0, -3, 0, -5, 0, 0, 0, 0, 2,
],
off: (0, 0)
}
);
}
#[test]
fn get_off() {
let board = Board::new();
assert_eq!(board.get_off(), (0, 0));
}
#[test]
fn set_player0() -> Result<(), Error> {
let mut board = Board::new();
2024-01-09 17:58:10 +01:00
let player = Player {
name: "".into(),
color: Color::White,
};
2023-10-29 20:48:53 +01:00
board.set(&player, 1, 1)?;
2023-10-07 20:46:24 +02:00
assert_eq!(board.get().board[1], 1);
Ok(())
}
#[test]
fn set_player1() -> Result<(), Error> {
let mut board = Board::new();
2024-01-09 17:58:10 +01:00
let player = Player {
name: "".into(),
color: Color::Black,
};
2023-10-29 20:48:53 +01:00
board.set(&player, 2, 1)?;
2023-10-07 20:46:24 +02:00
assert_eq!(board.get().board[21], -1);
Ok(())
}
#[test]
fn set_player0_off() -> Result<(), Error> {
let mut board = Board::new();
2024-01-09 17:58:10 +01:00
let player = Player {
name: "".into(),
color: Color::White,
};
2023-10-28 15:12:04 +02:00
board.set_off(player, 1)?;
2023-10-07 20:46:24 +02:00
assert_eq!(board.get().off.0, 1);
Ok(())
}
#[test]
fn set_player1_off() -> Result<(), Error> {
let mut board = Board::new();
2024-01-09 17:58:10 +01:00
let player = Player {
name: "".into(),
color: Color::Black,
};
2023-10-28 15:12:04 +02:00
board.set_off(player, 1)?;
2023-10-07 20:46:24 +02:00
assert_eq!(board.get().off.1, 1);
Ok(())
}
#[test]
fn set_player1_off1() -> Result<(), Error> {
let mut board = Board::new();
2024-01-09 17:58:10 +01:00
let player = Player {
name: "".into(),
color: Color::Black,
};
2023-10-28 15:12:04 +02:00
board.set_off(player, 1)?;
board.set_off(player, 1)?;
2023-10-07 20:46:24 +02:00
assert_eq!(board.get().off.1, 2);
Ok(())
}
#[test]
fn blocked_player0() -> Result<(), Error> {
let board = Board::new();
2024-01-09 17:58:10 +01:00
assert!(board.blocked(
&Player {
name: "".into(),
color: Color::White
},
0
)?);
2023-10-07 20:46:24 +02:00
Ok(())
}
#[test]
fn blocked_player1() -> Result<(), Error> {
let board = Board::new();
2024-01-09 17:58:10 +01:00
assert!(board.blocked(
&Player {
name: "".into(),
color: Color::Black
},
0
)?);
2023-10-07 20:46:24 +02:00
Ok(())
}
#[test]
fn blocked_player0_a() -> Result<(), Error> {
let mut board = Board::new();
2024-01-09 17:58:10 +01:00
board.set(
&Player {
name: "".into(),
color: Color::Black,
},
1,
2,
)?;
assert!(board.blocked(
&Player {
name: "".into(),
color: Color::White
},
22
)?);
2023-10-07 20:46:24 +02:00
Ok(())
}
#[test]
fn blocked_player1_a() -> Result<(), Error> {
let mut board = Board::new();
2024-01-09 17:58:10 +01:00
board.set(
&Player {
name: "".into(),
color: Color::White,
},
1,
2,
)?;
assert!(board.blocked(
&Player {
name: "".into(),
color: Color::Black
},
22
)?);
2023-10-07 20:46:24 +02:00
Ok(())
}
#[test]
fn blocked_invalid_field() {
let board = Board::new();
2024-01-09 17:58:10 +01:00
assert!(board
.blocked(
&Player {
name: "".into(),
color: Color::White
},
24
)
.is_err());
2023-10-07 20:46:24 +02:00
}
#[test]
fn set_field_with_1_checker_player0_a() -> Result<(), Error> {
let mut board = Board::new();
2024-01-09 17:58:10 +01:00
board.set(
&Player {
name: "".into(),
color: Color::White,
},
1,
1,
)?;
board.set(
&Player {
name: "".into(),
color: Color::Black,
},
22,
1,
)?;
2023-10-07 20:46:24 +02:00
assert_eq!(board.get().board[1], -1);
Ok(())
}
#[test]
fn set_field_with_1_checker_player0_b() -> Result<(), Error> {
let mut board = Board::new();
2024-01-09 17:58:10 +01:00
board.set(
&Player {
name: "".into(),
color: Color::White,
},
1,
1,
)?;
board.set(
&Player {
name: "".into(),
color: Color::Black,
},
22,
1,
)?;
2023-10-07 20:46:24 +02:00
assert_eq!(board.get().board[1], -1);
Ok(())
}
#[test]
fn set_field_with_1_checker_player1_a() -> Result<(), Error> {
let mut board = Board::new();
2024-01-09 17:58:10 +01:00
board.set(
&Player {
name: "".into(),
color: Color::Black,
},
1,
1,
)?;
board.set(
&Player {
name: "".into(),
color: Color::White,
},
22,
1,
)?;
2023-10-07 20:46:24 +02:00
assert_eq!(board.get().board[22], 1);
Ok(())
}
#[test]
fn set_field_with_1_checker_player1_b() -> Result<(), Error> {
let mut board = Board::new();
2024-01-09 17:58:10 +01:00
board.set(
&Player {
name: "".into(),
color: Color::Black,
},
1,
1,
)?;
board.set(
&Player {
name: "".into(),
color: Color::White,
},
22,
1,
)?;
2023-10-07 20:46:24 +02:00
assert_eq!(board.get().board[22], 1);
Ok(())
}
#[test]
fn set_field_with_2_checkers_player0_a() -> Result<(), Error> {
let mut board = Board::new();
2024-01-09 17:58:10 +01:00
board.set(
&Player {
name: "".into(),
color: Color::White,
},
23,
2,
)?;
2023-10-07 20:46:24 +02:00
assert_eq!(board.get().board[23], 4);
Ok(())
}
#[test]
fn set_field_with_2_checkers_player0_b() -> Result<(), Error> {
let mut board = Board::new();
2024-01-09 17:58:10 +01:00
board.set(
&Player {
name: "".into(),
color: Color::White,
},
23,
-1,
)?;
2023-10-07 20:46:24 +02:00
assert_eq!(board.get().board[23], 1);
Ok(())
}
#[test]
fn set_field_blocked() {
let mut board = Board::new();
2024-01-09 17:58:10 +01:00
assert!(board
.set(
&Player {
name: "".into(),
color: Color::White
},
0,
2
)
.is_err());
2023-10-07 20:46:24 +02:00
}
#[test]
fn set_wrong_field1() {
let mut board = Board::new();
2024-01-09 17:58:10 +01:00
assert!(board
.set(
&Player {
name: "".into(),
color: Color::White
},
50,
2
)
.is_err());
2023-10-07 20:46:24 +02:00
}
#[test]
fn set_wrong_amount0() {
let mut board = Board::new();
2024-01-09 17:58:10 +01:00
assert!(board
.set(
&Player {
name: "".into(),
color: Color::White
},
23,
-3
)
.is_err());
2023-10-07 20:46:24 +02:00
}
#[test]
fn set_wrong_amount1() {
let mut board = Board::new();
2024-01-09 17:58:10 +01:00
assert!(board
.set(
&Player {
name: "".into(),
color: Color::Black
},
23,
-3
)
.is_err());
2023-10-07 20:46:24 +02:00
}
}