|
@@ -114,6 +114,8 @@ impl error::Error for GameLoadError {}
|
|
|
#[derive(Debug, Clone)]
|
|
|
pub struct AnyBoard {
|
|
|
pub size: u8,
|
|
|
+ pub width: u8,
|
|
|
+ pub max_index: usize,
|
|
|
pub board: Vec<u8>,
|
|
|
}
|
|
|
|
|
@@ -126,11 +128,14 @@ impl AnyBoard {
|
|
|
let n = board_size as usize;
|
|
|
let s = AnyBoard {
|
|
|
size: board_size,
|
|
|
+ width: board_size*board_size,
|
|
|
+ max_index: n*n*n*n,
|
|
|
board: vec![0; n * n * n * n],
|
|
|
};
|
|
|
s
|
|
|
}
|
|
|
|
|
|
+ /*
|
|
|
/// Max board width (size*size)
|
|
|
pub fn width(&self) -> u8 {
|
|
|
self.size * self.size
|
|
@@ -140,6 +145,7 @@ impl AnyBoard {
|
|
|
pub fn max_index(&self) -> usize {
|
|
|
self.width() as usize * self.width() as usize
|
|
|
}
|
|
|
+ */
|
|
|
|
|
|
/// Clear out the board
|
|
|
pub fn clear(&mut self) {
|
|
@@ -148,12 +154,12 @@ impl AnyBoard {
|
|
|
|
|
|
/// Calculate index position of (x,y)
|
|
|
pub fn pos(&self, x: u8, y: u8) -> usize {
|
|
|
- x as usize + y as usize * self.width() as usize
|
|
|
+ x as usize + y as usize * self.width as usize
|
|
|
}
|
|
|
|
|
|
/// Set a position in the board with a value
|
|
|
pub fn set(&mut self, x: u8, y: u8, value: u8) {
|
|
|
- assert!(value <= self.size * self.size);
|
|
|
+ assert!(value <= self.width);
|
|
|
let index = self.pos(x, y);
|
|
|
assert!(index <= self.board.capacity());
|
|
|
self.board[index] = value;
|
|
@@ -177,13 +183,13 @@ impl AnyBoard {
|
|
|
let mut x: u8 = 0;
|
|
|
let mut y: u8 = 0;
|
|
|
|
|
|
- if s.len() != self.max_index() {
|
|
|
+ if s.len() != self.max_index {
|
|
|
// self.size * self.size*self.size*self.size {
|
|
|
return Err(Box::new(GameLoadError {
|
|
|
message: format!(
|
|
|
"String exceeds ({}) expected length {}.",
|
|
|
s.len(),
|
|
|
- self.size * self.size
|
|
|
+ self.width
|
|
|
),
|
|
|
}));
|
|
|
}
|
|
@@ -191,20 +197,20 @@ impl AnyBoard {
|
|
|
for ch in s.chars() {
|
|
|
if ch != blank {
|
|
|
let value = (ch as u8 - start_ch as u8) + 1;
|
|
|
- if value == 0 || value > self.width() {
|
|
|
+ if value == 0 || value > self.width {
|
|
|
return Err(Box::new(GameLoadError {
|
|
|
message: format!(
|
|
|
"String symbol ({}) represents value {}, expecting 1 to {}.",
|
|
|
ch,
|
|
|
value,
|
|
|
- self.size * self.size
|
|
|
+ self.width
|
|
|
),
|
|
|
}));
|
|
|
}
|
|
|
self.set(x, y, value);
|
|
|
}
|
|
|
y += 1;
|
|
|
- if y >= self.size * self.size {
|
|
|
+ if y >= self.width {
|
|
|
y = 0;
|
|
|
x += 1;
|
|
|
}
|
|
@@ -215,12 +221,12 @@ impl AnyBoard {
|
|
|
/// Save puzzle to a string (top,left) going down.
|
|
|
pub fn save_to_tld(&self, start_ch: char, blank: char) -> String {
|
|
|
let mut result = String::new();
|
|
|
- result.reserve(self.max_index());
|
|
|
+ result.reserve(self.max_index);
|
|
|
let start_ch = (start_ch as u8 - 1) as char;
|
|
|
let mut x: u8 = 0;
|
|
|
let mut y: u8 = 0;
|
|
|
|
|
|
- for _i in 0..self.max_index() {
|
|
|
+ for _i in 0..self.max_index {
|
|
|
let value = self.get(x, y);
|
|
|
if value == 0 {
|
|
|
result.push(blank);
|
|
@@ -228,7 +234,7 @@ impl AnyBoard {
|
|
|
result.push((start_ch as u8 + value) as char);
|
|
|
}
|
|
|
y += 1;
|
|
|
- if y >= self.width() {
|
|
|
+ if y >= self.width {
|
|
|
y = 0;
|
|
|
x += 1;
|
|
|
}
|
|
@@ -236,9 +242,68 @@ impl AnyBoard {
|
|
|
result
|
|
|
}
|
|
|
|
|
|
+ /// Load puzzle from string (top,left) going right.
|
|
|
+ pub fn load_from_tlr(
|
|
|
+ &mut self,
|
|
|
+ start_ch: char,
|
|
|
+ blank: char,
|
|
|
+ s: &str,
|
|
|
+ ) -> Result<(), Box<dyn error::Error>> {
|
|
|
+ self.clear();
|
|
|
+
|
|
|
+ if s.len() != self.max_index {
|
|
|
+ // self.size * self.size*self.size*self.size {
|
|
|
+ return Err(Box::new(GameLoadError {
|
|
|
+ message: format!(
|
|
|
+ "String exceeds ({}) expected length {}.",
|
|
|
+ s.len(),
|
|
|
+ self.width
|
|
|
+ ),
|
|
|
+ }));
|
|
|
+ }
|
|
|
+
|
|
|
+ let mut i: usize = 0;
|
|
|
+
|
|
|
+ for ch in s.chars() {
|
|
|
+ if ch != blank {
|
|
|
+ let value = (ch as u8 - start_ch as u8) + 1;
|
|
|
+ if value == 0 || value > self.width {
|
|
|
+ return Err(Box::new(GameLoadError {
|
|
|
+ message: format!(
|
|
|
+ "String symbol ({}) represents value {}, expecting 1 to {}.",
|
|
|
+ ch,
|
|
|
+ value,
|
|
|
+ self.width
|
|
|
+ ),
|
|
|
+ }));
|
|
|
+ }
|
|
|
+ self.board[i] = value;
|
|
|
+ i += 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Ok(())
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Save puzzle to a string (top,left) going right.
|
|
|
+ pub fn save_to_tlr(&self, start_ch: char, blank: char) -> String {
|
|
|
+ let mut result = String::new();
|
|
|
+ result.reserve(self.max_index);
|
|
|
+ let start_ch = (start_ch as u8 - 1) as char;
|
|
|
+
|
|
|
+ for i in 0..self.max_index {
|
|
|
+ let value = self.board[i];
|
|
|
+ if value == 0 {
|
|
|
+ result.push(blank);
|
|
|
+ } else {
|
|
|
+ result.push((start_ch as u8 + value) as char);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ result
|
|
|
+ }
|
|
|
+
|
|
|
pub fn display(&self) {
|
|
|
let line = "═".repeat(self.size as usize);
|
|
|
- let alpha_display = self.width() >= 10;
|
|
|
+ let alpha_display = self.width >= 10;
|
|
|
// println!("╔{}╦{}╦{}╗", line, line, line);
|
|
|
|
|
|
println!("alpha = {}", alpha_display);
|
|
@@ -253,9 +318,9 @@ impl AnyBoard {
|
|
|
}
|
|
|
println!("╗");
|
|
|
|
|
|
- for y in 0..self.width() {
|
|
|
+ for y in 0..self.width {
|
|
|
print!("║");
|
|
|
- for x in 0..self.width() {
|
|
|
+ for x in 0..self.width {
|
|
|
let value = self.get(x, y);
|
|
|
if value == 0 {
|
|
|
print!(" ");
|
|
@@ -272,7 +337,7 @@ impl AnyBoard {
|
|
|
}
|
|
|
println!("");
|
|
|
if y % self.size == self.size - 1 {
|
|
|
- if y + 1 == self.width() {
|
|
|
+ if y + 1 == self.width {
|
|
|
// Bottom
|
|
|
for i in 0..self.size {
|
|
|
if i == 0 {
|
|
@@ -299,6 +364,20 @@ impl AnyBoard {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ /// Is the puzzle completed?
|
|
|
+ /// Have all of the locations been filled with a value?
|
|
|
+ /// - This does not validate that it is a correct puzzle.
|
|
|
+ /// It doesn't check for duplicate digits (for example).
|
|
|
+ pub fn complete(&self) -> bool {
|
|
|
+ for i in 0..self.max_index {
|
|
|
+ if self.board[i] == 0 {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ true
|
|
|
+ }
|
|
|
+
|
|
|
}
|
|
|
|
|
|
// Need to use u32, so 5*5=25, 25 bits can be accessed.
|
|
@@ -307,25 +386,35 @@ impl AnyBoard {
|
|
|
#[derive(Debug, Clone)]
|
|
|
pub struct AnyPossible {
|
|
|
pub size: u8,
|
|
|
+ pub width: u8,
|
|
|
+ pub max_index: usize,
|
|
|
pub possible: Vec<GenBits<u32>>,
|
|
|
}
|
|
|
|
|
|
impl AnyPossible {
|
|
|
pub fn new(board_size: u8) -> Self {
|
|
|
let mut initial = GenBits::<u32>(0);
|
|
|
- initial.set_bits(1..board_size);
|
|
|
+ let width = board_size * board_size;
|
|
|
+ initial.set_bits(1..width);
|
|
|
+
|
|
|
Self {
|
|
|
size: board_size,
|
|
|
- possible: vec![initial; board_size as usize * board_size as usize],
|
|
|
+ width: width,
|
|
|
+ max_index: width as usize * width as usize,
|
|
|
+ possible: vec![initial; width as usize * width as usize],
|
|
|
}
|
|
|
}
|
|
|
|
|
|
pub fn clear(&mut self) {
|
|
|
let mut initial = GenBits::<u32>(0);
|
|
|
- initial.set_bits(1..self.size);
|
|
|
- self.possible = vec![initial; self.size as usize * self.size as usize];
|
|
|
+ // let width = self.size * self.size;
|
|
|
+ initial.set_bits(1..self.width);
|
|
|
+
|
|
|
+ self.possible = vec![initial; self.max_index];
|
|
|
+ // width as usize * width as usize];
|
|
|
}
|
|
|
|
|
|
+ #[inline]
|
|
|
pub fn set(&mut self, index: usize, value: usize, state: bool) {
|
|
|
self.possible[index].set(value, state);
|
|
|
}
|
|
@@ -341,7 +430,7 @@ impl AnySolver {
|
|
|
pub fn new(initial_board: &AnyBoard) -> Self {
|
|
|
let mut s = AnySolver {
|
|
|
board: initial_board.clone(),
|
|
|
- possible: AnyPossible::new(initial_board.max_index() as u8),
|
|
|
+ possible: AnyPossible::new(initial_board.size),
|
|
|
};
|
|
|
s.reset_possible();
|
|
|
s
|
|
@@ -372,8 +461,8 @@ impl AnySolver {
|
|
|
/// - When something has changed, and the possibles are out of sync.
|
|
|
pub fn reset_possible(&mut self) {
|
|
|
self.possible.clear();
|
|
|
- for y in 0..self.board.width() {
|
|
|
- for x in 0..self.board.width() {
|
|
|
+ for y in 0..self.board.width {
|
|
|
+ for x in 0..self.board.width {
|
|
|
let value = self.board.get(x, y);
|
|
|
if value != 0 {
|
|
|
self.process_move(x, y, value);
|
|
@@ -381,6 +470,15 @@ impl AnySolver {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ /// set (x,y) to value.
|
|
|
+ /// - This updates the board.
|
|
|
+ /// - This updates all the possibles (row,column,cell).
|
|
|
+ /// - Clears the possible for (x,y) [See process_move].
|
|
|
+ pub fn set(&mut self, x: u8, y: u8, value: u8) {
|
|
|
+ self.board.set(x, y, value);
|
|
|
+ self.process_move(x, y, value);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
#[cfg(test)]
|