DEV Community

fanioz
fanioz

Posted on • Originally published at benihkode.web.id

Solstice Cipher: Routing Light to Crack Codes — A Puzzle Game for the June Solstice Game Jam

Solstice Chiper

This is a submission for the June Solstice Game Jam

What I Built

The June solstice is the moment the sun reaches its peak — the longest day, the shortest shadow. I wanted to build a game where you become the sun.

Solstice Cipher is an optical puzzle game where you manipulate light beams to decrypt hidden words. Each level gives you a sun emitter, a set of cipher glyphs hiding the letters of a secret word, and a briefcase full of optical tools. Your job: route the light beam through the correct sequence of glyphs to illuminate every letter and reveal the cipher.

The game progresses through 15 hand-crafted levels that track the arc of the solstice day. Early levels teach you the basics with a single mirror; by the end, you're juggling prisms that split beams, color filters that tint white light into red, green, or blue, combiners that additively blend two colored beams back together, portals that teleport light across the board, and benders that deflect beams at fixed 45° angles. Every tool snaps to 15-degree increments, turning freeform placement into a satisfying logic constraint.

The solstice connection isn't just thematic window dressing — it's the core mechanic. You literally control light, casting it through shadow to reveal hidden knowledge. The deeper you progress, the more tools you need, mirroring how the sun's journey across the sky grows more complex as the day unfolds.

You can play it right now — no downloads required:

Video Demo

Code

Solstice Cipher

A neon-lit optical puzzle game about manipulating light beams to decrypt hidden codes.
Built for the June Solstice Game Jam

Play on Vercel Godot 4.6 Platforms


About the Game

Solstice Cipher is a sci-fi optical puzzle game where you manipulate light beams to decrypt hidden codes. You are given a grid and a briefcase full of optical tools like mirrors and beam splitters. Your goal is to route a single light emitter through the correct sequence of receiver glyphs to reveal the hidden cipher.

It features a sleek, high-contrast neon aesthetic with strict orthogonal grids, capturing the feeling of classic "laser reflecting" games with modern polish and smooth animations.

Features

  • Optical Puzzles: Drag and drop tools like mirrors and splitters to direct light beams around obstacles.
  • Contextual Learning: Learn the mechanics naturally as you play, thanks to a contextual UI tutorial system that guides you without breaking immersion.
  • Neon Aesthetic: A sleek, high-contrast visual…

How I Built It

I didn't write a single line of GDScript by hand. Instead, I took on the role of Game Director, using Google Antigravity — a Gemini-powered autonomous coding agent — as my lead programmer and Godot engine specialist.

I chose Godot 4.6 with the GL Compatibility renderer (WebGL 2) to target both web browsers and Android. My process was entirely prompt-driven: I described architectural constraints and game mechanics in plain English, and Antigravity generated the implementation.

1. A Multi-Pass 2D Raycasting Engine

The heart of the game is a custom raycasting system built on Godot's PhysicsDirectSpaceState2D. Every time a tool is moved or rotated, the engine recalculates the entire light path from scratch:

var space_state = get_world_2d().direct_space_state

active_rays.append({
    "origin": sun.global_position,
    "direction": Vector2.RIGHT.rotated(sun.rotation),
    "color": "white",
    "bounces_left": MAX_BOUNCES,
    "exclude": [],
    "current_dist": 0.0
})
Enter fullscreen mode Exit fullscreen mode

Each ray segment is cast using PhysicsRayQueryParameters2D, and when the beam hits a tool, the tool's process_beam() method returns zero, one, or two new rays depending on its type. Mirrors use Vector2.bounce() for physically accurate reflections. Prisms split a single beam into two — one straight-through and one at a right angle. The whole system runs in a propagation queue that resolves in multiple passes (up to 10) to handle combiners that need to accumulate inputs from separate beam branches before emitting their blended output:

while active_rays.size() > 0 and pass_count < 10:
    var propagation_queue = active_rays.duplicate()
    active_rays.clear()

    while propagation_queue.size() > 0:
        var ray = propagation_queue.pop_front()
        _cast_ray_segment(space_state, ray, propagation_queue)
Enter fullscreen mode Exit fullscreen mode

2. Seven Optical Tools, One Unified Interface

Every tool implements a single polymorphic process_beam() method. The raycaster doesn't care what it hit — it just calls process_beam() and follows the output. This made it trivial to add new tool types without touching the core engine:

Tool Behavior
Mirror Reflects the beam (Vector2.bounce())
Prism Splits into straight-through + right-angle beams
Filter Passes beam through, tinting it red/green/blue
Combiner Collects two colored beams, additively blends RGB, emits one output
Portal Teleports beam to a linked portal with local-space direction inversion
Bender Deflects beam at a fixed 45° angle relative to its normal
Shade Blocks the beam entirely

3. Color-Matched Cipher Glyphs

The real "aha!" moment in puzzle design came from coupling color to the cipher. Each glyph has a required_color property — if the beam hitting it doesn't match, it stays dark:

func set_illuminated(state: bool, hit_color: String = "white") -> void:
    if state and hit_color != required_color:
        state = false
Enter fullscreen mode Exit fullscreen mode

This means later levels aren't just about routing light to the right place — you need the right color of light to arrive there. You might need to split a white beam through a prism, filter one branch to red and the other to blue, then recombine them downstream for a magenta glyph. It transforms each puzzle into a logic circuit.

4. Contextual Tutorial Without Breaking Flow

With the jam clock ticking, I couldn't afford to build scripted tutorial levels. Instead, I implemented a lightweight contextual UI system that teaches mechanics as the player discovers them:

func _on_item_dropped(tool_type: String, drop_position: Vector2, _slot_ref: Node) -> void:
    if instruction_1:
        instruction_1.hide()
    if tool_type == "mirror" and instruction_3 and not instruction_3.visible:
        instruction_3.show()
        instruction_3.position = drop_position + Vector2(-300, -80)
Enter fullscreen mode Exit fullscreen mode

The instructions only appear after the player successfully performs an action. Drop your first mirror? Now the rotation hint appears — positioned right above where you dropped it. It's reactive, not prescriptive.

5. Procedural Puzzle Verification with a BFS Backwards Solver

To validate that generated puzzle layouts are actually solvable, I tasked Antigravity with building a BackwardsSolver. It uses BFS pathfinding on the grid to find routes from the light source to each glyph, then analyzes turns and intersections to determine exactly which tools (mirrors, prisms, filters) need to be placed and where. If the required tool count exceeds the tier's budget, the layout is rejected:

# Budget check
if tools.size() > get_piece_budget(tier):
    return {} # Over budget — reject and regenerate
Enter fullscreen mode Exit fullscreen mode

This guarantees every procedurally generated level is mathematically solvable before the player ever sees it.

Prize Category

Best Ode to Alan Turing

Solstice Cipher is, at its core, a game about mechanical decryption.

Just as Turing's Bombe machine required operators to wire together rotors in precise configurations to complete an electrical circuit and crack Enigma, players in Solstice Cipher must wire together optical tools in precise configurations to complete a light circuit and crack a hidden word. The parallel is structural, not superficial:

  • Turing's rotor → the Mirror. Both redirect a signal (electrical/optical) along a new path.
  • The plugboard → the Filter and Combiner. Both transform the signal's properties (letter substitution / color transformation) as it passes through.
  • The Enigma ciphertext → the darkened glyphs. Both are meaningless until the correct circuit configuration illuminates the answer.

The game's multi-pass raycasting engine effectively makes each puzzle chamber a programmable optical computer. The player doesn't just solve puzzles — they physically program a decryption machine out of light and glass, one mirror at a time. Every valid solution is a working program; every incorrect configuration is a bug.

Best Google AI Usage

Solstice Cipher was built from the ground up using Google Antigravity, a Gemini-powered autonomous coding agent, acting as my lead programmer and Godot 4.6 specialist. My role was pure game direction — defining architecture constraints, reviewing generated code, and steering design decisions.

Antigravity co-developed every technical system in the project:

  • The multi-pass 2D raycasting engine with PhysicsDirectSpaceState2D, including the propagation queue and combiner resolution loop
  • All seven optical tool implementations with their polymorphic process_beam() interfaces
  • The BFS backwards solver for procedural puzzle validation with tier-based budget constraints
  • The contextual tutorial system with tween-driven breathing animations
  • The CipherUI system that connects glyph illumination signals to the win condition checker
  • The SaveManager, AudioManager (with SFX pooling), and TransitionManager autoloads
  • A complete GUT test suite validating the scaffolder's determinism and the solver's correctness

This project demonstrates Google AI not as an embedded runtime feature, but as a full-spectrum game development partner — from architecture to implementation to testing.


Discussion: What's your approach to teaching players new mechanics during a jam? Scripted tutorials, contextual hints, or something else entirely? Let me know below!

Top comments (0)