use array_macro::array;
use strum_macros::EnumIter;
use crate::error;
use crate::prelude::macros;
const MASKS: [BitMask; 64] = {
let mask0 = BitMask::new();
array![n => mask0.rotate_left_all(n as u32); 64]
};
#[derive(Debug, Clone)]
pub struct BitMask {
pub core: u64,
pub outfield: u64,
pieces: [u64; 3],
edges: [u64; 4], walls: [u64; 2], for_hmin: [u64; 3],
for_hmax: [u64; 3],
for_vmin: [u64; 3],
for_vmax: [u64; 3],
for_idx: [u64; 4],
}
impl BitMask {
const fn new() -> Self {
let core = 0x000000000f0f0f0f_u64;
let outfield = 0x607fffe060606060_u64;
let pieces = [
0x0000001f90101010_u64,
0x9000001f00808080_u64,
0x8f00001080808080_u64,
];
let edges = [
0x000000000f000000_u64, 0x0000000001010101_u64, 0x0000000008080808_u64, 0x000000000000000f_u64, ];
let walls = [
0x0f00000f00000000_u64, 0x8000000010909090_u64, ];
let for_hmin = array![n => core.rotate_left(3 - n as u32); 3];
let for_hmax = array![n => core.rotate_right(3 - n as u32); 3];
let for_vmin = array![n => core.rotate_left(8 * (3 - n) as u32); 3];
let for_vmax = array![n => core.rotate_right(8 * (3 - n) as u32); 3];
let for_idx = [
0x0f0f0000_u64,
0x0f000f00_u64,
0x0c0c0c0c_u64,
0x0a0a0a0a_u64,
];
Self {
core,
outfield,
pieces,
edges,
walls,
for_hmin,
for_hmax,
for_vmin,
for_vmax,
for_idx,
}
}
const fn rotate_left_all(&self, n: u32) -> Self {
const fn _make_rotated<const N: usize>(base: &[u64; N], n: u32) -> [u64; N] {
let mut ret = [0; N];
macros::for_loop!(let mut i = 0; i < N; i += 1 => {
ret[i] = base[i].rotate_left(n);
});
ret
}
macro_rules! define_rotated {
($($field:ident),*) => {
$(let $field = _make_rotated(&self.$field, n);)*
};
}
let core = self.core.rotate_left(n);
let outfield = self.outfield.rotate_left(n);
define_rotated!(pieces, edges, walls, for_hmin, for_hmax, for_vmin, for_vmax, for_idx);
Self {
core,
outfield,
pieces,
edges,
walls,
for_hmin,
for_hmax,
for_vmin,
for_vmax,
for_idx,
}
}
pub fn field_idx(&self, bit: u64) -> Option<usize> {
if bit & self.core != bit {
return None;
}
let mut idx = 0_usize;
for mask in self.for_idx {
idx <<= 1;
if bit & mask == bit {
idx += 1;
}
}
Some(idx)
}
fn target_bit_to_direction(
&self,
target_bit: u64,
) -> Result<Option<Direction>, error::DirectionError> {
if target_bit & self.core == target_bit {
return Ok(None);
}
if target_bit & self.outfield == target_bit {
return Err(error::DirectionError::BitOutOfField(target_bit));
}
let mut idx = 0_usize;
for piece in self.pieces {
idx <<= 1;
if target_bit & piece == target_bit {
idx += 1;
}
}
let direction = Direction::try_from(idx)?;
Ok(Some(direction))
}
pub fn calc_wall_bits(&self, bits: u64) -> u64 {
let mut wall_bits = 0;
if (bits & self.edges[0] != 0) && (bits & self.edges[3] != 0) {
wall_bits |= self.walls[0];
}
if (bits & self.edges[1] != 0) && (bits & self.edges[2] != 0) {
wall_bits |= self.walls[1];
}
wall_bits
}
pub fn minimum_rectangle(&self, bits: u64) -> Rectangle {
macro_rules! calc_edge {
($field:ident, $default:expr, $i:ident, $eval:expr) => {{
let mut val = $default;
for ($i, m) in self.$field.into_iter().enumerate() {
if bits & m == bits {
val = $eval;
break;
}
}
val
}};
}
let hmin = calc_edge!(for_hmin, 0, i, 3 - i);
let hmax = calc_edge!(for_hmax, 3, i, i);
let vmin = calc_edge!(for_vmin, 0, i, 3 - i);
let vmax = calc_edge!(for_vmax, 3, i, i);
Rectangle {
hmin,
hmax,
vmin,
vmax,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Rectangle {
pub hmin: usize,
pub hmax: usize,
pub vmin: usize,
pub vmax: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct RectangleSize {
pub vsize: usize,
pub hsize: usize,
}
impl Rectangle {
pub fn size(&self) -> RectangleSize {
let hsize = self.hmax - self.hmin + 1;
let vsize = self.vmax - self.vmin + 1;
RectangleSize { hsize, vsize }
}
}
#[derive(Debug, Clone, Copy, EnumIter)]
pub(crate) enum Direction {
SE,
S,
SW,
E,
W,
NE,
N,
NW,
}
impl TryFrom<usize> for Direction {
type Error = error::DirectionError;
fn try_from(value: usize) -> Result<Self, Self::Error> {
use Direction::*;
let direction = match value {
0 => SE,
1 => S,
2 => SW,
3 => E,
4 => W,
5 => NE,
6 => N,
7 => NW,
_ => return Err(error::DirectionError::IndexOutOfRange(value)),
};
Ok(direction)
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct MaskViewer {
status: usize,
}
impl MaskViewer {
pub fn new() -> Self {
Self { status: 0 }
}
pub fn view_mask(&self) -> &BitMask {
unsafe { MASKS.get_unchecked(self.status) }
}
pub(crate) fn view_next_mask(&self, d: Direction) -> &BitMask {
let next_status = Self::shift_status(self.status, d);
unsafe { MASKS.get_unchecked(next_status) }
}
pub(crate) fn view_mask_at(&self, bit: u64) -> Result<&BitMask, error::DirectionError> {
match self.view_mask().target_bit_to_direction(bit)? {
Some(d) => Ok(self.view_next_mask(d)),
None => Ok(self.view_mask()),
}
}
pub(crate) fn shift_toward(&mut self, bit: u64) -> Result<(), error::DirectionError> {
if let Some(d) = self.view_mask().target_bit_to_direction(bit)? {
self.do_shift(d);
}
Ok(())
}
fn do_shift(&mut self, d: Direction) {
self.status = Self::shift_status(self.status, d);
}
fn shift_status(status: usize, d: Direction) -> usize {
use Direction::*;
let x = match d {
SE => 55,
S => 56,
SW => 57,
E => 63,
W => 1,
NE => 7,
N => 8,
NW => 9,
};
(status + x) % 64
}
}