Skip to content

Commit

Permalink
Implement suffocation rules
Browse files Browse the repository at this point in the history
  • Loading branch information
TrueDoctor committed May 25, 2024
1 parent 28ea66e commit 845e748
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 19 deletions.
76 changes: 59 additions & 17 deletions backend/src/game.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,51 @@ impl Board {
x + usize::from(self.width) * y
}

fn is_suicide(&mut self, x: u16, y: u16, id: u8) -> bool {
let empty_tiles = self.adjacent_filter(x, y, Tile::Empty);
let mut player_tiles = self.adjacent_filter(x, y, Tile::Player(id));
(empty_tiles.count() == 0) && player_tiles.all(|(x, y)| self.uf.get_liberties(self.index(x, y)) < 2)
}

fn tile_mut(&mut self, x: u16, y: u16) -> Option<&mut Tile> {
let index = self.index(x, y);
self.tiles.get_mut(index)
}

fn try_tile(&self, x: i16, y: i16) -> Option<(u16, u16, Tile)> {
if x < 0 || y < 0 {
return None;
}
self.tile(x as u16, y as u16).map(|t| (x as u16, y as u16, t))
}

fn tile(&self, x: u16, y: u16) -> Option<Tile> {
let index = self.index(x, y);
self.tiles.get(index).copied()
}

pub(crate) fn resolve_conflict(&mut self, x: u16, y: u16) {
let Some(tile) = self.tile_mut(x, y) else { return };
*tile = match *tile {
Tile::TryPlace(id) => Tile::Player(id),
let index = self.index(x, y);
let Some(mut tile) = self.tile(x, y) else { return };
tile = match tile {
Tile::TryPlace(id) => {
self.adjacent_tiles(x, y)
.for_each(|(x, y, _)| self.uf.subtract_liberty(self.index(x, y)));
self.adjacent_filter(x, y, Tile::Player(id))
.for_each(|(x, y)| self.uf.union(index, self.index(x, y)));

Tile::Player(id)
}
Tile::Contested => Tile::Empty,
t => t,
};
self.tiles[index] = tile;
}

pub(crate) fn try_place(&mut self, x: u16, y: u16, id: u8) {
if self.is_suicide(x, y, id) {
return;
}
let Some(tile) = self.tile_mut(x, y) else { return };
*tile = match *tile {
Tile::Empty => Tile::TryPlace(id),
Expand All @@ -55,27 +85,33 @@ impl Board {
};
}

pub(crate) fn kill_neighbors(&mut self, x: u16, y: u16) {
for (x, y, _) in self.adjacent_tiles(x, y) {
let index = self.index(x, y);
if self.uf.get_liberties(index) == 0 {
self.remove_group(x, y);
}
}
}

pub(crate) fn serialize(&self) -> String {
self.tiles.iter().map(Tile::to_char).collect()
}

fn remove_group(&mut self, x: u16, y: u16) {
let tile = *self.tile_mut(x, y).expect("Tried to remove non board space");
let mut stack = vec![(x, y)];
while let Some((x, y)) = stack.pop() {
if self.tile_mut(x, y).as_deref() == Some(&tile) {
let mut stack = vec![(x, y, tile)];
while let Some((x, y, t)) = stack.pop() {
if t == tile {
self.uf.reset_node(self.index(x, y));
self.adjacent_tiles(x, y, &mut stack)
self.adjacent_tiles(x, y).for_each(|d| stack.push(d))
}
}
}

fn adjacent_tiles(&self, x: u16, y: u16, stack: &mut Vec<(u16, u16)>) {
for i in (x.max(1) - 1)..=(x.min(u16::from(self.width) - 1) + 1) {
for j in (y.max(1) - 1)..=(y.min(u16::from(self.height) - 1) + 1) {
stack.push((i, j))
}
}
fn adjacent_filter(&self, x: u16, y: u16, tile: Tile) -> impl Iterator<Item = (u16, u16)> {
self.adjacent_tiles(x, y)
.filter_map(move |(x, y, t)| (t == tile).then(|| (x, y)))
}

pub(crate) fn reset_timer(&mut self) {
Expand All @@ -88,11 +124,17 @@ impl Board {
.unwrap_or(Duration::ZERO)
.as_millis()
}
}

pub enum GoError {
OutOfBounds,
Suicide,
fn adjacent_tiles(&self, x: u16, y: u16) -> impl Iterator<Item = (u16, u16, Tile)> {
let (x, y) = (x as i16, y as i16);
let data = [
self.try_tile(x - 1, y),
self.try_tile(x, y - 1),
self.try_tile(x + 1, y),
self.try_tile(x, y + 1),
];
data.into_iter().flatten()
}
}

#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
Expand Down
4 changes: 2 additions & 2 deletions backend/src/game/uf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ impl UnionFind {
}
}

pub fn add_liberty(&mut self, x: usize, n: usize) {
pub fn add_liberty(&mut self, x: usize, n: isize) {
let root = self.find(x);
self.liberties[root] += n;
self.liberties[root] = self.liberties[root].saturating_add_signed(n);
}

pub fn get_liberties(&mut self, x: usize) -> usize {
Expand Down
5 changes: 5 additions & 0 deletions backend/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ impl GameState {
self.board.resolve_conflict(x, y);
}
}
for user in &mut self.users {
if let Some((x, y)) = user.next_stone.take() {
self.board.kill_neighbors(x, y);
}
}
}

pub(crate) fn alloc_char(&mut self, addr: SocketAddr) -> Option<u8> {
Expand Down

0 comments on commit 845e748

Please sign in to comment.