Place the queens. Become the winner.
A puzzle game based on the classic N-Queens problem.
The goal is to place N queens on an N Γ N board so that no two queens threaten each other.
- Framework: Vue (SPA)
- Language: TypeScript
- Routing: Vue Router
- State management: Pinia
- Unit test: Vitest
- E2E test: Cypress
- Linting: ESLint
- Formatting: oxfmt
- Bundler: Vite
- Package Manager: Bun
- Welcome message
- Start button
- Game Board
- Quit button (returns to Home)
- Hint button (reveals one queen's position, usable once per game)
The board is an N Γ N grid containing N queens.
Each queen must satisfy the following conditions:
- Only one queen per row
- Only one queen per column
- Only one queen per region
- Queens cannot be adjacent, including diagonally
The player starts with 3 hearts and must find all N queens before running out of hearts.
Users can interact with each square on the board:
- Single click / drag
- Mark the square with X (indicating no queen)
- Double click
- Mark the square with πΈ (indicating a queen)
If the selected square does not contain a queen:
- One heart is deducted
- If all three hearts are lost, a Game Over message is shown
If the selected square is the final queen:
- A Victory message is displayed
Puzzle boards are manually created.
The game logic follows a simple separation between the game engine and the UI layer.
The core idea is that all game rules are handled by QueenGame, while Vue components are only responsible for rendering and forwarding user interactions.
User interactions follow this flow:
GameCell (UI)
β emits event
GameBoard (UI container)
β calls
QueenGame (game engine)
β updates
BoardCell (game state)
- The main game engine
- Holds the single source of truth for the game state
- Manages:
- the board
- hearts
- hints
- win / lose conditions
- Applies game rules (for example, losing a heart when marking the wrong queen)
Example:
game.markQueen(position)Represents the state of a single cell on the board.
It only stores local state and simple behaviors, such as:
- whether the cell contains a queen
- whether it has been marked
- the region it belongs to
BoardCell does not implement game rules.
A container component responsible for rendering the grid of cells.
It receives the QueenGame instance and forwards events from GameCell to the game engine.
The smallest UI unit representing one cell.
It does not modify the game state directly. Instead, it emits events describing user actions.
Example:
dblclick β emit('markQueen', position)This architecture ensures that:
- Game rules are centralized in the engine
- UI components remain simple and predictable
- State changes happen in a single place
- The game logic is easier to test
For example, the engine can be tested independently:
game.markQueen([2, 3])
expect(game.hearts).toBe(2)without involving any UI components.
UI components should not mutate the game state directly.
Instead, they emit user intentions, and the game engine decides how the state should change.
UI β intent β engine β state update
This keeps the system predictable and maintainable.
Work in progress.
/
βββ public/ # Static assets
βββ src/
β βββ components/ # Reusable UI components
β β βββ common/
β β β βββ BaseButton.vue
β β β βββ HeartCounter.vue
β β β
β β βββ game/ # Game-related components
β β βββ GameBoard.vue
β β βββ GameCell.vue
β β
β βββ views/ # Route-level pages
β β βββ HomeView.vue
β β βββ GameView.vue
β β
β βββ router/ # Vue Router configuration
β β βββ index.ts
β β
β βββ stores/ # Pinia stores
β β βββ game.ts
β β βββ ui.ts
β β
β βββ types/ # TypeScript type definitions
β β βββ board.ts
β β βββ game.ts
β β
β βββ constants/ # Game constants
β β βββ board.ts
β β βββ game.ts
β β
β βββ composables/ # Vue composables
β β βββ useBoardInteraction.ts
β β βββ useGameStatus.ts
β β
β βββ utils/ # Helper utilities
β β βββ board-validator.ts
β β βββ puzzle-loader.ts
β β
β βββ puzzles/ # Puzzle definitions
β β βββ n7.ts
β β βββ n8.ts
β β
β βββ App.vue
β βββ main.ts
β
βββ cypress/ # End-to-end tests
β βββ e2e/
β β βββ home.cy.ts
β β βββ game.cy.ts
β βββ support/
β
βββ vitest.config.ts
βββ cypress.config.ts
βββ README.md
bun installbun run devThe app will start at:
http://localhost:5173
bun run test:unitbun run test:e2ebun run lintbun run formatbun run buildMIT
AI assistants should refer to .github/copilot-instructions.md
for project guidelines.