Gnomon

Stand uphill. Cast the shadow. A sun circles the board, every stone casts a shadow, and a stone caught in an enemy's shadow is gone.

Click the game once so it receives keyboard input. Open it full page.

Stand uphill. Cast the shadow.

A two-player abstract game about light. A sun circles the board, every stone casts a shadow, and a stone caught in an enemy's shadow is gone. Games take 5–10 minutes.

godot --path gnomon          # from the playground folder

Click a stone, then a highlighted cell. R new game · U undo · H hints on/off · S show tomorrow's shadows · F1 rules · right-click or Esc to deselect. Play against the computer (four levels) or two players on one screen.


How to play

Imagine we're sitting at a 7×7 board. Amber has five round stones along the bottom, Teal has five diamond stones along the top. Around the board is a dial with a sun on it. Amber moves first.

Shadows. Every stone casts a shadow straight away from the sun, two cells long. If the sun is in the north, shadows point south; if it's in the north-east, they point south-west. Shadows run in a straight line, orthogonal or diagonal, and they don't stop at stones.

Eclipse. After you move, every enemy stone standing in one of your shadows is eclipsed and removed. Your own shadows never hurt you, and an enemy's shadow only hurts you after they move. So "standing in a shadow" is a threat, not a death: you get a turn to react. Put it another way: whoever is uphill (nearer the sun, in line, within two cells) wins that standoff.

Your turn. Move one of your stones one step, in any of the 8 directions, onto an empty cell. You must move; you can't pass unless you truly can't.

The sun turns. After each round (one move by Amber, one by Teal) the sun moves one notch clockwise, 45°. The standoffs rotate with it: two stones that are safe now may be a death sentence in two rounds, and the other way round. A pair of enemy stones in line within two cells is a ticking clock; the sun's position decides who dies when it arrives, and you can see it coming.

Dusk. One full circuit of the sun is a day, 8 rounds. At the end of each day the outermost ring of cells goes dark and any stone on it is lost. The board shrinks 7×7 → 5×5 → 3×3. When the third dusk falls, the game ends.

Winning. Eclipse every enemy stone and you win. If the last dusk falls first, whoever has more stones wins; equal is a draw. (Both sides losing their last stones to the same dusk is also a draw.)

Reading the board

A few things worth knowing


Design notes

What I was going for. A game where the rules themselves have weather: one shared, perfectly predictable global state (the sun) that every local fight depends on, so the question is never just "can I take that stone?" but "when, and who will be uphill by then?" I wanted decisions that need you to hold a rotating picture in your head, with the rotation public and deterministic so it's a puzzle rather than a gamble, and a shrinking board so nobody can hide.

What I tried and threw out, with the numbers from AI-vs-AI games. The AI is a plain alpha-beta search, and all of this is reproducible with the tools below.

What I'm unsure about.

Ideas I'd try next. Let players choose the starting sun (pie rule). A second, slower "moon" casting shadows the other way. Stones of different shadow lengths. Hex board with a 6-position sun. A move that lets you spend a stone to hold the sun for a round.


For tinkerers

godot --headless --path gnomon -s tools/test_rules.gd                    # rules unit tests (7000+ checks)
godot --headless --path gnomon -s tools/selfplay.gd -- games=100 p1=3 p2=3   # AI vs AI statistics
godot --headless --path gnomon -s tools/transcript.gd -- depth=3 seed=1  # one game, move by move
godot --path gnomon -- --autoplay    # watch the computer play both sides in the real UI

selfplay options: p1/p2 search depth (0 = random), len shadow length, day rounds per day, sun start direction 0–7 (N, NE, E, SE, S, SW, W, NW), snake=1, mover=0 (instant-eclipse rule). Game logic lives in scripts/rules.gd with no rendering; scripts/ai.gd is the search; scripts/view.gd draws; scripts/main.gd runs the game.