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: + +
+ + + + + + + +
RankingAward 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. + +
+
+ CLRG Award Points Speculator +
+ Points: + +
+ + + + + + +
Possible Rankings
Computing: this could take a while!
+
+
+ + +## 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: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AliceBob
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 @@ +
+ {{- $img := $.Page.Resources.GetMatch (.Get "src")}} + {{.Get +
\ 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; }