Visitar URL original
Build Snake Game with 60 Second Timer by cwchanap · Pull Request #5 · cwchanap/cetus · GitHub
Skip to content

Build Snake Game with 60 Second Timer - #5

Merged
cwchanap merged 26 commits into
mainfrom
claude/build-snake-game-011CUwxr6qupokc4q3b87irF
Nov 15, 2025
Merged

cwchanap merged 26 commits into
mainfrom
claude/build-snake-game-011CUwxr6qupokc4q3b87irF

Conversation

@cwchanap

@cwchanap cwchanap commented Nov 9, 2025 •

Copy link
Copy Markdown
Owner

Implemented classic snake game following existing game patterns:

  • 60-second time limit with real-time countdown
  • Score based on snake length (10 points per food eaten)
  • Game ends if snake hits wall or its own body
  • Food spawns every second
  • PixiJS-based renderer with grid and neon styling
  • WASD and arrow key controls
  • Pause/resume functionality
  • Score tracking with achievement integration

New files:

  • src/lib/games/snake/types.ts - Game state and type definitions
  • src/lib/games/snake/utils.ts - Helper functions for collision detection
  • src/lib/games/snake/game.ts - Core game logic and state management
  • src/lib/games/snake/renderer.ts - PixiJS rendering with visual effects
  • src/lib/games/snake/init.ts - Game initialization and event handling
  • src/pages/snake/index.astro - Game page with controls and statistics

Updated:

  • src/lib/games.ts - Added SNAKE game ID, metadata, and icon

Summary by CodeRabbit

  • New Features
    • Playable Snake mini-game with its own page, lifecycle, and UI.
    • Interactive 500×500 board with smooth animated grid, snake, and food rendering.
    • Live score, time remaining, length badges; pause/resume, restart, end controls and before-unload warning.
    • Keyboard (arrow/WASD) and on-screen controls; game-over overlay with final stats and achievements.
    • Snake added to the game list with icon, metadata, and homepage card.

Implemented classic snake game following existing game patterns:
- 60-second time limit with real-time countdown
- Score based on snake length (10 points per food eaten)
- Game ends if snake hits wall or its own body
- Food spawns every second
- PixiJS-based renderer with grid and neon styling
- WASD and arrow key controls
- Pause/resume functionality
- Score tracking with achievement integration

New files:
- src/lib/games/snake/types.ts - Game state and type definitions
- src/lib/games/snake/utils.ts - Helper functions for collision detection
- src/lib/games/snake/game.ts - Core game logic and state management
- src/lib/games/snake/renderer.ts - PixiJS rendering with visual effects
- src/lib/games/snake/init.ts - Game initialization and event handling
- src/pages/snake/index.astro - Game page with controls and statistics

Updated:
- src/lib/games.ts - Added SNAKE game ID, metadata, and icon
Copilot AI review requested due to automatic review settings November 9, 2025 08:14
@vercel

vercel Bot commented Nov 9, 2025 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
cetus Ready Ready Preview Comment Nov 15, 2025 8:00am

@coderabbitai

coderabbitai Bot commented Nov 9, 2025 •

Copy link
Copy Markdown

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds a full Snake mini-game: new types and utilities, PixiJS renderer, core game controller and UI update logic, an initializer exposing a lifecycle API, registry and card mapping updates, and a new /snake page with controls and overlay.

Changes

Cohort / File(s) Summary
Types & Utilities
src/lib/games/snake/types.ts, src/lib/games/snake/utils.ts
Add Snake-specific types (Position, Direction, SnakeSegment, Food, GameState, GameConstants, GameStats, callbacks) and utilities for colors, bounds/collision checks, position equality, food generation, movement, direction validation, and id generation.
Core Game Logic
src/lib/games/snake/game.ts
New game controller with GAME_CONSTANTS, createGameState, spawnFood, moveSnake, changeDirection, start/togglePause/reset/end flows, gameLoop, updateUI, and async score submission/onGameOver handling.
Renderer (PixiJS)
src/lib/games/snake/renderer.ts
New PixiJS renderer: RendererState, setupPixiJS, clearPixiJS, drawGrid, drawSnakeSegment, drawFood, and draw; manages canvas, graphics, redraw flag, and DOM error handling.
Initialization & Lifecycle
src/lib/games/snake/init.ts
Exports initSnakeGame() and SnakeGameInstance; wires renderer, state, UI buttons (Start/Pause/End/Restart), keyboard controls, beforeunload, render loop, and onGameOver UI updates.
Registry & Metadata
src/lib/games.ts
Add SNAKE = 'snake' to GameID, append Snake entry to GAMES with metadata, and add icon mapping in GAME_ICONS.
UI Routing / Card Mapping
src/components/ui/GameCard.astro, src/pages/index.astro
Add Snake case in game URL mapping (returns /snake) and update id mapping (snake: 11) used by index card conversion.
Snake Page
src/pages/snake/index.astro
New page with #snake-container (500×500), layout, controls, live badges (score/time/length), GameOverlay for final stats, and script to initialize/manage the Snake game instance and UI interactions.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant User
    participant Page as /snake Page
    participant Init as initSnakeGame()
    participant Renderer as PixiJS Renderer
    participant GameLogic as Snake Game Logic

    User->>Page: Navigate to /snake
    Page->>Init: DOMContentLoaded → initSnakeGame()
    Init->>Renderer: setupPixiJS(`#snake-container`, GAME_CONSTANTS)
    Renderer-->>Init: RendererState
    Init->>GameLogic: createGameState()
    Init->>Renderer: drawGrid()

    User->>Page: Click Start
    Page->>GameLogic: startGame(state, gameLoopFn)

    loop Game loop
        GameLogic->>GameLogic: moveSnake / spawnFood / detect collisions
        GameLogic->>Renderer: draw(state) when needsRedraw
        User->>GameLogic: Key input → changeDirection()
    end

    alt Game Over
        GameLogic->>Page: onGameOver -> show overlay
    end

    User->>Page: Click Restart
    Page->>Init: restart() -> resetGame(state)
    Init->>Renderer: clearPixiJS()
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Review focus:
    • src/lib/games/snake/game.ts — timing, pause/resume semantics, collision/timestamp correctness.
    • src/lib/games/snake/init.ts — event listener lifecycle, beforeunload handling, DOM selection and error paths.
    • src/lib/games/snake/renderer.ts — PIXI resource creation/destruction, canvas insertion, redraw correctness.
    • Cross-file consistency between types.ts, utils.ts, game.ts, renderer.ts, and init.ts.

Poem

🐰 I nibbled pixels on a tiled green track,
Chased a dot that vanished — then came back,
My tail stretched long and wiggled with cheer,
I hopped, I coded, a new game is here!
Carrots and dots — hooray, press Start, my dear 🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.04% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main feature added: a Snake game with a 60-second timer, which is the core mechanic of this substantial new implementation.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch claude/build-snake-game-011CUwxr6qupokc4q3b87irF

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello @cwchanap, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request adds a new, engaging Snake game to the platform. The game features a 60-second time limit, classic mechanics where players grow their snake by consuming food, and game-over conditions for hitting boundaries or the snake's own body. It leverages PixiJS for a visually appealing neon-themed rendering, offers intuitive keyboard controls (WASD/arrows), and includes pause/resume functionality. Beyond gameplay, it integrates score tracking and achievement notifications, enhancing the overall user experience.

Highlights

  • New Snake Game: Introduces a fully functional classic Snake game with a 60-second time limit.
  • PixiJS Rendering: Utilizes PixiJS for smooth, neon-styled graphics, including grid rendering, snake segments (with head glow), and pulsing food.
  • Core Gameplay Mechanics: Implements standard Snake gameplay: growing by eating food, collision detection with walls and the snake's own body, and food spawning every second.
  • Controls and State Management: Supports intuitive WASD and arrow key controls, along with pause/resume functionality and comprehensive game state management.
  • Score and Achievement Integration: Tracks score based on food eaten (10 points per food) and integrates with an achievement system upon game over.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds a complete Snake game. The implementation is well-structured, with logic, rendering, and initialization separated into different files. However, there are a few issues to address:

  • A critical bug with duplicate HTML IDs that prevents the UI from updating correctly.
  • A high-severity logic bug in the pause/resume functionality that incorrectly calculates the pause duration.
  • Several medium-severity issues related to code quality, including direct DOM manipulation from game logic files, use of deprecated methods, and some dead/redundant code.

I've left specific comments with suggestions for each of these points. Overall, a great addition once these issues are resolved.

Comment thread src/lib/games/snake/game.ts Resolved
Comment thread src/pages/snake/index.astro Resolved
Comment thread src/lib/games/snake/game.ts Resolved
Comment thread src/lib/games/snake/game.ts Resolved
Comment thread src/lib/games/snake/game.ts Resolved
Comment thread src/lib/games/snake/init.ts Resolved
Comment thread src/lib/games/snake/types.ts Resolved
Comment thread src/lib/games/snake/utils.ts Resolved

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/lib/games/snake/game.ts Resolved
@codecov-commenter

codecov-commenter commented Nov 9, 2025 •

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 2.15909% with 861 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/pages/snake/index.astro 0.00% 258 Missing and 1 partial ⚠️
src/lib/games/snake/game.ts 0.39% 254 Missing ⚠️
src/lib/games/snake/init.ts 0.72% 137 Missing ⚠️
src/lib/games/snake/renderer.ts 0.78% 127 Missing ⚠️
src/lib/games/snake/utils.ts 1.21% 81 Missing ⚠️
src/components/ui/GameCard.astro 0.00% 2 Missing ⚠️
src/pages/index.astro 0.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds a new Snake game to the Cetus minigames platform, implementing the classic arcade game where players control a snake that grows by eating food while avoiding walls and self-collision. The game features a 60-second survival mode with PixiJS-based rendering, keyboard controls (arrow keys/WASD), and full integration with the existing achievement and scoring systems.

Key Changes:

  • New Snake game with time-based survival gameplay and score tracking
  • PixiJS canvas renderer for smooth snake movement and food display
  • Complete game page with controls guide, statistics panel, and game info

Reviewed Changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
src/pages/snake/index.astro Game page with UI layout, controls, statistics display, and game initialization script
src/lib/games/snake/types.ts TypeScript type definitions for game state, constants, segments, and callbacks
src/lib/games/snake/utils.ts Utility functions for color conversion, collision detection, position calculations, and direction validation
src/lib/games/snake/renderer.ts PixiJS rendering implementation for snake segments, food items, and grid display
src/lib/games/snake/init.ts Game initialization with event listeners, achievement integration, and score submission
src/lib/games/snake/game.ts Core game logic including movement, collision detection, food spawning, and game state management
src/lib/games.ts Added Snake game registration with metadata, icon, and game ID enum entry

Comment thread src/lib/games/snake/game.ts Resolved
Comment thread src/lib/games/snake/utils.ts Resolved
Comment thread src/lib/games/snake/game.ts Resolved
Comment thread src/pages/snake/index.astro Resolved
Comment thread src/pages/snake/index.astro Resolved
Comment thread src/pages/snake/index.astro Resolved
Comment thread src/lib/games/snake/utils.ts Resolved
Comment thread src/lib/games/snake/game.ts Resolved
Added missing Snake game URL mapping to enable proper navigation:
- Added 'snake: 11' to ID mapping in index.astro
- Added 'Snake' case returning '/snake' in GameCard.astro getGameUrl()

This fixes the issue where clicking "Play Now" on the Snake game
card didn't redirect to the game page.
Fixed the duplicate ID issue where two elements both had id="snake-length":
- Renamed the statistics card element to id="snake-length-stats" (line 171)
- Updated updateUI() function to update both elements
- This ensures the snake length is properly displayed in both locations

This fixes the bug identified by gemini-code-assist where duplicate IDs
prevented proper UI updates.
Fixed the bug where pauseDuration was always calculated as ~0 because
timestamps were updated before calculating the duration.

Changed order of operations in togglePause():
1. Calculate pauseDuration = Date.now() - state.lastMoveTime
2. Adjust gameStartTime forward by pauseDuration
3. Then reset lastMoveTime and lastFoodSpawnTime to current time

This ensures the game timer doesn't count down while paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (4)
src/lib/games/snake/game.ts (1)

191-194: Remove or rename the semantically incorrect averageLength calculation.

The averageLength is calculated as maxLength / foodsEaten, which doesn't represent an average snake length over time. This metric is misleading. If tracking actual average length is desired, you'd need to sample the snake length at multiple points during gameplay. Otherwise, remove this stat or rename it to better reflect what it represents (e.g., "growthRate" or "lengthPerFood").

src/pages/snake/index.astro (1)

186-186: Correct the scoring description.

The scoring description is incorrect. According to the game logic (game.ts line 92), the score increases by 10 points for each food eaten, not "snake length × 10".

Apply this diff:

-              <li>• Score = snake length × 10</li>
+              <li>• Score = +10 points per food eaten</li>
src/lib/games/snake/utils.ts (2)

110-110: Replace deprecated substr.
String.prototype.substr is legacy. Use slice to keep the ID generator modern and lint-friendly.

-export function generateId(): string {
-    return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
-}
+export function generateId(): string {
+    return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`
+}

50-63: Ensure food never spawns on the snake.
If maxAttempts is reached we still return a colliding position, so food can appear inside the snake. Add a deterministic fallback (e.g., iterate the grid) before giving up.

     } while (collidesWithSnake(position, snake) && attempts < maxAttempts)

-    return position
+    if (collidesWithSnake(position, snake)) {
+        for (let x = 0; x < constants.GRID_WIDTH; x++) {
+            for (let y = 0; y < constants.GRID_HEIGHT; y++) {
+                const candidate = { x, y }
+                if (!collidesWithSnake(candidate, snake)) {
+                    return candidate
+                }
+            }
+        }
+        throw new Error('No available position for food')
+    }
+
+    return position
🧹 Nitpick comments (4)
src/lib/games/snake/game.ts (4)

55-64: Cache Date.now() to avoid redundant system calls.

The function calls Date.now() twice (lines 60 and 62). While the performance difference is negligible, it's better practice to call it once and reuse the value for consistency.

Apply this diff:

 export function spawnFood(state: GameState): void {
     const position = generateFoodPosition(state.snake, GAME_CONSTANTS)
+    const now = Date.now()
     state.food = {
         ...position,
         id: generateId(),
-        spawnTime: Date.now(),
+        spawnTime: now,
     }
-    state.lastFoodSpawnTime = Date.now()
+    state.lastFoodSpawnTime = now
     state.needsRedraw = true
 }

86-103: Remove redundant needsRedraw assignment.

Line 96 sets state.needsRedraw = true when food is eaten, but line 102 unconditionally sets it to true regardless of the if/else branch, making line 96 redundant.

Apply this diff:

     // Check if food is eaten
     if (state.food && positionsEqual(newHead, state.food)) {
         // Don't remove tail (snake grows)
         state.score += 10
         state.foodsEaten++
         state.maxLength = Math.max(state.maxLength, state.snake.length)
         state.food = null
-        state.needsRedraw = true
     } else {
         // Remove tail (normal movement)
         state.snake.pop()
     }

     state.needsRedraw = true

115-180: Consider decoupling game logic from DOM manipulation.

The functions startGame, togglePause, and resetGame directly manipulate DOM elements (buttons, overlays), tightly coupling game logic to the view layer. This makes the code harder to test, maintain, and reuse.

Consider refactoring to keep game logic pure (only managing state) and have the UI layer (init.ts or the Astro component) react to state changes. For example, these functions could return state updates or emit events, and the caller would be responsible for updating button text and disabled states.


143-156: Consider tracking pause start time explicitly for clarity.

While the current pause logic is functionally correct (calculating pauseDuration = Date.now() - state.lastMoveTime before updating timestamps), it could be clearer by explicitly tracking when the pause started.

Consider this approach for improved clarity:

// In GameState interface (types.ts), add:
// pauseStartTime: number | null

// When pausing:
if (!state.paused) {
    state.pauseStartTime = Date.now()
}

// When resuming:
if (state.pauseStartTime) {
    const pauseDuration = Date.now() - state.pauseStartTime
    if (state.gameStartTime) {
        state.gameStartTime += pauseDuration
    }
    state.pauseStartTime = null
    state.lastMoveTime = Date.now()
    state.lastFoodSpawnTime = Date.now()
    gameLoopFn()
}

This makes the intent explicit and avoids relying on lastMoveTime for dual purposes.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d9cd6fd and 416acc2.

📒 Files selected for processing (9)
  • src/components/ui/GameCard.astro (1 hunks)
  • src/lib/games.ts (3 hunks)
  • src/lib/games/snake/game.ts (1 hunks)
  • src/lib/games/snake/init.ts (1 hunks)
  • src/lib/games/snake/renderer.ts (1 hunks)
  • src/lib/games/snake/types.ts (1 hunks)
  • src/lib/games/snake/utils.ts (1 hunks)
  • src/pages/index.astro (1 hunks)
  • src/pages/snake/index.astro (1 hunks)
🧰 Additional context used
📓 Path-based instructions (13)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Avoid unused variables; prefix with _ to intentionally ignore
Prefer strict typing and avoid any unless explicitly justified

Files:

  • src/lib/games.ts
  • src/lib/games/snake/init.ts
  • src/lib/games/snake/types.ts
  • src/lib/games/snake/game.ts
  • src/lib/games/snake/renderer.ts
  • src/lib/games/snake/utils.ts
src/{components,pages}/**/*.astro

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use TypeScript interfaces for all component props in Astro components

Files:

  • src/pages/snake/index.astro
  • src/components/ui/GameCard.astro
  • src/pages/index.astro
src/**/*.astro

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.astro: Follow Astro component syntax with frontmatter and JSX-like templates
Utilize the cn() utility for conditional class composition
Apply the established sci-fi theme classes (e.g., text-holographic, animate-glow) consistently
Ensure accessible markup: ARIA labels, sufficient color contrast, and keyboard navigation

Files:

  • src/pages/snake/index.astro
  • src/components/ui/GameCard.astro
  • src/pages/index.astro
src/pages/*/index.astro

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/pages/*/index.astro: All games must be organized as /pages/[game-name]/index.astro
Game pages must include a standard container with IDs: [game]-container and [game]-board
Game pages should structure: frontmatter, static layout (container/overlay), and a script section for initialization and handlers
Implement button state management (Start/End/Reset) in the page script
Integrate GameOverlay with score display on each game page

Files:

  • src/pages/snake/index.astro
src/pages/**/index.astro

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/pages/**/index.astro: All game HTML structure must be authored in Astro; provide all containers/overlays up front
Provide breadcrumb navigation and a Back to Menu link on game pages

Files:

  • src/pages/snake/index.astro
  • src/pages/index.astro
src/pages/**/*.astro

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

On protected pages, check Astro.locals.user for authentication

Files:

  • src/pages/snake/index.astro
  • src/pages/index.astro
src/**/@(init|game|renderer).ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/@(init|game|renderer).ts: In game TypeScript, manipulate only dynamic children of existing DOM nodes; do not overwrite container HTML
Avoid using element.innerHTML to generate structure; query existing elements and add/remove children instead

Files:

  • src/lib/games/snake/init.ts
  • src/lib/games/snake/game.ts
  • src/lib/games/snake/renderer.ts
src/components/ui/**/*.astro

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use class-variance-authority (CVA) for UI component variants

Files:

  • src/components/ui/GameCard.astro
src/components/**/*.{tsx,astro}

📄 CodeRabbit inference engine (CLAUDE.md)

src/components/**/*.{tsx,astro}: Use existing UI components from src/components/ui/ where applicable
Follow TypeScript interface patterns for component props
Apply sci-fi theme utility classes consistently
Use cn() utility for conditional class names
Implement responsive, mobile-first design with proper hover/focus and ARIA where needed

Files:

  • src/components/ui/GameCard.astro
src/lib/games/**/{types,game,renderer,utils}.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Follow the modular game structure: types.ts → game.ts → renderer.ts → utils.ts

Files:

  • src/lib/games/snake/types.ts
  • src/lib/games/snake/game.ts
  • src/lib/games/snake/renderer.ts
  • src/lib/games/snake/utils.ts
src/**/@(renderer|game).ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/@(renderer|game).ts: Handle both touch and mouse events for gameplay input
Implement responsive canvas sizing and proper scaling across devices

Files:

  • src/lib/games/snake/game.ts
  • src/lib/games/snake/renderer.ts
src/lib/games/**/game.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Integrate score submission via the centralized Score Service

Files:

  • src/lib/games/snake/game.ts
src/lib/games/**/renderer.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Use PixiJS for canvas-based game rendering

Files:

  • src/lib/games/snake/renderer.ts
🧠 Learnings (18)
📓 Common learnings
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-15T07:40:29.364Z
Learning: Applies to src/lib/games/**/game.ts : Integrate score submission via the centralized Score Service
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-15T07:40:29.364Z
Learning: Applies to src/lib/games/**/renderer.ts : Use PixiJS for canvas-based game rendering
📚 Learning: 2025-09-15T07:40:29.364Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-15T07:40:29.364Z
Learning: Applies to src/lib/games/**/game.ts : Integrate score submission via the centralized Score Service

Applied to files:

  • src/lib/games.ts
  • src/pages/snake/index.astro
  • src/lib/games/snake/init.ts
  • src/lib/games/snake/game.ts
📚 Learning: 2025-09-15T07:39:22.602Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T07:39:22.602Z
Learning: Applies to src/pages/*/index.astro : Game pages should structure: frontmatter, static layout (container/overlay), and a script section for initialization and handlers

Applied to files:

  • src/pages/snake/index.astro
  • src/components/ui/GameCard.astro
  • src/pages/index.astro
📚 Learning: 2025-09-15T07:39:22.602Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T07:39:22.602Z
Learning: Applies to src/pages/*/index.astro : Integrate GameOverlay with score display on each game page

Applied to files:

  • src/pages/snake/index.astro
  • src/components/ui/GameCard.astro
  • src/pages/index.astro
  • src/lib/games/snake/game.ts
📚 Learning: 2025-09-15T07:39:22.602Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T07:39:22.602Z
Learning: Applies to src/pages/*/index.astro : Game pages must include a standard container with IDs: [game]-container and [game]-board

Applied to files:

  • src/pages/snake/index.astro
  • src/components/ui/GameCard.astro
  • src/pages/index.astro
📚 Learning: 2025-09-15T07:39:22.602Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T07:39:22.602Z
Learning: Applies to src/pages/**/index.astro : All game HTML structure must be authored in Astro; provide all containers/overlays up front

Applied to files:

  • src/pages/snake/index.astro
  • src/pages/index.astro
  • src/lib/games/snake/game.ts
📚 Learning: 2025-09-15T07:39:22.602Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T07:39:22.602Z
Learning: Applies to src/pages/**/index.astro : Provide breadcrumb navigation and a Back to Menu link on game pages

Applied to files:

  • src/pages/snake/index.astro
📚 Learning: 2025-09-15T07:39:22.602Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T07:39:22.602Z
Learning: Applies to src/pages/*/index.astro : All games must be organized as /pages/[game-name]/index.astro

Applied to files:

  • src/pages/snake/index.astro
  • src/components/ui/GameCard.astro
  • src/pages/index.astro
📚 Learning: 2025-09-15T07:39:22.602Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T07:39:22.602Z
Learning: Applies to src/pages/*/index.astro : Implement button state management (Start/End/Reset) in the page script

Applied to files:

  • src/pages/snake/index.astro
  • src/lib/games/snake/init.ts
  • src/lib/games/snake/game.ts
📚 Learning: 2025-09-15T07:40:29.364Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-15T07:40:29.364Z
Learning: Applies to src/lib/games/**/{types,game,renderer,utils}.ts : Follow the modular game structure: `types.ts` → `game.ts` → `renderer.ts` → `utils.ts`

Applied to files:

  • src/lib/games/snake/init.ts
  • src/lib/games/snake/types.ts
  • src/lib/games/snake/game.ts
  • src/lib/games/snake/renderer.ts
  • src/lib/games/snake/utils.ts
📚 Learning: 2025-09-15T07:39:22.602Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T07:39:22.602Z
Learning: Applies to src/**/@(init|game|renderer).ts : In game TypeScript, manipulate only dynamic children of existing DOM nodes; do not overwrite container HTML

Applied to files:

  • src/lib/games/snake/init.ts
  • src/lib/games/snake/game.ts
  • src/lib/games/snake/renderer.ts
📚 Learning: 2025-09-15T07:40:29.364Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-15T07:40:29.364Z
Learning: Applies to src/lib/games/{reflex,memory-matrix}/**/*.test.ts : Maintain dedicated unit tests for Reflex and Memory Matrix game logic

Applied to files:

  • src/lib/games/snake/init.ts
  • src/lib/games/snake/game.ts
  • src/lib/games/snake/renderer.ts
  • src/lib/games/snake/utils.ts
📚 Learning: 2025-09-15T07:39:22.602Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T07:39:22.602Z
Learning: Applies to src/lib/server/db/types.ts : Maintain complete TypeScript interfaces for DB schema in src/lib/server/db/types.ts

Applied to files:

  • src/lib/games/snake/types.ts
📚 Learning: 2025-09-15T07:39:22.602Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T07:39:22.602Z
Learning: Applies to src/**/@(renderer|game).ts : Handle both touch and mouse events for gameplay input

Applied to files:

  • src/lib/games/snake/game.ts
  • src/lib/games/snake/renderer.ts
📚 Learning: 2025-09-15T07:39:22.602Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T07:39:22.602Z
Learning: Applies to src/**/@(init|game|renderer).ts : Avoid using element.innerHTML to generate structure; query existing elements and add/remove children instead

Applied to files:

  • src/lib/games/snake/game.ts
📚 Learning: 2025-09-15T07:40:29.364Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-15T07:40:29.364Z
Learning: Applies to src/components/**/*.{tsx,astro} : Implement responsive, mobile-first design with proper hover/focus and ARIA where needed

Applied to files:

  • src/lib/games/snake/game.ts
📚 Learning: 2025-09-15T07:40:29.364Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-15T07:40:29.364Z
Learning: Applies to src/lib/games/**/renderer.ts : Use PixiJS for canvas-based game rendering

Applied to files:

  • src/lib/games/snake/renderer.ts
📚 Learning: 2025-09-15T07:39:22.602Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T07:39:22.602Z
Learning: Applies to src/**/@(renderer|game).ts : Implement responsive canvas sizing and proper scaling across devices

Applied to files:

  • src/lib/games/snake/renderer.ts
🧬 Code graph analysis (4)
src/lib/games/snake/init.ts (3)
src/lib/games/snake/types.ts (2)
  • GameState (18-35)
  • GameStats (52-58)
src/lib/games/snake/game.ts (9)
  • createGameState (29-53)
  • GAME_CONSTANTS (14-27)
  • gameLoop (202-244)
  • startGame (115-130)
  • togglePause (132-157)
  • resetGame (159-180)
  • changeDirection (106-113)
  • updateUI (246-269)
  • endGame (182-200)
src/lib/games/snake/renderer.ts (3)
  • setupPixiJS (13-55)
  • drawGrid (66-87)
  • draw (148-172)
src/lib/games/snake/game.ts (2)
src/lib/games/snake/types.ts (4)
  • GameConstants (37-50)
  • GameState (18-35)
  • SnakeSegment (9-11)
  • Direction (7-7)
src/lib/games/snake/utils.ts (8)
  • hexToPixiColor (7-9)
  • generateId (109-111)
  • generateFoodPosition (46-63)
  • getNextPosition (68-87)
  • isOutOfBounds (14-24)
  • collidesWithSnake (29-34)
  • positionsEqual (39-41)
  • isValidDirectionChange (92-104)
src/lib/games/snake/renderer.ts (1)
src/lib/games/snake/types.ts (4)
  • GameConstants (37-50)
  • SnakeSegment (9-11)
  • Food (13-16)
  • GameState (18-35)
src/lib/games/snake/utils.ts (1)
src/lib/games/snake/types.ts (4)
  • Position (2-5)
  • GameConstants (37-50)
  • SnakeSegment (9-11)
  • Direction (7-7)
🪛 ast-grep (0.39.7)
src/lib/games/snake/renderer.ts

[warning] 50-51: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: gameContainer.innerHTML =
'

Failed to initialize game renderer. Please check if your browser supports WebGL.
'
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html

(unsafe-html-content-assignment)


[warning] 50-51: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: gameContainer.innerHTML =
'

Failed to initialize game renderer. Please check if your browser supports WebGL.
'
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html

(dom-content-modification)

🔇 Additional comments (4)
src/lib/games.ts (1)

13-13: LGTM! Snake game properly registered.

The Snake game has been correctly added to the games catalog with appropriate metadata, icon, and enum entry. The implementation follows the established pattern for game registration.

Also applies to: 149-160, 213-213

src/pages/snake/index.astro (2)

194-254: LGTM! Game initialization and lifecycle management properly implemented.

The script correctly handles game initialization, button state management, and restart functionality. Error handling is present, and the integration with the GameOverlay follows the established pattern.


73-89: No changes needed – final score is properly displayed in the GameOverlay.

The GameOverlay component has a built-in score display (id="final-score") that defaults to showing "0". When the game ends, the snake game initialization's onGameOver callback updates this element with the final score, alongside the custom stats (max length, foods eaten, time survived). The score is prominently displayed in the overlay.

src/lib/games/snake/game.ts (1)

246-269: LGTM! UI update correctly handles both length display elements.

The function properly updates both snake-length and snake-length-stats elements, ensuring both the game status badge and the statistics panel stay synchronized.

Comment thread src/lib/games/snake/init.ts Resolved
Comment thread src/lib/games/snake/init.ts Resolved
Comment thread src/lib/games/snake/renderer.ts Resolved
Replaced insecure innerHTML usage with proper DOM manipulation:
- Clear child nodes using while loop with removeChild()
- Create error div using document.createElement()
- Set classes via className property
- Set message via textContent (not innerHTML)
- Append to container using appendChild()

This prevents potential XSS vulnerabilities from innerHTML usage.
Changed the beforeunload event handler to set e.returnValue, which is
required in modern browsers when using addEventListener:
- Set e.returnValue = message to trigger browser prompt
- Still return message for older browser compatibility
- preventDefault() is called before setting returnValue

This ensures users see a confirmation dialog when trying to leave
the page with an active game in progress.
Updated button reset logic to fully restore Start button functionality:
- Set disabled = false to re-enable the button
- Set textContent = 'Start' to restore the label
- Applied fix in both onGameOver callback (init.ts) and restart handler (index.astro)

This ensures users can launch a new round after game over without issues.
Moved score submission from init.ts callback to game.ts endGame():
- Import saveGameScore and GameID in game.ts
- Submit score to Score Service before calling onGameOver callback
- Wrap submission in try/catch to handle errors gracefully
- Log submission errors without throwing to ensure onGameOver still runs
- Service automatically skips submission if user not logged in (401)
- Handle achievement notifications via window event dispatch
- Remove duplicate submission from init.ts onGameOver callback
- Clean up unused imports in init.ts

This centralizes score submission logic in the core game module and
ensures scores are always submitted regardless of onGameOver callback.
Changed Math.random().toString(36).substr(2, 9) to use slice(2, 11)
instead of the deprecated substr() method. Both produce the same
result (9 characters starting from position 2).
Removed useless state.needsRedraw = true on line 98 that was always
overridden by the unconditional assignment at the end of the function.
The needsRedraw flag is set once at line 103 for all cases.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/pages/snake/index.astro (1)

182-187: Fix the score description to match game logic.

Reiterating earlier feedback: moveSnake adds 10 points per food while the snake starts length 3, so “snake length × 10” is incorrect and confuses players. Please align the copy with the actual scoring rule.

-              <li>• Score = snake length × 10</li>
+              <li>• Score = foods eaten × 10</li>
src/lib/games/snake/utils.ts (1)

54-63: Ensure spawned food never overlaps the snake.

When the snake almost fills the grid, the 1000 random retries can still miss the lone free cell and return a position already occupied. That spawns food on the body, and the very next move triggers a false self-collision. Please add a deterministic fallback (or throw) so we only return a free coordinate.

     do {
         position = {
             x: Math.floor(Math.random() * constants.GRID_WIDTH),
             y: Math.floor(Math.random() * constants.GRID_HEIGHT),
         }
         attempts++
     } while (collidesWithSnake(position, snake) && attempts < maxAttempts)
 
+    if (collidesWithSnake(position, snake)) {
+        for (let x = 0; x < constants.GRID_WIDTH; x++) {
+            for (let y = 0; y < constants.GRID_HEIGHT; y++) {
+                const candidate = { x, y }
+                if (!collidesWithSnake(candidate, snake)) {
+                    return candidate
+                }
+            }
+        }
+        throw new Error('No available position for food')
+    }
+
     return position
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dcd8986 and 0df5f2b.

📒 Files selected for processing (4)
  • src/lib/games/snake/game.ts (1 hunks)
  • src/lib/games/snake/init.ts (1 hunks)
  • src/lib/games/snake/utils.ts (1 hunks)
  • src/pages/snake/index.astro (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/lib/games/snake/init.ts
🧰 Additional context used
📓 Path-based instructions (10)
src/**/@(init|game|renderer).ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/@(init|game|renderer).ts: In game TypeScript, manipulate only dynamic children of existing DOM nodes; do not overwrite container HTML
Avoid using element.innerHTML to generate structure; query existing elements and add/remove children instead

Files:

  • src/lib/games/snake/game.ts
src/**/@(renderer|game).ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/@(renderer|game).ts: Handle both touch and mouse events for gameplay input
Implement responsive canvas sizing and proper scaling across devices

Files:

  • src/lib/games/snake/game.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Avoid unused variables; prefix with _ to intentionally ignore
Prefer strict typing and avoid any unless explicitly justified

Files:

  • src/lib/games/snake/game.ts
  • src/lib/games/snake/utils.ts
src/lib/games/**/{types,game,renderer,utils}.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Follow the modular game structure: types.ts → game.ts → renderer.ts → utils.ts

Files:

  • src/lib/games/snake/game.ts
  • src/lib/games/snake/utils.ts
src/lib/games/**/game.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Integrate score submission via the centralized Score Service

Files:

  • src/lib/games/snake/game.ts
src/{components,pages}/**/*.astro

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use TypeScript interfaces for all component props in Astro components

Files:

  • src/pages/snake/index.astro
src/**/*.astro

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.astro: Follow Astro component syntax with frontmatter and JSX-like templates
Utilize the cn() utility for conditional class composition
Apply the established sci-fi theme classes (e.g., text-holographic, animate-glow) consistently
Ensure accessible markup: ARIA labels, sufficient color contrast, and keyboard navigation

Files:

  • src/pages/snake/index.astro
src/pages/*/index.astro

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/pages/*/index.astro: All games must be organized as /pages/[game-name]/index.astro
Game pages must include a standard container with IDs: [game]-container and [game]-board
Game pages should structure: frontmatter, static layout (container/overlay), and a script section for initialization and handlers
Implement button state management (Start/End/Reset) in the page script
Integrate GameOverlay with score display on each game page

Files:

  • src/pages/snake/index.astro
src/pages/**/index.astro

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/pages/**/index.astro: All game HTML structure must be authored in Astro; provide all containers/overlays up front
Provide breadcrumb navigation and a Back to Menu link on game pages

Files:

  • src/pages/snake/index.astro
src/pages/**/*.astro

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

On protected pages, check Astro.locals.user for authentication

Files:

  • src/pages/snake/index.astro
🧠 Learnings (17)
📓 Common learnings
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-15T07:40:29.364Z
Learning: Applies to src/lib/games/**/game.ts : Integrate score submission via the centralized Score Service
📚 Learning: 2025-09-15T07:40:29.364Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-15T07:40:29.364Z
Learning: Applies to src/lib/games/**/{types,game,renderer,utils}.ts : Follow the modular game structure: `types.ts` → `game.ts` → `renderer.ts` → `utils.ts`

Applied to files:

  • src/lib/games/snake/game.ts
  • src/lib/games/snake/utils.ts
📚 Learning: 2025-09-15T07:40:29.364Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-15T07:40:29.364Z
Learning: Applies to src/lib/games/{reflex,memory-matrix}/**/*.test.ts : Maintain dedicated unit tests for Reflex and Memory Matrix game logic

Applied to files:

  • src/lib/games/snake/game.ts
  • src/lib/games/snake/utils.ts
📚 Learning: 2025-09-15T07:40:29.364Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-15T07:40:29.364Z
Learning: Applies to src/lib/games/**/game.ts : Integrate score submission via the centralized Score Service

Applied to files:

  • src/lib/games/snake/game.ts
  • src/pages/snake/index.astro
📚 Learning: 2025-09-15T07:39:22.602Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T07:39:22.602Z
Learning: Applies to src/**/@(init|game|renderer).ts : In game TypeScript, manipulate only dynamic children of existing DOM nodes; do not overwrite container HTML

Applied to files:

  • src/lib/games/snake/game.ts
📚 Learning: 2025-09-15T07:39:22.602Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T07:39:22.602Z
Learning: Applies to src/**/@(renderer|game).ts : Handle both touch and mouse events for gameplay input

Applied to files:

  • src/lib/games/snake/game.ts
📚 Learning: 2025-09-15T07:39:22.602Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T07:39:22.602Z
Learning: Applies to src/pages/*/index.astro : Implement button state management (Start/End/Reset) in the page script

Applied to files:

  • src/lib/games/snake/game.ts
  • src/pages/snake/index.astro
📚 Learning: 2025-09-15T07:39:22.602Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T07:39:22.602Z
Learning: Applies to src/pages/*/index.astro : Integrate GameOverlay with score display on each game page

Applied to files:

  • src/lib/games/snake/game.ts
  • src/pages/snake/index.astro
📚 Learning: 2025-09-15T07:39:22.602Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T07:39:22.602Z
Learning: Applies to src/**/@(init|game|renderer).ts : Avoid using element.innerHTML to generate structure; query existing elements and add/remove children instead

Applied to files:

  • src/lib/games/snake/game.ts
📚 Learning: 2025-09-15T07:39:22.602Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T07:39:22.602Z
Learning: Applies to src/pages/**/index.astro : All game HTML structure must be authored in Astro; provide all containers/overlays up front

Applied to files:

  • src/lib/games/snake/game.ts
  • src/pages/snake/index.astro
📚 Learning: 2025-09-15T07:40:29.364Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-15T07:40:29.364Z
Learning: Applies to src/components/**/*.{tsx,astro} : Implement responsive, mobile-first design with proper hover/focus and ARIA where needed

Applied to files:

  • src/lib/games/snake/game.ts
📚 Learning: 2025-09-15T07:39:22.602Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T07:39:22.602Z
Learning: Applies to src/lib/server/db/queries.ts : Use saveGameScoreWithAchievements() for score submission and achievement checking

Applied to files:

  • src/lib/games/snake/game.ts
📚 Learning: 2025-09-15T07:39:22.602Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T07:39:22.602Z
Learning: Applies to src/pages/api/scores.ts : Submit game scores via the /api/scores endpoint

Applied to files:

  • src/lib/games/snake/game.ts
📚 Learning: 2025-09-15T07:39:22.602Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T07:39:22.602Z
Learning: Applies to src/pages/*/index.astro : Game pages should structure: frontmatter, static layout (container/overlay), and a script section for initialization and handlers

Applied to files:

  • src/pages/snake/index.astro
📚 Learning: 2025-09-15T07:39:22.602Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T07:39:22.602Z
Learning: Applies to src/pages/*/index.astro : Game pages must include a standard container with IDs: [game]-container and [game]-board

Applied to files:

  • src/pages/snake/index.astro
📚 Learning: 2025-09-15T07:39:22.602Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T07:39:22.602Z
Learning: Applies to src/pages/*/index.astro : All games must be organized as /pages/[game-name]/index.astro

Applied to files:

  • src/pages/snake/index.astro
📚 Learning: 2025-09-15T07:39:22.602Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T07:39:22.602Z
Learning: Applies to src/pages/**/index.astro : Provide breadcrumb navigation and a Back to Menu link on game pages

Applied to files:

  • src/pages/snake/index.astro
🧬 Code graph analysis (2)
src/lib/games/snake/game.ts (2)
src/lib/games/snake/types.ts (4)
  • GameConstants (37-50)
  • GameState (18-35)
  • SnakeSegment (9-11)
  • Direction (7-7)
src/lib/games/snake/utils.ts (8)
  • hexToPixiColor (7-9)
  • generateId (109-111)
  • generateFoodPosition (46-63)
  • getNextPosition (68-87)
  • isOutOfBounds (14-24)
  • collidesWithSnake (29-34)
  • positionsEqual (39-41)
  • isValidDirectionChange (92-104)
src/lib/games/snake/utils.ts (1)
src/lib/games/snake/types.ts (4)
  • Position (2-5)
  • GameConstants (37-50)
  • SnakeSegment (9-11)
  • Direction (7-7)

Comment thread src/pages/snake/index.astro Resolved
Added nested <div id="snake-board"> inside #snake-container to provide
the expected DOM structure for the renderer to mount the PIXI view.
This follows the standard [game]-board pattern used by other games.
@cwchanap

Copy link
Copy Markdown
Owner Author

@codex review

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Comment on lines +239 to +269
// Submit score to centralized Score Service in the background
// Don't await to avoid blocking UI on slow/offline connections
saveGameScore(
GameID.SNAKE,
state.score,
result => {
// Score submitted successfully, handle achievements
if (result.newAchievements && result.newAchievements.length > 0) {
// Dispatch achievement event for UI notification
if (typeof window !== 'undefined') {
window.dispatchEvent(
new CustomEvent('achievementsEarned', {
detail: {
achievementIds: result.newAchievements,
},
})
)
}
}
},
error => {
// Log submission error without throwing
// eslint-disable-next-line no-console
console.error('Failed to submit score:', error)
},
stats
).catch(error => {
// Handle any unexpected errors gracefully
// eslint-disable-next-line no-console
console.error('Error during score submission:', error)
})

Copilot AI Nov 15, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Score submission logic should be moved to init.ts to maintain architectural consistency with other games (Tetris, Reflex, etc.). Following the established pattern, score submission via saveGameScore should be called in the onGameOver callback in init.ts rather than directly in the game.ts endGame function. This separation keeps game logic (game.ts) independent of presentation/service layer concerns (init.ts), improving maintainability and consistency across the codebase.

Copilot uses AI. Check for mistakes.
Comment on lines +82 to +83
// Note: Score submission is now handled in game.ts endGame()
// This callback only handles UI updates

Copilot AI Nov 15, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this comment acknowledges that score submission is handled in game.ts, this deviation from the established pattern (where score submission occurs in init.ts callbacks) should be reconsidered. The comment should either explain WHY this architectural decision was made, or the code should be refactored to follow the pattern used by other games like Tetris and Reflex for consistency.

Copilot uses AI. Check for mistakes.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/lib/games/snake/game.ts Resolved
Previously, clicking Start after a game ended would appear to work (button
changes to "Playing...") but the snake wouldn't move. This happened because:

1. endGame() sets state.gameOver = true and state.gameStarted = false
2. onGameOver callback re-enables the Start button
3. Player clicks Start
4. startGame() sets state.gameStarted = true but leaves state.gameOver = true
5. gameLoop() immediately returns because state.gameOver is still true

Fix: Reset both gameOver and paused flags at the start of startGame() to
allow players to restart the game after it ends without requiring a full
Reset or page reload.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/lib/games/snake/game.ts (1)

70-114: Consider adding unit tests for core game logic.

The Codecov report shows 0.39% coverage for this file (252 lines missing). While not blocking, unit tests for moveSnake, collision detection, direction changes, and scoring would help catch regressions and make refactoring safer.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b1de4ec and 9d2ef50.

📒 Files selected for processing (1)
  • src/lib/games/snake/game.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
src/**/@(init|game|renderer).ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/@(init|game|renderer).ts: In game TypeScript, manipulate only dynamic children of existing DOM nodes; do not overwrite container HTML
Avoid using element.innerHTML to generate structure; query existing elements and add/remove children instead

Files:

  • src/lib/games/snake/game.ts
src/**/@(renderer|game).ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/@(renderer|game).ts: Handle both touch and mouse events for gameplay input
Implement responsive canvas sizing and proper scaling across devices

Files:

  • src/lib/games/snake/game.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Avoid unused variables; prefix with _ to intentionally ignore
Prefer strict typing and avoid any unless explicitly justified

Files:

  • src/lib/games/snake/game.ts
src/lib/games/**/{types,game,renderer,utils}.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Follow the modular game structure: types.ts → game.ts → renderer.ts → utils.ts

Files:

  • src/lib/games/snake/game.ts
src/lib/games/**/game.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Integrate score submission via the centralized Score Service

Files:

  • src/lib/games/snake/game.ts
🧠 Learnings (14)
📓 Common learnings
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-15T07:40:29.364Z
Learning: Applies to src/lib/games/**/game.ts : Integrate score submission via the centralized Score Service
📚 Learning: 2025-09-15T07:40:29.364Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-15T07:40:29.364Z
Learning: Applies to src/lib/games/**/{types,game,renderer,utils}.ts : Follow the modular game structure: `types.ts` → `game.ts` → `renderer.ts` → `utils.ts`

Applied to files:

  • src/lib/games/snake/game.ts
📚 Learning: 2025-09-15T07:40:29.364Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-15T07:40:29.364Z
Learning: Applies to src/lib/games/{reflex,memory-matrix}/**/*.test.ts : Maintain dedicated unit tests for Reflex and Memory Matrix game logic

Applied to files:

  • src/lib/games/snake/game.ts
📚 Learning: 2025-09-15T07:40:29.364Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-15T07:40:29.364Z
Learning: Applies to src/lib/games/**/game.ts : Integrate score submission via the centralized Score Service

Applied to files:

  • src/lib/games/snake/game.ts
📚 Learning: 2025-09-15T07:39:22.602Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T07:39:22.602Z
Learning: Applies to src/**/@(init|game|renderer).ts : In game TypeScript, manipulate only dynamic children of existing DOM nodes; do not overwrite container HTML

Applied to files:

  • src/lib/games/snake/game.ts
📚 Learning: 2025-09-15T07:39:22.602Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T07:39:22.602Z
Learning: Applies to src/lib/achievements.ts : Define all achievements (with rarity levels) centrally in src/lib/achievements.ts

Applied to files:

  • src/lib/games/snake/game.ts
📚 Learning: 2025-09-15T07:39:22.602Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T07:39:22.602Z
Learning: Applies to src/**/@(renderer|game).ts : Handle both touch and mouse events for gameplay input

Applied to files:

  • src/lib/games/snake/game.ts
📚 Learning: 2025-09-15T07:39:22.602Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T07:39:22.602Z
Learning: Applies to src/pages/*/index.astro : Implement button state management (Start/End/Reset) in the page script

Applied to files:

  • src/lib/games/snake/game.ts
📚 Learning: 2025-09-15T07:39:22.602Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T07:39:22.602Z
Learning: Applies to src/pages/*/index.astro : Integrate GameOverlay with score display on each game page

Applied to files:

  • src/lib/games/snake/game.ts
📚 Learning: 2025-09-15T07:39:22.602Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T07:39:22.602Z
Learning: Applies to src/**/@(init|game|renderer).ts : Avoid using element.innerHTML to generate structure; query existing elements and add/remove children instead

Applied to files:

  • src/lib/games/snake/game.ts
📚 Learning: 2025-09-15T07:39:22.602Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T07:39:22.602Z
Learning: Applies to src/pages/**/index.astro : All game HTML structure must be authored in Astro; provide all containers/overlays up front

Applied to files:

  • src/lib/games/snake/game.ts
📚 Learning: 2025-09-15T07:40:29.364Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-15T07:40:29.364Z
Learning: Applies to src/components/**/*.{tsx,astro} : Implement responsive, mobile-first design with proper hover/focus and ARIA where needed

Applied to files:

  • src/lib/games/snake/game.ts
📚 Learning: 2025-09-15T07:39:22.602Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T07:39:22.602Z
Learning: Applies to src/pages/api/scores.ts : Submit game scores via the /api/scores endpoint

Applied to files:

  • src/lib/games/snake/game.ts
📚 Learning: 2025-09-15T07:39:22.602Z
Learnt from: CR
Repo: cwchanap/cetus PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-15T07:39:22.602Z
Learning: Applies to src/lib/server/db/queries.ts : Use saveGameScoreWithAchievements() for score submission and achievement checking

Applied to files:

  • src/lib/games/snake/game.ts
🧬 Code graph analysis (1)
src/lib/games/snake/game.ts (2)
src/lib/games/snake/types.ts (4)
  • GameConstants (38-51)
  • GameState (18-36)
  • SnakeSegment (9-11)
  • Direction (7-7)
src/lib/games/snake/utils.ts (8)
  • hexToPixiColor (7-9)
  • generateId (125-127)
  • generateFoodPosition (46-79)
  • getNextPosition (84-103)
  • isOutOfBounds (14-24)
  • positionsEqual (39-41)
  • collidesWithSnake (29-34)
  • isValidDirectionChange (108-120)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: 🏗️ Build
🔇 Additional comments (8)
src/lib/games/snake/game.ts (8)

16-56: LGTM — solid initialization.

The game constants are well-organized, and createGameState provides a clean factory with all necessary fields properly initialized.


70-114: LGTM — collision logic is sound.

The tail-exclusion logic (line 91) correctly prevents false self-collisions when the snake isn't growing, and the wall/boundary checks are solid. This properly addresses previous feedback.


116-125: LGTM — direction validation prevents 180° reversals.

Validating against state.direction (the current heading) rather than state.nextDirection is the correct approach here. Checking the queued direction would allow a sequence like RIGHT → UP (queued) → LEFT (valid vs. UP) to produce an instant RIGHT → LEFT reversal on the next tick. The current design allows one buffered input while blocking reversals.


147-178: LGTM — pause compensation is correct.

The pause duration is now calculated (lines 163-165) before updating the movement timestamps, ensuring pauseStartedAt isn't prematurely cleared. The adjusted gameStartTime properly compensates the countdown timer.


180-212: LGTM — reset properly restores UI and preserves callbacks.

Caching and restoring onGameOver (lines 181-182, 188) prevents the callback from being wiped, and the button visibility restoration (lines 195-204) ensures the UI returns to its pre-game state.


214-273: LGTM — robust error handling and non-blocking score submission.

The game-over flow is well-structured:

  • Pause compensation (lines 218-222) ensures accurate time stats even if ended while paused
  • The onGameOver callback is wrapped in try/catch (lines 233-239) to prevent unhandled rejections
  • saveGameScore fires in the background (line 244) with comprehensive error handling via callbacks and .catch, so network delays don't block the UI
  • Achievement events are dispatched correctly (lines 249-260)

The calls to endGame in gameLoop (lines 292, 301) don't need await because all async operations inside endGame handle their own errors, and state.gameOver = true on line 215 stops the loop immediately.

Based on learnings


275-317: LGTM — clean game loop with proper timing.

The loop correctly gates on gameOver, paused, and gameStarted, uses interval-based movement and food spawning, and schedules the next frame via requestAnimationFrame.


319-342: LGTM — UI updates both length displays correctly.

All DOM queries target existing elements and update only textContent, avoiding innerHTML. The dual length updates (lines 333-338) ensure both the game HUD and stats panel stay in sync.

@cwchanap

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. You're on a roll.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@cwchanap
cwchanap merged commit b544f3c into main Nov 15, 2025
6 checks passed
cwchanap added a commit that referenced this pull request Jul 10, 2026
…d fix token bugs

Convert component tests from static file reading to behavioral testing using
AstroContainer for actual rendered output verification. Fix multiple token-related
bugs discovered during test refactoring:

- Bug #1: Button outline hover uses text-background (solid) instead of --cetus-page-bg (gradient)
- Bug #2: Hero h1 uses individual font properties instead of font: shorthand (resets font-size)
- Bug #4: Navigation logo changed from h1 to span to avoid double-h1 on homepage
- Bug #5: Hero particles use var(--cetus-accent) tokens instead of hardcoded hex colors
- Bug #6: --cetus-surface and --cetus-hairline use black-opacity in :root, white-opacity in .dark

Test improvements:
- Organism.test.ts: Verify shape rendering, orbit ring, SVG paths, chain segments
- SpecimenCard.test.ts: Verify active/inactive states, vessel structure, organism embedding
- shell-tokens.test.ts: Verify token usage in Button, Card, Navigation, Footer
- AppLayout.test.ts: Verify theme switching, font loading, background visibility
- index-markup.test.ts: Verify hero composition, depth zones, token usage
- abyssal-shell.test.ts: Verify CSS retints and component rendering
- global.test.ts: Verify token definitions and Tailwind mapping with CSS block extraction
- vitest.config.ts: Integrate with Astro's getViteConfig for proper test environment

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
cwchanap added a commit that referenced this pull request Jul 10, 2026
…d fix token bugs

Convert component tests from static file reading to behavioral testing using
AstroContainer for actual rendered output verification. Fix multiple token-related
bugs discovered during test refactoring:

- Bug #1: Button outline hover uses text-background (solid) instead of --cetus-page-bg (gradient)
- Bug #2: Hero h1 uses individual font properties instead of font: shorthand (resets font-size)
- Bug #4: Navigation logo changed from h1 to span to avoid double-h1 on homepage
- Bug #5: Hero particles use var(--cetus-accent) tokens instead of hardcoded hex colors
- Bug #6: --cetus-surface and --cetus-hairline use black-opacity in :root, white-opacity in .dark

Test improvements:
- Organism.test.ts: Verify shape rendering, orbit ring, SVG paths, chain segments
- SpecimenCard.test.ts: Verify active/inactive states, vessel structure, organism embedding
- shell-tokens.test.ts: Verify token usage in Button, Card, Navigation, Footer
- AppLayout.test.ts: Verify theme switching, font loading, background visibility
- index-markup.test.ts: Verify hero composition, depth zones, token usage
- abyssal-shell.test.ts: Verify CSS retints and component rendering
- global.test.ts: Verify token definitions and Tailwind mapping with CSS block extraction
- vitest.config.ts: Integrate with Astro's getViteConfig for proper test environment

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

This branch was successfully deployed

1 active deployment
Preview — 9d2ef500 Deployed Nov 15, 2025 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants