///| D4 coordinate candidates are retained only when both holes AND all directed jumps are preserved.
///|
/// This is conservative on triangular lattices: no unsupported 60-degree symmetry is assumed.
fn Board::symmetries(self : Board, goal : Goal) -> Array[Array[Int]] {
let maps : Array[Array[Int]] = []
for t = 0; t < 8; t = t + 1 {
let permutation : Array[Int] = []
for c in self.cells {
let (x, y) = match t {
0 => (c.x, c.y)
1 => (self.width - 1 - c.x, c.y)
2 => (c.x, self.height - 1 - c.y)
3 => (self.width - 1 - c.x, self.height - 1 - c.y)
4 => (c.y, c.x)
5 => (self.height - 1 - c.y, c.x)
6 => (c.y, self.width - 1 - c.x)
_ => (self.height - 1 - c.y, self.width - 1 - c.x)
}
match self.index(x, y) {
Some(i) => permutation.push(i)
None => break
}
}
if permutation.length() != self.size() {
continue
}
let mut valid = true
for j in self.jumps {
let transformed : Jump = {
from: permutation[j.from],
over: permutation[j.over],
to: permutation[j.to],
}
if !self.jumps.contains(transformed) {
valid = false
break
}
}
if !valid {
continue
}
match goal {
Exact(p) => if transform_bits(p.bits, permutation) != p.bits { continue }
AnySingle => ()
}
if !maps.contains(permutation) {
maps.push(permutation)
}
}
maps
}
///|
fn transform_bits(bits : UInt64, permutation : Array[Int]) -> UInt64 {
let mut result = 0UL
for i = 0; i < permutation.length(); i = i + 1 {
if (bits & bit(i)) != 0UL {
result = result | bit(permutation[i])
}
}
result
}
///|
fn canonical_bits(bits : UInt64, maps : Array[Array[Int]]) -> UInt64 {
let mut best = bits
for m in maps {
let v = transform_bits(bits, m)
if v < best {
best = v
}
}
best
}
///|
/// Canonicalization is goal-dependent, so a corner goal is never swapped to another corner.
pub fn Board::canonical(
self : Board,
p : Position,
goal : Goal,
) -> Position raise PegError {
self.validate(p)
self.validate_goal(goal)
{ bits: canonical_bits(p.bits, self.symmetries(goal)) }
}
///|
pub fn Board::symmetry_count(self : Board, goal : Goal) -> Int raise PegError {
self.validate_goal(goal)
self.symmetries(goal).length()
}