Solver for Shenzen Solitaire
AGPL-3.0 License
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.
move :: Game -> Move -> Game
move
with MoveFromColumnToCell
move
with MoveFromCellToColumn
move
with BuildFromColumn
move
with BuildFromCell
move
with Pack
move
with CollectDragons
Card
sFlower
Move
appliedmayTakeTo
takes to last instance of a Dragon
, not firstPrelude.head: empty list
Exception: shouldn't be trying to build Flower
Flower
Move
smart constructors should error on Flower
Exception: Non-Suited card on Foundation
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[DragonCell]
should be a type that exposes only one empty cell at a time, cut down on solution spacecanonicalize
head
- maybe move to classy-prelude
?Move
slastCardsOfRuns
-Wincomplete-patterns
and -Wincomplete-uni-patterns
on util funcitonsTableau
has exactly one Foundation
per Suit
Tableau
has exactly one DragonCell
per Suit
FlowerCell
can only hold a Flower
Bound
or Enum
for Rank
mayTakeTo
and mkRunTo
want some kind of helpMove
constructors must enforce validity to avoid passing around broken Move
data, but then move
has none. Does this make move
clear or unsafe? What if this was more mature with Move
in its own module not exporting the default constructor?mayTakeTo
and unsafeTakeTo
. mkMove
must use the former but move
really wants unsafe to avoid unwrapping Maybe
. I can't even see how to unwrap it, really.novelPossibleMoves
, which is almost just move now (possibleMoves now) \\ previous game where now = current game
Card | CollectedDragons | Nothing
lost
correct? It might be only correct in the context of a depth-first search, where a previous
Tableau
would've already been searched for a win.Game
only includes Move
that apply to prior Tableau
?move
is only given Move
s generated from the Tableau
they're being applied to?nextRankForFoundation
should call nextCardForFoundation
automaticBuild
could be functor applicationLosses
it won't find shorter routes to Tableau
s it happened to time out on.Foundations
's three stacks in data constructor from the three data constructors for Suit
Timeouts
(carrying in existing Losses
)