|
|
|
@ -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<Position2d> {
|
|
|
|
|
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<Vec<Position2d>> {
|
|
|
|
|
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<Position2d> {
|
|
|
|
|
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<Vec<Position2d>> {
|
|
|
|
|
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::<Vec<Position2d>>::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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|