Moved site to hugo
|
@ -0,0 +1,5 @@
|
||||||
|
+++
|
||||||
|
title = '{{ replace .File.ContentBaseName "-" " " | title }}'
|
||||||
|
date = {{ .Date }}
|
||||||
|
draft = true
|
||||||
|
+++
|
|
@ -0,0 +1,22 @@
|
||||||
|
body {
|
||||||
|
color: #222;
|
||||||
|
font-family: sans-serif;
|
||||||
|
line-height: 1.5;
|
||||||
|
margin: 1rem;
|
||||||
|
max-width: 768px;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
border-bottom: 1px solid #222;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
border-top: 1px solid #222;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #00e;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
|
@ -16,7 +16,7 @@ img {
|
||||||
max-height: 500px;
|
max-height: 500px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.center-img {
|
.center-img img {
|
||||||
display: block;
|
display: block;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
|
@ -1,17 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
$-- Run with the following command:
|
|
||||||
$-- pandoc -s --template=blog/pandoc.html -o public/blog/sudoku.html blog/2023-04-10-sudoku.md
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<title>$title$</title>
|
|
||||||
<link rel="stylesheet" href="/css/styles.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container">
|
|
||||||
<h1 class="page-title">$title$</h1>
|
|
||||||
<div class="date">Published $date$</div>
|
|
||||||
$body$
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 136 KiB After Width: | Height: | Size: 136 KiB |
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
@ -1,6 +1,7 @@
|
||||||
---
|
---
|
||||||
title: "Writing a Sudoku Solver: Displaying the Grid"
|
title: "Writing a Sudoku Solver: Displaying the Grid"
|
||||||
date: 2023-04-10
|
date: 2023-04-10
|
||||||
|
draft: true
|
||||||
---
|
---
|
||||||
|
|
||||||
I previously dabbled with writing a [sudoku solver](https://gitlab.com/dgalbraith33/sudoku-solver)
|
I previously dabbled with writing a [sudoku solver](https://gitlab.com/dgalbraith33/sudoku-solver)
|
||||||
|
@ -40,7 +41,8 @@ puzzle with nothing in it.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
![A box with nothing in it.](images/sudoku-1.png){.center-img}
|
{{< figure src="images/sudoku-1.png" alt="A box with nothing in it."
|
||||||
|
class="center-img" >}}
|
||||||
|
|
||||||
I'm not joking when I say I'm going to have to do baby steps here.
|
I'm not joking when I say I'm going to have to do baby steps here.
|
||||||
|
|
||||||
|
@ -67,33 +69,37 @@ want a narrower one for the cells.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
![](images/sudoku-2.png){.center-img}
|
{{< figure src="images/sudoku-2.png" alt="Boxes stacked vertically."
|
||||||
|
class="center-img" >}}
|
||||||
|
|
||||||
Reload that and... right div's auto linebreak after them.
|
Reload that and... right div's auto linebreak after them.
|
||||||
|
|
||||||
I think we can "float: left" these bad boys and...
|
I think we can "float: left" these bad boys and...
|
||||||
|
|
||||||
![](images/sudoku-3.png){.center-img}
|
{{< figure src="images/sudoku-3.png" alt="Boxes stacked vertically in pairs."
|
||||||
|
class="center-img" >}}
|
||||||
|
|
||||||
Right, now I'm pretty sure the boxes are 210 + 4 pixels wide because the border isn't included.
|
Right, now I'm pretty sure the boxes are 210 + 4 pixels wide because the border
|
||||||
While I'm tempted to just math my way out of this I recall that you can specify the border-box
|
isn't included. While I'm tempted to just math my way out of this I recall that
|
||||||
sizing to avoid this.
|
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
|
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.
|
screenshot with the 2 columns. That became even more obvious in the full grid.
|
||||||
|
|
||||||
![Off by one errors...](images/sudoku-4.png){.center-img}
|
{{< figure src="images/sudoku-4.png" alt="A 3x3 grid of boxes with one extra below it."
|
||||||
|
class="center-img" >}}
|
||||||
|
|
||||||
Ok now we can just recreate all of this with the cells and should be good to go right?
|
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
|
Nope! The internal size of the boxes are now only 206x206 because of the
|
||||||
I now realize I can just get rid of the puzzle sizing all together and go back to regular sizing on
|
border-box attribute. But I now realize I can just get rid of the puzzle sizing
|
||||||
the boxes. This happens to work because 4 214px boxes won't fit in the 800px wide container. (Again
|
all together and go back to regular sizing on the boxes. This happens to work
|
||||||
just use flexbox).
|
because 4 214px boxes won't fit in the 800px wide container. (Again just use
|
||||||
|
flexbox).
|
||||||
|
|
||||||
Finally this works!
|
Finally this works!
|
||||||
|
|
||||||
![We have a grid!](images/sudoku-5.png){.center-img}
|
{{< figure src="images/sudoku-5.png" alt="A 3x3 grid of boxes." class="center-img" >}}
|
||||||
|
|
||||||
## Displaying a puzzle
|
## Displaying a puzzle
|
||||||
|
|
||||||
|
@ -114,7 +120,7 @@ StackOverflow answer convinced me.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
![](images/sudoku-6.png){.center-img}
|
{{< figure src="images/sudoku-6.png" alt="A box with 9 cells in it." class="center-img" >}}
|
||||||
|
|
||||||
### Taking the puzzle from the URL
|
### Taking the puzzle from the URL
|
||||||
|
|
||||||
|
@ -153,7 +159,7 @@ window.onload = (event) => {
|
||||||
|
|
||||||
And hooray it works super well!
|
And hooray it works super well!
|
||||||
|
|
||||||
![This isn't quite right.](images/sudoku-7.png){.center-img}
|
{{< figure src="images/sudoku-7.png" alt="A full sudoku puzzle!" class="center-img" >}}
|
||||||
|
|
||||||
Oh wait... I didn't think about the fact that the elements in the HTMLCollection from the
|
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
|
document.getElementsByClassName call wouldn't be in the row order of the puzzle (all of the cells
|
||||||
|
@ -176,7 +182,7 @@ for (i = 0; i < 81; i++) {
|
||||||
|
|
||||||
It works!
|
It works!
|
||||||
|
|
||||||
![We have a puzzle!](images/sudoku-8.png){.center-img}
|
{{< figure src="images/sudoku-8.png" alt="A full sudoku puzzle for real this time!" class="center-img" >}}
|
||||||
|
|
||||||
Still some goofiness like the border on the outside being thinner than the interiors but I'm pretty
|
Still some goofiness like the border on the outside being thinner than the interiors but I'm pretty
|
||||||
happy with this for now.
|
happy with this for now.
|
||||||
|
@ -202,7 +208,8 @@ string and let the browser break them up into multiple lines for us.
|
||||||
|
|
||||||
This comes out quite nicely:
|
This comes out quite nicely:
|
||||||
|
|
||||||
![](images/sudoku-9.png){.center-img}
|
{{< figure src="images/sudoku-9.png" alt="The top boxes of a sudoku puzzle with pencil marks"
|
||||||
|
class="center-img" >}}
|
||||||
|
|
||||||
### Reading the pencil marks from the url
|
### Reading the pencil marks from the url
|
||||||
|
|
||||||
|
@ -238,4 +245,5 @@ Using the string above we can use the following javascript code to parse and ins
|
||||||
|
|
||||||
Which also comes out nicely:
|
Which also comes out nicely:
|
||||||
|
|
||||||
![](images/sudoku-10.png){.center-img}
|
{{< figure src="images/sudoku-10.png" alt="The top boxes of a sudoku puzzle with pencil marks"
|
||||||
|
class="center-img" >}}
|
|
@ -1,17 +1,21 @@
|
||||||
---
|
+++
|
||||||
title: "AcadiaOS 0.1.0"
|
title = 'Acadia 0.1.0'
|
||||||
date: 2023-12-06
|
date = 2023-12-06
|
||||||
---
|
draft = true
|
||||||
|
tags = ['osdev']
|
||||||
|
+++
|
||||||
|
|
||||||
For the last six months or so I've been periodically working on developing a
|
For the last six months or so I've been periodically working on developing a
|
||||||
hobby operating system. A couple weeks ago I decided that I should finally aim
|
hobby operating system. A couple weeks ago I decided that I should finally aim
|
||||||
to cut a "release." This very-early release doesn't include a bunch of user
|
to cut a "release." This very-early release doesn't include a bunch of user
|
||||||
functionality. Namely you can navigate a filesystem in a primitive manner and
|
functionality but does have a fair amount of kernel features.
|
||||||
|
|
||||||
|
Namely you can navigate a filesystem in a primitive manner and
|
||||||
execute binaries. The following image shows just about everything the OS can do.
|
execute binaries. The following image shows just about everything the OS can do.
|
||||||
(The black window is the OS running in QEMU and the larger gray window is debug
|
(The black window is the OS running in QEMU and the larger gray window is debug
|
||||||
output sent to COM1).
|
output sent to COM1).
|
||||||
|
|
||||||
![AcadiaOS in action](images/acadiaos-0.1.0.png)
|
![AcadiaOS in action](/images/blog/acadiaos-0.1.0.png)
|
||||||
|
|
||||||
While there isn't much to do as a user, there are a lot of building blocks there
|
While there isn't much to do as a user, there are a lot of building blocks there
|
||||||
that I spent the last 6 months learning about and working on.
|
that I spent the last 6 months learning about and working on.
|
|
@ -0,0 +1,4 @@
|
||||||
|
+++
|
||||||
|
title = 'Sudoku'
|
||||||
|
layout = 'sudoku'
|
||||||
|
+++
|
|
@ -0,0 +1,3 @@
|
||||||
|
baseURL = 'https://example.org/'
|
||||||
|
languageCode = 'en-us'
|
||||||
|
title = 'Tiramisu'
|
|
@ -0,0 +1,19 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="{{ or site.Language.LanguageCode site.Language.Lang }}" dir="{{ or site.Language.LanguageDirection `ltr` }}">
|
||||||
|
<head>
|
||||||
|
{{ partial "head.html" . }}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<header>
|
||||||
|
{{ partial "header.html" . }}
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
{{ block "main" . }}{{ end }}
|
||||||
|
</main>
|
||||||
|
<footer>
|
||||||
|
{{ partial "footer.html" . }}
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,7 @@
|
||||||
|
{{ define "main" }}
|
||||||
|
{{ .Content }}
|
||||||
|
{{ range site.RegularPages }}
|
||||||
|
<h2><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></h2>
|
||||||
|
{{ .Summary }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
|
@ -0,0 +1,8 @@
|
||||||
|
{{ define "main" }}
|
||||||
|
<h1>{{ .Title }}</h1>
|
||||||
|
{{ .Content }}
|
||||||
|
{{ range .Pages }}
|
||||||
|
<h2><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></h2>
|
||||||
|
{{ .Summary }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
|
@ -0,0 +1,10 @@
|
||||||
|
{{ define "main" }}
|
||||||
|
<h1>{{ .Title }}</h1>
|
||||||
|
|
||||||
|
{{ $dateMachine := .Date | time.Format "2006-01-02T15:04:05-07:00" }}
|
||||||
|
{{ $dateHuman := .Date | time.Format ":date_long" }}
|
||||||
|
<time datetime="{{ $dateMachine }}" class="date">{{ $dateHuman }}</time>
|
||||||
|
|
||||||
|
{{ .Content }}
|
||||||
|
{{ partial "terms.html" (dict "taxonomy" "tags" "page" .) }}
|
||||||
|
{{ end }}
|
|
@ -1,15 +1,4 @@
|
||||||
<!DOCTYPE html>
|
{{ define "main" }}
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Sudoku</title>
|
|
||||||
<link rel="stylesheet" href="css/styles.css">
|
|
||||||
<link rel="stylesheet" href="css/sudoku.css">
|
|
||||||
<script src="scripts/sudoku.js"></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container">
|
|
||||||
<h1>Sudoku</h1>
|
|
||||||
|
|
||||||
<div class="puzzle">
|
<div class="puzzle">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div id="1" class="cell">
|
<div id="1" class="cell">
|
||||||
|
@ -192,6 +181,4 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{{ end }}
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -0,0 +1 @@
|
||||||
|
<p>Copyright {{ now.Year }}. All rights reserved.</p>
|
|
@ -0,0 +1,5 @@
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width">
|
||||||
|
<title>{{ if .IsHome }}{{ site.Title }}{{ else }}{{ printf "%s | %s" .Title site.Title }}{{ end }}</title>
|
||||||
|
{{ partialCached "head/css.html" . }}
|
||||||
|
{{ partialCached "head/js.html" . }}
|
|
@ -0,0 +1,19 @@
|
||||||
|
{{- with resources.Get "css/styles.css" }}
|
||||||
|
{{- if eq hugo.Environment "development" }}
|
||||||
|
<link rel="stylesheet" href="{{ .RelPermalink }}">
|
||||||
|
{{- else }}
|
||||||
|
{{- with . | minify | fingerprint }}
|
||||||
|
<link rel="stylesheet" href="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous">
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- range .Resources.Match "css/*.css" }}
|
||||||
|
{{- if eq hugo.Environment "development" }}
|
||||||
|
<link rel="stylesheet" href="{{ .RelPermalink }}">
|
||||||
|
{{- else }}
|
||||||
|
{{- with . | minify | fingerprint }}
|
||||||
|
<link rel="stylesheet" href="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous">
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
|
@ -0,0 +1,25 @@
|
||||||
|
{{- with resources.Get "js/main.js" }}
|
||||||
|
{{- if eq hugo.Environment "development" }}
|
||||||
|
{{- with . | js.Build }}
|
||||||
|
<script src="{{ .RelPermalink }}"></script>
|
||||||
|
{{- end }}
|
||||||
|
{{- else }}
|
||||||
|
{{- $opts := dict "minify" true }}
|
||||||
|
{{- with . | js.Build $opts | fingerprint }}
|
||||||
|
<script src="{{ .RelPermalink }}" integrity="{{- .Data.Integrity }}" crossorigin="anonymous"></script>
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- range .Resources.Match "js/*.js" }}
|
||||||
|
{{- if eq hugo.Environment "development" }}
|
||||||
|
{{- with . | js.Build }}
|
||||||
|
<script src="{{ .RelPermalink }}"></script>
|
||||||
|
{{- end }}
|
||||||
|
{{- else }}
|
||||||
|
{{- $opts := dict "minify" true }}
|
||||||
|
{{- with . | js.Build $opts | fingerprint }}
|
||||||
|
<script src="{{ .RelPermalink }}" integrity="{{- .Data.Integrity }}" crossorigin="anonymous"></script>
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
|
@ -0,0 +1,2 @@
|
||||||
|
<h1>{{ site.Title }}</h1>
|
||||||
|
{{ partial "menu.html" (dict "menuID" "main" "page" .) }}
|
|
@ -0,0 +1,51 @@
|
||||||
|
{{- /*
|
||||||
|
Renders a menu for the given menu ID.
|
||||||
|
|
||||||
|
@context {page} page The current page.
|
||||||
|
@context {string} menuID The menu ID.
|
||||||
|
|
||||||
|
@example: {{ partial "menu.html" (dict "menuID" "main" "page" .) }}
|
||||||
|
*/}}
|
||||||
|
|
||||||
|
{{- $page := .page }}
|
||||||
|
{{- $menuID := .menuID }}
|
||||||
|
|
||||||
|
{{- with index site.Menus $menuID }}
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
{{- partial "inline/menu/walk.html" (dict "page" $page "menuEntries" .) }}
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- define "partials/inline/menu/walk.html" }}
|
||||||
|
{{- $page := .page }}
|
||||||
|
{{- range .menuEntries }}
|
||||||
|
{{- $attrs := dict "href" .URL }}
|
||||||
|
{{- if $page.IsMenuCurrent .Menu . }}
|
||||||
|
{{- $attrs = merge $attrs (dict "class" "active" "aria-current" "page") }}
|
||||||
|
{{- else if $page.HasMenuCurrent .Menu .}}
|
||||||
|
{{- $attrs = merge $attrs (dict "class" "ancestor" "aria-current" "true") }}
|
||||||
|
{{- end }}
|
||||||
|
{{- $name := .Name }}
|
||||||
|
{{- with .Identifier }}
|
||||||
|
{{- with T . }}
|
||||||
|
{{- $name = . }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
{{- range $k, $v := $attrs }}
|
||||||
|
{{- with $v }}
|
||||||
|
{{- printf " %s=%q" $k $v | safeHTMLAttr }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end -}}
|
||||||
|
>{{ $name }}</a>
|
||||||
|
{{- with .Children }}
|
||||||
|
<ul>
|
||||||
|
{{- partial "inline/menu/walk.html" (dict "page" $page "menuEntries" .) }}
|
||||||
|
</ul>
|
||||||
|
{{- end }}
|
||||||
|
</li>
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
|
@ -0,0 +1,23 @@
|
||||||
|
{{- /*
|
||||||
|
For a given taxonomy, renders a list of terms assigned to the page.
|
||||||
|
|
||||||
|
@context {page} page The current page.
|
||||||
|
@context {string} taxonomy The taxonony.
|
||||||
|
|
||||||
|
@example: {{ partial "terms.html" (dict "taxonomy" "tags" "page" .) }}
|
||||||
|
*/}}
|
||||||
|
|
||||||
|
{{- $page := .page }}
|
||||||
|
{{- $taxonomy := .taxonomy }}
|
||||||
|
|
||||||
|
{{- with $page.GetTerms $taxonomy }}
|
||||||
|
{{- $label := (index . 0).Parent.LinkTitle }}
|
||||||
|
<div>
|
||||||
|
<div>{{ $label }}:</div>
|
||||||
|
<ul>
|
||||||
|
{{- range . }}
|
||||||
|
<li><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></li>
|
||||||
|
{{- end }}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{{- end }}
|
Before Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 136 KiB |
Before Width: | Height: | Size: 7.4 KiB |
Before Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 18 KiB |
|
@ -1,235 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<title>Writing a Sudoku Solver: Displaying the Grid</title>
|
|
||||||
<link rel="stylesheet" href="/css/styles.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container">
|
|
||||||
<h1 class="page-title">Writing a Sudoku Solver: Displaying the
|
|
||||||
Grid</h1>
|
|
||||||
<div class="date">Published 2023-04-10</div>
|
|
||||||
<p>I previously dabbled with writing a <a
|
|
||||||
href="https://gitlab.com/dgalbraith33/sudoku-solver">sudoku
|
|
||||||
solver</a> 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.</p>
|
|
||||||
<p>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).</p>
|
|
||||||
<p>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.</p>
|
|
||||||
<h2 id="creating-a-sudoku-grid">Creating a sudoku grid</h2>
|
|
||||||
<p>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.</p>
|
|
||||||
<div class="sourceCode" id="cb1"><pre
|
|
||||||
class="sourceCode html"><code class="sourceCode html"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw"><div</span> <span class="er">class</span><span class="ot">=</span><span class="st">"container"</span><span class="kw">></span></span>
|
|
||||||
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a> <span class="kw"><h1></span>Sudoku<span class="kw"></h1></span></span>
|
|
||||||
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a> <span class="kw"><div</span> <span class="er">class</span><span class="ot">=</span><span class="st">"puzzle"</span><span class="kw">></span>nothing<span class="kw"></div></span></span>
|
|
||||||
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="kw"></div></span></span></code></pre></div>
|
|
||||||
<div class="sourceCode" id="cb2"><pre
|
|
||||||
class="sourceCode css"><code class="sourceCode css"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="fu">.container</span> {</span>
|
|
||||||
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">max-width</span>: <span class="dv">800</span><span class="dt">px</span><span class="op">;</span></span>
|
|
||||||
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">margin</span>: <span class="bu">auto</span><span class="op">;</span></span>
|
|
||||||
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>}</span>
|
|
||||||
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a></span>
|
|
||||||
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a><span class="fu">.puzzle</span> {</span>
|
|
||||||
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a> <span class="kw">border</span>: <span class="dv">1</span><span class="dt">px</span> <span class="dv">solid</span> <span class="cn">black</span><span class="op">;</span></span>
|
|
||||||
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
|
|
||||||
<figure>
|
|
||||||
<img src="images/sudoku-1.png" class="center-img"
|
|
||||||
alt="A box with nothing in it." />
|
|
||||||
<figcaption aria-hidden="true">A box with nothing in
|
|
||||||
it.</figcaption>
|
|
||||||
</figure>
|
|
||||||
<p>I’m not joking when I say I’m going to have to do baby steps
|
|
||||||
here.</p>
|
|
||||||
<p>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.</p>
|
|
||||||
<p>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.</p>
|
|
||||||
<p>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.</p>
|
|
||||||
<div class="sourceCode" id="cb3"><pre
|
|
||||||
class="sourceCode css"><code class="sourceCode css"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="fu">.puzzle</span> {</span>
|
|
||||||
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">width</span>: <span class="dv">630</span><span class="dt">px</span><span class="op">;</span></span>
|
|
||||||
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">height</span>: <span class="dv">630</span><span class="dt">px</span><span class="op">;</span></span>
|
|
||||||
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>}</span>
|
|
||||||
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a></span>
|
|
||||||
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a><span class="fu">.box</span> {</span>
|
|
||||||
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a> <span class="kw">border</span>: <span class="dv">2</span><span class="dt">px</span> <span class="dv">solid</span> <span class="cn">black</span><span class="op">;</span></span>
|
|
||||||
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">width</span>: <span class="dv">210</span><span class="dt">px</span><span class="op">;</span></span>
|
|
||||||
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a> <span class="kw">height</span>: <span class="dv">210</span><span class="dt">px</span><span class="op">;</span></span>
|
|
||||||
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
|
|
||||||
<p><img src="images/sudoku-2.png" class="center-img" /></p>
|
|
||||||
<p>Reload that and… right div’s auto linebreak after them.</p>
|
|
||||||
<p>I think we can “float: left” these bad boys and…</p>
|
|
||||||
<p><img src="images/sudoku-3.png" class="center-img" /></p>
|
|
||||||
<p>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.</p>
|
|
||||||
<p>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.</p>
|
|
||||||
<figure>
|
|
||||||
<img src="images/sudoku-4.png" class="center-img"
|
|
||||||
alt="Off by one errors…" />
|
|
||||||
<figcaption aria-hidden="true">Off by one errors…</figcaption>
|
|
||||||
</figure>
|
|
||||||
<p>Ok now we can just recreate all of this with the cells and should
|
|
||||||
be good to go right?</p>
|
|
||||||
<p>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).</p>
|
|
||||||
<p>Finally this works!</p>
|
|
||||||
<figure>
|
|
||||||
<img src="images/sudoku-5.png" class="center-img"
|
|
||||||
alt="We have a grid!" />
|
|
||||||
<figcaption aria-hidden="true">We have a grid!</figcaption>
|
|
||||||
</figure>
|
|
||||||
<h2 id="displaying-a-puzzle">Displaying a puzzle</h2>
|
|
||||||
<p>Next up is to actually get some numbers in this bad boy. Now this
|
|
||||||
is where I relent and use flexboxes because <a
|
|
||||||
href="https://stackoverflow.com/questions/2939914/how-do-i-vertically-align-text-in-a-div/13515693">this</a>
|
|
||||||
StackOverflow answer convinced me.</p>
|
|
||||||
<div class="sourceCode" id="cb4"><pre
|
|
||||||
class="sourceCode css"><code class="sourceCode css"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="fu">.cell</span> {</span>
|
|
||||||
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a> <span class="fu">...</span></span>
|
|
||||||
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a></span>
|
|
||||||
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a> <span class="kw">font-size</span>: <span class="dv">40</span><span class="dt">px</span><span class="op">;</span></span>
|
|
||||||
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">font-weight</span>: <span class="dv">bold</span><span class="op">;</span></span>
|
|
||||||
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a> <span class="kw">display</span>: <span class="dv">flex</span><span class="op">;</span></span>
|
|
||||||
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a> <span class="kw">align-items</span>: <span class="dv">center</span><span class="op">;</span></span>
|
|
||||||
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">justify-content</span>: <span class="dv">center</span><span class="op">;</span></span>
|
|
||||||
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
|
|
||||||
<p><img src="images/sudoku-6.png" class="center-img" /></p>
|
|
||||||
<h3 id="taking-the-puzzle-from-the-url">Taking the puzzle from the
|
|
||||||
URL</h3>
|
|
||||||
<p>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.</p>
|
|
||||||
<p>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.</p>
|
|
||||||
<div class="sourceCode" id="cb5"><pre
|
|
||||||
class="sourceCode js"><code class="sourceCode javascript"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="bu">window</span><span class="op">.</span><span class="at">onload</span> <span class="op">=</span> (<span class="bu">event</span>) <span class="kw">=></span> {</span>
|
|
||||||
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> params <span class="op">=</span> <span class="kw">new</span> <span class="fu">URLSearchParams</span>(<span class="bu">window</span><span class="op">.</span><span class="at">location</span><span class="op">.</span><span class="at">search</span>)<span class="op">;</span></span>
|
|
||||||
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> puzzle <span class="op">=</span> params<span class="op">.</span><span class="fu">get</span>(<span class="st">"p"</span>)<span class="op">;</span></span>
|
|
||||||
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> (puzzle <span class="op">===</span> <span class="kw">null</span>) {</span>
|
|
||||||
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span><span class="op">;</span></span>
|
|
||||||
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a> }</span>
|
|
||||||
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> (puzzle<span class="op">.</span><span class="at">length</span> <span class="op">!=</span> <span class="dv">81</span>) {</span>
|
|
||||||
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a> <span class="bu">console</span><span class="op">.</span><span class="fu">log</span>(<span class="st">"Failure: puzzle url len is "</span> <span class="op">+</span> puzzle<span class="op">.</span><span class="at">length</span>)<span class="op">;</span></span>
|
|
||||||
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span><span class="op">;</span></span>
|
|
||||||
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a> }</span>
|
|
||||||
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> cells <span class="op">=</span> <span class="bu">document</span><span class="op">.</span><span class="fu">getElementsByClassName</span>(<span class="st">"cell"</span>)<span class="op">;</span></span>
|
|
||||||
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> (cells<span class="op">.</span><span class="at">length</span> <span class="op">!=</span> <span class="dv">81</span>) {</span>
|
|
||||||
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a> <span class="bu">console</span><span class="op">.</span><span class="fu">log</span>(<span class="st">"Failure: wrong number of cells: "</span> <span class="op">+</span> cells<span class="op">.</span><span class="at">length</span>)<span class="op">;</span></span>
|
|
||||||
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span><span class="op">;</span></span>
|
|
||||||
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true" tabindex="-1"></a> }</span>
|
|
||||||
<span id="cb5-16"><a href="#cb5-16" aria-hidden="true" tabindex="-1"></a></span>
|
|
||||||
<span id="cb5-17"><a href="#cb5-17" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> (i <span class="op">=</span> <span class="dv">0</span><span class="op">;</span> i <span class="op"><</span> <span class="dv">81</span><span class="op">;</span> i<span class="op">++</span>) {</span>
|
|
||||||
<span id="cb5-18"><a href="#cb5-18" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> (puzzle[i] <span class="op">!=</span> <span class="st">'.'</span>) {</span>
|
|
||||||
<span id="cb5-19"><a href="#cb5-19" aria-hidden="true" tabindex="-1"></a> cells[i]<span class="op">.</span><span class="at">innerText</span> <span class="op">=</span> puzzle[i]</span>
|
|
||||||
<span id="cb5-20"><a href="#cb5-20" aria-hidden="true" tabindex="-1"></a> }</span>
|
|
||||||
<span id="cb5-21"><a href="#cb5-21" aria-hidden="true" tabindex="-1"></a> }</span>
|
|
||||||
<span id="cb5-22"><a href="#cb5-22" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
|
|
||||||
<p>And hooray it works super well!</p>
|
|
||||||
<figure>
|
|
||||||
<img src="images/sudoku-7.png" class="center-img"
|
|
||||||
alt="This isn’t quite right." />
|
|
||||||
<figcaption aria-hidden="true">This isn’t quite right.</figcaption>
|
|
||||||
</figure>
|
|
||||||
<p>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).</p>
|
|
||||||
<p>You can see the effect of this as there are 2 9s in column 2 of
|
|
||||||
the puzzle. Oops.</p>
|
|
||||||
<p>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.</p>
|
|
||||||
<p>Then with a quick update to the code.</p>
|
|
||||||
<div class="sourceCode" id="cb6"><pre
|
|
||||||
class="sourceCode js"><code class="sourceCode javascript"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> (i <span class="op">=</span> <span class="dv">0</span><span class="op">;</span> i <span class="op"><</span> <span class="dv">81</span><span class="op">;</span> i<span class="op">++</span>) {</span>
|
|
||||||
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> (puzzle[i] <span class="op">!=</span> <span class="st">'.'</span>) {</span>
|
|
||||||
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a> <span class="bu">document</span><span class="op">.</span><span class="fu">getElementById</span>(i<span class="op">+</span><span class="dv">1</span>)<span class="op">.</span><span class="at">innerText</span> <span class="op">=</span> puzzle[i]</span>
|
|
||||||
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a> }</span>
|
|
||||||
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
|
|
||||||
<p>It works!</p>
|
|
||||||
<figure>
|
|
||||||
<img src="images/sudoku-8.png" class="center-img"
|
|
||||||
alt="We have a puzzle!" />
|
|
||||||
<figcaption aria-hidden="true">We have a puzzle!</figcaption>
|
|
||||||
</figure>
|
|
||||||
<p>Still some goofiness like the border on the outside being thinner
|
|
||||||
than the interiors but I’m pretty happy with this for now.</p>
|
|
||||||
<h2 id="showing-pencil-marks">Showing pencil marks</h2>
|
|
||||||
<p>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.</p>
|
|
||||||
<div class="sourceCode" id="cb7"><pre
|
|
||||||
class="sourceCode css"><code class="sourceCode css"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="fu">.cell</span> <span class="op">></span> span {</span>
|
|
||||||
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">font-size</span>: <span class="dv">10</span><span class="dt">px</span><span class="op">;</span></span>
|
|
||||||
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">font-weight</span>: <span class="dv">normal</span><span class="op">;</span></span>
|
|
||||||
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a> <span class="kw">text-align</span>: <span class="dv">center</span><span class="op">;</span></span>
|
|
||||||
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">letter-spacing</span>: <span class="dv">6</span><span class="dt">px</span><span class="op">;</span></span>
|
|
||||||
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a> <span class="kw">word-wrap</span>: anywhere<span class="op">;</span></span>
|
|
||||||
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
|
|
||||||
<p>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.</p>
|
|
||||||
<p>This comes out quite nicely:</p>
|
|
||||||
<p><img src="images/sudoku-9.png" class="center-img" /></p>
|
|
||||||
<h3 id="reading-the-pencil-marks-from-the-url">Reading the pencil
|
|
||||||
marks from the url</h3>
|
|
||||||
<p>For this url trick we will add the pencil marks using a comma
|
|
||||||
separated array like so:</p>
|
|
||||||
<p>123,,,,456,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,</p>
|
|
||||||
<p>Using the string above we can use the following javascript code
|
|
||||||
to parse and insert them:</p>
|
|
||||||
<div class="sourceCode" id="cb8"><pre
|
|
||||||
class="sourceCode js"><code class="sourceCode javascript"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> marks_param <span class="op">=</span> params<span class="op">.</span><span class="fu">get</span>(<span class="st">"m"</span>)<span class="op">;</span></span>
|
|
||||||
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> (marks_param <span class="op">===</span> <span class="kw">null</span>) {</span>
|
|
||||||
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span><span class="op">;</span></span>
|
|
||||||
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a> }</span>
|
|
||||||
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> marks <span class="op">=</span> marks_param<span class="op">.</span><span class="fu">split</span>(<span class="st">","</span>)<span class="op">;</span></span>
|
|
||||||
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a></span>
|
|
||||||
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> (marks<span class="op">.</span><span class="at">length</span> <span class="op">!=</span> <span class="dv">81</span>) {</span>
|
|
||||||
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a> <span class="bu">console</span><span class="op">.</span><span class="fu">log</span>(<span class="st">"Failure: marks url len is "</span> <span class="op">+</span> marks<span class="op">.</span><span class="at">length</span>)<span class="op">;</span></span>
|
|
||||||
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span><span class="op">;</span></span>
|
|
||||||
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a> }</span>
|
|
||||||
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true" tabindex="-1"></a></span>
|
|
||||||
<span id="cb8-12"><a href="#cb8-12" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> (i <span class="op">=</span> <span class="dv">0</span><span class="op">;</span> i <span class="op"><</span> <span class="dv">81</span><span class="op">;</span> i<span class="op">++</span>) {</span>
|
|
||||||
<span id="cb8-13"><a href="#cb8-13" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> (marks[i]<span class="op">.</span><span class="at">length</span> <span class="op">></span> <span class="dv">0</span>) {</span>
|
|
||||||
<span id="cb8-14"><a href="#cb8-14" aria-hidden="true" tabindex="-1"></a> <span class="kw">var</span> cell <span class="op">=</span> <span class="bu">document</span><span class="op">.</span><span class="fu">getElementById</span>(i<span class="op">+</span><span class="dv">1</span>)<span class="op">;</span></span>
|
|
||||||
<span id="cb8-15"><a href="#cb8-15" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> (cell<span class="op">.</span><span class="at">innerHTML</span><span class="op">.</span><span class="fu">trim</span>()<span class="op">.</span><span class="at">length</span> <span class="op">></span> <span class="dv">0</span>) {</span>
|
|
||||||
<span id="cb8-16"><a href="#cb8-16" aria-hidden="true" tabindex="-1"></a> <span class="bu">console</span><span class="op">.</span><span class="fu">log</span>(<span class="st">"Pencil marks in cell with number: "</span> <span class="op">+</span> (i<span class="op">+</span><span class="dv">1</span>))<span class="op">;</span></span>
|
|
||||||
<span id="cb8-17"><a href="#cb8-17" aria-hidden="true" tabindex="-1"></a> } <span class="cf">else</span> {</span>
|
|
||||||
<span id="cb8-18"><a href="#cb8-18" aria-hidden="true" tabindex="-1"></a> cell<span class="op">.</span><span class="at">innerHTML</span> <span class="op">=</span> <span class="st">"<span>"</span> <span class="op">+</span> marks[i] <span class="op">+</span> <span class="st">"</span>"</span><span class="op">;</span></span>
|
|
||||||
<span id="cb8-19"><a href="#cb8-19" aria-hidden="true" tabindex="-1"></a> }</span>
|
|
||||||
<span id="cb8-20"><a href="#cb8-20" aria-hidden="true" tabindex="-1"></a> }</span>
|
|
||||||
<span id="cb8-21"><a href="#cb8-21" aria-hidden="true" tabindex="-1"></a> }</span></code></pre></div>
|
|
||||||
<p>Which also comes out nicely:</p>
|
|
||||||
<p><img src="images/sudoku-10.png" class="center-img" /></p>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,323 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<title>AcadiaOS 0.1.0</title>
|
|
||||||
<link rel="stylesheet" href="/css/styles.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container">
|
|
||||||
<h1 class="page-title">AcadiaOS 0.1.0</h1>
|
|
||||||
<div class="date">Published 2023-12-06</div>
|
|
||||||
<p>For the last six months or so I’ve been periodically working on
|
|
||||||
developing a hobby operating system. A couple weeks ago I decided
|
|
||||||
that I should finally aim to cut a “release.” This very-early
|
|
||||||
release doesn’t include a bunch of user functionality. Namely you
|
|
||||||
can navigate a filesystem in a primitive manner and execute
|
|
||||||
binaries. The following image shows just about everything the OS can
|
|
||||||
do. (The black window is the OS running in QEMU and the larger gray
|
|
||||||
window is debug output sent to COM1).</p>
|
|
||||||
<figure>
|
|
||||||
<img src="images/acadiaos-0.1.0.png" alt="AcadiaOS in action" />
|
|
||||||
<figcaption aria-hidden="true">AcadiaOS in action</figcaption>
|
|
||||||
</figure>
|
|
||||||
<p>While there isn’t much to do as a user, there are a lot of
|
|
||||||
building blocks there that I spent the last 6 months learning about
|
|
||||||
and working on.</p>
|
|
||||||
<h2 id="what-i-knew-going-into-this">What I knew going into
|
|
||||||
this</h2>
|
|
||||||
<p>Frankly, not a lot.</p>
|
|
||||||
<p>I took an OS class in college, but while it covered OS
|
|
||||||
fundamentals the projects were based on writing modules for the
|
|
||||||
Linux kernel rather than working on our own barebones kernel and OS.
|
|
||||||
So while I vaguely knew of how things like process scheduling,
|
|
||||||
interrupts, and memory management worked, I had no experience
|
|
||||||
getting down to the brass tacks of how to actually implement these
|
|
||||||
things.</p>
|
|
||||||
<p>I had over the previous couple years spent some time writing a
|
|
||||||
small kernel to start learning some of these things. However, since
|
|
||||||
I used it as a testing ground for learning with no real design goals
|
|
||||||
or long term plan, it was kind of a mess. I had gotten to user space
|
|
||||||
with some primitive syscalls but it was memory issues and page
|
|
||||||
faults galore. So I decided to “reboot” things earlier this
|
|
||||||
year.</p>
|
|
||||||
<h2 id="design-goals">Design Goals</h2>
|
|
||||||
<p>I decided I wanted to write a microkernel based OS because I
|
|
||||||
figured the more of my messy code I can move to user space the
|
|
||||||
better. And also because that’s what OS nerds do. I’m not too
|
|
||||||
concerned about the performance cost of extra syscalls because by
|
|
||||||
god this thing isn’t gonna be too performant anyways.</p>
|
|
||||||
<p>Additionally, I wanted to try to make the system
|
|
||||||
capability-based. Trying a new permission model was appealing to me
|
|
||||||
because I’ve always felt the unix style one was a bit clunky. After
|
|
||||||
spending some time reading about seL4 and digging into the Zircon
|
|
||||||
interface I had a (very) rough idea of how these systems worked. I
|
|
||||||
have no illusions that my OS will every be “secure” but I find the
|
|
||||||
model interesting.</p>
|
|
||||||
<h2 id="references-and-resources">References and Resources</h2>
|
|
||||||
<p>Over the course of this project I used a lot of resources, not
|
|
||||||
least of which the OSDev.org <a
|
|
||||||
href="https://wiki.osdev.org">wiki</a> and <a
|
|
||||||
href="https://forum.osdev.org">forums</a>. The resources provided
|
|
||||||
there were invaluable, but the biggest lesson I learned since my
|
|
||||||
first time around writing a kernel was to rely on specs more than
|
|
||||||
other’s code samples and tutorials.</p>
|
|
||||||
<p>For the low-level stuff I spent a lot of time digging through
|
|
||||||
Intel and AMD’s monstrous programming manuals. It was helpful to use
|
|
||||||
the wiki to learn for instance that using the “iret” instruction is
|
|
||||||
a good way to jump to user-space for the first time, but from there
|
|
||||||
using the programming manuals to understand exactly how that
|
|
||||||
instruction works rather than just copying code from somewhere. I
|
|
||||||
had a similar experience with initializing the GDT in 64 bit
|
|
||||||
software. There are a lot of random claims out there on exactly how
|
|
||||||
you have to set it up, so it was much more efficient to just go dig
|
|
||||||
through the AMD64 spec however dry it may be.</p>
|
|
||||||
<p>As I worked my way up the stack, I used the SATA and AHCI specs
|
|
||||||
as well. They pose the additional complication of splitting things
|
|
||||||
up across multiple specs so you have to go back and forth a lot in
|
|
||||||
non-obvious ways. Hey at least they don’t try to charge you
|
|
||||||
thousands of dollars to get the spec like PCI.</p>
|
|
||||||
<p>I also found that when you needed examples of how to do something
|
|
||||||
specific it can be far better to look at an existing operating
|
|
||||||
system’s approach to help contextualize a specification. Andreas
|
|
||||||
Kling’s SerenityOS was invaluable for this for some low level x86
|
|
||||||
things. I also referenced the Zircon microkernel to figure out how
|
|
||||||
to use C++ templates to downcast capability pointers to their
|
|
||||||
specific objects types without relying on RTTI (run time type
|
|
||||||
information).</p>
|
|
||||||
<h2 id="kernel-implementation-details">Kernel Implementation
|
|
||||||
Details</h2>
|
|
||||||
<p>Ok enough about high level information, ambitions, and goals.
|
|
||||||
Let’s discuss a little bit more about what the actual system can do
|
|
||||||
at this point. I named the kernel Zion because it is another place I
|
|
||||||
love and it is also kind of fun to think of the operating system as
|
|
||||||
everything from (A)cadia down to (Z)ion.</p>
|
|
||||||
<p>This section will frequently reference the source code which is
|
|
||||||
available on my self-hosted <a
|
|
||||||
href="https://gitea.tiramisu.one">gitea</a> or mirrored to <a
|
|
||||||
href="https://github.com/dgalbraith33/acadia">GitHub</a>.</p>
|
|
||||||
<h3 id="low-level-x86-64-stuff">Low-level x86-64 stuff</h3>
|
|
||||||
<p>Because I found setting up paging, the higher half kernel, and
|
|
||||||
getting to long mode to be a pain the first time around, I decided
|
|
||||||
to use the <a
|
|
||||||
href="https://github.com/limine-bootloader/limine">limine
|
|
||||||
bootloader</a> to start the kernel this time around instead of GRUB
|
|
||||||
so I could focus on slightly higher level things. I have ambitions
|
|
||||||
to make the kernel more bootloader-agnostic in the future but for
|
|
||||||
now it is tightly coupled to the limine protocol.</p>
|
|
||||||
<p>On top of the things mentioned above, we use the limine protocol
|
|
||||||
to:</p>
|
|
||||||
<ul>
|
|
||||||
<li>Get a map of physical memory.</li>
|
|
||||||
<li>Set up a higher-half direct map of memory.</li>
|
|
||||||
<li>Find the RDSP.</li>
|
|
||||||
<li>Get a VGA framebuffer from UEFI.</li>
|
|
||||||
<li>Load the 3 init programs that are needed to bootstrap the
|
|
||||||
VFS.</li>
|
|
||||||
</ul>
|
|
||||||
<p>Following boot we immediately initialize the global descriptor
|
|
||||||
table (GDT) and interrupt descriptor table (IDT). The
|
|
||||||
<strong>GDT</strong> is mostly irrelevant for x86-64, however it was
|
|
||||||
interesting trying to get it to work with the sysret function which
|
|
||||||
expects two copies of the user-space segment descriptors to allow
|
|
||||||
returing to 32bit code from a 64 bit OS. Right now the system
|
|
||||||
doesn’t support 32 bit code (and likely never will) so we just
|
|
||||||
duplicate the 64 bit code segment.</p>
|
|
||||||
<p>The <strong>IDT</strong> is fairly straightforward and barebones
|
|
||||||
for now. I slowly add more debugging information to faults as I run
|
|
||||||
into them and it is useful. One of the biggest improvements was
|
|
||||||
setting up a seperate kernel stack for Page Faults and General
|
|
||||||
Protection Faults. That way if I broke memory related to the current
|
|
||||||
stack frame I get useful debugging information rather than an
|
|
||||||
immediate triple fault. I also recently added some very sloppy stack
|
|
||||||
unwind code so I can more easily find the context that the fault
|
|
||||||
occurred in.</p>
|
|
||||||
<p>Finally we also initialize the <strong>APIC</strong> in a
|
|
||||||
rudimentary fashion. The timer is used to trigger scheduling events
|
|
||||||
and we map PCI and PS/2 Keyboard interrupts to appropriate vectors
|
|
||||||
in the IDT.</p>
|
|
||||||
<h3 id="memory-management">Memory management</h3>
|
|
||||||
<p>Memory management seems to be one of those areas where every time
|
|
||||||
I make progress on something I discover about 4 more things I’ll
|
|
||||||
have to do down the line. I’m somewhat happy with the progress I’ve
|
|
||||||
made so far but I still have a lot to read up on and learn -
|
|
||||||
especially relating to caching policies for mapped pages.</p>
|
|
||||||
<p>For <strong>physical memory management</strong> I maintain the
|
|
||||||
available memory regions in two separate linked lists. One list
|
|
||||||
contains single pages for when those are requested, the other
|
|
||||||
contains the large memory regions which are populated during
|
|
||||||
initialization. This design allows us to easily reuse freed pages
|
|
||||||
(using the list of small pages) while still efficiently finding
|
|
||||||
large blocks for things like memory mapped IO (using the list of
|
|
||||||
large pages).</p>
|
|
||||||
<p>The one catch is that to build these linked lists we need an
|
|
||||||
available heap. And to have an available heap we need to be able to
|
|
||||||
allocate a physical memory region for it (and its necessary paging
|
|
||||||
structures). To accommodate this, we initialize a temporary physical
|
|
||||||
memory manager that just takes a hardcoded number of pages from the
|
|
||||||
first memory region and doles them out in sequence. Right now I
|
|
||||||
hardcode the number of necessary pages to exactly the number it
|
|
||||||
needs. This means if I change something that causes more pages to be
|
|
||||||
allocated earlier than they need to be it is obvious because things
|
|
||||||
break.</p>
|
|
||||||
<p>For <strong>virtual memory management</strong> I keep the higher
|
|
||||||
half (kernel) mappings identical in each address space. Most of the
|
|
||||||
kernel mappings are already availble from the bootloader but some
|
|
||||||
are added for heaps and additional stacks. For user memory we
|
|
||||||
maintain a tree of the mapped in objects to ensure that none
|
|
||||||
intersect. Right now the tree is innefficient because it doesn’t
|
|
||||||
self balance and most objects are inserted in ascending order
|
|
||||||
(i.e. it is essentially a linked list).</p>
|
|
||||||
<p>For user space memory structures we wait until the memory is
|
|
||||||
accessed and generates a page fault to actually map it in. In order
|
|
||||||
to map it in we check each paging structure in the higher-half
|
|
||||||
direct map (rather than using a recursive page structure) to ensure
|
|
||||||
it exists, allocating a page table if necessary. All physical pages
|
|
||||||
used for paging structures are freed when the process exits.</p>
|
|
||||||
<p>For <strong>kernel heap management</strong> I wrote a <a
|
|
||||||
href="https://en.wikipedia.org/wiki/Slab_allocation">slab-allocator</a>
|
|
||||||
for relatively small allocations (up to 128 bytes currently). I plan
|
|
||||||
on raising the limit for that as well as adding a buddy allocator
|
|
||||||
for larger allocations in the future but for now there is no need -
|
|
||||||
all of the allocations are 128 bytes or less! Larger allocations for
|
|
||||||
now are done using a linear allocator.</p>
|
|
||||||
<h3 id="scheduling">Scheduling</h3>
|
|
||||||
<p>Right now the scheduling process is very straight forward. Each
|
|
||||||
runnable thread is kept in an intrusive linked list and scheduled
|
|
||||||
for a single time slice in a round robin fashion.</p>
|
|
||||||
<p>Thread can block on other threads, semaphores, or mutexes. When
|
|
||||||
this happens they are flagged as blocked and moved to an intrusive
|
|
||||||
linked list on that object which is responsible for scheduling those
|
|
||||||
threads once the relevant state changes.</p>
|
|
||||||
<p>The context switching code simply dumps all of the registers onto
|
|
||||||
the stack and then writes the stack pointer into the thread
|
|
||||||
structure. It also writes the SSE registers to an allocated space on
|
|
||||||
the thread structure. I believe this code could be made more
|
|
||||||
efficient by only pushing callee-saved registers and using the x86
|
|
||||||
feature that allows you to lazily save the SSE registers only once
|
|
||||||
they are used. However for now I prefer this code be more reliable
|
|
||||||
than efficient (because it scares me and is a PITA to debug).</p>
|
|
||||||
<p>Finally, there are definitely critical sections in the kernel
|
|
||||||
code that are not mutex protected currently. It is on the TODO list
|
|
||||||
to do a good audit of this in preparation for SMP (AcadiaOS 0.2
|
|
||||||
anyone?).</p>
|
|
||||||
<h3 id="interface">Interface</h3>
|
|
||||||
<p>Most system calls the kernel provides either (a) create and
|
|
||||||
return a capability or (b) operate on an existing capability.
|
|
||||||
Capabilities can be duplicated and/or transmitted to other processes
|
|
||||||
using IPC.</p>
|
|
||||||
<p>For syscalls that operate on an existing capability, the kernel
|
|
||||||
checks that the capability exists, that it is of the correct type,
|
|
||||||
and that the caller has the correct permissions on it. Only then
|
|
||||||
does it act on the request.</p>
|
|
||||||
<p>The kernel provides APIs to:</p>
|
|
||||||
<ul>
|
|
||||||
<li>Manage processes and threads.</li>
|
|
||||||
<li>Synchronizes threads using mutexes and semaphores.</li>
|
|
||||||
<li>Allocate memory and map it into an address space.</li>
|
|
||||||
<li>Communicate with other processes using Endpoints, Ports, and
|
|
||||||
Channels.</li>
|
|
||||||
<li>Register IRQ handlers.</li>
|
|
||||||
<li>Manage Capabilites.</li>
|
|
||||||
<li>Print debug information to the VM output.</li>
|
|
||||||
</ul>
|
|
||||||
<h3 id="ipc">IPC</h3>
|
|
||||||
<p>Interprocess communication can be done using Endpoints, Ports, or
|
|
||||||
Channels. <strong>Endpoints</strong> are like servers that can be
|
|
||||||
called and provide a response. For each call a “ReplyPort”
|
|
||||||
capability is generated that the caller can wait for a response on
|
|
||||||
and the server can send its response to. <strong>Ports</strong> are
|
|
||||||
simply one-way streams of messages that don’t expect a response.
|
|
||||||
Example uses are for process initialization information or for IRQ
|
|
||||||
handlers. <strong>Channels</strong> are for bidirectional message
|
|
||||||
passing that I haven’t found a use for and will probably replace in
|
|
||||||
the future with a byte-stream interface.</p>
|
|
||||||
<p>Message that are passed on these interfaces consist of two parts:
|
|
||||||
a byte array, and an array of capabilities. Each capability passed
|
|
||||||
is removed from the existing process and passed along to whichever
|
|
||||||
process receives the request.</p>
|
|
||||||
<p>I’m fairly happy with these interfaces so far and was able to
|
|
||||||
build a user-space IDL (Yunq) on top of them to facilitate message
|
|
||||||
and capability passing. However, I’m concerned about their ability
|
|
||||||
to handle certain concerns. For instance, since endpoints aren’t
|
|
||||||
“owned” by a specific process, it is impossible to tell if you are
|
|
||||||
“shouting into the void” at a process that has crashed or isn’t
|
|
||||||
listening to the specific endpoint anymore.</p>
|
|
||||||
<h2 id="user-space-programs">User Space Programs</h2>
|
|
||||||
<p>There are a few user-space programs that are run on the
|
|
||||||
system:</p>
|
|
||||||
<ul>
|
|
||||||
<li><strong>Yellowstone</strong>: The init process that starts all
|
|
||||||
others and maintains a registry of endpoints. (Because Yellowstone
|
|
||||||
was first).</li>
|
|
||||||
<li><strong>Denali</strong>: A basic AHCI driver to read from disk.
|
|
||||||
(D for disk).</li>
|
|
||||||
<li><strong>VictoriaFallS</strong>: A VFS server with a super simple
|
|
||||||
read-only ext2 implementation. (I couldn’t resist because it has VFS
|
|
||||||
in it).</li>
|
|
||||||
<li><strong>Teton</strong>: A terminal application with a
|
|
||||||
lightweight shell in it (should eventually be split). (T for
|
|
||||||
terminal).</li>
|
|
||||||
<li><strong>Voyageurs</strong>: PS/2 Keyboard driver with the intent
|
|
||||||
of becoming the USB driver. (Idk bytes traveling over USB are making
|
|
||||||
a voyage I guess).</li>
|
|
||||||
</ul>
|
|
||||||
<p>These programs are all bare-bones versions of what they could be
|
|
||||||
in the future. I hope to describe them in further detail in the
|
|
||||||
future, but for now the initialization process works like this.</p>
|
|
||||||
<ol type="1">
|
|
||||||
<li>Yellowstone, Denali, and VictoriaFallS binaries are loaded into
|
|
||||||
memory as modules by the bootloader.</li>
|
|
||||||
<li>The kernel loads and starts the Yellowstone process, passing it
|
|
||||||
memory capabilities to the Denali and VictoriaFallS binaries.</li>
|
|
||||||
<li>Yellowstone starts Denali and waits for it to register
|
|
||||||
itself.</li>
|
|
||||||
<li>Yellowstone reads the GPT and then starts VictoriaFallS on the
|
|
||||||
correct partition and waits for it to register itself.</li>
|
|
||||||
<li>Yellowstone then reads the /init.txt file from the disk and
|
|
||||||
starts each process specified (one per line) in succession.</li>
|
|
||||||
</ol>
|
|
||||||
<h2 id="yunq-idl">Yunq IDL</h2>
|
|
||||||
<p>As I began writing system services, I found a huge speed bump was
|
|
||||||
creating client and server classes for the service. I started by
|
|
||||||
just passing structs as a byte array and hardcoding whether or not
|
|
||||||
the process expected to receive a capability with the call. This
|
|
||||||
approach worked but was painful and led to me dreading each new
|
|
||||||
service I added to the system (not how it should be for a
|
|
||||||
microkernel architecture!). Additionally I did things like avoiding
|
|
||||||
repeated fields or strings fields that weren’t possible to pass in a
|
|
||||||
single struct.</p>
|
|
||||||
<p>It was clear I needed some sort of IDL to handle this, but for
|
|
||||||
months I waffled on it as I tried to figure out how to incorporate
|
|
||||||
an existing one into the system. That didn’t work for two reasons.
|
|
||||||
First, we need a way to pass capabilities with the messages. These
|
|
||||||
kind of need to be sidechanneled because the kernel can’t just treat
|
|
||||||
them as another string of bytes (they have to be moved into the
|
|
||||||
other processes capability space). Second, existing serialization
|
|
||||||
libraries tend to have dependencies, so porting them would require
|
|
||||||
porting those dependencies first. Granted, some of them just require
|
|
||||||
super basic things like say a libc implementation - but we don’t
|
|
||||||
even have that yet. All that to say I ended up writing my own.</p>
|
|
||||||
<p>I was pleasantly surprised with how straightforward it ended up
|
|
||||||
being. I think it took me about 3 coding sessions to get the basic
|
|
||||||
parsing and codegen going for the language. It still doesn’t have
|
|
||||||
all of the features I planned for it (like nested messages), but it
|
|
||||||
works super well for setting up new services quickly and easily.
|
|
||||||
Currently the implementation is in python because I wanted to get
|
|
||||||
something working quickly, but I’ll probably reimplement it in a
|
|
||||||
compiled language in the future with a focus on better error
|
|
||||||
information.</p>
|
|
||||||
<h2 id="closing-thoughts">Closing thoughts</h2>
|
|
||||||
<p>Overall, I’m very pleased with how this project has turned out. I
|
|
||||||
feel like I’ve definitely accomplished my goal to learn more about
|
|
||||||
how operating systems are actually implemented. It has been cool to
|
|
||||||
be able to pull back the curtain and see some of the simple
|
|
||||||
primitives that underlay the complex features of an operating
|
|
||||||
system.</p>
|
|
||||||
<p>I aim to continue forward with this project - without throwing
|
|
||||||
out the code again as I did earlier this year. I’m happy with the
|
|
||||||
base and look to iterate on it, hopefully building something more
|
|
||||||
useful in the future but definitely learning more along the way.</p>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
Before Width: | Height: | Size: 268 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 268 KiB After Width: | Height: | Size: 268 KiB |