diff --git a/blog/2023/04/2023-04-10-sudoku.md b/blog/2023/04/2023-04-10-sudoku.md index 87be226..b399283 100644 --- a/blog/2023/04/2023-04-10-sudoku.md +++ b/blog/2023/04/2023-04-10-sudoku.md @@ -1,9 +1,8 @@ --- -title: Writing a Sudoku Solver +title: "Writing a Sudoku Solver: Displaying the Grid" +date: 2023-04-10 --- -# Writing a Sudoku Solver: Displaying the Grid - I previously dabbled with writing a [sudoku solver](https://gitlab.com/dgalbraith33/sudoku-solver) but got carried away early on by making crazy speed improvements rather than actually improving the solving ability. What I did enjoy about the project is making logical deductions rather than the diff --git a/blog/pandoc.html b/blog/pandoc.html new file mode 100644 index 0000000..c5b028e --- /dev/null +++ b/blog/pandoc.html @@ -0,0 +1,17 @@ + +$-- Run with the following command: +$-- pandoc -s --template=blog/pandoc.html -o public/blog/sudoku.html blog/2023-04-10-sudoku.md + + + + $title$ + + + +
+

$title$

+
Published $date$
+ $body$ +
+ + diff --git a/public/blog/2023/04/images/sudoku-1.png b/public/blog/2023/04/images/sudoku-1.png new file mode 100644 index 0000000..81ea383 Binary files /dev/null and b/public/blog/2023/04/images/sudoku-1.png differ diff --git a/public/blog/2023/04/images/sudoku-10.png b/public/blog/2023/04/images/sudoku-10.png new file mode 100644 index 0000000..f92110a Binary files /dev/null and b/public/blog/2023/04/images/sudoku-10.png differ diff --git a/public/blog/2023/04/images/sudoku-2.png b/public/blog/2023/04/images/sudoku-2.png new file mode 100644 index 0000000..df09597 Binary files /dev/null and b/public/blog/2023/04/images/sudoku-2.png differ diff --git a/public/blog/2023/04/images/sudoku-3.png b/public/blog/2023/04/images/sudoku-3.png new file mode 100644 index 0000000..7f4d02d Binary files /dev/null and b/public/blog/2023/04/images/sudoku-3.png differ diff --git a/public/blog/2023/04/images/sudoku-4.png b/public/blog/2023/04/images/sudoku-4.png new file mode 100644 index 0000000..85b7659 Binary files /dev/null and b/public/blog/2023/04/images/sudoku-4.png differ diff --git a/public/blog/2023/04/images/sudoku-5.png b/public/blog/2023/04/images/sudoku-5.png new file mode 100644 index 0000000..837dcba Binary files /dev/null and b/public/blog/2023/04/images/sudoku-5.png differ diff --git a/public/blog/2023/04/images/sudoku-6.png b/public/blog/2023/04/images/sudoku-6.png new file mode 100644 index 0000000..9fe2174 Binary files /dev/null and b/public/blog/2023/04/images/sudoku-6.png differ diff --git a/public/blog/2023/04/images/sudoku-7.png b/public/blog/2023/04/images/sudoku-7.png new file mode 100644 index 0000000..7fd59d2 Binary files /dev/null and b/public/blog/2023/04/images/sudoku-7.png differ diff --git a/public/blog/2023/04/images/sudoku-8.png b/public/blog/2023/04/images/sudoku-8.png new file mode 100644 index 0000000..c98a3b6 Binary files /dev/null and b/public/blog/2023/04/images/sudoku-8.png differ diff --git a/public/blog/2023/04/images/sudoku-9.png b/public/blog/2023/04/images/sudoku-9.png new file mode 100644 index 0000000..a67c63f Binary files /dev/null and b/public/blog/2023/04/images/sudoku-9.png differ diff --git a/public/blog/2023/04/sudoku.html b/public/blog/2023/04/sudoku.html new file mode 100644 index 0000000..b0418db --- /dev/null +++ b/public/blog/2023/04/sudoku.html @@ -0,0 +1,237 @@ + + + + + Writing a Sudoku Solver: Displaying the Grid + + + +
+

Writing a Sudoku Solver: Displaying the Grid

+
Published 2023-04-10
+

I previously dabbled with writing a sudoku + solver but got carried away early on by making crazy speed + improvements rather than actually improving the solving ability. + What I did enjoy about the project is making logical deductions + rather than the guess and backcheck method commonly employed.

+

I want to take another crack at doing this except this time focus + on the question “Given the current state what are any of the next + possible deductions” without focusing on speed. Eventually I’d also + like to take a crack at solving other types of sudokus (Chess + Sudoku, Killer Sudoku, etc).

+

One of the issues I had last time as I was debugging was + displaying the current board state in a clean way so I could see + what had gone wrong. So this time around I’m planning on writing a + quick HTML/javascript board state display to be able to visualize + the board. For now I don’t intend it to be interactive, however I’ll + likely add that in the future.

+

Creating a sudoku grid

+

I’m no front end dev so this may take some time. I literally just + want to create an outline for the puzzle with nothing in it.

+
<div class="container">
+  <h1>Sudoku</h1>
+  <div class="puzzle">nothing</div>
+</div>
+
.container {
+  max-width: 800px;
+  margin: auto;
+}
+
+.puzzle {
+  border: 1px solid black;
+}
+
+ border + +
+

I’m not joking when I say I’m going to have to do baby steps + here.

+

Next I’ll try to actually make a square grid. The best way to do + this is probably with flexbox or something but I’m just gonna hard + code some widths and heights.

+

Ok lets make this thing a width divisible by 9 so we can divide + it into equal portions. I chose 630 quite honestly because it was + the first number below 800px that popped into my head divisible by + 9.

+

Let’s focus on the 9 main boxes in the grid now before worrying + about the cells. I’ll make each box 210 pixels tall and wide. And + slap a big ole border on there. Let’s make it 2px because we will + want a narrower one for the cells.

+
.puzzle {
+  width: 630px;
+  height: 630px;
+}
+
+.box {
+  border: 2px solid black;
+  width: 210px;
+  height: 210px;
+}
+
+ boxes + +
+

Reload that and… right div’s auto linebreak after them.

+

I think we can “float: left” these bad boys and…

+
+ boxes2 + +
+

Right, now I’m pretty sure the boxes are 210 + 4 pixels wide + because the border isn’t included. While I’m tempted to just math my + way out of this I recall that you can specify the border-box sizing + to avoid this.

+

Now this works! Now the astute of you may have noticed that there + were 10 not 9 boxes in the screenshot with the 2 columns. That + became even more obvious in the full grid.

+
+ boxes3 + +
+

Ok now we can just recreate all of this with the cells and should + be good to go right?

+

Nope! The internal size of the boxes are now only 206x206 because + of the border-box attribute. But I now realize I can just get rid of + the puzzle sizing all together and go back to regular sizing on the + boxes. This happens to work because 4 214px boxes won’t fit in the + 800px wide container. (Again just use flexbox).

+

Finally this works!

+
+ grid + +
+

Displaying a puzzle

+

Next up is to actually get some numbers in this bad boy. Now this + is where I relent and use flexboxes because this + StackOverflow answer convinced me.

+
.cell {
+  ...
+
+  font-size: 40px;
+  font-weight: bold;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+ cell number + +
+

Taking the puzzle from the + URL

+

Now to make it so I can get the page to display any puzzle I want + easily from the solver, I’ll allow specifying it as a parameter in + the URL. For now in a row-major string of 81 characters using a + period to denote blank spaces.

+

I’ll can just get all of the cells by class name and iterate over + them in the same order as the puzzle string and it will display + relatively easily.

+
window.onload = (event) => {
+  var params = new URLSearchParams(window.location.search);
+  var puzzle = params.get("p");
+  if (puzzle === null) {
+    return;
+  }
+  if (puzzle.length != 81) {
+    console.log("Failure: puzzle url len is " + puzzle.length);
+    return;
+  }
+  var cells = document.getElementsByClassName("cell");
+  if (cells.length != 81) {
+    console.log("Failure: wrong number of cells: " + cells.length);
+    return;
+  }
+
+  for (i = 0; i < 81; i++) {
+    if (puzzle[i] != '.') {
+      cells[i].innerText = puzzle[i]
+    }
+  }
+}
+

And hooray it works super well!

+
+ bad puzzles + +
+

Oh wait… I didn’t think about the fact that the elements in the + HTMLCollection from the document.getElementsByClassName call + wouldn’t be in the row order of the puzzle (all of the cells in box + 1 come first).

+

You can see the effect of this as there are 2 9s in column 2 of + the puzzle. Oops.

+

I’m going to just do the old fashioned brute force way and give + each cell an id from 1-81 and insert those manually. I’m sure there + is a better way but hey this works.

+

Then with a quick update to the code.

+
for (i = 0; i < 81; i++) {
+  if (puzzle[i] != '.') {
+    document.getElementById(i+1).innerText = puzzle[i]
+  }
+}
+

It works!

+
+ good puzzle + +
+

Still some goofiness like the border on the outside being thinner + than the interiors but I’m pretty happy with this for now.

+

Showing pencil marks

+

Now to really visualize the solver’s state, we’ll also need to + see which pencil marks it has. These could be styled nicely but for + now I just went with a span inside the cell div with the following + style.

+
.cell > span {
+  font-size: 10px;
+  font-weight: normal;
+  text-align: center;
+  letter-spacing: 6px;
+  word-wrap: anywhere;
+}
+

The letter-spacing and wordwrap attributes let us just jam all of + the pencil marks in as a single string and let the browser break + them up into multiple lines for us.

+

This comes out quite nicely:

+
+ pencil marks + +
+

Reading the pencil + marks from the url

+

For this url trick we will add the pencil marks using a comma + separated array like so:

+

123,,,,456,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,

+

Using the string above we can use the following javascript code + to parse and insert them:

+
  var marks_param = params.get("m");
+  if (marks_param === null) {
+    return;
+  }
+  var marks = marks_param.split(",");
+
+  if (marks.length != 81) {
+    console.log("Failure: marks url len is " + marks.length);
+    return;
+  }
+
+  for (i = 0; i < 81; i++) {
+    if (marks[i].length > 0) {
+      var cell = document.getElementById(i+1);
+      if (cell.innerHTML.trim().length > 0) {
+        console.log("Pencil marks in cell with number: " + (i+1));
+      } else {
+        cell.innerHTML = "<span>" + marks[i] + "</span>";
+      }
+    }
+  }
+

Which also comes out nicely:

+
+ full puzzle + +
+
+ +