claudedino 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/.prettierignore +3 -0
  2. package/.prettierrc +10 -0
  3. package/CLAUDE.md +91 -0
  4. package/LICENSE +21 -0
  5. package/README.md +103 -0
  6. package/dist/app.d.ts +4 -0
  7. package/dist/app.d.ts.map +1 -0
  8. package/dist/app.js +16 -0
  9. package/dist/app.js.map +1 -0
  10. package/dist/cli.d.ts +3 -0
  11. package/dist/cli.d.ts.map +1 -0
  12. package/dist/cli.js +19 -0
  13. package/dist/cli.js.map +1 -0
  14. package/dist/game/dino-game.d.ts +8 -0
  15. package/dist/game/dino-game.d.ts.map +1 -0
  16. package/dist/game/dino-game.js +199 -0
  17. package/dist/game/dino-game.js.map +1 -0
  18. package/dist/game/obstacles.d.ts +6 -0
  19. package/dist/game/obstacles.d.ts.map +1 -0
  20. package/dist/game/obstacles.js +60 -0
  21. package/dist/game/obstacles.js.map +1 -0
  22. package/dist/game/physics.d.ts +9 -0
  23. package/dist/game/physics.d.ts.map +1 -0
  24. package/dist/game/physics.js +125 -0
  25. package/dist/game/physics.js.map +1 -0
  26. package/dist/game/renderer.d.ts +12 -0
  27. package/dist/game/renderer.d.ts.map +1 -0
  28. package/dist/game/renderer.js +156 -0
  29. package/dist/game/renderer.js.map +1 -0
  30. package/dist/game/sprites.d.ts +8 -0
  31. package/dist/game/sprites.d.ts.map +1 -0
  32. package/dist/game/sprites.js +69 -0
  33. package/dist/game/sprites.js.map +1 -0
  34. package/dist/game/types.d.ts +78 -0
  35. package/dist/game/types.d.ts.map +1 -0
  36. package/dist/game/types.js +56 -0
  37. package/dist/game/types.js.map +1 -0
  38. package/dist/hooks/setup.d.ts +3 -0
  39. package/dist/hooks/setup.d.ts.map +1 -0
  40. package/dist/hooks/setup.js +74 -0
  41. package/dist/hooks/setup.js.map +1 -0
  42. package/dist/index.d.ts +5 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +4 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/platform/detect.d.ts +15 -0
  47. package/dist/platform/detect.d.ts.map +1 -0
  48. package/dist/platform/detect.js +44 -0
  49. package/dist/platform/detect.js.map +1 -0
  50. package/dist/platform/split.d.ts +3 -0
  51. package/dist/platform/split.d.ts.map +1 -0
  52. package/dist/platform/split.js +40 -0
  53. package/dist/platform/split.js.map +1 -0
  54. package/dist/state/watcher.d.ts +6 -0
  55. package/dist/state/watcher.d.ts.map +1 -0
  56. package/dist/state/watcher.js +44 -0
  57. package/dist/state/watcher.js.map +1 -0
  58. package/docs/design-spec.md +562 -0
  59. package/docs/implementation-plan.md +602 -0
  60. package/eslint.config.mjs +96 -0
  61. package/package.json +38 -0
  62. package/scripts/check-all.sh +91 -0
  63. package/scripts/check-suppressions.sh +39 -0
  64. package/src/app.tsx +19 -0
  65. package/src/cli.ts +25 -0
  66. package/src/game/dino-game.tsx +279 -0
  67. package/src/game/obstacles.ts +81 -0
  68. package/src/game/physics.ts +171 -0
  69. package/src/game/renderer.ts +230 -0
  70. package/src/game/sprites.ts +84 -0
  71. package/src/game/types.ts +95 -0
  72. package/src/hooks/setup.ts +105 -0
  73. package/src/index.ts +4 -0
  74. package/src/platform/detect.ts +50 -0
  75. package/src/platform/split.ts +44 -0
  76. package/src/state/watcher.ts +53 -0
  77. package/tsconfig.json +35 -0
@@ -0,0 +1,3 @@
1
+ dist
2
+ node_modules
3
+ package-lock.json
package/.prettierrc ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "semi": true,
3
+ "singleQuote": false,
4
+ "tabWidth": 2,
5
+ "trailingComma": "all",
6
+ "printWidth": 100,
7
+ "bracketSpacing": true,
8
+ "arrowParens": "always",
9
+ "endOfLine": "lf"
10
+ }
package/CLAUDE.md ADDED
@@ -0,0 +1,91 @@
1
+ # ClaudeDino
2
+
3
+ ## Project Overview
4
+
5
+ ASCII dino runner game that runs alongside Claude Code in a split terminal pane. Built with Ink (React for terminals) and TypeScript. Uses Claude Code hooks for state detection.
6
+
7
+ ## Tech Stack
8
+
9
+ - TypeScript (strict mode, all flags enabled)
10
+ - Ink 7 (React for terminals)
11
+ - React 19
12
+ - Node.js 22+
13
+ - ESM modules
14
+
15
+ ## Quick Start
16
+
17
+ ```bash
18
+ npm install
19
+ npm run build
20
+ node dist/cli.js --attach
21
+ # In another terminal: echo working > $TMPDIR/claudedino-state
22
+ # Game starts. echo idle > $TMPDIR/claudedino-state to pause.
23
+ ```
24
+
25
+ ## Commands
26
+
27
+ - `npm run build` -- compile TypeScript to dist/
28
+ - `npm run check:all` -- run all 5 checks (typecheck, suppression scan, ESLint, Prettier, tsconfig audit)
29
+ - `npm run lint` -- ESLint only
30
+ - `npm run lint:fix` -- ESLint with auto-fix
31
+ - `npm run format` -- Prettier check
32
+ - `npm run format:fix` -- Prettier write
33
+ - `npm run typecheck` -- TypeScript type check (no emit)
34
+
35
+ ## Pre-Commit Workflow
36
+
37
+ 1. Run `npm run check:all`
38
+ 2. Zero warnings, zero errors, zero suppressions
39
+
40
+ ## Linting
41
+
42
+ ESLint with three strict rulesets stacked:
43
+
44
+ - `eslint/all` (every built-in rule)
45
+ - `typescript-eslint/strictTypeChecked` + `stylisticTypeChecked`
46
+ - `unicorn/all`
47
+
48
+ Key enforced rules:
49
+
50
+ - `explicit-function-return-type` -- all functions must have return types
51
+ - `consistent-type-imports` -- use `import type` for type-only imports
52
+ - `naming-convention` -- camelCase default, PascalCase for types/enums/React components, UPPER_CASE for const variables
53
+ - `no-console` -- use `process.stdout.write()` instead
54
+ - Zero lint suppressions allowed (checked by scripts/check-suppressions.sh)
55
+
56
+ ## Architecture
57
+
58
+ ```
59
+ src/
60
+ cli.ts -- Entry point, arg parsing, auto-split
61
+ app.tsx -- Root Ink component, connects state watcher to game
62
+ game/
63
+ types.ts -- All enums, interfaces, constants
64
+ sprites.ts -- Sprite definitions (2D string arrays)
65
+ physics.ts -- Jump, gravity, crouch, collision (pure functions)
66
+ obstacles.ts -- Spawning, movement, difficulty progression
67
+ renderer.ts -- Frame buffer creation, sprite drawing, text output
68
+ dino-game.tsx -- Main game component, state machine, game loop
69
+ hooks/
70
+ setup.ts -- Claude Code hook configuration in ~/.claude/settings.json
71
+ platform/
72
+ detect.ts -- OS and terminal detection
73
+ split.ts -- Cross-platform terminal splitting
74
+ state/
75
+ watcher.ts -- Watches temp state file for working/idle changes
76
+ ```
77
+
78
+ ## Key Patterns
79
+
80
+ - All game logic functions are pure (return new objects, no mutation)
81
+ - `noUncheckedIndexedAccess` is enabled -- guard all array access with undefined checks
82
+ - Use `execFileSync` not `execSync` to avoid shell injection
83
+ - React components use function declarations with PascalCase names
84
+ - Ink's `useInput` for keyboard, `useStdout` for terminal dimensions
85
+
86
+ ## Gotchas
87
+
88
+ - `ink-use-stdout-dimensions` is CJS and incompatible with ink v7 (ESM with top-level await). Use `useStdout()` from ink directly.
89
+ - State file path is `os.tmpdir() + "/claudedino-state"`. On macOS this is `/var/folders/.../claudedino-state`, not `/tmp/`.
90
+ - Hook commands in `~/.claude/settings.json` are tagged with `# claudedino` suffix for identification.
91
+ - Ink has no keyup event. Crouch detection works by resetting `crouchHeld` each tick and re-setting it when down arrow fires.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Furkan Erday
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,103 @@
1
+ # ClaudeDino
2
+
3
+ A tiny dino runner game that takes over the dead space in Claude Code's terminal while the AI is processing. Jump over cacti, dodge birds, rack up points. It pauses when Claude is done and waits for you to send another task before it lets you play again. Turns waiting time into game time and keeps your eyes where the work is happening.
4
+
5
+ ## Why This Exists
6
+
7
+ Every time Claude starts working on a task, you wait. And waiting is when your brain starts looking for something else to do. You check your phone. You open YouTube. You scroll X. You switch to a browser tab and forget you were even coding. Five minutes later Claude is done and you have no idea because you are watching a video about why socks disappear in the dryer.
8
+
9
+ ClaudeDino fixes this by giving you something to do right there in your terminal. A simple, dumb, addictive little dino game that only runs while Claude is working. The moment Claude finishes, the game pauses. The only way to keep playing is to give Claude another task. So instead of losing focus, you stay on the screen, you stay in the flow, and you actually notice when Claude is done.
10
+
11
+ It is not productivity software. It is an anti-distraction trick disguised as a game.
12
+
13
+ ## How It Works
14
+
15
+ ClaudeDino runs in a separate terminal pane right below Claude Code. When you type `claudedino`, it automatically splits your terminal and starts the game in the bottom half. No manual setup needed.
16
+
17
+ Behind the scenes, it uses Claude Code's **hooks system** to know when Claude is working and when Claude is idle. When Claude starts processing your task, the game starts with a 3-2-1 countdown. When Claude finishes, the game pauses instantly. Your score and position are saved so you pick up exactly where you left off next time.
18
+
19
+ ```
20
+ ┌─────────────────────────────────────────────────────────────┐
21
+ │ │
22
+ │ Claude Code (normal, unmodified) │
23
+ │ │
24
+ │ > Working on your task... │
25
+ │ ■ Running tests │
26
+ │ │
27
+ │ > type bar │
28
+ ├─────────────────────────────────────────────────────────────┤
29
+ │ Score: 00042 HI: 00891 │
30
+ │ ░░ │
31
+ │ ██ │
32
+ │ ██ ████ ██ │
33
+ │ ████ █ ████ ██ │
34
+ │ █ █ ██ ████ │
35
+ │ ████ ████ ████ │
36
+ │ ═══════════████═════════════════════════════════════════════ │
37
+ └─────────────────────────────────────────────────────────────┘
38
+ ```
39
+
40
+ ## Installation
41
+
42
+ ```bash
43
+ npm install -g claudedino
44
+ ```
45
+
46
+ ## Usage
47
+
48
+ Just run it in your terminal while Claude Code is open:
49
+
50
+ ```bash
51
+ claudedino
52
+ ```
53
+
54
+ That is it. The app will:
55
+
56
+ 1. Detect your terminal (iTerm2, Windows Terminal, tmux, etc.)
57
+ 2. Automatically split the pane and launch the game in the bottom half
58
+ 3. Configure Claude Code hooks so the game knows when Claude is working
59
+ 4. Start playing the moment Claude gets a task
60
+
61
+ If auto-split does not work on your setup, you can split manually and run:
62
+
63
+ ```bash
64
+ claudedino --attach
65
+ ```
66
+
67
+ ## Controls
68
+
69
+ | Key | Action |
70
+ | --------------------- | ------------------ |
71
+ | `Space` or `Up Arrow` | Jump over cacti |
72
+ | `Down Arrow` | Crouch under birds |
73
+
74
+ Controls only work while the game is active (Claude is working). When Claude is idle, the game is paused and your keyboard works normally.
75
+
76
+ ## Game Rules
77
+
78
+ - You are a dino running through an endless desert
79
+ - Jump over cacti, duck under birds
80
+ - Speed increases as your score goes up
81
+ - Birds start appearing after score 200
82
+ - When Claude finishes a task, the game pauses with your score saved
83
+ - When Claude starts the next task, the game resumes after a 3-2-1 countdown
84
+ - If you hit an obstacle, game over. Score resets next round.
85
+ - High score is tracked for the session
86
+
87
+ ## Platform Support
88
+
89
+ | OS | Terminal | Auto-Split |
90
+ | ------- | ---------------- | ------------------------ |
91
+ | macOS | iTerm2 | Yes |
92
+ | macOS | Terminal.app | Yes |
93
+ | Windows | Windows Terminal | Yes |
94
+ | Linux | tmux | Yes |
95
+ | Any | Manual split | Fallback with `--attach` |
96
+
97
+ ## How the State Detection Works
98
+
99
+ ClaudeDino hooks into Claude Code's event system by adding hooks to your `~/.claude/settings.json`. These hooks write a tiny state flag whenever Claude starts or finishes processing. The game watches this flag and reacts instantly.
100
+
101
+ ## License
102
+
103
+ MIT
package/dist/app.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import React from "react";
2
+ declare function App(): React.ReactElement;
3
+ export default App;
4
+ //# sourceMappingURL=app.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAMnD,iBAAS,GAAG,IAAI,KAAK,CAAC,YAAY,CAUjC;AAED,eAAe,GAAG,CAAC"}
package/dist/app.js ADDED
@@ -0,0 +1,16 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import React, { useEffect, useState } from "react";
3
+ import { ClaudeState } from "./game/types.js";
4
+ import DinoGame from "./game/dino-game.js";
5
+ import { initStateFile, watchState } from "./state/watcher.js";
6
+ function App() {
7
+ const [claudeState, setClaudeState] = useState(ClaudeState.Idle);
8
+ useEffect(() => {
9
+ initStateFile();
10
+ const cleanup = watchState(setClaudeState);
11
+ return cleanup;
12
+ }, []);
13
+ return _jsx(DinoGame, { claudeState: claudeState });
14
+ }
15
+ export default App;
16
+ //# sourceMappingURL=app.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEnD,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,QAAQ,MAAM,qBAAqB,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAE/D,SAAS,GAAG;IACV,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAc,WAAW,CAAC,IAAI,CAAC,CAAC;IAE9E,SAAS,CAAC,GAAG,EAAE;QACb,aAAa,EAAE,CAAC;QAChB,MAAM,OAAO,GAAG,UAAU,CAAC,cAAc,CAAC,CAAC;QAC3C,OAAO,OAAO,CAAC;IACjB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,KAAC,QAAQ,IAAC,WAAW,EAAE,WAAW,GAAI,CAAC;AAChD,CAAC;AAED,eAAe,GAAG,CAAC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ import React from "react";
3
+ import { render } from "ink";
4
+ import App from "./app.js";
5
+ import { setupHooks } from "./hooks/setup.js";
6
+ import { detectTerminal } from "./platform/detect.js";
7
+ import { trySplit } from "./platform/split.js";
8
+ const attachFlag = process.argv.includes("--attach");
9
+ if (!attachFlag) {
10
+ setupHooks();
11
+ const terminal = detectTerminal();
12
+ const didSplit = trySplit(terminal);
13
+ if (didSplit) {
14
+ process.exit(0);
15
+ }
16
+ process.stdout.write("Could not auto-split terminal. Running game inline.\n");
17
+ }
18
+ render(React.createElement(App));
19
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,MAAM,EAAE,MAAM,KAAK,CAAC;AAE7B,OAAO,GAAG,MAAM,UAAU,CAAC;AAC3B,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE/C,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;AAErD,IAAI,CAAC,UAAU,EAAE,CAAC;IAChB,UAAU,EAAE,CAAC;IACb,MAAM,QAAQ,GAAG,cAAc,EAAE,CAAC;IAClC,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAEpC,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,uDAAuD,CAAC,CAAC;AAChF,CAAC;AAED,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC"}
@@ -0,0 +1,8 @@
1
+ import React from "react";
2
+ import { ClaudeState } from "./types.js";
3
+ interface DinoGameProps {
4
+ claudeState: ClaudeState;
5
+ }
6
+ declare function DinoGame({ claudeState }: DinoGameProps): React.ReactNode;
7
+ export default DinoGame;
8
+ //# sourceMappingURL=dino-game.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dino-game.d.ts","sourceRoot":"","sources":["../../src/game/dino-game.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAG3D,OAAO,EAQL,WAAW,EAOZ,MAAM,YAAY,CAAC;AAgBpB,UAAU,aAAa;IACrB,WAAW,EAAE,WAAW,CAAC;CAC1B;AA2BD,iBAAS,QAAQ,CAAC,EAAE,WAAW,EAAE,EAAE,aAAa,GAAG,KAAK,CAAC,SAAS,CAqNjE;AAED,eAAe,QAAQ,CAAC"}
@@ -0,0 +1,199 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import React, { useEffect, useRef, useState } from "react";
3
+ import { Box, Text, useInput, useStdout } from "ink";
4
+ import { BASE_SPEED, CLOUD_CULL_X, CLOUD_MAX_Y, CLOUD_SPAWN_PROBABILITY, CLOUD_SPEED_FACTOR, COUNTDOWN_SECONDS, ClaudeState, DinoPose, GameState, MAX_CLOUDS, SCORE_INCREMENT_TICKS, SPAWN_X_OFFSET, TICK_INTERVAL_MS, } from "./types.js";
5
+ import { applyGravity, checkCollision, getDefaultDinoY, startCrouch, startJump, stopCrouch, } from "./physics.js";
6
+ import { getCurrentSpeed, moveObstacles, shouldSpawnObstacle, spawnObstacle } from "./obstacles.js";
7
+ import { renderFrame } from "./renderer.js";
8
+ function noop() {
9
+ // intentionally empty
10
+ }
11
+ function createInitialWorld(highScore) {
12
+ return {
13
+ dino: {
14
+ pose: DinoPose.Run1,
15
+ y: getDefaultDinoY(),
16
+ velocityY: 0,
17
+ isCrouching: false,
18
+ isJumping: false,
19
+ },
20
+ obstacles: [],
21
+ clouds: [],
22
+ score: 0,
23
+ highScore,
24
+ speed: BASE_SPEED,
25
+ groundOffset: 0,
26
+ tickCount: 0,
27
+ distanceSinceLastObstacle: 0,
28
+ };
29
+ }
30
+ function DinoGame({ claudeState }) {
31
+ const { stdout } = useStdout();
32
+ const columns = stdout.columns;
33
+ const [gameState, setGameState] = useState(GameState.Hidden);
34
+ const [world, setWorld] = useState(() => createInitialWorld(0));
35
+ const [countdown, setCountdown] = useState(COUNTDOWN_SECONDS);
36
+ const worldRef = useRef(world);
37
+ const gameStateRef = useRef(gameState);
38
+ const inputRef = useRef({ jump: false, crouchHeld: false });
39
+ // Keep refs in sync
40
+ worldRef.current = world;
41
+ gameStateRef.current = gameState;
42
+ // ---------------------------------------------------------------------------
43
+ // Claude state effect
44
+ // ---------------------------------------------------------------------------
45
+ useEffect(() => {
46
+ const current = gameStateRef.current;
47
+ if (claudeState === ClaudeState.Working &&
48
+ (current === GameState.Hidden ||
49
+ current === GameState.Paused ||
50
+ current === GameState.GameOver)) {
51
+ if (current === GameState.GameOver) {
52
+ const previousHighScore = worldRef.current.highScore;
53
+ setWorld(createInitialWorld(previousHighScore));
54
+ }
55
+ setCountdown(COUNTDOWN_SECONDS);
56
+ setGameState(GameState.Countdown);
57
+ }
58
+ if (claudeState === ClaudeState.Idle &&
59
+ (current === GameState.Playing || current === GameState.Countdown)) {
60
+ setGameState(GameState.Paused);
61
+ }
62
+ }, [claudeState]);
63
+ // ---------------------------------------------------------------------------
64
+ // Countdown effect
65
+ // ---------------------------------------------------------------------------
66
+ useEffect(() => {
67
+ if (gameState !== GameState.Countdown) {
68
+ return noop;
69
+ }
70
+ const interval = setInterval(() => {
71
+ setCountdown((previous) => {
72
+ if (previous <= 1) {
73
+ setGameState(GameState.Playing);
74
+ return COUNTDOWN_SECONDS;
75
+ }
76
+ return previous - 1;
77
+ });
78
+ }, 1000);
79
+ return () => {
80
+ clearInterval(interval);
81
+ };
82
+ }, [gameState]);
83
+ // ---------------------------------------------------------------------------
84
+ // Game loop effect
85
+ // ---------------------------------------------------------------------------
86
+ useEffect(() => {
87
+ if (gameState !== GameState.Playing) {
88
+ return noop;
89
+ }
90
+ const interval = setInterval(() => {
91
+ setWorld((previous) => {
92
+ // 1. Read input
93
+ const input = inputRef.current;
94
+ const wantJump = input.jump;
95
+ const wantCrouch = input.crouchHeld;
96
+ // Reset input for next tick
97
+ input.jump = false;
98
+ input.crouchHeld = false;
99
+ // 2. Apply jump/crouch
100
+ let dino = previous.dino;
101
+ if (wantJump) {
102
+ dino = startJump(dino);
103
+ }
104
+ if (wantCrouch && !dino.isJumping) {
105
+ dino = startCrouch(dino);
106
+ }
107
+ else if (!wantCrouch && dino.isCrouching) {
108
+ dino = stopCrouch(dino);
109
+ }
110
+ // 3. Apply gravity
111
+ dino = applyGravity(dino);
112
+ // 4. Move obstacles
113
+ const speed = getCurrentSpeed(previous.score);
114
+ let obstacles = moveObstacles(previous.obstacles, speed);
115
+ // 5. Spawn obstacles
116
+ let distanceSinceLastObstacle = previous.distanceSinceLastObstacle + speed;
117
+ if (shouldSpawnObstacle({ ...previous, distanceSinceLastObstacle }, columns)) {
118
+ obstacles = [...obstacles, spawnObstacle(previous, columns)];
119
+ distanceSinceLastObstacle = 0;
120
+ }
121
+ // 6. Check collisions
122
+ for (const obstacle of obstacles) {
123
+ if (checkCollision(dino, obstacle)) {
124
+ const finalScore = previous.score;
125
+ const updatedHighScore = Math.max(finalScore, previous.highScore);
126
+ setGameState(GameState.GameOver);
127
+ return {
128
+ ...previous,
129
+ dino,
130
+ obstacles,
131
+ score: finalScore,
132
+ highScore: updatedHighScore,
133
+ speed,
134
+ distanceSinceLastObstacle,
135
+ };
136
+ }
137
+ }
138
+ // 7. Move/spawn clouds
139
+ let clouds = previous.clouds
140
+ .map((cloud) => ({
141
+ ...cloud,
142
+ x: cloud.x - speed * CLOUD_SPEED_FACTOR,
143
+ }))
144
+ .filter((cloud) => cloud.x >= CLOUD_CULL_X);
145
+ if (clouds.length < MAX_CLOUDS && Math.random() < CLOUD_SPAWN_PROBABILITY) {
146
+ clouds = [
147
+ ...clouds,
148
+ {
149
+ x: columns + SPAWN_X_OFFSET,
150
+ y: Math.floor(Math.random() * CLOUD_MAX_Y),
151
+ },
152
+ ];
153
+ }
154
+ // 8. Increment score
155
+ const tickCount = previous.tickCount + 1;
156
+ const score = tickCount % SCORE_INCREMENT_TICKS === 0 ? previous.score + 1 : previous.score;
157
+ // 9. Update ground offset
158
+ const groundOffset = (previous.groundOffset + speed) % columns;
159
+ return {
160
+ dino,
161
+ obstacles,
162
+ clouds,
163
+ score,
164
+ highScore: Math.max(score, previous.highScore),
165
+ speed,
166
+ groundOffset,
167
+ tickCount,
168
+ distanceSinceLastObstacle,
169
+ };
170
+ });
171
+ }, TICK_INTERVAL_MS);
172
+ return () => {
173
+ clearInterval(interval);
174
+ };
175
+ }, [gameState, columns]);
176
+ // ---------------------------------------------------------------------------
177
+ // Input handling
178
+ // ---------------------------------------------------------------------------
179
+ useInput((input, key) => {
180
+ if (input === " " || key.upArrow) {
181
+ inputRef.current.jump = true;
182
+ }
183
+ if (key.downArrow) {
184
+ inputRef.current.crouchHeld = true;
185
+ }
186
+ }, { isActive: gameState === GameState.Playing });
187
+ // ---------------------------------------------------------------------------
188
+ // Render
189
+ // ---------------------------------------------------------------------------
190
+ const frame = renderFrame({
191
+ world,
192
+ width: columns,
193
+ gameState,
194
+ countdownValue: countdown,
195
+ });
196
+ return (_jsx(Box, { flexDirection: "column", children: _jsx(Text, { children: frame }) }));
197
+ }
198
+ export default DinoGame;
199
+ //# sourceMappingURL=dino-game.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dino-game.js","sourceRoot":"","sources":["../../src/game/dino-game.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { type GameWorld, type Obstacle } from "./types.js";
2
+ export declare function getCurrentSpeed(score: number): number;
3
+ export declare function shouldSpawnObstacle(world: GameWorld, terminalWidth: number): boolean;
4
+ export declare function spawnObstacle(world: GameWorld, terminalWidth: number): Obstacle;
5
+ export declare function moveObstacles(obstacles: Obstacle[], speed: number): Obstacle[];
6
+ //# sourceMappingURL=obstacles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"obstacles.d.ts","sourceRoot":"","sources":["../../src/game/obstacles.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,SAAS,EACd,KAAK,QAAQ,EAMd,MAAM,YAAY,CAAC;AAkBpB,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGrD;AAkBD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAOpF;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,GAAG,QAAQ,CAY/E;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,QAAQ,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,QAAQ,EAAE,CAU9E"}
@@ -0,0 +1,60 @@
1
+ import { BASE_SPEED, DIFFICULTY_TABLE, MIN_OBSTACLE_GAP, ObstacleType, SPAWN_X_OFFSET, } from "./types.js";
2
+ function getCurrentTier(score) {
3
+ for (const tier of DIFFICULTY_TABLE) {
4
+ if (score <= tier.maxScore) {
5
+ return tier;
6
+ }
7
+ }
8
+ // Fallback to last tier (unreachable since last tier has Infinity maxScore)
9
+ const lastTier = DIFFICULTY_TABLE.at(-1);
10
+ if (lastTier === undefined) {
11
+ throw new Error("DIFFICULTY_TABLE is empty");
12
+ }
13
+ return lastTier;
14
+ }
15
+ export function getCurrentSpeed(score) {
16
+ const tier = getCurrentTier(score);
17
+ return BASE_SPEED * tier.speedMultiplier;
18
+ }
19
+ function getAvailableObstacleTypes(score) {
20
+ const tier = getCurrentTier(score);
21
+ const types = [ObstacleType.SmallCactus];
22
+ if (score >= 100) {
23
+ types.push(ObstacleType.LargeCactus, ObstacleType.CactusGroup);
24
+ }
25
+ if (tier.birdsEnabled) {
26
+ types.push(ObstacleType.BirdHigh, ObstacleType.BirdMid);
27
+ }
28
+ return types;
29
+ }
30
+ export function shouldSpawnObstacle(world, terminalWidth) {
31
+ const tier = getCurrentTier(world.score);
32
+ const speed = BASE_SPEED * tier.speedMultiplier;
33
+ const minGap = Math.max(MIN_OBSTACLE_GAP, Math.round(40 / speed));
34
+ const spawnThreshold = minGap + Math.round((Math.random() * 15) / tier.spawnRateMultiplier);
35
+ return world.distanceSinceLastObstacle > spawnThreshold && terminalWidth > 0;
36
+ }
37
+ export function spawnObstacle(world, terminalWidth) {
38
+ const types = getAvailableObstacleTypes(world.score);
39
+ const randomIndex = Math.floor(Math.random() * types.length);
40
+ const type = types[randomIndex] ?? ObstacleType.SmallCactus;
41
+ return {
42
+ type,
43
+ position: {
44
+ x: terminalWidth + SPAWN_X_OFFSET,
45
+ y: 0,
46
+ },
47
+ };
48
+ }
49
+ export function moveObstacles(obstacles, speed) {
50
+ return obstacles
51
+ .map((obstacle) => ({
52
+ ...obstacle,
53
+ position: {
54
+ ...obstacle.position,
55
+ x: obstacle.position.x - speed,
56
+ },
57
+ }))
58
+ .filter((obstacle) => obstacle.position.x >= -10);
59
+ }
60
+ //# sourceMappingURL=obstacles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"obstacles.js","sourceRoot":"","sources":["../../src/game/obstacles.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { type DinoState, type Obstacle, ObstacleType } from "./types.js";
2
+ export declare function getDefaultDinoY(): number;
3
+ export declare function startJump(dino: DinoState): DinoState;
4
+ export declare function startCrouch(dino: DinoState): DinoState;
5
+ export declare function stopCrouch(dino: DinoState): DinoState;
6
+ export declare function applyGravity(dino: DinoState): DinoState;
7
+ export declare function getObstacleY(type: ObstacleType): number;
8
+ export declare function checkCollision(dino: DinoState, obstacle: Obstacle): boolean;
9
+ //# sourceMappingURL=physics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"physics.d.ts","sourceRoot":"","sources":["../../src/game/physics.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,SAAS,EACd,KAAK,QAAQ,EAMb,YAAY,EACb,MAAM,YAAY,CAAC;AAmCpB,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,CAWpD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,CAWtD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,CAOrD;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,CAwBvD;AAwBD,wBAAgB,YAAY,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CAoBvD;AASD,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAU3E"}