Skip to main content

This package solves Agnes (Agnes Sorel) solitaire card games. It can be used to solve games having the rules implemented in the GNOME AisleRiot package and the rules attributed to Dalton in 1909 and Parlett in 1979 among others and to calculate win rates.

Project description

Summary

This package solves Agnes (Agnes Sorel) solitaire card games. It can be used to solve games having the rules implemented in the GNOME AisleRiot package and the rules attributed to Dalton in 1909 (with a minor variant in the table layout) and Parlett in 1979 among others [1–3] and to calculate win rates.

Example

import random
import quagnes

random.seed(12345)
n_reps = 1000

attributes = ['n_states_checked', 'n_deal', 'n_move_to_foundation',
      'n_move_card_in_tableau','n_no_move_possible','max_score','max_depth',
      'current_depth']

# header for the output file
print('rc,' + ','.join(attributes))

for rep in range(0, n_reps):
    new_game=quagnes.Agnes()
    rc = new_game.play()

    # Write the return code and selected attributes from the game
    out_str = (str(rc) + ',' +
        ','.join([ str(getattr(new_game, attr)) for attr in attributes ]))
    print(out_str, flush=True)

    # rc==1 are games that were won
    if rc==1:
        f = open(f'winners/win{rep}.txt', 'w', encoding='utf-8')
        f.write(new_game.print_history())
        f.close()

Release Notes [v0.8.2]

  • Bug fix: add check for loops when move_to_empty_pile != 'none'in addition to the existing check when split_same_suit_runs=True.
  • Add Optimizations #7 and #8 listed below.
  • When printing a state, add a single line with a string that has the same values stored in the set of losing states. This alllows the user to easily search in the output for when the previous state occurred. Note that a more compressed version of this string (one byte per card) is stored in the set of losing states. A longer version is printed that avoids characters that are special in regular expressions to facilitate searching.
  • Remove extensive description of the rules from the module docstring.

Background

Agnes is a difficult solitaire card game under some rule variants. This package solves the game automatically.

Users can simulate random games and calculate win rates under various permutations of rules, including moving sequences (runs) by same-suit or same-color, allowing or not same-suit runs to be split in the middle of the run for a move, dealing all tableau cards face-up at the start versus dealing only the final tableau card face-up, and whether and how empty columns can be filled in between deals (never, a single card of any rank, a single card of the highest rank, a run starting with a single card of any rank, or a run starting with a card of the highest rank). The package provides additional options for debugging and tuning of the search algorithm to be more memory-efficient at the expense of speed.

In 1979 Parlett named the two main variants of Agnes as Agnes Sorel (the variant / set of variants described here) and Agnes Bernauer (a variant/set of variants that uses a reserve) [3]. This package only considers Agnes Sorel.

Some analyses of win rates for Agnes Sorel has previously been conducted. Wolter published an experimental analysis of the win rates of this and other solitaires, reporting 14 winnable games in a million deals under presumably the Up-Suit-None rules (defined below) [4]. Masten modified Wolter's solver and reported 900/100,000 (0.9%) games were winnable when empty columns could not be filled by any card, although it is unclear whether same-suit sequences could be split during a move [5]. In this analysis, we conduct our own simulations to estimate win rates, assess agreement whether Wolter or Master are correct and add win rates for other rule variants. As in the prior analyses, the win rates calculated here are under perfect play (ie, the solver is allowed to undo losing moves).

Rules

There is considerable heterogeneity in the rules that have been proposed for Agnes (see Wikipedia [1] and Keller [6] for some discussion of the history). We describe first the rules according to Dalton [1-2] and variations that change only the handling of play on empty piles. The other main set of variants allow moving of same-color sequences instead of same-suit sequences. We describe the parameter settings to be used in our software when playing the different rule variants.

Because our interest in the problem arose from playing the game in the GNOME AisleRiot package, the default parameters correspond to this variant.

Dalton (1909)

The first description is of the game is attributed to Dalton in 1909 [1-2]. We implement his rules, except most software we have seen have the tableau piles ascending in length from left to right, so we follow that convention, rather than the right to left described by Dalton. (It should be noted that the win rates are not identical under this variation because only the first two piles are dealt to in the final deal.) The rules are the following:

Deal seven piles as columns face-up in the tableau such that the first pile has one card and the last column has seven. Then, deal a single card and place it above the tableau in the foundation (base card). Foundations are built up by suit and the tableau is built down by color. Wrap from king to ace when necessary. Piles of cards in sequence in the same suit can be moved in the tableau. The top card in each tableau pile is exposed and can be moved to a different pile (if it has the appropriate color and rank) or to the foundation. If a movable sequence occurs by chance at the start of the game or after a deal, the sequence could also be moved. When a card or pile is moved, the card in the column above it becomes exposed and available for play. Empty tableau piles can be filled by any card or movable pack, or they can be left empty. Dealing from the stock adds one card to the bottom of each tableau pile before play resumes, so a game will have three more deals of seven cards and a final deal of two cards. Cards cannot be played back from the foundation to the tableau.

Note that while Dalton used the singular when saying 'Any exposed card may be moved into a vacant space [pile]', in the example game he plays, he moves a group of cards into an empty pile. We follow the latter interpretation.

Dalton states that at the start the bottom card of each pile is exposed and doesn't explicitly state what is permitted if a movable sequence occurs by chance. However, in the sample game he plays, he does not distinguish between cards that appeared at the bottom of the column at the start versus those that did not, so we adopt the interpretation that sequences may be moved even if they first occurred by chance. This is consistent with the software implementations we have seen.

The game as described above can be played by specifying the parameters move_same_suit=True, face_up=True, and move_to_empty_pile='any run'.

We denote the rules above as Up-Suit-AnyRun. The third part of each name is because many variants have evolved that alter what can be done with empty piles. We refer to these as:

Up-Suit-None: Empty piles can only be filled when dealing from stock. Up-Suit-Any1: Empty piles can be filled by any single card, but not by a movable run. Up-Suit-HighRun: Empty piles can be filled a single highest rank card, or a run starting with the highest rank card (ie, if the base card was a seven, an empty pile can be filled only with a six or run starting with a six). Up-Suit-High1: Empty piles can be filled by a single highest rank card.

The move_to_empty_pile parameter takes the values none (default), any 1, high run, and high 1, respectively, for the above four variants.

Parlett (1979) and NoSplit variants

Parlett gives rules like Up-Suit-None but also forbids splitting same suit sequences (split_same_suit_runs=False) [3]. We denote this as Up-Suit-None-NoSplit. We also allow other rules that move by suit to have this option and denote these by adding -NoSplit to the end of of the rule name.

Down-Color- and Up-Color variants

Among this group, the primary variant of interest is Down-Color-None, which corresponds to the game as played on AisleRiot, which is where we became interested in the game. We verified the rules on the current AisleRiot version 3.22.23.

The first variation from the Dalton rules is that cards are dealt face down in the tableau except for the final card dealt in each pile. We start these variant names with Down- instead of Up-. The second variation is that sequences may be moved if they are the same color instead of requiring they be the same suit. We use -Color- as the second part of the variant name to indicate this. The third part of the variant name is as described for the Dalton variants.

Other variants of interest in this class are Up-Color-AnyRun. This and Up-Color-None match the rules described on the website poltaire.com, which offers solitaire play [7]. This site is of interest to us as it hosts an article by Wolter [4] that analyzes the winnability of the game as played on the site.

Methodology

The following optimizations were used to improve the run-time over a naive depth-first search of all possible moves:

  1. When move_to_empty_pile='none', if the highest rank card (e.g., a king if the base card is an ace) is placed in a tableau pile below a lower card of the same suit, the lower card can never be moved from the pile. This is detected as soon as the king is placed. Therefore, some games can be determined to be not winnable by examining only the starting layout. Note while this optimization improves run time, it is not possible to determine the maximum possible score if a branch is terminated for this reason. This concept can be extended by noting that if the king of clubs blocks the queen of hearts and the king of hearts blocks the queen of clubs, the game is also unwinnable. In general, we define after each deal a four vertex graph (one vertex for each suit), where an edge denotes which suits are blocked by the suit corresponding to the vertex. The state is not winnable if a cycle exists in the graph. This only needs to be checked after a deal. This optimization is disabled when move_to_empty_pile != 'none' or when maximize_score=True.

  2. A card is immediately placed on a foundation if its rank is no more than the rank of the top foundation card of the same color suit plus two. For example, if the base card is an ace, and we have already played up to the six of spades in the foundation, we immediately play the ace of clubs up to the eight of clubs (if possible) to the foundation. We would not immediately play the nine of clubs to the foundation as it might be needed in the tableau to move the eight of spades.

  3. We track states that we have already determined to be not winnable in a set. For example, suppose the initial state has two possible moves: M1) move a card from pile 1 to 2; M2) move a card from pile 4 to 5. Once we determine the sequence M1 → M2 is not winnable, there is no need to check the sequence M2 → M1, as they both result in the same state. This optimization can be tuned using the track_threshold parameter.

  4. To prevent infinite loops, we use a stack of sets that stores the state of the game (number of cards in the stock and arrangement of cards in the tableau) and check whether this has been repeated since the last deal or move to foundation. If the state is a repeat, we consider no valid moves to be available at the repeated state. This second method is disabled if split_same_suit_runs=False and move_to_empty_pile 'none'}, as loops cannot occur under this combination of parameter settings.

  1. The simulation is implemented using two or three equal-length stacks where the nth item describes an aspect of the state of the game at the nth move. These three stacks store: (1) the moves performed, (2) the valid moves not yet attempted, and (3) a set containing the arrangement of all tableau layouts that have occurred since the last deal or move to foundation.

A single state object (_AgnesState) in the Agnes object initially stores information about the starting state, such as the cards in the foundation, the arrangement of cards in the tableau, and the number of cards left in the stock. When a move is performed or undone, the AgnesState object is updated in-place based on the move information. Initial testing found this implementation to be about 5–7 times faster than using a stack of _AgnesState objects, although this hasn't been retested after the addition of some of the other optimizations.

  1. If win rate is the only output statistic of interest and results are being calculated for multiple rule sets, users can exploit the fact that all wins under some rule set is sometimes a subset of wins under another rule set. For example, adding split_same_suit_runs=True will give at least the same number of wins as split_same_suit_runs=False when other parameter values are fixed.

  2. If there are multiple empty columns in the tableau that cannot be covered by a future deal, then it doesn't matter which column is played in, so the first is always chosen. Similarly, we do not consider a move that moves the entirety of one column to a different empty column when both columns cannot be covered by a future deal.

  3. Do not allow splitting same-suit runs when the stock is empty regardless of the value of the split_same_suit_runs parameter, unless move_to_empty_pile = 'any 1' or 'high 1'.

Statistical Analysis

Ten thousand decks were simulated using this package version 8.0.2. We calculated the win rates and the 95% confidence intervals (95% CI) using the score (Wilson) method for the following rules: Down-Color-None, Up-Color-None, Up-Suit-None, Up-Suit-None-NoSplit, Up-Suit-HighRun, Up-Suit-HighRun-NoSplit, Up-Suit-AnyRun, Up-Suit-AnyRun-NoSplit. If a set of simulations consumed too move memory, the simulation was run setting a maximum number of states examined to 50 million, and win rates were calculated assuming all incomplete simulations were losses and then again assuming all were wins.

For each set of rules, the mean (standard deviation) and maximum number of states examined were also reported. This statistic counts repeated states each time they are created.

Lastly, we calculated separate p-values testing the equality of our estimate with those published by Masten [5] and Wolter [4] (the latter only when the rule for empty piles was None). A Chi-square test or Fisher exact test was used, as appropriate.

All simulations were run using a C++ port of this package. The first thousand simulations for each rule variant were also run using this Python package version 0.8.2 on Python version 3.11.2, and the simulation result (win, loss, exceeded maximum states) and number of states examined were confirmed to match the C++ version. A laptop running 32-bit Debian GNU/Linux 12 was used for the simulations. Statistical analyses were conducted in R v4.2.2.

Results

The results of the simulations are shown in the table below.

Rule Variant Completed Simulations, (n) Wins, n (% [95% CI]) P-value [a] Mean (SD) States Examined (10^3) Max States Examined (10^6)
Down-Color-None 10,000 99 (1.0% [0.8%, 1.2%]) 0.40 89.0 (156.1) 63.7
Up-Color-None 9,996 [b] 113 (1.1% [0.9%, 1.4%], 1.2% [1.0%, 1.4%]) 0.02, 0.01 72.6 (112.8) 46.3
Up-Suit-None 10,000 42 (0.4% [0.3%, 0.6%] <.0001 10.2 (163.9) 8.5
Up-Suit-None-NoSplit 10,000 40 (0.4% [0.3%, 0.5%]) <.0001 5.9 (94.1) 6.1
Up-Suit-HighRun 10,000 1454 (14.5% [13.9%, 15.2%]) 0.92 207.3 (999.3) 31.9
Up-Suit-HighRun-NoSplit 10,000 1411 (14.1% [13.4%, 14.8%]) 0.30 112.9 (500.9) 22.6
Up-Suit-AnyRun-NoSplit 10,000 6384 (63.8% [62.9%, 64.8%]) 0.69 59.2 (340.2) 16.1

[a] P-value for comparison vs Masten results. [b] Simulations stopped after 50,000,000 states examined. Win rates and P-values are reported twice assuming all incomplete simulations are losses and wins. Mean and maximum states examined reported for completed simulations.

Discussion

We found 1.1% - 1.2% of games were winnable under the Up-Color-None rules and 1.0% of games were winnable under the Down-Color-None rules. For the former, the comparison to Masten's results gave a statistically significant difference at the 95% confidence level while the latter did not. Our simulations for Up-Suit-HighRun, Up-Suit-HighRun-NoSplit, and Up-Suit-AnyRun-NoSplit were not statistically significantly different from those of Masten, although Up-Suit-None and Up-Suit-None-NoSplit were.

A limitation of using Masten's results is he states win rates, but isn't explicit about which rules are used, with the exception of how empty piles are filled. He refers readers to Keller's discussion [6] for more information, and this discussion suggests that Agnes rules follow Whitehead rules (another solitaire) and require movable runs to be the same suit. That is, Keller indicates the rules are Up-Suit-None in our terminology [5,6]. However, Keller then goes on to note that all computer implementations he had seen allow moves by colored runs and not by same-suit runs, which would corresponds to the Up-Color-None rules in our terminology.

It was unexpected to find that when the rule for empty piles is None, our results for dealing face-down and moving by color matched Masten, while when the rule for empty piles is HighRun or AnyRun, our results for dealing face-up and moving by suit matched. It is unclear if Masten reported results for different move rules, if the statistically significant differences we found were due to chance, or if consistent rules were used for a variant we have not yet tested (eg, Up-Color-None-NoSplit, Up-Color-HighRun-NoSplit, and Up-Color-AnyRun-NoSplit).

Despite these uncertainties about Masten's estimates, we add 95% CIs to his reported win rates and include them here. He reports the previously mentioned win rate (95% CI) of 0.9% (0.8%, 1.0%) when empty columns cannot be filled with moves from the tableau, 63.6% (63.3%, 63.9%) when the empty column can be filled by any sequence (or perhaps single card — Masten is not explicit, but our results match filling with a sequence), and 14.5% (14.3%, 14.7%) when empty columns can be filled only by a sequence starting with the highest rank card (or perhaps sequence).

All of our results were larger than the 14/1,000,000 reported by Wolter at P<.0001. Given the large difference between our results and Wolter's results, we believe Wolter's results are incorrect and should not be cited.

We have estimated win rates that to our knowledge have not been previously reported for Parlett's variant (Up-Suit-None-NoSplit) and Down-Color-None, with the former less than half of Up-Suit-None and the latter about 10% smaller.

Much of the work for this project was originally completed in 2019 assuming all cards were dealt face-up using Python 3.5. This 2024 update used Python 3.11.2. In 2019 we ported the Python code to C++ for performance testing and found a five-fold improvement in speed. When porting the code to Python 3.11, we found a two-fold decrease in run-time when switching the data structures used from tuples to dataclasses for the structures representing cards and moves.

We have not all possible rule variants with this package. We considered including Up-Suit-AnyRun, but preliminary estimates indicated high memory usage and a longer run time, and analysis will be deferred until a later package version. The current package has some options to manage the memory (eg, disabling the set that tracking losing states at various thresholds), but additional optimizations are possible once the set is disabled that are not yet implemented (eg, requiring cards to be placed to the foundation before moving a pile that we hope will improve run time). In addition, the package options allow disabling of splitting same-suit runs before a move, but this option will likely be generalized to disallow splitting of movable runs and then applied when moves are allowed by color.

References

[1] Agnes (card game). Wikipedia. https://en.wikipedia.org/wiki/Agnes_(card_game). Retrieved March 15, 2024.

[2] Dalton W (1909). "My favourite Patiences" in The Strand Magazine, Vol 38.

[3] Parlett D (1979). The Penguin Book of Patience. London: Penguin.

[4] Wolter J (2013). Experimental analysis of Agnes Sorel solitaire. https://politaire.com/article/agnessorel.html. Retrieved March 15, 2024.

[5] Masten M (2021). Agnes Sorel. https://solitairewinrates.com/AgnesSorel.html. Retrieved March 17, 2024.

[6] Keller M (2021). Whitehead and Agnes -- packing in color. https://www.solitairelaboratory.com/whitehead.html. Retrieved March 17, 2024.

[7] Wolter J (2014). Rules for Agnes Sorel solitaire. https://politaire.com/help/agnessorel. Retrieved March 17, 2024.

Disclosures

We are not affiliated with any of the books, websites, or applications discussed in this documentation, except of course for this Python package which we wrote.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

quagnes-0.8.2.tar.gz (68.4 kB view details)

Uploaded Source

Built Distribution

quagnes-0.8.2-py3-none-any.whl (49.9 kB view details)

Uploaded Python 3

File details

Details for the file quagnes-0.8.2.tar.gz.

File metadata

  • Download URL: quagnes-0.8.2.tar.gz
  • Upload date:
  • Size: 68.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.0.0 CPython/3.11.2

File hashes

Hashes for quagnes-0.8.2.tar.gz
Algorithm Hash digest
SHA256 36f903c562dfc8dc66396ecde3fce88d1e6b47b2a662438077cedeb6aa20ea33
MD5 c27529a04a8c874f5eb26c793b47f7f6
BLAKE2b-256 97d04b2dcc3768552a59a86adb56d61803e5f28ccac40a5daa56bff3fc29cbab

See more details on using hashes here.

File details

Details for the file quagnes-0.8.2-py3-none-any.whl.

File metadata

  • Download URL: quagnes-0.8.2-py3-none-any.whl
  • Upload date:
  • Size: 49.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.0.0 CPython/3.11.2

File hashes

Hashes for quagnes-0.8.2-py3-none-any.whl
Algorithm Hash digest
SHA256 1b3b910cdbcc110b4a170d0b43f9e73e6888b1b05388e0d9a690ade4c1d8bf9d
MD5 faa760731892efe740d0980ef76ce69e
BLAKE2b-256 64bbd3d05bace5a4dc6afa05cd6f8c2bdcc48c9ff231966ddce504c98f610125

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page