Skip to content

MohnDoe/sudoecool

Repository files navigation

Sudoecool

A daily sudoku puzzle game built as a Discord Activity, letting you play the same puzzle as your friends and compete on leaderboards.

Features

  • Daily Puzzles — One unique sudoku per day, available in three difficulty levels (Easy, Medium, Hard). All players get the same puzzle on the same day.
  • Discord Integration — Authenticate via Discord OAuth2, play inside Discord servers, and see server-specific leaderboards.
  • Leaderboards — Compete on time-based rankings (with notes and mistakes as tiebreakers). Top 10 + your position shown when you rank outside it.
  • Progress Persistence — Auto-saves game state (grid, notes, timer, mistakes) every 10 seconds and after key actions.
  • Trophies — Earn badges for flawless runs: 💎 No mistakes and ✨ No notes.
  • Responsive Design — Works in Discord's focused and picture-in-picture layouts, with CSS container queries for fluid sizing.

Tech Stack

Layer Technology
Framework Nuxt 4 (SSR disabled, SPA)
UI Nuxt UI + Tailwind CSS v4
State Pinia
Database PostgreSQL via Drizzle ORM
Auth nuxt-auth-utils + Discord OAuth2
Sudoku Engine sudoku-core
Error Handling neverthrow (Result types)
Validation Zod v4
Discord SDK @discord/embedded-app-sdk
Fonts Nuxt Fonts (Switzer, Alpino local; Outfit Google)
CI/CD GitHub Actions → Railpack → GHCR → Dokploy

Architecture

app/                    # Frontend (Vue 3 + Nuxt)
├── components/sudoku/  # Game UI: Board, Cell, NPad, Modals
├── stores/             # Pinia stores: game, leaderboard, discord, timer, homepage
├── composables/        # useGameSync, useCountdown, useDiscordSdk/Event
├── pages/              # / (homepage), /daily/[difficulty] (game page)
├── assets/css/         # Tailwind config, elevated design tokens, sudoku grid styles
└── plugins/            # Discord SDK client initialization

server/                 # Backend (Nitro / Nuxt server)
├── api/                # REST endpoints
│   ├── auth/discord/   # OAuth2 token exchange
│   ├── games/daily/    # Puzzle fetch, progress, verification, status
│   └── leaderboard/    # Server-scoped rankings
├── services/           # Business logic (GameService, LeaderboardService, UserService)
├── db/                 # Drizzle schema, migrations, relations
├── tasks/              # Nitro tasks (db:seed for dev data)
└── utils/              # DB client, leaderboard queries, time/date helpers

shared/                 # Code shared between client and server
├── types/              # TypeScript interfaces (game, sudoku, discord, errors)
└── utils/              # Sudoku generation, parsing, verification, avatar URLs

Database Schema

Table Purpose
users Discord user profiles (id, username, avatar, discriminator)
user_guild_memberships Which users belong to which Discord servers
daily_puzzles One sudoku puzzle per date + difficulty (81-char string, unique constraint)
game_progress Per-user per-puzzle state (grid, moves, notes, hints, mistakes, time, completion)

Leaderboard indexes are partial (WHERE is_completed = true) on timeSpent, notes, hints, moves, and mistakes.

API Endpoints

All endpoints require Discord authentication via session.

Method Path Description
POST /api/auth/discord/token Exchange Discord OAuth2 code for session
GET /api/games/daily/[difficulty] Get today's puzzle + user progress
GET /api/games/daily Convenience: today's easy puzzle
POST /api/games/progress Save game state (grid, timer, stats)
POST /api/games/verify Verify completed board, mark as done
GET /api/games/daily/status User's status across all 3 daily difficulties
GET /api/games/daily/statuses/global Global completion counts per difficulty
GET /api/games/daily/statuses/guild/[guildId] Guild-specific completion stats
GET /api/leaderboard/daily/[scope]/[difficulty] Top 7 leaderboard entries + player context

Development

Prerequisites

  • Node.js (LTS)
  • Bun (package manager)
  • PostgreSQL database

Setup

# Install dependencies
bun install

# Copy and configure environment
cp .env.example .env   # (create .env with your values)

Environment Variables

Variable Description
NUXT_DATABASE_URL PostgreSQL connection string
NUXT_SESSION_PASSWORD Session encryption key (32+ chars)
NUXT_DISCORD_CLIENT_ID Discord OAuth2 Client ID
NUXT_DISCORD_CLIENT_SECRET Discord OAuth2 Client Secret
NUXT_PUBLIC_DEV_MODE Set to true for mock Discord auth in dev

Commands

bun dev              # Start dev server (hosted, accessible on LAN)
bun dev:discord      # Start dev server with Discord Activity mode
bun build            # Production build
bun generate         # Static generation
bun preview          # Preview production build
bun lint             # ESLint
bun ngrok            # Expose dev server via ngrok

Database

# Run migrations
bun drizzle-kit generate   # Generate migration from schema changes
bun drizzle-kit push       # Push schema to database
bun drizzle-kit migrate     # Run pending migrations

# Seed dev data (400 fake users, random progress)
npx nitro run db:seed

Design

"Elevated" UI System

Custom CSS variables create a tactile, layered aesthetic with hard box shadows (no blur):

.elevated {
  box-shadow: 0 8px 0 4px var(--box-shadow-color), 0 0 0 4px var(--box-shadow-color);
}

Buttons and cards "press down" on hover/active states by translating along the Y axis, maintaining the shadow offset.

Sudoku Grid

  • Built with CSS Grid (3×3 subgrids, each containing a 3×3 cell grid)
  • Uses CSS container queries (100cqmin) for responsive sizing
  • Related cells (same row/column/box) and same-number highlighting
  • Notes rendered as a 3×3 sub-grid within each cell

Game Sync

The useGameSync composable provides:

  1. Debounce saves — 2s after player actions (insert/clear)
  2. Periodic autosave — Every 10 seconds with change detection
  3. Immediate saves — On pause and reset grid
  4. Final save — On component unmount

Discord Activity

The app runs inside Discord as an Embedded App. Key integrations:

  • OAuth2 flow — Discord SDK authorize() → code exchange → session creation
  • Layout modes — Adapts to FOCUSED (full game) and PIP (minimal title)
  • Guild context — Server leaderboards show only members of the host server
  • Dev mock mode — When NUXT_PUBLIC_DEV_MODE=true, uses @discord/embedded-app-sdk's DiscordSDKMock with fake guild/user data

Code Quality

  • TypeScript strict mode with typeCheck: true
  • ESLint with @antfu/eslint-config
  • neverthrow plugin for exhaustive error handling on Result types
  • Zod v4 for runtime API body validation
  • Drizzle ORM for type-safe database queries with auto-generated relations

Deployment

CI/CD pipeline (.github/workflows):

  1. Push to main or staging → GitHub Actions
  2. Railpack builds the container image (BuildKit enabled)
  3. Image pushed to GHCR
  4. Webhook triggers Dokploy for deployment

License

Private project.

About

A daily Sudoku puzzles activity for Discord made with Vue 3 and Nuxt 4.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors