diff --git a/content/blog/2021-12-23-drwho-S01E27-mexico/index.md b/content/blog/2021-12-23-drwho-S01E27-mexico/index.md
index 591972c..41b05e5 100644
--- a/content/blog/2021-12-23-drwho-S01E27-mexico/index.md
+++ b/content/blog/2021-12-23-drwho-S01E27-mexico/index.md
@@ -1,9 +1,6 @@
---
-date: "2021-12-23T00:00:00Z"
-tags:
-- drwho
-title: 'Doctor Who S01E27-30: Doctor Who goes to Mexico'
-url: blog/2021-12-23-drwho-S01E27-mexico/
+title: "Doctor Who S01E27-30: Doctor Who goes to Mexico"
+date: 2021-12-23
---
A bunch of white people pretend to be Aztecs, and explore their moral
@@ -42,7 +39,7 @@ Barbara abusing her god status to outlaw human sacrifice. The Doctor gets
married or engaged or something. Susan finally expresses an emotion other
than terror:
-{{< video "susan-well-hello-there.mp4" "Well, hello there." >}}
+{{< video src="susan-well-hello-there.mp4" text="Well, hello there." >}}
I'm back to being uncomfortable with the cultural framing here. I mean,
maybe this is useful for viewing 1960s British culture, but that's still
diff --git a/content/blog/2022-09-06-truck-bling/index.md b/content/blog/2022-09-06-truck-bling/index.md
index 701f5cf..38ab566 100644
--- a/content/blog/2022-09-06-truck-bling/index.md
+++ b/content/blog/2022-09-06-truck-bling/index.md
@@ -1,7 +1,6 @@
---
-date: "2022-09-06T12:11:00-0600"
title: Truck bling
-url: blog/2022-09-06-truck-bling/
+date: "2022-09-06T12:11:00-0600"
---
Yesterday,
@@ -18,7 +17,7 @@ the friend soldered a bunch of stuff together,
and we plugged it in.
It friggin' worked!
-{{< video "truck-bling.m4v" "Pickup truck with color-changing ground effects">}}
+{{< video src="truck-bling.m4v" text="Pickup truck with color-changing ground effects">}}
The really cool part, at least for me,
is that now she can hang out with her laptop in the cabin,
diff --git a/content/blog/2022-10-09-CLRG-Scoring/awardPoints.mjs b/content/blog/2022-10-09-CLRG-Scoring/awardPoints.mjs
new file mode 100644
index 0000000..40a3372
--- /dev/null
+++ b/content/blog/2022-10-09-CLRG-Scoring/awardPoints.mjs
@@ -0,0 +1,122 @@
+let awardPoints = [
+ 100, // 1
+ 75, // 2
+ 65, // 3
+ 60, // 4
+ 56, // 5
+ 53, // 6
+ 50, // 7
+ 47, // 8
+ 45, // 9
+ 43, // 10
+ 41, // 11
+ 39, // 12
+ 38, // 13
+ 37, // 14
+ 36, // 15
+ 35, // 16
+ 34, // 17
+ 33, // 18
+ 32, // 19
+ 31, // 20
+ 30, // 21
+ 29, // 22
+ 28, // 23
+ 27, // 24
+ 26, // 25
+ 25, // 26
+ 24, // 27
+ 23, // 28
+ 22, // 29
+ 21, // 30
+ 20, // 31
+ 19, // 32
+ 18, // 33
+ 17, // 34
+ 16, // 35
+ 15, // 36
+ 14, // 37
+ 13, // 38
+ 12, // 39
+ 11, // 40
+ 10, // 41
+ 9, // 42
+ 8, // 43
+ 7, // 44
+ 6, // 45
+ 5, // 46
+ 4, // 47
+ 3, // 48
+ 2, // 49
+ 1, // 50
+ 0.75, // 51
+ 0.65, // 52
+ 0.60, // 53
+ 0.56, // 54
+ 0.53, // 55
+ 0.50, // 56
+ 0.47, // 57
+ 0.45, // 58
+ 0.43, // 59
+ 0.41, // 60
+ 0.39, // 61
+ 0.38, // 62
+ 0.37, // 63
+ 0.36, // 64
+ 0.35, // 65
+ 0.34, // 66
+ 0.33, // 67
+ 0.32, // 68
+ 0.31, // 69
+ 0.30, // 70
+ 0.29, // 71
+ 0.28, // 72
+ 0.27, // 73
+ 0.26, // 74
+ 0.25, // 75
+ 0.24, // 76
+ 0.23, // 77
+ 0.22, // 78
+ 0.21, // 79
+ 0.20, // 80
+ 0.19, // 81
+ 0.18, // 82
+ 0.17, // 83
+ 0.16, // 84
+ 0.15, // 85
+ 0.14, // 86
+ 0.13, // 87
+ 0.12, // 88
+ 0.11, // 89
+ 0.10, // 90
+ 0.09, // 91
+ 0.08, // 92
+ 0.07, // 93
+ 0.06, // 94
+ 0.05, // 95
+ 0.04, // 96
+ 0.03, // 97
+ 0.02, // 98
+ 0.01, // 99
+ 0.00, // 100
+]
+
+function init() {
+ for (let tbody of document.querySelectorAll(".awardPoints tbody")) {
+ for (let i = 0; i < awardPoints.length; i++) {
+ let tr = tbody.appendChild(document.createElement("tr"))
+ tr.appendChild(document.createElement("td")).textContent = i + 1
+ tr.appendChild(document.createElement("td")).textContent = awardPoints[i].toFixed(2)
+ }
+ }
+}
+
+if (document.readyState === "loading") {
+ document.addEventListener("DOMContentLoaded", init)
+} else {
+ init()
+}
+
+export {
+ awardPoints,
+}
diff --git a/content/blog/2022-10-09-CLRG-Scoring/chart.png b/content/blog/2022-10-09-CLRG-Scoring/chart.png
new file mode 100644
index 0000000..c7e2714
Binary files /dev/null and b/content/blog/2022-10-09-CLRG-Scoring/chart.png differ
diff --git a/content/blog/2022-10-09-CLRG-Scoring/index.md b/content/blog/2022-10-09-CLRG-Scoring/index.md
new file mode 100644
index 0000000..9ede31e
--- /dev/null
+++ b/content/blog/2022-10-09-CLRG-Scoring/index.md
@@ -0,0 +1,175 @@
+---
+title: CLRG Scoring Analyzed
+date: 2022-10-09
+stylesheets:
+ - toys.css
+scripts:
+ - speculator.mjs
+ - scorecard.mjs
+---
+
+Let's take a look how how CLRG does its scoring!
+*With math!*
+
+I'm going to only view this mathematically.
+I know there's a lot of other stuff that goes on, like line-ups.
+I'm only going to look at this with numbers.
+
+
+## How CLRG Scoring Works
+
+As I am given to understand, the scoring works like so:
+
+1. Adjudicators give you a "raw score": a real number between 0 and 100
+2. The scoring system ranks each dancer per adjudicator, based on raw scores
+3. These rankings are mapped into "award points"
+4. All of a dancer's award points are summed
+5. Final ranking is determined by comparing total award points
+
+## Raw Scoring
+
+The way raw scores translate into rankings and award points is a little
+confusing, so I've made a little tool you can play with to get a feel for how it
+works. Essentially, it's a way of normalizing places to an adjudicator: score
+weights are only relative to the judge that assigns them.
+
+Adjudicator A can assign scores between 80 and 100;
+adjudicator B can assign scores between 1 and 40;
+and they'll both have a first, second, third, fourth place, etc.
+These places then get translated into award points.
+
+
+## Award Points
+
+Award points are handed out based on ranking against other dancers for that
+adjudicator. I obtained these values from a FeisWorx results page for my kid:
+
+
+
+
+
+
Ranking
Award Points
+
+
+
+
+
+
+If there's a 2-way, 3-way, or n-way tie,
+all tied dancers get the average of the next 2, 3, or n award points,
+and the next 2, 3, or n rankings are skipped.
+
+### Award Points artifacts
+
+One quirk of awards points is that for any given overall
+score, there are only a handful of possible judge rankings that could have led
+to it. That means you can make some guesses about how each judge ranked an
+individual dancer, based on only their total award points.
+
+Here's a handy calculator!
+It (currently) doesn't consider the possibility of a tie.
+
+
+
+
+
+
+## What's with these values?
+
+At first glance, the award points look like the output of an exponential function.
+
+{{
}}
+
+In an effort to figure out where these numbers came from,
+I ran some curve fitting against the data.
+Here's the best I could come up with:
+
+| Ranking range | Award Points Function | Type of function |
+| --: | --: | --- |
+| 1 - 11 | 100 * x^-0.358 | Exponential |
+| 12 - 50 | 51 - x | Linear |
+| 51 - 60 | 14.2 - 0.46x + 0.00385x | Polynomial |
+| 61 - 100 | 1 - x/100 | Linear |
+
+If you, dear reader, are a mathematician,
+I would love to hear your thoughts on why they went with this algorithm.
+
+There are a few points to note here:
+
+* 1st place is a *huge deal*. Disproportionately huge.
+* Places 2-10 are similarly big deals compared to places 3-11.
+* Places 12-50 operate the way most people probably assume ranking works: linearly.
+* Places 51-60 are a second degree polynomial, but it doesn't matter much for so few points.
+* Places 61-100 are all less than 1 point. If you're a judge trying to tank a top dancer, anywhere in this range is equivalent to anywhere else.
+
+
+## Consequences of Exponential Award Points
+
+Playing around with this,
+I've found a few interesting consequences
+of the exponential growth in the top 11 places.
+
+
+### 1st place is super important
+
+1st place is weighted so heavily that it's almost impossible to overcome without
+your own 1st.
+
+Take for example this scenario,
+in which Adjudicator 1 has promised to give 1st place to Alice:
+
+
+
+
+
+
Alice
+
Bob
+
+
+
+
+
Adj. 1
+
+
+
+
+
Adj. 2
+
+
+
+
+
Adj. 3
+
+
+
+
+
+
+
Award Points
+
+
+
+
+
Ranking
+
+
+
+
+
+
+
+
+
diff --git a/content/blog/2022-10-09-CLRG-Scoring/scorecard.mjs b/content/blog/2022-10-09-CLRG-Scoring/scorecard.mjs
new file mode 100644
index 0000000..0603bbd
--- /dev/null
+++ b/content/blog/2022-10-09-CLRG-Scoring/scorecard.mjs
@@ -0,0 +1,54 @@
+import {awardPoints} from "./awardPoints.mjs"
+
+function scorecardUpdate(scorecard) {
+ let scores = []
+ let points = []
+
+ let firstRow = scorecard.querySelector("tbody tr")
+ for (let input of firstRow.querySelectorAll("input")) {
+ scores.push(0)
+ points.push(0)
+ }
+
+ for (let row of scorecard.querySelectorAll("tbody tr")) {
+ let i = 0
+ for (let input of row.querySelectorAll("input")) {
+ let ranking = Number(input.value)
+ scores[i] += ranking
+ points[i] += awardPoints[ranking]
+ i += 1
+ }
+ }
+
+ {
+ let i = 0
+ for (let out of scorecard.querySelectorAll("tfoot output[name='points']")) {
+ out.value = points[i]
+ i += 1
+ }
+ }
+
+ {
+ let i = 0
+ for (let out of scorecard.querySelectorAll("tfoot output[name='ranking']")) {
+ out.value = scores[i]
+ i += 1
+ }
+ }
+}
+
+function init() {
+ for (let scorecard of document.querySelectorAll(".scorecard")) {
+ for (let input of scorecard.querySelectorAll("input")) {
+ input.addEventListener("input", () => scorecardUpdate(scorecard))
+ }
+ scorecardUpdate(scorecard)
+ }
+}
+
+if (document.readyState === "loading") {
+ document.addEventListener("DOMContentLoaded", init)
+} else {
+ init()
+}
+
\ No newline at end of file
diff --git a/content/blog/2022-10-09-CLRG-Scoring/speculator.mjs b/content/blog/2022-10-09-CLRG-Scoring/speculator.mjs
new file mode 100644
index 0000000..e9eaca1
--- /dev/null
+++ b/content/blog/2022-10-09-CLRG-Scoring/speculator.mjs
@@ -0,0 +1,104 @@
+import { awardPoints } from "./awardPoints.mjs"
+
+function arraysEqual(a, b) {
+ if (a === b) return true;
+ if (a == null || b == null) return false;
+ if (a.length != b.length) return false;
+
+ for (let i = 0; i < a.length; ++i) {
+ if (a[i] !== b[i]) return false;
+ }
+ return true;
+}
+function awardPossibilities(total=0, depth=1) {
+ if (depth == 1) {
+ if (awardPoints.includes(total)) {
+ return [[total]]
+ } else {
+ return []
+ }
+ }
+
+ let possibilities = []
+ for (let p of awardPoints) {
+ if (p <= total) {
+ for (let subPossibility of awardPossibilities(total - p, depth - 1)) {
+ let v = [p].concat(subPossibility)
+ v.sort((a,b) => b-a)
+ possibilities.push(v)
+ }
+ }
+ }
+
+ possibilities.sort((a,b) => b.reduce((a,b) => a*100+b) - a.reduce((a,b) => a*100+b))
+ let uniquePossibilities = []
+ for (let p of possibilities) {
+ if (uniquePossibilities.length == 0 || !arraysEqual(p, uniquePossibilities[uniquePossibilities.length - 1])) {
+ uniquePossibilities.push(p)
+ }
+ }
+ return uniquePossibilities
+}
+
+function speculate(calc) {
+ let points = calc.querySelector("[name=points]").value || 0
+ let adjudicators = calc.querySelector("[name=adjudicators]").value || 3
+ let results = calc.querySelector(".results tbody")
+ while (results.firstChild) {
+ results.removeChild(results.firstChild)
+ }
+
+ for (let warning of calc.querySelectorAll(".warning")) {
+ if (adjudicators >3) {
+ warning.classList.add("visible")
+ setTimeout(() => asyncSpeculate(calc, points, adjudicators), 0)
+ } else {
+ warning.classList.remove("visible")
+ asyncSpeculate(calc, points, adjudicators)
+ }
+ }
+
+}
+
+async function asyncSpeculate(calc, points, adjudicators) {
+ let results = calc.querySelector(".results tbody")
+ let possibilites = awardPossibilities(points, adjudicators)
+
+ if (possibilites.length == 0) {
+ let row = results.appendChild(document.createElement("tr"))
+ let cell = row.appendChild(document.createElement("th"))
+ cell.textContent = "No possible combinations"
+ } else {
+ let row = results.appendChild(document.createElement("tr"))
+ for (let i = 1; i <= adjudicators; ++i) {
+ let cell = row.appendChild(document.createElement("th"))
+ cell.textContent = "Adj. " + i
+ }
+ for (let possibility of possibilites) {
+ let row = results.appendChild(document.createElement("tr"))
+ for (let p of possibility) {
+ let cell = row.appendChild(document.createElement("td"))
+ cell.textContent = p
+ }
+ }
+ }
+ for (let warning of calc.querySelectorAll(".warning")) {
+ warning.classList.remove("visible")
+ }
+}
+
+function init() {
+ for (let calc of document.querySelectorAll(".speculator")) {
+ for (let input of calc.querySelectorAll("input")) {
+ input.addEventListener("input", () => speculate(calc))
+ }
+ speculate(calc)
+ }
+}
+
+if (document.readyState === "loading") {
+ document.addEventListener("DOMContentLoaded", init)
+} else {
+ init()
+}
+
\ No newline at end of file
diff --git a/content/blog/2022-10-09-CLRG-Scoring/toys.css b/content/blog/2022-10-09-CLRG-Scoring/toys.css
new file mode 100644
index 0000000..8f7d521
--- /dev/null
+++ b/content/blog/2022-10-09-CLRG-Scoring/toys.css
@@ -0,0 +1,36 @@
+.winner {
+ color:cornsilk;
+}
+
+figure img {
+ max-width: 100%;
+}
+
+.warning {
+ color: #e64;
+ display: none;
+}
+.warning.visible {
+ display: initial;
+}
+
+.awardPoints {
+ display: inline-block;
+ max-height: 60vh;
+ overflow-y: auto;
+ margin: 1em;
+}
+.awardPoints table {
+ margin: initial;
+}
+.awardPoints thead {
+ position: sticky;
+ top: 0;
+}
+.awardPoints tbody {
+ max-height: 60vh;
+ overflow-y: auto;
+}
+.awardPoints td {
+ text-align: right;
+}
\ No newline at end of file
diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html
index 196ff59..2b60966 100644
--- a/layouts/_default/baseof.html
+++ b/layouts/_default/baseof.html
@@ -5,13 +5,20 @@
-
+
{{range .AlternativeOutputFormats}}
{{end}}
+ {{range .Params.stylesheets}}
+
+ {{end}}
{{range .Params.scripts}}
-
+ {{end}}
+ {{range .Params.scripts}}
+
{{end}}
{{range .Params.headers}}
{{. | safeHTML}}
diff --git a/layouts/shortcodes/figure.html b/layouts/shortcodes/figure.html
new file mode 100644
index 0000000..1105d69
--- /dev/null
+++ b/layouts/shortcodes/figure.html
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/layouts/shortcodes/video.html b/layouts/shortcodes/video.html
index 8d10820..551cfe5 100644
--- a/layouts/shortcodes/video.html
+++ b/layouts/shortcodes/video.html
@@ -1,4 +1,4 @@
diff --git a/run.sh b/run.sh
index 5d78714..2e16e09 100755
--- a/run.sh
+++ b/run.sh
@@ -4,6 +4,9 @@ case "$(hostname)" in
sweetums)
baseURL=http://sweetums.lan:1313/
;;
+ penguin)
+ baseURL=http://penguin.linux.test:1313/
+ ;;
*)
baseURL=http://$(hostname --fqdn):1313/
;;
diff --git a/static/assets/css/default.css b/static/assets/css/default.css
index 7f370d7..a889c96 100644
--- a/static/assets/css/default.css
+++ b/static/assets/css/default.css
@@ -19,12 +19,12 @@ body {
}
input {
- font-family: "Lato", "Roboto", sans-serif;
- font-size: 13pt;
- border: 0;
- outline: 0;
- background: transparent;
- border-bottom: 1px solid black;
+ font-family: inherit;
+ font-size: inherit;
+ border: thin solid #ccc;
+}
+input:read-only {
+ background-color: #eee;
}
.title, td.main {
@@ -146,6 +146,36 @@ button.big {
background: inherit;
}
+legend {
+ background-color: #e0e4cc;
+}
+
+table {
+ margin: 1em;
+ border-collapse: collapse;
+}
+thead, tfoot {
+ color: #e64;
+ background-color: rgba(224, 228, 204, 0.8);
+}
+tbody tr:nth-of-type(even) {
+ background-color: rgba(238, 102, 68, 0.05);
+}
+
+td, th {
+ padding: 0.2em 0.5em;
+}
+caption {
+ caption-side: bottom;
+ font-size: small;
+}
+.justify-right, input[type="number"] {
+ text-align: right;
+}
+.justify-left {
+ text-align: left;
+}
+
.tags {
font-size: small;
}