use crate::{
cells::{Alive, CellRef, Dead, State},
rules::Rule,
search::Reason,
world::World,
};
use bitflags::bitflags;
use ca_rules::ParseNtLife;
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct NbhdDesc(usize);
bitflags! {
struct ImplFlags: u32 {
const CONFLICT = 0b_0000_0001;
const SUCC_ALIVE = 0b_0000_0100;
const SUCC_DEAD = 0b_0000_1000;
const SUCC = Self::SUCC_ALIVE.bits | Self::SUCC_DEAD.bits;
const SELF_ALIVE = 0b_0001_0000;
const SELF_DEAD = 0b_0010_0000;
const SELF = Self::SELF_ALIVE.bits | Self::SELF_DEAD.bits;
const NBHD = 0xffff << 6;
}
}
pub struct NtLife {
b0: bool,
impl_table: Vec<ImplFlags>,
}
impl NtLife {
pub fn new(b: Vec<u8>, s: Vec<u8>) -> Self {
let b0 = b.contains(&0);
let impl_table = vec![ImplFlags::empty(); 1 << 20];
NtLife { b0, impl_table }
.init_trans(b, s)
.init_conflict()
.init_impl()
.init_impl_nbhd()
}
fn init_trans(mut self, b: Vec<u8>, s: Vec<u8>) -> Self {
for alives in 0..=0xff {
let desc = (0xff & !alives) << 12 | alives << 4;
let alives = alives as u8;
self.impl_table[desc | Dead as usize] |= if b.contains(&alives) {
ImplFlags::SUCC_ALIVE
} else {
ImplFlags::SUCC_DEAD
};
self.impl_table[desc | Alive as usize] |= if s.contains(&alives) {
ImplFlags::SUCC_ALIVE
} else {
ImplFlags::SUCC_DEAD
};
self.impl_table[desc] |= if b.contains(&alives) && s.contains(&alives) {
ImplFlags::SUCC_ALIVE
} else if !b.contains(&alives) && !s.contains(&alives) {
ImplFlags::SUCC_DEAD
} else {
ImplFlags::empty()
};
}
for unknowns in 1usize..=0xff {
let n = unknowns.next_power_of_two() >> usize::from(!unknowns.is_power_of_two());
for alives in (0..=0xff).filter(|a| a & unknowns == 0) {
let desc = (0xff & !alives & !unknowns) << 12 | alives << 4;
let desc0 = (0xff & !alives & !unknowns | n) << 12 | alives << 4;
let desc1 = (0xff & !alives & !unknowns) << 12 | (alives | n) << 4;
for &state in [Dead as usize, Alive as usize, 0].iter() {
let trans0 = self.impl_table[desc0 | state];
if trans0 == self.impl_table[desc1 | state] {
self.impl_table[desc | state] |= trans0;
}
}
}
}
self
}
fn init_conflict(mut self) -> Self {
for nbhd_state in 0..0xffff {
for &state in [Dead as usize, Alive as usize, 0].iter() {
let desc = nbhd_state << 4 | state;
if self.impl_table[desc].contains(ImplFlags::SUCC_ALIVE) {
self.impl_table[desc | (Dead as usize) << 2] = ImplFlags::CONFLICT;
} else if self.impl_table[desc].contains(ImplFlags::SUCC_DEAD) {
self.impl_table[desc | (Alive as usize) << 2] = ImplFlags::CONFLICT;
}
}
}
self
}
fn init_impl(mut self) -> Self {
for unknowns in 0..=0xff {
for alives in (0..=0xff).filter(|a| a & unknowns == 0) {
let desc = (0xff & !alives & !unknowns) << 12 | alives << 4;
for &succ_state in [Dead as usize, Alive as usize].iter() {
let flag = if succ_state == Dead as usize {
ImplFlags::SUCC_ALIVE | ImplFlags::CONFLICT
} else {
ImplFlags::SUCC_DEAD | ImplFlags::CONFLICT
};
let possibly_dead = !self.impl_table[desc | Dead as usize].intersects(flag);
let possibly_alive = !self.impl_table[desc | Alive as usize].intersects(flag);
let index = desc | succ_state << 2;
if possibly_dead && !possibly_alive {
self.impl_table[index] |= ImplFlags::SELF_DEAD;
} else if !possibly_dead && possibly_alive {
self.impl_table[index] |= ImplFlags::SELF_ALIVE;
} else if !possibly_dead && !possibly_alive {
self.impl_table[index] = ImplFlags::CONFLICT;
}
}
}
}
self
}
fn init_impl_nbhd(mut self) -> Self {
for unknowns in 1usize..=0xff {
for n in (0..8).map(|i| 1 << i).filter(|n| unknowns & n != 0) {
for alives in 0..=0xff {
let desc = (0xff & !alives & !unknowns) << 12 | alives << 4;
let desc0 = (0xff & !alives & !unknowns | n) << 12 | alives << 4;
let desc1 = (0xff & !alives & !unknowns) << 12 | (alives | n) << 4;
for &succ_state in [Dead as usize, Alive as usize].iter() {
let flag = if succ_state == Dead as usize {
ImplFlags::SUCC_ALIVE | ImplFlags::CONFLICT
} else {
ImplFlags::SUCC_DEAD | ImplFlags::CONFLICT
};
let index = desc | succ_state << 2;
for &state in [Dead as usize, Alive as usize, 0].iter() {
let possibly_dead = !self.impl_table[desc0 | state].intersects(flag);
let possibly_alive = !self.impl_table[desc1 | state].intersects(flag);
if possibly_dead && !possibly_alive {
self.impl_table[index | state] |=
ImplFlags::from_bits((n.pow(2) << 7) as u32).unwrap();
} else if !possibly_dead && possibly_alive {
self.impl_table[index | state] |=
ImplFlags::from_bits((n.pow(2) << 6) as u32).unwrap();
} else if !possibly_dead && !possibly_alive {
self.impl_table[index | state] = ImplFlags::CONFLICT;
}
}
}
}
}
}
self
}
pub fn parse_rule(input: &str) -> Result<Self, String> {
ParseNtLife::parse_rule(input).map_err(|e| e.to_string())
}
}
impl Rule for NtLife {
type Desc = NbhdDesc;
fn b0(&self) -> bool {
self.b0
}
fn new_desc(state: State, succ_state: State) -> Self::Desc {
let nbhd_state = match state {
Dead => 0xff00,
Alive => 0x00ff,
};
NbhdDesc(nbhd_state << 4 | (succ_state as usize) << 2 | state as usize)
}
fn update_desc(cell: CellRef<Self>, old_state: Option<State>, state: Option<State>) {
let nbhd_change_num = match (state, old_state) {
(Some(Dead), Some(Alive)) | (Some(Alive), Some(Dead)) => 0x0101,
(Some(Dead), None) | (None, Some(Dead)) => 0x0100,
(Some(Alive), None) | (None, Some(Alive)) => 0x0001,
_ => 0x0000,
};
for (i, &neigh) in cell.nbhd.iter().rev().enumerate() {
let neigh = neigh.unwrap();
let mut desc = neigh.desc.get();
desc.0 ^= nbhd_change_num << i << 4;
neigh.desc.set(desc);
}
let change_num = match (state, old_state) {
(Some(Dead), Some(Alive)) | (Some(Alive), Some(Dead)) => 0b11,
(Some(Dead), None) | (None, Some(Dead)) => 0b10,
(Some(Alive), None) | (None, Some(Alive)) => 0b01,
_ => 0,
};
if let Some(pred) = cell.pred {
let mut desc = pred.desc.get();
desc.0 ^= change_num << 2;
pred.desc.set(desc);
}
let mut desc = cell.desc.get();
desc.0 ^= change_num;
cell.desc.set(desc);
}
fn consistify<'a>(world: &mut World<'a, Self>, cell: CellRef<'a, Self>) -> bool {
let flags = world.rule.impl_table[cell.desc.get().0];
if flags.contains(ImplFlags::CONFLICT) {
return false;
}
if flags.intersects(ImplFlags::SUCC_DEAD | ImplFlags::SUCC_ALIVE) {
let state = if flags.contains(ImplFlags::SUCC_DEAD) {
Dead
} else {
Alive
};
let succ = cell.succ.unwrap();
return world.set_cell(succ, state, Reason::Deduce);
}
if flags.intersects(ImplFlags::SELF_DEAD | ImplFlags::SELF_ALIVE) {
let state = if flags.contains(ImplFlags::SELF_DEAD) {
Dead
} else {
Alive
};
if !world.set_cell(cell, state, Reason::Deduce) {
return false;
}
}
if flags.intersects(ImplFlags::NBHD) {
for (i, &neigh) in cell.nbhd.iter().enumerate() {
if flags.intersects(ImplFlags::from_bits(3 << (2 * i + 6)).unwrap()) {
if let Some(neigh) = neigh {
let state =
if flags.contains(ImplFlags::from_bits(1 << (2 * i + 7)).unwrap()) {
Dead
} else {
Alive
};
if !world.set_cell(neigh, state, Reason::Deduce) {
return false;
}
}
}
}
}
true
}
}
impl ParseNtLife for NtLife {
fn from_bs(b: Vec<u8>, s: Vec<u8>) -> Self {
Self::new(b, s)
}
}