use crate::{Coord, CoordState};
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Row {
data: u64,
}
impl Row {
pub(crate) const E: u64 = 0b00;
pub(crate) const B: u64 = 0b10;
pub(crate) const W: u64 = 0b11;
const LEN_OFFSET: u8 = 64 - 5;
pub fn new(len: u8) -> Self {
assert!((1u8..=26u8).contains(&len));
Self {
data: (len as u64) << Self::LEN_OFFSET,
}
}
#[inline(always)]
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> u8 {
(self.data >> Self::LEN_OFFSET) as u8
}
pub fn clear(&mut self) {
self.data = (self.len() as u64) << Self::LEN_OFFSET;
}
#[inline(always)]
pub fn get_raw(&self) -> u64 {
self.data & !(0b11111 << Self::LEN_OFFSET)
}
#[inline(always)]
fn bit_pos(pos: u8) -> u8 {
2 * pos
}
#[inline(always)]
pub fn get(&self, pos: u8) -> CoordState {
match (self.data >> Self::bit_pos(pos)) & 0b11 {
Self::B => CoordState::Black,
Self::W => CoordState::White,
_ => CoordState::Empty,
}
}
#[inline(always)]
pub fn iter(
&self,
) -> std::iter::Map<std::ops::Range<u8>, impl FnMut(u8) -> CoordState + use<'_>> {
(0..self.len()).map(|i| self.get(i))
}
#[inline(always)]
pub fn set(&mut self, pos: u8, state: CoordState) {
let mask = u64::MAX ^ (0b11 << Self::bit_pos(pos));
self.data &= mask;
self.data |= match state {
CoordState::Empty => Self::E,
CoordState::Black => Self::B,
CoordState::White => Self::W,
} << Self::bit_pos(pos);
}
}
impl std::fmt::Debug for Row {
#[inline(always)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use std::fmt::Write;
for st in self.iter() {
let ch = match st {
CoordState::Black => 'X',
CoordState::White => 'O',
CoordState::Empty => '.',
};
f.write_char(ch)?;
}
Ok(())
}
}
impl From<&[CoordState]> for Row {
fn from(sts: &[CoordState]) -> Self {
if sts.is_empty() {
return Self::new(1);
}
let mut row = Self::new(sts.len().min(26) as u8);
for i in 0..row.len() {
row.set(i, sts[i as usize]);
}
row
}
}
impl<const SZ: usize> Default for Rows<SZ> {
fn default() -> Self {
Self::new()
}
}
#[test]
fn row_test() {
let mut row = Row::new(15);
assert_eq!(row.len(), 15);
assert_eq!(row.get_raw(), 0b_00_00_00_00_00);
row.set(0, CoordState::Black);
assert_eq!(row.get(0), CoordState::Black);
assert_eq!(row.get_raw(), 0b_00_00_00_00_10);
row.set(2, CoordState::White);
assert_eq!(row.get(2), CoordState::White);
assert_eq!(row.get_raw(), 0b_00_00_11_00_10);
row.set(2, CoordState::Black);
assert_eq!(row.get(2), CoordState::Black);
row.set(0, CoordState::Empty);
assert_eq!(row.get(0), CoordState::Empty);
assert_eq!(row.len(), 15);
row.clear();
assert_eq!(row.get_raw(), 0b_00_00_00_00_00);
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Rows<const SZ: usize> {
horizontal: [Row; SZ], vertical: [Row; SZ], diagonal_l1: [Row; SZ], diagonal_l2: [Row; SZ], diagonal_r1: [Row; SZ], diagonal_r2: [Row; SZ], }
impl<const SZ: usize> Rows<SZ> {
pub fn new() -> Self {
assert!(SZ >= 5 && SZ <= 26);
let fake = Row::new(SZ as u8);
let mut rows = Self {
horizontal: [fake; SZ],
vertical: [fake; SZ],
diagonal_l1: [fake; SZ],
diagonal_l2: [fake; SZ],
diagonal_r1: [fake; SZ],
diagonal_r2: [fake; SZ],
};
rows.clear();
rows
}
pub fn clear(&mut self) {
self.horizontal = [Row::new(SZ as u8); SZ];
self.vertical = [Row::new(SZ as u8); SZ];
for len in 1..=SZ {
self.diagonal_l1[len - 1] = Row::new(len as u8);
self.diagonal_r1[len - 1] = Row::new(len as u8);
if SZ > len {
self.diagonal_l2[len - 1] = Row::new((SZ - len) as u8);
self.diagonal_r2[len - 1] = Row::new((SZ - len) as u8);
}
}
}
#[inline(always)]
pub fn get(&self, coord: Coord<SZ>) -> CoordState {
let Some((x, y)) = coord.get() else {
return CoordState::Empty;
};
self.horizontal[y as usize].get(x)
}
#[inline]
pub fn get_quad_rows(&self, coord: Coord<SZ>) -> Option<[Row; 4]> {
let (x, y) = coord.get()?;
let row_diagonal_l = if x <= y {
self.diagonal_l1[SZ - 1 - (y - x) as usize]
} else {
self.diagonal_l2[(x - y - 1) as usize]
};
let row_diagonal_r = if x + y <= SZ as u8 - 1 {
self.diagonal_r1[(x + y) as usize]
} else {
self.diagonal_r2[(x + y) as usize - SZ]
};
Some([
self.horizontal[y as usize],
self.vertical[x as usize],
row_diagonal_l,
row_diagonal_r,
])
}
#[inline]
pub fn set(&mut self, coord: Coord<SZ>, st: CoordState) {
let Some((x, y)) = coord.get() else {
return;
};
self.horizontal[y as usize].set(x, st);
self.vertical[x as usize].set(y, st);
if x <= y {
self.diagonal_l1[SZ - 1 - (y - x) as usize].set(x, st);
} else {
self.diagonal_l2[(x - y - 1) as usize].set(y, st);
}
if x + y <= SZ as u8 - 1 {
self.diagonal_r1[(x + y) as usize].set(x, st);
} else {
self.diagonal_r2[(x + y) as usize - SZ].set(SZ as u8 - 1 - y, st);
}
}
}
#[test]
fn rows_test() {
let mut rows = Rows::<15>::new();
let rec = ["h7", "i7", "j8", "k9", "i9", "h10", "k8", "i8"];
let mut st = CoordState::Black;
for coord in rec.iter() {
rows.set(coord.parse().unwrap(), st);
st = if st == CoordState::Black {
CoordState::White
} else {
CoordState::Black
};
}
let quad_rows = rows.get_quad_rows(Coord::from(7, 7)).unwrap();
assert_eq!(quad_rows[0].len(), 15);
assert_eq!(
format!(
"{:?} {:?} {:?} {:?}",
quad_rows[0], quad_rows[1], quad_rows[2], quad_rows[3]
),
"........OXX.... ......X..O..... ........X...... ........O......"
);
let quad_rows = rows.get_quad_rows(Coord::from(7, 5)).unwrap();
assert_eq!(
format!(
"{:?} {:?} {:?} {:?}",
quad_rows[0], quad_rows[1], quad_rows[2], quad_rows[3]
),
"............... ......X..O..... ......OXO.... ............."
);
let quad_rows = rows.get_quad_rows(Coord::from(9, 8)).unwrap();
assert_eq!(
format!(
"{:?} {:?} {:?} {:?}",
quad_rows[0], quad_rows[1], quad_rows[2], quad_rows[3]
),
"........X.O.... .......X....... ......XO...... .......X...."
);
let quad_rows = rows.get_quad_rows(Coord::from(6, 8)).unwrap();
assert_eq!(
format!(
"{:?} {:?} {:?} {:?}",
quad_rows[0], quad_rows[1], quad_rows[2], quad_rows[3]
),
"........X.O.... ............... .......O..... ........O......"
);
rows.clear();
rows.set(Coord::from(0, 0), CoordState::Black);
rows.set(Coord::from(0, 14), CoordState::White);
rows.set(Coord::from(14, 0), CoordState::Black);
rows.set(Coord::from(14, 14), CoordState::White);
let quad_rows = rows.get_quad_rows(Coord::from(0, 0)).unwrap();
assert_eq!(
format!(
"{:?} {:?} {:?} {:?}",
quad_rows[0], quad_rows[1], quad_rows[2], quad_rows[3]
),
"X.............X X.............O X.............O X"
);
let quad_rows = rows.get_quad_rows(Coord::from(14, 0)).unwrap();
assert_eq!(
format!(
"{:?} {:?} {:?} {:?}",
quad_rows[0], quad_rows[1], quad_rows[2], quad_rows[3]
),
"X.............X X.............O X O.............X"
);
}