chore: add moving the pawn

This commit is contained in:
2026-08-11 22:49:23 +02:00
parent 52a9d795c0
commit 0d1a9cafbb
4 changed files with 188 additions and 24 deletions

View File

@@ -1,11 +1,11 @@
use crate::logic::{ use crate::logic::{
pawn::Pawn, pawn::{Pawn, PawnState},
player::Player, player::Player,
utils::{Field, Position}, utils::{Field, Position},
}; };
use std::ops::Index; use std::ops::{Index, IndexMut};
use log::{debug, info}; use log::{debug, info, warn};
pub struct Board { pub struct Board {
board: Vec<Field>, board: Vec<Field>,
@@ -13,11 +13,16 @@ pub struct Board {
} }
impl Board { impl Board {
/// Board is always a square. Size parameter is a side of this rectangle. Defaults to 8 /// Board is always a square. Size parameter is a side of this square. Defaults to 8
pub fn new(player1: &Player, player2: &Player, size: Option<usize>) -> Self { pub fn new(player1: &mut Player, player2: &mut Player, size: Option<usize>) -> Self {
let size = size.unwrap_or(8); let size = size.unwrap_or(8);
info!("Creating new board of size: {}", size); info!("Creating new board of size: {}", size);
player1.end_row = size - 1;
player2.end_row = 0;
let p1_ref: &Player = &*player1;
let p2_ref: &Player = &*player2;
let board: Vec<Field> = (0..size) let board: Vec<Field> = (0..size)
.flat_map(|row| { .flat_map(|row| {
@@ -29,17 +34,17 @@ impl Board {
// first player's pawns // first player's pawns
if row < 3 && is_valid_place_for_a_pawn { if row < 3 && is_valid_place_for_a_pawn {
debug!("Placing new pawn at: {}, owned by: {}", position, player1); debug!("Placing new pawn at: {}, owned by: {}", position, &p1_ref);
Field { Field {
position, position,
pawn: Some(Pawn::new(position, &player1)), pawn: Some(Pawn::new(position, &p1_ref)),
} }
} else if row >= size - 3 && is_valid_place_for_a_pawn { } else if row >= size - 3 && is_valid_place_for_a_pawn {
debug!("Placing new pawn at: {}, owned by: {}", position, player2); debug!("Placing new pawn at: {}, owned by: {}", position, &p2_ref);
Field { Field {
position, position,
pawn: Some(Pawn::new(position, &player2)), pawn: Some(Pawn::new(position, &p2_ref)),
} }
} else { } else {
// no pawn // no pawn
@@ -55,6 +60,51 @@ impl Board {
Board { board, size } Board { board, size }
} }
/// Checks if move from position to position is valid.
/// It returns true if you want to move pawn from position to position where there is no pawn
///
/// Doesn't check turn
///
/// Not meant for capturing
fn is_move_valid(&self, from: &Position, to: &Position) -> bool {
if let Some(_) = &self[from.row][from.column].pawn {
self[to.row][to.column].pawn.is_none()
} else {
false
}
}
fn is_player_owner_of_the_pawn(&self, player: &Player, pawn: &Pawn) -> bool {
player.id == pawn.owner
}
/// Returns true if move was successful
pub fn move_pawn(&mut self, player: &Player, from: Position, to: Position) -> bool {
if !self.is_move_valid(&from, &to) {
warn!("Tried invalid move: {} -> {}", from, to);
return false;
}
if !self.is_player_owner_of_the_pawn(&player, self[from].pawn.as_ref().unwrap()) {
warn!("Player {} tried moving a pawn which he doesn own!", player);
debug!("Tried moving from {} to {}", from, to);
return false;
}
// we can safely unwrap, because if there's not pawn function returns false earlier
let mut mowing_pawn: Pawn = self[from].pawn.take().unwrap();
if to.row == player.end_row || matches!(mowing_pawn.state, PawnState::Dame(_)) {
mowing_pawn.state = super::pawn::PawnState::Dame(to);
} else {
mowing_pawn.state = super::pawn::PawnState::Man(to);
}
self[to.row][to.column].pawn = Some(mowing_pawn);
true
}
} }
impl Index<usize> for Board { impl Index<usize> for Board {
@@ -67,10 +117,32 @@ impl Index<usize> for Board {
} }
} }
impl Index<Position> for Board {
type Output = Field;
fn index(&self, index: Position) -> &Self::Output {
let index = index.row * &self.size + index.column;
&self.board[index]
}
}
impl IndexMut<usize> for Board {
fn index_mut<'a>(&'a mut self, i: usize) -> &'a mut [Field] {
let start = i * &self.size;
let end: usize = start + &self.size;
&mut self.board[start..end]
}
}
impl IndexMut<Position> for Board {
fn index_mut(&mut self, index: Position) -> &mut Self::Output {
let index = index.row * &self.size + index.column;
&mut self.board[index]
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use uuid::Uuid;
use crate::logic::pawn::PawnState; use crate::logic::pawn::PawnState;
use super::*; use super::*;
@@ -83,17 +155,10 @@ mod tests {
fn new_board_test() { fn new_board_test() {
init_logger(); init_logger();
let player1: Player = Player { let mut player1: Player = Player::new("Morbius".to_string());
name: String::from("Morbius"), let mut player2: Player = Player::new("Milo".to_string());
id: Uuid::new_v4(),
};
let player2: Player = Player { let test_board: Board = Board::new(&mut player1, &mut player2, None);
name: String::from("Milo"),
id: Uuid::new_v4(),
};
let test_board: Board = Board::new(&player1, &player2, None);
let size: usize = test_board.size; let size: usize = test_board.size;
for row in 0..size { for row in 0..size {
@@ -136,4 +201,71 @@ mod tests {
} }
} }
} }
#[test]
fn test_overloaded_operators() {
init_logger();
let mut player1: Player = Player::new("Morbius".to_string());
let mut player2: Player = Player::new("Milo".to_string());
let test_board: Board = Board::new(&mut player1, &mut player2, None);
let row1 = 0;
let column1 = 0;
let position1: Position = Position {
row: row1,
column: column1,
};
let row2 = row1;
let column2 = 1;
let position2: Position = Position {
row: row2,
column: column2,
};
assert_eq!(test_board[row1][column1], test_board[position1]);
assert_eq!(test_board[row2][column2], test_board[position2]);
assert!(test_board[position1].pawn.is_none());
assert!(test_board[position2].pawn.is_some());
}
#[test]
fn test_moving_pawn() {
init_logger();
let mut player1: Player = Player::new("Morbius".to_string());
let mut player2: Player = Player::new("Milo".to_string());
let mut test_board: Board = Board::new(&mut player1, &mut player2, None);
// we cant move pawn into another pawn; this should fail
assert!(!test_board.move_pawn(
&player1,
Position { row: 0, column: 1 },
Position { row: 1, column: 0 },
));
// player can't move pawn that he doesn't own; this should fail
assert!(!test_board.move_pawn(
&player2,
Position { row: 2, column: 1 },
Position { row: 3, column: 0 },
));
// walid move
assert!(test_board.move_pawn(
&player1,
Position { row: 2, column: 1 },
Position { row: 3, column: 0 }
));
assert!(test_board[3][0].pawn.is_some());
assert_eq!(test_board[3][0].pawn.as_ref().unwrap().owner, player1.id);
assert!(test_board[2][1].pawn.is_none());
}
} }

View File

@@ -2,7 +2,7 @@ use uuid::Uuid;
use crate::logic::{player::Player, utils::Position}; use crate::logic::{player::Player, utils::Position};
#[derive(PartialEq, Eq)] #[derive(PartialEq, Eq, Debug)]
pub enum PawnState { pub enum PawnState {
Man(Position), Man(Position),
Dame(Position), Dame(Position),
@@ -10,6 +10,7 @@ pub enum PawnState {
} }
/// Basic pawn /// Basic pawn
#[derive(Debug, PartialEq, Eq)]
pub struct Pawn { pub struct Pawn {
pub state: PawnState, pub state: PawnState,
pub owner: Uuid, pub owner: Uuid,

View File

@@ -5,10 +5,26 @@ use uuid::Uuid;
pub struct Player { pub struct Player {
pub name: String, pub name: String,
pub id: Uuid, pub id: Uuid,
/// Row where players pawns turn into dames
pub end_row: usize,
}
impl Player {
pub fn new(name: String) -> Self {
Player {
name,
id: Uuid::new_v4(),
end_row: 0,
}
}
} }
impl Display for Player { impl Display for Player {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "(name: {}, id, {})", self.name, self.id) write!(
f,
"(name: {}, id: {}, end_row: {})",
self.name, self.id, self.end_row
)
} }
} }

View File

@@ -2,7 +2,7 @@ use core::fmt;
use crate::logic::pawn::Pawn; use crate::logic::pawn::Pawn;
#[derive(Clone, Copy, PartialEq, Eq)] #[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct Position { pub struct Position {
pub row: usize, pub row: usize,
pub column: usize, pub column: usize,
@@ -14,7 +14,22 @@ impl fmt::Display for Position {
} }
} }
#[derive(Debug, PartialEq, Eq)]
pub struct Field { pub struct Field {
pub position: Position, pub position: Position,
pub pawn: Option<Pawn>, pub pawn: Option<Pawn>,
} }
impl fmt::Display for Field {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(pawn) = &self.pawn {
write!(
f,
"Field at pos: {}. Pawn here owned by: {}",
self.position, pawn.owner
)
} else {
write!(f, "Field at pos: {}. No pawn here.", self.position)
}
}
}