Add reverse game of life
This commit is contained in:
parent
23cbd39039
commit
e5f408aed2
8 changed files with 269 additions and 3 deletions
134
src/bin/gen_reverse_game_of_life.rs
Normal file
134
src/bin/gen_reverse_game_of_life.rs
Normal file
|
|
@ -0,0 +1,134 @@
|
||||||
|
use simple_sat_solver::{
|
||||||
|
cdcl::cdcl,
|
||||||
|
expr::Expr,
|
||||||
|
solver::{normal_form_solver, normal_form_solver_from_cnf, SolutionIterator},
|
||||||
|
};
|
||||||
|
use std::ops::{Index, IndexMut};
|
||||||
|
|
||||||
|
fn neightbors(gol: &Gameoflife, x: usize, y: usize) -> Vec<(usize, usize)> {
|
||||||
|
let mut v = Vec::new();
|
||||||
|
v.push((x, y));
|
||||||
|
if y > 0 {
|
||||||
|
if x > 0 {
|
||||||
|
v.push((x - 1, y - 1));
|
||||||
|
}
|
||||||
|
v.push((x, y - 1));
|
||||||
|
if x < gol.width() - 1 {
|
||||||
|
v.push((x + 1, y - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if x > 0 {
|
||||||
|
v.push((x - 1, y));
|
||||||
|
}
|
||||||
|
if x < gol.width() - 1 {
|
||||||
|
v.push((x + 1, y));
|
||||||
|
}
|
||||||
|
if y < gol.height() - 1 {
|
||||||
|
if x > 0 {
|
||||||
|
v.push((x - 1, y + 1));
|
||||||
|
}
|
||||||
|
v.push((x, y + 1));
|
||||||
|
if x < gol.width() - 1 {
|
||||||
|
v.push((x + 1, y + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gen_reverse_game_of_life(gol: &Gameoflife) -> Expr<(usize, usize)> {
|
||||||
|
let mut and = Vec::new();
|
||||||
|
|
||||||
|
for x in 0..gol.width() {
|
||||||
|
for y in 0..gol.height() {
|
||||||
|
let n = neightbors(gol, x, y);
|
||||||
|
|
||||||
|
if gol[(x, y)] {
|
||||||
|
and.push(Expr::cnf_from_truth_function(
|
||||||
|
|s| {
|
||||||
|
s[1..].iter().filter(|&&b| b).count() == 3
|
||||||
|
|| (s[0] && s[1..].iter().filter(|&&b| b).count() == 2)
|
||||||
|
},
|
||||||
|
&n,
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
and.push(Expr::cnf_from_truth_function(
|
||||||
|
|s| {
|
||||||
|
!(s[1..].iter().filter(|&&b| b).count() == 3
|
||||||
|
|| (s[0] && s[1..].iter().filter(|&&b| b).count() == 2))
|
||||||
|
},
|
||||||
|
&n,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr::And(and)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Gameoflife {
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
data: Vec<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Gameoflife {
|
||||||
|
fn new(width: usize, height: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
data: vec![false; width * height],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn width(&self) -> usize {
|
||||||
|
self.width
|
||||||
|
}
|
||||||
|
|
||||||
|
fn height(&self) -> usize {
|
||||||
|
self.height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index<(usize, usize)> for Gameoflife {
|
||||||
|
type Output = bool;
|
||||||
|
|
||||||
|
fn index(&self, index: (usize, usize)) -> &Self::Output {
|
||||||
|
&self.data[index.1 * self.width + index.0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IndexMut<(usize, usize)> for Gameoflife {
|
||||||
|
fn index_mut(&mut self, index: (usize, usize)) -> &mut Self::Output {
|
||||||
|
&mut self.data[index.1 * self.width + index.0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut gol = Gameoflife::new(5, 5);
|
||||||
|
|
||||||
|
// gol[(1, 3)] = true;
|
||||||
|
// gol[(2, 3)] = true;
|
||||||
|
// gol[(3, 3)] = true;
|
||||||
|
// gol[(3, 2)] = true;
|
||||||
|
// gol[(2, 1)] = true;
|
||||||
|
|
||||||
|
let e = gen_reverse_game_of_life(&gol);
|
||||||
|
|
||||||
|
// let s = normal_form_solver(cdcl, e).unwrap();
|
||||||
|
|
||||||
|
let solver = SolutionIterator::new(cdcl, e);
|
||||||
|
|
||||||
|
for s in solver {
|
||||||
|
println!("Solution:");
|
||||||
|
for y in 0..gol.height() {
|
||||||
|
for x in 0..gol.width() {
|
||||||
|
if s.get(&(x, y)).copied().unwrap_or(false) {
|
||||||
|
print!("#");
|
||||||
|
} else {
|
||||||
|
print!(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -225,9 +225,10 @@ fn cdcl_internal(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cdcl(cnf: &mut NormalForm) -> Option<Vec<Index>> {
|
pub fn cdcl(cnf: &NormalForm) -> Option<Vec<Index>> {
|
||||||
|
let mut cnf = cnf.clone();
|
||||||
let num_literals = cnf.get_num_literals();
|
let num_literals = cnf.get_num_literals();
|
||||||
match cdcl_internal(cnf, State::new(num_literals), 0) {
|
match cdcl_internal(&mut cnf, State::new(num_literals), 0) {
|
||||||
Ok(state) => {
|
Ok(state) => {
|
||||||
if state.is_complete() {
|
if state.is_complete() {
|
||||||
Some(state.get_solution())
|
Some(state.get_solution())
|
||||||
|
|
|
||||||
|
|
@ -115,4 +115,38 @@ where
|
||||||
Expr::not(Expr::less_than_three_of(s)),
|
Expr::not(Expr::less_than_three_of(s)),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn cnf_from_truth_function<F: Fn(&[bool]) -> bool>(f: F, s: &[L]) -> Expr<L> {
|
||||||
|
let mut bits = vec![false; s.len()];
|
||||||
|
let mut and = Vec::new();
|
||||||
|
'outer: loop {
|
||||||
|
let mut i = 0;
|
||||||
|
|
||||||
|
if !f(&bits) {
|
||||||
|
and.push(Expr::Or(
|
||||||
|
s.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, l)| {
|
||||||
|
if !bits[i] {
|
||||||
|
Expr::not(Expr::Literal(l.clone()))
|
||||||
|
} else {
|
||||||
|
Expr::Literal(l.clone())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
while bits[i] == true {
|
||||||
|
bits[i] = false;
|
||||||
|
i += 1;
|
||||||
|
if i >= bits.len() {
|
||||||
|
break 'outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bits[i] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr::And(and)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
|
pub mod cdcl;
|
||||||
pub mod dpll;
|
pub mod dpll;
|
||||||
pub mod dpll_normal_form;
|
pub mod dpll_normal_form;
|
||||||
pub mod expr;
|
pub mod expr;
|
||||||
pub mod mapping;
|
pub mod mapping;
|
||||||
pub mod normal_form;
|
pub mod normal_form;
|
||||||
|
pub mod solver;
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ mod dpll_normal_form;
|
||||||
mod expr;
|
mod expr;
|
||||||
mod mapping;
|
mod mapping;
|
||||||
mod normal_form;
|
mod normal_form;
|
||||||
|
mod solver;
|
||||||
|
|
||||||
#[derive(ValueEnum, Clone)]
|
#[derive(ValueEnum, Clone)]
|
||||||
enum Method {
|
enum Method {
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,6 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn backward(&self, i: Index) -> &L {
|
pub fn backward(&self, i: Index) -> &L {
|
||||||
&self.backward[(i - 1) as usize]
|
&self.backward[(i.abs() - 1) as usize]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,24 @@ impl NormalForm {
|
||||||
pub fn add_clause(&mut self, s: &[Index]) {
|
pub fn add_clause(&mut self, s: &[Index]) {
|
||||||
let len = s.len();
|
let len = s.len();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut i = 0;
|
||||||
|
let mut j = s.len() - 1;
|
||||||
|
while s[i] < 0 && s[j] > 0 {
|
||||||
|
match (-s[i]).cmp(&s[j]) {
|
||||||
|
std::cmp::Ordering::Less => {
|
||||||
|
j -= 1;
|
||||||
|
}
|
||||||
|
std::cmp::Ordering::Equal => {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::cmp::Ordering::Greater => {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if self.data.len() <= len {
|
if self.data.len() <= len {
|
||||||
self.data.resize(len + 1, Vec::new());
|
self.data.resize(len + 1, Vec::new());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
76
src/solver.rs
Normal file
76
src/solver.rs
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
use std::{collections::HashMap, marker::PhantomData, ops::Neg};
|
||||||
|
|
||||||
|
use crate::{dpll::Index, expr::Expr, mapping::Mapping, normal_form::NormalForm};
|
||||||
|
|
||||||
|
pub fn normal_form_solver<
|
||||||
|
L: Clone + Eq + std::hash::Hash,
|
||||||
|
F: Fn(&NormalForm) -> Option<Vec<Index>>,
|
||||||
|
>(
|
||||||
|
f: F,
|
||||||
|
expr: Expr<L>,
|
||||||
|
) -> Option<HashMap<L, bool>> {
|
||||||
|
let (cnf, map) = expr.cnf_normal_form();
|
||||||
|
|
||||||
|
normal_form_solver_from_cnf(f, &cnf, &map)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn normal_form_solver_from_cnf<
|
||||||
|
L: Clone + Eq + std::hash::Hash,
|
||||||
|
F: Fn(&NormalForm) -> Option<Vec<Index>>,
|
||||||
|
>(
|
||||||
|
f: F,
|
||||||
|
cnf: &NormalForm,
|
||||||
|
mapping: &Mapping<L>,
|
||||||
|
) -> Option<HashMap<L, bool>> {
|
||||||
|
let s = f(cnf)?;
|
||||||
|
|
||||||
|
let mut solution = HashMap::new();
|
||||||
|
|
||||||
|
for l in s {
|
||||||
|
if l < 0 {
|
||||||
|
solution.insert(mapping.backward(l).clone(), true);
|
||||||
|
} else {
|
||||||
|
solution.insert(mapping.backward(l).clone(), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(solution)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SolutionIterator<F, L> {
|
||||||
|
f: F,
|
||||||
|
cnf: NormalForm,
|
||||||
|
mapping: Mapping<L>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F, L: Clone + Eq + std::hash::Hash> SolutionIterator<F, L> {
|
||||||
|
pub fn new(f: F, expr: Expr<L>) -> Self {
|
||||||
|
let (cnf, mapping) = expr.cnf_normal_form();
|
||||||
|
Self { f, cnf, mapping }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: Fn(&NormalForm) -> Option<Vec<Index>>, L: Clone + Eq + std::hash::Hash> Iterator
|
||||||
|
for SolutionIterator<F, L>
|
||||||
|
{
|
||||||
|
type Item = HashMap<L, bool>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let s = (self.f)(&self.cnf)?;
|
||||||
|
|
||||||
|
let inverted: Vec<_> = s.iter().copied().map(Index::neg).collect();
|
||||||
|
self.cnf.add_clause(&inverted);
|
||||||
|
|
||||||
|
let mut solution = HashMap::new();
|
||||||
|
|
||||||
|
for l in s {
|
||||||
|
if l < 0 {
|
||||||
|
solution.insert(self.mapping.backward(l).clone(), true);
|
||||||
|
} else {
|
||||||
|
solution.insert(self.mapping.backward(l).clone(), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(solution)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue