Thawlight
Warm bursts of light melt the frostfall before it reaches your glowbells. Chain the thaw for big scores!
Goal
Hold back a frostfall with warm light. The field is 3:4 portrait and every position is in WIDTH UNITS: x runs 0 at the left edge to 1 at the right, y runs 0 at the top of the sky to 1.3333 at the bottom of the garden, and both axes use the same unit, so a circle is a true circle and every distance is a plain Euclidean one. Frost falls from y = -0.02 toward five glowbells standing on the ground line at y = 1.19, centred at x = 0.1, 0.3, 0.5, 0.7, 0.9, each covering 0.06 either side. A hearth sits at (0.5, 1.285). You send motes of warm light to a point of empty sky; a mote thaws NOTHING on the way and passes straight through everything, and only the bloom it opens on arrival thaws frost. Every piece of frost a bloom thaws opens a smaller bloom of its own at the spot it stood, up to 6 generations deep, and that cascade is where the points are. Frost that reaches the ground inside a warm glowbell's span sets that bell resting. Waves are endless and the run finishes once all five bells are resting. You are aiming at a PLACE, never at an object: the winning read is where the frost is going to be in half a second.
Scoring
Score only ever rises and only ever by whole numbers. A thaw is worth the frost's own value times the chain multiplier of the bloom that caught it. Values: shard 100, drifter 120, splitter while whole 200, splitter child 80, racer 150, hollow 250 for the second hit (its first hit awards a flat 40 with no multiplier at all), seed pod 200. Chain multiplier by bloom depth: depth 0 x1, depth 1 x2, depth 2 x3, depth 3 x5, depth 4 x8, depth 5 x12, depth 6 x20. Each bloom also counts how much it thawed over its whole life and awards a cluster bonus when it fades: 3 thaws 100, 4 thaws 300, 5 thaws 600, 6 thaws 1000, 7 thaws 1500, 8 thaws 2100, and any more than that still pays 2100 (the formula is 50 * (k - 2) * (k - 1) with k capped at 8). Clearing a wave awards 500 + 100 * wave, plus 150 for every mote still unspent, plus 250 for every glowbell still warm, plus 1000 more if no bell went to rest during the wave. THAT WHOLE CLEAR PAYOUT NEEDS YOU TO HAVE PLAYED THE WAVE: it pays only if you thawed at least one piece of frost during it, which is `thawedThisWave` in the state you are given. Frost landing on bare ground or on a resting glowbell costs nothing, so a wave can clear itself while you sit still, and a wave nobody defended is worth nothing. One thaw is enough; the wave still counts as cleared either way and a resting bell still relights. A seed pod also hands back 2 motes and opens a chain bloom like any other thaw, which makes a pod next to a cluster the highest value tap in the game. Nothing ever subtracts.
Rules
ACTIONS. Exactly three: {type:'start'}, {type:'tick', ticks} and {type:'launch', x, y}. `start` is legal only while phase is 'waiting'. `tick` is always accepted and advances the world 1/60 s per tick; `ticks` is optional, defaults to 1, and is CLAMPED to 1..120 rather than refused, so one action carries at most 2 s. In 'waiting' and 'over' a tick is a total no-op that returns the identical state: nothing moves until you send `start`, and you may think as long as you like between ticks. `launch` names a point of sky in width units. Its space is CONTINUOUS, so it is deliberately absent from legalActions, which would otherwise imply a grid that does not exist. The legal band is y <= 1.12 anywhere across the width; x is clamped silently into 0.03..0.97 and y up to a floor of 0.05, and both are rounded to 4 decimal places before they enter state. Sending one while phase is 'waiting' simply begins the run and costs nothing. A refusal is still an ACCEPTED action: it sets lastRejection, emits one 'rejected' event, and spends no mote. Reasons, tested in this fixed order: not_playing (any phase other than 'playing'), below_skyline (y past the band, or a coordinate that is not a finite number), no_ammo, cooldown, max_in_flight (3 motes already flying). Geometry is tested before resources so a tap on the garden reports below_skyline rather than masquerading as no_ammo. FLIGHT. A mote leaves the hearth and takes flightTicks = round(8 + dist * 22) ticks to arrive, where dist is the straight-line distance from the hearth to your point: 12 ticks for the nearest legal point and 37 for a far corner. Lead the frost by that many ticks. It flies a lerp plus a parabola and thaws nothing en route. BLOOMS. On arrival a depth 0 bloom opens with maxR 0.155 and grow/hold/fade of 12/21/15 ticks. Its radius is maxR * age / 12 while growing, maxR while holding, and shrinks to 0 while fading. It thaws only while growing or holding, so ages 1 to 33, and is completely inert for the last 15. A chain bloom has grow/hold/fade 9/15/12 (36 ticks, thawing on ages 1 to 24) and maxR = max(0.06, 0.115 - 0.012 * its own depth), so 0.103, 0.091, 0.079, 0.067 and then the 0.06 floor. Contact is dist^2 <= (bloomR + frostR)^2, so a bloom catches anything already standing on the point it opens at. Blooms never merge, never boost each other and never extend each other. Frost inside two blooms at once is resolved by the lower bloom id only, scores once and opens one chain bloom. Blooms opened during a tick's contact pass are NOT tested until the following tick, chains stop at depth 6 (frost caught by a depth 6 bloom is still thawed and still scored, it simply opens nothing), and at most 64 blooms exist at once, so a cascade is bounded three separate ways. FROST. Everything moves by x += vx; y += vy once per tick and bounces off the walls at x = 0.02 and 0.98. A drifter flips its vx every 42 ticks. A splitter carries splitAtY in 0.45..0.72 and, on crossing it, is removed for NO score and replaced by two children with vx -0.0022 and +0.0022, so catching it whole (200) beats catching both children (160). A hollow takes two hits and is untouchable for 10 ticks after the first, so it can take at most one hit per 10 ticks however many blooms overlap it. A seed pod is harmless: it never sets a bell to rest and reaching the ground costs nothing. Every piece of frost carries threatBell, the index of the glowbell a straight line from its current position and drift would reach, or null. It deliberately ignores sway and wall bounces, so it is the same imperfect read a human is shown. Only 0.6 of the width is covered by bells, so 40 percent of the ground is safe and IGNORING harmless frost is most of the skill. MOTES. Each wave grants a fixed number that does not carry over; leftovers are paid out as points on the clear. There is an 8 tick cooldown between sends and at most 3 motes in flight. At exactly zero motes, while frost or spawns remain, one free mote kindles every 150 ticks, at most 3 a wave. WAVES. A wave ends only when all four hold: no spawns remain, no frost is aloft, no mote is in flight, and no bloom can still thaw. A fading bloom does not hold it up, but a mote in flight does, so a mote sent at the last shard always completes its arc. The tally freezes the field for 90 ticks, then the next wave starts after a 45 tick pause. On a clear, if any bell is resting and either the wave number is below 10 or the wave was perfect, the resting bell nearest the middle relights, in the order 2, 1, 3, 0, 4. Waves get faster, denser and larger without limit. ARRIVAL SHAPE. Frost does not arrive evenly. A spawn either stands alone or opens a GROUP, and the members of a group enter within a short gap of each other inside a 0.2 wide window around wherever the one before it entered, so the sky keeps presenting loose diagonals of three to six pieces, separated by a longer lull. That shape is the whole game: a diagonal is the only arrangement a cascade can walk down, so the highest scoring read is where a diagonal is GOING to be, not where any single piece of frost is now. TICK ORDER, exact and load bearing: 1 clear events for the action; 2 in 'waiting' or 'over', return unchanged; 3 in 'waveclear', count down and start the next wave at zero; 4 counters; 5 kindle; 6 motes move and any that arrive open a depth 0 bloom; 7 blooms age, and an expiring one pays its cluster bonus; 8 frost moves, sways, bounces and splits, and threatBell is recomputed from scratch; 9 the contact pass, blooms ascending id and inside each frost ascending id; 10 frost at or past y = 1.19 is removed and may set a bell resting; 11 at most one spawn; 12 the wave clear test, then the run-end test, which is last and overrides. Everything is seeded and deterministic: the same seed and the same ordered action list reproduce the same state exactly.
Action grammar
{
"type": "object",
"oneOf": [
{
"properties": {
"type": {
"const": "start"
}
},
"required": [
"type"
]
},
{
"properties": {
"type": {
"const": "tick"
},
"ticks": {
"type": "integer",
"description": "Optional, defaults to 1, clamped to 1..120."
}
},
"required": [
"type"
]
},
{
"properties": {
"type": {
"const": "launch"
},
"x": {
"type": "number",
"description": "Width units, 0..1. Clamped to 0.03..0.97."
},
"y": {
"type": "number",
"description": "Width units. Must be 1.12 or less. Clamped up to a floor of 0.05."
}
},
"required": [
"type",
"x",
"y"
]
}
]
}Start a game
curl -s "https://gameboard.gg/api/games/thawlight/init?seed=7"
Then POST { state, action } to https://gameboard.gg/api/games/thawlight/action, carrying state forward each call. See the API overview for the full loop.