|
@@ -38,15 +38,58 @@ impl error::Error for GameLoadError {}
|
|
|
// const DEBUG_OUTPUT: bool = false;
|
|
|
|
|
|
// Vec doesn't implement Copy ...
|
|
|
+
|
|
|
+/// AnyBoard - Any sized sudoku board storage
|
|
|
+/// Currently board limited to < 8x8 boards because of u8 limit. (8x8 say WAT?!)
|
|
|
+/// Also limited on display (we handle 5X5 boards A-Y). Anything > 5, we would have
|
|
|
+/// figure out something else for display output.
|
|
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
|
|
pub struct AnyBoard {
|
|
|
+ /// Size of board (cell width)
|
|
|
pub size: u8,
|
|
|
+ /// Size of board (size*size())
|
|
|
pub width: u8,
|
|
|
+ /// Max index size (width*width)
|
|
|
pub max_index: usize,
|
|
|
+ /// Actual board data
|
|
|
+ /// 0 = blank (not 1!)
|
|
|
+ /// 1..=width+1 = values
|
|
|
pub board: Vec<u8>,
|
|
|
}
|
|
|
|
|
|
+use std::ops::{Index, IndexMut};
|
|
|
+
|
|
|
+impl Index<usize> for AnyBoard {
|
|
|
+ type Output = u8;
|
|
|
+
|
|
|
+ fn index<'a>(&'a self, i: usize) -> &'a u8 {
|
|
|
+ &self.board[i]
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl Index<(u8, u8)> for AnyBoard {
|
|
|
+ type Output = u8;
|
|
|
+
|
|
|
+ fn index<'a>(&'a self, i: (u8, u8)) -> &'a u8 {
|
|
|
+ &self.board[self.pos(i.0, i.1)]
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl IndexMut<usize> for AnyBoard {
|
|
|
+ fn index_mut<'a>(&'a mut self, i: usize) -> &'a mut u8 {
|
|
|
+ &mut self.board[i]
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl IndexMut<(u8, u8)> for AnyBoard {
|
|
|
+ fn index_mut<'a>(&'a mut self, i: (u8, u8)) -> &'a mut u8 {
|
|
|
+ let idx = self.pos(i.0, i.1);
|
|
|
+ &mut self.board[idx]
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
impl AnyBoard {
|
|
|
+ /// Construct board of given size.
|
|
|
pub fn new(board_size: u8) -> Self {
|
|
|
if (board_size < 3) || (board_size > 5) {
|
|
|
panic!("Board size must be 3-5.");
|
|
@@ -67,8 +110,10 @@ impl AnyBoard {
|
|
|
self.board.fill(0);
|
|
|
}
|
|
|
|
|
|
+ /// Copy from another board.
|
|
|
+ /// Panics if board sizes don't match.
|
|
|
pub fn copy(&mut self, copy_from: &Self) {
|
|
|
- debug_assert!(
|
|
|
+ assert!(
|
|
|
self.size == copy_from.size,
|
|
|
"Can't copy size {} into size {}",
|
|
|
copy_from.size,
|
|
@@ -82,6 +127,7 @@ impl AnyBoard {
|
|
|
#[inline]
|
|
|
#[must_use]
|
|
|
/// Calculate index position of (x,y)
|
|
|
+ /// debug_assert if x or y values are out of range.
|
|
|
pub fn pos(&self, x: u8, y: u8) -> usize {
|
|
|
debug_assert!(
|
|
|
x < self.width && y < self.width,
|
|
@@ -104,6 +150,7 @@ impl AnyBoard {
|
|
|
}
|
|
|
|
|
|
/// Set a position in the board with a value
|
|
|
+ /// debug_assert if x or y values are out of range.
|
|
|
pub fn set(&mut self, x: u8, y: u8, value: u8) {
|
|
|
debug_assert!(
|
|
|
x < self.width && y < self.width,
|
|
@@ -127,6 +174,7 @@ impl AnyBoard {
|
|
|
|
|
|
#[must_use]
|
|
|
/// Get value at position (x,y)
|
|
|
+ /// debug_assert if x or y values are out of range.
|
|
|
pub fn get(&self, x: u8, y: u8) -> u8 {
|
|
|
debug_assert!(
|
|
|
x < self.width && y < self.width,
|
|
@@ -155,6 +203,7 @@ impl AnyBoard {
|
|
|
|
|
|
#[must_use]
|
|
|
/// Load puzzle from string (top,left) going down.
|
|
|
+ /// String is in columns, row format, (not row, column).
|
|
|
pub fn load_from_tld(
|
|
|
&mut self,
|
|
|
start_ch: char,
|
|
@@ -200,6 +249,7 @@ impl AnyBoard {
|
|
|
|
|
|
#[must_use]
|
|
|
/// Save puzzle to a string (top,left) going down.
|
|
|
+ /// We store as row, column, output is column, row.
|
|
|
pub fn save_to_tld(&self, start_ch: char, blank: char) -> String {
|
|
|
let mut result = String::new();
|
|
|
result.reserve(self.max_index);
|
|
@@ -225,6 +275,7 @@ impl AnyBoard {
|
|
|
|
|
|
#[must_use]
|
|
|
/// Load puzzle from string (top,left) going right.
|
|
|
+ /// Column, row translated to row, column.
|
|
|
pub fn load_from_tlr(
|
|
|
&mut self,
|
|
|
start_ch: char,
|
|
@@ -390,7 +441,7 @@ impl AnyBoard {
|
|
|
/// Solve by brute force
|
|
|
/// - Returns up to max # of solutions.
|
|
|
/// - Returns total solutions found, and vector of boards (up to max).
|
|
|
- /// - Uses brute_force (recursive function) to solve.
|
|
|
+ /// - Uses brute_force (recursion) to solve.
|
|
|
pub fn brute_force_solver(&self, max: u16) -> (u16, Vec<AnyBoard>) {
|
|
|
let mut workset = self.clone();
|
|
|
let mut total_solutions: u16 = 0;
|
|
@@ -403,6 +454,7 @@ impl AnyBoard {
|
|
|
}
|
|
|
|
|
|
/// Recursive brute force solver.
|
|
|
+ /// Called by brute_force_solver.
|
|
|
fn brute_force(
|
|
|
&mut self,
|
|
|
total_solutions: &mut u16,
|
|
@@ -460,40 +512,111 @@ impl AnyBoard {
|
|
|
false
|
|
|
}
|
|
|
|
|
|
+ // Mess with your brain:
|
|
|
+ // The board manipulations below should be implemented without cloning the board.
|
|
|
+ // We should be able to just swap values. FUTURE: TODO: no allocations.
|
|
|
+
|
|
|
+ /// Invert the index
|
|
|
+ ///
|
|
|
+ /// Convert 0..width to width..=0
|
|
|
+ pub fn invert(&self, index: u8) -> u8 {
|
|
|
+ self.width - 1 - index
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ The flip process.
|
|
|
+ X values don't change or move.
|
|
|
+ Uppercase letter gets swapped with lower case.
|
|
|
+
|
|
|
+ ╔═══╦═══╦═══╗
|
|
|
+ ║Xab║dfi║lp ║
|
|
|
+ ║AXc║ehk║o ║
|
|
|
+ ║BCX║gjn║ ║
|
|
|
+ ╠═══╬═══╬═══╣
|
|
|
+ ║DEG║Xm ║ ║
|
|
|
+ ║FHJ║MX ║ ║
|
|
|
+ ║IKN║ X║ ║
|
|
|
+ ╠═══╬═══╬═══╣
|
|
|
+ ║LO ║ ║X ║
|
|
|
+ ║P ║ ║ X ║
|
|
|
+ ║ ║ ║ X║
|
|
|
+ ╚═══╩═══╩═══╝
|
|
|
+ */
|
|
|
+
|
|
|
/// Flip board X and Y
|
|
|
pub fn flip(&mut self) {
|
|
|
+ let (mut x1, mut x2, mut y1, mut y2): (u8, u8, u8, u8);
|
|
|
+ // (x1,y1) = lower case
|
|
|
+ // (x2,y2) = upper case
|
|
|
+
|
|
|
+ for idx in 0..self.width-1 {
|
|
|
+ // println!("idx: {idx}");
|
|
|
+ for pass in 0..2 {
|
|
|
+ x1 = idx;
|
|
|
+ x2 = idx;
|
|
|
+ y1 = idx;
|
|
|
+ y2 = idx;
|
|
|
+
|
|
|
+ if pass == 0 {
|
|
|
+ x1 += 1;
|
|
|
+ y2 += 1;
|
|
|
+ } else {
|
|
|
+ if idx == 0 {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ x1 += 1;
|
|
|
+ y1 -= 1;
|
|
|
+ x2 -= 1;
|
|
|
+ y2 += 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ // println!("Idx {idx} Pass {pass} Starting ({x1},{y1}), ({x2},{y2})");
|
|
|
+ for _swap in 0..self.width {
|
|
|
+ // println!("Swap ({x1},{y1}) <=> ({x2},{y2})");
|
|
|
+ (self[(x1,y1)], self[(x2,y2)]) = (self[(x2,y2)], self[(x1,y1)]);
|
|
|
+
|
|
|
+ if (y1 == 0) || (x2 == 0) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ x1 += 1;
|
|
|
+ y1 -= 1;
|
|
|
+ x2 -= 1;
|
|
|
+ y2 += 1;
|
|
|
+
|
|
|
+ if (x1 == self.width) || (y2 == self.width) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ // The memory allocation way of swapping.
|
|
|
let temp = self.clone();
|
|
|
for x in 0..self.width {
|
|
|
for y in 0..self.width {
|
|
|
self[(x, y)] = temp[(y, x)]
|
|
|
}
|
|
|
}
|
|
|
+ */
|
|
|
}
|
|
|
|
|
|
/// Flip board around X
|
|
|
pub fn flip_x(&mut self) {
|
|
|
- let mut temp = vec![0; self.width as usize];
|
|
|
-
|
|
|
for y in 0..self.width {
|
|
|
- for x in 0..self.width {
|
|
|
- temp[x as usize] = self[(x, y)];
|
|
|
- }
|
|
|
- for x in 0..self.width {
|
|
|
- self[(x, y)] = temp[self.width as usize - 1 - x as usize];
|
|
|
+ for x in 0..self.width / 2 {
|
|
|
+ let invert_x = self.invert(x);
|
|
|
+ (self[(x, y)], self[(invert_x, y)]) = (self[(invert_x, y)], self[(x, y)]);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// Flip board around Y
|
|
|
pub fn flip_y(&mut self) {
|
|
|
- let mut temp = vec![0; self.width as usize];
|
|
|
-
|
|
|
for x in 0..self.width {
|
|
|
- for y in 0..self.width {
|
|
|
- temp[y as usize] = self[(x, y)];
|
|
|
- }
|
|
|
- for y in 0..self.width {
|
|
|
- self[(x, y)] = temp[self.width as usize - 1 - y as usize];
|
|
|
+ for y in 0..self.width / 2 {
|
|
|
+ let invert_y = self.invert(y);
|
|
|
+ (self[(x, y)], self[(x, invert_y)]) = (self[(x, invert_y)], self[(x, y)]);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -531,49 +654,26 @@ impl AnyBoard {
|
|
|
// This allows you to index the board by (u8,u8) or usize.
|
|
|
//
|
|
|
|
|
|
-use std::ops::{Index, IndexMut};
|
|
|
-
|
|
|
-impl Index<usize> for AnyBoard {
|
|
|
- type Output = u8;
|
|
|
-
|
|
|
- fn index<'a>(&'a self, i: usize) -> &'a u8 {
|
|
|
- &self.board[i]
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-impl Index<(u8, u8)> for AnyBoard {
|
|
|
- type Output = u8;
|
|
|
-
|
|
|
- fn index<'a>(&'a self, i: (u8, u8)) -> &'a u8 {
|
|
|
- &self.board[self.pos(i.0, i.1)]
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-impl IndexMut<usize> for AnyBoard {
|
|
|
- fn index_mut<'a>(&'a mut self, i: usize) -> &'a mut u8 {
|
|
|
- &mut self.board[i]
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-impl IndexMut<(u8, u8)> for AnyBoard {
|
|
|
- fn index_mut<'a>(&'a mut self, i: (u8, u8)) -> &'a mut u8 {
|
|
|
- let idx = self.pos(i.0, i.1);
|
|
|
- &mut self.board[idx]
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
// Need to use u32, so 5*5=25, 25 bits can be accessed.
|
|
|
// u16 is fine for 3*3=9.
|
|
|
|
|
|
+/// AnyPossible - Track what values can possibly go in what positions.
|
|
|
#[derive(Debug, Clone)]
|
|
|
pub struct AnyPossible {
|
|
|
+ /// Board size
|
|
|
pub size: u8,
|
|
|
+ /// Board width (size*size)
|
|
|
pub width: u8,
|
|
|
+ /// Board max index (width*width)
|
|
|
pub max_index: usize,
|
|
|
+ /// What values are possible at what index position.
|
|
|
+ /// true = possible
|
|
|
+ /// Note: The flags values go from 0..width!
|
|
|
pub possible: Vec<Flags>,
|
|
|
}
|
|
|
|
|
|
impl AnyPossible {
|
|
|
+ /// Construct AnyPossible for given board_size
|
|
|
pub fn new(board_size: u8) -> Self {
|
|
|
let width = board_size as usize * board_size as usize;
|
|
|
let mut initial = Flags::new(width);
|
|
@@ -588,15 +688,17 @@ impl AnyPossible {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /// Reset Possible (all values, all positions)
|
|
|
pub fn clear(&mut self) {
|
|
|
let mut initial = Flags::new(self.width as usize);
|
|
|
- // let width = self.size * self.size;
|
|
|
initial.set_range(0..self.width as usize);
|
|
|
self.possible.fill(initial);
|
|
|
}
|
|
|
|
|
|
+ /// Copy possible
|
|
|
+ /// assert! if sizes don't match.
|
|
|
pub fn copy(&mut self, copy_from: &Self) {
|
|
|
- debug_assert!(
|
|
|
+ assert!(
|
|
|
self.size == copy_from.size,
|
|
|
"Can't copy size {} into size {}",
|
|
|
copy_from.size,
|
|
@@ -630,6 +732,7 @@ impl AnyPossible {
|
|
|
#[inline]
|
|
|
/// Get state for given index and value.
|
|
|
/// - value range is from 1..=width.
|
|
|
+ /// - debug_assert if index is out of range.
|
|
|
pub fn get(&self, index: usize, value: usize) -> bool {
|
|
|
debug_assert!(
|
|
|
index < self.max_index,
|
|
@@ -643,6 +746,7 @@ impl AnyPossible {
|
|
|
#[must_use]
|
|
|
#[inline]
|
|
|
/// Return index for given (x,y).
|
|
|
+ /// - debug_assert if x or y are out of range.
|
|
|
pub fn pos(&self, x: u8, y: u8) -> usize {
|
|
|
debug_assert!(
|
|
|
x < self.width && y < self.width,
|
|
@@ -657,6 +761,7 @@ impl AnyPossible {
|
|
|
#[must_use]
|
|
|
#[inline]
|
|
|
/// Return (x,y) position for given index.
|
|
|
+ /// - debug_assert if x or y are out of range.
|
|
|
pub fn xy(&self, idx: usize) -> (u8, u8) {
|
|
|
debug_assert!(idx < self.max_index, "Index {} >= {}", idx, self.max_index);
|
|
|
(
|
|
@@ -783,8 +888,6 @@ impl AnyPossible {
|
|
|
(pairs, possibles_updated)
|
|
|
}
|
|
|
|
|
|
- // This is working, it was code elsewhere that was having issues.
|
|
|
-
|
|
|
#[must_use]
|
|
|
/// Find positions where each value is possible.
|
|
|
/// - Vec index is the value - 1 !
|
|
@@ -852,29 +955,15 @@ impl IndexMut<(u8, u8)> for AnyPossible {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
-An idea I have for AnySolver is to store the AnyPossible as
|
|
|
-sections. row/column/cell.
|
|
|
-I think this would better allow me to judge how hard the puzzle
|
|
|
-is. If it can be solved by looking at just one section --
|
|
|
-that would be one level. If it takes 2, another level. All 3,
|
|
|
-yet another. I think that's how I mentally judge the puzzles.
|
|
|
-
|
|
|
-"Not all hard puzzles are hard."
|
|
|
-
|
|
|
- */
|
|
|
-
|
|
|
-/*
|
|
|
-pub struct Pairs {
|
|
|
- pub values: [u8; 2],
|
|
|
- pub pos: [u8; 2],
|
|
|
-}
|
|
|
-*/
|
|
|
-
|
|
|
+/// AnySolver - a Sudoku puzzle solver
|
|
|
+/// - This uses logic.
|
|
|
#[derive(Debug, Clone)]
|
|
|
pub struct AnySolver {
|
|
|
+ /// The board
|
|
|
pub board: AnyBoard,
|
|
|
+ /// Possibles
|
|
|
pub possible: AnyPossible,
|
|
|
+ /// Groups (Rows, Columns, Cells)
|
|
|
pub group: AnyGroup,
|
|
|
// Is this value set in the given cell?
|
|
|
pub cell_poss: Vec<Flags>,
|
|
@@ -919,10 +1008,17 @@ impl AnySolver {
|
|
|
/// Clear out board and possible.
|
|
|
/// - group is ok, unless they change the board size.
|
|
|
pub fn clear(&mut self) {
|
|
|
+ self.board.clear();
|
|
|
+ self.possible.clear();
|
|
|
+ self.reset_possible();
|
|
|
+ /*
|
|
|
+ // I should be able to reset here, and not have to re-allocate.
|
|
|
+
|
|
|
let board_size: u8 = self.board.size;
|
|
|
self.board = AnyBoard::new(board_size);
|
|
|
self.possible = AnyPossible::new(board_size);
|
|
|
self.reset_possible();
|
|
|
+ */
|
|
|
}
|
|
|
|
|
|
/// Process a move
|
|
@@ -946,6 +1042,7 @@ impl AnySolver {
|
|
|
self.board.width
|
|
|
);
|
|
|
|
|
|
+ // Remove value from the row, column, and cell possibles.
|
|
|
let mut g = self.group.row(y);
|
|
|
let val: usize = value as usize;
|
|
|
for g in g {
|
|
@@ -959,57 +1056,27 @@ impl AnySolver {
|
|
|
for g in g {
|
|
|
self.possible.set(*g, val, false);
|
|
|
}
|
|
|
+
|
|
|
let idx = self.possible.pos(x, y);
|
|
|
self.possible.possible[idx] = Flags::new(self.board.width as usize);
|
|
|
self.board.board[idx] = value;
|
|
|
|
|
|
- //
|
|
|
- // When working with GenBits, remember to use value -1
|
|
|
- //
|
|
|
-
|
|
|
- /*
|
|
|
- let ok = self.cell_poss[cell_index as usize].get(value as usize-1);
|
|
|
- if !ok {
|
|
|
- println!("{:?}", self.cell_poss);
|
|
|
- println!("Cell {} ({},{})={}", cell_index, x, y, value);
|
|
|
- }
|
|
|
- */
|
|
|
-
|
|
|
// Tests are run as debug, and this kills things...
|
|
|
// Like the invalid board in validated_board test.
|
|
|
let cell_index = self.group.which_cell(x, y);
|
|
|
|
|
|
- /*
|
|
|
- Never use this code. It asserts/panics on invalid boards.
|
|
|
- (Which is what validate_board checks...)
|
|
|
-
|
|
|
- debug_assert!(
|
|
|
- self.cell_poss[cell_index as usize].get(value as usize - 1),
|
|
|
- "Cell {} ({},{})={}",
|
|
|
- cell_index,
|
|
|
- x,
|
|
|
- y,
|
|
|
- value
|
|
|
- );
|
|
|
- */
|
|
|
self.cell_poss[cell_index as usize].set(value as usize - 1, false);
|
|
|
|
|
|
if finalize {
|
|
|
self.finalize_possible();
|
|
|
// self.finalize_cell(cell_index);
|
|
|
}
|
|
|
- // Should I do the checks here for the logic possible fix?
|
|
|
- // Or, should I make it a different function?
|
|
|
- // Put somewhere else, since it has to re-check the entire board!
|
|
|
-
|
|
|
- // OR, just check row and columns?
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
This is very heavy to call (especially for 5X5 boards).
|
|
|
Maybe call this when we're getting stuck? (before logic 2 pass?)
|
|
|
|
|
|
- Still to do: columns. :P
|
|
|
*/
|
|
|
|
|
|
pub fn finalize_possible(&mut self) -> bool {
|
|
@@ -1835,10 +1902,13 @@ impl AnySolver {
|
|
|
}
|
|
|
self.process_move(x, y, value, false);
|
|
|
} else {
|
|
|
+ // Below, we check to see if blanks have possible values.
|
|
|
+
|
|
|
has_blanks = true;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
// Ok, the pieces given fit correctly with the sudoku constraints.
|
|
|
self.finalize_possible();
|
|
|
// self.finalize_move();
|
|
@@ -1917,6 +1987,8 @@ impl AnySolver {
|
|
|
}
|
|
|
|
|
|
#[must_use]
|
|
|
+ /// get (x,y) value.
|
|
|
+ /// - debug_assert if x or y are out of range.
|
|
|
pub fn get(&self, x: u8, y: u8) -> u8 {
|
|
|
debug_assert!(
|
|
|
x < self.board.width && y < self.board.width,
|
|
@@ -1937,7 +2009,7 @@ impl AnySolver {
|
|
|
}
|
|
|
|
|
|
/// Recursively completely fill the board.
|
|
|
- /// This takes - quite a bit of time - for a 25x25 board.
|
|
|
+ /// This takes - some time (2.5 minutes) - for a 25x25 board.
|
|
|
fn fill_board(&mut self, rng: &mut ChaCha20Rng) -> bool {
|
|
|
let backup = self.clone();
|
|
|
let max_index = self.board.max_index;
|
|
@@ -2035,6 +2107,9 @@ impl AnySolver {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+ /// logic_pass1
|
|
|
+ /// Look for single values in the possibles.
|
|
|
+ /// (If there's only one value possible, it must go there.)
|
|
|
fn logic_pass1(&mut self) -> bool {
|
|
|
// Pass 1: Look for singles in the possible sets.
|
|
|
let mut pass1 = false;
|
|
@@ -3159,8 +3234,10 @@ mod tests {
|
|
|
// Reset the board, flip it, and try this again!
|
|
|
|
|
|
board.copy(&base_board);
|
|
|
+ board.display();
|
|
|
board.flip();
|
|
|
let strings = board.to_strings();
|
|
|
+ println!("board.flip() display:");
|
|
|
board.display();
|
|
|
/*
|
|
|
╔═══╦═══╦═══╗
|
|
@@ -3385,10 +3462,10 @@ mod tests {
|
|
|
board[(width, width)] = 3;
|
|
|
board[(0, width)] = 4;
|
|
|
// Also check these as well: ?
|
|
|
- board[(width/2, size-1)] = 1;
|
|
|
- board[(width+1-size, width/2)] = 2;
|
|
|
- board[(width/2, width+1-size)] = 3;
|
|
|
- board[(size-1, width/2)] = 4;
|
|
|
+ board[(width / 2, size - 1)] = 1;
|
|
|
+ board[(width + 1 - size, width / 2)] = 2;
|
|
|
+ board[(width / 2, width + 1 - size)] = 3;
|
|
|
+ board[(size - 1, width / 2)] = 4;
|
|
|
board.display();
|
|
|
/*
|
|
|
╔═══╦═══╦═══╗
|