Worktree games asteroids blackjack - #368
Merged
Merged
Conversation
`/games asteroids` and `/games blackjack` (also `21`), sharing the frame, the key decoder and the driver every other game in the cabinet already uses. Asteroids is the real-time one. The 1979 cabinet did not have to care that a terminal cell is twice as tall as it is wide; everything here measures distance in columns and counts a row as two, which is the only reason the rocks come out round and a collision happens where the screen showed it. Rocks break into two of the next size down, small ones pay most, the screen has no edges, and the ship blinks while it cannot be hit. Blackjack deals before the frame lands — no bet prompt, no options screen. Hit, stand, double and split, dealer stands on all 17s, blackjack pays 3:2, and a split 21 is twenty-one rather than a natural, which is the rule most home versions get wrong. Between hands the arrows are the chips; the stack is 100 and the game ends when it is gone. Both are pure the way the rest of the arcade is, so test/games.test.mjs plays them: it wrecks a ship, splits rocks down to nothing, walks a stack to broke and proves the box does not change width between hands. Fixes two things the letter games had been quietly losing to the shared decoder. `h j k l` became arrows for every game, and `r` was always a restart — so hangman could not be told the letters in `refactor`, and `h` could never mean hit. Games that read letters now opt out with `vim: false` and `restartable: false`; arrows are untouched, so blackjack still bets with them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`/games stagedive` (also `dive`, `runner`, `stage`). You are running the barricade: hop the monitor wedges, clear the amp stacks, duck the crowdsurfers, take the picks. One mistake is the whole run. A side-scroller in a terminal is really a scrolling list — the runner never moves along the x axis at all, everything else slides left past a fixed column. That is why speed is measured in columns per tick, and why the gap between hazards is multiplied by it: a jump lasts a fixed number of ticks, so a fair gap has to get longer as the stage gets faster. Without that the stage eventually outruns the jump and the game stops being losable-by-mistake and starts being unfair. Three things to read and two ways to answer. Wedges and stacks are jumped, and the stack is two rows tall so it takes the height of the jump rather than the start of it. A crowdsurfer sits at head height: crouch and it goes over you, because a crouched runner is one row and a standing one is two. `↓` in mid-air is a slam instead, which is how you save a jump you took too early. Every hazard's hitbox is its own art, so a hazard cannot hit wider than it is drawn — the oldest bug in the genre. The tests play it: a jump at six columns clears a wedge and a jump one column late still wrecks on a stack, and a player with reflexes runs 25 seeded stages to the end while standing still and holding jump both die on every one of them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Five more for the cabinet, on the same frame, decoder and driver as the rest. Space Invaders is the one with the joke the original hardware told by accident: cadence() divides by the number left alive, so forty of them crawl and the last one sprints. Bunkers take two hits a cell from either side — including yours, which is the whole reason stepping out from under your own cover is a skill. Breakout's paddle is a steering wheel rather than a wall: where the ball lands on it decides the angle it leaves at, which is what lets you dig a channel up the side. Pong's machine waits until the ball crosses the halfway line and then moves too slowly to reach a corner from the middle, so a flat return it always gets and a sharp one it never does. Tank searches the yard rather than facing you and driving: breadth-first over 546 cells, every decision. "Turn towards the enemy" grinds into the same block for ever, and the first version of this duel finished 0–0 after twenty thousand ticks with neither tank ever having found the other. Spy Hunter's road is generated a row at a time from the row before it, with the bend pulled back towards the middle in proportion to how far out it already is. A plain random walk parks the road against one edge and leaves it there, which reads as a bug rather than a road. Three bugs worth naming, all found by playing them without a terminal: - The invader ranks were two rows apart, which made the fleet nine rows tall on a board with eleven above the bunkers. It "landed" after two drops and no wave was survivable by anybody. One row per rank, and an aiming player now always dies to the bombs instead — which is the test. - Shots that move faster than their target is tall skipped straight through it. Both the cannon and the car now travel in sub-steps and check what they pass. - The tank yard's size was declared next to the map it described, and disagreed with it. It is derived from the map now. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Centipede's body is not a linked list, it is a bag of segments that each obey the same rule: walk sideways, and when something is in the way drop a row and turn round. Modelled that way a shot to the middle needs no surgery — you delete one segment and the ones behind it carry on, which is exactly what splitting looks like. Every piece you shoot leaves a mushroom where it fell, so the field thickens as you play and the next wave is harder for free. The spider is in there because without it the bottom of the board is safe: the centipede only reaches your row on the sweeps it happens to end on, and a player who never moves at all survived three thousand ticks. It comes in from the side, bounces diagonally through your strip eating mushrooms, and is worth more than anything else on the board. Standing still now loses on every seed, which is the test. Frogger is the inversion: on the road, being on something is death; on the river, being on nothing is. The log carries you, including off the end of the world, which is the lesson every player learns twice. Landing on the bank between the homes is a loss and so is a home you have already filled. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Dig Dug has no pathfinding, and that is the design: a monster walks the tunnel it is in and turns towards you at a junction, so the shape you dig is the shape of the fight. Dig one straight line and they queue up behind you. Once in a while one gives up on the tunnels and comes straight through the ground, because without that a player who digs a single deep hole is untouchable. The harpoon only travels down open tunnel, three pumps burst a monster, and walking away drops it — so the pump is a commitment, not a button. Kong's girders are flat. The originals slope, and a slope is the one thing a terminal row cannot honestly draw; faking it with half-blocks makes a board that looks like the arcade and plays like a bug report. What the slope *does* is kept: each girder has a direction, they alternate, and the barrel chute sits at the far end of each one, so barrels cross a whole girder before they drop. Two rounds of that layout were unplayable before this one, both found by playing it without a terminal: - Barrels flipped direction when they landed, which sent them back the way they came and straight off the board. Nothing ever reached the lower girders and a player who never moved could not be hit at all. - With one ladder per girder, the only way up was the chute the barrels come down — a coin toss you cannot jump out of. Each girder has a second ladder now, and a climbing player reaches level 21 before the pace catches them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Pitfall's vine is what stops it being another jumping game. A jump is short and you commit to it the instant you press it; a swing is long and can only be started where a vine hangs. The spawner never puts down a pit without a vine over it, because a pit is five columns wide and a jump covers about five — a pit with no vine is a pit nobody gets past, and that is a test rather than a promise. Tuning it was the usual business of playing it without a terminal: the first swing was sixteen ticks, which carried you over all of a pit except its last column, and the first jump was seven, which cleared a log and landed you on its back half. Long enough now that a player who jumps and swings at the right moments is only ever beaten by the clock — which is the game Pitfall actually is. Choplifter keeps its world in world columns and only turns them into screen columns in render(). The map is three screens wide with the pad at the far left end, so "get them home" is a journey rather than a step, and the camera follows you. Four seats in the back, and a hit costs everybody in them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Excitebike is two mechanics and a side-scroller to hang them on. Turbo is free until it isn't — heat climbs while it is held and the engine seizes at the top of the gauge, so a fast race is one that cools off in the right places. And a jump is only as good as its landing: the nose drops on its own all the way down, and you pitch against it. That last part was inert in the first version. The ramp set your pitch and nothing changed it, so whatever you left the ramp with you landed on, and the landing — the second half of the game — could be ignored entirely. A rider who holds turbo and never touches the pitch now spills two hundred times over eight runs, where one who rides it properly spills none. That difference is the test. OutRun is the only board in the cabinet that fakes a third dimension, and it does it the way the eighties did: the road is not an object, it is a rule for drawing a row. roadHalf() and centreAt() are the entire renderer, and the same two functions decide what you have hit — so what is drawn and what is solid cannot drift apart. The bend is squared against distance, which is why it opens up towards the horizon and closes to nothing under your bumper, and why a corner throws you at the outside of itself. The clock is the opponent. The first checkpoint handed back 420 ticks for 260 of road, which meant a competent driver's clock only ever grew; it is 220 for 320 now, which is about fifty ticks of slack at full speed and none at all if you spin. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ThreatCrush Security Scan95 finding(s) HIGH/CRITICAL: 5 | MEDIUM: 39 | LOW: 51
…and 45 more. Full results in the Security tab. Snippets are redacted; ThreatCrush never prints matched credential material. |
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.