shenzhen
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.
TODO
- 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-
movewithMoveFromColumnToCell -
movewithMoveFromCellToColumn -
movewithBuildFromColumn -
movewithBuildFromCell -
movewithPack -
movewithCollectDragons
-
- replicate automatic build of released
Cards - automatically build
Flower - automatically build after player
Moveapplied - 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:
mayTakeTotakes 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 -
Movesmart constructors should error onFlower - Bug:
Exception: Non-Suited card on Foundation - Bug:
Exception: element not in list, I warned you I was unsafe -
showcolsdoes 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
Cleanups and open questions:
- 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
Moves - there must be a nicer way to express
lastCardsOfRuns - Silence
-Wincomplete-patternsand-Wincomplete-uni-patternson util funcitons - Encode that
Tableauhas exactly oneFoundationperSuit - Encode that
Tableauhas exactly oneDragonCellperSuit - Encode that
FlowerCellcan only hold aFlower - Look at
BoundorEnumforRank -
mayTakeToandmkRunTowant some kind of help - The
Moveconstructors must enforce validity to avoid passing around brokenMovedata, but thenmovehas none. Does this makemoveclear or unsafe? What if this was more mature withMovein its own module not exporting the default constructor? - This ties into
mayTakeToandunsafeTakeTo.mkMovemust use the former butmovereally 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
lostcorrect? It might be only correct in the context of a depth-first search, where apreviousTableauwould've already been searched for a win. - Can I enforce that a
Gameonly includesMovethat apply to priorTableau? - Can I ensure
moveis only givenMoves generated from theTableauthey're being applied to? -
nextRankForFoundationshould callnextCardForFoundation -
automaticBuildcould be functor application - Would limiting to 100 moves cut off winning games? (Needs a second data structure; if I put timeouts in
Lossesit won't find shorter routes toTableaus 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
