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 + +
+ +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.
+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;
+}
+
+ 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;
+}
+
+
+ Reload that and… right div’s auto linebreak after them.
+I think we can “float: left” these bad boys and…
+
+
+ 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.
+
+
+ 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!
+
+
+ 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;
+}
+
+
+ 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!
+
+
+ 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!
+
+
+ Still some goofiness like the border on the outside being thinner + than the interiors but I’m pretty happy with this for now.
+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:
+
+
+ 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:
+
+
+