Codemojex · C4.2 · a dive of Scoring and Settlement
The total out of 600.
The six position points sum to the guess’s total. A perfect crack is 6 × 100 = 600 — the maximum, and the percentage a client shows is round(total / 600 × 100), computed on read, never stored. A player’s best total is their rank.
Grounding — echo/…/codemojex/scoring.ex (score/2) · board.ex (record/3, top/2) · game.ex (ScoreWorker) · design §The data model · the scoring story.
total = 600 and percentage = round(600/600×100) = 100. A perfect crack — the maximum a guess can score.Six positions summed
scoring.ex · score/2Scoring.score/2 walks the guess, finds each emoji’s home in the secret, and folds the per-position points into a total. It returns the total, the max of 600, the percentage computed on read, and a per-position breakdown — the total is a sum, nothing more.
def score(secret, guess) when length(secret) == 6 and length(guess) == 6 do
breakdown =
for {emoji, i} <- Enum.with_index(guess) do
case Enum.find_index(secret, &(&1 == emoji)) do
nil -> {i, emoji, :miss, 0, status(:miss)}
j -> d = distance(i, j); {i, emoji, d, points(d), status(d)}
end
end
total = breakdown |> Enum.map(&elem(&1, 3)) |> Enum.sum()
%{total: total, max: 600, percentage: round(total / 600 * 100), breakdown: breakdown}
end
The scoring story pins four totals against one secret, and they read straight off this fold: an exact crack is 600 at 100%; positions 0 and 1 swapped are each ADJACENT (80 points), the rest exact — 560 at 93%; the two ends swapped are MAX distance (0 points each) — 400 at 67%; one emoji absent is a clean MISS — 500 at 83%.
The best total is the rank
board.ex · record/3The leaderboard is one Valkey sorted set per game, and a player’s score on it is their best linear total. Board.record/3 reads the player’s current best from the cm:<game>:base hash, folds the new score in with max, and writes that best straight to the board ZSET — no read-modify-write beyond the best-of. Move an incoming score below to watch the fold.
max(440, 380) = 440 — the stored best wins, so the board is unchanged. A worse guess never lowers a player’s rank; only a new best moves it.def record(game, player, base) do old = hget_int(conn, k(game, "base"), player) new_base = max(old, base) Cmd.hset(k(game, "base"), player, to_string(new_base)) |> Wire.run(conn) # cm:<game>:base Cmd.zadd(k(game, "board")) |> Cmd.score(new_base, player) |> Wire.run(conn) # cm:<game>:board new_base end
There is no ptier, bonus, or tierfirst layer, and the guesses row stores only the linear points — no tier and no percentage column. The board ranks the raw best total, so the score a player sees is the score they earned.
A perfect 600 closes a classic game
game.ex · ScoreWorkerInside the scoring authority, right after the board is recorded, a perfect crack ends an open classic game immediately:
eff = Board.record(game, player, s.total) # ... classic feedback fans out on the bus and over PubSub ... if s.total == 600 and started?(game), do: Rooms.close_game(game) # the authoritative state gate — only an :open game settles on a 600 defp started?(game), do: Map.get(Store.game(game) || %{}, :status) == :open
started?/1 reads the status from the system of record, not the cache, because a Golden Room’s cached status can lag its gather. So a 600 during a Golden Room’s :gathering builds standing without settling an unfilled pool — only an :open game closes on the crack. That close is the seam into settlement.
Next — C4.3 takes the close: the race resolved exactly once, and the three payout strategies the game selects.
References
sources · relatedSources
- Mastermind — the board gamethe maximum a perfect crack scores
- The feedback function and minimax code-breakingthe code-space structure behind the total
Related
- C4.1 — Distance and pointsthe per-position law that sums here
- C4.3 — Settlement strategieswhat the close triggers
- Redis Patterns Appliedthe ZSET leaderboard pattern applied