Skip to content

Commit 6f8804b

Browse files
add the hidden geometry of dice
1 parent 72f2ba9 commit 6f8804b

File tree

6 files changed

+165
-11
lines changed

6 files changed

+165
-11
lines changed

deps.edn

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
metosin/malli {:mvn/version "0.18.0"}
1010
clj-fuzzy/clj-fuzzy {:mvn/version "0.4.1"}
1111
org.scicloj/clay {:git/url "https://github.com/scicloj/clay"
12-
:git/sha "c049e839a496bd8b41faaa6993dae4da27d8220c"}
12+
:git/sha "d64df566e3dd0e90ac9360d86a481a7be7587eaf"}
1313
org.eclipse.elk/org.eclipse.elk.core {:mvn/version "0.10.0"}
1414
org.eclipse.elk/org.eclipse.elk.graph {:mvn/version "0.10.0"}
1515
org.eclipse.elk/org.eclipse.elk.graph.json {:mvn/version "0.10.0"}

src/civitas/authors.clj

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
^:kindly/hide-code
2-
^{:clay {:quarto {:title "Authors"
3-
:type :page}}}
2+
^{:clay {:title "Authors"
3+
:quarto {:type :page}}}
44
(ns civitas.authors
55
(:require [clojure.edn :as edn]
66
[scicloj.kindly.v4.kind :as kind]))
77

8+
;; You belong here!
9+
10+
;; Thank you for sharing your ideas.
11+
812
^:kindly/hide-code
913
(defn card [{:keys [name url links affiliation email image]}]
1014
(kind/hiccup

src/civitas/explorer.clj

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
^{:kindly/hide-code true
2-
:clay {:title "Civitas Explorer"
3-
:type :page
4-
:quarto {:author :timothypratley
5-
:format {:html {:page-layout :full}}}}}
1+
^:kindly/hide-code
2+
^{:clay {:title "Civitas Explorer"
3+
:quarto {:type :page
4+
:format {:html {:page-layout :full}}}}}
65
(ns civitas.explorer
76
(:require [scicloj.kindly.v4.kind :as kind]
87
[civitas.explorer.db :as db]

src/clojure+/print/objects_and_protocols.clj

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
:draft true}}}
55
(ns clojure+.print.objects-and-protocols
66
(:require [clojure.core.async :as async]
7+
[clojure.core.async.flow :as flow]
78
[clojure.datafy :as datafy]
89
[clojure.string :as str]
910
[core.async.flow.example.stats :as stats]))
@@ -47,7 +48,7 @@
4748
;; Especially in the world of notebooks where we like to show things as we go,
4849
;; but also just keeping a tidy REPL or looking into data that contains objects.
4950

50-
(stats/create-flow)
51+
(flow/create-flow stats/config)
5152

5253
;; Hmmmm. not so nice. We'll dig into this further below.
5354
;; But we also need to be aware that Clojure munges it's names to make Java valid names.
@@ -111,7 +112,7 @@
111112

112113
*ns*
113114
(((fn aaa [] (fn bbb [] (fn ccc [])))))
114-
(stats/create-flow)
115+
(flow/create-flow stats/config)
115116

116117
;; What is this? It's a reified object that implements protocols.
117118
;; We can see this by the $reify part at the end.
@@ -135,7 +136,7 @@
135136
;; Leaving aside those concerns, it returns quite a long list...
136137

137138
(def stats-flow
138-
(stats/create-flow))
139+
(flow/create-flow stats/config))
139140

140141
(all-protocol-vars stats-flow)
141142

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
^{:kindly/hide-code true
2+
:clay {:title "The Hidden Geometry of Dice"
3+
:quarto {:type :post
4+
:author [:timothypratley]
5+
:date "2025-06-05"
6+
:description "A simple probability puzzle turns into a journey through triangular numbers and tessellated hexagons."
7+
:category :math
8+
:tags [:probability :stats]
9+
:keywords [:quantquestions]}}}
10+
(ns math.stats.quantquestions.what-are-the-odds.the-hidden-geometry-of-dice
11+
(:require [civitas.explorer.geometry :as geom]
12+
[civitas.explorer.svg :as svg]))
13+
14+
;; Welcome to What Are the Odds?
15+
;; The show where we answer life’s important questions, like can I outsmart a six-sided die?
16+
;; Today we’re starting small.
17+
;; Two rolls of the dice and one burning question.
18+
;; No magic formulas, just curiosity, some patient counting, and a faint hope that math is on our side.
19+
;; Let’s roll.
20+
21+
;; > *Pop quiz:*
22+
;; > You roll a fair 6-sided die twice.
23+
;; > Calculate the probability that the value of the first roll is strictly less than the value of the second roll.
24+
25+
;; As I always like to say to my niece,
26+
;; "the secret to answering any probability question is to enumerate the outcomes and count the ones we care about."
27+
28+
;; $P(Interesting) = InterestingOutcomes / TotalOutcomes$
29+
30+
;; Rolling 1 die once has 6 outcomes: 1 2 3 4 5 6.
31+
32+
;; Rolling 1 die twice gives us a combination of outcomes, let's write out a few.
33+
34+
;; ```
35+
;; [1 1] [1 2] '... [1 6]
36+
;; [2 1] [2 2] '...
37+
;; ```
38+
39+
;; Following this pattern would produce 6 rows of 6 columns,
40+
;; so there must be `36` outcomes.
41+
;; We write down just enough of the pattern to figure out the best way to count it.
42+
;; Now we count how many of those meet the criteria.
43+
44+
;; ```
45+
;; [1 1 :no] [1 2 :yes] '...
46+
;; [2 1 :no] [2 2 :no] [2 3 :yes] '...
47+
;; ```
48+
49+
;; Logically we should see 5 yeses on the first row,
50+
;; then 4, 3, 2, 1, and 0, which we can ignore.
51+
;; Add them all up and we get `15`.
52+
53+
;; So the answer to the question is `15/36` which reduces to `5/12`,
54+
;; dividing top and bottom by the greatest common divisor 3.
55+
56+
;; You might be thinking that it's not practical to enumerate everything all the time,
57+
;; I should use the formulas of probability.
58+
;; That's true, those are marvelous.
59+
;; However, in my experience it is also easy to go wrong reasoning from formulas.
60+
;; It's harder to go wrong starting with a counting problem,
61+
;; then improving your method of counting.
62+
;; You end up in the same place, but more confident in the answer.
63+
64+
;; The full enumeration of our simple 2 roll question as a counting problem is just big enough to be too tedious to use only counting.
65+
66+
(let [roll [1 2 3 4 5 6]]
67+
(for [i roll]
68+
(for [j roll]
69+
[i j])))
70+
71+
;; Identifying the pattern is enough to realize the answer
72+
73+
(+ 5 4 3 2 1)
74+
75+
;; What a marvelous pattern it is!
76+
;; Predictable, but not flat.
77+
;; Smooth, but not boring.
78+
;; Recursive, and not obvious.
79+
80+
^:kindly/hide-code
81+
(into [] (map #(reduce + (range (inc %)))) (range 1 21))
82+
83+
;; There's something special about this sequence.
84+
;; Aren't those numbers just... pleasing in some way?
85+
86+
;; This sequence is called the triangular numbers.
87+
88+
;; ```
89+
;; .
90+
;; . .
91+
;; . . .
92+
;; . . . .
93+
;; ```
94+
95+
;; You can find the first 10 or so numbers in your head,
96+
;; and with some paper many more quite quickly.
97+
98+
;; ![So many dots](the_hidden_geometry_of_dice.jpg)
99+
100+
;; There is a formula for calculating the nth triangular number:
101+
;; $T_n = 1 + 2 + 3 + \dots + n = \frac{n(n + 1)}{2}$
102+
103+
;; The 20th triangular number is `(20x21)/2 = (400+20)/2 = 210`.
104+
;; Isn't it wonderful how there's so many different ways to find the same answer in math?
105+
106+
;; There is something curious about the formula; it divides by 2 but only produces integers.
107+
;; How can we be certain we will only ever get an integer?
108+
;; So mysterious.
109+
;; `n(n+1)` is always even!
110+
;; Let's think about that a bit more, if n is odd, then n+1 is even.
111+
;; If n is even, then n+1 is odd.
112+
;; One of the multiples is always even, meaning that 2 is a factor,
113+
;; so the multiple must always have a factor 2, and be even.
114+
115+
;; It's easy now to imagine if we had a 1000 sided dice what the answer would be.
116+
;; But be careful! For a 1000 side die, we want the 999nth triangular number:
117+
;; $(999 \times 1000)/2 = (1000000-1000)/2 = 500000-500 = 499500$
118+
;; and the total outcomes would be `1000x1000`, so the answer would be `0.4995`.
119+
;; It's comforting to see that for a large range, we land closer to 50%.
120+
121+
;; The point is that once we know what we are counting,
122+
;; it feels more obvious that we used the right formula to count it.
123+
124+
;; Triangular numbers show up in many situations,
125+
;; my favorite is that they can be used to lay out hexagons.
126+
;; The [code that draws Clojure Civitas hexagons](https://github.com/ClojureCivitas/clojurecivitas.github.io/blob/main/src/civitas/explorer/geometry.clj)
127+
;; is based on the triangular number formula.
128+
129+
^:kindly/hide-code
130+
(let [layers 4
131+
r 80
132+
vr (* layers r 2)]
133+
^:kind/hiccup
134+
[:svg {:xmlns "http://www.w3.org/2000/svg"
135+
:viewBox [(- vr) (- vr) (* 2 vr) (* 2 vr)]
136+
:width "100%"}
137+
(for [[x y] (->> (geom/cube-spiral layers)
138+
(map geom/cube-to-cartesian-flat))]
139+
(let [x (* r x)
140+
y (* r y)]
141+
[:g {:transform (str "translate(" x "," y ")")}
142+
(svg/polygon {:fill "lightblue"} (geom/hex (* 0.9 r)))]))])
143+
144+
;; Triangular numbers also show up in the number of pairs,
145+
;; handshakes, edges in a complete graph, diagonals sum to triangular numbers,
146+
;; square numbers as sums of consecutive odd numbers, differences of triangulars,
147+
;; acceleration frames, smooth transitions, spacing.
148+
;; Such a beautiful pattern that can be found in so many situations!
149+
150+
;; Until next time, may your dice be fair and your outcomes interesting.
67 KB
Loading

0 commit comments

Comments
 (0)