-
Notifications
You must be signed in to change notification settings - Fork 23
feat: add Sliding Puzzle (15 Puzzle) game (closes #4) #123
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| .container { | ||
| display: flex; | ||
| flex-direction: column; | ||
| align-items: center; | ||
| padding: 2rem; | ||
| min-height: 100vh; | ||
| font-family: system-ui, sans-serif; | ||
| } | ||
|
|
||
| .container h1 { | ||
| margin-bottom: 0.5rem; | ||
| } | ||
|
|
||
| .sizeSelector { | ||
| display: flex; | ||
| gap: 8px; | ||
| margin: 1rem 0; | ||
| } | ||
|
|
||
| .sizeButton { | ||
| padding: 10px 20px; | ||
| min-height: 44px; | ||
| font-size: 1rem; | ||
|
Comment on lines
+20
to
+23
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎨 Design Review — LOW Would you want to increase the size-selector button hit area, since the current padding likely produces controls under the 44×44px touch target guidance on mobile (WCAG 2.5.5 Target Size)? Fix in Cursor | Fix in VSCode Claude (Use Cmd/Ctrl + Click for best experience) Prompt for AI Agent 🤖This is a **Design Review** comment — a question about the UX/design of frontend code. It is intentionally framed as a question, not a prescription. The author may agree or disagree.
**Path:** src/app/game/sliding-puzzle/SlidingPuzzle.module.css
**Line:** 20:22
**Comment:**
*LOW: Would you want to increase the size-selector button hit area, since the current padding likely produces controls under the 44×44px touch target guidance on mobile (WCAG 2.5.5 Target Size)?
- If you agree with the proposal, apply a small, localized change (swap a color token, bump a font size, adjust spacing, add an aria-label, etc.).
- If you disagree, or the answer depends on a design decision a human should make, explain your reasoning and ask the user how to proceed.
Do NOT refactor surrounding components or apply other design changes that weren't asked about. |
||
| font-weight: bold; | ||
| border: 2px solid #2c3e50; | ||
| border-radius: 6px; | ||
| background-color: white; | ||
| color: #2c3e50; | ||
| cursor: pointer; | ||
| transition: all 0.2s; | ||
| } | ||
|
|
||
| .sizeButton:hover { | ||
| background-color: #ecf0f1; | ||
| } | ||
|
|
||
| .activeSize { | ||
| background-color: #2c3e50; | ||
| color: white; | ||
| } | ||
|
|
||
| .activeSize:hover { | ||
| background-color: #34495e; | ||
| } | ||
|
|
||
| .moves { | ||
| font-size: 1.1rem; | ||
| color: #555; | ||
| } | ||
|
|
||
| .grid { | ||
| display: grid; | ||
| gap: 6px; | ||
| margin: 1.5rem 0; | ||
| background-color: #2c3e50; | ||
| padding: 10px; | ||
| border-radius: 12px; | ||
| width: min(440px, calc(100vw - 40px)); | ||
| box-sizing: border-box; | ||
| } | ||
|
|
||
| .tile { | ||
| aspect-ratio: 1; | ||
| min-width: 0; | ||
| display: flex; | ||
|
Comment on lines
+62
to
+65
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎨 Design Review — MEDIUM Do you think fixed 80px tile sizing could cause horizontal overflow on smaller phones (especially 5x5 mode), since the board width grows beyond typical sub-400px viewports once gaps and padding are included? Fix in Cursor | Fix in VSCode Claude (Use Cmd/Ctrl + Click for best experience) Prompt for AI Agent 🤖This is a **Design Review** comment — a question about the UX/design of frontend code. It is intentionally framed as a question, not a prescription. The author may agree or disagree.
**Path:** src/app/game/sliding-puzzle/SlidingPuzzle.module.css
**Line:** 59:62
**Comment:**
*MEDIUM: Do you think fixed 80px tile sizing could cause horizontal overflow on smaller phones (especially 5x5 mode), since the board width grows beyond typical sub-400px viewports once gaps and padding are included?
- If you agree with the proposal, apply a small, localized change (swap a color token, bump a font size, adjust spacing, add an aria-label, etc.).
- If you disagree, or the answer depends on a design decision a human should make, explain your reasoning and ask the user how to proceed.
Do NOT refactor surrounding components or apply other design changes that weren't asked about. |
||
| align-items: center; | ||
| justify-content: center; | ||
| font-size: clamp(0.9rem, 3.5vw, 1.5rem); | ||
| font-weight: bold; | ||
| background-color: #3498db; | ||
| color: white; | ||
| border: none; | ||
| border-radius: 8px; | ||
| cursor: pointer; | ||
| user-select: none; | ||
| transition: transform 0.15s ease, background-color 0.2s; | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| .tile:hover:not(.empty) { | ||
| background-color: #2980b9; | ||
| transform: scale(1.05); | ||
| } | ||
|
|
||
| .empty { | ||
| background-color: transparent; | ||
| cursor: default; | ||
| pointer-events: none; | ||
| } | ||
|
|
||
| .resetButton { | ||
| padding: 12px 24px; | ||
| font-size: 1.1rem; | ||
| background-color: #2c3e50; | ||
| color: white; | ||
| border: none; | ||
| border-radius: 8px; | ||
| cursor: pointer; | ||
| transition: background-color 0.2s; | ||
| } | ||
|
|
||
| .resetButton:hover { | ||
| background-color: #34495e; | ||
| } | ||
|
|
||
| .winMessage { | ||
| color: #27ae60; | ||
| font-weight: bold; | ||
| font-size: 1.3rem; | ||
| margin: 0.5rem 0; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,132 @@ | ||
| "use client"; | ||
| import { useState, useEffect, useCallback } from "react"; | ||
| import styles from "./SlidingPuzzle.module.css"; | ||
|
|
||
| const SIZES = [3, 4, 5]; | ||
|
|
||
| function generateSolvablePuzzle(size: number): number[] { | ||
| const total = size * size; | ||
| let tiles: number[]; | ||
| do { | ||
| tiles = [...Array(total - 1)].map((_, i) => i + 1); | ||
| for (let i = tiles.length - 1; i > 0; i--) { | ||
| const j = Math.floor(Math.random() * (i + 1)); | ||
| [tiles[i], tiles[j]] = [tiles[j], tiles[i]]; | ||
| } | ||
| tiles.push(0); | ||
| } while (!isSolvable(tiles, size) || isSolved(tiles)); | ||
| return tiles; | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| } | ||
|
|
||
| function isSolvable(tiles: number[], size: number): boolean { | ||
| let inversions = 0; | ||
| const filtered = tiles.filter((t) => t !== 0); | ||
| for (let i = 0; i < filtered.length; i++) { | ||
| for (let j = i + 1; j < filtered.length; j++) { | ||
| if (filtered[i] > filtered[j]) inversions++; | ||
| } | ||
| } | ||
|
|
||
| if (size % 2 === 1) { | ||
| // Odd grid: solvable if inversions is even | ||
| return inversions % 2 === 0; | ||
| } else { | ||
| // Even grid: find row of blank from bottom (1-based) | ||
| const emptyIndex = tiles.indexOf(0); | ||
| const rowFromBottom = size - Math.floor(emptyIndex / size); | ||
| return (rowFromBottom % 2 === 1) === (inversions % 2 === 0); | ||
| } | ||
| } | ||
|
|
||
| function isSolved(tiles: number[]): boolean { | ||
| for (let i = 0; i < tiles.length - 1; i++) { | ||
| if (tiles[i] !== i + 1) return false; | ||
| } | ||
| return tiles[tiles.length - 1] === 0; | ||
| } | ||
|
|
||
| export default function SlidingPuzzle() { | ||
| const [size, setSize] = useState(4); | ||
| const [tiles, setTiles] = useState<number[]>([]); | ||
| const [moves, setMoves] = useState(0); | ||
| const [won, setWon] = useState(false); | ||
|
|
||
| const startGame = useCallback(() => { | ||
| setTiles(generateSolvablePuzzle(size)); | ||
| setMoves(0); | ||
| setWon(false); | ||
| }, [size]); | ||
|
|
||
| useEffect(() => { | ||
| startGame(); | ||
| }, [startGame]); | ||
|
|
||
| const handleTileClick = (index: number) => { | ||
| if (won) return; | ||
| const emptyIndex = tiles.indexOf(0); | ||
| const row = Math.floor(index / size); | ||
| const col = index % size; | ||
| const emptyRow = Math.floor(emptyIndex / size); | ||
| const emptyCol = emptyIndex % size; | ||
|
|
||
| const isAdjacent = | ||
| (Math.abs(row - emptyRow) === 1 && col === emptyCol) || | ||
| (Math.abs(col - emptyCol) === 1 && row === emptyRow); | ||
|
|
||
| if (isAdjacent) { | ||
| const newTiles = [...tiles]; | ||
| [newTiles[index], newTiles[emptyIndex]] = [newTiles[emptyIndex], newTiles[index]]; | ||
| setTiles(newTiles); | ||
| setMoves(moves + 1); | ||
|
|
||
| if (isSolved(newTiles)) { | ||
| setWon(true); | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| return ( | ||
| <div className={styles.container}> | ||
| <h1>Sliding Puzzle</h1> | ||
|
|
||
| <div className={styles.sizeSelector}> | ||
| {SIZES.map((s) => ( | ||
| <button | ||
| key={s} | ||
| className={`${styles.sizeButton} ${s === size ? styles.activeSize : ""}`} | ||
| onClick={() => setSize(s)} | ||
| > | ||
| {s}x{s} | ||
| </button> | ||
| ))} | ||
| </div> | ||
|
|
||
| <p className={styles.moves}>Moves: {moves}</p> | ||
|
|
||
| {won && ( | ||
| <p className={styles.winMessage}>🎉 Congratulations! Solved in {moves} moves!</p> | ||
| )} | ||
|
|
||
| <div | ||
| className={styles.grid} | ||
| style={{ gridTemplateColumns: `repeat(${size}, 1fr)` }} | ||
| > | ||
| {tiles.map((tile, index) => ( | ||
| <button | ||
| key={index} | ||
| className={`${styles.tile} ${tile === 0 ? styles.empty : ""}`} | ||
| onClick={() => handleTileClick(index)} | ||
| aria-label={tile !== 0 ? `Tile ${tile}` : "Empty space"} | ||
| disabled={tile === 0} | ||
| > | ||
| {tile !== 0 ? tile : ""} | ||
| </button> | ||
| ))} | ||
| </div> | ||
|
|
||
| <button className={styles.resetButton} onClick={startGame}> | ||
| New Game | ||
| </button> | ||
| </div> | ||
| ); | ||
| } | ||
|
Comment on lines
+48
to
+132
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟠 Architect Review — HIGH The Sliding Puzzle is implemented as its own route but is not added to the existing "Choose Your Game" selection, so users following the normal game discovery flow will not see or reach this game. Suggestion: Add a Sliding Puzzle card to the game selection grid in src/app/game/page.tsx (and any other standard game discovery surfaces used in this app) so the new route is reachable via the normal navigation flow. Fix in Cursor | Fix in VSCode Claude (Use Cmd/Ctrl + Click for best experience) Prompt for AI Agent 🤖This is an **Architect / Logical Review** comment left during a code review. These reviews are first-class, important findings — not optional suggestions. Do NOT dismiss this as a 'big architectural change' just because the title says architect review; most of these can be resolved with a small, localized fix once the intent is understood.
**Path:** src/app/game/sliding-puzzle/page.tsx
**Line:** 48:130
**Comment:**
*HIGH: The Sliding Puzzle is implemented as its own route but is not added to the existing "Choose Your Game" selection, so users following the normal game discovery flow will not see or reach this game.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
If a suggested approach is provided above, use it as the authoritative instruction. If no explicit code suggestion is given, you MUST still draft and apply your own minimal, localized fix — do not punt back with 'no suggestion provided, review manually'. Keep the change as small as possible: add a guard clause, gate on a loading state, reorder an await, wrap in a conditional, etc. Do not refactor surrounding code or expand scope beyond the finding.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: ajaynegi33/Old-School-Game
Length of output: 164
🏁 Script executed:
Repository: ajaynegi33/Old-School-Game
Length of output: 2205
Update the workflow to use Node.js 24
package.jsonnow requiresnode >=24.x, but.github/workflows/code-quality.ymlstill sets up Node.js 18 beforenpm ci. Please switch that job to Node 24 so CI matches the supported runtime.🤖 Prompt for AI Agents