This package provides the game logic of Zapster Solitaire, my original solitaire/patience game, as well as utilities and examples for bot play. I made it available in the hopes of seeing fellow coders develop better bot players or interesting game variations with it.
Learn how to play Zapster Solitaire or play the Web version.
Everything here is for use by fellow code nerds. If you only want to play Zapster Solitaire without doing any coding, use the Web version above (not open source).
This project has a Code of Conduct. By participating in the Git repo or issues tracker, you agree to be as courteous, welcoming, and generally a lovely person as its terms require. 😊
- Rules implementation:
zapster.mjs
- Bot proving grounds:
botTrial.mjs
- Stricter rules enforcement:
strictEnforcement.mjs
This module implements everything needed to play Zapster Solitaire, and it's the exact same module used by the Web version.
Each object represents one card, and a given card's object will be reused as it reappears throughout the state
and moveLog
objects. It has these properties:
rank
: Can be any of the values passed tostart
assettings.ranks
.suit
: An integer in the range 0-3. It doesn't matter which suit you map to which integer, but the mapping the web game uses is 0 = hearts, 1 = spades, 2 = diamonds, and 3 = clubs.
Object
The state of the game in progress. It starts as null
and is replaced with a new object whenever start
and resumeGame
are called. It is mutated as the current game progresses. Its properties are:
drawPile
: An array representing the draw pile. The contents of this array should be considered private, but you can usestate.drawPile.length
to see how many cards remain.noOfCells
: The number of cells the player currently has.cellCards
: An array of cards currently placed in the cells. It only has elements for cells that have a card.zaps
: The number of zaps the player has left to use.
Parameter: settings
(object)
No return value
Starts a new game. settings
requires the following properties:
decks
: The number of copies of a deck that will be shuffled into the draw pile.startingZaps
: The number of available zaps at the start of the game.startingCells
: The number of cells at the start of the game.ranks
: An array of the card ranks that will be included in each deck copy. Any positive integer or string can be used as a rank, but only"J"
,"Q"
, and"K"
will have the powers of Jacks, Queens, and Kings respectively, and only ranks that are numbers will be used by Jack and Queen powers. To use the "Aces have the number 1" rule,1
must be used instead of"A"
.
Object
Has the properties quick
and marathon
. Each has an object usable as the settings
parameter to the above start
function, reflecting the settings used for the standard Quick and Marathon modes.
No parameters
No return value
Performs a draw move in the current game, and updates moveLog
with the results.
Parameter: target
(card)
No return value
Uses a zap on target
(which must be an object held in state.cellCards
), and updates moveLog
with the results.
Object
The results of the most recent move. Replaced with a new object for each move. Has the following properties:
- (optional)
drew
: The card drawn at the start of a draw move. - (optional)
zapped
: The chosen card for a zap move. - (optional)
discards
: An array of cards sent from the cells to the trash. Doesn't containmoveLog.drew
ormoveLog.zapped
. For draw moves, the absence of this property means the drawn card matched nothing and was placed in a cell. For zap moves, this property always exists but may be an empty array. - (optional)
jackPower
: An object representing the results of a Jack's power. Contains these properties: - (optional)
queenPower
: An object representing the results of a Queen's power. Contains these properties:success
: As forjackPower
.- (optional)
usedValueOf
: As forjackPower
. - (optional)
changedFrom
: The previous number of cells the player had.
- (optional)
kingPower
:true
if a King's power granted a zap. - (optional)
won
:true
if the game ended with the player's victory! - (optional)
lost
: An array of cards that couldn't be placed in cells, resulting in the player losing the game.
No parameters
Returns an object
Determines various pieces of data about the game in progress that players would find interesting. In the web game, this function provides the data for the "Cards to Draw" feature. The returned object has these properties:
drawPileCounts
: An object with a property for each rank in the game settings, where each value is the number of cards of that rank remaining in the draw pile. (Someone playing this game in real life could figure this out by adding together the cards in the trash pile and cells, and subtracting them all from the cards they know they started with.)cellCounts
: LikedrawPileCounts
, but instead counting cards in the cells.- (optional)
jackPower
: Present if there's a Jack and a numbered card in the cells. It's an object detailing what happens if the Jack's power were activated now, with the following properties:draws
: The number of cards to be drawn, before applying the rule "If there are not enough cards remaining in the draw pile, draw all of them".loseOnDraw
: A boolean indicating whether you would lose if your next move were a draw, and the drawn card were a Jack. Calculated after applying the rule mentioned above, with the draw pile not having the drawn Jack.
- (optional)
queenPower
: LikejackPower
, but for Queens. The object has these properties:cells
: The number of cells you would have.lose
: A boolean indicating whether this power would make you lose the game.
Parameter: card
([card]#card-objects())
Returns a boolean
Check whether card
has a power.
No parameters
Returns an object
Mutates state
into a form more suitable for serializing to JSON, returns it, and sets state
to null
. Pass the returned object to resumeGame
below to continue the game.
Parameters: data
(object returned by suspendGame
above), settings
No return value
Mutates data
to reverse the changes of suspendGame
and sets it to state
, allowing the game to proceed. settings
must be the same object used in the start
call that started the game.
This is a CLI app for testing bot players for Zapster Solitaire. See its dedicated README for more info. It is not designed to be imported by another module.
As zapster.mjs is made for use by a web app that already prevents illegal moves, it does not enforce these rules by default:
- You cannot make any more moves once you've won or lost.
- You must have at least one zap remaining to use a zap.
- A card must be in a cell in order to zap it.
If you're writing a bot for Zapster and you want to avoid accidentally breaking these rules, you can import strictEnforcement.mjs
. Its default export is a function that takes a zapster.mjs exports object (as you'd get from import * as myObject from "./zapster.mjs";
or await import("./zapster.mjs")
), and returns an object that has the same interface but will throw errors when any rule above is broken. If you're using the botTrial.mjs app, it will do this for you when you use the --strict
option.
This module implements a new version of the game rules - in short, aces are out, 10s are in, and starting cells are increased to 7. It is otherwise fully interchangeable with zapster.mjs, and bot authors are invited to play against both rulesets. It is hoped that for the best human & bot players, the new rules will increase the winrate but not to 100%, and the rate of losing to Jack powers will see no more than a slight increase.