Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions ComputerSolitaire/Game/Shared/GameMode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ enum GameMode: String, CaseIterable, Codable {
case spiderTwoSuits = "spider.suits2"
case spiderFourSuits = "spider.suits4"
case freecell
case tripeaks
case pyramid
case tripeaks
case golf
case fortyThieves = "fortythieves"
case yukon
case scorpion
case fortyThieves = "fortythieves"
case canfield

var variant: GameVariant {
Expand Down
4 changes: 2 additions & 2 deletions ComputerSolitaire/Game/Shared/GameVariant.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ enum GameVariant: String, CaseIterable, Codable {
case klondike
case spider
case freecell
case tripeaks
case pyramid
case tripeaks
case golf
case fortyThieves = "fortythieves"
case yukon
case scorpion
case fortyThieves = "fortythieves"
case canfield

var title: String {
Expand Down
14 changes: 4 additions & 10 deletions ComputerSolitaire/GameMenuCommands.swift
Original file line number Diff line number Diff line change
Expand Up @@ -120,17 +120,11 @@ struct GameMenuCommands: Commands {
}
}

/// ⌘1 through ⌘9 select the first nine variants and ⌘0 the tenth; any
/// beyond that get no shortcut — there are only ten digit keys.
/// ⌘0 through ⌘9 select the first ten variants in presentation order;
/// any beyond that get no shortcut — there are only ten digit keys.
private static func variantShortcut(at index: Int) -> KeyEquivalent? {
let ordinal = index + 1
if ordinal <= 9 {
return KeyEquivalent(Character("\(ordinal)"))
}
if ordinal == 10 {
return "0"
}
return nil
guard (0...9).contains(index) else { return nil }
return KeyEquivalent(Character("\(index)"))
}
}
#endif
8 changes: 4 additions & 4 deletions ComputerSolitaire/Views/AboutView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import SwiftUI

#if os(iOS)
enum AppInfo {
static let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? ""
static let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown"
static let copyrightYear = String(Calendar.current.component(.year, from: Date()))
static let githubURL = URL(string: "https://github.com/austin-smith/ComputerSolitaire")
}
Expand Down Expand Up @@ -32,7 +32,7 @@ struct AboutView: View {

Text(
"Computer Solitaire is a low-frills, ad-free solitaire game for your computer. "
+ "Includes Klondike, FreeCell, and other things you enjoy."
+ "Includes Klondike, Spider, FreeCell, and other things you enjoy."
)
.font(.system(size: 14))
.foregroundStyle(.primary)
Expand Down Expand Up @@ -88,7 +88,7 @@ struct AboutView: View {
@Environment(\.openURL) private var openURL

private var appVersion: String {
Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? ""
Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "Unknown"
}

private var copyrightYear: String {
Expand Down Expand Up @@ -121,7 +121,7 @@ struct AboutView: View {

Text(
"Computer Solitaire is a low-frills, ad-free solitaire game for your computer. "
+ "Includes Klondike, FreeCell, and other things you enjoy."
+ "Includes Klondike, Spider, FreeCell, and other things you enjoy."
)
.font(.system(size: 11))
.foregroundStyle(.primary)
Expand Down
139 changes: 70 additions & 69 deletions ComputerSolitaire/Views/RulesAndScoringView.swift

Large diffs are not rendered by default.

28 changes: 16 additions & 12 deletions ComputerSolitaire/Views/StatisticsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ private struct StatisticsOverviewView: View {
Text("Games")
}

#if os(iOS)
Section {
Button("Reset All Statistics", role: .destructive) {
isShowingResetConfirmation = true
}
}
#endif

Text("Tracked since \(statsTrackedSinceLabel(aggregate))")
.font(.footnote)
.foregroundStyle(.tertiary)
Expand All @@ -102,12 +110,6 @@ private struct StatisticsOverviewView: View {
isShowingResetConfirmation = true
}
}
#else
ToolbarItem(placement: .topBarLeading) {
Button("Reset Stats") {
isShowingResetConfirmation = true
}
}
#endif
ToolbarItem(placement: .confirmationAction) {
Button("Done", action: onDone)
Expand Down Expand Up @@ -287,6 +289,14 @@ private struct GameStatisticsDetailView: View {
Text("Performance")
}

#if os(iOS)
Section {
Button("Reset \(effectiveScopeTitle) Statistics", role: .destructive) {
isShowingResetConfirmation = true
}
}
#endif

Text("Tracked since \(statsTrackedSinceLabel(stats))")
.font(.footnote)
.foregroundStyle(.tertiary)
Expand All @@ -304,12 +314,6 @@ private struct GameStatisticsDetailView: View {
isShowingResetConfirmation = true
}
}
#else
ToolbarItem(placement: .topBarLeading) {
Button("Reset Stats") {
isShowingResetConfirmation = true
}
}
#endif
ToolbarItem(placement: .confirmationAction) {
Button("Done", action: onDone)
Expand Down
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Computer Solitaire is a fully native Solitaire app for iOS, iPadOS, and macOS.
## Features

- Fully native apps for iOS, iPadOS, and macOS
- Multiple game variants: **Klondike** (both 1-card and 3-card draw), **Spider** (1, 2, or 4 suits), **FreeCell**, **TriPeaks**, **Pyramid**, **Golf**, **Forty Thieves**, **Yukon**, **Scorpion**, and **Canfield**
- Multiple game variants: **Klondike** (both 1-card and 3-card draw), **Spider** (1, 2, or 4 suits), **FreeCell**, **Pyramid**, **TriPeaks**, **Golf**, **Yukon**, **Scorpion**, **Forty Thieves**, and **Canfield**
- Automatic game persistence and resume
- Customizable table appearance
- Other things you enjoy
Expand All @@ -23,12 +23,12 @@ Computer Solitaire is a fully native Solitaire app for iOS, iPadOS, and macOS.
| Variant | Description | Rules |
|---------|-------------|-------|
| **Klondike** | Classic Solitaire, with 1-card and 3-card draw modes | [Rules](docs/rules/klondike.md) |
| **Spider** | Two decks, ten piles — build full suit runs from King to Ace, with 1/2/4-suit difficulty | [Rules](docs/rules/spider.md) |
| **FreeCell** | Strategy-focused variant where every card is visible from the start | [Rules](docs/rules/freecell.md) |
| **TriPeaks** | Chain uncovered cards one rank up or down to level three peaks | [Rules](docs/rules/tripeaks.md) |
| **Pyramid** | Pair exposed cards totaling 13 to dismantle a 28-card pyramid | [Rules](docs/rules/pyramid.md) |
| **Golf** | Play columns down to nothing, one rank up or down, scored like golf across a nine-hole match | [Rules](docs/rules/golf.md) |
| **Forty Thieves** | Two decks, ten columns — build down by suit one card at a time, managing a single-pass stock | [Rules](docs/rules/fortythieves.md) |
| **Yukon** | Klondike's wilder sibling — no stock, and any face-up card moves with everything stacked on it | [Rules](docs/rules/yukon.md) |
| **Scorpion** | Spider's single-deck cousin — untangle suit runs in place with Yukon-style group moves | [Rules](docs/rules/scorpion.md) |
| **Canfield** | The casino classic (Demon in Britain) — drain a 13-card reserve onto foundations that start at a dealt rank and wrap | [Rules](docs/rules/canfield.md) |
| **Spider** | Build full King-to-Ace suit runs across ten piles, using two decks composed of 1, 2, or 4 suits | [Rules](docs/rules/spider.md) |
| **FreeCell** | All 52 cards are dealt face up, with four free cells that each hold one card | [Rules](docs/rules/freecell.md) |
| **Pyramid** | Remove pairs of exposed cards that total 13 to clear a 28-card pyramid | [Rules](docs/rules/pyramid.md) |
| **TriPeaks** | Play uncovered cards one rank above or below the waste to clear three peaks | [Rules](docs/rules/tripeaks.md) |
| **Golf** | Play column cards one rank above or below the waste, scored across a nine-hole match where lower is better | [Rules](docs/rules/golf.md) |
| **Yukon** | Like Klondike, but with no stock — any face-up card moves along with every card stacked on it | [Rules](docs/rules/yukon.md) |
| **Scorpion** | Build down by suit to complete four King-to-Ace runs in place, moving face-up cards with everything stacked on them | [Rules](docs/rules/scorpion.md) |
| **Forty Thieves** | Build ten columns down by suit, one card at a time, using two decks and a single pass through the stock | [Rules](docs/rules/fortythieves.md) |
| **Canfield** | Play a 13-card reserve onto foundations that start at a dealt base rank and wrap | [Rules](docs/rules/canfield.md) |
32 changes: 16 additions & 16 deletions docs/rules/canfield.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
# Canfield Rules

These rules describe Canfield as implemented in the app: the strict classic gameknown as Demon in Britain, where it was first recordedwith a thirteen-card reserve, foundations that start at a dealt base rank and wrap, whole-pile tableau moves only, and a draw-three stock with unlimited redeals. The classic sources agree on the essentials; where modern software commonly relaxes them, the choices made here (and why) are called out below.
These rules describe Canfield as implemented in the app: the strict classic game, known as Demon in Britain where it was first recorded, with a thirteen-card reserve, foundations that start at a dealt base rank and wrap, whole-pile tableau moves only, and a draw-three stock with unlimited redeals. The classic sources agree on the essentials. Where modern software commonly relaxes them, the choices made here (and why) are called out below.

## Objective
Move all 52 cards onto the four foundations, building each up by suit from the base rank the rank dealt to the first foundation and turning the corner from King to Ace along the way.
Move all 52 cards onto the four foundations, building each up by suit from the base rank (the rank dealt to the first foundation) and turning the corner from King to Ace along the way.

## Terminology
- **Reserve:** A packet of thirteen cards, all face down except the exposed top card, which is always playable. British sources call it *the demon*. It must fill any empty tableau pile at once.
- **Base card:** The card dealt face up to the first foundation; its rank is where all four foundations start.
- **Base card:** The card dealt face up to the first foundation its rank is where all four foundations start.
- **Tableau:** Four piles built down in alternating colors, wrapping from Ace to King. A pile moves onto another only in its entirety.
- **Foundations:** Four suit piles built up from the base rank, wrapping from King to Ace. Cards placed here never return to play.
- **Stock:** The face-down draw pile (34 cards after the deal). Three cards turn at a time, with unlimited redeals.
- **Waste:** Face-up cards turned from the stock; only the top card is playable.
- **Waste:** Face-up cards turned from the stock only the top card is playable.

## Setup
- Use one standard 52-card deck (no jokers).
Expand All @@ -22,9 +22,9 @@ Move all 52 cards onto the four foundations, building each up by suit from the b

## Play
### Tableau
- Build piles **down in alternating colors**, turning the corner from Ace to King the K♠ plays onto the A♥ or A♦.
- A pile moves onto another pile **only in its entirety**; a partial sequence never moves, however well packed. The exposed top card may always play to a foundation.
- An **empty pile** fills at once from the reserve's top card — this is compulsory, and the app performs it automatically. Once the reserve is empty, fill a space with the **top waste card** whenever you choose; a space never fills from another tableau pile.
- Build piles **down in alternating colors**, turning the corner from Ace to King (the K♠ plays onto the A♥ or A♦).
- A pile moves onto another pile **only in its entirety** a partial sequence never moves, however well packed. The exposed top card may always play to a foundation.
- An **empty pile** fills at once from the reserve's top card — this is compulsory, and the app performs it automatically. Once the reserve is empty, fill a space with the **top waste card** whenever you choose. A space never fills from another tableau pile.

### Foundations
- Each foundation starts with a card of the **base rank** and builds up in its suit, turning the corner from King to Ace, until it holds all thirteen cards.
Expand All @@ -34,9 +34,9 @@ Move all 52 cards onto the four foundations, building each up by suit from the b
- The exposed top card is always available, to a foundation or onto a tableau pile. When it leaves, the next reserve card turns face up.

### The stock
- Tap the stock to turn **three** cards face up onto the waste, order preserved; fewer than three remaining turn together.
- Tap the stock to turn **three** cards face up onto the waste, order preserved. If fewer than three remain, they turn together.
- Only the top waste card is playable, to the tableau or a foundation.
- When the stock is spent, tap again to turn the waste overunshuffled as the new stock. **Redeals are unlimited.**
- When the stock is spent, tap again to turn the waste over, unshuffled, as the new stock. **Redeals are unlimited.**

## Scoring
- Waste to tableau: +5.
Expand All @@ -48,15 +48,15 @@ Move all 52 cards onto the four foundations, building each up by suit from the b
- The score never goes below zero.

## Winning
You win by moving all 52 cards to the foundations. The game is lost when no play exists from the reserve, tableau, or waste, and a full unchanged pass through the stock surfaces nothing playable. Canfield's reputation as a grind is earned — the casino legend has players buying the deck for $50 and winning $5 per card banked, against an average of five or six — but the deals themselves are more generous than the legend: solver studies find roughly two thirds winnable under these strict rules, with expert play converting about a third. Draining the reserve is the heart of the game.
You win by moving all 52 cards to the foundations. The game is lost when no play exists from the reserve, tableau, or waste, and a full unchanged pass through the stock surfaces nothing playable. Canfield's reputation as a grind is earned. The casino legend has players buying the deck for $50 and winning $5 per card banked, against an average of five or six. The deals themselves are more generous than the legend: solver studies find roughly two thirds winnable under these strict rules, with expert play converting about a third. Draining the reserve is the heart of the game.

## Rule choices
The classic sources are unusually consistent for a patience game; the choices here follow them against the relaxations common in software:
- **Whole-pile movement only** — the classic rule in every printed source (Coops' *100 Games of Solitaire*: "sequences on tableau may be moved bodily, but not parts of sequences"; Morehead & Mott-Smith agree). Much modern software permits partial-sequence moves, a measurable easing (solver studies put it at roughly four points of winnability); the app keeps the by-the-book rule.
- **Wrapping in both directions** — foundations turn King-to-Ace and tableau builds turn Ace-to-King. Both are definitional: with a random base rank, the cards just below it would otherwise be nearly unplayable.
- **Compulsory reserve fill** — a space takes the reserve's top card at once, not at the player's option; choice enters only after the reserve is out, when the top waste card may fill a space (and nothing else may — not another tableau pile).
- **Locked foundations** — a banked card never returns to play. No classic source permits "worrying back"; some apps offer it as a house option.
- **Draw three with unlimited redeals** universal across the classic sources. Deal-one variants exist (Rainbow) but are a different, far easier game. The in-app **Redeal** command replays the same deal from the start; it is a fresh attempt at the layout, not the stock recycle, which is unlimited and free.
The classic sources are unusually consistent for a patience game. The choices here follow them against the relaxations common in software:
- **Whole-pile movement only.** The classic rule in every printed source (Coops' *100 Games of Solitaire*: "sequences on tableau may be moved bodily, but not parts of sequences", and Morehead & Mott-Smith agree). Much modern software permits partial-sequence moves, a measurable easing (solver studies put it at roughly four points of winnability). The app keeps the by-the-book rule.
- **Wrapping in both directions.** Foundations turn King-to-Ace and tableau builds turn Ace-to-King. Both are definitional: with a random base rank, the cards just below it would otherwise be nearly unplayable.
- **Compulsory reserve fill.** A space takes the reserve's top card at once, not at the player's option. Choice enters only after the reserve is out, when the top waste card may fill a space (and nothing else may — not even another tableau pile).
- **Locked foundations.** A banked card never returns to play. No classic source permits "worrying back", though some apps offer it as a house option.
- **Draw three with unlimited redeals.** This is universal across the classic sources. Deal-one variants exist (Rainbow) but are a different, far easier game. The in-app **Redeal** command replays the same deal from the start a fresh attempt at the layout, not the stock recycle, which is unlimited and free.

## Sources
- https://en.wikipedia.org/wiki/Canfield_(solitaire)
Expand Down
Loading