1use std::collections::HashMap;
2use std::fmt;
3
4use crate::types::{Coord, Player};
5
6#[derive(Clone)]
8pub struct Board {
9 stones: HashMap<Coord, Player>,
10}
11
12#[derive(Debug, PartialEq, Eq)]
14pub enum PlaceError {
15 CellOccupied,
16}
17
18impl fmt::Display for PlaceError {
19 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20 match self {
21 PlaceError::CellOccupied => write!(f, "cell is already occupied"),
22 }
23 }
24}
25
26impl std::error::Error for PlaceError {}
27
28impl Board {
29 pub fn new() -> Self {
31 let mut stones = HashMap::new();
32 stones.insert((0, 0), Player::P1);
33 Board { stones }
34 }
35
36 pub fn place(&mut self, coord: Coord, player: Player) -> Result<(), PlaceError> {
40 if self.stones.contains_key(&coord) {
41 return Err(PlaceError::CellOccupied);
42 }
43 self.stones.insert(coord, player);
44 Ok(())
45 }
46
47 pub fn get(&self, coord: Coord) -> Option<Player> {
49 self.stones.get(&coord).copied()
50 }
51
52 pub fn stones(&self) -> &HashMap<Coord, Player> {
54 &self.stones
55 }
56
57 pub fn stone_count(&self) -> usize {
59 self.stones.len()
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66
67 #[test]
68 fn new_has_p1_at_origin() {
69 let board = Board::new();
70 assert_eq!(board.get((0, 0)), Some(Player::P1));
71 }
72
73 #[test]
74 fn place_on_empty_cell_succeeds() {
75 let mut board = Board::new();
76 let result = board.place((1, 0), Player::P2);
77 assert!(result.is_ok());
78 assert_eq!(board.get((1, 0)), Some(Player::P2));
79 }
80
81 #[test]
82 fn place_on_occupied_cell_fails() {
83 let mut board = Board::new();
84 let result = board.place((0, 0), Player::P2);
86 assert_eq!(result, Err(PlaceError::CellOccupied));
87 }
88
89 #[test]
90 fn get_on_empty_cell_returns_none() {
91 let board = Board::new();
92 assert_eq!(board.get((99, 99)), None);
93 }
94
95 #[test]
96 fn stones_returns_all_placed_stones() {
97 let mut board = Board::new();
98 board.place((1, 0), Player::P2).unwrap();
99 let stones = board.stones();
100 assert_eq!(stones.len(), 2);
101 assert_eq!(stones.get(&(0, 0)), Some(&Player::P1));
102 assert_eq!(stones.get(&(1, 0)), Some(&Player::P2));
103 }
104
105 #[test]
106 fn clone_is_independent() {
107 let mut original = Board::new();
108 let mut cloned = original.clone();
109
110 cloned.place((5, 5), Player::P2).unwrap();
112
113 assert_eq!(original.get((5, 5)), None);
115
116 original.place((3, 3), Player::P1).unwrap();
118
119 assert_eq!(cloned.get((3, 3)), None);
121 }
122}