Skip to content

About

Type-safe Solitaire in React + Vite + TypeScript — Klondike (draw 1 / draw 3), Spider (1, 2 or 4 suits) and FreeCell (Microsoft deal numbers) with drag and drop, click-to-move, full keyboard play, hints, undo, auto-complete, safe auto-moves to the foundations, autosave & resume, dental mode and English / Hungarian / German. Part of Arcade

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

 
 

Repository files navigation

♠️ Solitaire

English · Deutsch · Magyar

A type-safe Solitaire built with React 18, Vite and TypeScript: three classic patience games on one table — Klondike (draw 1 or draw 3), Spider (1, 2 or 4 suits) and FreeCell (the Microsoft deal numbers). Drag a card or a run, click a card to send it to the best place, tap your way through it on a phone, or play the whole game from the keyboard. Hints, undo, safe auto-moves and one-key auto-complete included; suits are told apart by shape, so colour is only a bonus. Runs on its own and as one of the fifteen games of Zoltan's Arcade: play it at dulzoltan.hu/arcade/#/play/solitaire.

CI license node react vite typescript


📑 Table of contents

🕹️ The Zoltan's Arcade family

Every game is a standalone repo that also plugs into the portal. They share one design system, one scoring model and one save format through the vendored src/arcade/ kit.

Game Repo Genre
🏠 Arcade portal Arcade-Game-Center All games in one place, live demos, stats, leaderboard
🧱 Tetris Tetris-Game Arcade
🐍 Snake Snake-Game Reflex
🔢 2048 2048-Game Puzzle
🟡 Pac-Man Pac-Man-Game Maze
⚫ Gomoku Gomoku-Game Strategy
🦷 Tic-Tac-Toe Tic-Tac-Toe-Game Duel
🏓 Breakout Breakout-Game Paddle
👾 Space Invaders Space-Invaders-Game Shooter
🔴 Connect Four Connect-Four-Game Duel
🔩 Nut Sort Nut-Sort-Game Sorting
🃏 Texas Hold'em Texas-Holdem-Game Cards
💣 Minesweeper Minesweeper-Game Logic
♠️ Solitaire (this repo) Solitaire-Game Cards
🎴 Ulti Ulti-Game Cards
🂡 Snapszer Snapszer-Game Cards

✨ Features

  • 🃏 Three games, six variants: Klondike draw 1 / draw 3, Spider 1 / 2 / 4 suits, FreeCell
  • 🖱️ Drag and drop of a card or a whole valid run (mouse, pen and touch through pointer events); an illegal drop snaps back
  • 👆 Touch play: on a phone, tap a card to select it (with the cards on it), tap a column, free cell or foundation to put it there, double-tap a card to send it home; dragging with a finger works too
  • 🖱️ Click-to-move: a mouse click sends the card (and the cards on it) to the best legal place — home first, then a column with cards (in Spider the same suit first), then an empty column, then a free cell
  • ⌨️ Full keyboard play: Tab between piles, Enter picks up and puts down, ↑ / ↓ change how many cards you hold
  • 💡 Hints (H): a dashed ring with “1” on the source and a solid ring with “2” on the target, plus the move in words — never colour alone; it prefers moves home, moves that turn a card over and moves that free a cell or column
  • ↩️ Undo (U / Ctrl+Z), up to 200 steps; it costs a little score
  • 🏠 Safe auto-moves: aces, twos and any card no column can still need go home by themselves after each move (not after you take a card back down from a foundation — that would undo your move)
  • 📖 Rules (R or the Rules button): goal, controls, the rules of all three games, the scoring with its real numbers and a diagram of an alternating run — opening it pauses the game
  • 🏁 Finish early: the Finish button ends the game after a confirm; it goes on the leaderboard with 0 points, because only a won game scores
  • ⛶ Full screen: the Full screen button lets the table fill the screen (on iPhone a full-window mode, since Safari has no full-screen API for pages)
  • ⏩ Auto-complete (A): once every card is open (Klondike: stock and waste empty; FreeCell: every column in order) one key plays the rest home
  • 🎴 Kit playing cards: the arcade's four-colour French deck (♠ black, ♥ red, ♦ blue, ♣ green) or a single grey tone (Deck setting), with a small suit shape in each card's corner so the suit stays readable even when the cards overlap tightly
  • 🏆 Unified arcade scoring — only a won game scores; Best, plays and wins are shared with the portal
  • ⏸️ Pause (P, or automatically when the tab is hidden) — freezes the table and the timer
  • 💾 Autosave & resume — the position, its undo history, the clock and the undo count survive reloads and tab closes
  • 🌗 Dark / light theme shared with every arcade game (press T)
  • 🌍 Three languages — English, Hungarian (Pasziánsz: Klondike / Pók / FreeCell) and German (Solitär); switch in the top bar (also ?lang=en|hu|de); numbers follow the locale's grouping, card names in screen-reader labels follow the language
  • 🦷 Dental mode — a „Fogas mód” / "Dental mode" / „Zahnmodus” toggle in the settings, off by default (or ?dental=1), shared across the whole arcade: the card backs carry a tooth, the empty foundations show a tooth and a win is “Every tooth in its place!”; presentation only
  • 🤖 Attract-mode demo (<SolitaireDemo />) — replays a scripted solution of FreeCell deal #164 on the real engine
  • 📱 Phone-friendly — the cards scale with the table width, so a 360 px screen fits all seven Klondike, eight FreeCell and ten Spider columns without horizontal scroll (below 430 px the columns sit closer and face-down cards overlap more tightly); the card size is also capped by the screen height, so a phone in landscape still shows the whole table; long columns squeeze to fit
  • ♿ Accessible — every pile is a focusable button labelled with its contents (Column 3: 5 cards (3 face down), top: Seven of hearts)
  • 🧪 Tested — rules of all three engines, the deal formats, undo, scoring, hints, click targets, saves, drag and drop, keyboard play, touch play, the rules dialog, finishing early, full screen, auto-complete, languages, dental mode and the demo

🚀 Quick start

Requires Node ≥ 20 and npm.

git clone https://github.com/ZoliQua/Solitaire-Game.git
cd Solitaire-Game
npm install
npm run dev          # http://localhost:5173

Production build: npm run build && npm run preview. The build uses relative asset paths, so dist/ can be served from any sub-folder.

📏 Games and rules

Game Deal Building Goal
Klondike 7 columns of 1…7 cards (top card up), 24 in the stock Down, alternating red (♥ ♦) and black (♠ ♣); only a King (or a King-led run) fills an empty column All 52 cards on the four foundations, ace to king
Spider 104 cards: 10 columns of 6,6,6,6,5,5,5,5,5,5 (top up), 50 in the stock Down regardless of suit; only same-suit runs move together; any card fills an empty column Eight complete K→A same-suit runs, each removed as soon as it forms
FreeCell All 52 cards open in 8 columns (Microsoft deal numbers 1…32 000) Down, alternating red (♥ ♦) and black (♠ ♣); 4 free cells hold one card each All 52 cards home
  • Klondike — Draw 1 turns one card, Draw 3 turns three; the last three waste cards are fanned but only the top one plays. Redeals are unlimited. A foundation's top card may come back down onto a column.
  • Spider — the stock deals one card onto every column and is refused while a column is empty. 1 suit uses ♠ only, 2 suits ♠ ♥, 4 suits all four.
  • FreeCell — runs move as a “supermove” of up to (free cells + 1) × 2^(empty columns) cards (an empty target column does not count). Deals are seeded with the classic Microsoft generator, so deal #1 here is deal #1 in Windows.
  • Klondike and Spider deals come from a seeded shuffle; the deal number is shown under the Game buttons.

Switching the game or the variant, or New game, during a game asks first with an in-page card; the last chosen variant is remembered (solitaire.mode), and so is the deck (solitaire.deck).

⌨️ Controls

Key Action
🖱️ Drag a card Move it and every card on it; drop on a pile
🖱️ Click a card Send it to the best legal place
🖱️ Click the stock Draw (Klondike) / deal a row (Spider)
Tab / Shift+Tab Move between piles
Enter / Space on a pile Pick up its movable cards; on another pile, put them down; on the same pile, send them to the best place
↑ / ↓ Hold more / fewer cards of the picked run
H Hint
U / Ctrl+Z / Cmd+Z Undo
A Auto-complete (every card home)
D Draw / deal from the stock
N New game (asks first)
Esc Drop what you picked up · pause
Enter Start · play again (outside a pile)
P Pause / resume
R Rules (pauses the game)
T Toggle dark / light theme (standalone page)

Hotkeys are ignored while you type in a form field or hold Cmd/Ctrl/Alt (Ctrl/Cmd+Z excepted).

📱 On a phone

Tap a card to select it — the cards on top of it come along, and the status line says what you hold. Then tap the column, free cell or foundation where it should go; if it does not fit there, the tapped card becomes your new selection instead. Double-tap a card to send it home (if it cannot go home, it goes to the best other place). You can also drag cards with your finger, tap the stock to draw or deal, and tap the table between the piles to drop the selection. The table never scrolls or zooms under your finger. Full screen gives the cards the whole screen; on an iPhone, Share → Add to Home Screen also hides the browser bars.

🎯 Scoring & stats

Only a won game scores:

score      = round(base × timeFactor × undoFactor)
timeFactor = clamp(par / seconds, 0.25, 1)
undoFactor = max(0.5, 1 − 0.02 × undos)
Game Base Par
Klondike draw 1 1000 300 s
Klondike draw 3 1500 300 s
Spider 1 suit 1000 600 s
Spider 2 suits 2000 600 s
Spider 4 suits 4000 600 s
FreeCell 2000 420 s

The highest possible score is 4000 (Spider, four suits, on par, no undo). A won game is recorded once with outcome win. The Finish button ends a game early after a confirm: it is recorded with outcome finished and goes on the leaderboard with 0 points. A game abandoned through New game or a variant switch is not recorded. The stat strip shows Moves, the active Time, the Score (0 until you win) and Best. Every finished game is recorded by the kit under arcade.stats.solitaire.

💾 Autosave & resume

The session is the whole game: the variant, the position, the undo history (at most 200 states), the move and undo counters and the clock (key arcade.session.solitaire, schema-versioned). A validator checks every saved position — pile counts per variant and the whole deck present (52 or 104 cards) — and rejects a save whose variant does not match its position. It is saved about once a second, whenever the page is hidden and when the game unmounts. Reloading shows Welcome back with the game paused. A won or finished game is never restored, so it can never be recorded twice; Discard next to the Autosaved badge drops the save.

🧩 Embedding in Arcade-Game-Center

import { SolitaireGame, SolitaireDemo } from '@arcade/solitaire';

<SolitaireGame
  embedded                                   // hide the page title; the portal shows it
  onGameOver={(score, info) => submitScore('solitaire', score, info)}  // won games; Finish reports 0 with { finished: true }
  onRunEnd={(run) => console.log(run.outcome, run.score)}  // every finished game
  onStatusChange={({ status, score, seconds }) => updateHeader(status, seconds)}
/>
<SolitaireDemo active={cardIsVisible} />     // landing-page attract mode

Both components follow the shared ArcadeGameProps / ArcadeDemoProps contract from src/arcade/types.ts. Status mapping: idle before the first game of a visit (Start overlay), running while a game is played, paused while suspended (or restored), gameOver once the game is won or finished early.

🧪 Scripts

Script Purpose
npm run dev Vite dev server
npm run build Type-check (tsc -b) and production build
npm run preview Serve the production build
npm run typecheck tsc -b
npm run lint ESLint
npm test Vitest (logic + component tests)
npm run check lint → typecheck → test → build (what CI runs)

🏗️ Architecture

src/
  arcade/                 vendored Zoltan's Arcade kit — do not edit here (see its README)
  logic/                  pure game rules, no React
    types.ts deck.ts      cards, piles, seeded shuffle, foundation and run rules
    klondike.ts spider.ts freecell.ts   deals and moves of each game
    moves.ts              legalMoves, applyMove, isWon, autoFoundation, canAutoComplete
    history.ts            immutable undo stack
    game.ts               modes, playMove (+ safe auto-moves), undo, auto-complete, hints, click targets, save validator
    scoring.ts            base × timeFactor × undoFactor
  components/             Table, Pile, SuitMark, useDrag (pointer events), layout (card size, fans), DentalToggle
  rules.tsx               Rules dialog sections (numbers from the real constants) and the run diagram
  labels.ts               variant / mode names and the touch help rows
  demo/                   SolitaireDemo + the scripted FreeCell solution
  SolitaireGame.tsx       the embeddable game (ArcadeGameProps)
  i18n.ts                 en / hu / de strings
  App.tsx                 standalone page: <ArcadeShell><SolitaireGame/></ArcadeShell>
  main.tsx                entry; loads the shared arcade.css once
  index.ts                package entry used by Arcade-Game-Center

The UI changes the position only through the engine (playMove, undoGame, autoComplete, finishGame in logic/game.ts), which never mutates its input — undo simply restores a stored state.

🤝 Contributing

See CONTRIBUTING.md, the Code of Conduct and SECURITY.md. Contributors are listed in CONTRIBUTORS.md; changes in CHANGELOG.md.

⚖️ License

MIT © 2026 Zoltán Dul


Made with ❤️ by Zoltán Dul · part of Zoltan's Arcade

About

Type-safe Solitaire in React + Vite + TypeScript — Klondike (draw 1 / draw 3), Spider (1, 2 or 4 suits) and FreeCell (Microsoft deal numbers) with drag and drop, click-to-move, full keyboard play, hints, undo, auto-complete, safe auto-moves to the foundations, autosave & resume, dental mode and English / Hungarian / German. Part of Arcade

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages