A port of elm-fasttrack to Roc, with a JS layer drawing the board. The first version may leave the JS fat; the goal is for Roc to own the UI, through something we build ourselves, fitted to this game rather than general.
Nothing is built yet. This is the proposal, plus the four questions only you can answer.
About 4,900 lines, in three unequal parts.
| part | files | lines | what it does |
|---|---|---|---|
| rules | Type, Config, Setup, Piece, Graph, LegalMove, Player, Move, Game, History, Color, WhatIf | ~3,400 | pure: the board graph, legal moves, turns, the deck, undo |
| view | View, Polygon, Main | ~900 | Html + Svg, plus the polygon geometry |
| tests | tests/Example.elm | 749 | elm-test over the rules |
The only effects in the whole program are two:
Time.now, read once to seed the deck.elm/random, which draws a card from the hand.Everything else is a pure function of a message and a model. The Elm
architecture carries over almost directly: init(seed), update(model, msg),
view(model).
Two Elm libraries need a decision in Roc:
AssocList / AssocSet keep insertion order, and that order reaches the
screen: it decides the order of the moves, the highlighted starts and the
cheat sheet. Roc's Dict has its own order. A faithful port needs a small
ordered-list module of our own, a few dozen lines.elm/random is a PCG variant. If we port its initialSeed, int and
step exactly, a seed deals the same game in Roc as in Elm. That makes the
Elm build an oracle for the whole rules layer. canvas_apps/lib/Random.roc
is an LCG, and deliberately not faithful, so it doesn't serve here.Elm rebuilds the whole tree on every message and lets elm/virtual-dom diff
it. We don't need that, because the board never changes shape.
Once the player count is fixed, the board is a fixed set of slots: N zones
times the same configLocations, plus the bullseye. Polygon places them once.
Rotating the board doesn't move a slot. It changes which zone color each slot
is drawn in. Every render of the board is therefore one map:
slot -> { shape, fill, stroke, piece color (or none), piece radius, what a click sends (or nothing) }
This is what drawLocationAtCoords already computes. It is the real content of
View.elm, and it can be in Roc from the first version.
The rest of the screen is small and changes wholesale:
So our "virtual DOM" is two mechanisms, neither of them general:
A click never carries a JS closure. Each clickable slot or card carries the
message Roc gave it, and JS hands that message straight back to update.
tests/Example.elm into Roc expects. Also write the ordered
Dict/Set and the faithful elm/random. The layer is done when the tests
pass and, if we take up the oracle, when a scripted game matches Elm move
for move.host.zig + main.roc), but driven by events instead of a clock:
init : U64 -> Box(model), update : Box(model), Msg -> Box(model),
view : Box(model) -> View. roc glue generates the JS reader, as
JsGlue.roc does for canvas apps.view also
reports slot positions, JS draws whatever it is told, and nothing about
Fast Track is left in JS.A checker runs at every step. The canvas apps are gated through page_check
plus a shot rendered with mini_canvas, with no browser. Here the equivalent
drives a scripted game through the wasm and asserts on the slot map, then
renders the board to a PNG so I can look at it.
JsGlue doesn't read Str, which is on the canvas-apps open list. Fast
Track needs text: card names, instructions and hints.
So JsGlue gains RocStr decoding: the small-string case and the heap case,
about twenty lines of JS emitted by the script. That makes Fast Track its
second user, which is when that code earns its place.
roc-fasttrack), which
would copy the platform and JsGlue. The other is a directory in roc-apps
beside canvas_apps/, which shares glue/, docs/roc-notes.md and the
deploy to roc.lynrummy.com. I'd pick roc-apps, because the platform and
the glue are shared the day we start. Your phrase "brand new project" could
mean either.elm 0.19.1 binary (one
file, about 25 MB, no npm) and building ft.html to replay seeded games.
Without it, the ported tests are the only judge of the rules.Polygon,
zoneColors), but the todo says the UI to pick it was never built. Should
the port stay at Elm's hard-coded 4 (Game.beginGame), or should picking 2–6 be in scope?WhatIf.elm (358 lines, with Debug in it) is the
start of the computer player. Should it be ported with the rest, or left
until the game plays?