Skip to content

clhuang224/queener

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

35 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Queener

Place the queens. Become the winner.

Introduction

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.

Tech Stack

  • 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

Features

Pages

Home

  • Welcome message
  • Start button

Game

  • Game Board
  • Quit button (returns to Home)
  • Hint button (reveals one queen's position, usable once per game)

Game Rules

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.

Player Interaction

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

Question Generation

Puzzle boards are manually created.

Architecture Overview

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.

Data Flow

User interactions follow this flow:

GameCell (UI)
   ↓ emits event
GameBoard (UI container)
   ↓ calls
QueenGame (game engine)
   ↓ updates
BoardCell (game state)

Responsibilities

QueenGame.ts

  • 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)

BoardCell.ts

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.

GameBoard.vue

A container component responsible for rendering the grid of cells.

It receives the QueenGame instance and forwards events from GameCell to the game engine.

GameCell.vue

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)

Why This Design

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.

Key Principle

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.

Project Status

Work in progress.

Folder Structure

/
β”œβ”€β”€ 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

How to Run

Install dependencies

bun install

Start development server

bun run dev

The app will start at:

http://localhost:5173

Run unit tests

bun run test:unit

Run E2E tests

bun run test:e2e

Lint Code

bun run lint

Format Code

bun run format

Build

bun run build

License

MIT

AI Assistance

AI assistants should refer to .github/copilot-instructions.md for project guidelines.

About

WIP: A puzzle game based on the classic N-Queens problem.

Resources

License

Stars

Watchers

Forks

Contributors