diff --git a/board-shared/src/board.rs b/board-shared/src/board.rs index ff60056..ea46527 100644 --- a/board-shared/src/board.rs +++ b/board-shared/src/board.rs @@ -19,6 +19,7 @@ impl Board { self.tiles[y * self.width + x] = Some(tile); } + /// Gets the difference between this board and another. pub fn difference(&self, other: &Board) -> Vec { let mut diff = Vec::new(); for y in 0..self.height { @@ -31,6 +32,67 @@ impl Board { diff } + /// Gets all chains of tiles that are adjacent to the given positions. + pub fn find_chains(&self, positions: &[Position2d]) -> Vec> { + let mut chains = Vec::new(); + for &pos in positions { + for &alignment in &[Alignment::Horizontal, Alignment::Vertical] { + if let Some(start) = self.find_starting_tile(pos, alignment) { + if let Some(chain) = self.find_chain_in_direction(start, alignment) { + if chain.len() > 1 { + chains.push(chain); + } + } + } + } + } + chains + } + + /// Finds the starting tile of a chain in the given direction. + fn find_starting_tile(&self, pos: Position2d, alignment: Alignment) -> Option { + self.get(pos.x, pos.y)?; + let mut pos = pos; + let direction = alignment.start(); + loop { + if let Some(relative) = pos.relative(direction, self) { + if self.get(relative.x, relative.y).is_some() { + pos = relative; + } else { + return Some(pos); + } + } else { + return Some(pos); + } + } + } + + /// Finds a chain of tiles in the given direction. + fn find_chain_in_direction( + &self, + pos: Position2d, + direction: Alignment, + ) -> Option> { + let mut chain = Vec::new(); + let mut pos = pos; + loop { + chain.push(pos); + if let Some(relative) = pos.relative(direction.end(), self) { + if self.get(relative.x, relative.y).is_none() { + break; + } + pos = relative; + } else { + break; + } + } + if chain.is_empty() { + None + } else { + Some(chain) + } + } + /// Determines whether the given positions are contiguous. /// /// Contiguous means that the positions are adjacent in a straight line, either @@ -167,4 +229,25 @@ mod tests { Alignment::Vertical )); } + + #[test] + fn test_find_chains() { + let mut board = Board::default(); + for x in 1..5 { + board.set(x, 2, Tile::Equals); + } + assert_eq!( + board.find_chains(&[Position2d::new(0, 0)]), + Vec::>::new() + ); + let expected = vec![vec![ + Position2d::new(1, 2), + Position2d::new(2, 2), + Position2d::new(3, 2), + Position2d::new(4, 2), + ]]; + assert_eq!(board.find_chains(&[Position2d::new(1, 2)]), expected); + assert_eq!(board.find_chains(&[Position2d::new(2, 2)]), expected); + assert_eq!(board.find_chains(&[Position2d::new(4, 2)]), expected); + } }