Solves games of Shenzhen Solitaire, a minigame available standalone or as part of Shenzhen I/O from Zachtronics. Here's an video intro to the gameplay, but if you know FreeCell it's basically that with three suits and "dragon" cards that block gameplay. And this glossary of Solitaire terms may be useful.
Written for practice with Haskell and HaskellStack.
- create Stack project
- types for cards
- create standard deck
- Layout type
- custom Show instances for Card, Layout
- enter game Layout
- shuffle deck to create Layout
- Game type
- Use standard terms
- Move type for card moves, collecting dragons
-
move :: Game -> Move -> Game
-
move
withMoveFromColumnToCell
-
move
withMoveFromCellToColumn
-
move
withBuildFromColumn
-
move
withBuildFromCell
-
move
withPack
-
move
withCollectDragons
-
- replicate automatic build of released
Card
s - automatically build
Flower
- automatically build after player
Move
applied - automatically build at game start and between moves
- Detect game win
- Detect game loss
- Generate list of possible Moves for a position
- Filter possible Moves against Game history to avoid loops
- Bug: solver loops infinitely at losing states
- Take moves until game win/loss
- QuickCheck that the number + distribution of cards in the game is constant
- Bug:
mayTakeTo
takes to last instance of aDragon
, not first - Bug:
Prelude.head: empty list
- Bug:
Exception: shouldn't be trying to build Flower
- Bug: automatic building isn't always building the
Flower
-
Move
smart constructors should error onFlower
- Bug:
Exception: Non-Suited card on Foundation
- Bug:
Exception: element not in list, I warned you I was unsafe
-
showcols
does not print empty space or__
for empty columns so columns shift left -
obviated by[DragonCell]
should be a type that exposes only one empty cell at a time, cut down on solution spacecanonicalize
- Some kind of memoization to avoid solver re-attempting from known-losing positions
- Run a hundred times, report statistics
- Bug: trying to move Flower to cell, add tests
- Solver should filter to only pack to leftmost empty column
- Never use
head
- maybe move toclassy-prelude
? - Organize code into modules, don't export constructors or the many unsafe utility functions for
Move
s - there must be a nicer way to express
lastCardsOfRuns
- Silence
-Wincomplete-patterns
and-Wincomplete-uni-patterns
on util funcitons - Encode that
Tableau
has exactly oneFoundation
perSuit
- Encode that
Tableau
has exactly oneDragonCell
perSuit
- Encode that
FlowerCell
can only hold aFlower
- Look at
Bound
orEnum
forRank
-
mayTakeTo
andmkRunTo
want some kind of help - The
Move
constructors must enforce validity to avoid passing around brokenMove
data, but thenmove
has none. Does this makemove
clear or unsafe? What if this was more mature withMove
in its own module not exporting the default constructor? - This ties into
mayTakeTo
andunsafeTakeTo
.mkMove
must use the former butmove
really wants unsafe to avoid unwrappingMaybe
. I can't even see how to unwrap it, really. - And
novelPossibleMoves
, which is almost justmove now (possibleMoves now) \\ previous game where now = current game
- Require cells be used left-to-right to cut down state space of possible moves.
- DragonCell could model explicitly that it's
Card | CollectedDragons | Nothing
- Is
lost
correct? It might be only correct in the context of a depth-first search, where aprevious
Tableau
would've already been searched for a win. - Can I enforce that a
Game
only includesMove
that apply to priorTableau
? - Can I ensure
move
is only givenMove
s generated from theTableau
they're being applied to? -
nextRankForFoundation
should callnextCardForFoundation
-
automaticBuild
could be functor application - Would limiting to 100 moves cut off winning games? (Needs a second data structure; if I put timeouts in
Losses
it won't find shorter routes toTableau
s it happened to time out on. - Use some clever extension to derive
Foundations
's three stacks in data constructor from the three data constructors forSuit
- On loss, try from
Timeouts
(carrying in existingLosses
) - Record stats about the time it takes to solve games