use super::{
helpers::{collect_labels, create_bound_string},
stringify_variables,
variables::{Variable, Variables},
Puzzle,
};
use crate::utils::{get_n, Tile};
pub fn each_tile_used_once_bound(vars: &Variables) -> Vec<String> {
let mut prob_bounds = Vec::new();
for variables in vars.by_tile.values() {
let first_var = variables.get(0).unwrap();
let mut remapped_vars = collect_labels(variables);
if first_var.tile.0 != first_var.tile.1 {
let rotated_tile = (first_var.tile.1, first_var.tile.0);
let empty_vec = vec![];
let unwrapped_rotated_vars = vars.by_tile.get(&rotated_tile).unwrap_or(&empty_vec);
remapped_vars.extend(collect_labels(unwrapped_rotated_vars));
}
if !remapped_vars.is_empty() {
let bound = create_bound_string(remapped_vars);
prob_bounds.push(bound);
}
}
prob_bounds.push(String::new()); prob_bounds
}
pub fn each_position_filled_bound(vars: &Variables) -> Vec<String> {
let mut prob_bounds = Vec::new();
for variables in vars.by_position.values() {
let labels = collect_labels(variables);
let bound = create_bound_string(labels);
prob_bounds.push(bound);
}
prob_bounds.push(String::new()); prob_bounds
}
pub fn next_adjacent_bound(puzzle: &Puzzle, vars: &Variables) -> Vec<String> {
let mut prob_bounds = Vec::new();
let n = get_n(puzzle).expect("Puzzle does not have a valid length") as usize;
for position in 0..puzzle.0.len() {
if puzzle.0[position].is_some() {
continue;
}
if let Some(tile) = puzzle.0[(position + 1) % puzzle.0.len()] {
if let Some(bound) = next_enforced_bound(vars, tile, position) {
prob_bounds.push(bound);
}
} else {
for number in 0..=n {
if let Some(bound) = next_bound(puzzle, vars, position, number) {
prob_bounds.push(bound);
}
}
}
}
prob_bounds.push(String::new()); prob_bounds
}
fn next_enforced_bound(vars: &Variables, tile: Tile, position: usize) -> Option<String> {
let number = tile.0 as usize;
let condition = |var: &Variable| var.tile.1 == number;
let left_member_variables: Vec<String> =
variables_at_position_with_condition(vars, position, condition);
if left_member_variables.is_empty() {
return None;
}
let bound = format!("{} = 1", stringify_variables!(left_member_variables, " + "));
Some(bound)
}
fn next_bound(puzzle: &Puzzle, vars: &Variables, position: usize, number: usize) -> Option<String> {
let condition = |var: &Variable| var.tile.1 == number;
let left_member_variables: Vec<String> =
variables_at_position_with_condition(vars, position, condition);
let next_position = (position + 1) % puzzle.0.len();
let condition = |var: &Variable| var.tile.0 == number;
let right_member_variables: Vec<String> =
variables_at_position_with_condition(vars, next_position, condition);
if left_member_variables.is_empty() {
return None;
}
let bound = format!(
"{} - {} = 0",
stringify_variables!(left_member_variables, " + "),
stringify_variables!(right_member_variables, " - ")
);
Some(bound)
}
fn variables_at_position_with_condition(
vars: &Variables,
position: usize,
condition: impl Fn(&Variable) -> bool,
) -> Vec<String> {
vars.clone()
.by_position
.get(&position)
.unwrap_or(&vec![])
.clone()
.into_iter()
.filter(|var| condition(var))
.map(|var| var.label)
.collect()
}
pub fn partial_tiles_bound(puzzle: &Puzzle, vars: &Variables) -> Vec<String> {
let mut prob_bounds = Vec::new();
for (index, tile) in puzzle.0.iter().enumerate() {
if let Some(tile) = tile {
let variables = vars
.by_tile.get(&(tile.0 as usize, tile.1 as usize));
if let Some(variables) = variables {
let first = variables.iter().filter(|variable| variable.position == index)
.next();
if let Some(tile) = first {
let label = tile.label.clone();
prob_bounds.push(format!("{} = 1", label));
}
}
}
}
prob_bounds
}