1
0
Fork 0
mirror of https://github.com/shlomif/PySolFC.git synced 2025-04-05 00:02:29 -04:00

Compare commits

...

42 commits

Author SHA1 Message Date
Joe R
bdd3093a2b Compress tile images 2025-03-30 15:42:45 -04:00
Joe R
c4a62baf79 Add Cribbage Patience II game 2025-03-30 14:04:18 -04:00
Joe R
b1370fce08 Add more alternate names 2025-03-25 19:09:39 -04:00
Joe R
7b85049f7c Add Flamboyant game 2025-03-25 17:42:31 -04:00
Joe R
afc74a86f5 Remove extra paragraph tags to improve documentation formatting 2025-03-23 19:41:21 -04:00
Joe R
50e003e256 Add Ace of Penguins to the compatibility list 2025-03-17 19:44:23 -04:00
Joe R
78de569074 Fix for the total text crashing Ninety-One on Android (#436) 2025-03-17 18:04:38 -04:00
Joe R
a79c81109b Fix binding error in Kivy 2025-03-15 20:11:50 -04:00
Joe R
c14be7b73e Add Wizard's Castle game 2025-03-14 22:09:56 -04:00
Joe R
69c1440b2b Improve display details of cardset info window 2025-03-14 19:21:14 -04:00
Joe R
72386af5e6 Configure highlight matching for more games 2025-03-10 22:02:24 -04:00
Joe R
a57f1eae38 Add Noumea game 2025-03-10 19:47:40 -04:00
Shlomi Fish
6f0c6e00e1
Condition "chdir" in script dir being local. (#434)
So "python3 scripts/linux-install.py" as documented in README will work.
2025-03-09 19:44:55 -04:00
Joe R
512d3ea466 Improve new Mahjongg layouts 2025-03-06 23:13:43 -05:00
Joe R
e7e9f0ff87 Configure topmost dialogs to not stay on top when PySol loses focus 2025-03-06 22:38:13 -05:00
Joe R
9773eee2d6 Add Wizard's Storeroom game 2025-03-03 21:28:35 -05:00
Joe R
015e860557 Fix endgame hints for Crossword 2025-03-03 19:37:54 -05:00
Joe R
04b7eacfcb Fix highlight match logic for Nines/Tens/Elevens 2025-03-01 22:24:17 -05:00
Joe R
52e2ffc58f Add two mahjongg layouts 2025-03-01 16:17:53 -05:00
Joe R
81150b4681 Add Relaxed Cruel game 2025-02-26 19:55:01 -05:00
Joe R
f799093e35 Restore unfinished Thirteen game as Pyramid Thirteen, using correct AisleRiot rules 2025-02-23 22:32:22 -05:00
Joe R
12118d12ed Clean up Scorpion type games 2025-02-23 14:18:08 -05:00
Joe R
fd4a4e1378 Fill in missing translated names 2025-02-20 18:59:01 -05:00
Joe R
63a63fdfd3 Add Louis game 2025-02-20 18:47:21 -05:00
Joe R
ff9cc5e98c Add FAQ to the in-app documentation 2025-02-20 17:26:10 -05:00
Joe R
cadf8b2084 Fix some controls being unselectable by tab 2025-02-18 19:16:04 -05:00
Joe R
f4dec3ed16 Add Ides of March game 2025-02-17 21:24:26 -05:00
Joe R
5c8d5c26b4 Cleanup 2025-02-17 20:49:25 -05:00
Joe R
cb0dd1ec2f Add definition of super move to the glossary 2025-02-13 21:09:17 -05:00
Joe R
dc5ab96c80 Add Families game 2025-02-13 20:55:16 -05:00
Joe R
54a978b4e2 Fix flake8 2025-02-11 21:56:10 -05:00
Joe R
5b9f64a7eb Support alternate names for alternate deck games 2025-02-11 21:39:43 -05:00
Joe R
21c2780e8e Tweak menu text for consistency 2025-02-06 21:56:32 -05:00
Joe R
ed2da8cd46 Restore lost Outback Patience game, with the correct rules 2025-02-06 19:59:16 -05:00
Joe R
054c0f0368 Enable log for demo games 2025-01-28 23:32:15 -05:00
Joe R
487d1c52f3 Use Python 3.13 in AppVeyor 2025-01-28 19:38:23 -05:00
Joe R
6852bb40ff Add Little Ishido game 2025-01-24 21:58:56 -05:00
Joe R
6006e0f4c0 Shift children's games to use tag rather than deprecated list 2025-01-24 20:03:08 -05:00
Joe R
68138c7284 Add Colours game 2025-01-23 18:01:20 -05:00
Joe R
96a06108af Fix flake8 2025-01-20 21:06:13 -05:00
Joe R
ac03fa741d Add Microbe game 2025-01-20 20:18:32 -05:00
Joe R
7cac54b714 Add more alternate names 2025-01-20 19:25:16 -05:00
105 changed files with 1666 additions and 528 deletions

View file

@ -2,7 +2,7 @@
image: Visual Studio 2022
environment:
matrix:
- PYTHON: "C:\\Python312"
- PYTHON: "C:\\Python313"
# Shamelessly taken from https://github.com/plicease/Dist-Zilla-PluginBundle-Author-Plicease/blob/master/.appveyor.yml
# Thanks!
install:

BIN
data/tiles/Felt_Blue.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 351 B

After

Width:  |  Height:  |  Size: 350 B

View file

@ -38,42 +38,18 @@ back01.ext;back02.ext;back03.ext</pre>
<p><b>$B:</b> The type of the cardset (see types below)</p>
<h3>Cardsets Types:</h3>
<ul>
<li>
<p>French = 1</p>
</li>
<li>
<p>Hanafuda = 2</p>
</li>
<li>
<p>Tarock = 3</p>
</li>
<li>
<p>Mahjongg = 4</p>
</li>
<li>
<p>Hex A Deck = 5</p>
</li>
<li>
<p>Mughal Ganjifa = 6</p>
</li>
<li>
<p>Navagraha Ganjiga = 7</p>
</li>
<li>
<p>Dashavatara Ganjifa = 8</p>
</li>
<li>
<p>Trumps Only = 9</p>
</li>
<li>
<p>Matching = 10</p>
</li>
<li>
<p>Puzzle = 11</p>
</li>
<li>
<p>Ishido = 12</p>
</li>
<li>French = 1</li>
<li>Hanafuda = 2</li>
<li>Tarock = 3</li>
<li>Mahjongg = 4</li>
<li>Hex A Deck = 5</li>
<li>Mughal Ganjifa = 6</li>
<li>Navagraha Ganjiga = 7</li>
<li>Dashavatara Ganjifa = 8</li>
<li>Trumps Only = 9</li>
<li>Matching = 10</li>
<li>Puzzle = 11</li>
<li>Ishido = 12</li>
</ul>
<p><b>$C:</b> The number of cards in the cardset, generally 42,
48, 52, 68, 78, 96 or 120</p>
@ -81,190 +57,70 @@ back01.ext;back02.ext;back03.ext</pre>
or more comma separated values)</p>
<h3>Cardsets Styles:</h3>
<ul>
<li>
<p>Abstract = 35</p>
</li>
<li>
<p>Adult = 1</p>
</li>
<li>
<p>Animals = 2</p>
</li>
<li>
<p>Anime = 3</p>
</li>
<li>
<p>Art = 4</p>
</li>
<li>
<p>Cartoons = 5</p>
</li>
<li>
<p>Children = 6</p>
</li>
<li>
<p>Classic Look = 7</p>
</li>
<li>
<p>Collectors = 8</p>
</li>
<li>
<p>Computers = 9</p>
</li>
<li>
<p>Divination = 36</p>
</li>
<li>
<p>Engines = 10</p>
</li>
<li>
<p>Fantasy = 11</p>
</li>
<li>
<p>Four Color = 37</p>
</li>
<li>
<p>Ganjifa= 30</p>
</li>
<li>
<p>Hanafuda = 12</p>
</li>
<li>
<p>Hex A Deck = 29</p>
</li>
<li>
<p>Holiday = 13</p>
</li>
<li>
<p>Ishido = 34</p>
</li>
<li>
<p>Mahjongg = 28</p>
</li>
<li>
<p>Matching = 32</p>
</li>
<li>
<p>Monochrome = 38</p>
</li>
<li>
<p>Movies = 14</p>
</li>
<li>
<p>Matrix = 31</p>
</li>
<li>
<p>Music = 15</p>
</li>
<li>
<p>Nature = 16</p>
</li>
<li>
<p>Operating Systems = 17</p>
</li>
<li>
<p>People = 19</p>
</li>
<li>
<p>Places = 20</p>
</li>
<li>
<p>Plain = 21</p>
</li>
<li>
<p>Products = 22</p>
</li>
<li>
<p>Puzzle = 33</p>
</li>
<li>
<p>Round Cardsets = 18</p>
</li>
<li>
<p>Science Fiction = 23</p>
</li>
<li>
<p>Sports = 24</p>
</li>
<li>
<p>Tarock = 27</p>
</li>
<li>
<p>Vehicles = 25</p>
</li>
<li>
<p>Video Games = 26</p>
</li>
<li>Abstract = 35</li>
<li>Adult = 1</li>
<li>Animals = 2</li>
<li>Anime = 3</li>
<li>Art = 4</li>
<li>Cartoons = 5</li>
<li>Children = 6</li>
<li>Classic Look = 7</li>
<li>Collectors = 8</li>
<li>Computers = 9</li>
<li>Divination = 36</li>
<li>Engines = 10</li>
<li>Fantasy = 11</li>
<li>Four Color = 37</li>
<li>Ganjifa= 30</li>
<li>Hanafuda = 12</li>
<li>Hex A Deck = 29</li>
<li>Holiday = 13</li>
<li>Ishido = 34</li>
<li>Mahjongg = 28</li>
<li>Matching = 32</li>
<li>Monochrome = 38</li>
<li>Movies = 14</li>
<li>Matrix = 31</li>
<li>Music = 15</li>
<li>Nature = 16</li>
<li>Operating Systems = 17</li>
<li>People = 19</li>
<li>Places = 20</li>
<li>Plain = 21</li>
<li>Products = 22</li>
<li>Puzzle = 33</li>
<li>Round Cardsets = 18</li>
<li>Science Fiction = 23</li>
<li>Sports = 24</li>
<li>Tarock = 27</li>
<li>Vehicles = 25</li>
<li>Video Games = 26</li>
</ul>
<p><b>$E:</b> The origin (country) of the cardset</p>
<h3>Cardsets Origins:</h3>
<ul>
<li>
<p>Australia = 1021</p>
</li>
<li>
<p>Austria = 1001</p>
</li>
<li>
<p>Belgium = 1019</p>
</li>
<li>
<p>Canada = 1010</p>
</li>
<li>
<p>China = 1011</p>
</li>
<li>
<p>Czech Republic = 1012</p>
</li>
<li>
<p>Denmark = 1013</p>
</li>
<li>
<p>England = 1003</p>
</li>
<li>
<p>France = 1004</p>
</li>
<li>
<p>Germany = 1006</p>
</li>
<li>
<p>Great Britain = 1014</p>
</li>
<li>
<p>Hungary = 1015</p>
</li>
<li>
<p>India = 1020</p>
</li>
<li>
<p>Italy = 1005</p>
</li>
<li>
<p>Japan = 1016</p>
</li>
<li>
<p>Netherlands = 1002</p>
</li>
<li>
<p>Portugal = 1022</p>
</li>
<li>
<p>Russia = 1007</p>
</li>
<li>
<p>Spain = 1008</p>
</li>
<li>
<p>Sweden = 1017</p>
</li>
<li>
<p>Switzerland = 1009</p>
</li>
<li>
<p>USA = 1018</p>
</li>
<li>Australia = 1021</li>
<li>Austria = 1001</li>
<li>Belgium = 1019</li>
<li>Canada = 1010</li>
<li>China = 1011</li>
<li>Czech Republic = 1012</li>
<li>Denmark = 1013</li>
<li>England = 1003</li>
<li>France = 1004</li>
<li>Germany = 1006</li>
<li>Great Britain = 1014</li>
<li>Hungary = 1015</li>
<li>India = 1020</li>
<li>Italy = 1005</li>
<li>Japan = 1016</li>
<li>Netherlands = 1002</li>
<li>Portugal = 1022</li>
<li>Russia = 1007</li>
<li>Spain = 1008</li>
<li>Sweden = 1017</li>
<li>Switzerland = 1009</li>
<li>USA = 1018</li>
</ul>
<p><b>$F:</b> The Year the cardset was created (in the range 1000
to 2299)</p>
@ -284,31 +140,16 @@ back01.ext;back02.ext;back03.ext</pre>
will be truncated.</p>
<h2>Line 3</h2>
<ul>
<li>
<p><b>X:</b> The cards' width (the card image width)</p>
</li>
<li>
<p><b>Y:</b> The cards' height (the card image height)</p>
</li>
<li>
<p><b>D:</b> The color depth (mostly 8)</p>
</li>
<li><b>X:</b> The cards' width (the card image width)</li>
<li><b>Y:</b> The cards' height (the card image height)</li>
<li><b>D:</b> The color depth (mostly 8)</li>
</ul>
<h2>Line 4</h2>
<ul>
<li>
<p><b>XOFF:</b> The horizontal offset of the cards</p>
</li>
<li>
<p><b>YOFF:</b> The vertical offset of the cards</p>
</li>
<li>
<p><b>SXOFF:</b> The offset of the horizontal shadow (mostly
7)</p>
</li>
<li>
<p><b>SYOFF:</b> The offset of the vertical shadow (mostly 7)</p>
</li>
<li><b>XOFF:</b> The horizontal offset of the cards</li>
<li><b>YOFF:</b> The vertical offset of the cards</li>
<li><b>SXOFF:</b> The offset of the horizontal shadow (mostly 7)</li>
<li><b>SYOFF:</b> The offset of the vertical shadow (mostly 7)</li>
</ul>
<h2>Line 5</h2>
<p><code>back01.ext</code>: The name of the default back image (where <code>ext</code> means the file extension e.g: <code>png</code> or <code>gif</code>.)</p>
@ -330,7 +171,9 @@ back01.ext;back02.ext;back03.ext</pre>
<a href="https://github.com/shlomif/shlomif-computer-settings/blob/master/shlomif-settings/git/commit-messages/cc0-copyright-disclaimer.txt">CC0/Public Domain/MIT License/GPL terms</a></p>
<h3>Credits:</h3>
<p>Credits go to:</p>
<p><a href="mailto:andsa@web.de">Andreas Sauer</a> for clarifying certain
points<br></p>
<p><a href="https://www.shlomifish.org/">Shlomi Fish</a> for making some
improvements to this tutorial</p>
<ul>
<li><a href="mailto:andsa@web.de">Andreas Sauer</a> for clarifying certain
points</li>
<li><a href="https://www.shlomifish.org/">Shlomi Fish</a> for making some
improvements to this tutorial</li>
</ul>

64
html-src/faq.html Normal file
View file

@ -0,0 +1,64 @@
<h1>FAQ</h1>
<h2>The animation is too slow...</h2>
<p>
Unfortunately the Tcl/Tk toolkit lacks a sprite concept, so
there is a lot of (invisible double-buffered) redraw going on
when dragging cards around.
<p>
Disabling <i>Card shadow</i>, <i>Shade legal moves</i>,
background table tiles and sound will somewhat improve the display speed.
<h2>The table tiles look strange</h2>
<p>
Background table tiles should only be enabled when using
a true-color video mode - otherwise they may look bad
because of dithering.
<p>
BTW, you can add your own background tiles by copying the images
to the main <i>data/tiles</i> or your home <i>~/.PySolFC/tiles</i> directory.
<!-- They must be in GIF or PPM format. -->
<h2>My antivirus app says the Windows installer contains a virus.</h2>
<p>
We have been asked about the fact that some anti viruses, including those
on <a href="https://www.virustotal.com/">VirusTotal</a>, have identified the Microsoft
Windows downloads as containing malware. What we know is that they are generated from
the <a href="https://en.wikipedia.org/wiki/Free_and_open-source_software">open source</a>
source code by <a href="https://www.appveyor.com/">AppVeyor</a>, are checked using
VirusTotal before they are uploaded, and at that point were considered to be malware clean.
<p>
However, as time passes, they seem to accumulate classifyings as containing malware
in what appears to be <a href="https://en.wikipedia.org/wiki/False_positives_and_false_negatives">false positives</a>. So we believe the downloads are nonetheless safe and free of
malware.
<p>
Furthermore, note that we believe that many anti-malware applications are harmful
by themselves, and that they are all an incomplete (non-)solution to an
issue that does not exist in <a href="https://en.wikipedia.org/wiki/Linux#Desktop">Linux</a> and the <a href="https://en.wikipedia.org/wiki/List_of_BSD_operating_systems">BSDs operating systems</a>.
<p>
For more information, see:
<ol>
<li><a href="https://sourceforge.net/p/pysolfc/discussion/503709/thread/d841b6a1/">Forum discussion</a></li>
<li><a href="http://linuxmafia.com/~rick/faq/">Viruses on Linux</a></li>
<li><a href="https://www.mail-archive.com/wikimedia-l@lists.wikimedia.org/msg30001.html">Discussion about anti-viruses.</a></li>
</ol>
<h2>I received an error that there is no module named "formatter".</h2>
<p>
This error occurs if you're trying to run an older version of PySolFC
with Python 3.10. If you are using Python 3.10 or later, please upgrade your
version of PySolFC to 2.14.0 or later - older versions are not compatible with
Python 3.10.
<p>
If you are getting your copy from your Linux distribution's package manager,
please contact the people who maintain the packages for your distribution.
You can always get the latest version from the PySolFC website or from
Flathub.
<h2>It won't let me deal more cards in Spider.</h2>
<p>
When playing Spider, you are not allowed to deal more cards if there are any
empty piles. If there aren't enough cards left to put one in each pile, the
game is lost. Part of the game is planning ahead when to deal more cards to
avoid this situation.
<p>
If you want a variation without this rule, you can play Relaxed Spider instead.

View file

@ -46,6 +46,7 @@ fix_gettext()
files = [
('credits.html', 'PySol Credits'),
('credits_old.html', 'PySol Credits'),
('faq.html', 'PySol - FAQ'),
('ganjifa.html', 'PySol - General Ganjifa Card Rules'),
('general_rules.html', 'PySol - General Rules'),
('glossary.html', 'PySol - Glossary'),

View file

@ -323,6 +323,15 @@ the deck.</p>
Hearts, and Diamonds.</p>
</dd>
<dt><b>SUPER MOVE</b></dt>
<dd>
<p>A special move where you move a sequence of cards at once, in a game
that does not normally allow it. However, if there are enough open
reserves and empty stacks to perform the move by moving cards between
them one at a time, you can make it as a single move as a shortcut.</p>
</dd>
<dt><b>TABLEAU</b></dt>
<dd>

View file

@ -55,27 +55,6 @@ disable certain features as they would be trivial otherwise.
The logic involved is not too clever on purpose (i.e. it does not consult the hint system).
</ul>
<h2>The animation is too slow...</h2>
<p>
Unfortunately the Tcl/Tk toolkit lacks a sprite concept, so
there is a lot of (invisible double-buffered) redraw going on
when dragging cards around.
<p>
Disabling <i>Card shadow</i>, <i>Shade legal moves</i>,
background table tiles and sound will somewhat improve the display speed.
<h2>The table tiles look strange</h2>
<p>
Background table tiles should only be enabled when using
a true-color video mode - otherwise they may look bad
because of dithering.
<p>
BTW, you can add your own background tiles by copying the images
to the main <i>data/tiles</i> or your home <i>~/.PySolFC/tiles</i> directory.
<!-- They must be in GIF or PPM format. -->
<h2>Some notes about scoring</h2>
<p>
<ul type="disc">

View file

@ -19,9 +19,10 @@
<li> <a href="cardset_customization.html">Cardset Customization</a>
<li> <a href="plugins.html">Plugins</a>
</ul>
<h2>Misc</h2>
<h2>About</h2>
<ul>
<li> <a href="news.html">What's new?</a>
<li> <a href="faq.html">FAQ</a>
<li> <a href="report_bug.html">Report a Bug</a>
<li> <a href="license.html">PySol license terms</a>
<li> <a href="credits.html">PySol credits</a>

View file

@ -1,10 +1,9 @@
<h1>Report a Bug</h1>
<p>
Before you report a bug, please verify that you are running
the latest version of PySolFC, and also check the PySolFC site's
<a href="https://pysolfc.sourceforge.io/faq.html">FAQ page</a>,
and issues previously reported on GitHub, to ensure that the bug
was not previously reported.
the latest version of PySolFC. Also, check the
<a href="faq.html">FAQ</a>, and issues previously reported
on GitHub, to ensure that the bug was not previously reported.
<p>
If you found a bug in PySolFC, the best place to report it
is on GitHub. To do so, create an issue at

View file

@ -15,7 +15,7 @@ This card must be the last remaining card in order to win the game.
<h3>Notes</h3>
<p>
Accordion's Revenge is unwinnable if the first or second card is
declared. As such, PySol will reselect the declared card if it
is chosen.
declared. As such, PySol will never select either of those cards as
the target.
<p>
The concept of Accordion's Revenge was invented by Mark Masten.

View file

@ -1,4 +1,4 @@
<h1>Big Braid</h1>
<h1>Big Braid (Der gro&szlig;e Zopf)</h1>
<p>
Napoleon type. 3 decks. 2 redeals.

View file

@ -0,0 +1,13 @@
<h1>Big Storeroom</h1>
<p>
Hex A Deck type. 2 decks. 1 redeal.
<h3>Object</h3>
<p>
Move all cards to the foundations.
<h3>Rules</h3>
<p>
Like <a href="wizardsstoreroom.html">Wizard's Storeroom</a>
but with two decks and nine piles.

View file

@ -0,0 +1,15 @@
<h1>Colours</h1>
<p>
Numerica type. 1 deck. No redeal.
<h3>Object</h3>
<p>
Move all cards to the foundations.
<h3>Quick Description</h3>
<p>
Like <a href="ladybetty.html">Lady Betty</a>,
but the foundations are built up by same color, and
wrap from king to ace. At the start of the game, a
two through five are dealt to the foundations, in
alternating colors.

View file

@ -4,7 +4,7 @@ Cribbage type. 1 deck. No redeal.
<h3>Object</h3>
<p>
Deal five cribbage hands to get a total score of 61 points or more.
Deal five cribbage hands to get a total score of 80 points or more.
<h3>Rules</h3>
<p>
@ -16,7 +16,8 @@ Points are awarded for the 2 Cribbage hands: the remaining four cards, plus
the four cards in the crib.
<p>
After each hand, repeat the process four more times to deal a total of five
hands. You win if after all five hands, your score reaches 61 points.
hands. You win if after all five hands, your score reaches 80 points.
<h3>Cribbage Scoring</h3>
<p>
Cribbage hands are scored as follows - each hand is worth the total value

View file

@ -0,0 +1,18 @@
<h1>Cribbage Patience II</h1>
<p>
Cribbage type. 1 deck. No redeal.
<h3>Object</h3>
<p>
Deal four cribbage hands to get a total score of 80 points or more.
<h3>Quick Description</h3>
<p>
Like <a href="cribbagepatience.html">Cribbage Patience</a>,
but two cards are not dealt face-down to the crib. After moving two
cards from the hand to the crib, a second six-card hand is dealt, and
you move two cards from that hand to finish the crib. Then, all
three hands are scored.
<p>
Due to the increased number of cards per hand, only four hands are dealt
per game.

View file

@ -0,0 +1,27 @@
<h1>Families</h1>
<p>
Memory game type. 32 cards. No redeal.
<h3>Object</h3>
<p>
Remove all groups of jack-queen-king of the same suit.
<h3>Rules</h3>
<p>
Families is played with eight sets of Jack, Queen, and King - two of
each suit, along with six black jokers and two red jokers.
<p>
Flip three cards. If you flip a sequence of Jack, Queen, and King of
the same suit, the cards are removed. If not, they are flipped face-down.
<p>
If a red joker is flipped, all of the remaining unmatched cards that were
not flipped are reshuffled and redealt (after you've flipped all three
cards).
<p>
If a pair of black jokers is flipped, the game is lost. If all eight
sequences are removed before this happens, the game is won.
<h3>Notes</h3>
<p>
<i>Undo</i>, <i>Bookmarks</i>, <i>Autodrop</i> and <i>Quickplay</i>
are disabled for this game.

View file

@ -0,0 +1,22 @@
<h1>Flamboyant</h1>
<p>
Fan game type. 1 deck. No redeal.
<h3>Object</h3>
<p>
Move all cards to the foundations.
<h3>Rules</h3>
<p>
The tableau piles are built in seventeen sets of three cards, with
two in the back row face-down, and a third face-up card overlapping
them. The final card is dealt separately.
<p>
The tableau piles are built down by alternate color, with any face-up card
or valid sequence of cards being movable. Once the top card is moved,
the two below it can be flipped face-up and played. Empty piles cannot
be filled, except for the final single card, where a king or sequence of
cards starting with a king can be played.
<p>
Foundations are built up in suit from Ace to King. The game is won
when all cards are moved to the foundations.

View file

@ -1,6 +1,6 @@
<h1>Hurricane</h1>
<p>
Pairing game type. 1 deck. No redeal.
Pairing type. 1 deck. No redeal.
<h3>Object</h3>
<p>

View file

@ -0,0 +1,12 @@
<h1>Ides of March</h1>
<p>
Pairing type. 1 deck. No redeal.
<h3>Object</h3>
<p>
Move all cards to the single foundation.
<h3>Quick Description</h3>
<p>
Like <a href="hurricane.html">Hurricane</a>,
but remove pairs whose ranks total 15, or pairs of aces.

View file

@ -0,0 +1,12 @@
<h1>Little Ishido</h1>
<p>
Ishido game type. 2 decks. No redeal.
<h3>Object</h3>
<p>
Move all tiles to the playing area.
<h3>Quick Description</h3>
<p>
Like <a href='ishido.html'>Ishido</a>, but with four colors and symbols,
and a 6X8 grid. The initial tiles are placed in the four corners only.

View file

@ -0,0 +1,14 @@
<h1>Little Ishido Relaxed</h1>
<p>
Ishido game type. 2 decks. No redeal.
<h3>Object</h3>
<p>
Move all tiles to the playing area.
<h3>Quick Description</h3>
<p>
Like <a href='ishido.html'>Ishido</a>, but with four colors and symbols,
and a 6X8 grid. The initial tiles are placed in the four corners only.
Also, there are no restrictions when placing a tile next to two or more
other tiles - they just have to match the color or symbol of each.

19
html-src/rules/louis.html Normal file
View file

@ -0,0 +1,19 @@
<h1>Louis</h1>
<p>
Two-Deck game type. 2 decks. 2 redeals.
<h3>Object</h3>
<p>
Move all the cards to the foundations.
<h3>Quick Description</h3>
<p>
Like <a href="sthelena.html">St. Helena</a>,
but at the start of the game, a card is dealt to each of the
twelve tableau piles. During this round, an empty tableau pile
will be immediately filled from the talon. When no moves are left,
the rest of the deck can be dealt.
<p>
Also, there are no restrictions as to which tableau piles
cards can be moved to foundations from, and tableau piles are
built up or down by same suit.

View file

@ -0,0 +1,27 @@
<h1>Microbe</h1>
<p>
Spider type. 2 decks. No redeal.
<h3>Object</h3>
<p>
Group all the cards in sets of 13 cards in descending sequence
by alternate color from King to Ace and move such sets to the foundations.
<h3>Rules</h3>
<p>
The decks are separated. From one deck, four cards are dealt to each of
eleven tableau piles, with alternating cards face-up and face-down (the
bottom and third from the bottom cards are face-down). The remaining
eight cards from that deck are combined with the other deck to form the
talon.
<p>
Tableau piles are built down by alternate color, and any card or valid
sequence of cards can be moved between tableau piles. Any valid card or
sequence can fill empty piles.
<p>
The object is to group the cards in sets of 13 cards, from King to Ace
in an alternating color sequence. Such groups can be moved to the foundations.
<p>
When there are no moves left, you can deal one card from the talon to
each tableau pile. The game is won if all cards are grouped into sequences
and moved to the foundations.

View file

@ -0,0 +1,27 @@
<h1>Noumea</h1>
<p>
One-Deck game type. 1 decks. 2 redeals.
<h3>Object</h3>
<p>
Move all cards to the foundations.
<h3>Rules</h3>
<p>
The four aces are dealt to the foundations, and 20 cards
are dealt to reserve piles. The reserve piles can hold a
single card and are automatically filled from the waste or
talon when empty.
<p>
There is no building on the tableau piles, so you can
only move cards to the foundations.
<p>
When there are no moves left, you can deal cards three at
a time from the talon, and move them to an appropriate
foundation. Two redeals are allowed, so you can go through
the deck three times. The game is won if all cards are
moved to the foundations.
<h3>Notes</h3>
<p>
<i>Autodrop</i> is disabled for this game.

View file

@ -0,0 +1,12 @@
<h1>Outback Patience</h1>
<p>
Yukon type. 2 decks. No redeal.
<h3>Object</h3>
<p>
Move all cards to the foundations.
<h3>Quick Description</h3>
<p>
Like <a href="austalianpatience.html">Australian Patience</a>,
but with two decks, and eight piles of seven cards each.

View file

@ -0,0 +1,22 @@
<h1>Pyramid Thirteen</h1>
<p>
Pairing game type. 1 deck. No redeal.
<h3>Object</h3>
<p>
Move all cards to the single foundation.
<h3>Quick Description</h3>
<p>
Like <a href="pyramid.html">Pyramid</a>, but all but the
front row of the pyramid are dealt face-down, and no redeals
are allowed.
<h3>Notes</h3>
<p>
This difficult variant of Pyramid is based on the Gnome AisleRiot
rules. It is called "Thirteen" there, but renamed Pyramid Thirteen
here to avoid confusion with the game
<a href="thirteens.html">Thirteens</a>.
<p>
<i>Quickplay</i> is disabled for this game.

View file

@ -0,0 +1,12 @@
<h1>Relaxed Cruel</h1>
<p>
Baker's Dozen type. 1 deck. Unlimited redeals.
<h3>Object</h3>
<p>
Move all cards to the foundations.
<h3>Quick Description</h3>
<p>
Just like <a href="cruel.html">Cruel</a>,
but the number of cards you can move as a sequence is not restricted.

View file

@ -0,0 +1,13 @@
<h1>Wasp II</h1>
<p>
Spider type. 1 deck. No redeal.
<h3>Object</h3>
<p>
Move all cards to the foundations.
<h3>Quick Description</h3>
<p>
Like <a href="wasp.html">Wasp</a>,
but only three of the tableau piles contain
face-down cards (similar to <a href="scorpionii.html">Scorpion II</a>).

View file

@ -0,0 +1,15 @@
<h1>Wizard's Castle</h1>
<p>
Hex A Deck type. 1 deck. No redeal.
<h3>Object</h3>
<p>
Move all cards to the foundations.
<h3>Rules</h3>
<p>
Rows build down in rank in alternating color. Wizards are wild,
so any card can be played on one, and a wizard can be played on
any card. Only one card can be moved at a time. Foundations build
up in rank by suit. Any card can be played on an empty row. Cards
can be played from the foundations.

View file

@ -0,0 +1,23 @@
<h1>Wizard's Storeroom</h1>
<p>
Hex A Deck type. 1 deck. 1 redeal.
<h3>Object</h3>
<p>
Move all cards to the foundations.
<h3>Rules</h3>
<p>
The tableau consists of one reserve stack and five row stacks.
Fourteen cards are dealt to the reserve stack and one card each to the
row stacks. When a row stack is emptied it must be filled from the reserve
stack first. When the reserve stack is empty any card can be played on an
empty row stack. Wizards are wild - any card can be played on them, and they
can be played on any card. The Wizards have ranks like the suit cards
corresponding to Ace through Four. If a Wizard is played in it's proper rank
position the row can still be moved, otherwise, the full sequence can't be moved.
The row stacks build down in rank by alternate colors.
<p>
The foundations build up in rank by suit. The Wizards will not move off of the
tableau until all the other cards have been moved to the foundations.
The game is won when all cards are moved to the foundations.

View file

@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: PySol 0.0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-12-10 10:19-0500\n"
"PO-Revision-Date: 2024-12-28 19:10-0500\n"
"PO-Revision-Date: 2025-03-14 19:16-0400\n"
"Last-Translator: H. Schaekel <Holger.Schaekel@web.de>\n"
"Language-Team: German\n"
"Language: de\n"
@ -2653,7 +2653,7 @@ msgid "Automatic play"
msgstr "Automatisierung"
#: pysollib/kivy/menubar.py:513
msgid "Auto face up"
msgid "Auto face-up"
msgstr "Automatisch aufdecken"
#: pysollib/kivy/menubar.py:523
@ -4189,6 +4189,9 @@ msgstr "Nationalität:"
msgid "Year:"
msgstr "Jahr:"
msgid "Num. cards:"
msgstr ""
msgid "Compatible with current game"
msgstr ""
@ -4370,6 +4373,9 @@ msgstr "Aktuelle Sitzung"
msgid "Log"
msgstr "Protokoll"
msgid "Demo Log"
msgstr ""
#: pysollib/tile/tkstats.py:523 data/pysolfc.glade:1404
msgid "Full log"
msgstr "Volles Protokoll"
@ -5171,6 +5177,9 @@ msgstr "Kommentare..."
msgid "Log..."
msgstr ""
msgid "Demo log..."
msgstr ""
#: pysollib/ui/tktile/menubar.py:427
msgid "D&emo statistics..."
msgstr "D&emo Statistiken..."

View file

@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: 1.02\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-12-10 10:19-0500\n"
"PO-Revision-Date: 2024-12-28 19:10-0500\n"
"PO-Revision-Date: 2025-03-14 19:16-0400\n"
"Last-Translator: Eric Rausch <neelix570@gmail.com>\n"
"Language-Team: French\n"
"Language: fr\n"
@ -2694,7 +2694,7 @@ msgid "Automatic play"
msgstr "Jouer auto"
#: pysollib/kivy/menubar.py:513
msgid "Auto face up"
msgid "Auto face-up"
msgstr "Retourner auto"
#: pysollib/kivy/menubar.py:523
@ -4240,6 +4240,9 @@ msgstr "Origine:"
msgid "Year:"
msgstr "Année:"
msgid "Num. cards:"
msgstr ""
msgid "Compatible with current game"
msgstr ""
@ -4421,6 +4424,9 @@ msgstr "Session en cours"
msgid "Log"
msgstr "Journal"
msgid "Demo Log"
msgstr ""
#: pysollib/tile/tkstats.py:523 data/pysolfc.glade:1404
msgid "Full log"
msgstr "Journal complet"
@ -5222,6 +5228,9 @@ msgstr "&Commentaires..."
msgid "Log..."
msgstr "Journal..."
msgid "Demo log..."
msgstr ""
#: pysollib/ui/tktile/menubar.py:427
msgid "D&emo statistics..."
msgstr "Statistiques d&émo..."

View file

@ -12,7 +12,7 @@ msgstr ""
"Project-Id-Version: it_pysol\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-12-10 10:19-0500\n"
"PO-Revision-Date: 2024-12-28 19:11-0500\n"
"PO-Revision-Date: 2025-03-14 19:15-0400\n"
"Last-Translator: Giuliano Colla <giuliano.colla@gmail.com>\n"
"Language-Team: Italiano <it@li.org>\n"
"Language: it\n"
@ -2707,7 +2707,7 @@ msgstr "Gioco automatico"
#: pysollib/kivy/menubar.py:513
#, fuzzy
msgid "Auto face up"
msgid "Auto face-up"
msgstr "Auto &scopri"
#: pysollib/kivy/menubar.py:523
@ -4303,6 +4303,9 @@ msgstr "Nazionalità:"
msgid "Year:"
msgstr "Anno:"
msgid "Num. cards:"
msgstr ""
msgid "Compatible with current game"
msgstr ""
@ -4485,6 +4488,9 @@ msgstr "Questa sessione"
msgid "Log"
msgstr "Log"
msgid "Demo Log"
msgstr ""
#: pysollib/tile/tkstats.py:523 data/pysolfc.glade:1404
msgid "Full log"
msgstr "Log completo"
@ -5288,6 +5294,9 @@ msgstr "&Commenti..."
msgid "Log..."
msgstr "Log..."
msgid "Demo log..."
msgstr ""
#: pysollib/ui/tktile/menubar.py:427
msgid "D&emo statistics..."
msgstr "Statistiche d&emo..."

View file

@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: PySolFC\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-12-10 10:19-0500\n"
"PO-Revision-Date: 2024-12-28 19:11-0500\n"
"PO-Revision-Date: 2025-03-14 19:15-0400\n"
"Last-Translator: Jerzy Trzeciak <artusek@wp.pl>\n"
"Language-Team: Polish <pl@li.org>\n"
"Language: pl\n"
@ -2707,7 +2707,7 @@ msgid "Automatic play"
msgstr "Gra &automatyczna"
#: pysollib/kivy/menubar.py:513
msgid "Auto face up"
msgid "Auto face-up"
msgstr "Odkrywaj automatycznie"
#: pysollib/kivy/menubar.py:523
@ -4257,6 +4257,9 @@ msgstr "Narodowość:"
msgid "Year:"
msgstr "Rok:"
msgid "Num. cards:"
msgstr ""
msgid "Compatible with current game"
msgstr ""
@ -4438,6 +4441,9 @@ msgstr "Bieżąca sesja"
msgid "Log"
msgstr "Log"
msgid "Demo Log"
msgstr ""
#: pysollib/tile/tkstats.py:523 data/pysolfc.glade:1404
msgid "Full log"
msgstr "Kompletny log"
@ -5242,6 +5248,9 @@ msgstr "Komentarze..."
msgid "Log..."
msgstr "Log..."
msgid "Demo log..."
msgstr ""
#: pysollib/ui/tktile/menubar.py:427
msgid "D&emo statistics..."
msgstr "D&emo statystyk..."

View file

@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-12-10 10:19-0500\n"
"PO-Revision-Date: 2024-12-28 19:11-0500\n"
"PO-Revision-Date: 2025-03-14 19:15-0400\n"
"Last-Translator: Matheus Knack <mtknack555@gmail.com>\n"
"Language-Team: \n"
"Language: pt_BR\n"
@ -2715,7 +2715,7 @@ msgid "Automatic play"
msgstr "Jogar automaticamente"
#: pysollib/kivy/menubar.py:513
msgid "Auto face up"
msgid "Auto face-up"
msgstr "Virar pra cima automaticamente"
#: pysollib/kivy/menubar.py:523
@ -4263,6 +4263,9 @@ msgstr "Nacionalidade:"
msgid "Year:"
msgstr "Ano:"
msgid "Num. cards:"
msgstr ""
msgid "Compatible with current game"
msgstr ""
@ -4444,6 +4447,9 @@ msgstr "Sessão atual"
msgid "Log"
msgstr "registro"
msgid "Demo Log"
msgstr ""
#: pysollib/tile/tkstats.py:523 data/pysolfc.glade:1404
msgid "Full log"
msgstr "Registro completo"
@ -5244,6 +5250,9 @@ msgstr "&Comentários..."
msgid "Log..."
msgstr "Registro"
msgid "Demo log..."
msgstr ""
#: pysollib/ui/tktile/menubar.py:427
msgid "D&emo statistics..."
msgstr "Estatísticas de D&emo..."

View file

@ -2524,7 +2524,7 @@ msgid "Automatic play"
msgstr ""
#: pysollib/kivy/menubar.py:513
msgid "Auto face up"
msgid "Auto face-up"
msgstr ""
#: pysollib/kivy/menubar.py:523
@ -4045,6 +4045,9 @@ msgstr ""
msgid "Year:"
msgstr ""
msgid "Num. cards:"
msgstr ""
msgid "Compatible with current game"
msgstr ""
@ -4224,6 +4227,9 @@ msgstr ""
msgid "Log"
msgstr ""
msgid "Demo Log"
msgstr ""
#: pysollib/tile/tkstats.py:523 data/pysolfc.glade:1404
msgid "Full log"
msgstr ""
@ -4974,6 +4980,9 @@ msgstr ""
msgid "Log..."
msgstr ""
msgid "Demo log..."
msgstr ""
#: pysollib/ui/tktile/menubar.py:427
msgid "D&emo statistics..."
msgstr ""

View file

@ -7,7 +7,7 @@ msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-12-10 10:19-0500\n"
"PO-Revision-Date: 2024-12-28 19:12-0500\n"
"PO-Revision-Date: 2025-03-14 19:14-0400\n"
"Last-Translator: Skomoroh <skomoroh@gmail.com>\n"
"Language-Team: Russian <ru@li.org>\n"
"Language: ru\n"
@ -2713,7 +2713,7 @@ msgstr "Настройки &автоматической игры"
#: pysollib/kivy/menubar.py:513
#, fuzzy
msgid "Auto face up"
msgid "Auto face-up"
msgstr "Автоматически &переворачивать"
#: pysollib/kivy/menubar.py:523
@ -4312,6 +4312,9 @@ msgstr "Национальность:"
msgid "Year:"
msgstr "Год:"
msgid "Num. cards:"
msgstr ""
msgid "Compatible with current game"
msgstr ""
@ -4494,6 +4497,9 @@ msgstr "Текущая сессия"
msgid "Log"
msgstr "Лог"
msgid "Demo Log"
msgstr ""
#: pysollib/tile/tkstats.py:523 data/pysolfc.glade:1404
msgid "Full log"
msgstr "Полный лог"
@ -5311,6 +5317,9 @@ msgstr "&Комментарии..."
msgid "Log..."
msgstr "Лог..."
msgid "Demo log..."
msgstr ""
#: pysollib/ui/tktile/menubar.py:427
msgid "D&emo statistics..."
msgstr "Статистика демо..."

View file

@ -35,7 +35,8 @@ from pysollib.app_stat_result import GameStatResult
from pysollib.app_statistics import Statistics
from pysollib.cardsetparser import read_cardset_config
from pysollib.gamedb import GAME_DB, GI, loadGame
from pysollib.help import destroy_help_html, help_about, raise_help_html
from pysollib.help import (destroy_help_html, help_about, raise_help_html,
unraise_help_html)
from pysollib.images import Images, SubsampledImages
from pysollib.mfxutil import Struct, destruct
from pysollib.mfxutil import USE_PIL
@ -58,6 +59,9 @@ from pysollib.pysoltk import loadImage, wm_withdraw
from pysollib.pysoltk import raise_find_card_dialog
from pysollib.pysoltk import raise_full_picture_dialog
from pysollib.pysoltk import raise_solver_dialog
from pysollib.pysoltk import unraise_find_card_dialog
from pysollib.pysoltk import unraise_full_picture_dialog
from pysollib.pysoltk import unraise_solver_dialog
from pysollib.resource import CSI, CardsetManager
from pysollib.resource import Music, MusicManager
from pysollib.resource import Sample, SampleManager
@ -525,6 +529,12 @@ class Application:
raise_solver_dialog(self.game)
raise_help_html(self.game)
def unraiseAll(self):
unraise_find_card_dialog()
unraise_full_picture_dialog()
unraise_solver_dialog()
unraise_help_html()
def loadImages1(self):
# load dialog images
dirname = os.path.join("images", "logos")

View file

@ -115,18 +115,13 @@ class Statistics:
game.GAME_VERSION)
# full log
if status >= 0:
if player is None:
# demo
ret = self.updateGameStat(player, game, status)
else:
# player
if player not in self.prev_games:
self.prev_games[player] = []
self.prev_games[player].append(log)
if player not in self.all_prev_games:
self.all_prev_games[player] = []
self.all_prev_games[player].append(log)
ret = self.updateGameStat(player, game, status)
if player not in self.prev_games:
self.prev_games[player] = []
self.prev_games[player].append(log)
if player not in self.all_prev_games:
self.all_prev_games[player] = []
self.all_prev_games[player].append(log)
ret = self.updateGameStat(player, game, status)
# session log
if player not in self.session_games:
self.session_games[player] = []

View file

@ -589,8 +589,11 @@ class Game(object):
if not self.cards:
self.cards = self.createCards(progress=self.app.intro.progress)
self.initBindings()
# self.top.bind('<ButtonPress>', self.top._sleepEvent)
# self.top.bind('<3>', self.top._sleepEvent)
if TOOLKIT == 'tk':
# self.top.bind('<ButtonPress>', self.top._sleepEvent)
# self.top.bind('<3>', self.top._sleepEvent)
self.top.bind("<FocusOut>", self.top._focusOutEvent)
self.top.bind("<FocusIn>", self.top._focusInEvent)
# update display properties
self.canvas.busy = True
# geometry

View file

@ -112,7 +112,7 @@ class GI:
# extra flags
GT_BETA = 1 << 12 # beta version of game driver
GT_CHILDREN = 1 << 13 # *not used*
GT_CHILDREN = 1 << 13
GT_CONTRIB = 1 << 14 # contributed games under the GNU GPL
GT_HIDDEN = 1 << 15 # not visible in menus, but games can be loaded
GT_OPEN = 1 << 16
@ -320,7 +320,6 @@ class GI:
904: 68, # Lexington Harp
237: 22231, # Three Peaks
297: 631, # Alternation/Alternations
526: 447, # Australian/Outback Patience
640: 566, # Hypotenuse/Brazilian Patience
# Lost Mahjongg Layouts
@ -346,6 +345,15 @@ class GI:
# adding it should be a priority.
GAMES_BY_COMPATIBILITY = (
# Ace of Penguins (We have 6 out of 6 card games)
# Non-card games missing:
# - Mastermind
# - Minesweeper (Needs a new cardset type)
# - Pegged layouts (Not all compatible with the PySol engine)
# - Mahjongg/Taipei layouts
("Ace of Penguins", (2, 8, 11, 36, 105, 181, 210, 492,
5401, 22399)),
# Atari ST Patience game v2.13 (we have 10 out of 10 games)
("Atari ST Patience", (1, 3, 4, 7, 12, 14, 15, 16, 17, 39,)),
@ -368,12 +376,12 @@ class GI:
# Hamilton, Labyrinth, Treize, Wall
("Gnome AisleRiot", (
1, 2, 8, 9, 11, 12, 13, 19, 24, 27, 29, 31, 33, 34, 35, 36,
38, 40, 41, 42, 43, 45, 48, 58, 65, 67, 89, 91, 92, 93, 94,
95, 96, 97, 100, 104, 105, 111, 112, 113, 130, 135, 139, 144,
146, 147, 148, 200, 201, 206, 224, 225, 229, 230, 233, 257,
258, 277, 280, 281, 282, 283, 284, 334, 384, 479, 495, 551,
552, 553, 572, 593, 674, 700, 715, 716, 737, 772, 810, 819,
824, 829, 859, 874, 906, 934, 22231,
38, 40, 41, 42, 43, 44, 45, 48, 58, 65, 67, 89, 91, 92, 93,
94, 95, 96, 97, 100, 104, 105, 111, 112, 113, 130, 135, 139,
144, 146, 147, 148, 200, 201, 206, 224, 225, 229, 230, 233,
257, 258, 277, 280, 281, 282, 283, 284, 334, 384, 479, 495,
551, 552, 553, 572, 593, 674, 700, 715, 716, 737, 772, 810,
819, 824, 829, 859, 874, 906, 934, 22231,
)),
# Hoyle Card Games
@ -575,7 +583,7 @@ class GI:
('fc-0.9.0', tuple(range(323, 421))),
('fc-0.9.1', tuple(range(421, 441))),
('fc-0.9.2', tuple(range(441, 466))),
('fc-0.9.3', tuple(range(466, 661))),
('fc-0.9.3', tuple(range(466, 526)) + tuple(range(527, 661))),
('fc-0.9.4', tuple(range(661, 671))),
('fc-1.0', tuple(range(671, 711))),
('fc-1.1', tuple(range(711, 759))),
@ -595,12 +603,14 @@ class GI:
tuple(range(19000, 19012)) + tuple(range(22303, 22311)) +
tuple(range(22353, 22361))),
('fc-3.1', tuple(range(961, 971))),
('dev', tuple(range(971, 981)) + tuple(range(5419, 5421)) +
tuple(range(16683, 16686)) + tuple(range(18005, 18007)) +
(44, 526, 22399,)),
)
# deprecated - the correct way is to or a GI.GT_XXX flag
# in the registerGame() call
_CHILDREN_GAMES = [16, 33, 55, 90, 91, 96, 97, 176, 328, 329, 862, 865,
886, 903, ]
_CHILDREN_GAMES = []
_OPEN_GAMES = []

View file

@ -504,8 +504,8 @@ class MaineCoon(TabbyCat):
# register the game
registerGame(GameInfo(903, AcesUp, "Aces Up", # was: 52
GI.GT_1DECK_TYPE, 1, 0, GI.SL_LUCK,
altnames=("Aces High", "Drivel")))
GI.GT_1DECK_TYPE | GI.GT_CHILDREN, 1, 0, GI.SL_LUCK,
altnames=("Aces High", "Drivel", "Discard")))
registerGame(GameInfo(206, Fortunes, "Fortunes",
GI.GT_1DECK_TYPE, 1, 0, GI.SL_LUCK))
registerGame(GameInfo(213, RussianAces, "Russian Aces",
@ -514,7 +514,7 @@ registerGame(GameInfo(130, PerpetualMotion, "Perpetual Motion",
GI.GT_1DECK_TYPE, 1, -1, GI.SL_MOSTLY_LUCK,
altnames=("First Law", "Narcotic")))
registerGame(GameInfo(353, AcesUp5, "Aces Up 5",
GI.GT_1DECK_TYPE, 1, 0, GI.SL_LUCK))
GI.GT_1DECK_TYPE | GI.GT_CHILDREN, 1, 0, GI.SL_LUCK))
registerGame(GameInfo(552, Cover, "Cover",
GI.GT_1DECK_TYPE, 1, 0, GI.SL_LUCK))
registerGame(GameInfo(583, FiringSquad, "Firing Squad",

View file

@ -225,6 +225,7 @@ class Vineyard(CastlesInSpain):
# ************************************************************************
# * Cruel
# * Relaxed Cruel
# * Unusual
# ************************************************************************
@ -310,6 +311,10 @@ class Cruel(CastlesInSpain):
shallHighlightMatch = Game._shallHighlightMatch_SS
class RelaxedCruel(Cruel):
RowStack_Class = StackWrapper(SS_RowStack, base_rank=NO_RANK)
class Unusual(Cruel):
def createGame(self):
@ -453,3 +458,6 @@ registerGame(GameInfo(876, Vineyard, "Vineyard",
GI.SL_MOSTLY_SKILL))
registerGame(GameInfo(907, Martha, "Stewart",
GI.GT_BAKERS_DOZEN, 1, 0, GI.SL_BALANCED))
registerGame(GameInfo(977, RelaxedCruel, "Relaxed Cruel",
GI.GT_BAKERS_DOZEN | GI.GT_OPEN | GI.GT_RELAXED, 1, -1,
GI.SL_BALANCED))

View file

@ -957,7 +957,7 @@ registerGame(GameInfo(831, Siegecraft, "Siegecraft",
GI.SL_MOSTLY_SKILL))
registerGame(GameInfo(881, Lasker, "Lasker",
GI.GT_BELEAGUERED_CASTLE | GI.GT_OPEN, 1, 0,
GI.SL_SKILL))
GI.SL_SKILL, altnames=('Xantia',)))
registerGame(GameInfo(951, Morphy, "Morphy",
GI.GT_BELEAGUERED_CASTLE | GI.GT_OPEN, 1, 0,
GI.SL_SKILL))

View file

@ -646,7 +646,8 @@ registerGame(GameInfo(376, Backbone, "Backbone",
registerGame(GameInfo(377, BackbonePlus, "Backbone +",
GI.GT_NAPOLEON, 2, 0, GI.SL_BALANCED))
registerGame(GameInfo(510, BigBraid, "Big Braid",
GI.GT_NAPOLEON | GI.GT_ORIGINAL, 3, 2, GI.SL_BALANCED))
GI.GT_NAPOLEON | GI.GT_ORIGINAL, 3, 2, GI.SL_BALANCED,
altnames=("Der grose Zopf",)))
registerGame(GameInfo(694, Casket, "Casket",
GI.GT_2DECK_TYPE, 2, 0, GI.SL_BALANCED))
registerGame(GameInfo(717, Well, "Well",

View file

@ -46,6 +46,11 @@ class Crossword_Hint(AbstractHint):
r = rows[i]
if r.cards:
continue
if len(game.s.talon.cards) == 0:
for s in game.s.reserves:
if game.isValidPlay(r.id, s.getCard().rank + 1):
self.addHint(5000, 1, s, r)
continue
if game.isValidPlay(r.id, game.s.talon.getCard().rank + 1):
# TODO: Check a few moves ahead to get better hints.
self.addHint(5000, 1, game.s.talon, r)

View file

@ -105,6 +105,7 @@ class StrictEiffelTower(EiffelTower):
# register the game
registerGame(GameInfo(16, EiffelTower, "Eiffel Tower",
GI.GT_PAIRING_TYPE, 2, 0, GI.SL_MOSTLY_LUCK))
GI.GT_PAIRING_TYPE | GI.GT_CHILDREN, 2, 0,
GI.SL_MOSTLY_LUCK))
# registerGame(GameInfo(801, StrictEiffelTower, "Strict Eiffel Tower",
# GI.GT_PAIRING_TYPE, 2, 0))

View file

@ -776,11 +776,11 @@ registerGame(GameInfo(697, BigBen, "Big Ben",
GI.GT_2DECK_TYPE, 2, 0, GI.SL_BALANCED,
altnames=("Father Time")))
registerGame(GameInfo(737, Clock, "Clock",
GI.GT_1DECK_TYPE, 1, 0, GI.SL_LUCK,
GI.GT_1DECK_TYPE | GI.GT_CHILDREN, 1, 0, GI.SL_LUCK,
altnames=("Travellers", "Sundial")))
registerGame(GameInfo(827, GermanClock, "German Clock",
GI.GT_1DECK_TYPE, 1, 1, GI.SL_MOSTLY_LUCK,
altnames=("Die Uhr",)))
registerGame(GameInfo(915, RelaxedClock, "Relaxed Clock",
GI.GT_1DECK_TYPE | GI.GT_RELAXED, 1, 0, GI.SL_LUCK,
altnames=("Watch")))
GI.GT_1DECK_TYPE | GI.GT_RELAXED | GI.GT_CHILDREN, 1, 0,
GI.SL_LUCK, altnames=("Watch")))

View file

@ -1125,7 +1125,8 @@ registerGame(GameInfo(666, TrapdoorSpider, "Trapdoor Spider",
registerGame(GameInfo(712, Leprechaun, "Leprechaun",
GI.GT_GYPSY | GI.GT_ORIGINAL, 2, 0, GI.SL_MOSTLY_SKILL))
registerGame(GameInfo(718, LockedCards, "Locked Cards",
GI.GT_2DECK_TYPE, 2, 2, GI.SL_BALANCED))
GI.GT_2DECK_TYPE, 2, 2, GI.SL_BALANCED,
altnames=("Prisoners",)))
registerGame(GameInfo(721, Thirty, "Thirty",
GI.GT_1DECK_TYPE | GI.GT_OPEN | GI.GT_STRIPPED, 1, 0,
GI.SL_MOSTLY_SKILL, ranks=(0, 6, 7, 8, 9, 10, 11, 12)))
@ -1133,7 +1134,7 @@ registerGame(GameInfo(725, TopsyTurvyQueens, "Topsy-Turvy Queens",
GI.GT_2DECK_TYPE, 2, 2, GI.SL_BALANCED))
registerGame(GameInfo(792, KingsSecrets, "King's Secrets",
GI.GT_2DECK_TYPE, 2, 2, GI.SL_BALANCED,
altnames=('Royal Secrets',)))
altnames=('Royal Secrets', "King's Captives")))
registerGame(GameInfo(842, SwissPatience, "Swiss Patience",
GI.GT_GYPSY, 1, 0, GI.SL_BALANCED))
registerGame(GameInfo(890, YeastDough, "Yeast Dough",

View file

@ -170,5 +170,5 @@ registerGame(GameInfo(774, HitOrMiss, "Hit or Miss",
GI.GT_1DECK_TYPE, 1, VARIABLE_REDEALS,
GI.SL_LUCK, altnames=("Roll Call",)))
registerGame(GameInfo(865, HitOrMissUnlimited, "Hit or Miss Unlimited",
GI.GT_1DECK_TYPE, 1, UNLIMITED_REDEALS,
GI.GT_1DECK_TYPE | GI.GT_CHILDREN, 1, UNLIMITED_REDEALS,
GI.SL_LUCK))

View file

@ -36,7 +36,7 @@ from pysollib.stack import \
WasteTalonStack, \
Yukon_AC_RowStack, \
getNumberOfFreeStacks
from pysollib.util import ANY_RANK, KING
from pysollib.util import ANY_RANK, KING, NO_RANK
# ************************************************************************
@ -301,6 +301,96 @@ class Sarlacc(Interlock):
self.s.talon.dealRow(rows=self.s.rows[42:])
# ************************************************************************
# * Flamboyant
# ************************************************************************
class Flamboyant_RowStack(Interlock_RowStack):
STEP = ((2,), (1,), (), (2,), (1,), (), (2,), (1,), (),
(2,), (1,), (), (2,), (1,), (), (2,), (1,), (),
(2,), (1,), (), (2,), (1,), (), (2,), (1,), (),
(2,), (1,), (), (2,), (1,), (), (2,), (1,), (),
(2,), (1,), (), (2,), (1,), (), (2,), (1,), (),
(2,), (1,), (), (2,), (1,), ())
class Flamboyant(Interlock):
RowStack_Class = Flamboyant_RowStack
Talon_Class = InitialDealTalonStack
PLAYCARDS = 8
def _createTableauPiece(self, layout, x0, y0):
for i in range(2):
x = x0 + i * layout.XS // 2
y = y0 + i * layout.YS // 4
for j in range(2 - i):
stack = self.RowStack_Class(x, y, self, base_rank=NO_RANK)
self.s.rows.append(stack)
x = x + layout.XS
def createGame(self):
lay, s = Layout(self), self.s
w = (14.5 * lay.XS) + lay.XM
h = (4.2 * lay.YS) + (self.PLAYCARDS * lay.YOFFSET * 3) + lay.YM
self.setSize(w, h)
x, y = lay.XM, lay.YM
# create stacks
for i in range(2):
for i in range(6):
self._createTableauPiece(lay, x, y)
x += (2.25 * lay.XS)
y += (1.4 * lay.YS) + (self.PLAYCARDS * lay.YOFFSET)
x = lay.XM
for i in range(5):
self._createTableauPiece(lay, x, y)
x += (2.25 * lay.XS)
x += (.5 * lay.XS)
self.s.rows.append(AC_RowStack(x, y, self, base_rank=KING))
x, y = lay.XM + (13.5 * lay.XS), lay.YM
for i in range(4):
s.foundations.append(SS_FoundationStack(x, y, self, i))
y += lay.YS
s.talon = InitialDealTalonStack(x, y, self)
lay.defaultStackGroups()
def startGame(self):
self.startDealSample()
backrows = []
frontrows = []
for i, item in enumerate(self.s.rows):
if (i + 1) % 3 == 0 or i >= 51:
frontrows.append(item)
else:
backrows.append(item)
self.s.talon.dealRow(rows=backrows, flip=0, frames=0)
self.s.talon.dealRow(rows=frontrows)
def _getClosestStack(self, cx, cy, stacks, dragstack):
closest, cdist = None, 999999999
# Since we only compare distances,
# we don't bother to take the square root.
for stack in stacks:
# Flamboyant uses different logic to determine back row
# stacks.
backrows = []
frontrows = []
for i, item in enumerate(self.s.rows):
if (i + 1) % 3 == 0:
frontrows.append(item)
else:
backrows.append(item)
if len(stack.cards) == 0 and stack in frontrows:
continue
dist = (stack.x - cx)**2 + (stack.y - cy)**2
if dist < cdist:
closest, cdist = stack, dist
return closest
# register the game
registerGame(GameInfo(852, Guardian, "Guardian",
GI.GT_KLONDIKE, 1, -1, GI.SL_BALANCED))
@ -310,3 +400,5 @@ registerGame(GameInfo(939, LoveADuck, "Love a Duck",
GI.GT_YUKON | GI.GT_OPEN, 1, 0, GI.SL_MOSTLY_SKILL))
registerGame(GameInfo(946, Sarlacc, "Sarlacc",
GI.GT_FREECELL | GI.GT_OPEN, 1, 0, GI.SL_MOSTLY_SKILL))
registerGame(GameInfo(979, Flamboyant, "Flamboyant",
GI.GT_FAN_TYPE, 1, 0, GI.SL_BALANCED))

View file

@ -1640,7 +1640,8 @@ registerGame(GameInfo(66, Eastcliff, "Eastcliff",
registerGame(GameInfo(224, Easthaven, "Easthaven",
GI.GT_GYPSY, 1, 0, GI.SL_MOSTLY_LUCK))
registerGame(GameInfo(33, Westcliff, "Westcliff",
GI.GT_KLONDIKE, 1, 0, GI.SL_MOSTLY_LUCK))
GI.GT_KLONDIKE | GI.GT_CHILDREN, 1, 0,
GI.SL_MOSTLY_LUCK))
registerGame(GameInfo(225, Westhaven, "Westhaven",
GI.GT_GYPSY, 1, 0, GI.SL_BALANCED))
registerGame(GameInfo(107, PasSeul, "Pas Seul",

View file

@ -407,7 +407,7 @@ class AbstractMahjonggGame(Game):
# width of self.texts.info
# ti_width = Font(self.canvas, font).measure(_('Remaining'))
ti_width = 80
ti_width = max(80, l.CW)
# set window size
dxx, dyy = abs(dx) * (max_tl+1), abs(dy) * (max_tl+1)

View file

@ -356,6 +356,48 @@ r(5418, "Zigzag", layout="0aabaajaaracahcb" +
"ynvyniyoaypaAehA" +
"faAgaAmhAnaAoaCa" +
"aCfaCnaCs")
r(5419, "Lizard", layout="0aadaafaahaajhbe" +
"hbghbibccaceacga" +
"ciackidjbebaejae" +
"laeqheraesifkbga" +
"agehgfaggagkagmb" +
"grihlaiabifailai" +
"nbirijmojqakaakg" +
"hkhakiokihkjakko" +
"kkvklakmvknakook" +
"ohkpakqhllolmhln" +
"ambamkammamohnlo" +
"nmhnnaokaomaoohp" +
"lopmhpnaqjaqlaqn" +
"hrkorlhrmasjasla" +
"snhtkotlhtmaujau" +
"launhvkovlhvmawi" +
"awkawmhxjpxkhxla" +
"yiaykaymazehzfaz" +
"gozghzhozihzjvzj" +
"ozkhzlvzlozmhzna" +
"zoozohzpazqaAiaA" +
"kaAmbBdiBkbBraCj" +
"aClaDchDdaDehDka" +
"DqhDraDsaEjaElaF" +
"daFraGk")
r(5420, "Candy", layout="0daadaccaebagbai" +
"aakdcbccdbcfbcha" +
"cjcecbeebegaeibg" +
"dbgfaghbieaigakc" +
"akeakgakiamaamcb" +
"mebmgamiamkaoabo" +
"cboebogboiaokopf" +
"aqabqcbqebqgbqia" +
"qkorfasabscbsebs" +
"gbsiaskauaaucbue" +
"bugauiaukawcawea" +
"wgawiayebygaAdbA" +
"fbAhaCcbCebCgcCi" +
"aEbbEdbEfcEhdEja" +
"GabGcbGecGgdGidG" +
"k")
# ----------------------------------------------------------------------

View file

@ -299,6 +299,9 @@ class Quatorze(MonteCarlo):
return (stack1.id // 5 == stack2.id // 5 or
stack1.id % 5 == stack2.id % 5)
def shallHighlightMatch(self, stack1, card1, stack2, card2):
return card1.rank + card2.rank == 12
# ************************************************************************
# * Simple Pairs
@ -364,6 +367,10 @@ class BlockTen(SimplePairs):
def isGameWon(self):
return len(self.s.foundations[0].cards) == 48
def shallHighlightMatch(self, stack1, card1, stack2, card2):
return (card1.rank + card2.rank == 8 or
(9 < card1.rank == card2.rank > 9))
class SimpleTens(BlockTen):
def isGameWon(self):
@ -1108,6 +1115,10 @@ class AcesSquare(MonteCarlo):
return (stack1.id // 4 == stack2.id // 4 or
stack1.id % 4 == stack2.id % 4)
def shallHighlightMatch(self, stack1, card1, stack2, card2):
return (card1.suit == card2.suit and
card1.rank != card2.rank != 0)
# register the game
registerGame(GameInfo(89, MonteCarlo, "Monte Carlo",
@ -1118,15 +1129,16 @@ registerGame(GameInfo(216, MonteCarlo2Decks, "Monte Carlo (2 Decks)",
registerGame(GameInfo(212, Weddings, "Weddings",
GI.GT_PAIRING_TYPE, 1, 0, GI.SL_MOSTLY_LUCK))
registerGame(GameInfo(90, SimpleCarlo, "Simple Carlo",
GI.GT_PAIRING_TYPE, 1, 0, GI.SL_MOSTLY_LUCK))
GI.GT_PAIRING_TYPE | GI.GT_CHILDREN, 1, 0,
GI.SL_MOSTLY_LUCK))
registerGame(GameInfo(91, SimplePairs, "Simple Pairs",
GI.GT_PAIRING_TYPE, 1, 0, GI.SL_LUCK,
altnames=("Jamestown", "Pirate Gold", "Treasure Hunt",
"Hunter")))
GI.GT_PAIRING_TYPE | GI.GT_CHILDREN, 1, 0,
GI.SL_LUCK, altnames=("Jamestown", "Pirate Gold",
"Treasure Hunt", "Hunter")))
registerGame(GameInfo(92, Neighbour, "Neighbour",
GI.GT_PAIRING_TYPE, 1, 0, GI.SL_MOSTLY_LUCK))
registerGame(GameInfo(96, Fourteen, "Fourteen",
GI.GT_PAIRING_TYPE | GI.GT_OPEN, 1, 0,
GI.GT_PAIRING_TYPE | GI.GT_OPEN | GI.GT_CHILDREN, 1, 0,
GI.SL_MOSTLY_LUCK, altnames=("Fourteen Out",
"Fourteen Puzzle",
"Take Fourteen")))
@ -1137,12 +1149,13 @@ registerGame(GameInfo(152, DerLetzteMonarch, "The Last Monarch",
GI.GT_1DECK_TYPE | GI.GT_OPEN, 1, 0, GI.SL_MOSTLY_SKILL,
altnames=("Der letzte Monarch",)))
registerGame(GameInfo(328, TheWish, "The Wish",
GI.GT_PAIRING_TYPE | GI.GT_STRIPPED, 1, 0,
GI.SL_MOSTLY_LUCK, ranks=(0, 6, 7, 8, 9, 10, 11, 12)))
GI.GT_PAIRING_TYPE | GI.GT_STRIPPED | GI.GT_CHILDREN,
1, 0, GI.SL_MOSTLY_LUCK,
ranks=(0, 6, 7, 8, 9, 10, 11, 12)))
registerGame(GameInfo(329, TheWishOpen, "The Wish (Open)",
GI.GT_PAIRING_TYPE | GI.GT_OPEN | GI.GT_ORIGINAL |
GI.GT_STRIPPED, 1, 0, GI.SL_MOSTLY_SKILL,
ranks=(0, 6, 7, 8, 9, 10, 11, 12),
GI.GT_STRIPPED | GI.GT_CHILDREN, 1, 0,
GI.SL_MOSTLY_SKILL, ranks=(0, 6, 7, 8, 9, 10, 11, 12),
rules_filename="thewish.html"))
registerGame(GameInfo(368, Vertical, "Vertical",
GI.GT_PAIRING_TYPE | GI.GT_OPEN, 1, 0,
@ -1162,8 +1175,8 @@ registerGame(GameInfo(810, Quatorze, "Quatorze",
registerGame(GameInfo(829, BlockTen, "Block Ten",
GI.GT_PAIRING_TYPE, 1, 0, GI.SL_LUCK))
registerGame(GameInfo(862, SimpleTens, "Simple Tens",
GI.GT_PAIRING_TYPE | GI.GT_STRIPPED, 1, 0, GI.SL_LUCK,
ranks=(0, 1, 2, 3, 4, 5, 6, 7, 8),
GI.GT_PAIRING_TYPE | GI.GT_STRIPPED | GI.GT_CHILDREN,
1, 0, GI.SL_LUCK, ranks=(0, 1, 2, 3, 4, 5, 6, 7, 8),
altnames=("Add Up Tens",)))
registerGame(GameInfo(867, DoubleFourteen, "Double Fourteen",
GI.GT_PAIRING_TYPE | GI.GT_OPEN, 2, 0,

View file

@ -41,6 +41,7 @@ from pysollib.stack import \
RK_FoundationStack, \
RK_RowStack, \
ReserveStack, \
SC_FoundationStack, \
SS_FoundationStack, \
Stack, \
StackWrapper, \
@ -176,6 +177,7 @@ class Numerica2Decks(Numerica):
# ************************************************************************
# * Lady Betty
# * Last Chance
# * Colours
# ************************************************************************
class LadyBetty(Numerica):
@ -215,6 +217,40 @@ class LastChance(LadyBetty):
self.s.talon.dealCards()
class Colours(LadyBetty):
Foundation_Class = StackWrapper(SC_FoundationStack, mod=13, suit=ANY_SUIT)
def startGame(self):
self.startDealSample()
self.s.talon.dealRow(rows=self.s.foundations)
self.s.talon.dealCards() # deal first card to WasteStack
def _shuffleHook(self, cards):
# prepare first cards
topcards = [None] * 4
evencolor = -1
oddcolor = -1
for c in cards[:]:
if 0 < c.rank <= 4 and topcards[c.rank - 1] is None:
if c.rank % 2 == 0:
if evencolor != -1 and c.color != evencolor:
continue
if oddcolor != -1 and c.color == oddcolor:
continue
evencolor = c.color
elif c.rank % 2 == 1:
if oddcolor != -1 and c.color != oddcolor:
continue
if evencolor != -1 and c.color == evencolor:
continue
oddcolor = c.color
topcards[c.rank - 1] = c
cards.remove(c)
topcards.reverse()
return cards + topcards
# ************************************************************************
# * Puss in the Corner
# ************************************************************************
@ -1373,7 +1409,7 @@ class NinetyOne(Game):
def updateText(self):
if self.preview > 1 or not self.texts.score:
return
self.texts.score.config(text=self.getTotal())
self.texts.score.config(text=str(self.getTotal()))
def getTotal(self):
total = 0
@ -1460,3 +1496,5 @@ registerGame(GameInfo(931, TheBogey, "The Bogey",
GI.GT_1DECK_TYPE, 1, -1, GI.SL_BALANCED))
registerGame(GameInfo(958, NinetyOne, "Ninety-One",
GI.GT_1DECK_TYPE, 1, 0, GI.SL_MOSTLY_SKILL))
registerGame(GameInfo(972, Colours, "Colours",
GI.GT_NUMERICA, 1, 0, GI.SL_BALANCED))

View file

@ -383,51 +383,18 @@ class PyramidDozen(Giza):
# ************************************************************************
# * Thirteen
# * FIXME: UNFINISHED
# * (this doesn't work yet as 2 cards of the Waste should be playable)
# * Pyramid Thirteen
# ************************************************************************
# Previous comments suggest there would need to be two waste piles. This
# is not true. Based on AisleRiot's rules, the two waste cards can only
# be played with each other, which is the same as how PySol's traditional
# version works. So the remaining AisleRiot differences are captured
# in "Pyramid Thirteen", renamed from "Thirteen" to avoid confusion with
# "Thirteens"
class Thirteen(Pyramid):
#
# game layout
#
def createGame(self):
# create layout
layout, s = Layout(self), self.s
# set window
self.setSize(7*layout.XS+layout.XM, 5*layout.YS+layout.YM)
# create stacks
for i in range(7):
x = layout.XM + (6-i) * layout.XS // 2
y = layout.YM + layout.YS + i * layout.YS // 2
for j in range(i+1):
s.rows.append(Pyramid_RowStack(x, y, self))
x = x + layout.XS
x, y = layout.XM, layout.YM
s.talon = WasteTalonStack(x, y, self, max_rounds=1)
layout.createText(s.talon, "s")
x = x + layout.XS
s.waste = Pyramid_Waste(x, y, self, max_accept=1)
layout.createText(s.waste, "s")
s.waste.CARD_XOFFSET = 14
x, y = self.width - layout.XS, layout.YM
s.foundations.append(Pyramid_Foundation(x, y, self,
suit=ANY_SUIT, dir=0, base_rank=ANY_RANK,
max_move=0, max_cards=52))
# define stack-groups
self.sg.talonstacks = [s.talon] + [s.waste]
self.sg.openstacks = s.rows + self.sg.talonstacks
self.sg.dropstacks = s.rows + self.sg.talonstacks
#
# game overrides
#
Talon_Class = StackWrapper(Pyramid_Talon, max_rounds=1, max_accept=1)
def startGame(self):
self.startDealSample()
@ -616,8 +583,8 @@ class Elevens(Pyramid):
self.leaveState(old_state)
def shallHighlightMatch(self, stack1, card1, stack2, card2):
# FIXME
return False
return (card1.rank + card2.rank == 9 or
(9 < card1.rank != card2.rank > 9))
class ElevensToo(Elevens):
@ -666,6 +633,11 @@ class SuitElevens(Elevens):
def createGame(self):
Elevens.createGame(self, rows=3, cols=5)
def shallHighlightMatch(self, stack1, card1, stack2, card2):
return (Elevens.shallHighlightMatch(self, stack1, card1,
stack2, card2)
and card1.suit == card2.suit)
# ************************************************************************
# * Tens
@ -698,6 +670,10 @@ class Tens(ElevensToo):
def createGame(self):
Elevens.createGame(self, rows=2, cols=7, maxpiles=13, reserves=4)
def shallHighlightMatch(self, stack1, card1, stack2, card2):
return (card1.rank + card2.rank == 8 or
(8 < card1.rank == card2.rank > 8))
class Nines_RowStack(Elevens_RowStack):
ACCEPTED_SUM = 7
@ -742,6 +718,10 @@ class Nines(Tens):
def createGame(self):
Elevens.createGame(self, rows=3, cols=3, reserves=4)
def shallHighlightMatch(self, stack1, card1, stack2, card2):
return (card1.rank + card2.rank == 7 or
(8 < card1.rank != card2.rank > 8))
# ************************************************************************
# * The Lucky Number
@ -881,6 +861,9 @@ class Fifteens(Elevens):
self._dropReserve()
self.leaveState(old_state)
def shallHighlightMatch(self, stack1, card1, stack2, card2):
return card1.rank + card2.rank == 13
# ************************************************************************
# * Eighteens
@ -973,6 +956,9 @@ class Eighteens(Fifteens):
self._dropReserve()
self.leaveState(old_state)
def shallHighlightMatch(self, stack1, card1, stack2, card2):
return False # How?
# ************************************************************************
# * Neptune
@ -1002,6 +988,10 @@ class Neptune(Thirteens):
def isGameWon(self):
return len(self.s.talon.cards) == 0
def shallHighlightMatch(self, stack1, card1, stack2, card2):
return (card1.rank == card2.rank - 1 or
card1.rank == card2.rank + 1)
# ************************************************************************
# * Eight Cards
@ -1094,6 +1084,9 @@ class EightCards(Thirteens):
# save vars (for undo/redo)
return [self.draws]
def shallHighlightMatch(self, stack1, card1, stack2, card2):
return card1.rank + card2.rank == 9
# ************************************************************************
# * Triple Alliance
@ -1422,6 +1415,10 @@ class ElevenTriangle(Apophis):
INVERT = True
MAX_ROUNDS = 1
def shallHighlightMatch(self, stack1, card1, stack2, card2):
return (card1.rank + card2.rank == 9 or
(9 < card1.rank == card2.rank > 9))
# ************************************************************************
# * Cheops
@ -1791,6 +1788,8 @@ class Hurricane_Reserve(Hurricane_StackMethods, OpenStack):
class Hurricane(Pyramid):
Hint_Class = Hurricane_Hint
RowStack_Class = Hurricane_RowStack
Reserve_Class = Hurricane_Reserve
def createGame(self):
# create layout
@ -1808,7 +1807,7 @@ class Hurricane(Pyramid):
(0, 2), (1, 2), (2, 2), (3, 2),
):
x, y = layout.XM + 1.5*layout.XS + ww*xx, layout.YM + layout.YS*yy
stack = Hurricane_Reserve(x, y, self, max_accept=1)
stack = self.Reserve_Class(x, y, self, max_accept=1)
stack.CARD_XOFFSET, stack.CARD_YOFFSET = layout.XOFFSET, 0
s.reserves.append(stack)
@ -1816,7 +1815,7 @@ class Hurricane(Pyramid):
x = layout.XM + 1.5*layout.XS + layout.XS+2*layout.XOFFSET + d//2
y = layout.YM+layout.YS
for i in range(3):
stack = Hurricane_RowStack(x, y, self, max_accept=1)
stack = self.RowStack_Class(x, y, self, max_accept=1)
s.rows.append(stack)
x += layout.XS
@ -1846,6 +1845,45 @@ class Hurricane(Pyramid):
self.s.talon.moveMove(1, stack)
self.leaveState(old_state)
def shallHighlightMatch(self, stack1, card1, stack2, card2):
return card1.rank + card2.rank == 12
# ************************************************************************
# * Ides of March
# ************************************************************************
class IdesOfMarch_StackMethods(Pyramid_StackMethods):
def acceptsCards(self, from_stack, cards):
if from_stack is self:
return False
if len(cards) != 1:
return False
if not self.cards:
return False
c1 = self.cards[-1]
c2 = cards[0]
return (c1.face_up and c2.face_up and
(c1.rank + c2.rank == 13 or c1.rank + c2.rank == 0))
class IdesOfMarch_RowStack(IdesOfMarch_StackMethods, BasicRowStack):
pass
class IdesOfMarch_Reserve(IdesOfMarch_StackMethods, OpenStack):
pass
class IdesOfMarch(Hurricane):
RowStack_Class = IdesOfMarch_RowStack
Reserve_Class = IdesOfMarch_Reserve
def shallHighlightMatch(self, stack1, card1, stack2, card2):
return (card1.rank + card2.rank == 13 or
card1.rank + card2.rank == 0)
# register the game
registerGame(GameInfo(38, Pyramid, "Pyramid",
@ -1854,8 +1892,8 @@ registerGame(GameInfo(193, RelaxedPyramid, "Relaxed Pyramid",
GI.GT_PAIRING_TYPE | GI.GT_RELAXED, 1, 2,
GI.SL_MOSTLY_LUCK,
altnames=("Pyramid's Stones", "Pyramid Clear")))
# registerGame(GameInfo(44, Thirteen, "Thirteen",
# GI.GT_PAIRING_TYPE, 1, 0))
registerGame(GameInfo(44, Thirteen, "Pyramid Thirteen",
GI.GT_PAIRING_TYPE, 1, 0, GI.SL_MOSTLY_LUCK))
registerGame(GameInfo(592, Giza, "Giza",
GI.GT_PAIRING_TYPE | GI.GT_OPEN, 1, 0, GI.SL_BALANCED))
registerGame(GameInfo(593, Thirteens, "Thirteens",
@ -1874,7 +1912,8 @@ registerGame(GameInfo(619, TripleAlliance, "Triple Alliance",
GI.GT_PAIRING_TYPE | GI.GT_OPEN, 1, 0,
GI.SL_MOSTLY_SKILL, altnames=('Triplets',)))
registerGame(GameInfo(655, Pharaohs, "Pharaohs",
GI.GT_PAIRING_TYPE | GI.GT_OPEN, 1, 0, GI.SL_BALANCED))
GI.GT_PAIRING_TYPE | GI.GT_OPEN, 1, 0, GI.SL_BALANCED,
altnames=("Three Pharaohs",)))
registerGame(GameInfo(657, Baroness, "Baroness",
GI.GT_PAIRING_TYPE, 1, 0, GI.SL_BALANCED,
altnames=('Five Piles',)))
@ -1895,7 +1934,7 @@ registerGame(GameInfo(699, DoublePyramid, "Double Pyramid",
GI.GT_PAIRING_TYPE, 2, 2, GI.SL_MOSTLY_LUCK))
registerGame(GameInfo(700, Triangle, "Triangle",
GI.GT_PAIRING_TYPE, 1, 2, GI.SL_MOSTLY_LUCK,
altnames=('Yield',)))
altnames=('Yield', 'Funnel')))
registerGame(GameInfo(701, UpAndDown, "Up and Down",
GI.GT_PAIRING_TYPE | GI.GT_ORIGINAL, 2, 2,
GI.SL_MOSTLY_LUCK))
@ -1927,3 +1966,6 @@ registerGame(GameInfo(961, Nines, "Nines",
GI.GT_PAIRING_TYPE, 1, 0, GI.SL_LUCK))
registerGame(GameInfo(969, ElevenTriangle, "Eleven Triangle",
GI.GT_PAIRING_TYPE, 1, 0, GI.SL_MOSTLY_LUCK))
registerGame(GameInfo(974, IdesOfMarch, "Ides of March",
GI.GT_PAIRING_TYPE, 1, 0, GI.SL_MOSTLY_LUCK,
altnames=("XV",)))

View file

@ -231,6 +231,58 @@ class Kingdom(RoyalCotillion):
self.s.talon.dealCards() # deal first card to WasteStack
# ************************************************************************
# * Noumea
# ************************************************************************
class Noumea(RoyalCotillion):
Foundation_Class = SS_FoundationStack
def createGame(self):
# create layout
l, s = Layout(self), self.s
# set window
self.setSize(l.XM + 5*l.XS, l.YM + 6*l.YS)
# create stacks
x, y, = l.XM + .5 * l.XS, l.YM
for i in range(4):
s.foundations.append(self.Foundation_Class(x, y, self, i))
x += l.XS
x, y, = l.XM, y + l.YS
for j in range(4):
for i in range(5):
s.reserves.append(ReserveStack(x, y, self, max_accept=0))
x += l.XS
x = l.XM
y += l.YS
x, y = l.XM + 1.5 * l.XS, l.YM + 5 * l.YS
s.talon = WasteTalonStack(x, y, self, max_rounds=3, num_deal=3)
l.createText(s.talon, "sw")
x += l.XS
s.waste = WasteStack(x, y, self)
l.createText(s.waste, "se")
# define stack-groups
l.defaultStackGroups()
#
# game overrides
#
def _shuffleHook(self, cards):
# move Aces to top of the Talon (i.e. first cards to be dealt)
return self._shuffleHookMoveToTop(
cards, lambda c: (c.rank == ACE, c.suit))
def startGame(self):
self.s.talon.dealRow(rows=self.s.foundations, frames=0)
self.startDealSample()
self.s.talon.dealRow(rows=self.s.reserves)
self.s.talon.dealCards() # deal first card to WasteStack
# ************************************************************************
# * Alhambra
# * Granada
@ -1573,13 +1625,14 @@ registerGame(GameInfo(54, RoyalCotillion, "Royal Cotillion",
GI.GT_2DECK_TYPE, 2, 0, GI.SL_LUCK,
altnames=("Lords and Ladies",)))
registerGame(GameInfo(55, OddAndEven, "Odd and Even",
GI.GT_2DECK_TYPE, 2, 1, GI.SL_LUCK))
GI.GT_2DECK_TYPE | GI.GT_CHILDREN, 2, 1, GI.SL_LUCK))
registerGame(GameInfo(143, Kingdom, "Kingdom",
GI.GT_2DECK_TYPE, 2, 0, GI.SL_MOSTLY_LUCK))
registerGame(GameInfo(234, Alhambra, "Alhambra",
GI.GT_2DECK_TYPE, 2, 2, GI.SL_BALANCED))
registerGame(GameInfo(97, Carpet, "Carpet",
GI.GT_1DECK_TYPE, 1, 0, GI.SL_MOSTLY_LUCK))
GI.GT_1DECK_TYPE | GI.GT_CHILDREN, 1, 0,
GI.SL_MOSTLY_LUCK))
registerGame(GameInfo(391, BritishConstitution, "British Constitution",
GI.GT_2DECK_TYPE | GI.GT_STRIPPED, 2, 0, GI.SL_BALANCED,
ranks=list(range(11)), # without Queens and Kings
@ -1623,3 +1676,5 @@ registerGame(GameInfo(943, RosamundsBower, "Rosamund's Bower",
altnames=("Rosamund",)))
registerGame(GameInfo(952, BigAlhambra, "Big Alhambra",
GI.GT_3DECK_TYPE, 3, 2, GI.SL_BALANCED))
registerGame(GameInfo(978, Noumea, "Noumea",
GI.GT_1DECK_TYPE, 1, 2, GI.SL_MOSTLY_LUCK))

View file

@ -291,7 +291,8 @@ class Maze(Game):
# register the game
registerGame(GameInfo(118, SiebenBisAs, "Sieben bis As",
GI.GT_MONTANA | GI.GT_OPEN | GI.GT_STRIPPED, 1, 0,
GI.SL_MOSTLY_SKILL, ranks=(0, 6, 7, 8, 9, 10, 11, 12)))
GI.SL_MOSTLY_SKILL, ranks=(0, 6, 7, 8, 9, 10, 11, 12),
altnames=("Seven to Ace",)))
registerGame(GameInfo(144, Maze, "Maze",
GI.GT_MONTANA | GI.GT_OPEN, 1, 0, GI.SL_MOSTLY_SKILL,
si={"ncards": 48}))

View file

@ -355,7 +355,7 @@ class CribbagePatience_HandStack(ReserveStack):
def clickHandler(self, event):
for s in self.game.s.rows[0:4]:
if len(s.cards) == 0:
if len(s.cards) == 0 and s.acceptsCards(self, self.cards):
return self.playMoveMove(1, s)
return 0
@ -367,26 +367,32 @@ class CribbagePatience_HandStack(ReserveStack):
def moveMove(self, ncards, to_stack, frames=-1, shadow=-1):
ReserveStack.moveMove(self, ncards, to_stack, frames=frames,
shadow=shadow)
self.game.dealAdditionalHand()
if self.game.isBoardFull():
self.game.finalizeHand()
class CribbagePatience_CribStack(ReserveStack):
def acceptsCards(self, from_stack, cards):
if from_stack not in self.game.s.rows[4:10]:
if from_stack not in self.game.s.rows[4:]:
return False
if self.game.HANDS > 1:
if (len(self.game.s.rows[10].cards) > 0 and
from_stack not in self.game.s.rows[10:]):
return False
return ReserveStack.acceptsCards(self, from_stack, cards)
class CribbagePatience(CribbageShuffle):
WIN_SCORE = 61
WIN_SCORE = 80
HANDS = 1
def createGame(self):
self.score = 0
self.isFinalizedHand = False
l, s = Layout(self), self.s
self.setSize((2 * l.XM) + 8 * l.XS,
l.YM + l.YS + 12 * l.YOFFSET)
l.YM + ((self.HANDS + 1) * l.YS))
x, y = self.getInvisibleCoords()
s.waste = ReserveStack(x, y, self)
x, y = l.XM, l.YM
@ -398,20 +404,33 @@ class CribbagePatience(CribbageShuffle):
x += l.XS
x, y = l.XM, l.YM + l.YS
s.reserves.append(ReserveStack(x, y, self))
x += 2 * l.XS
for i in range(6):
s.rows.append(CribbagePatience_HandStack(x, y, self, max_move=1))
x += l.XS
for i in range(self.HANDS):
x += 2 * l.XS
for i in range(6):
s.rows.append(CribbagePatience_HandStack(x, y, self,
max_move=1))
x += l.XS
x, y = l.XM, y + l.YS
# define hands for scoring
r = s.rows
self.cribbage_hands = [
r[0:4], r[4:8]
]
if self.HANDS == 1:
self.cribbage_hands = [
r[0:4], r[4:8]
]
else:
self.cribbage_hands = [
r[0:4], r[4:8], r[10:14]
]
self.cribbage_hands = list(map(tuple, self.cribbage_hands))
if self.preview <= 1:
for i in (0, 4):
if self.HANDS == 2:
scores = (0, 4, 10)
else:
scores = (0, 4)
for i in scores:
tx, ty, ta, tf = l.getTextAttr(s.rows[i], anchor="w")
t = MfxCanvasText(self.canvas, tx - 8, ty,
anchor=ta,
@ -444,11 +463,12 @@ class CribbagePatience(CribbageShuffle):
def dealHand(self):
self.startDealSample()
if self.isFinalizedHand:
for r in reversed(self.s.rows[:8]):
r.moveMove(1, self.s.waste)
self.s.reserves[0].moveMove(1, self.s.waste)
self.saveStateMove(2 | 16)
if self.isFinalizedHand:
for r in reversed(self.s.rows):
if (len(r.cards) > 0):
r.moveMove(1, self.s.waste)
self.s.reserves[0].moveMove(1, self.s.waste)
self.isFinalizedHand = False
self.saveStateMove(1 | 16)
@ -456,6 +476,9 @@ class CribbagePatience(CribbageShuffle):
self.s.talon.dealRow(rows=self.s.rows[4:10])
self.stopSamples()
def dealAdditionalHand(self):
return
def isBoardFull(self):
for i in range(8):
if len(self.s.rows[i].cards) == 0:
@ -474,7 +497,7 @@ class CribbagePatience(CribbageShuffle):
self.s.talon.flipMove()
self.s.talon.moveMove(1, self.s.reserves[0])
self.leaveState(old_state)
for i in range(2):
for i in range(len(self.cribbage_hands)):
value = self.getHandScore(self.cribbage_hands[i])
self.texts.list[i].config(text=str(value))
self.score += value
@ -485,12 +508,12 @@ class CribbagePatience(CribbageShuffle):
if self.preview > 1:
return
if self.isBoardFull():
for i in range(2):
for i in range(len(self.cribbage_hands)):
value = self.getHandScore(self.cribbage_hands[i])
self.texts.list[i].config(text=str(value))
else:
for i in range(2):
for i in range(len(self.cribbage_hands)):
self.texts.list[i].config(text="")
#
t = ""
@ -499,6 +522,10 @@ class CribbagePatience(CribbageShuffle):
t += _("Total: %d") % self.score
self.texts.score.config(text=t)
def _autoDeal(self, sound=True):
# don't deal a card to the waste if the waste is empty
return 0
def getUpcardStack(self):
return self.s.reserves[0]
@ -526,6 +553,66 @@ class CribbagePatience(CribbageShuffle):
return [self.score, self.isFinalizedHand]
class CribbagePatienceII(CribbagePatience):
WIN_SCORE = 80
HANDS = 2
def startGame(self):
self.score = 0
self.isFinalizedHand = False
self.dealHand()
def fillStack(self, stack):
if not stack.cards:
old_state = self.enterState(self.S_FILL)
hand = self.s.rows[4:10] + self.s.rows[10:16]
if stack in hand:
i = list(hand).index(stack)
if i < len(hand)-1:
from_stack = hand[i+1]
pile = from_stack.getPile()
if pile:
from_stack.moveMove(len(pile), stack)
self.leaveState(old_state)
def dealHand(self):
self.startDealSample()
if self.isFinalizedHand:
for r in reversed(self.s.rows):
if (len(r.cards) > 0):
r.moveMove(1, self.s.waste)
self.s.reserves[0].moveMove(1, self.s.waste)
self.saveStateMove(2 | 16)
self.isFinalizedHand = False
self.saveStateMove(1 | 16)
self.s.talon.dealRow(rows=self.s.rows[4:10])
self.stopSamples()
def dealAdditionalHand(self):
for i in range(2):
if len(self.s.rows[i].cards) == 0:
return
for i in range(2, 4):
if len(self.s.rows[i].cards) > 0:
return
if len(self.s.rows[10].cards) == 0:
self.startDealSample()
self.saveStateMove(2 | 16)
self.s.talon.dealRow(rows=self.s.rows[10:16])
self.saveStateMove(1 | 16)
self.stopSamples()
def isBoardFull(self):
for i in range(8):
if len(self.s.rows[i].cards) == 0:
return False
for i in range(10, 14):
if len(self.s.rows[i].cards) == 0:
return False
return True
# register the game
registerGame(GameInfo(805, CribbageSquare, "Cribbage Square",
GI.GT_CRIBBAGE_TYPE | GI.GT_SCORE, 1, 0,
@ -551,3 +638,6 @@ registerGame(GameInfo(809, CribbageShuffle, "Cribbage Shuffle",
registerGame(GameInfo(955, CribbagePatience, "Cribbage Patience",
GI.GT_CRIBBAGE_TYPE | GI.GT_SCORE, 1, 0,
GI.SL_MOSTLY_SKILL))
registerGame(GameInfo(980, CribbagePatienceII, "Cribbage Patience II",
GI.GT_CRIBBAGE_TYPE | GI.GT_SCORE, 1, 0,
GI.SL_MOSTLY_SKILL))

View file

@ -1266,10 +1266,12 @@ class Dashavatara(Game):
# *
# ***********************************************************************/
def r(id, gameclass, name, game_type, decks, redeals, skill_level):
def r(id, gameclass, name, game_type, decks, redeals, skill_level,
altnames=()):
game_type = game_type | GI.GT_DASHAVATARA_GANJIFA
gi = GameInfo(id, gameclass, name, game_type, decks, redeals, skill_level,
suits=list(range(10)), ranks=list(range(12)))
suits=list(range(10)), ranks=list(range(12)),
altnames=altnames)
registerGame(gi)
return gi

View file

@ -62,7 +62,8 @@ class HexATrump_Foundation(HexADeck_FoundationStack):
def acceptsCards(self, from_stack, cards):
if not self.basicAcceptsCards(from_stack, cards):
return 0
for s in self.game.s.foundations[:3]:
for s in self.game.s.foundations[:((self.game.gameinfo.decks
* 4) - 1)]:
if len(s.cards) != 16:
return 0
return 1
@ -93,11 +94,12 @@ class Merlins_Foundation(AbstractFoundationStack):
class HexADeck_OpenStack(OpenStack):
def __init__(self, x, y, game, yoffset, **cap):
def __init__(self, x, y, game, yoffset=None, **cap):
kwdefault(cap, max_move=UNLIMITED_MOVES, max_accept=UNLIMITED_ACCEPTS,
dir=-1)
OpenStack.__init__(self, x, y, game, **cap)
self.CARD_YOFFSET = yoffset
if yoffset is not None:
self.CARD_YOFFSET = yoffset
def isRankSequence(self, cards, dir=None):
if not dir:
@ -1498,7 +1500,6 @@ class HexYukon(Game):
shallHighlightMatch = Game._shallHighlightMatch_AC
# ************************************************************************
# *
# ************************************************************************
@ -1600,16 +1601,162 @@ class MagicMontana(Montana):
return True
# ************************************************************************
# * Wizard's Storeroom
# ************************************************************************
class WizardsStoreroom(AbstractHexADeckGame):
MAX_ROUNDS = 2
#
# Game layout
#
def createGame(self):
l, s = Layout(self), self.s
# Set window size
decks = self.gameinfo.decks
self.setSize(2*l.XM + (2 + 5*decks)*l.XS, 3*l.YM + 5*l.YS)
yoffset = min(l.YOFFSET, max(10, l.YOFFSET // 2))
# Create talon
x = l.XM
y = l.YM
s.talon = WasteTalonStack(
x, y, self, num_deal=1, max_rounds=self.MAX_ROUNDS)
l.createText(s.talon, "s")
x = x + l.XS
s.waste = WasteStack(x, y, self)
l.createText(s.waste, "s")
# Create foundations
x = x + l.XM + l.XS
for j in range(4):
for i in range(decks):
s.foundations.append(
SS_FoundationStack(x, y, self, j, max_cards=16))
x = x + l.XS
for i in range(decks):
s.foundations.append(
HexATrump_Foundation(x, y, self, 4, max_cards=4))
x = x + l.XS
# Create reserve
x = l.XM
y = l.YM + l.YS + l.TEXT_HEIGHT
s.reserves.append(OpenStack(x, y, self))
s.reserves[0].CARD_YOFFSET = (l.YOFFSET, yoffset)[decks == 2]
# Create rows
x = x + l.XM + l.XS
for i in range(4*decks+1):
s.rows.append(HexAKlon_RowStack(x, y, self))
x = x + l.XS
self.setRegion(s.rows, (-999, y - l.YS, 999999, 999999))
# Define stack groups
l.defaultStackGroups()
#
# Game over rides
#
def startGame(self):
decks = self.gameinfo.decks
self.startDealSample()
for i in range(14 * decks):
self.s.talon.dealRow(rows=self.s.reserves, flip=0, frames=4)
self.s.reserves[0].flipMove()
self.s.talon.dealRow(rows=self.s.rows)
self.s.talon.dealCards() # deal first card to WasteStack
def fillStack(self, stack):
r = self.s.reserves[0]
if not stack.cards and stack in self.s.rows:
if r.cards and stack.acceptsCards(r, r.cards[-1:]):
r.moveMove(1, stack)
if r.canFlipCard():
r.flipMove()
def shallHighlightMatch(self, stack1, card1, stack2, card2):
return ((card1.rank + 1 == card2.rank or
card1.rank - 1 == card2.rank) and
card1.color != card2.color)
# ************************************************************************
# * Wizard's Castle
# ************************************************************************
class WizardsCastle(AbstractHexADeckGame):
Hint_Class = CautiousDefaultHint
#
# Game layout
#
def createGame(self):
l, s = Layout(self), self.s
# Set window size
h = max(5 * l.YS, 20 * l.YOFFSET)
self.setSize(l.XM + 9 * l.XS, l.YM + l.YS + h)
# Create foundations
x = self.width - l.XS
y = l.YM
s.foundations.append(SS_FoundationStack(x, y, self, 4, max_cards=22))
y = y + l.YS
for i in range(4):
s.foundations.append(
SS_FoundationStack(x, y, self, i, max_cards=14))
y = y + l.YS
# Create rows
x = l.XM
y = l.YM
for j in range(2):
for i in range(8):
s.rows.append(
HexAKlon_RowStack(x, y, self, max_move=1, max_accept=1))
x = x + l.XS
x = l.XM
y = y + l.YS * 3
self.setRegion(s.rows, (-999, -999, l.XM + l.XS * 8, 999999))
# Create talon
s.talon = InitialDealTalonStack(l.XM, self.height-l.YS, self)
# Define stack groups
l.defaultStackGroups()
#
# Game over rides
#
def startGame(self):
for i in range(2):
self.s.talon.dealRow(flip=0, frames=0)
self.s.talon.dealRow(flip=0, frames=0)
self.s.talon.dealRow(rows=self.s.rows[:4], flip=0, frames=0)
self._startAndDealRow()
def shallHighlightMatch(self, stack1, card1, stack2, card2):
return (card1.suit == card2.suit and
(card1.rank + 1 == card2.rank or card2.rank + 1 == card1.rank))
# ************************************************************************
# *
# ************************************************************************
def r(id, gameclass, name, game_type, decks, redeals, skill_level):
def r(id, gameclass, name, game_type, decks, redeals, skill_level,
altnames=()):
game_type = game_type | GI.GT_HEXADECK
gi = GameInfo(id, gameclass, name, game_type, decks, redeals, skill_level,
suits=list(range(4)), ranks=list(range(16)),
trumps=list(range(4)))
trumps=list(range(4)), altnames=altnames)
registerGame(gi)
return gi
@ -1633,7 +1780,7 @@ r(16674, HiddenPassages, 'Hidden Passages', GI.GT_HEXADECK, 1, 1,
r(16675, CluitjarsLair, 'Cluitjar\'s Lair', GI.GT_HEXADECK, 1, 0,
GI.SL_BALANCED)
r(16676, MerlinsMeander, 'Merlin\'s Meander', GI.GT_HEXADECK, 2, 2,
GI.SL_BALANCED)
GI.SL_BALANCED, altnames=('Merlin\'s Coil'))
r(16677, MagesGame, 'Mage\'s Game', GI.GT_HEXADECK | GI.GT_OPEN, 1, 0,
GI.SL_BALANCED)
r(16678, Convolution, 'Convolution', GI.GT_HEXADECK | GI.GT_OPEN, 2, 0,
@ -1645,4 +1792,10 @@ r(16680, Snakestone, 'Snakestone', GI.GT_HEXADECK | GI.GT_OPEN, 2, 0,
r(16681, HexYukon, 'Hex Yukon', GI.GT_HEXADECK, 1, 0, GI.SL_BALANCED)
r(16682, MagicMontana, 'Magic Montana', GI.GT_HEXADECK | GI.GT_OPEN, 1, 2,
GI.SL_MOSTLY_SKILL)
r(16683, WizardsStoreroom, "Wizard's Storeroom", GI.GT_HEXADECK, 1, 1,
GI.SL_MOSTLY_SKILL)
r(16684, WizardsStoreroom, "Big Storeroom", GI.GT_HEXADECK, 2, 1,
GI.SL_MOSTLY_SKILL)
r(16685, WizardsCastle, "Wizard's Castle", GI.GT_HEXADECK, 1, 0,
GI.SL_BALANCED)
del r

View file

@ -99,6 +99,9 @@ class Ishido(Game):
STRICT_FOUR_WAYS = True
SCORING = False
COLS = 12
ROWS = 8
#
# game layout
#
@ -115,13 +118,13 @@ class Ishido(Game):
w2 = max(2 * l.XS, x)
# set window
w, h = w2 + l.XM * 2 + l.CW * 12, l.YM * 2 + l.CH * 8
w, h = w2 + l.XM * 2 + l.CW * self.COLS, l.YM * 2 + l.CH * self.ROWS
self.setSize(w, h)
# Create rows
for j in range(8):
for j in range(self.ROWS):
x, y = w2 + l.XM, l.YM + l.CH * j
for i in range(12):
for i in range(self.COLS):
s.rows.append(self.RowStack_Class(x, y, self))
x = x + l.CW
@ -269,17 +272,17 @@ class Ishido(Game):
def getAdjacent(self, playSpace):
adjacentRows = []
if playSpace % 12 != 11:
if playSpace % self.COLS != self.COLS - 1:
adjacentRows.append(self.s.rows[playSpace + 1])
if playSpace % 12 != 0:
if playSpace % self.COLS != 0:
adjacentRows.append(self.s.rows[playSpace - 1])
if playSpace + 12 < 96:
adjacentRows.append(self.s.rows[playSpace + 12])
if playSpace + self.COLS < (self.COLS * self.ROWS):
adjacentRows.append(self.s.rows[playSpace + self.COLS])
if playSpace - 12 > -1:
adjacentRows.append(self.s.rows[playSpace - 12])
if playSpace - self.COLS > -1:
adjacentRows.append(self.s.rows[playSpace - self.COLS])
return adjacentRows
@ -301,10 +304,46 @@ class IshidoScored(Ishido):
SCORING = True
class LittleIshido(Ishido):
ROWS = 6
COLS = 8
def startGame(self):
self.score = 0
self.fourways = 0
self.moveMove(1, self.s.talon, self.s.rows[0], frames=0)
self.s.rows[0].flipMove()
self.moveMove(1, self.s.talon, self.s.rows[7], frames=0)
self.s.rows[7].flipMove()
self.moveMove(1, self.s.talon, self.s.rows[40], frames=0)
self.s.rows[40].flipMove()
self.moveMove(1, self.s.talon, self.s.rows[47], frames=0)
self.s.rows[47].flipMove()
self.s.talon.fillStack()
def _shuffleHook(self, cards):
# prepare first cards
symbols = []
colors = []
topcards = []
for c in cards[:]:
if c.suit not in colors and c.rank not in symbols:
topcards.append(c)
cards.remove(c)
symbols.append(c.rank)
colors.append(c.suit)
if len(colors) >= 4 or len(symbols) >= 4:
break
return cards + topcards
class LittleIshidoRelaxed(LittleIshido):
STRICT_FOUR_WAYS = False
def r(id, gameclass, name, decks, redeals, skill_level,
game_type=GI.GT_ISHIDO):
game_type=GI.GT_ISHIDO, colors=6):
gi = GameInfo(id, gameclass, name, game_type, decks, redeals, skill_level,
ranks=list(range(6)), suits=list(range(6)),
ranks=list(range(colors)), suits=list(range(colors)),
category=GI.GC_ISHIDO)
registerGame(gi)
return gi
@ -316,3 +355,6 @@ r(18002, FreeIshido, 'Free Ishido', 2, 0, GI.SL_MOSTLY_SKILL)
r(18003, FreeIshidoRelaxed, 'Free Ishido Relaxed', 2, 0, GI.SL_MOSTLY_SKILL)
r(18004, IshidoScored, 'Ishido Scored', 2, 0, GI.SL_MOSTLY_SKILL,
game_type=GI.GT_ISHIDO | GI.GT_SCORE)
r(18005, LittleIshido, 'Little Ishido', 2, 0, GI.SL_MOSTLY_SKILL, colors=4)
r(18006, LittleIshidoRelaxed, 'Little Ishido Relaxed', 2, 0,
GI.SL_MOSTLY_SKILL, colors=4)

View file

@ -222,6 +222,7 @@ def r(id, short_name, width):
return gi
r(22399, "Lights Out 3x3", 3)
r(22400, "Lights Out 4x4", 4)
r(22401, "Lights Out 5x5", 5)
r(22402, "Lights Out 6x6", 6)

View file

@ -71,7 +71,7 @@ class Memory_RowStack(OpenStack):
def _dropPairMove(self, n, other_stack, frames=-1, shadow=-1):
game = self.game
game.playSample("droppair", priority=200)
game.closed_cards = game.closed_cards - 2
game.closed_cards -= 2
game.score = game.score + 5
rightclickHandler = clickHandler
@ -108,6 +108,7 @@ class Memory24(Game):
# game extras
self.other_stack = None
self.other_stack2 = None
self.closed_cards = -1
self.score = 0
@ -255,7 +256,7 @@ class Concentration_RowStack(Memory_RowStack):
def _dropPairMove(self, n, other_stack, frames=-1, shadow=-1):
game = self.game
game.playSample("droppair", priority=200)
game.closed_cards = game.closed_cards - 2
game.closed_cards -= 2
game.score = game.score + 5
#
old_state = game.enterState(game.S_FILL)
@ -271,6 +272,8 @@ class Concentration(Memory24):
WIN_SCORE = 50
PERFECT_SCORE = 130 # 5 * (13*4)/2
RowStack_Class = Concentration_RowStack
#
# game layout
#
@ -281,6 +284,7 @@ class Concentration(Memory24):
# game extras
self.other_stack = None
self.other_stack2 = None
self.closed_cards = -1
self.score = 0
@ -291,11 +295,12 @@ class Concentration(Memory24):
for i in range(self.ROWS):
for j in range(self.COLUMNS):
x, y = l.XM + j*l.XS, l.YM + i*l.YS
s.rows.append(Concentration_RowStack(x, y, self,
s.rows.append(self.RowStack_Class(x, y, self,
max_move=0, max_accept=0, max_cards=1))
x, y = l.XM + self.COLUMNS*l.XS//2, self.height - l.YS
s.talon = InitialDealTalonStack(x, y, self)
l.createText(s.talon, dx=-10, anchor="sw", text_format="%D")
s.internals.append(InvisibleStack(self))
# create text
x, y = l.XM, self.height - l.YM
@ -390,14 +395,159 @@ class MemorySequence(Memory24):
return card1.suit == card2.suit and card1.rank == card2.rank + 1
# ************************************************************************
# * Families
# ************************************************************************
class Families_RowStack(Memory_RowStack):
def clickHandler(self, event):
game = self.game
if (game.score == -1):
return 1
if len(self.cards) != 1 or self.cards[-1].face_up:
return 1
if game.other_stack is None:
game.playSample("flip", priority=5)
self.flipMove()
game.other_stack = self
elif game.other_stack2 is None:
game.playSample("flip", priority=5)
self.flipMove()
game.other_stack2 = self
else:
assert len(game.other_stack.cards) == 1 and \
game.other_stack.cards[-1].face_up
c1, c2, c3 = self, game.other_stack, game.other_stack2
self.flipMove()
if not self.game.handleMatch(c1, c2, c3):
game.playSample("flip", priority=5)
game.updateStatus(moves=game.moves.index+1) # update moves now
game.updateText()
game.canvas.update_idletasks()
game.sleep(0.5)
game.other_stack.flipMove()
game.canvas.update_idletasks()
game.sleep(0.2)
game.other_stack2.flipMove()
game.canvas.update_idletasks()
game.sleep(0.2)
self.flipMove()
game.other_stack = None
game.other_stack2 = None
self.game.finishMove()
self.game.checkForWin()
return 1
class Families(Concentration):
Hint_Class = None
COLUMNS = 8
ROWS = 4
RowStack_Class = Families_RowStack
def updateText(self):
pass
def _restoreGameHook(self, game):
if game.loadinfo.other_stack_id >= 0:
self.other_stack = self.allstacks[game.loadinfo.other_stack_id]
else:
self.other_stack = None
if game.loadinfo.other_stack2_id >= 0:
self.other_stack2 = self.allstacks[game.loadinfo.other_stack2_id]
else:
self.other_stack2 = None
self.closed_cards = game.loadinfo.closed_cards
self.score = game.loadinfo.score
def handleMatch(self, stack1, stack2, stack3):
card1 = stack1.cards[-1]
card2 = stack2.cards[-1]
card3 = stack3.cards[-1]
if (card1.suit == card2.suit and card2.suit == card3.suit and
card1.rank != card2.rank and card2.rank != card3.rank and
card1.rank != card3.rank):
self.playSample("droppair", priority=200)
self.closed_cards -= 3
#
old_state = self.enterState(self.S_FILL)
f = self.s.talon
self.moveMove(1, stack1, f)
self.moveMove(1, stack2, f)
self.moveMove(1, stack3, f)
self.leaveState(old_state)
return True
else:
redjokers = 0
blackjokers = 0
if card1.suit == 4 and card1.rank == 0:
blackjokers += 1
if card2.suit == 4 and card2.rank == 0:
blackjokers += 1
if card3.suit == 4 and card3.rank == 0:
blackjokers += 1
if card1.suit == 4 and card1.rank == 1:
redjokers += 1
if card2.suit == 4 and card2.rank == 1:
redjokers += 1
if card3.suit == 4 and card3.rank == 1:
redjokers += 1
if blackjokers > 1:
self.score = -1
return True
if redjokers > 0:
self.reshuffle()
def reshuffle(self):
old_state = self.enterState(self.S_FILL)
stacks = ()
for r in self.s.rows:
if r.cards and not r.cards[-1].face_up:
stacks += (r,)
self.moveMove(len(r.cards), r, self.s.internals[0],
frames=0)
self.shuffleStackMove(self.s.internals[0])
self.startDealSample()
for r in stacks:
self.moveMove(1, self.s.internals[0], r)
self.stopSamples()
self.leaveState(old_state)
def isGameWon(self):
return self.closed_cards == 8 and self.score > -1
def getStuck(self):
return self.score == -1
def _loadGameHook(self, p):
self.loadinfo.addattr(other_stack_id=p.load())
self.loadinfo.addattr(other_stack2_id=p.load())
self.loadinfo.addattr(closed_cards=p.load())
self.loadinfo.addattr(score=p.load())
def _saveGameHook(self, p):
if self.other_stack:
p.dump(self.other_stack.id)
else:
p.dump(-1)
if self.other_stack2:
p.dump(self.other_stack2.id)
else:
p.dump(-1)
p.dump(self.closed_cards)
p.dump(self.score)
# register the game
registerGame(GameInfo(886, Memory16, "Memory 16",
GI.GT_MEMORY | GI.GT_SCORE, 2, 0, GI.SL_SKILL,
category=GI.GC_MATCHING,
GI.GT_MEMORY | GI.GT_SCORE | GI.GT_CHILDREN, 2, 0,
GI.SL_SKILL, category=GI.GC_MATCHING,
suits=(), ranks=(), trumps=list(range(8))))
registerGame(GameInfo(176, Memory24, "Memory 24",
GI.GT_MEMORY | GI.GT_SCORE, 2, 0, GI.SL_SKILL,
category=GI.GC_MATCHING,
GI.GT_MEMORY | GI.GT_SCORE | GI.GT_CHILDREN, 2, 0,
GI.SL_SKILL, category=GI.GC_MATCHING,
suits=(), ranks=(), trumps=list(range(12))))
registerGame(GameInfo(219, Memory30, "Memory 30",
GI.GT_MEMORY | GI.GT_SCORE, 2, 0, GI.SL_SKILL,
@ -417,3 +567,7 @@ registerGame(GameInfo(178, Concentration, "Concentration",
registerGame(GameInfo(843, MemorySequence, "Memory Sequence",
GI.GT_MEMORY | GI.GT_SCORE, 1, 0, GI.SL_SKILL,
suits=(1,), altnames=('Ace Through King',)))
registerGame(GameInfo(973, Families, "Families",
GI.GT_MEMORY, 2, 0, GI.SL_MOSTLY_SKILL,
ranks=(10, 11, 12), subcategory=GI.GS_JOKER_DECK,
trumps=(0, 0, 0, 1)))

View file

@ -1157,10 +1157,12 @@ class AshtaDikapala(Game):
# *
# ************************************************************************
def r(id, gameclass, name, game_type, decks, redeals, skill_level):
def r(id, gameclass, name, game_type, decks, redeals, skill_level,
altnames=()):
game_type = game_type | GI.GT_MUGHAL_GANJIFA
gi = GameInfo(id, gameclass, name, game_type, decks, redeals, skill_level,
suits=list(range(8)), ranks=list(range(12)))
suits=list(range(8)), ranks=list(range(12)),
altnames=altnames)
registerGame(gi)
return gi
@ -1187,6 +1189,6 @@ r(16001, Danda, 'Danda', GI.GT_MUGHAL_GANJIFA, 1, 0, GI.SL_MOSTLY_SKILL)
r(16002, Khadga, 'Khadga', GI.GT_MUGHAL_GANJIFA, 1, 0, GI.SL_MOSTLY_SKILL)
r(16003, Makara, 'Makara', GI.GT_MUGHAL_GANJIFA, 1, 0, GI.SL_MOSTLY_SKILL)
r(16004, AshtaDikapala, 'Ashta Dikapala', GI.GT_MUGHAL_GANJIFA, 1, 0,
GI.SL_BALANCED)
GI.SL_BALANCED, altnames=('Eight Guardians'))
del r

View file

@ -501,11 +501,11 @@ class TrumpsRow(Montana):
# ************************************************************************
def r(id, gameclass, name, game_type, decks, redeals, skill_level,
numcards=78):
numcards=78, altnames=()):
game_type = game_type | GI.GT_TAROCK | GI.GT_CONTRIB | GI.GT_ORIGINAL
gi = GameInfo(id, gameclass, name, game_type, decks, redeals, skill_level,
ranks=list(range(14)), trumps=list(range(22)),
si={"ncards": numcards})
altnames=altnames, si={"ncards": numcards})
registerGame(gi)
return gi
@ -519,7 +519,8 @@ r(13166, Serpent, 'Serpent', GI.GT_TAROCK | GI.GT_OPEN, 2, 0,
GI.SL_MOSTLY_SKILL)
r(13167, Rambling, 'Rambling', GI.GT_TAROCK | GI.GT_OPEN, 2, 0,
GI.SL_MOSTLY_SKILL)
r(13168, FoolsUp, "Fool's Up", GI.GT_TAROCK, 1, 0, GI.SL_LUCK)
r(13168, FoolsUp, "Fool's Up", GI.GT_TAROCK, 1, 0, GI.SL_LUCK,
altnames=('Solitairot'))
r(13169, TrumpsRow, "Trumps Row", GI.GT_TAROCK, 1, 4, GI.SL_MOSTLY_SKILL,
numcards=73)
r(22232, LeGrandeTeton, 'Le Grande Teton', GI.GT_TAROCK, 1, 0, GI.SL_BALANCED)

View file

@ -348,15 +348,16 @@ class Scorpion_RowStack(Yukon_SS_RowStack, Spider_RowStack):
class Scorpion(RelaxedSpider):
Hint_Class = YukonType_Hint
RowStack_Class = StackWrapper(Scorpion_RowStack, base_rank=KING)
FACEDOWNS = (4, 4, 4, 0, 0, 0)
def createGame(self):
RelaxedSpider.createGame(self, rows=7, playcards=20)
def startGame(self):
for i in (4, 4, 4, 0, 0, 0):
for i in self.FACEDOWNS:
self.s.talon.dealRow(rows=self.s.rows[:i], flip=0, frames=0)
self.s.talon.dealRow(rows=self.s.rows[i:], flip=1, frames=0)
self._startAndDealRow()
@ -379,46 +380,46 @@ class ScorpionTail(Scorpion):
shallHighlightMatch = Game._shallHighlightMatch_AC
# ************************************************************************
# * Double Scorpion
# * Triple Scorpion
# ************************************************************************
class DoubleScorpion(Scorpion):
Talon_Class = InitialDealTalonStack
FACEDOWNS = (5, 5, 5, 5, 0, 0, 0, 0, 0)
def createGame(self):
RelaxedSpider.createGame(self, rows=10, playcards=26, texts=0)
def startGame(self):
for i in (5, 5, 5, 5, 0, 0, 0, 0, 0):
self.s.talon.dealRow(rows=self.s.rows[:i], flip=0, frames=0)
self.s.talon.dealRow(rows=self.s.rows[i:], flip=1, frames=0)
self.startDealSample()
self.s.talon.dealRow()
self.s.talon.dealRowAvail()
class TripleScorpion(Scorpion):
Talon_Class = InitialDealTalonStack
FACEDOWNS = (5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0)
def createGame(self):
RelaxedSpider.createGame(self, rows=13, playcards=30, texts=0)
def startGame(self):
for i in (5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0):
self.s.talon.dealRow(rows=self.s.rows[:i], flip=0, frames=0)
self.s.talon.dealRow(rows=self.s.rows[i:], flip=1, frames=0)
self._startAndDealRow()
# ************************************************************************
# * Scorpion II
# ************************************************************************
class ScorpionII(Scorpion):
FACEDOWNS = (3, 3, 3, 0, 0, 0)
# ************************************************************************
# * Wasp
# * Wasp II
# ************************************************************************
class Wasp(Scorpion):
RowStack_Class = Scorpion_RowStack # anything on an empty space
def startGame(self):
for i in (3, 3, 3, 0, 0, 0):
self.s.talon.dealRow(rows=self.s.rows[:i], flip=0, frames=0)
self.s.talon.dealRow(rows=self.s.rows[i:], flip=1, frames=0)
self._startAndDealRow()
class WaspII(ScorpionII):
RowStack_Class = Scorpion_RowStack
# ************************************************************************
@ -1124,19 +1125,6 @@ class Incompatibility(Spidike):
self._startDealNumRowsAndDealSingleRow(4)
# ************************************************************************
# * Scorpion II
# ************************************************************************
class ScorpionII(Scorpion):
def startGame(self):
for i in (3, 3, 3, 0, 0, 0):
self.s.talon.dealRow(rows=self.s.rows[:i], flip=0, frames=0)
self.s.talon.dealRow(rows=self.s.rows[i:], flip=1, frames=0)
self._startAndDealRow()
# ************************************************************************
# * Tarantula
# ************************************************************************
@ -1158,6 +1146,7 @@ class Tarantula(Spider):
# ************************************************************************
# * Fechter's Game
# * Microbe
# ************************************************************************
class FechtersGame_Talon(TalonStack):
@ -1180,14 +1169,7 @@ class FechtersGame_Talon(TalonStack):
class FechtersGame_RowStack(AC_RowStack):
def canDropCards(self, stacks):
if len(self.cards) < 13:
return (None, 0)
cards = self.cards[-13:]
for s in stacks:
if s is not self and s.acceptsCards(self, cards):
return (s, 13)
return (None, 0)
canDropCards = BasicRowStack.spiderCanDropCards
class FechtersGame(RelaxedSpider):
@ -1208,6 +1190,17 @@ class FechtersGame(RelaxedSpider):
shallHighlightMatch = Game._shallHighlightMatch_AC
class Microbe(FechtersGame):
Talon_Class = DealRowTalonStack
RowStack_Class = StackWrapper(FechtersGame_RowStack, base_rank=ANY_RANK)
def createGame(self):
RelaxedSpider.createGame(self, rows=11)
def shuffle(self):
self.shuffleSeparateDecks()
# ************************************************************************
# * Bebop
# ************************************************************************
@ -1663,3 +1656,8 @@ registerGame(GameInfo(870, FairMaids, "Fair Maids",
GI.GT_SPIDER, 1, 0, GI.SL_BALANCED))
registerGame(GameInfo(917, Astrocyte, "Astrocyte",
GI.GT_SPIDER, 2, 0, GI.SL_MOSTLY_SKILL))
registerGame(GameInfo(971, Microbe, "Microbe",
GI.GT_SPIDER | GI.GT_SEPARATE_DECKS, 2, 0,
GI.SL_MOSTLY_SKILL))
registerGame(GameInfo(976, WaspII, "Wasp II",
GI.GT_SPIDER, 1, 0, GI.SL_MOSTLY_SKILL))

View file

@ -51,10 +51,18 @@ class StHelena_Talon(TalonStack):
# move all cards to the Talon and redeal
lr = len(self.game.s.rows)
num_cards = 0
assert len(self.cards) == 0
if len(self.cards) > 0:
num_cards = len(self.cards)
self.game.startDealSample()
for i in range(lr):
k = min(lr, len(self.cards))
for j in range(k):
self.game.flipAndMoveMove(self, self.game.s.rows[j], 4)
self.game.stopSamples()
return num_cards
for r in self.game.s.rows[::-1]:
for i in range(len(r.cards)):
num_cards = num_cards + 1
num_cards += 1
self.game.moveMove(1, r, self, frames=0)
assert len(self.cards) == num_cards
if num_cards == 0: # game already finished
@ -169,6 +177,32 @@ class BoxKite(StHelena):
shallHighlightMatch = Game._shallHighlightMatch_RKW
# ************************************************************************
# * Louis
# ************************************************************************
class Louis(StHelena):
Foundation_Class = SS_FoundationStack
RowStack_Class = StackWrapper(UD_SS_RowStack, base_rank=NO_RANK, mod=13)
shallHighlightMatch = Game._shallHighlightMatch_RKW
def startGame(self):
self.startDealSample()
self.s.talon.dealRow(self.s.foundations)
self.s.talon.dealRow()
def _shuffleHook(self, cards):
return self._shuffleHookMoveToTop(
cards, lambda c: (c.deck == 0 and c.rank in (0, 12),
(-c.rank, c.suit)), 8)
def fillStack(self, stack):
if (self.s.talon.cards and stack in self.s.rows
and len(stack.cards) == 0):
self.s.talon.dealRow(rows=[stack])
# ************************************************************************
# * Les Quatre Coins
@ -452,3 +486,5 @@ registerGame(GameInfo(621, RegalFamily, "Regal Family",
registerGame(GameInfo(859, KingsAudience, "King's Audience",
GI.GT_1DECK_TYPE, 1, 0, GI.SL_MOSTLY_LUCK,
altnames=("Queen's Audience")))
registerGame(GameInfo(975, Louis, "Louis",
GI.GT_2DECK_TYPE, 2, 2, GI.SL_BALANCED))

View file

@ -472,6 +472,7 @@ class Panopticon(TenAcross):
# ************************************************************************
# * Australian Patience
# * Outback Patience
# * Tasmanian Patience
# * Canberra
# * Raw Prawn
@ -482,9 +483,9 @@ class AustralianPatience(RussianSolitaire):
RowStack_Class = StackWrapper(Yukon_SS_RowStack, base_rank=KING)
def createGame(self, rows=7, max_rounds=1, num_deal=1):
def createGame(self, rows=7, max_rounds=1, num_deal=1, playcards=16):
l, s = Layout(self), self.s
Layout.klondikeLayout(l, rows=rows, waste=1)
Layout.klondikeLayout(l, rows=rows, waste=1, playcards=playcards)
self.setSize(l.size[0], l.size[1])
s.talon = WasteTalonStack(l.s.talon.x, l.s.talon.y, self,
max_rounds=max_rounds, num_deal=num_deal)
@ -500,6 +501,14 @@ class AustralianPatience(RussianSolitaire):
self._startDealNumRowsAndDealRowAndCards(3)
class OutbackPatience(AustralianPatience):
def createGame(self):
AustralianPatience.createGame(self, rows=8, playcards=25)
def startGame(self):
self._startDealNumRowsAndDealRowAndCards(6)
class TasmanianPatience(AustralianPatience):
def createGame(self):
AustralianPatience.createGame(self, max_rounds=-1, num_deal=3)
@ -895,8 +904,7 @@ registerGame(GameInfo(387, Roslin, "Roslin",
GI.GT_YUKON, 1, 0, GI.SL_MOSTLY_SKILL,
altnames=("Roslyn",)))
registerGame(GameInfo(447, AustralianPatience, "Australian Patience",
GI.GT_YUKON, 1, 0, GI.SL_BALANCED,
altnames=('Outback Patience',)))
GI.GT_YUKON, 1, 0, GI.SL_BALANCED))
registerGame(GameInfo(450, RawPrawn, "Raw Prawn",
GI.GT_YUKON, 1, 0, GI.SL_BALANCED))
registerGame(GameInfo(456, BimBom, "Bim Bom",
@ -906,9 +914,12 @@ registerGame(GameInfo(466, DoubleRussianSolitaire, "Double Russian Solitaire",
registerGame(GameInfo(488, TripleRussianSolitaire, "Triple Russian Solitaire",
GI.GT_YUKON, 3, 0, GI.SL_BALANCED))
registerGame(GameInfo(492, Geoffrey, "Geoffrey",
GI.GT_YUKON, 1, 0, GI.SL_MOSTLY_SKILL))
GI.GT_YUKON, 1, 0, GI.SL_MOSTLY_SKILL,
altnames=('Thornq',)))
registerGame(GameInfo(525, Queensland, "Queensland",
GI.GT_YUKON, 1, 0, GI.SL_BALANCED))
registerGame(GameInfo(526, OutbackPatience, "Outback Patience",
GI.GT_YUKON, 2, 0, GI.SL_BALANCED))
registerGame(GameInfo(530, RussianSpider, "Russian Spider",
GI.GT_SPIDER, 1, 0, GI.SL_BALANCED,
altnames=('Ukrainian Solitaire',)))
@ -941,4 +952,5 @@ registerGame(GameInfo(942, QuadrupleYukon, "Quadruple Yukon",
registerGame(GameInfo(963, Sevastopol, "Sevastopol",
GI.GT_SPIDER, 1, 0, GI.SL_BALANCED))
registerGame(GameInfo(964, RussianCell, "Russian Cell",
GI.GT_YUKON, 1, 0, GI.SL_BALANCED))
GI.GT_YUKON, 1, 0, GI.SL_BALANCED,
altnames=('Finnish Solitaire',)))

View file

@ -175,6 +175,13 @@ def raise_help_html(game):
pass
def unraise_help_html():
try:
help_html_viewer.parent.attributes("-topmost", False)
except Exception:
pass
def destroy_help_html():
try:
help_html_viewer.destroy()

View file

@ -73,6 +73,10 @@ def raise_find_card_dialog(game):
pass
def unraise_find_card_dialog():
pass
def destroy_find_card_dialog():
pass
'''

View file

@ -112,6 +112,10 @@ def raise_full_picture_dialog(game):
pass
def unraise_full_picture_dialog():
pass
def destroy_full_picture_dialog():
pass
'''

View file

@ -571,7 +571,7 @@ class LOptionsMenuGenerator(LTreeGenerator):
LTreeNode(text=_('Automatic play')))
if rg:
self.addCheckNode(tv, rg,
_('Auto face up'),
_('Auto face-up'),
self.menubar.tkopt.autofaceup,
self.menubar.mOptAutoFaceUp)

View file

@ -50,6 +50,10 @@ def raise_solver_dialog(game):
pass
def unraise_solver_dialog():
pass
def destroy_solver_dialog():
global solver_dialog
solver_dialog = None

View file

@ -456,7 +456,7 @@ class AShuffleStackMove(AtomicMove):
def redo(self, game):
stack = game.allstacks[self.stack_id]
# paranoia
assert stack is game.s.talon
assert stack is game.s.talon or stack in game.s.internals
# shuffle (see random)
game.random.setstate(self.state)
seq = stack.cards

View file

@ -54,5 +54,9 @@ def raise_find_card_dialog(game):
pass
def unraise_find_card_dialog():
pass
def destroy_find_card_dialog():
pass

View file

@ -50,7 +50,11 @@ def connect_game_full_picture_dialog(game):
pass
def raise_find_card_dialog(game):
def raise_full_picture_dialog(game):
pass
def unraise_full_picture_dialog():
pass

View file

@ -259,7 +259,7 @@ class PysolMenubarTk:
]
for label, action, opt_name, update_game in (
('A&uto drop', 'optautodrop', 'autodrop', False),
('Auto &face up', '', 'autofaceup', False),
('Auto &face-up', '', 'autofaceup', False),
('Auto &deal', '', 'autodeal', False),
('&Quick play', '', 'quickplay', False),
('Enable &undo', '', 'undo', False),

View file

@ -34,6 +34,10 @@ def raise_solver_dialog(game):
pass
def unraise_solver_dialog():
pass
def destroy_solver_dialog():
pass
# solver_dialog = None

View file

@ -353,7 +353,6 @@ class SelectCardsetDialogWithPreview(MfxDialog):
check = ttk.Checkbutton(
size_frame, text=_('Auto scaling'),
variable=self.auto_scale,
takefocus=False,
command=self._updateAutoScale
)
check.grid(row=5, column=0, columnspan=2, sticky='ew',
@ -364,7 +363,6 @@ class SelectCardsetDialogWithPreview(MfxDialog):
self.aspect_check = ttk.Checkbutton(
size_frame, text=_('Preserve aspect ratio'),
variable=self.preserve_aspect,
takefocus=False,
# command=self._updateScale
)
self.aspect_check.grid(row=6, column=0, sticky='ew',
@ -664,20 +662,21 @@ class CardsetInfoDialog(MfxDialog):
row += 1
styles = nationalities = year = None
if cardset.si.styles:
styles = '\n'.join([CSI.STYLE[i] for i in cardset.si.styles])
styles = '\n'.join(sorted([CSI.STYLE[i]
for i in cardset.si.styles]))
if cardset.si.nationalities:
nationalities = '\n'.join([CSI.NATIONALITY[i]
for i in cardset.si.nationalities])
nationalities = '\n'.join(sorted([CSI.NATIONALITY[i]
for i in
cardset.si.nationalities]))
if cardset.year:
year = str(cardset.year)
frow = 0
for n, t in (
# ('Version:', str(cardset.version)),
(_('Type:'), CSI.TYPE[cardset.type]),
(_('Styles:'), styles),
(_('Nationality:'), nationalities),
(_('Year:'), year),
# (_('Number of cards:'), str(cardset.ncards)),
(_('Num. cards:'), str(cardset.ncards)),
(_('Size:'), '%d x %d' % (cardset.CARDW, cardset.CARDH)),
):
if t is not None:

View file

@ -95,3 +95,10 @@ def raise_solver_dialog(game):
solver_dialog.top.attributes("-topmost", False)
except Exception:
pass
def unraise_solver_dialog():
try:
solver_dialog.top.attributes("-topmost", False)
except Exception:
pass

View file

@ -109,7 +109,7 @@ class SoundOptionsDialog(MfxDialog):
ttk.Label(frame, text=_('Sample volume:'), anchor='w'
).grid(row=row, column=0, sticky='ew')
w = PysolScale(frame, from_=0, to=128, resolution=1,
orient='horizontal', takefocus=0,
orient='horizontal',
length="3i", # label=_('Sample volume'),
variable=self.sample_volume)
w.grid(row=row, column=1, sticky='w', padx=5)
@ -122,7 +122,7 @@ class SoundOptionsDialog(MfxDialog):
ttk.Label(frame, text=_('Music volume:'), anchor='w'
).grid(row=row, column=0, sticky='ew')
w = PysolScale(frame, from_=0, to=128, resolution=1,
orient='horizontal', takefocus=0,
orient='horizontal',
length="3i", # label=_('Music volume'),
variable=self.music_volume)
w.grid(row=row, column=1, sticky='w', padx=5)

View file

@ -72,7 +72,7 @@ class TimeoutsDialog(MfxDialog):
row=row, column=0, sticky='we')
widget = PysolScale(lframe, from_=0.2, to=9.9, value=var.get(),
resolution=0.1, orient='horizontal',
length="3i", variable=var, takefocus=0)
length="3i", variable=var)
widget.grid(row=row, column=1)
row += 1
#

View file

@ -377,7 +377,7 @@ class TreeFormatter(PysolStatsFormatter):
return 1
def writeLog(self, player, prev_games, sort_by='date'):
if not player or not prev_games:
if not prev_games:
return 0
num_rows = 0
results = self.getLogResults(player, prev_games)
@ -520,6 +520,8 @@ class LogDialog(MfxDialog):
kw = self.initKw(kw)
title = _('Log')
if player is None:
title = _('Demo Log')
MfxDialog.__init__(self, parent, title, kw.resizable, kw.default)
self.top.wm_minsize(400, 200)

View file

@ -105,8 +105,7 @@ class WizardDialog(MfxDialog):
elif w.widget == 'check':
if w.variable is None:
w.variable = tkinter.BooleanVar()
ch = ttk.Checkbutton(frame, variable=w.variable,
takefocus=False)
ch = ttk.Checkbutton(frame, variable=w.variable)
ch.grid(row=row, column=1, sticky='ew', padx=2, pady=2)
if w.current_value is None:

View file

@ -271,7 +271,6 @@ class SelectCardsetDialogWithPreview(MfxDialog):
check = tkinter.Checkbutton(
left_frame, text=_('Auto scaling'),
variable=self.auto_scale,
takefocus=False,
command=self._updateAutoScale
)
check.grid(row=3, column=0, columnspan=2, sticky='w',
@ -282,7 +281,6 @@ class SelectCardsetDialogWithPreview(MfxDialog):
self.aspect_check = tkinter.Checkbutton(
left_frame, text=_('Preserve aspect ratio'),
variable=self.preserve_aspect,
takefocus=False,
# command=self._updateScale
)
self.aspect_check.grid(row=4, column=0, sticky='w',
@ -438,20 +436,21 @@ class CardsetInfoDialog(MfxDialog):
padx=0, pady=5, ipadx=5, ipady=5)
styles = nationalities = year = None
if cardset.si.styles:
styles = '\n'.join([CSI.STYLE[i] for i in cardset.si.styles])
styles = '\n'.join(sorted([CSI.STYLE[i]
for i in cardset.si.styles]))
if cardset.si.nationalities:
nationalities = '\n'.join([CSI.NATIONALITY[i]
for i in cardset.si.nationalities])
nationalities = '\n'.join(sorted([CSI.NATIONALITY[i]
for i in
cardset.si.nationalities]))
if cardset.year:
year = str(cardset.year)
row = 0
for n, t in (
# ('Version:', str(cardset.version)),
(_('Type:'), CSI.TYPE[cardset.type]),
(_('Styles:'), styles),
(_('Nationality:'), nationalities),
(_('Year:'), year),
# (_('Number of cards:'), str(cardset.ncards)),
(_('Num. cards:'), str(cardset.ncards)),
(_('Size:'), '%d x %d' % (cardset.CARDW, cardset.CARDH)),
):
if t is not None:

View file

@ -109,7 +109,7 @@ class SoundOptionsDialog(MfxDialog):
w = tkinter.Label(frame, text=_('Sample volume:'))
w.grid(row=row, column=0, sticky='w', padx=5)
w = tkinter.Scale(frame, from_=0, to=128, resolution=1,
orient='horizontal', takefocus=0,
orient='horizontal',
length="3i", # label=_('Sample volume'),
variable=self.sample_volume)
w.grid(row=row, column=1, sticky='ew', padx=5)
@ -117,7 +117,7 @@ class SoundOptionsDialog(MfxDialog):
w = tkinter.Label(frame, text=_('Music volume:'))
w.grid(row=row, column=0, sticky='w', padx=5)
w = tkinter.Scale(frame, from_=0, to=128, resolution=1,
orient='horizontal', takefocus=0,
orient='horizontal',
length="3i", # label=_('Music volume'),
variable=self.music_volume)
w.grid(row=row, column=1, sticky='ew', padx=5)

View file

@ -71,7 +71,7 @@ class TimeoutsDialog(MfxDialog):
).grid(row=row, column=0, sticky='we')
widget = tkinter.Scale(frame, from_=0.2, to=9.9,
resolution=0.1, orient='horizontal',
length="3i", variable=var, takefocus=0)
length="3i", variable=var)
widget.grid(row=row, column=1)
row += 1
#

View file

@ -94,7 +94,7 @@ class WizardDialog(MfxDialog):
if w.variable is None:
w.variable = tkinter.BooleanVar()
ch = tkinter.Checkbutton(frame, variable=w.variable,
takefocus=False, anchor='w')
anchor='w')
ch.grid(row=row, column=1, sticky='ew', padx=2, pady=2)
if w.current_value is None:

Some files were not shown because too many files have changed in this diff Show more