Skip to content

Commit

Permalink
WIP working on go board optimization?
Browse files Browse the repository at this point in the history
  • Loading branch information
KarelPeeters committed Jan 20, 2024
1 parent cf4cb61 commit 73e30f0
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 6 deletions.
28 changes: 24 additions & 4 deletions src/games/go/board.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,13 @@ impl GoBoard {
}
}

fn is_available_move_sim(rules: &Rules, history: &IntSet<Zobrist>, kind: PlacementKind, next_zobrist: Zobrist) -> bool {
fn is_available_move_sim(
rules: &Rules,
history: &IntSet<Zobrist>,
kind: PlacementKind,
has_had_same_color: bool,
next_zobrist: Zobrist,
) -> bool {
// check placement kind
match kind {
PlacementKind::Normal => {}
Expand All @@ -223,9 +229,11 @@ fn is_available_move_sim(rules: &Rules, history: &IntSet<Zobrist>, kind: Placeme
}
}

let could_repeat = has_had_same_color || kind == PlacementKind::SuicideMulti;

// check history
// scan in reverse to hopefully find quicker matches
if !rules.allow_repeating_tiles() && history.contains(&next_zobrist) {
if could_repeat && !rules.allow_repeating_tiles() && history.contains(&next_zobrist) {
return false;
}

Expand All @@ -250,7 +258,18 @@ impl Board for GoBoard {
} else {
let tile = tile.to_flat(self.size());
match self.chains.simulate_place_stone_minimal(tile, self.next_player) {
Ok(sim) => is_available_move_sim(&self.rules, &self.history, sim.kind, sim.next_zobrist),
Ok(sim) => {
// TODO move earlier for perf
let could_be_super_ko = self.chains.has_had_stone_at(tile, self.next_player);

is_available_move_sim(
&self.rules,
&self.history,
sim.kind,
could_be_super_ko,
sim.next_zobrist,
)
}
Err(TileOccupied) => false,
}
}
Expand Down Expand Up @@ -288,8 +307,9 @@ impl Board for GoBoard {
let tile = tile.to_flat(self.size());
let rules = &self.rules;
let history = &self.history;
let has_had_same_color = self.chains.has_had_stone_at(tile, curr) | true;
let place_result = self.chains.place_stone_if(tile, curr, |sim| {
is_available_move_sim(rules, history, sim.kind, sim.next_zobrist)
is_available_move_sim(rules, history, sim.kind, has_had_same_color, sim.next_zobrist)
});
match place_result {
Ok((_, true)) => {}
Expand Down
27 changes: 25 additions & 2 deletions src/games/go/chains.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ pub struct Chains {
pub struct TileContent {
pub group_id: OptionU16,
pub link: LinkNode,
/// Whether a tile of the given color has ever been present at the current position.
/// Bitset with indices corresponding to [Player::index].
pub has_had_stone: u8,
}

// TODO compact? we can at least force player into one of the other fields
Expand Down Expand Up @@ -125,6 +128,7 @@ impl Chains {
.map(|i| TileContent {
group_id: OptionU16::None,
link: LinkNode::full(area, i),
has_had_stone: 0,
})
.collect_vec();

Expand Down Expand Up @@ -180,6 +184,10 @@ impl Chains {
self.group_at(tile).map(|group| group.color)
}

pub fn has_had_stone_at(&self, tile: FlatTile, color: Player) -> bool {
(self.content_at(tile).has_had_stone & (1 << color.index())) != 0
}

pub fn zobrist(&self) -> Zobrist {
self.zobrist
}
Expand Down Expand Up @@ -568,7 +576,9 @@ impl Chains {
};

// set tile itself
self.tiles[tile_index as usize].group_id = OptionU16::Some(group_id);
let content = &mut self.tiles[tile_index as usize];
content.group_id = OptionU16::Some(group_id);
content.has_had_stone |= 1 << color.index();

// decrement adjacent liberties
change_liberty_edges_at(
Expand Down Expand Up @@ -651,7 +661,9 @@ impl Chains {

// mark tiles as part of new group
new_group_stones.for_each_mut(&mut self.tiles, |tiles, tile_index| {
tiles[tile_index as usize].group_id = OptionU16::Some(new_group_id);
let content = &mut tiles[tile_index as usize];
content.group_id = OptionU16::Some(new_group_id);
content.has_had_stone |= 1 << color.index();
});

new_group_id
Expand Down Expand Up @@ -748,6 +760,7 @@ impl Chains {
TileContent {
group_id: old_content.group_id,
link: old_content.link.map_index(map_tile_index),
has_had_stone: old_content.has_had_stone,
}
})
.collect_vec();
Expand Down Expand Up @@ -809,6 +822,16 @@ impl Chains {
// group must be alive
assert!(!group.is_dead());

// has_had_stone must at least cover the current stones
assert_ne!(
content.has_had_stone & (1 << group.color.index()),
0,
"Tile {:?} has history {:0b} but group {:?}",
tile,
content.has_had_stone,
group
);

// track info
let group_zobrist = group_info.entry(id).or_insert((Zobrist::default(), HashSet::default()));
group_zobrist.0 ^= Zobrist::for_color_tile(group.color, tile);
Expand Down
13 changes: 13 additions & 0 deletions tests/board/go_chains.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,14 @@ fn capture_corner() {
println!("{}", chains);
assert_eq!(chains.to_fen(), "...../...../...../w..../b....");

let tile_dead = Tile::new(0, 0).to_flat(chains.size());
let tile_final = Tile::new(1, 0).to_flat(chains.size());

assert!(chains.has_had_stone_at(tile_dead, Player::A));
assert!(!chains.has_had_stone_at(tile_dead, Player::B));
assert!(!chains.has_had_stone_at(tile_final, Player::A));
assert!(!chains.has_had_stone_at(tile_final, Player::B));

let sim = chains
.place_stone(Tile::new(1, 0).to_flat(chains.size()), Player::B)
.unwrap();
Expand All @@ -175,6 +183,11 @@ fn capture_corner() {
expected.assert_eq(chains.group_at(Tile::new(1, 0).to_flat(chains.size())));
expected.assert_eq(chains.group_at(Tile::new(0, 1).to_flat(chains.size())));

assert!(chains.has_had_stone_at(tile_dead, Player::A));
assert!(!chains.has_had_stone_at(tile_dead, Player::B));
assert!(!chains.has_had_stone_at(tile_final, Player::A));
assert!(chains.has_had_stone_at(tile_final, Player::B));

chains_test_main(&chains);
}

Expand Down

0 comments on commit 73e30f0

Please sign in to comment.