0
diff --git a/data/tiles/Fade_Blue-ffffff.gif b/data/tiles/Fade_Blue.gif
similarity index 100%
rename from data/tiles/Fade_Blue-ffffff.gif
rename to data/tiles/Fade_Blue.gif
diff --git a/data/tiles/Gammi-ffffff.gif b/data/tiles/Gammi.gif
similarity index 100%
rename from data/tiles/Gammi-ffffff.gif
rename to data/tiles/Gammi.gif
diff --git a/data/tiles/Castle-stretch.gif b/data/tiles/stretch/Castle.gif
similarity index 100%
rename from data/tiles/Castle-stretch.gif
rename to data/tiles/stretch/Castle.gif
diff --git a/data/tiles/Sunset-stretch-ffffff.gif b/data/tiles/stretch/Sunset.gif
similarity index 100%
rename from data/tiles/Sunset-stretch-ffffff.gif
rename to data/tiles/stretch/Sunset.gif
diff --git a/html-src/index.html b/html-src/index.html
index 8c672b95..cfbcf6ce 100644
--- a/html-src/index.html
+++ b/html-src/index.html
@@ -1,5 +1,4 @@
PySol - a Solitaire Game Collection
-
Introduction
Installation
diff --git a/html-src/rules/akbarsconquest.html b/html-src/rules/akbarsconquest.html
index 88d9b846..524704d7 100644
--- a/html-src/rules/akbarsconquest.html
+++ b/html-src/rules/akbarsconquest.html
@@ -26,6 +26,3 @@ by court artisans. No cards from this pack are known to still exist.
Strategy
Build sequences on the rows that will play when the correct card turns
over from the talon. This game type requires careful strategy to win.
-
-
-General Ganjifa Rules
diff --git a/html-src/rules/khadga.html b/html-src/rules/khadga.html
index 9a20365e..c68daf69 100644
--- a/html-src/rules/khadga.html
+++ b/html-src/rules/khadga.html
@@ -15,5 +15,5 @@ and the number of cards you can move as a sequence is not restricted.
All cards are dealt to 9 piles at the start of the game, each Raja or King
starting a new pile. Rows build down in rank and alternate color.
-Refer to the general Ganjifa page.
+Refer to the general Ganjifa page.
Empty rows cannot be filled. The eight free cells will hold one card each.
diff --git a/html-src/rules/kings.html b/html-src/rules/kings.html
index 5ae710b7..4de03864 100644
--- a/html-src/rules/kings.html
+++ b/html-src/rules/kings.html
@@ -8,7 +8,7 @@ Move all cards to the foundations.
Quick Description
-Like FreeCell,
+Like Relaxed FreeCell,
but with 2 decks, and empty rows are not filled.
Rules
@@ -19,7 +19,3 @@ To compensate for this there are 8 free cells which can hold any
- and just one - card.
Piles build down by alternate color, and an empty space cannot be filled.
-
-The number of cards you can move as a sequence is restricted by
-the number of free cells - the number of free cells required is the
-same as if you would make an equivalent sequence of moves with single cards.
diff --git a/html-src/rules/newbritishconstitution.html b/html-src/rules/newbritishconstitution.html
new file mode 100644
index 00000000..63864cc3
--- /dev/null
+++ b/html-src/rules/newbritishconstitution.html
@@ -0,0 +1,16 @@
+
New British Constitution
+
+Two-Deck game type. 2 decks. No redeals.
+
+
Object
+
+Move all cards to the foundations.
+
+
Quick Description
+
+Like British Constitution,
+but only Jacks on empty spaces and the piles build down by rank ignoring suit.
+
+
Rules
+
+[To be written]
diff --git a/html-src/rules/retinue.html b/html-src/rules/retinue.html
new file mode 100644
index 00000000..b62ef70a
--- /dev/null
+++ b/html-src/rules/retinue.html
@@ -0,0 +1,25 @@
+
Retinue
+
+FreeCell type. 2 decks. No redeal.
+
+
Object
+
+Move all cards to the foundations.
+
+
Quick Description
+
+Like FreeCell,
+but with 2 decks, and empty rows are not filled.
+
+
Rules
+
+All cards are dealt to 8 piles at the start of the game, each King
+starting a new pile.
+To compensate for this there are 8 free cells which can hold any
+- and just one - card.
+
+Piles build down by alternate color, and an empty space cannot be filled.
+
+The number of cards you can move as a sequence is restricted by
+the number of free cells - the number of free cells required is the
+same as if you would make an equivalent sequence of moves with single cards.
diff --git a/html-src/wikipedia/sevendevils.html b/html-src/wikipedia/sevendevils.html
index 3bf70a92..0d03bac3 100644
--- a/html-src/wikipedia/sevendevils.html
+++ b/html-src/wikipedia/sevendevils.html
@@ -5,7 +5,7 @@ Seven Devils is arguably the most difficult of all solitaire games. It is a
two pack game widely available as a computer version.
28 cards are dealt out to seven diminishing columns with the bottom card of
-each column face up, and a further seven cards (the \u201cdevil\u201d) are
+each column face up, and a further seven cards (the "devil") are
dealt face up to the right of the columns.
The aim is to move all the cards into thirteen-card sequences on the goal
diff --git a/po/games.pot b/po/games.pot
index 61ca8371..b7fc323c 100644
--- a/po/games.pot
+++ b/po/games.pot
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PySol 0.0.1\n"
-"POT-Creation-Date: Fri Jan 12 13:34:09 2007\n"
+"POT-Creation-Date: Thu Feb 15 18:35:01 2007\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -216,6 +216,9 @@ msgstr ""
msgid "Baroness"
msgstr ""
+msgid "Barrier"
+msgstr ""
+
msgid "Bastille Day"
msgstr ""
@@ -243,6 +246,9 @@ msgstr ""
msgid "Beatle"
msgstr ""
+msgid "Bebop"
+msgstr ""
+
msgid "Beetle"
msgstr ""
@@ -3717,6 +3723,9 @@ msgstr ""
msgid "Wasp"
msgstr ""
+msgid "Waterfall"
+msgstr ""
+
msgid "Waterloo"
msgstr ""
diff --git a/po/pysol.pot b/po/pysol.pot
index c3254d92..2de9fe7a 100644
--- a/po/pysol.pot
+++ b/po/pysol.pot
@@ -14,7 +14,7 @@ msgid ""
msgstr ""
"#-#-#-#-# pysol-1.pot (PACKAGE VERSION) #-#-#-#-#\n"
"Project-Id-Version: PACKAGE VERSION\n"
-"POT-Creation-Date: Fri Jan 12 13:35:13 2007\n"
+"POT-Creation-Date: Thu Feb 15 18:36:07 2007\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -24,7 +24,7 @@ msgstr ""
"Generated-By: pygettext.py 1.5\n"
"#-#-#-#-# pysol-2.pot (PACKAGE VERSION) #-#-#-#-#\n"
"Project-Id-Version: PACKAGE VERSION\n"
-"POT-Creation-Date: 2007-01-12 13:35+0300\n"
+"POT-Creation-Date: 2007-02-15 18:36+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -32,84 +32,85 @@ msgstr ""
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
-#: pysollib/actions.py:257 pysollib/tile/toolbar.py:189
+#: pysollib/actions.py:259 pysollib/tile/toolbar.py:189
#: pysollib/tk/toolbar.py:192
msgid "New game"
msgstr ""
-#: pysollib/actions.py:270 pysollib/tile/menubar.py:822
-#: pysollib/tile/menubar.py:836 pysollib/tk/menubar.py:818
-#: pysollib/tk/menubar.py:832
+#: pysollib/actions.py:272 pysollib/tile/menubar.py:832
+#: pysollib/tile/menubar.py:846 pysollib/tk/menubar.py:828
+#: pysollib/tk/menubar.py:842
msgid "Select game"
msgstr ""
-#: pysollib/actions.py:284
+#: pysollib/actions.py:286
msgid "Invalid game number"
msgstr ""
-#: pysollib/actions.py:285
+#: pysollib/actions.py:287
msgid "Invalid game number\n"
msgstr ""
-#: pysollib/actions.py:302
+#: pysollib/actions.py:304
msgid "Select next game number"
msgstr ""
-#: pysollib/actions.py:311 pysollib/actions.py:321
+#: pysollib/actions.py:313 pysollib/actions.py:323
msgid "Select new game number"
msgstr ""
-#: pysollib/actions.py:312
+#: pysollib/actions.py:314
msgid ""
"\n"
"\n"
"Enter new game number"
msgstr ""
-#: pysollib/actions.py:313
+#: pysollib/actions.py:315
msgid "&Next number"
msgstr ""
-#: pysollib/actions.py:313 pysollib/app.py:886 pysollib/app.py:1169
-#: pysollib/app.py:1181 pysollib/game.py:950 pysollib/game.py:2048
-#: pysollib/tile/colorsdialog.py:123 pysollib/tile/edittextdialog.py:83
+#: pysollib/actions.py:315 pysollib/app.py:891 pysollib/app.py:1178
+#: pysollib/app.py:1190 pysollib/game.py:1010 pysollib/game.py:2245
+#: pysollib/tile/colorsdialog.py:112 pysollib/tile/edittextdialog.py:83
#: pysollib/tile/fontsdialog.py:144 pysollib/tile/fontsdialog.py:204
-#: pysollib/tile/gameinfodialog.py:155 pysollib/tile/menubar.py:1334
+#: pysollib/tile/gameinfodialog.py:155 pysollib/tile/menubar.py:1344
#: pysollib/tile/playeroptionsdialog.py:111 pysollib/tile/selectcardset.py:387
#: pysollib/tile/selecttile.py:161 pysollib/tile/soundoptionsdialog.py:168
#: pysollib/tile/soundoptionsdialog.py:206 pysollib/tile/timeoutsdialog.py:94
-#: pysollib/tile/tkhtml.py:501 pysollib/tile/tkstats.py:214
-#: pysollib/tile/tkstats.py:384 pysollib/tile/tkstats.py:457
-#: pysollib/tile/tkstats.py:480 pysollib/tile/tkstats.py:523
-#: pysollib/tile/tkstats.py:594 pysollib/tile/tkstats.py:678
-#: pysollib/tile/tkwidget.py:154 pysollib/tile/tkwidget.py:329
-#: pysollib/tk/colorsdialog.py:122 pysollib/tk/edittextdialog.py:82
-#: pysollib/tk/fontsdialog.py:143 pysollib/tk/fontsdialog.py:205
-#: pysollib/tk/gameinfodialog.py:155 pysollib/tk/playeroptionsdialog.py:85
+#: pysollib/tile/tkhtml.py:501 pysollib/tile/tkstats.py:216
+#: pysollib/tile/tkstats.py:388 pysollib/tile/tkstats.py:461
+#: pysollib/tile/tkstats.py:484 pysollib/tile/tkstats.py:527
+#: pysollib/tile/tkstats.py:598 pysollib/tile/tkstats.py:682
+#: pysollib/tile/tkstats.py:844 pysollib/tile/tkwidget.py:154
+#: pysollib/tile/tkwidget.py:329 pysollib/tk/colorsdialog.py:111
+#: pysollib/tk/edittextdialog.py:82 pysollib/tk/fontsdialog.py:143
+#: pysollib/tk/fontsdialog.py:205 pysollib/tk/gameinfodialog.py:155
+#: pysollib/tk/playeroptionsdialog.py:85
#: pysollib/tk/playeroptionsdialog.py:160 pysollib/tk/selectcardset.py:241
#: pysollib/tk/selectcardset.py:397 pysollib/tk/selecttile.py:159
#: pysollib/tk/soundoptionsdialog.py:170 pysollib/tk/soundoptionsdialog.py:211
#: pysollib/tk/timeoutsdialog.py:92 pysollib/tk/tkhtml.py:500
-#: pysollib/tk/tkstats.py:288 pysollib/tk/tkstats.py:514
-#: pysollib/tk/tkstats.py:581 pysollib/tk/tkstats.py:596
-#: pysollib/tk/tkstats.py:638 pysollib/tk/tkstats.py:710
-#: pysollib/tk/tkstats.py:794 pysollib/tk/tkwidget.py:160
-#: pysollib/tk/tkwidget.py:324
+#: pysollib/tk/tkstats.py:290 pysollib/tk/tkstats.py:516
+#: pysollib/tk/tkstats.py:583 pysollib/tk/tkstats.py:598
+#: pysollib/tk/tkstats.py:640 pysollib/tk/tkstats.py:712
+#: pysollib/tk/tkstats.py:796 pysollib/tk/tkstats.py:970
+#: pysollib/tk/tkwidget.py:160 pysollib/tk/tkwidget.py:324
msgid "&OK"
msgstr ""
-#: pysollib/actions.py:313 pysollib/app.py:887 pysollib/app.py:1181
-#: pysollib/game.py:950 pysollib/game.py:1502 pysollib/game.py:1518
-#: pysollib/game.py:1525 pysollib/game.py:1531
-#: pysollib/tile/colorsdialog.py:123 pysollib/tile/edittextdialog.py:83
+#: pysollib/actions.py:315 pysollib/app.py:892 pysollib/app.py:1190
+#: pysollib/game.py:1010 pysollib/game.py:1674 pysollib/game.py:1690
+#: pysollib/game.py:1697 pysollib/game.py:1703
+#: pysollib/tile/colorsdialog.py:112 pysollib/tile/edittextdialog.py:83
#: pysollib/tile/fontsdialog.py:144 pysollib/tile/fontsdialog.py:204
#: pysollib/tile/playeroptionsdialog.py:111 pysollib/tile/selectcardset.py:237
#: pysollib/tile/selectgame.py:267 pysollib/tile/selectgame.py:398
#: pysollib/tile/selecttile.py:161 pysollib/tile/soundoptionsdialog.py:168
#: pysollib/tile/timeoutsdialog.py:94 pysollib/tile/tkwidget.py:329
-#: pysollib/tk/colorsdialog.py:122 pysollib/tk/edittextdialog.py:82
+#: pysollib/tk/colorsdialog.py:111 pysollib/tk/edittextdialog.py:82
#: pysollib/tk/fontsdialog.py:143 pysollib/tk/fontsdialog.py:205
-#: pysollib/tk/menubar.py:1133 pysollib/tk/menubar.py:1135
+#: pysollib/tk/menubar.py:1143 pysollib/tk/menubar.py:1145
#: pysollib/tk/playeroptionsdialog.py:85
#: pysollib/tk/playeroptionsdialog.py:160 pysollib/tk/selectcardset.py:241
#: pysollib/tk/selectgame.py:266 pysollib/tk/selectgame.py:407
@@ -118,122 +119,126 @@ msgstr ""
msgid "&Cancel"
msgstr ""
-#: pysollib/actions.py:329
+#: pysollib/actions.py:331
msgid "Select random game"
msgstr ""
-#: pysollib/actions.py:365
+#: pysollib/actions.py:367
msgid "Select next game"
msgstr ""
-#: pysollib/actions.py:398 pysollib/tile/toolbar.py:203
+#: pysollib/actions.py:400 pysollib/tile/toolbar.py:203
#: pysollib/tk/toolbar.py:206
msgid "Quit "
msgstr ""
-#: pysollib/actions.py:449
+#: pysollib/actions.py:451
msgid "Clear bookmarks"
msgstr ""
-#: pysollib/actions.py:450
+#: pysollib/actions.py:452
msgid "Clear all bookmarks ?"
msgstr ""
-#: pysollib/actions.py:460
+#: pysollib/actions.py:462
msgid "Restart game"
msgstr ""
-#: pysollib/actions.py:461
+#: pysollib/actions.py:463
msgid "Restart this game ?"
msgstr ""
-#: pysollib/actions.py:502
+#: pysollib/actions.py:509
msgid ""
"Comments for %s:\n"
"\n"
msgstr ""
-#: pysollib/actions.py:504
+#: pysollib/actions.py:511
msgid "Comments for "
msgstr ""
-#: pysollib/actions.py:522 pysollib/actions.py:550
+#: pysollib/actions.py:529 pysollib/actions.py:557
msgid "Error while writing to file"
msgstr ""
-#: pysollib/actions.py:525 pysollib/actions.py:553
+#: pysollib/actions.py:532 pysollib/actions.py:560
msgid " Info"
msgstr ""
-#: pysollib/actions.py:526
+#: pysollib/actions.py:533
msgid ""
"Comments were appended to\n"
"\n"
msgstr ""
-#: pysollib/actions.py:537
+#: pysollib/actions.py:544
msgid "Demo statistics"
msgstr ""
-#: pysollib/actions.py:540
+#: pysollib/actions.py:547
msgid "Your statistics"
msgstr ""
-#: pysollib/actions.py:554
+#: pysollib/actions.py:561
msgid ""
" were appended to\n"
"\n"
msgstr ""
-#: pysollib/actions.py:569
+#: pysollib/actions.py:576
msgid " Demo"
msgstr ""
-#: pysollib/actions.py:569
+#: pysollib/actions.py:576
msgid " Demo "
msgstr ""
-#: pysollib/actions.py:572 pysollib/actions.py:591
+#: pysollib/actions.py:579 pysollib/actions.py:598
msgid " for "
msgstr ""
-#: pysollib/actions.py:578 pysollib/stats.py:205
+#: pysollib/actions.py:585 pysollib/stats.py:205
msgid "Statistics for "
msgstr ""
-#: pysollib/actions.py:581 pysollib/tile/selectgame.py:345
+#: pysollib/actions.py:588 pysollib/tile/selectgame.py:345
#: pysollib/tile/toolbar.py:200 pysollib/tk/selectgame.py:350
#: pysollib/tk/toolbar.py:203
msgid "Statistics"
msgstr ""
-#: pysollib/actions.py:585 data/glade-translations:31
+#: pysollib/actions.py:592 data/glade-translations:31
msgid "Full log"
msgstr ""
-#: pysollib/actions.py:588 data/glade-translations:32
+#: pysollib/actions.py:595 data/glade-translations:32
msgid "Session log"
msgstr ""
-#: pysollib/actions.py:594
+#: pysollib/actions.py:601
msgid "Game Info"
msgstr ""
-#: pysollib/actions.py:610
+#: pysollib/actions.py:604
+msgid "Statistics progression"
+msgstr ""
+
+#: pysollib/actions.py:620
msgid "Reset all statistics"
msgstr ""
-#: pysollib/actions.py:611
+#: pysollib/actions.py:621
msgid ""
"Reset ALL statistics and logs for player\n"
"%s ?"
msgstr ""
-#: pysollib/actions.py:617
+#: pysollib/actions.py:627
msgid "Reset game statistics"
msgstr ""
-#: pysollib/actions.py:618
+#: pysollib/actions.py:628
msgid ""
"Reset statistics and logs for player\n"
"%s\n"
@@ -241,53 +246,53 @@ msgid ""
"%s ?"
msgstr ""
-#: pysollib/actions.py:674
+#: pysollib/actions.py:684
msgid "Play demo"
msgstr ""
-#: pysollib/actions.py:685
+#: pysollib/actions.py:695
msgid "Set player options"
msgstr ""
-#: pysollib/actions.py:699 data/glade-translations:40
+#: pysollib/actions.py:709 data/glade-translations:40
msgid "Set colors"
msgstr ""
-#: pysollib/actions.py:719
+#: pysollib/actions.py:726
msgid "Set fonts"
msgstr ""
-#: pysollib/actions.py:728 data/glade-translations:33
+#: pysollib/actions.py:735 data/glade-translations:33
msgid "Set timeouts"
msgstr ""
-#: pysollib/app.py:87
+#: pysollib/app.py:90
msgid "Unknown"
msgstr ""
-#: pysollib/app.py:888 pysollib/game.py:1502 pysollib/game.py:1518
-#: pysollib/game.py:1525 pysollib/game.py:1531 pysollib/tile/menubar.py:361
-#: pysollib/tk/menubar.py:358
+#: pysollib/app.py:893 pysollib/game.py:1674 pysollib/game.py:1690
+#: pysollib/game.py:1697 pysollib/game.py:1703 pysollib/tile/menubar.py:358
+#: pysollib/tk/menubar.py:355
msgid "&New game"
msgstr ""
-#: pysollib/app.py:1031
+#: pysollib/app.py:1036
msgid "Loading %s %s..."
msgstr ""
-#: pysollib/app.py:1066
+#: pysollib/app.py:1075
msgid " load error"
msgstr ""
-#: pysollib/app.py:1067
+#: pysollib/app.py:1076
msgid "Error while loading "
msgstr ""
-#: pysollib/app.py:1161
+#: pysollib/app.py:1170
msgid "Incompatible "
msgstr ""
-#: pysollib/app.py:1163
+#: pysollib/app.py:1172
msgid ""
"The currently selected %s %s\n"
"is not compatible with the game\n"
@@ -296,45 +301,45 @@ msgid ""
"Please select a %s type %s.\n"
msgstr ""
-#: pysollib/app.py:1179
+#: pysollib/app.py:1188
msgid "Please select a %s type %s"
msgstr ""
-#: pysollib/game.py:869 pysollib/game.py:875
+#: pysollib/game.py:929 pysollib/game.py:935
msgid "Player\n"
msgstr ""
-#: pysollib/game.py:946
+#: pysollib/game.py:1006
msgid "Discard current game ?"
msgstr ""
-#: pysollib/game.py:1455
+#: pysollib/game.py:1627
msgid ""
"\n"
"You have reached\n"
"#%d in the %s of playing time"
msgstr ""
-#: pysollib/game.py:1460
+#: pysollib/game.py:1632
msgid ""
"\n"
"and #%d in the %s of moves"
msgstr ""
-#: pysollib/game.py:1463
+#: pysollib/game.py:1635
msgid ""
"\n"
"You have reached\n"
"#%d in the %s of moves"
msgstr ""
-#: pysollib/game.py:1493 pysollib/game.py:1510
+#: pysollib/game.py:1665 pysollib/game.py:1682
#: pysollib/tile/soundoptionsdialog.py:102
#: pysollib/tk/soundoptionsdialog.py:100
msgid "Game won"
msgstr ""
-#: pysollib/game.py:1494
+#: pysollib/game.py:1666
msgid ""
"\n"
"Congratulations, this\n"
@@ -345,7 +350,7 @@ msgid ""
"%s\n"
msgstr ""
-#: pysollib/game.py:1511
+#: pysollib/game.py:1683
msgid ""
"\n"
"Congratulations, you did it !\n"
@@ -355,101 +360,101 @@ msgid ""
"%s\n"
msgstr ""
-#: pysollib/game.py:1523 pysollib/game.py:1529
+#: pysollib/game.py:1695 pysollib/game.py:1701
#: pysollib/tile/soundoptionsdialog.py:100
#: pysollib/tk/soundoptionsdialog.py:98
msgid "Game finished"
msgstr ""
-#: pysollib/game.py:1524 pysollib/game.py:2049
+#: pysollib/game.py:1696 pysollib/game.py:2246
msgid ""
"\n"
"Game finished\n"
msgstr ""
-#: pysollib/game.py:1530
+#: pysollib/game.py:1702
msgid ""
"\n"
"Game finished, but not without my help...\n"
msgstr ""
-#: pysollib/game.py:1531
+#: pysollib/game.py:1703
msgid "&Restart"
msgstr ""
-#: pysollib/game.py:1941
+#: pysollib/game.py:2135
msgid "Score %6d"
msgstr ""
-#: pysollib/game.py:2040
+#: pysollib/game.py:2237
msgid "&Cool"
msgstr ""
-#: pysollib/game.py:2040
+#: pysollib/game.py:2237
msgid "&Great"
msgstr ""
-#: pysollib/game.py:2040
+#: pysollib/game.py:2237
msgid "&Wow"
msgstr ""
-#: pysollib/game.py:2040
+#: pysollib/game.py:2237
msgid "&Yeah"
msgstr ""
-#: pysollib/game.py:2041 pysollib/game.py:2052 pysollib/game.py:2064
+#: pysollib/game.py:2238 pysollib/game.py:2249 pysollib/game.py:2261
msgid " Autopilot"
msgstr ""
-#: pysollib/game.py:2042
+#: pysollib/game.py:2239
msgid ""
"\n"
"Game solved in %d moves.\n"
msgstr ""
-#: pysollib/game.py:2063
+#: pysollib/game.py:2260
msgid "&Hmm"
msgstr ""
-#: pysollib/game.py:2063
+#: pysollib/game.py:2260
msgid "&Oh well"
msgstr ""
-#: pysollib/game.py:2063
+#: pysollib/game.py:2260
msgid "&That's life"
msgstr ""
-#: pysollib/game.py:2065
+#: pysollib/game.py:2262
msgid ""
"\n"
"This won't come out...\n"
msgstr ""
-#: pysollib/game.py:2474
+#: pysollib/game.py:2702
msgid "Set bookmark"
msgstr ""
-#: pysollib/game.py:2475
+#: pysollib/game.py:2703
msgid "Replace existing bookmark %d ?"
msgstr ""
-#: pysollib/game.py:2497
+#: pysollib/game.py:2725
msgid "Goto bookmark"
msgstr ""
-#: pysollib/game.py:2498
+#: pysollib/game.py:2726
msgid "Goto bookmark %d ?"
msgstr ""
-#: pysollib/game.py:2529
+#: pysollib/game.py:2757
msgid "Open game"
msgstr ""
-#: pysollib/game.py:2540 pysollib/game.py:2549 pysollib/game.py:2554
+#: pysollib/game.py:2768 pysollib/game.py:2777 pysollib/game.py:2782
msgid "Load game error"
msgstr ""
-#: pysollib/game.py:2541
+#: pysollib/game.py:2769
msgid ""
"Error while loading game.\n"
"\n"
@@ -457,22 +462,22 @@ msgid ""
"but this could also be a bug you might want to report."
msgstr ""
-#: pysollib/game.py:2550
+#: pysollib/game.py:2778
msgid "Error while loading game"
msgstr ""
-#: pysollib/game.py:2555
+#: pysollib/game.py:2783
msgid ""
"Internal error while loading game.\n"
"\n"
"Please report this bug."
msgstr ""
-#: pysollib/game.py:2580
+#: pysollib/game.py:2808
msgid "Save game error"
msgstr ""
-#: pysollib/game.py:2581
+#: pysollib/game.py:2809
msgid "Error while saving game"
msgstr ""
@@ -684,25 +689,25 @@ msgstr ""
msgid "Puzzle type"
msgstr ""
-#: pysollib/games/auldlangsyne.py:158 pysollib/games/calculation.py:104
-#: pysollib/games/camelot.py:588 pysollib/games/numerica.py:94
+#: pysollib/games/auldlangsyne.py:160 pysollib/games/calculation.py:104
+#: pysollib/games/camelot.py:590 pysollib/games/numerica.py:94
#: pysollib/games/numerica.py:276 pysollib/games/numerica.py:666
#: pysollib/games/numerica.py:779
msgid "Tableau. Build regardless of rank and suit."
msgstr ""
-#: pysollib/games/auldlangsyne.py:555
+#: pysollib/games/auldlangsyne.py:564 pysollib/games/golf.py:295
msgid "Foundation. Build up or down regardless of suit."
msgstr ""
-#: pysollib/games/braid.py:248 pysollib/games/camelot.py:555
+#: pysollib/games/braid.py:248 pysollib/games/camelot.py:557
#: pysollib/games/napoleon.py:183 pysollib/games/ultra/dashavatara.py:959
#: pysollib/games/ultra/hanafuda1.py:257 pysollib/games/ultra/hexadeck.py:1190
#: pysollib/games/ultra/mughal.py:802
msgid " Ascending"
msgstr ""
-#: pysollib/games/braid.py:250 pysollib/games/camelot.py:554
+#: pysollib/games/braid.py:250 pysollib/games/camelot.py:556
#: pysollib/games/napoleon.py:185 pysollib/games/ultra/dashavatara.py:961
#: pysollib/games/ultra/hanafuda1.py:259 pysollib/games/ultra/hexadeck.py:1192
#: pysollib/games/ultra/mughal.py:804
@@ -718,12 +723,12 @@ msgid ""
msgstr ""
#: pysollib/games/canfield.py:528 pysollib/games/special/tarock.py:224
-#: pysollib/stack.py:1389 pysollib/util.py:86
+#: pysollib/stack.py:1482 pysollib/util.py:86
msgid "King"
msgstr ""
#: pysollib/games/canfield.py:531 pysollib/games/special/tarock.py:224
-#: pysollib/stack.py:1388 pysollib/util.py:86
+#: pysollib/stack.py:1481 pysollib/util.py:86
msgid "Queen"
msgstr ""
@@ -731,21 +736,21 @@ msgstr ""
msgid "Tableau. Build down by suit or of the same rank."
msgstr ""
-#: pysollib/games/fan.py:280
+#: pysollib/games/fan.py:285
msgid "Draw"
msgstr ""
-#: pysollib/games/fan.py:280
+#: pysollib/games/fan.py:285
msgid "X"
msgstr ""
-#: pysollib/games/golf.py:114 pysollib/games/golf.py:300
-#: pysollib/stack.py:2054
+#: pysollib/games/golf.py:114 pysollib/games/golf.py:302
+#: pysollib/stack.py:2145
msgid "Tableau. No building."
msgstr ""
-#: pysollib/games/golf.py:385 pysollib/games/pileon.py:257
-#: pysollib/stack.py:1987
+#: pysollib/games/golf.py:387 pysollib/games/pileon.py:257
+#: pysollib/stack.py:2078
msgid "Foundation. Build up regardless of suit."
msgstr ""
@@ -753,7 +758,7 @@ msgstr ""
msgid "Balance $%d"
msgstr ""
-#: pysollib/games/klondike.py:169 pysollib/stack.py:2095
+#: pysollib/games/klondike.py:169 pysollib/stack.py:2186
msgid "Tableau. Build down by color."
msgstr ""
@@ -761,32 +766,32 @@ msgstr ""
msgid "Reserve. Only Kings are acceptable."
msgstr ""
-#: pysollib/games/larasgame.py:163 pysollib/stack.py:1605
+#: pysollib/games/larasgame.py:163 pysollib/stack.py:1694
msgid "Round %d"
msgstr ""
-#: pysollib/games/mahjongg/mahjongg.py:308
+#: pysollib/games/mahjongg/mahjongg.py:307
msgid ""
"No Free\n"
"Matching\n"
"Pairs"
msgstr ""
-#: pysollib/games/mahjongg/mahjongg.py:309
+#: pysollib/games/mahjongg/mahjongg.py:308
msgid ""
"1 Free\n"
"Matching\n"
"Pair"
msgstr ""
-#: pysollib/games/mahjongg/mahjongg.py:310
+#: pysollib/games/mahjongg/mahjongg.py:309
msgid ""
" Free\n"
"Matching\n"
"Pairs"
msgstr ""
-#: pysollib/games/mahjongg/mahjongg.py:311
+#: pysollib/games/mahjongg/mahjongg.py:310
msgid ""
"\n"
"Tiles\n"
@@ -794,7 +799,7 @@ msgid ""
"\n"
msgstr ""
-#: pysollib/games/mahjongg/mahjongg.py:312
+#: pysollib/games/mahjongg/mahjongg.py:311
msgid ""
"\n"
"Tiles\n"
@@ -810,7 +815,7 @@ msgstr ""
msgid "Deal %d"
msgstr ""
-#: pysollib/games/numerica.py:263 pysollib/games/royalcotillion.py:850
+#: pysollib/games/numerica.py:263 pysollib/games/royalcotillion.py:865
msgid "Foundation. Build up by color."
msgstr ""
@@ -864,7 +869,7 @@ msgstr ""
#: pysollib/games/special/tarock.py:223
#: pysollib/games/ultra/dashavatara.py:351
#: pysollib/games/ultra/hexadeck.py:273 pysollib/games/ultra/mughal.py:254
-#: pysollib/stack.py:1390 pysollib/util.py:85
+#: pysollib/stack.py:1483 pysollib/util.py:85
msgid "Ace"
msgstr ""
@@ -876,7 +881,7 @@ msgstr ""
msgid "Valet"
msgstr ""
-#: pysollib/games/spider.py:1131
+#: pysollib/games/spider.py:1156
msgid ""
"Tableau. Build down regardless of suit. Sequences of cards in the same color "
"can be moved as a unit."
@@ -1141,25 +1146,25 @@ msgstr ""
msgid "Tan"
msgstr ""
-#: pysollib/games/yukon.py:139
+#: pysollib/games/yukon.py:140
msgid ""
"Tableau. Build down in any suit but the same, can move any face-up cards "
"regardless of sequence."
msgstr ""
-#: pysollib/games/yukon.py:198
+#: pysollib/games/yukon.py:199
msgid ""
"Tableau. Build up or down by suit, can move any face-up cards regardless of "
"sequence."
msgstr ""
-#: pysollib/games/yukon.py:215
+#: pysollib/games/yukon.py:216
msgid ""
"Tableau. Build up or down by alternate color, can move any face-up cards "
"regardless of sequence."
msgstr ""
-#: pysollib/games/yukon.py:317
+#: pysollib/games/yukon.py:318
msgid ""
"Club: A 2 3 4 5 6 7 8 9 T J Q K\n"
"Spade: 2 4 6 8 T Q A 3 5 7 9 J K\n"
@@ -1167,7 +1172,7 @@ msgid ""
"Diamond: 4 8 Q 3 7 J 2 6 T A 5 9 K"
msgstr ""
-#: pysollib/games/yukon.py:639
+#: pysollib/games/yukon.py:640
msgid ""
"Tableau. Build down regardless of suit, can move any face-up cards "
"regardless of sequence."
@@ -1265,8 +1270,8 @@ msgid ""
"Please check your %s installation.\n"
msgstr ""
-#: pysollib/main.py:72 pysollib/main.py:257 pysollib/tile/menubar.py:381
-#: pysollib/tk/menubar.py:378
+#: pysollib/main.py:72 pysollib/main.py:257 pysollib/tile/menubar.py:378
+#: pysollib/tk/menubar.py:375
msgid "&Quit"
msgstr ""
@@ -1582,218 +1587,218 @@ msgstr ""
msgid "USA"
msgstr ""
-#: pysollib/settings.py:67 data/glade-translations:29
+#: pysollib/settings.py:72 data/glade-translations:29
msgid "Top 10"
msgstr ""
-#: pysollib/stack.py:1384
+#: pysollib/stack.py:1477
msgid "Base card - %s."
msgstr ""
-#: pysollib/stack.py:1385
+#: pysollib/stack.py:1478
msgid "Empty row cannot be filled."
msgstr ""
-#: pysollib/stack.py:1386
+#: pysollib/stack.py:1479
msgid "any card"
msgstr ""
-#: pysollib/stack.py:1387 pysollib/util.py:86
+#: pysollib/stack.py:1480 pysollib/util.py:86
msgid "Jack"
msgstr ""
-#: pysollib/stack.py:1400
+#: pysollib/stack.py:1489
msgid "No cards"
msgstr ""
-#: pysollib/stack.py:1401
+#: pysollib/stack.py:1490
msgid "1 card"
msgstr ""
-#: pysollib/stack.py:1402
+#: pysollib/stack.py:1491
msgid " cards"
msgstr ""
-#: pysollib/stack.py:1614 pysollib/stack.py:1616 pysollib/stack.py:1652
+#: pysollib/stack.py:1703 pysollib/stack.py:1705 pysollib/stack.py:1741
msgid "Redeal"
msgstr ""
-#: pysollib/stack.py:1616
+#: pysollib/stack.py:1705
msgid "Stop"
msgstr ""
-#: pysollib/stack.py:1677
+#: pysollib/stack.py:1766
msgid "Variable redeals."
msgstr ""
-#: pysollib/stack.py:1678
+#: pysollib/stack.py:1767
msgid "Unlimited redeals."
msgstr ""
-#: pysollib/stack.py:1679
+#: pysollib/stack.py:1768
msgid "No redeals."
msgstr ""
-#: pysollib/stack.py:1680
+#: pysollib/stack.py:1769
msgid "One redeal."
msgstr ""
-#: pysollib/stack.py:1681
+#: pysollib/stack.py:1770
msgid " redeals."
msgstr ""
-#: pysollib/stack.py:1683
+#: pysollib/stack.py:1772
msgid "Talon."
msgstr ""
-#: pysollib/stack.py:1917 pysollib/stack.py:2368
+#: pysollib/stack.py:2008 pysollib/stack.py:2537
msgid "Reserve. No building."
msgstr ""
-#: pysollib/stack.py:1955
+#: pysollib/stack.py:2046
msgid "Foundation."
msgstr ""
-#: pysollib/stack.py:1971
+#: pysollib/stack.py:2062
msgid "Foundation. Build up by suit."
msgstr ""
-#: pysollib/stack.py:1972
+#: pysollib/stack.py:2063
msgid "Foundation. Build down by suit."
msgstr ""
-#: pysollib/stack.py:1973 pysollib/stack.py:1989 pysollib/stack.py:2011
+#: pysollib/stack.py:2064 pysollib/stack.py:2080 pysollib/stack.py:2102
msgid "Foundation. Build by same rank."
msgstr ""
-#: pysollib/stack.py:1988
+#: pysollib/stack.py:2079
msgid "Foundation. Build down regardless of suit."
msgstr ""
-#: pysollib/stack.py:2009
+#: pysollib/stack.py:2100
msgid "Foundation. Build up by alternate color."
msgstr ""
-#: pysollib/stack.py:2010
+#: pysollib/stack.py:2101
msgid "Foundation. Build down by alternate color."
msgstr ""
-#: pysollib/stack.py:2084
+#: pysollib/stack.py:2175
msgid "Tableau. Build up by alternate color."
msgstr ""
-#: pysollib/stack.py:2085
+#: pysollib/stack.py:2176
msgid "Tableau. Build down by alternate color."
msgstr ""
-#: pysollib/stack.py:2086 pysollib/stack.py:2096 pysollib/stack.py:2105
-#: pysollib/stack.py:2114 pysollib/stack.py:2124 pysollib/stack.py:2147
-#: pysollib/stack.py:2157
+#: pysollib/stack.py:2177 pysollib/stack.py:2187 pysollib/stack.py:2196
+#: pysollib/stack.py:2205 pysollib/stack.py:2215 pysollib/stack.py:2244
+#: pysollib/stack.py:2254
msgid "Tableau. Build by same rank."
msgstr ""
-#: pysollib/stack.py:2094
+#: pysollib/stack.py:2185
msgid "Tableau. Build up by color."
msgstr ""
-#: pysollib/stack.py:2103
+#: pysollib/stack.py:2194
msgid "Tableau. Build up by suit."
msgstr ""
-#: pysollib/stack.py:2104
+#: pysollib/stack.py:2195
msgid "Tableau. Build down by suit."
msgstr ""
-#: pysollib/stack.py:2112
+#: pysollib/stack.py:2203
msgid "Tableau. Build up regardless of suit."
msgstr ""
-#: pysollib/stack.py:2113
+#: pysollib/stack.py:2204
msgid "Tableau. Build down regardless of suit."
msgstr ""
-#: pysollib/stack.py:2122
+#: pysollib/stack.py:2213
msgid "Tableau. Build up in any suit but the same."
msgstr ""
-#: pysollib/stack.py:2123
+#: pysollib/stack.py:2214
msgid "Tableau. Build down in any suit but the same."
msgstr ""
-#: pysollib/stack.py:2145
+#: pysollib/stack.py:2242
msgid ""
"Tableau. Build up regardless of suit. Sequences of cards in alternate color "
"can be moved as a unit."
msgstr ""
-#: pysollib/stack.py:2146
+#: pysollib/stack.py:2243
msgid ""
"Tableau. Build down regardless of suit. Sequences of cards in alternate "
"color can be moved as a unit."
msgstr ""
-#: pysollib/stack.py:2155
+#: pysollib/stack.py:2252
msgid ""
"Tableau. Build up regardless of suit. Sequences of cards in the same suit "
"can be moved as a unit."
msgstr ""
-#: pysollib/stack.py:2156
+#: pysollib/stack.py:2253
msgid ""
"Tableau. Build down regardless of suit. Sequences of cards in the same suit "
"can be moved as a unit."
msgstr ""
-#: pysollib/stack.py:2178
+#: pysollib/stack.py:2275
msgid ""
"Tableau. Build up by alternate color, can move any face-up cards regardless "
"of sequence."
msgstr ""
-#: pysollib/stack.py:2179
+#: pysollib/stack.py:2276
msgid ""
"Tableau. Build down by alternate color, can move any face-up cards "
"regardless of sequence."
msgstr ""
-#: pysollib/stack.py:2180 pysollib/stack.py:2193
+#: pysollib/stack.py:2277 pysollib/stack.py:2290
msgid ""
"Tableau. Build by same rank, can move any face-up cards regardless of "
"sequence."
msgstr ""
-#: pysollib/stack.py:2191
+#: pysollib/stack.py:2288
msgid ""
"Tableau. Build up by suit, can move any face-up cards regardless of sequence."
msgstr ""
-#: pysollib/stack.py:2192
+#: pysollib/stack.py:2289
msgid ""
"Tableau. Build down by suit, can move any face-up cards regardless of "
"sequence."
msgstr ""
-#: pysollib/stack.py:2225
+#: pysollib/stack.py:2322
msgid "Tableau. Build up or down by color."
msgstr ""
-#: pysollib/stack.py:2236
+#: pysollib/stack.py:2333
msgid "Tableau. Build up or down by alternate color."
msgstr ""
-#: pysollib/stack.py:2247
+#: pysollib/stack.py:2344
msgid "Tableau. Build up or down by suit."
msgstr ""
-#: pysollib/stack.py:2258
+#: pysollib/stack.py:2355
msgid "Tableau. Build up or down regardless of suit."
msgstr ""
-#: pysollib/stack.py:2269
+#: pysollib/stack.py:2433
msgid "Waste."
msgstr ""
-#: pysollib/stack.py:2369
+#: pysollib/stack.py:2538
msgid "Free cell."
msgstr ""
@@ -1801,11 +1806,17 @@ msgstr ""
msgid "Game"
msgstr ""
-#: pysollib/stats.py:53
+#: pysollib/stats.py:53 pysollib/tile/tkstats.py:772
+#: pysollib/tile/tkstats.py:773 pysollib/tile/tkstats.py:817
+#: pysollib/tk/tkstats.py:886 pysollib/tk/tkstats.py:887
+#: pysollib/tk/tkstats.py:940
msgid "Played"
msgstr ""
-#: pysollib/stats.py:54 pysollib/stats.py:157
+#: pysollib/stats.py:54 pysollib/stats.py:157 pysollib/tile/tkstats.py:777
+#: pysollib/tile/tkstats.py:778 pysollib/tile/tkstats.py:824
+#: pysollib/tk/tkstats.py:891 pysollib/tk/tkstats.py:892
+#: pysollib/tk/tkstats.py:948
msgid "Won"
msgstr ""
@@ -1813,8 +1824,8 @@ msgstr ""
msgid "Lost"
msgstr ""
-#: pysollib/stats.py:56 pysollib/tile/statusbar.py:147
-#: pysollib/tk/statusbar.py:158 data/glade-translations:25
+#: pysollib/stats.py:56 pysollib/tile/statusbar.py:150
+#: pysollib/tk/statusbar.py:161 data/glade-translations:25
msgid "Playing time"
msgstr ""
@@ -1822,7 +1833,11 @@ msgstr ""
msgid "Moves"
msgstr ""
-#: pysollib/stats.py:58
+#: pysollib/stats.py:58 pysollib/tile/tkstats.py:745
+#: pysollib/tile/tkstats.py:764 pysollib/tile/tkstats.py:782
+#: pysollib/tile/tkstats.py:831 pysollib/tk/tkstats.py:859
+#: pysollib/tk/tkstats.py:878 pysollib/tk/tkstats.py:896
+#: pysollib/tk/tkstats.py:956
msgid "% won"
msgstr ""
@@ -1830,14 +1845,14 @@ msgstr ""
msgid "Status"
msgstr ""
-#: pysollib/stats.py:118 pysollib/tile/statusbar.py:149
-#: pysollib/tile/tkstats.py:552 pysollib/tk/statusbar.py:160
-#: pysollib/tk/tkstats.py:668
+#: pysollib/stats.py:118 pysollib/tile/statusbar.py:152
+#: pysollib/tile/tkstats.py:556 pysollib/tk/statusbar.py:163
+#: pysollib/tk/tkstats.py:670
msgid "Game number"
msgstr ""
-#: pysollib/stats.py:118 pysollib/tile/tkstats.py:555
-#: pysollib/tk/tkstats.py:671
+#: pysollib/stats.py:118 pysollib/tile/tkstats.py:559
+#: pysollib/tk/tkstats.py:673
msgid "Started at"
msgstr ""
@@ -1865,8 +1880,8 @@ msgstr ""
msgid "Demo"
msgstr ""
-#: pysollib/stats.py:215 pysollib/tile/tkstats.py:283
-#: pysollib/tk/tkstats.py:419
+#: pysollib/stats.py:215 pysollib/tile/tkstats.py:287
+#: pysollib/tk/tkstats.py:421
msgid "Total (%d out of %d games)"
msgstr ""
@@ -1878,18 +1893,53 @@ msgstr ""
msgid "Session log for "
msgstr ""
-#: pysollib/tile/colorsdialog.py:72 pysollib/tk/colorsdialog.py:71
-#: data/glade-translations:41
+#: pysollib/tile/colorsdialog.py:71 pysollib/tk/colorsdialog.py:70
+#: data/glade-translations:56
msgid "Text foreground:"
msgstr ""
-#: pysollib/tile/colorsdialog.py:77 pysollib/tile/colorsdialog.py:95
-#: pysollib/tile/fontsdialog.py:185 pysollib/tk/colorsdialog.py:76
-#: pysollib/tk/colorsdialog.py:94 pysollib/tk/fontsdialog.py:186
-#: data/glade-translations:49 data/glade-translations:50
-#: data/glade-translations:51 data/glade-translations:52
-#: data/glade-translations:53 data/glade-translations:54
-#: data/glade-translations:55 data/glade-translations:56
+#: pysollib/tile/colorsdialog.py:72 pysollib/tile/timeoutsdialog.py:70
+#: pysollib/tk/colorsdialog.py:71 pysollib/tk/timeoutsdialog.py:68
+#: data/glade-translations:37 data/glade-translations:41
+msgid "Highlight piles:"
+msgstr ""
+
+#: pysollib/tile/colorsdialog.py:73 pysollib/tk/colorsdialog.py:72
+#: data/glade-translations:42
+msgid "Highlight cards 1:"
+msgstr ""
+
+#: pysollib/tile/colorsdialog.py:74 pysollib/tk/colorsdialog.py:73
+#: data/glade-translations:43
+msgid "Highlight cards 2:"
+msgstr ""
+
+#: pysollib/tile/colorsdialog.py:75 pysollib/tk/colorsdialog.py:74
+#: data/glade-translations:44
+msgid "Highlight same rank 1:"
+msgstr ""
+
+#: pysollib/tile/colorsdialog.py:76 pysollib/tk/colorsdialog.py:75
+#: data/glade-translations:45
+msgid "Highlight same rank 2:"
+msgstr ""
+
+#: pysollib/tile/colorsdialog.py:77 pysollib/tk/colorsdialog.py:76
+#: data/glade-translations:46
+msgid "Hint arrow:"
+msgstr ""
+
+#: pysollib/tile/colorsdialog.py:78 pysollib/tk/colorsdialog.py:77
+#: data/glade-translations:47
+msgid "Highlight not matching:"
+msgstr ""
+
+#: pysollib/tile/colorsdialog.py:85 pysollib/tile/fontsdialog.py:185
+#: pysollib/tk/colorsdialog.py:84 pysollib/tk/fontsdialog.py:186
+#: data/glade-translations:48 data/glade-translations:49
+#: data/glade-translations:50 data/glade-translations:51
+#: data/glade-translations:52 data/glade-translations:53
+#: data/glade-translations:54 data/glade-translations:55
#: data/glade-translations:65 data/glade-translations:66
#: data/glade-translations:67 data/glade-translations:68
#: data/glade-translations:69 data/glade-translations:70
@@ -1897,48 +1947,11 @@ msgstr ""
msgid "Change..."
msgstr ""
-#: pysollib/tile/colorsdialog.py:82 pysollib/tile/timeoutsdialog.py:70
-#: pysollib/tk/colorsdialog.py:81 pysollib/tk/timeoutsdialog.py:68
-#: data/glade-translations:37 data/glade-translations:42
-msgid "Highlight piles:"
-msgstr ""
-
-#: pysollib/tile/colorsdialog.py:83 pysollib/tk/colorsdialog.py:82
-#: data/glade-translations:43
-msgid "Highlight cards 1:"
-msgstr ""
-
-#: pysollib/tile/colorsdialog.py:84 pysollib/tk/colorsdialog.py:83
-#: data/glade-translations:44
-msgid "Highlight cards 2:"
-msgstr ""
-
-#: pysollib/tile/colorsdialog.py:85 pysollib/tk/colorsdialog.py:84
-#: data/glade-translations:45
-msgid "Highlight same rank 1:"
-msgstr ""
-
-#: pysollib/tile/colorsdialog.py:86 pysollib/tk/colorsdialog.py:85
-#: data/glade-translations:46
-msgid "Highlight same rank 2:"
-msgstr ""
-
-#: pysollib/tile/colorsdialog.py:87 pysollib/tk/colorsdialog.py:86
-#: data/glade-translations:47
-msgid "Hint arrow:"
-msgstr ""
-
-#: pysollib/tile/colorsdialog.py:88 pysollib/tk/colorsdialog.py:87
-#: data/glade-translations:48
-msgid "Highlight not matching:"
-msgstr ""
-
-#: pysollib/tile/colorsdialog.py:115 pysollib/tk/colorsdialog.py:114
+#: pysollib/tile/colorsdialog.py:104 pysollib/tk/colorsdialog.py:103
msgid "Select color"
msgstr ""
-#: pysollib/tile/findcarddialog.py:54 pysollib/tile/menubar.py:434
-#: pysollib/tk/findcarddialog.py:54 pysollib/tk/menubar.py:431
+#: pysollib/tile/findcarddialog.py:54 pysollib/tk/findcarddialog.py:54
msgid "Find card"
msgstr ""
@@ -1998,536 +2011,553 @@ msgstr ""
msgid "Select font"
msgstr ""
-#: pysollib/tile/menubar.py:78 pysollib/tk/menubar.py:76
+#: pysollib/tile/menubar.py:81 pysollib/tk/menubar.py:79
msgid "Style"
msgstr ""
-#: pysollib/tile/menubar.py:86 pysollib/tk/menubar.py:85
+#: pysollib/tile/menubar.py:89 pysollib/tk/menubar.py:88
msgid "Compound"
msgstr ""
-#: pysollib/tile/menubar.py:92 pysollib/tk/menubar.py:91
+#: pysollib/tile/menubar.py:95 pysollib/tk/menubar.py:94
msgid "Hide"
msgstr ""
-#: pysollib/tile/menubar.py:95 pysollib/tk/menubar.py:94
+#: pysollib/tile/menubar.py:98 pysollib/tk/menubar.py:97
msgid "Top"
msgstr ""
-#: pysollib/tile/menubar.py:98 pysollib/tk/menubar.py:97
+#: pysollib/tile/menubar.py:101 pysollib/tk/menubar.py:100
msgid "Bottom"
msgstr ""
-#: pysollib/tile/menubar.py:101 pysollib/tk/menubar.py:100
+#: pysollib/tile/menubar.py:104 pysollib/tk/menubar.py:103
msgid "Left"
msgstr ""
-#: pysollib/tile/menubar.py:104 pysollib/tk/menubar.py:103
+#: pysollib/tile/menubar.py:107 pysollib/tk/menubar.py:106
msgid "Right"
msgstr ""
-#: pysollib/tile/menubar.py:108 pysollib/tk/menubar.py:107
+#: pysollib/tile/menubar.py:111 pysollib/tk/menubar.py:110
msgid "Small icons"
msgstr ""
-#: pysollib/tile/menubar.py:111 pysollib/tk/menubar.py:110
+#: pysollib/tile/menubar.py:114 pysollib/tk/menubar.py:113
msgid "Large icons"
msgstr ""
-#: pysollib/tile/menubar.py:117 pysollib/tk/menubar.py:116
+#: pysollib/tile/menubar.py:120 pysollib/tk/menubar.py:119
msgid "Customize toolbar"
msgstr ""
-#: pysollib/tile/menubar.py:357 pysollib/tk/menubar.py:354
+#: pysollib/tile/menubar.py:354 pysollib/tk/menubar.py:351
msgid "apple"
msgstr ""
-#: pysollib/tile/menubar.py:358 pysollib/tile/menubar.py:524
-#: pysollib/tk/menubar.py:355 pysollib/tk/menubar.py:520
+#: pysollib/tile/menubar.py:355 pysollib/tile/menubar.py:528
+#: pysollib/tk/menubar.py:352 pysollib/tk/menubar.py:524
msgid "&About "
msgstr ""
-#: pysollib/tile/menubar.py:360 pysollib/tk/menubar.py:357
+#: pysollib/tile/menubar.py:357 pysollib/tk/menubar.py:354
msgid "&File"
msgstr ""
-#: pysollib/tile/menubar.py:362 pysollib/tk/menubar.py:359
+#: pysollib/tile/menubar.py:359 pysollib/tk/menubar.py:356
msgid "R&ecent games"
msgstr ""
-#: pysollib/tile/menubar.py:364 pysollib/tk/menubar.py:361
+#: pysollib/tile/menubar.py:361 pysollib/tk/menubar.py:358
msgid "Select &random game"
msgstr ""
-#: pysollib/tile/menubar.py:365 pysollib/tk/menubar.py:362
+#: pysollib/tile/menubar.py:362 pysollib/tk/menubar.py:359
msgid "&All games"
msgstr ""
-#: pysollib/tile/menubar.py:366 pysollib/tk/menubar.py:363
+#: pysollib/tile/menubar.py:363 pysollib/tk/menubar.py:360
msgid "Games played and &won"
msgstr ""
-#: pysollib/tile/menubar.py:367 pysollib/tk/menubar.py:364
+#: pysollib/tile/menubar.py:364 pysollib/tk/menubar.py:361
msgid "Games played and ¬ won"
msgstr ""
-#: pysollib/tile/menubar.py:368 pysollib/tk/menubar.py:365
+#: pysollib/tile/menubar.py:365 pysollib/tk/menubar.py:362
msgid "Games not &played"
msgstr ""
-#: pysollib/tile/menubar.py:369 pysollib/tk/menubar.py:366
+#: pysollib/tile/menubar.py:366 pysollib/tk/menubar.py:363
msgid "Select game by nu&mber..."
msgstr ""
-#: pysollib/tile/menubar.py:371 pysollib/tk/menubar.py:368
+#: pysollib/tile/menubar.py:368 pysollib/tk/menubar.py:365
msgid "Fa&vorite games"
msgstr ""
-#: pysollib/tile/menubar.py:372 pysollib/tk/menubar.py:369
+#: pysollib/tile/menubar.py:369 pysollib/tk/menubar.py:366
msgid "A&dd to favorites"
msgstr ""
-#: pysollib/tile/menubar.py:373 pysollib/tk/menubar.py:370
-msgid "R&emove from favorites"
+#: pysollib/tile/menubar.py:370 pysollib/tk/menubar.py:367
+msgid "Remove &from favorites"
msgstr ""
-#: pysollib/tile/menubar.py:375 pysollib/tk/menubar.py:372
+#: pysollib/tile/menubar.py:372 pysollib/tk/menubar.py:369
msgid "&Open..."
msgstr ""
-#: pysollib/tile/menubar.py:376 pysollib/tk/menubar.py:373
+#: pysollib/tile/menubar.py:373 pysollib/tk/menubar.py:370
msgid "&Save"
msgstr ""
-#: pysollib/tile/menubar.py:377 pysollib/tk/menubar.py:374
+#: pysollib/tile/menubar.py:374 pysollib/tk/menubar.py:371
msgid "Save &as..."
msgstr ""
-#: pysollib/tile/menubar.py:379 pysollib/tk/menubar.py:376
+#: pysollib/tile/menubar.py:376 pysollib/tk/menubar.py:373
msgid "&Hold and quit"
msgstr ""
-#: pysollib/tile/menubar.py:385 pysollib/tile/selectgame.py:398
-#: pysollib/tk/menubar.py:382 pysollib/tk/selectgame.py:407
+#: pysollib/tile/menubar.py:382 pysollib/tile/selectgame.py:398
+#: pysollib/tk/menubar.py:379 pysollib/tk/selectgame.py:407
msgid "&Select"
msgstr ""
-#: pysollib/tile/menubar.py:390 pysollib/tk/menubar.py:387
+#: pysollib/tile/menubar.py:387 pysollib/tk/menubar.py:384
msgid "&Edit"
msgstr ""
-#: pysollib/tile/menubar.py:391 pysollib/tk/menubar.py:388
+#: pysollib/tile/menubar.py:388 pysollib/tk/menubar.py:385
msgid "&Undo"
msgstr ""
-#: pysollib/tile/menubar.py:392 pysollib/tk/menubar.py:389
+#: pysollib/tile/menubar.py:389 pysollib/tk/menubar.py:386
msgid "&Redo"
msgstr ""
-#: pysollib/tile/menubar.py:393 pysollib/tk/menubar.py:390
+#: pysollib/tile/menubar.py:390 pysollib/tk/menubar.py:387
msgid "Redo &all"
msgstr ""
-#: pysollib/tile/menubar.py:396 pysollib/tk/menubar.py:393
+#: pysollib/tile/menubar.py:393 pysollib/tk/menubar.py:390
msgid "&Set bookmark"
msgstr ""
-#: pysollib/tile/menubar.py:398 pysollib/tile/menubar.py:402
-#: pysollib/tk/menubar.py:395 pysollib/tk/menubar.py:399
+#: pysollib/tile/menubar.py:395 pysollib/tile/menubar.py:399
+#: pysollib/tk/menubar.py:392 pysollib/tk/menubar.py:396
msgid "Bookmark %d"
msgstr ""
-#: pysollib/tile/menubar.py:400 pysollib/tk/menubar.py:397
+#: pysollib/tile/menubar.py:397 pysollib/tk/menubar.py:394
msgid "Go&to bookmark"
msgstr ""
-#: pysollib/tile/menubar.py:405 pysollib/tk/menubar.py:402
+#: pysollib/tile/menubar.py:402 pysollib/tk/menubar.py:399
msgid "&Clear bookmarks"
msgstr ""
-#: pysollib/tile/menubar.py:408 pysollib/tile/toolbar.py:190
-#: pysollib/tk/menubar.py:405 pysollib/tk/toolbar.py:193
+#: pysollib/tile/menubar.py:405 pysollib/tile/toolbar.py:190
+#: pysollib/tk/menubar.py:402 pysollib/tk/toolbar.py:193
msgid "Restart"
msgstr ""
-#: pysollib/tile/menubar.py:410 pysollib/tk/menubar.py:407
+#: pysollib/tile/menubar.py:407 pysollib/tk/menubar.py:404
msgid "&Game"
msgstr ""
-#: pysollib/tile/menubar.py:411 pysollib/tk/menubar.py:408
+#: pysollib/tile/menubar.py:408 pysollib/tk/menubar.py:405
msgid "&Deal cards"
msgstr ""
-#: pysollib/tile/menubar.py:412 pysollib/tk/menubar.py:409
+#: pysollib/tile/menubar.py:409 pysollib/tk/menubar.py:406
msgid "&Auto drop"
msgstr ""
-#: pysollib/tile/menubar.py:413 pysollib/tk/menubar.py:410
+#: pysollib/tile/menubar.py:410 pysollib/tk/menubar.py:407
msgid "&Pause"
msgstr ""
-#: pysollib/tile/menubar.py:416 pysollib/tk/menubar.py:413
+#: pysollib/tile/menubar.py:413 pysollib/tk/menubar.py:410
msgid "S&tatus..."
msgstr ""
-#: pysollib/tile/menubar.py:417 pysollib/tk/menubar.py:414
+#: pysollib/tile/menubar.py:414 pysollib/tk/menubar.py:411
msgid "&Comments..."
msgstr ""
-#: pysollib/tile/menubar.py:419 pysollib/tk/menubar.py:416
+#: pysollib/tile/menubar.py:416 pysollib/tk/menubar.py:413
msgid "&Statistics"
msgstr ""
-#: pysollib/tile/menubar.py:420 pysollib/tile/menubar.py:428
-#: pysollib/tk/menubar.py:417 pysollib/tk/menubar.py:425
+#: pysollib/tile/menubar.py:417 pysollib/tile/menubar.py:426
+#: pysollib/tk/menubar.py:414 pysollib/tk/menubar.py:423
msgid "Current game..."
msgstr ""
-#: pysollib/tile/menubar.py:421 pysollib/tile/menubar.py:429
-#: pysollib/tk/menubar.py:418 pysollib/tk/menubar.py:426
+#: pysollib/tile/menubar.py:418 pysollib/tile/menubar.py:427
+#: pysollib/tk/menubar.py:415 pysollib/tk/menubar.py:424
msgid "All games..."
msgstr ""
-#: pysollib/tile/menubar.py:423 pysollib/tk/menubar.py:420
+#: pysollib/tile/menubar.py:420 pysollib/tk/menubar.py:417
msgid "Session log..."
msgstr ""
-#: pysollib/tile/menubar.py:424 pysollib/tk/menubar.py:421
+#: pysollib/tile/menubar.py:421 pysollib/tk/menubar.py:418
msgid "Full log..."
msgstr ""
-#: pysollib/tile/menubar.py:427 pysollib/tk/menubar.py:424
+#: pysollib/tile/menubar.py:424 pysollib/tk/menubar.py:421
+msgid "Progression..."
+msgstr ""
+
+#: pysollib/tile/menubar.py:425 pysollib/tk/menubar.py:422
msgid "D&emo statistics"
msgstr ""
-#: pysollib/tile/menubar.py:431 pysollib/tk/menubar.py:428
+#: pysollib/tile/menubar.py:429 pysollib/tk/menubar.py:426
msgid "&Assist"
msgstr ""
-#: pysollib/tile/menubar.py:432 pysollib/tk/menubar.py:429
+#: pysollib/tile/menubar.py:430 pysollib/tk/menubar.py:427
msgid "&Hint"
msgstr ""
-#: pysollib/tile/menubar.py:433 pysollib/tk/menubar.py:430
+#: pysollib/tile/menubar.py:431 pysollib/tk/menubar.py:428
msgid "Highlight p&iles"
msgstr ""
-#: pysollib/tile/menubar.py:436 pysollib/tk/menubar.py:433
+#: pysollib/tile/menubar.py:432 pysollib/tk/menubar.py:429
+msgid "&Find card"
+msgstr ""
+
+#: pysollib/tile/menubar.py:434 pysollib/tk/menubar.py:431
msgid "&Demo"
msgstr ""
-#: pysollib/tile/menubar.py:437 pysollib/tk/menubar.py:434
+#: pysollib/tile/menubar.py:435 pysollib/tk/menubar.py:432
msgid "Demo (&all games)"
msgstr ""
-#: pysollib/tile/menubar.py:439 pysollib/tk/menubar.py:436
-msgid "Piles description"
+#: pysollib/tile/menubar.py:437 pysollib/tile/menubar.py:439
+#: pysollib/tk/menubar.py:434 pysollib/tk/menubar.py:436
+msgid "&Solver (experimental)"
msgstr ""
-#: pysollib/tile/menubar.py:443 pysollib/tk/menubar.py:440
-msgid "&Options"
-msgstr ""
-
-#: pysollib/tile/menubar.py:444 pysollib/tk/menubar.py:441
-msgid "&Player options..."
+#: pysollib/tile/menubar.py:441 pysollib/tk/menubar.py:438
+msgid "&Piles description"
msgstr ""
#: pysollib/tile/menubar.py:445 pysollib/tk/menubar.py:442
-msgid "&Automatic play"
+msgid "&Options"
msgstr ""
#: pysollib/tile/menubar.py:446 pysollib/tk/menubar.py:443
-msgid "Auto &face up"
+msgid "&Player options..."
msgstr ""
#: pysollib/tile/menubar.py:447 pysollib/tk/menubar.py:444
-msgid "A&uto drop"
+msgid "&Automatic play"
msgstr ""
#: pysollib/tile/menubar.py:448 pysollib/tk/menubar.py:445
-msgid "Auto &deal"
+msgid "Auto &face up"
+msgstr ""
+
+#: pysollib/tile/menubar.py:449 pysollib/tk/menubar.py:446
+msgid "A&uto drop"
msgstr ""
#: pysollib/tile/menubar.py:450 pysollib/tk/menubar.py:447
-msgid "&Quick play"
-msgstr ""
-
-#: pysollib/tile/menubar.py:451 pysollib/tk/menubar.py:448
-msgid "Assist &level"
+msgid "Auto &deal"
msgstr ""
#: pysollib/tile/menubar.py:452 pysollib/tk/menubar.py:449
-msgid "Enable &undo"
+msgid "&Quick play"
msgstr ""
#: pysollib/tile/menubar.py:453 pysollib/tk/menubar.py:450
-msgid "Enable &bookmarks"
+msgid "Assist &level"
msgstr ""
#: pysollib/tile/menubar.py:454 pysollib/tk/menubar.py:451
-msgid "Enable &hint"
+msgid "Enable &undo"
msgstr ""
#: pysollib/tile/menubar.py:455 pysollib/tk/menubar.py:452
-msgid "Enable highlight p&iles"
+msgid "Enable &bookmarks"
msgstr ""
#: pysollib/tile/menubar.py:456 pysollib/tk/menubar.py:453
-msgid "Enable highlight &cards"
+msgid "Enable &hint"
msgstr ""
#: pysollib/tile/menubar.py:457 pysollib/tk/menubar.py:454
-msgid "Enable highlight same &rank"
+msgid "Enable highlight p&iles"
msgstr ""
#: pysollib/tile/menubar.py:458 pysollib/tk/menubar.py:455
-msgid "Highlight &no matching"
+msgid "Enable highlight &cards"
+msgstr ""
+
+#: pysollib/tile/menubar.py:459 pysollib/tk/menubar.py:456
+msgid "Enable highlight same &rank"
msgstr ""
#: pysollib/tile/menubar.py:460 pysollib/tk/menubar.py:457
+msgid "Highlight &no matching"
+msgstr ""
+
+#: pysollib/tile/menubar.py:462 pysollib/tk/menubar.py:459
msgid "&Show removed tiles (in Mahjongg games)"
msgstr ""
-#: pysollib/tile/menubar.py:461 pysollib/tk/menubar.py:458
+#: pysollib/tile/menubar.py:463 pysollib/tk/menubar.py:460
msgid "Show hint &arrow (in Shisen-Sho games)"
msgstr ""
-#: pysollib/tile/menubar.py:463 pysollib/tk/menubar.py:460
+#: pysollib/tile/menubar.py:465 pysollib/tk/menubar.py:462
msgid "&Sound..."
msgstr ""
-#: pysollib/tile/menubar.py:471 pysollib/tk/menubar.py:468
+#: pysollib/tile/menubar.py:473 pysollib/tk/menubar.py:470
msgid "Cards&et..."
msgstr ""
-#: pysollib/tile/menubar.py:472 pysollib/tk/menubar.py:469
+#: pysollib/tile/menubar.py:474 pysollib/tk/menubar.py:471
msgid "Table t&ile..."
msgstr ""
-#: pysollib/tile/menubar.py:474 pysollib/tk/menubar.py:471
+#: pysollib/tile/menubar.py:476 pysollib/tk/menubar.py:473
msgid "Card &background"
msgstr ""
-#: pysollib/tile/menubar.py:475 pysollib/tk/menubar.py:472
+#: pysollib/tile/menubar.py:477 pysollib/tk/menubar.py:474
msgid "Card &view"
msgstr ""
-#: pysollib/tile/menubar.py:476 pysollib/tk/menubar.py:473
+#: pysollib/tile/menubar.py:478 pysollib/tk/menubar.py:475
msgid "Card shado&w"
msgstr ""
-#: pysollib/tile/menubar.py:477 pysollib/tk/menubar.py:474
+#: pysollib/tile/menubar.py:479 pysollib/tk/menubar.py:476
msgid "Shade &legal moves"
msgstr ""
-#: pysollib/tile/menubar.py:478 pysollib/tk/menubar.py:475
+#: pysollib/tile/menubar.py:480 pysollib/tk/menubar.py:477
msgid "&Negative cards bottom"
msgstr ""
-#: pysollib/tile/menubar.py:479 pysollib/tk/menubar.py:476
+#: pysollib/tile/menubar.py:481 pysollib/tk/menubar.py:478
msgid "Shrink face-down cards"
msgstr ""
-#: pysollib/tile/menubar.py:480 pysollib/tk/menubar.py:477
+#: pysollib/tile/menubar.py:482 pysollib/tk/menubar.py:479
msgid "Shade &filled stacks"
msgstr ""
-#: pysollib/tile/menubar.py:481 pysollib/tk/menubar.py:478
+#: pysollib/tile/menubar.py:483 pysollib/tk/menubar.py:480
msgid "A&nimations"
msgstr ""
-#: pysollib/tile/menubar.py:482 pysollib/tk/menubar.py:479
+#: pysollib/tile/menubar.py:484 pysollib/tk/menubar.py:481
msgid "&None"
msgstr ""
-#: pysollib/tile/menubar.py:483 pysollib/tk/menubar.py:480
-msgid "&Timer based"
-msgstr ""
-
-#: pysollib/tile/menubar.py:484 pysollib/tk/menubar.py:481
-msgid "&Fast"
-msgstr ""
-
#: pysollib/tile/menubar.py:485 pysollib/tk/menubar.py:482
-msgid "&Slow"
+msgid "&Very fast"
msgstr ""
#: pysollib/tile/menubar.py:486 pysollib/tk/menubar.py:483
-msgid "&Very slow"
+msgid "&Fast"
+msgstr ""
+
+#: pysollib/tile/menubar.py:487 pysollib/tk/menubar.py:484
+msgid "&Medium"
msgstr ""
#: pysollib/tile/menubar.py:488 pysollib/tk/menubar.py:485
-msgid "&Redeal animation"
+msgid "&Slow"
msgstr ""
#: pysollib/tile/menubar.py:489 pysollib/tk/menubar.py:486
-msgid "&Winning animation"
-msgstr ""
-
-#: pysollib/tile/menubar.py:490 pysollib/tk/menubar.py:487
-msgid "&Mouse"
+msgid "V&ery slow"
msgstr ""
#: pysollib/tile/menubar.py:491 pysollib/tk/menubar.py:488
-msgid "&Drag-and-Drop"
-msgstr ""
-
-#: pysollib/tile/menubar.py:492 pysollib/tk/menubar.py:489
-msgid "&Point-and-Click"
+msgid "&Redeal animation"
msgstr ""
#: pysollib/tile/menubar.py:493 pysollib/tk/menubar.py:490
-msgid "&Sticky mouse"
+msgid "&Winning animation"
+msgstr ""
+
+#: pysollib/tile/menubar.py:494 pysollib/tk/menubar.py:491
+msgid "&Mouse"
msgstr ""
#: pysollib/tile/menubar.py:495 pysollib/tk/menubar.py:492
-msgid "Use mouse for undo/redo"
+msgid "&Drag-and-Drop"
+msgstr ""
+
+#: pysollib/tile/menubar.py:496 pysollib/tk/menubar.py:493
+msgid "&Point-and-Click"
msgstr ""
#: pysollib/tile/menubar.py:497 pysollib/tk/menubar.py:494
-msgid "&Fonts..."
-msgstr ""
-
-#: pysollib/tile/menubar.py:498 pysollib/tk/menubar.py:495
-msgid "&Colors..."
+msgid "&Sticky mouse"
msgstr ""
#: pysollib/tile/menubar.py:499 pysollib/tk/menubar.py:496
+msgid "Use mouse for undo/redo"
+msgstr ""
+
+#: pysollib/tile/menubar.py:501 pysollib/tk/menubar.py:498
+msgid "&Fonts..."
+msgstr ""
+
+#: pysollib/tile/menubar.py:502 pysollib/tk/menubar.py:499
+msgid "&Colors..."
+msgstr ""
+
+#: pysollib/tile/menubar.py:503 pysollib/tk/menubar.py:500
msgid "Time&outs..."
msgstr ""
-#: pysollib/tile/menubar.py:502 pysollib/tk/menubar.py:498
+#: pysollib/tile/menubar.py:506 pysollib/tk/menubar.py:502
msgid "&Toolbar"
msgstr ""
-#: pysollib/tile/menubar.py:504 pysollib/tk/menubar.py:500
+#: pysollib/tile/menubar.py:508 pysollib/tk/menubar.py:504
msgid "Stat&usbar"
msgstr ""
-#: pysollib/tile/menubar.py:505 pysollib/tk/menubar.py:501
+#: pysollib/tile/menubar.py:509 pysollib/tk/menubar.py:505
msgid "Show &statusbar"
msgstr ""
-#: pysollib/tile/menubar.py:506 pysollib/tk/menubar.py:502
+#: pysollib/tile/menubar.py:510 pysollib/tk/menubar.py:506
msgid "Show &number of cards"
msgstr ""
-#: pysollib/tile/menubar.py:507 pysollib/tk/menubar.py:503
+#: pysollib/tile/menubar.py:511 pysollib/tk/menubar.py:507
msgid "Show &help bar"
msgstr ""
-#: pysollib/tile/menubar.py:508 pysollib/tk/menubar.py:504
+#: pysollib/tile/menubar.py:512 pysollib/tk/menubar.py:508
msgid "Save games &geometry"
msgstr ""
-#: pysollib/tile/menubar.py:509 pysollib/tk/menubar.py:505
+#: pysollib/tile/menubar.py:513 pysollib/tk/menubar.py:509
msgid "&Demo logo"
msgstr ""
-#: pysollib/tile/menubar.py:510 pysollib/tk/menubar.py:506
+#: pysollib/tile/menubar.py:514 pysollib/tk/menubar.py:510
msgid "Startup splash sc&reen"
msgstr ""
-#: pysollib/tile/menubar.py:516 pysollib/tk/menubar.py:512
+#: pysollib/tile/menubar.py:520 pysollib/tk/menubar.py:516
msgid "&Help"
msgstr ""
-#: pysollib/tile/menubar.py:517 pysollib/tk/menubar.py:513
+#: pysollib/tile/menubar.py:521 pysollib/tk/menubar.py:517
msgid "&Contents"
msgstr ""
-#: pysollib/tile/menubar.py:518 pysollib/tk/menubar.py:514
+#: pysollib/tile/menubar.py:522 pysollib/tk/menubar.py:518
msgid "&How to play"
msgstr ""
-#: pysollib/tile/menubar.py:519 pysollib/tk/menubar.py:515
+#: pysollib/tile/menubar.py:523 pysollib/tk/menubar.py:519
msgid "&Rules for this game"
msgstr ""
-#: pysollib/tile/menubar.py:520 pysollib/tk/menubar.py:516
+#: pysollib/tile/menubar.py:524 pysollib/tk/menubar.py:520
msgid "&License terms"
msgstr ""
-#: pysollib/tile/menubar.py:637 pysollib/tk/menubar.py:633
+#: pysollib/tile/menubar.py:641 pysollib/tk/menubar.py:637
msgid "All &games..."
msgstr ""
-#: pysollib/tile/menubar.py:639 pysollib/tk/menubar.py:635
+#: pysollib/tile/menubar.py:643 pysollib/tk/menubar.py:639
msgid "Playable pre&view..."
msgstr ""
-#: pysollib/tile/menubar.py:690 pysollib/tk/menubar.py:686
+#: pysollib/tile/menubar.py:694 pysollib/tk/menubar.py:690
msgid "&Mahjongg games"
msgstr ""
-#: pysollib/tile/menubar.py:728 pysollib/tk/menubar.py:724
+#: pysollib/tile/menubar.py:732 pysollib/tk/menubar.py:728
msgid "&Popular games"
msgstr ""
-#: pysollib/tile/menubar.py:736 pysollib/tk/menubar.py:732
+#: pysollib/tile/menubar.py:740 pysollib/tk/menubar.py:736
msgid "&French games"
msgstr ""
-#: pysollib/tile/menubar.py:743 pysollib/tk/menubar.py:739
+#: pysollib/tile/menubar.py:747 pysollib/tk/menubar.py:743
msgid "&Oriental games"
msgstr ""
-#: pysollib/tile/menubar.py:751 pysollib/tk/menubar.py:747
+#: pysollib/tile/menubar.py:755 pysollib/tk/menubar.py:751
msgid "&Special games"
msgstr ""
-#: pysollib/tile/menubar.py:757 pysollib/tk/menubar.py:753
+#: pysollib/tile/menubar.py:761 pysollib/tk/menubar.py:757
msgid "&All games by name"
msgstr ""
-#: pysollib/tile/menubar.py:1030 pysollib/tk/menubar.py:1026
+#: pysollib/tile/menubar.py:1040 pysollib/tk/menubar.py:1036
#: data/glade-translations:72
msgid "Sound settings"
msgstr ""
-#: pysollib/tile/menubar.py:1138 pysollib/tk/menubar.py:1138
+#: pysollib/tile/menubar.py:1148 pysollib/tk/menubar.py:1148
msgid "Select "
msgstr ""
-#: pysollib/tile/menubar.py:1185 pysollib/tk/menubar.py:1190
+#: pysollib/tile/menubar.py:1194 pysollib/tk/menubar.py:1195
msgid "Select table background"
msgstr ""
-#: pysollib/tile/menubar.py:1329
+#: pysollib/tile/menubar.py:1339
msgid "Change theme"
msgstr ""
-#: pysollib/tile/menubar.py:1330
+#: pysollib/tile/menubar.py:1340
msgid ""
"This settings will take effect\n"
"the next time you restart "
msgstr ""
-#: pysollib/tile/menubar.py:1338
+#: pysollib/tile/menubar.py:1347
msgid "Set t&heme"
msgstr ""
-#: pysollib/tile/menubar.py:1344
+#: pysollib/tile/menubar.py:1353
msgid "Default"
msgstr ""
-#: pysollib/tile/menubar.py:1345
+#: pysollib/tile/menubar.py:1354
msgid "Classic"
msgstr ""
-#: pysollib/tile/menubar.py:1346
+#: pysollib/tile/menubar.py:1355
msgid "Revitalized"
msgstr ""
-#: pysollib/tile/menubar.py:1347
+#: pysollib/tile/menubar.py:1356
msgid "Windows native"
msgstr ""
-#: pysollib/tile/menubar.py:1348
+#: pysollib/tile/menubar.py:1357
msgid "XP Native"
msgstr ""
-#: pysollib/tile/menubar.py:1349
+#: pysollib/tile/menubar.py:1358
msgid "Aqua"
msgstr ""
@@ -2604,12 +2634,12 @@ msgstr ""
msgid "XLarge cardsets"
msgstr ""
-#: pysollib/tile/selectcardset.py:236 pysollib/tk/menubar.py:1135
+#: pysollib/tile/selectcardset.py:236 pysollib/tk/menubar.py:1145
msgid "&Info..."
msgstr ""
-#: pysollib/tile/selectcardset.py:237 pysollib/tk/menubar.py:1133
-#: pysollib/tk/menubar.py:1135 pysollib/tk/selectcardset.py:241
+#: pysollib/tile/selectcardset.py:237 pysollib/tk/menubar.py:1143
+#: pysollib/tk/menubar.py:1145 pysollib/tk/selectcardset.py:241
msgid "&Load"
msgstr ""
@@ -2855,28 +2885,28 @@ msgstr ""
msgid "Played:"
msgstr ""
-#: pysollib/tile/selectgame.py:361 pysollib/tile/tkstats.py:107
-#: pysollib/tile/tkstats.py:159 pysollib/tk/selectgame.py:371
-#: pysollib/tk/tkstats.py:111 pysollib/tk/tkstats.py:163
+#: pysollib/tile/selectgame.py:361 pysollib/tile/tkstats.py:109
+#: pysollib/tile/tkstats.py:161 pysollib/tk/selectgame.py:371
+#: pysollib/tk/tkstats.py:113 pysollib/tk/tkstats.py:165
#: data/glade-translations:9 data/glade-translations:13
msgid "Won:"
msgstr ""
-#: pysollib/tile/selectgame.py:362 pysollib/tile/tkstats.py:108
-#: pysollib/tile/tkstats.py:160 pysollib/tk/selectgame.py:372
-#: pysollib/tk/tkstats.py:112 pysollib/tk/tkstats.py:164
+#: pysollib/tile/selectgame.py:362 pysollib/tile/tkstats.py:110
+#: pysollib/tile/tkstats.py:162 pysollib/tk/selectgame.py:372
+#: pysollib/tk/tkstats.py:114 pysollib/tk/tkstats.py:166
#: data/glade-translations:11 data/glade-translations:14
msgid "Lost:"
msgstr ""
-#: pysollib/tile/selectgame.py:363 pysollib/tile/tkstats.py:622
-#: pysollib/tk/selectgame.py:373 pysollib/tk/tkstats.py:738
+#: pysollib/tile/selectgame.py:363 pysollib/tile/tkstats.py:626
+#: pysollib/tk/selectgame.py:373 pysollib/tk/tkstats.py:740
#: data/glade-translations:18
msgid "Playing time:"
msgstr ""
-#: pysollib/tile/selectgame.py:364 pysollib/tile/tkstats.py:629
-#: pysollib/tk/selectgame.py:374 pysollib/tk/tkstats.py:745
+#: pysollib/tile/selectgame.py:364 pysollib/tile/tkstats.py:633
+#: pysollib/tk/selectgame.py:374 pysollib/tk/tkstats.py:747
#: data/glade-translations:19
msgid "Moves:"
msgstr ""
@@ -2933,6 +2963,71 @@ msgstr ""
msgid "Select table color"
msgstr ""
+#: pysollib/tile/solverdialog.py:79 pysollib/tk/solverdialog.py:78
+#: data/glade-translations:8 data/glade-translations:28
+msgid "Game:"
+msgstr ""
+
+#: pysollib/tile/solverdialog.py:98 pysollib/tk/solverdialog.py:101
+msgid "Solving method:"
+msgstr ""
+
+#: pysollib/tile/solverdialog.py:111 pysollib/tk/solverdialog.py:114
+msgid "Preset:"
+msgstr ""
+
+#: pysollib/tile/solverdialog.py:135 pysollib/tk/solverdialog.py:138
+msgid "Max iterations:"
+msgstr ""
+
+#: pysollib/tile/solverdialog.py:145 pysollib/tk/solverdialog.py:148
+msgid "Max depth:"
+msgstr ""
+
+#: pysollib/tile/solverdialog.py:156 pysollib/tk/solverdialog.py:159
+msgid "Show progress"
+msgstr ""
+
+#: pysollib/tile/solverdialog.py:160 pysollib/tk/solverdialog.py:163
+msgid "Progress"
+msgstr ""
+
+#: pysollib/tile/solverdialog.py:167 pysollib/tk/solverdialog.py:170
+msgid "Iteration:"
+msgstr ""
+
+#: pysollib/tile/solverdialog.py:173 pysollib/tk/solverdialog.py:176
+msgid "Depth:"
+msgstr ""
+
+#: pysollib/tile/solverdialog.py:179 pysollib/tk/solverdialog.py:182
+msgid "Stored-States:"
+msgstr ""
+
+#: pysollib/tile/solverdialog.py:202 pysollib/tk/solverdialog.py:205
+msgid "&Close"
+msgstr ""
+
+#: pysollib/tile/solverdialog.py:202 pysollib/tk/solverdialog.py:205
+msgid "&New"
+msgstr ""
+
+#: pysollib/tile/solverdialog.py:202 pysollib/tk/solverdialog.py:205
+msgid "&Play"
+msgstr ""
+
+#: pysollib/tile/solverdialog.py:202 pysollib/tk/solverdialog.py:205
+msgid "&Start"
+msgstr ""
+
+#: pysollib/tile/solverdialog.py:272 pysollib/tk/solverdialog.py:275
+msgid "This game is solveable in %s moves."
+msgstr ""
+
+#: pysollib/tile/solverdialog.py:275 pysollib/tk/solverdialog.py:278
+msgid "I could not solve this game."
+msgstr ""
+
#: pysollib/tile/soundoptionsdialog.py:77 pysollib/tk/soundoptionsdialog.py:75
msgid "Are You Sure"
msgstr ""
@@ -3050,11 +3145,11 @@ msgid ""
"the next time you restart "
msgstr ""
-#: pysollib/tile/statusbar.py:148 pysollib/tk/statusbar.py:159
+#: pysollib/tile/statusbar.py:151 pysollib/tk/statusbar.py:162
msgid "Moves/Total moves"
msgstr ""
-#: pysollib/tile/statusbar.py:150 pysollib/tk/statusbar.py:161
+#: pysollib/tile/statusbar.py:153 pysollib/tk/statusbar.py:164
msgid "Games played: won/lost"
msgstr ""
@@ -3130,172 +3225,217 @@ msgstr ""
msgid "Unable to service request:\n"
msgstr ""
-#: pysollib/tile/tkstats.py:79 pysollib/tk/tkstats.py:78
+#: pysollib/tile/tkstats.py:81 pysollib/tk/tkstats.py:80
msgid "Demo games"
msgstr ""
-#: pysollib/tile/tkstats.py:91 pysollib/tk/tkstats.py:95
+#: pysollib/tile/tkstats.py:93 pysollib/tk/tkstats.py:97
#: data/glade-translations:16
msgid "Total"
msgstr ""
-#: pysollib/tile/tkstats.py:93 pysollib/tk/tkstats.py:97
+#: pysollib/tile/tkstats.py:95 pysollib/tk/tkstats.py:99
#: data/glade-translations:12
msgid "Current session"
msgstr ""
-#: pysollib/tile/tkstats.py:109 pysollib/tile/tkstats.py:161
-#: pysollib/tk/tkstats.py:113 pysollib/tk/tkstats.py:165
+#: pysollib/tile/tkstats.py:111 pysollib/tile/tkstats.py:163
+#: pysollib/tk/tkstats.py:115 pysollib/tk/tkstats.py:167
#: data/glade-translations:10 data/glade-translations:15
msgid "Total:"
msgstr ""
-#: pysollib/tile/tkstats.py:204 pysollib/tk/tkstats.py:278
+#: pysollib/tile/tkstats.py:206 pysollib/tk/tkstats.py:280
msgid "No games"
msgstr ""
-#: pysollib/tile/tkstats.py:215 pysollib/tk/tkstats.py:289
+#: pysollib/tile/tkstats.py:217 pysollib/tk/tkstats.py:291
msgid "&All games..."
msgstr ""
-#: pysollib/tile/tkstats.py:217 pysollib/tk/tkstats.py:291
+#: pysollib/tile/tkstats.py:219 pysollib/tk/tkstats.py:293
msgid "&Reset..."
msgstr ""
-#: pysollib/tile/tkstats.py:383
+#: pysollib/tile/tkstats.py:387
msgid "&Play this game"
msgstr ""
-#: pysollib/tile/tkstats.py:385 pysollib/tile/tkstats.py:458
-#: pysollib/tile/tkstats.py:481 pysollib/tk/tkstats.py:515
-#: pysollib/tk/tkstats.py:581 pysollib/tk/tkstats.py:596
+#: pysollib/tile/tkstats.py:389 pysollib/tile/tkstats.py:462
+#: pysollib/tile/tkstats.py:485 pysollib/tk/tkstats.py:517
+#: pysollib/tk/tkstats.py:583 pysollib/tk/tkstats.py:598
msgid "&Save to file"
msgstr ""
-#: pysollib/tile/tkstats.py:386 pysollib/tk/tkstats.py:516
+#: pysollib/tile/tkstats.py:390 pysollib/tk/tkstats.py:518
msgid "&Reset all..."
msgstr ""
-#: pysollib/tile/tkstats.py:457 pysollib/tk/tkstats.py:581
+#: pysollib/tile/tkstats.py:461 pysollib/tk/tkstats.py:583
msgid "Session &log..."
msgstr ""
-#: pysollib/tile/tkstats.py:480 pysollib/tk/tkstats.py:596
+#: pysollib/tile/tkstats.py:484 pysollib/tk/tkstats.py:598
msgid "&Full log..."
msgstr ""
-#: pysollib/tile/tkstats.py:496 pysollib/tk/tkstats.py:611
+#: pysollib/tile/tkstats.py:500 pysollib/tk/tkstats.py:613
msgid "Highlight piles: "
msgstr ""
-#: pysollib/tile/tkstats.py:497 pysollib/tk/tkstats.py:612
+#: pysollib/tile/tkstats.py:501 pysollib/tk/tkstats.py:614
msgid "Highlight cards: "
msgstr ""
-#: pysollib/tile/tkstats.py:498 pysollib/tk/tkstats.py:613
+#: pysollib/tile/tkstats.py:502 pysollib/tk/tkstats.py:615
msgid "Highlight same rank: "
msgstr ""
-#: pysollib/tile/tkstats.py:501 pysollib/tk/tkstats.py:616
+#: pysollib/tile/tkstats.py:505 pysollib/tk/tkstats.py:618
msgid ""
"\n"
"Redeals: "
msgstr ""
-#: pysollib/tile/tkstats.py:502 pysollib/tk/tkstats.py:617
+#: pysollib/tile/tkstats.py:506 pysollib/tk/tkstats.py:619
msgid ""
"\n"
"Cards in Talon: "
msgstr ""
-#: pysollib/tile/tkstats.py:504 pysollib/tk/tkstats.py:619
+#: pysollib/tile/tkstats.py:508 pysollib/tk/tkstats.py:621
msgid ""
"\n"
"Cards in Waste: "
msgstr ""
-#: pysollib/tile/tkstats.py:506 pysollib/tk/tkstats.py:621
+#: pysollib/tile/tkstats.py:510 pysollib/tk/tkstats.py:623
msgid ""
"\n"
"Cards in Foundations: "
msgstr ""
-#: pysollib/tile/tkstats.py:509 pysollib/tk/tkstats.py:624
+#: pysollib/tile/tkstats.py:513 pysollib/tk/tkstats.py:626
msgid "Game status"
msgstr ""
-#: pysollib/tile/tkstats.py:512 pysollib/tk/tkstats.py:627
+#: pysollib/tile/tkstats.py:516 pysollib/tk/tkstats.py:629
msgid "Playing time: "
msgstr ""
-#: pysollib/tile/tkstats.py:513 pysollib/tk/tkstats.py:628
+#: pysollib/tile/tkstats.py:517 pysollib/tk/tkstats.py:630
msgid "Started at: "
msgstr ""
-#: pysollib/tile/tkstats.py:514 pysollib/tk/tkstats.py:629
+#: pysollib/tile/tkstats.py:518 pysollib/tk/tkstats.py:631
msgid "Moves: "
msgstr ""
-#: pysollib/tile/tkstats.py:515 pysollib/tk/tkstats.py:630
+#: pysollib/tile/tkstats.py:519 pysollib/tk/tkstats.py:632
msgid "Undo moves: "
msgstr ""
-#: pysollib/tile/tkstats.py:516 pysollib/tk/tkstats.py:631
+#: pysollib/tile/tkstats.py:520 pysollib/tk/tkstats.py:633
msgid "Bookmark moves: "
msgstr ""
-#: pysollib/tile/tkstats.py:517 pysollib/tk/tkstats.py:632
+#: pysollib/tile/tkstats.py:521 pysollib/tk/tkstats.py:634
msgid "Demo moves: "
msgstr ""
-#: pysollib/tile/tkstats.py:518 pysollib/tk/tkstats.py:633
+#: pysollib/tile/tkstats.py:522 pysollib/tk/tkstats.py:635
msgid "Total player moves: "
msgstr ""
-#: pysollib/tile/tkstats.py:519 pysollib/tk/tkstats.py:634
+#: pysollib/tile/tkstats.py:523 pysollib/tk/tkstats.py:636
msgid "Total moves in this game: "
msgstr ""
-#: pysollib/tile/tkstats.py:520 pysollib/tk/tkstats.py:635
+#: pysollib/tile/tkstats.py:524 pysollib/tk/tkstats.py:637
msgid "Hints: "
msgstr ""
-#: pysollib/tile/tkstats.py:524 pysollib/tk/tkstats.py:639
+#: pysollib/tile/tkstats.py:528 pysollib/tk/tkstats.py:641
msgid "&Statistics..."
msgstr ""
-#: pysollib/tile/tkstats.py:549 pysollib/tk/tkstats.py:665
+#: pysollib/tile/tkstats.py:553 pysollib/tk/tkstats.py:667
msgid "N"
msgstr ""
-#: pysollib/tile/tkstats.py:558 pysollib/tk/tkstats.py:674
+#: pysollib/tile/tkstats.py:562 pysollib/tk/tkstats.py:676
msgid "Result"
msgstr ""
-#: pysollib/tile/tkstats.py:614 pysollib/tk/tkstats.py:730
+#: pysollib/tile/tkstats.py:618 pysollib/tk/tkstats.py:732
#: data/glade-translations:21
msgid "Minimum"
msgstr ""
-#: pysollib/tile/tkstats.py:615 pysollib/tk/tkstats.py:731
+#: pysollib/tile/tkstats.py:619 pysollib/tk/tkstats.py:733
#: data/glade-translations:22
msgid "Maximum"
msgstr ""
-#: pysollib/tile/tkstats.py:616 pysollib/tk/tkstats.py:732
+#: pysollib/tile/tkstats.py:620 pysollib/tk/tkstats.py:734
#: data/glade-translations:23
msgid "Average"
msgstr ""
-#: pysollib/tile/tkstats.py:636 pysollib/tk/tkstats.py:752
+#: pysollib/tile/tkstats.py:640 pysollib/tk/tkstats.py:754
#: data/glade-translations:20
msgid "Total moves:"
msgstr ""
-#: pysollib/tile/tkstats.py:667 pysollib/tk/tkstats.py:783
+#: pysollib/tile/tkstats.py:671 pysollib/tk/tkstats.py:785
msgid "No TOP for this game"
msgstr ""
+#: pysollib/tile/tkstats.py:743 pysollib/tile/tkstats.py:759
+#: pysollib/tile/tkstats.py:859 pysollib/tk/tkstats.py:857
+#: pysollib/tk/tkstats.py:873 pysollib/tk/tkstats.py:985
+msgid "Games/day"
+msgstr ""
+
+#: pysollib/tile/tkstats.py:744 pysollib/tile/tkstats.py:861
+#: pysollib/tk/tkstats.py:858 pysollib/tk/tkstats.py:987
+msgid "Games/week"
+msgstr ""
+
+#: pysollib/tile/tkstats.py:789 pysollib/tk/tkstats.py:903
+#: data/glade-translations:30
+msgid "All games"
+msgstr ""
+
+#: pysollib/tile/tkstats.py:794 pysollib/tk/tkstats.py:909
+#: data/glade-translations:17
+msgid "Current game"
+msgstr ""
+
+#: pysollib/tile/tkstats.py:799 pysollib/tk/tkstats.py:916
+msgid "Statistics for"
+msgstr ""
+
+#: pysollib/tile/tkstats.py:804 pysollib/tk/tkstats.py:923
+msgid "Last 7 days"
+msgstr ""
+
+#: pysollib/tile/tkstats.py:805 pysollib/tk/tkstats.py:924
+msgid "Last month"
+msgstr ""
+
+#: pysollib/tile/tkstats.py:806 pysollib/tk/tkstats.py:925
+msgid "Last year"
+msgstr ""
+
+#: pysollib/tile/tkstats.py:807 pysollib/tk/tkstats.py:926
+msgid "All time"
+msgstr ""
+
+#: pysollib/tile/tkstats.py:813 pysollib/tk/tkstats.py:934
+msgid "Show graphs"
+msgstr ""
+
#: pysollib/tile/toolbar.py:189 pysollib/tk/toolbar.py:192
msgid "New"
msgstr ""
@@ -3372,7 +3512,7 @@ msgstr ""
msgid "Player options"
msgstr ""
-#: pysollib/tile/toolbar.py:390 pysollib/tk/toolbar.py:431
+#: pysollib/tile/toolbar.py:395 pysollib/tk/toolbar.py:436
msgid "Toolbar"
msgstr ""
@@ -3420,14 +3560,6 @@ msgstr ""
msgid "Game Statistics"
msgstr ""
-#: data/glade-translations:8 data/glade-translations:28
-msgid "Game:"
-msgstr ""
-
-#: data/glade-translations:17
-msgid "Current game"
-msgstr ""
-
#: data/glade-translations:24
msgid "Summary"
msgstr ""
@@ -3436,10 +3568,6 @@ msgstr ""
msgid "Total moves"
msgstr ""
-#: data/glade-translations:30
-msgid "All games"
-msgstr ""
-
#: data/glade-translations:57
msgid "Set font"
msgstr ""
diff --git a/po/ru_games.po b/po/ru_games.po
index 663b53a5..b3ce0e96 100644
--- a/po/ru_games.po
+++ b/po/ru_games.po
@@ -5,8 +5,8 @@
msgid ""
msgstr ""
"Project-Id-Version: PySol 0.0.1\n"
-"POT-Creation-Date: Fri Jan 12 13:34:09 2007\n"
-"PO-Revision-Date: 2007-01-17 19:12+0300\n"
+"POT-Creation-Date: Thu Feb 15 18:35:01 2007\n"
+"PO-Revision-Date: 2007-02-12 19:08+0300\n"
"Last-Translator: Скоморох \n"
"Language-Team: Russian \n"
"MIME-Version: 1.0\n"
@@ -217,6 +217,10 @@ msgstr "Баларама"
msgid "Baroness"
msgstr "Баронесса"
+#, fuzzy
+msgid "Barrier"
+msgstr "Причудливый"
+
msgid "Bastille Day"
msgstr "День Бастилии"
@@ -244,6 +248,9 @@ msgstr "Клюв и ласты"
msgid "Beatle"
msgstr "Жук"
+msgid "Bebop"
+msgstr ""
+
msgid "Beetle"
msgstr "Жук"
@@ -3772,6 +3779,10 @@ msgstr "Фаворит Вашингтона"
msgid "Wasp"
msgstr "Оса"
+#, fuzzy
+msgid "Waterfall"
+msgstr "Ватерлоо"
+
msgid "Waterloo"
msgstr "Ватерлоо"
diff --git a/po/ru_pysol.po b/po/ru_pysol.po
index a3430288..303fe335 100644
--- a/po/ru_pysol.po
+++ b/po/ru_pysol.po
@@ -5,8 +5,8 @@
msgid ""
msgstr ""
"Project-Id-Version: PySol 0.0.1\n"
-"POT-Creation-Date: Fri Jan 12 13:35:13 2007\n"
-"PO-Revision-Date: 2007-01-12 13:44+0300\n"
+"POT-Creation-Date: Thu Feb 15 18:36:07 2007\n"
+"PO-Revision-Date: 2007-02-12 19:31+0300\n"
"Last-Translator: Скоморох \n"
"Language-Team: Russian \n"
"MIME-Version: 1.0\n"
@@ -14,34 +14,34 @@ msgstr ""
"Content-Transfer-Encoding: utf-8\n"
"Generated-By: pygettext.py 1.5\n"
-#: pysollib/actions.py:257 pysollib/tile/toolbar.py:189
+#: pysollib/actions.py:259 pysollib/tile/toolbar.py:189
#: pysollib/tk/toolbar.py:192
msgid "New game"
msgstr "Новая игра"
-#: pysollib/actions.py:270 pysollib/tile/menubar.py:822
-#: pysollib/tile/menubar.py:836 pysollib/tk/menubar.py:818
-#: pysollib/tk/menubar.py:832
+#: pysollib/actions.py:272 pysollib/tile/menubar.py:832
+#: pysollib/tile/menubar.py:846 pysollib/tk/menubar.py:828
+#: pysollib/tk/menubar.py:842
msgid "Select game"
msgstr "Выбрать игру"
-#: pysollib/actions.py:284
+#: pysollib/actions.py:286
msgid "Invalid game number"
msgstr "Неправильный номер игры"
-#: pysollib/actions.py:285
+#: pysollib/actions.py:287
msgid "Invalid game number\n"
msgstr "Неправильный номер игры\n"
-#: pysollib/actions.py:302
+#: pysollib/actions.py:304
msgid "Select next game number"
msgstr "Выберите номер следующей игры"
-#: pysollib/actions.py:311 pysollib/actions.py:321
+#: pysollib/actions.py:313 pysollib/actions.py:323
msgid "Select new game number"
msgstr "Выберите номер новой игры"
-#: pysollib/actions.py:312
+#: pysollib/actions.py:314
msgid ""
"\n"
"\n"
@@ -51,50 +51,51 @@ msgstr ""
"\n"
"Введите номер новой игры"
-#: pysollib/actions.py:313
+#: pysollib/actions.py:315
msgid "&Next number"
msgstr "&Следующий номер"
-#: pysollib/actions.py:313 pysollib/app.py:886 pysollib/app.py:1169
-#: pysollib/app.py:1181 pysollib/game.py:950 pysollib/game.py:2048
-#: pysollib/tile/colorsdialog.py:123 pysollib/tile/edittextdialog.py:83
+#: pysollib/actions.py:315 pysollib/app.py:891 pysollib/app.py:1178
+#: pysollib/app.py:1190 pysollib/game.py:1010 pysollib/game.py:2245
+#: pysollib/tile/colorsdialog.py:112 pysollib/tile/edittextdialog.py:83
#: pysollib/tile/fontsdialog.py:144 pysollib/tile/fontsdialog.py:204
-#: pysollib/tile/gameinfodialog.py:155 pysollib/tile/menubar.py:1334
+#: pysollib/tile/gameinfodialog.py:155 pysollib/tile/menubar.py:1344
#: pysollib/tile/playeroptionsdialog.py:111 pysollib/tile/selectcardset.py:387
#: pysollib/tile/selecttile.py:161 pysollib/tile/soundoptionsdialog.py:168
#: pysollib/tile/soundoptionsdialog.py:206 pysollib/tile/timeoutsdialog.py:94
-#: pysollib/tile/tkhtml.py:501 pysollib/tile/tkstats.py:214
-#: pysollib/tile/tkstats.py:384 pysollib/tile/tkstats.py:457
-#: pysollib/tile/tkstats.py:480 pysollib/tile/tkstats.py:523
-#: pysollib/tile/tkstats.py:594 pysollib/tile/tkstats.py:678
-#: pysollib/tile/tkwidget.py:154 pysollib/tile/tkwidget.py:329
-#: pysollib/tk/colorsdialog.py:122 pysollib/tk/edittextdialog.py:82
-#: pysollib/tk/fontsdialog.py:143 pysollib/tk/fontsdialog.py:205
-#: pysollib/tk/gameinfodialog.py:155 pysollib/tk/playeroptionsdialog.py:85
+#: pysollib/tile/tkhtml.py:501 pysollib/tile/tkstats.py:216
+#: pysollib/tile/tkstats.py:388 pysollib/tile/tkstats.py:461
+#: pysollib/tile/tkstats.py:484 pysollib/tile/tkstats.py:527
+#: pysollib/tile/tkstats.py:598 pysollib/tile/tkstats.py:682
+#: pysollib/tile/tkstats.py:844 pysollib/tile/tkwidget.py:154
+#: pysollib/tile/tkwidget.py:329 pysollib/tk/colorsdialog.py:111
+#: pysollib/tk/edittextdialog.py:82 pysollib/tk/fontsdialog.py:143
+#: pysollib/tk/fontsdialog.py:205 pysollib/tk/gameinfodialog.py:155
+#: pysollib/tk/playeroptionsdialog.py:85
#: pysollib/tk/playeroptionsdialog.py:160 pysollib/tk/selectcardset.py:241
#: pysollib/tk/selectcardset.py:397 pysollib/tk/selecttile.py:159
#: pysollib/tk/soundoptionsdialog.py:170 pysollib/tk/soundoptionsdialog.py:211
#: pysollib/tk/timeoutsdialog.py:92 pysollib/tk/tkhtml.py:500
-#: pysollib/tk/tkstats.py:288 pysollib/tk/tkstats.py:514
-#: pysollib/tk/tkstats.py:581 pysollib/tk/tkstats.py:596
-#: pysollib/tk/tkstats.py:638 pysollib/tk/tkstats.py:710
-#: pysollib/tk/tkstats.py:794 pysollib/tk/tkwidget.py:160
-#: pysollib/tk/tkwidget.py:324
+#: pysollib/tk/tkstats.py:290 pysollib/tk/tkstats.py:516
+#: pysollib/tk/tkstats.py:583 pysollib/tk/tkstats.py:598
+#: pysollib/tk/tkstats.py:640 pysollib/tk/tkstats.py:712
+#: pysollib/tk/tkstats.py:796 pysollib/tk/tkstats.py:970
+#: pysollib/tk/tkwidget.py:160 pysollib/tk/tkwidget.py:324
msgid "&OK"
msgstr "&ОК"
-#: pysollib/actions.py:313 pysollib/app.py:887 pysollib/app.py:1181
-#: pysollib/game.py:950 pysollib/game.py:1502 pysollib/game.py:1518
-#: pysollib/game.py:1525 pysollib/game.py:1531
-#: pysollib/tile/colorsdialog.py:123 pysollib/tile/edittextdialog.py:83
+#: pysollib/actions.py:315 pysollib/app.py:892 pysollib/app.py:1190
+#: pysollib/game.py:1010 pysollib/game.py:1674 pysollib/game.py:1690
+#: pysollib/game.py:1697 pysollib/game.py:1703
+#: pysollib/tile/colorsdialog.py:112 pysollib/tile/edittextdialog.py:83
#: pysollib/tile/fontsdialog.py:144 pysollib/tile/fontsdialog.py:204
#: pysollib/tile/playeroptionsdialog.py:111 pysollib/tile/selectcardset.py:237
#: pysollib/tile/selectgame.py:267 pysollib/tile/selectgame.py:398
#: pysollib/tile/selecttile.py:161 pysollib/tile/soundoptionsdialog.py:168
#: pysollib/tile/timeoutsdialog.py:94 pysollib/tile/tkwidget.py:329
-#: pysollib/tk/colorsdialog.py:122 pysollib/tk/edittextdialog.py:82
+#: pysollib/tk/colorsdialog.py:111 pysollib/tk/edittextdialog.py:82
#: pysollib/tk/fontsdialog.py:143 pysollib/tk/fontsdialog.py:205
-#: pysollib/tk/menubar.py:1133 pysollib/tk/menubar.py:1135
+#: pysollib/tk/menubar.py:1143 pysollib/tk/menubar.py:1145
#: pysollib/tk/playeroptionsdialog.py:85
#: pysollib/tk/playeroptionsdialog.py:160 pysollib/tk/selectcardset.py:241
#: pysollib/tk/selectgame.py:266 pysollib/tk/selectgame.py:407
@@ -103,36 +104,36 @@ msgstr "&ОК"
msgid "&Cancel"
msgstr "От&мена"
-#: pysollib/actions.py:329
+#: pysollib/actions.py:331
msgid "Select random game"
msgstr "Выбор случайной игры"
-#: pysollib/actions.py:365
+#: pysollib/actions.py:367
msgid "Select next game"
msgstr "Выбрать следующую игру"
-#: pysollib/actions.py:398 pysollib/tile/toolbar.py:203
+#: pysollib/actions.py:400 pysollib/tile/toolbar.py:203
#: pysollib/tk/toolbar.py:206
msgid "Quit "
msgstr "Выйти из "
-#: pysollib/actions.py:449
+#: pysollib/actions.py:451
msgid "Clear bookmarks"
msgstr "Удалить закладки"
-#: pysollib/actions.py:450
+#: pysollib/actions.py:452
msgid "Clear all bookmarks ?"
msgstr "Удалить все закладки?"
-#: pysollib/actions.py:460
+#: pysollib/actions.py:462
msgid "Restart game"
msgstr "Начать игру с начала"
-#: pysollib/actions.py:461
+#: pysollib/actions.py:463
msgid "Restart this game ?"
msgstr "Начать игру с начала?"
-#: pysollib/actions.py:502
+#: pysollib/actions.py:509
msgid ""
"Comments for %s:\n"
"\n"
@@ -140,19 +141,19 @@ msgstr ""
"Комментарий для %s:\n"
"\n"
-#: pysollib/actions.py:504
+#: pysollib/actions.py:511
msgid "Comments for "
msgstr "Комментарий для "
-#: pysollib/actions.py:522 pysollib/actions.py:550
+#: pysollib/actions.py:529 pysollib/actions.py:557
msgid "Error while writing to file"
msgstr "Ошибка при записи в файл"
-#: pysollib/actions.py:525 pysollib/actions.py:553
+#: pysollib/actions.py:532 pysollib/actions.py:560
msgid " Info"
msgstr " Информация"
-#: pysollib/actions.py:526
+#: pysollib/actions.py:533
msgid ""
"Comments were appended to\n"
"\n"
@@ -160,15 +161,15 @@ msgstr ""
"Комментарий добавлен в файл\n"
"\n"
-#: pysollib/actions.py:537
+#: pysollib/actions.py:544
msgid "Demo statistics"
msgstr "Статистика демо"
-#: pysollib/actions.py:540
+#: pysollib/actions.py:547
msgid "Your statistics"
msgstr "Ваша статистика"
-#: pysollib/actions.py:554
+#: pysollib/actions.py:561
msgid ""
" were appended to\n"
"\n"
@@ -176,45 +177,49 @@ msgstr ""
" добавлена в файл\n"
"\n"
-#: pysollib/actions.py:569
+#: pysollib/actions.py:576
msgid " Demo"
msgstr " Демо"
-#: pysollib/actions.py:569
+#: pysollib/actions.py:576
msgid " Demo "
msgstr " Демо "
-#: pysollib/actions.py:572 pysollib/actions.py:591
+#: pysollib/actions.py:579 pysollib/actions.py:598
msgid " for "
msgstr " для "
-#: pysollib/actions.py:578 pysollib/stats.py:205
+#: pysollib/actions.py:585 pysollib/stats.py:205
msgid "Statistics for "
msgstr "Статистика игры "
-#: pysollib/actions.py:581 pysollib/tile/selectgame.py:345
+#: pysollib/actions.py:588 pysollib/tile/selectgame.py:345
#: pysollib/tile/toolbar.py:200 pysollib/tk/selectgame.py:350
#: pysollib/tk/toolbar.py:203
msgid "Statistics"
msgstr "Статистика"
-#: pysollib/actions.py:585 data/glade-translations:31
+#: pysollib/actions.py:592 data/glade-translations:31
msgid "Full log"
msgstr "Полный лог"
-#: pysollib/actions.py:588 data/glade-translations:32
+#: pysollib/actions.py:595 data/glade-translations:32
msgid "Session log"
msgstr "Лог сессии"
-#: pysollib/actions.py:594
+#: pysollib/actions.py:601
msgid "Game Info"
msgstr "Информация об игре"
-#: pysollib/actions.py:610
+#: pysollib/actions.py:604
+msgid "Statistics progression"
+msgstr "Прогресс статистики"
+
+#: pysollib/actions.py:620
msgid "Reset all statistics"
msgstr "Очистить всю статистику"
-#: pysollib/actions.py:611
+#: pysollib/actions.py:621
msgid ""
"Reset ALL statistics and logs for player\n"
"%s ?"
@@ -222,11 +227,11 @@ msgstr ""
"Очистить всю статистику и лог для игрока\n"
"%s?"
-#: pysollib/actions.py:617
+#: pysollib/actions.py:627
msgid "Reset game statistics"
msgstr "Очистить статистику игры"
-#: pysollib/actions.py:618
+#: pysollib/actions.py:628
msgid ""
"Reset statistics and logs for player\n"
"%s\n"
@@ -238,53 +243,53 @@ msgstr ""
"и игры\n"
"%s?"
-#: pysollib/actions.py:674
+#: pysollib/actions.py:684
msgid "Play demo"
msgstr "Показать демо"
-#: pysollib/actions.py:685
+#: pysollib/actions.py:695
msgid "Set player options"
msgstr "Установить настройки игрока"
-#: pysollib/actions.py:699 data/glade-translations:40
+#: pysollib/actions.py:709 data/glade-translations:40
msgid "Set colors"
msgstr "Настроить цвета"
-#: pysollib/actions.py:719
+#: pysollib/actions.py:726
msgid "Set fonts"
msgstr "Настроить шрифт"
-#: pysollib/actions.py:728 data/glade-translations:33
+#: pysollib/actions.py:735 data/glade-translations:33
msgid "Set timeouts"
msgstr "Настроить таймауты"
-#: pysollib/app.py:87
+#: pysollib/app.py:90
msgid "Unknown"
msgstr "Неизвестный"
-#: pysollib/app.py:888 pysollib/game.py:1502 pysollib/game.py:1518
-#: pysollib/game.py:1525 pysollib/game.py:1531 pysollib/tile/menubar.py:361
-#: pysollib/tk/menubar.py:358
+#: pysollib/app.py:893 pysollib/game.py:1674 pysollib/game.py:1690
+#: pysollib/game.py:1697 pysollib/game.py:1703 pysollib/tile/menubar.py:358
+#: pysollib/tk/menubar.py:355
msgid "&New game"
msgstr "&Новая игра"
-#: pysollib/app.py:1031
+#: pysollib/app.py:1036
msgid "Loading %s %s..."
msgstr "Загружается %s %s..."
-#: pysollib/app.py:1066
+#: pysollib/app.py:1075
msgid " load error"
msgstr " ошибка при загрузке"
-#: pysollib/app.py:1067
+#: pysollib/app.py:1076
msgid "Error while loading "
msgstr "Ошибка при загрузке"
-#: pysollib/app.py:1161
+#: pysollib/app.py:1170
msgid "Incompatible "
msgstr "Несовместимый "
-#: pysollib/app.py:1163
+#: pysollib/app.py:1172
msgid ""
"The currently selected %s %s\n"
"is not compatible with the game\n"
@@ -298,19 +303,19 @@ msgstr ""
"\n"
"Необходимо выбрать %s типа %s.\n"
-#: pysollib/app.py:1179
+#: pysollib/app.py:1188
msgid "Please select a %s type %s"
msgstr "Выберите %s типа %s"
-#: pysollib/game.py:869 pysollib/game.py:875
+#: pysollib/game.py:929 pysollib/game.py:935
msgid "Player\n"
msgstr "Игрок\n"
-#: pysollib/game.py:946
+#: pysollib/game.py:1006
msgid "Discard current game ?"
msgstr "Завершить текущую игру?"
-#: pysollib/game.py:1455
+#: pysollib/game.py:1627
msgid ""
"\n"
"You have reached\n"
@@ -320,7 +325,7 @@ msgstr ""
"Вы достигли\n"
"#%d в %s игрового времени"
-#: pysollib/game.py:1460
+#: pysollib/game.py:1632
msgid ""
"\n"
"and #%d in the %s of moves"
@@ -328,7 +333,7 @@ msgstr ""
"\n"
"и #%d в %s количества ходов"
-#: pysollib/game.py:1463
+#: pysollib/game.py:1635
msgid ""
"\n"
"You have reached\n"
@@ -338,13 +343,13 @@ msgstr ""
"Вы достигли\n"
"#%d в %s количества ходов"
-#: pysollib/game.py:1493 pysollib/game.py:1510
+#: pysollib/game.py:1665 pysollib/game.py:1682
#: pysollib/tile/soundoptionsdialog.py:102
#: pysollib/tk/soundoptionsdialog.py:100
msgid "Game won"
msgstr "Игра выиграна"
-#: pysollib/game.py:1494
+#: pysollib/game.py:1666
msgid ""
"\n"
"Congratulations, this\n"
@@ -363,7 +368,7 @@ msgstr ""
"Количество ходов: %s\n"
"%s\n"
-#: pysollib/game.py:1511
+#: pysollib/game.py:1683
msgid ""
"\n"
"Congratulations, you did it !\n"
@@ -380,13 +385,13 @@ msgstr ""
"Количество ходов: %s\n"
"%s\n"
-#: pysollib/game.py:1523 pysollib/game.py:1529
+#: pysollib/game.py:1695 pysollib/game.py:1701
#: pysollib/tile/soundoptionsdialog.py:100
#: pysollib/tk/soundoptionsdialog.py:98
msgid "Game finished"
msgstr "Игра закончена"
-#: pysollib/game.py:1524 pysollib/game.py:2049
+#: pysollib/game.py:1696 pysollib/game.py:2246
msgid ""
"\n"
"Game finished\n"
@@ -394,7 +399,7 @@ msgstr ""
"\n"
"Игра закончена\n"
-#: pysollib/game.py:1530
+#: pysollib/game.py:1702
msgid ""
"\n"
"Game finished, but not without my help...\n"
@@ -402,35 +407,35 @@ msgstr ""
"\n"
"Игра закончена, но не без моей помощи...\n"
-#: pysollib/game.py:1531
+#: pysollib/game.py:1703
msgid "&Restart"
msgstr "&Начало"
-#: pysollib/game.py:1941
+#: pysollib/game.py:2135
msgid "Score %6d"
msgstr "Счёт %6d"
-#: pysollib/game.py:2040
+#: pysollib/game.py:2237
msgid "&Cool"
msgstr "&Отлично"
-#: pysollib/game.py:2040
+#: pysollib/game.py:2237
msgid "&Great"
msgstr "&Здорово"
-#: pysollib/game.py:2040
+#: pysollib/game.py:2237
msgid "&Wow"
msgstr "&Ура"
-#: pysollib/game.py:2040
+#: pysollib/game.py:2237
msgid "&Yeah"
msgstr "&Ага"
-#: pysollib/game.py:2041 pysollib/game.py:2052 pysollib/game.py:2064
+#: pysollib/game.py:2238 pysollib/game.py:2249 pysollib/game.py:2261
msgid " Autopilot"
msgstr " Автопилот"
-#: pysollib/game.py:2042
+#: pysollib/game.py:2239
msgid ""
"\n"
"Game solved in %d moves.\n"
@@ -438,19 +443,19 @@ msgstr ""
"\n"
"Игра решена за %d ходов\n"
-#: pysollib/game.py:2063
+#: pysollib/game.py:2260
msgid "&Hmm"
msgstr "&Хмм"
-#: pysollib/game.py:2063
+#: pysollib/game.py:2260
msgid "&Oh well"
msgstr "&Ох"
-#: pysollib/game.py:2063
+#: pysollib/game.py:2260
msgid "&That's life"
msgstr "&Такова жизнь"
-#: pysollib/game.py:2065
+#: pysollib/game.py:2262
msgid ""
"\n"
"This won't come out...\n"
@@ -458,31 +463,31 @@ msgstr ""
"\n"
"Не удалось...\n"
-#: pysollib/game.py:2474
+#: pysollib/game.py:2702
msgid "Set bookmark"
msgstr "Установить закладку"
-#: pysollib/game.py:2475
+#: pysollib/game.py:2703
msgid "Replace existing bookmark %d ?"
msgstr "Заменить существующую закладку %d ?"
-#: pysollib/game.py:2497
+#: pysollib/game.py:2725
msgid "Goto bookmark"
msgstr "Перейти к закладке"
-#: pysollib/game.py:2498
+#: pysollib/game.py:2726
msgid "Goto bookmark %d ?"
msgstr "Перейти к закладке %d ?"
-#: pysollib/game.py:2529
+#: pysollib/game.py:2757
msgid "Open game"
msgstr "Открыть игру"
-#: pysollib/game.py:2540 pysollib/game.py:2549 pysollib/game.py:2554
+#: pysollib/game.py:2768 pysollib/game.py:2777 pysollib/game.py:2782
msgid "Load game error"
msgstr "Ошибка при загрузке игры"
-#: pysollib/game.py:2541
+#: pysollib/game.py:2769
msgid ""
"Error while loading game.\n"
"\n"
@@ -494,11 +499,11 @@ msgstr ""
"Возможно повреждён файл,\n"
"или ошибка в программе."
-#: pysollib/game.py:2550
+#: pysollib/game.py:2778
msgid "Error while loading game"
msgstr "Ошибка при загрузке игры"
-#: pysollib/game.py:2555
+#: pysollib/game.py:2783
msgid ""
"Internal error while loading game.\n"
"\n"
@@ -508,11 +513,11 @@ msgstr ""
"\n"
"Пожалуйста сообщите об этой ошибке."
-#: pysollib/game.py:2580
+#: pysollib/game.py:2808
msgid "Save game error"
msgstr "Ошибка при сохранении игры"
-#: pysollib/game.py:2581
+#: pysollib/game.py:2809
msgid "Error while saving game"
msgstr "Ошибка при сохранении игры"
@@ -724,25 +729,26 @@ msgstr "Покер"
msgid "Puzzle type"
msgstr "Пазлы"
-#: pysollib/games/auldlangsyne.py:158 pysollib/games/calculation.py:104
-#: pysollib/games/camelot.py:588 pysollib/games/numerica.py:94
+#: pysollib/games/auldlangsyne.py:160 pysollib/games/calculation.py:104
+#: pysollib/games/camelot.py:590 pysollib/games/numerica.py:94
#: pysollib/games/numerica.py:276 pysollib/games/numerica.py:666
#: pysollib/games/numerica.py:779
msgid "Tableau. Build regardless of rank and suit."
msgstr "Игровой стол. Складывать не считаясь с мастью и достоинством."
-#: pysollib/games/auldlangsyne.py:555
+#: pysollib/games/auldlangsyne.py:564 pysollib/games/golf.py:295
msgid "Foundation. Build up or down regardless of suit."
-msgstr "Базовая ячейка. Складывать по возрастанию или убыванию не считаясь с мастью."
+msgstr ""
+"Базовая ячейка. Складывать по возрастанию или убыванию не считаясь с мастью."
-#: pysollib/games/braid.py:248 pysollib/games/camelot.py:555
+#: pysollib/games/braid.py:248 pysollib/games/camelot.py:557
#: pysollib/games/napoleon.py:183 pysollib/games/ultra/dashavatara.py:959
#: pysollib/games/ultra/hanafuda1.py:257 pysollib/games/ultra/hexadeck.py:1190
#: pysollib/games/ultra/mughal.py:802
msgid " Ascending"
msgstr " вверх"
-#: pysollib/games/braid.py:250 pysollib/games/camelot.py:554
+#: pysollib/games/braid.py:250 pysollib/games/camelot.py:556
#: pysollib/games/napoleon.py:185 pysollib/games/ultra/dashavatara.py:961
#: pysollib/games/ultra/hanafuda1.py:259 pysollib/games/ultra/hexadeck.py:1192
#: pysollib/games/ultra/mughal.py:804
@@ -762,12 +768,12 @@ msgstr ""
"4: 8 Д 3 7 В 2 6 10 Т 5 9 К"
#: pysollib/games/canfield.py:528 pysollib/games/special/tarock.py:224
-#: pysollib/stack.py:1389 pysollib/util.py:86
+#: pysollib/stack.py:1482 pysollib/util.py:86
msgid "King"
msgstr "Король"
#: pysollib/games/canfield.py:531 pysollib/games/special/tarock.py:224
-#: pysollib/stack.py:1388 pysollib/util.py:86
+#: pysollib/stack.py:1481 pysollib/util.py:86
msgid "Queen"
msgstr "Королева"
@@ -776,21 +782,21 @@ msgid "Tableau. Build down by suit or of the same rank."
msgstr ""
"Игровой стол. Складывать в масть по убыванию или с таким же достоинством."
-#: pysollib/games/fan.py:280
+#: pysollib/games/fan.py:285
msgid "Draw"
msgstr "Снять"
-#: pysollib/games/fan.py:280
+#: pysollib/games/fan.py:285
msgid "X"
msgstr "Х"
-#: pysollib/games/golf.py:114 pysollib/games/golf.py:300
-#: pysollib/stack.py:2054
+#: pysollib/games/golf.py:114 pysollib/games/golf.py:302
+#: pysollib/stack.py:2145
msgid "Tableau. No building."
msgstr "Игровой стол. Без выкладывания."
-#: pysollib/games/golf.py:385 pysollib/games/pileon.py:257
-#: pysollib/stack.py:1987
+#: pysollib/games/golf.py:387 pysollib/games/pileon.py:257
+#: pysollib/stack.py:2078
msgid "Foundation. Build up regardless of suit."
msgstr "Базовая ячейка. Складывать по возрастанию не считаясь с мастью."
@@ -798,7 +804,7 @@ msgstr "Базовая ячейка. Складывать по возраста
msgid "Balance $%d"
msgstr "Баланс $%d"
-#: pysollib/games/klondike.py:169 pysollib/stack.py:2095
+#: pysollib/games/klondike.py:169 pysollib/stack.py:2186
msgid "Tableau. Build down by color."
msgstr "Игровой стол. Складывать по убыванию в соответствии с цветом."
@@ -806,11 +812,11 @@ msgstr "Игровой стол. Складывать по убыванию в
msgid "Reserve. Only Kings are acceptable."
msgstr "Резерв. Только для королей."
-#: pysollib/games/larasgame.py:163 pysollib/stack.py:1605
+#: pysollib/games/larasgame.py:163 pysollib/stack.py:1694
msgid "Round %d"
msgstr "Раунд %d"
-#: pysollib/games/mahjongg/mahjongg.py:308
+#: pysollib/games/mahjongg/mahjongg.py:307
msgid ""
"No Free\n"
"Matching\n"
@@ -820,7 +826,7 @@ msgstr ""
"свободных\n"
"пар"
-#: pysollib/games/mahjongg/mahjongg.py:309
+#: pysollib/games/mahjongg/mahjongg.py:308
msgid ""
"1 Free\n"
"Matching\n"
@@ -830,7 +836,7 @@ msgstr ""
"свободная\n"
"пара"
-#: pysollib/games/mahjongg/mahjongg.py:310
+#: pysollib/games/mahjongg/mahjongg.py:309
msgid ""
" Free\n"
"Matching\n"
@@ -840,7 +846,7 @@ msgstr ""
"свободных\n"
"пар"
-#: pysollib/games/mahjongg/mahjongg.py:311
+#: pysollib/games/mahjongg/mahjongg.py:310
msgid ""
"\n"
"Tiles\n"
@@ -851,7 +857,7 @@ msgstr ""
"удалено\n"
"\n"
-#: pysollib/games/mahjongg/mahjongg.py:312
+#: pysollib/games/mahjongg/mahjongg.py:311
msgid ""
"\n"
"Tiles\n"
@@ -870,7 +876,7 @@ msgstr "Раунд %d/%d"
msgid "Deal %d"
msgstr "Сдача %d"
-#: pysollib/games/numerica.py:263 pysollib/games/royalcotillion.py:850
+#: pysollib/games/numerica.py:263 pysollib/games/royalcotillion.py:865
msgid "Foundation. Build up by color."
msgstr "Базовая ячейка. Складывать по возрастанию в соответствии с цветом."
@@ -935,7 +941,7 @@ msgstr "Жезлы"
#: pysollib/games/special/tarock.py:223
#: pysollib/games/ultra/dashavatara.py:351
#: pysollib/games/ultra/hexadeck.py:273 pysollib/games/ultra/mughal.py:254
-#: pysollib/stack.py:1390 pysollib/util.py:85
+#: pysollib/stack.py:1483 pysollib/util.py:85
msgid "Ace"
msgstr "Туз"
@@ -947,7 +953,7 @@ msgstr "Паж"
msgid "Valet"
msgstr "Валет"
-#: pysollib/games/spider.py:1131
+#: pysollib/games/spider.py:1156
msgid ""
"Tableau. Build down regardless of suit. Sequences of cards in the same color "
"can be moved as a unit."
@@ -1214,7 +1220,7 @@ msgstr "Резерв"
msgid "Tan"
msgstr ""
-#: pysollib/games/yukon.py:139
+#: pysollib/games/yukon.py:140
msgid ""
"Tableau. Build down in any suit but the same, can move any face-up cards "
"regardless of sequence."
@@ -1222,7 +1228,7 @@ msgstr ""
"Игровой стол. Складывать по убыванию в любую масть кроме такой же, можно "
"перемещать любую серию открытых карт."
-#: pysollib/games/yukon.py:198
+#: pysollib/games/yukon.py:199
msgid ""
"Tableau. Build up or down by suit, can move any face-up cards regardless of "
"sequence."
@@ -1230,7 +1236,7 @@ msgstr ""
"Игровой стол. Складывать по возрастанию или убыванию в соответствии с "
"мастью, можно перемещать любую серию открытых карт."
-#: pysollib/games/yukon.py:215
+#: pysollib/games/yukon.py:216
msgid ""
"Tableau. Build up or down by alternate color, can move any face-up cards "
"regardless of sequence."
@@ -1238,7 +1244,7 @@ msgstr ""
"Игровой стол. Складывать по возрастанию или убыванию чередуя цвет, можно "
"перемещать любую серию открытых карт."
-#: pysollib/games/yukon.py:317
+#: pysollib/games/yukon.py:318
msgid ""
"Club: A 2 3 4 5 6 7 8 9 T J Q K\n"
"Spade: 2 4 6 8 T Q A 3 5 7 9 J K\n"
@@ -1250,7 +1256,7 @@ msgstr ""
"Черви: 3 6 9 Д 2 5 8 В Т 4 7 10 К\n"
"Буби: 4 8 Д 3 7 В 2 6 10 Т 5 9 К"
-#: pysollib/games/yukon.py:639
+#: pysollib/games/yukon.py:640
msgid ""
"Tableau. Build down regardless of suit, can move any face-up cards "
"regardless of sequence."
@@ -1372,8 +1378,8 @@ msgstr ""
"\n"
"Пожалуйста проверьте установку %s.\n"
-#: pysollib/main.py:72 pysollib/main.py:257 pysollib/tile/menubar.py:381
-#: pysollib/tk/menubar.py:378
+#: pysollib/main.py:72 pysollib/main.py:257 pysollib/tile/menubar.py:378
+#: pysollib/tk/menubar.py:375
msgid "&Quit"
msgstr "В&ыход"
@@ -1713,145 +1719,145 @@ msgstr "Швейцария"
msgid "USA"
msgstr "США"
-#: pysollib/settings.py:67 data/glade-translations:29
+#: pysollib/settings.py:72 data/glade-translations:29
msgid "Top 10"
msgstr "Top 10"
-#: pysollib/stack.py:1384
+#: pysollib/stack.py:1477
msgid "Base card - %s."
msgstr "Базовая карта - %s."
-#: pysollib/stack.py:1385
+#: pysollib/stack.py:1478
msgid "Empty row cannot be filled."
msgstr "Пустой ряд не заполняется."
-#: pysollib/stack.py:1386
+#: pysollib/stack.py:1479
msgid "any card"
msgstr "любая карта"
-#: pysollib/stack.py:1387 pysollib/util.py:86
+#: pysollib/stack.py:1480 pysollib/util.py:86
msgid "Jack"
msgstr "Валет"
-#: pysollib/stack.py:1400
+#: pysollib/stack.py:1489
msgid "No cards"
msgstr "Нет карт"
-#: pysollib/stack.py:1401
+#: pysollib/stack.py:1490
msgid "1 card"
msgstr "1 карта"
-#: pysollib/stack.py:1402
+#: pysollib/stack.py:1491
msgid " cards"
msgstr " карт"
-#: pysollib/stack.py:1614 pysollib/stack.py:1616 pysollib/stack.py:1652
+#: pysollib/stack.py:1703 pysollib/stack.py:1705 pysollib/stack.py:1741
msgid "Redeal"
msgstr "Сдать"
-#: pysollib/stack.py:1616
+#: pysollib/stack.py:1705
msgid "Stop"
msgstr "Стоп"
-#: pysollib/stack.py:1677
+#: pysollib/stack.py:1766
msgid "Variable redeals."
msgstr "Переменное количество пересдач."
-#: pysollib/stack.py:1678
+#: pysollib/stack.py:1767
msgid "Unlimited redeals."
msgstr "Неограниченное количество пересдач."
-#: pysollib/stack.py:1679
+#: pysollib/stack.py:1768
msgid "No redeals."
msgstr "Без пересдачи."
-#: pysollib/stack.py:1680
+#: pysollib/stack.py:1769
msgid "One redeal."
msgstr "1 пересдача."
-#: pysollib/stack.py:1681
+#: pysollib/stack.py:1770
msgid " redeals."
msgstr " пересдачи."
-#: pysollib/stack.py:1683
+#: pysollib/stack.py:1772
msgid "Talon."
msgstr "Колода."
-#: pysollib/stack.py:1917 pysollib/stack.py:2368
+#: pysollib/stack.py:2008 pysollib/stack.py:2537
msgid "Reserve. No building."
msgstr "Резерв. Без выкладывания."
-#: pysollib/stack.py:1955
+#: pysollib/stack.py:2046
msgid "Foundation."
msgstr "Базовая ячейка"
-#: pysollib/stack.py:1971
+#: pysollib/stack.py:2062
msgid "Foundation. Build up by suit."
msgstr "Базовая ячейка. Складывать по возрастанию в соответствии с мастью."
-#: pysollib/stack.py:1972
+#: pysollib/stack.py:2063
msgid "Foundation. Build down by suit."
msgstr "Базовая ячейка. Складывать по убыванию в соответствии с мастью."
-#: pysollib/stack.py:1973 pysollib/stack.py:1989 pysollib/stack.py:2011
+#: pysollib/stack.py:2064 pysollib/stack.py:2080 pysollib/stack.py:2102
msgid "Foundation. Build by same rank."
msgstr "Базовая ячейка. Складывать в соответствии с достоинством."
-#: pysollib/stack.py:1988
+#: pysollib/stack.py:2079
msgid "Foundation. Build down regardless of suit."
msgstr "Базовая ячейка. Складывать не считаясь с мастью."
-#: pysollib/stack.py:2009
+#: pysollib/stack.py:2100
msgid "Foundation. Build up by alternate color."
msgstr "Базовая ячейка. Складывать по возрастанию чередуя цвет."
-#: pysollib/stack.py:2010
+#: pysollib/stack.py:2101
msgid "Foundation. Build down by alternate color."
msgstr "Базовая ячейка. Складывать по убыванию чередуя цвет."
-#: pysollib/stack.py:2084
+#: pysollib/stack.py:2175
msgid "Tableau. Build up by alternate color."
msgstr "Игровой стол. Складывать по возрастанию чередуя цвет."
-#: pysollib/stack.py:2085
+#: pysollib/stack.py:2176
msgid "Tableau. Build down by alternate color."
msgstr "Игровой стол. Складывать по убыванию чередуя цвет."
-#: pysollib/stack.py:2086 pysollib/stack.py:2096 pysollib/stack.py:2105
-#: pysollib/stack.py:2114 pysollib/stack.py:2124 pysollib/stack.py:2147
-#: pysollib/stack.py:2157
+#: pysollib/stack.py:2177 pysollib/stack.py:2187 pysollib/stack.py:2196
+#: pysollib/stack.py:2205 pysollib/stack.py:2215 pysollib/stack.py:2244
+#: pysollib/stack.py:2254
msgid "Tableau. Build by same rank."
msgstr "Игровой стол. Складывать в соответствии с достоинством."
-#: pysollib/stack.py:2094
+#: pysollib/stack.py:2185
msgid "Tableau. Build up by color."
msgstr "Игровой стол. Складывать по возрастанию в соответствии с цветом."
-#: pysollib/stack.py:2103
+#: pysollib/stack.py:2194
msgid "Tableau. Build up by suit."
msgstr "Игровой стол. Складывать по возрастанию в соответствии с мастью."
-#: pysollib/stack.py:2104
+#: pysollib/stack.py:2195
msgid "Tableau. Build down by suit."
msgstr "Игровой стол. Складывать по убыванию в соответствии с мастью."
-#: pysollib/stack.py:2112
+#: pysollib/stack.py:2203
msgid "Tableau. Build up regardless of suit."
msgstr "Игровой стол. Складывать по возрастанию не считаясь с мастью."
-#: pysollib/stack.py:2113
+#: pysollib/stack.py:2204
msgid "Tableau. Build down regardless of suit."
msgstr "Игровой стол. Складывать по убыванию не считаясь с мастью."
-#: pysollib/stack.py:2122
+#: pysollib/stack.py:2213
msgid "Tableau. Build up in any suit but the same."
msgstr "Игровой стол. Складывать по возрастанию в любую масть кроме такой же."
-#: pysollib/stack.py:2123
+#: pysollib/stack.py:2214
msgid "Tableau. Build down in any suit but the same."
msgstr "Игровой стол. Складывать по убыванию в любую масть кроме такой же."
-#: pysollib/stack.py:2145
+#: pysollib/stack.py:2242
msgid ""
"Tableau. Build up regardless of suit. Sequences of cards in alternate color "
"can be moved as a unit."
@@ -1859,7 +1865,7 @@ msgstr ""
"Игровой стол. Складывать по возрастанию не считаясь с мастью. Можно "
"перемещать серии карт чередующихся цветом."
-#: pysollib/stack.py:2146
+#: pysollib/stack.py:2243
msgid ""
"Tableau. Build down regardless of suit. Sequences of cards in alternate "
"color can be moved as a unit."
@@ -1867,7 +1873,7 @@ msgstr ""
"Игровой стол. Складывать по убыванию не считаясь с мастью. Можно перемещать "
"серии карт чередующихся цветом."
-#: pysollib/stack.py:2155
+#: pysollib/stack.py:2252
msgid ""
"Tableau. Build up regardless of suit. Sequences of cards in the same suit "
"can be moved as a unit."
@@ -1875,7 +1881,7 @@ msgstr ""
"Игровой стол. Складывать по возрастанию не считаясь с мастью. Можно "
"перемещать серии карт одинаковой масти."
-#: pysollib/stack.py:2156
+#: pysollib/stack.py:2253
msgid ""
"Tableau. Build down regardless of suit. Sequences of cards in the same suit "
"can be moved as a unit."
@@ -1883,7 +1889,7 @@ msgstr ""
"Игровой стол. Складывать по убыванию не считаясь с мастью. Можно перемещать "
"серии карт одинаковой масти."
-#: pysollib/stack.py:2178
+#: pysollib/stack.py:2275
msgid ""
"Tableau. Build up by alternate color, can move any face-up cards regardless "
"of sequence."
@@ -1891,7 +1897,7 @@ msgstr ""
"Игровой стол. Складывать по возрастанию чередуя цвет, можно перемещать любую "
"серию открытых карт."
-#: pysollib/stack.py:2179
+#: pysollib/stack.py:2276
msgid ""
"Tableau. Build down by alternate color, can move any face-up cards "
"regardless of sequence."
@@ -1899,7 +1905,7 @@ msgstr ""
"Игровой стол. Складывать по убыванию чередуя цвет, можно перемещать любую "
"серию открытых карт."
-#: pysollib/stack.py:2180 pysollib/stack.py:2193
+#: pysollib/stack.py:2277 pysollib/stack.py:2290
msgid ""
"Tableau. Build by same rank, can move any face-up cards regardless of "
"sequence."
@@ -1907,14 +1913,14 @@ msgstr ""
"Игровой стол. Складывать в соответствии с достоинством, можно перемещать "
"любую серию открытых карт."
-#: pysollib/stack.py:2191
+#: pysollib/stack.py:2288
msgid ""
"Tableau. Build up by suit, can move any face-up cards regardless of sequence."
msgstr ""
"Игровой стол. Складывать по возрастанию в соответствии с мастью, можно "
"перемещать любую серию открытых карт."
-#: pysollib/stack.py:2192
+#: pysollib/stack.py:2289
msgid ""
"Tableau. Build down by suit, can move any face-up cards regardless of "
"sequence."
@@ -1922,30 +1928,30 @@ msgstr ""
"Игровой стол. Складывать по убыванию в соответствии с мастью, можно "
"перемещать любую серию открытых карт."
-#: pysollib/stack.py:2225
+#: pysollib/stack.py:2322
msgid "Tableau. Build up or down by color."
msgstr ""
"Игровой стол. Складывать по возрастанию или убыванию в соответствии с цветом."
-#: pysollib/stack.py:2236
+#: pysollib/stack.py:2333
msgid "Tableau. Build up or down by alternate color."
msgstr "Игровой стол. Складывать по возрастанию или убыванию чередуя цвет."
-#: pysollib/stack.py:2247
+#: pysollib/stack.py:2344
msgid "Tableau. Build up or down by suit."
msgstr ""
"Игровой стол. Складывать по возрастанию или убыванию в соответствии с мастью."
-#: pysollib/stack.py:2258
+#: pysollib/stack.py:2355
msgid "Tableau. Build up or down regardless of suit."
msgstr ""
"Игровой стол. Складывать по возрастанию или убыванию не считаясь с мастью."
-#: pysollib/stack.py:2269
+#: pysollib/stack.py:2433
msgid "Waste."
msgstr "Сброс."
-#: pysollib/stack.py:2369
+#: pysollib/stack.py:2538
msgid "Free cell."
msgstr "Свободная ячейка."
@@ -1953,11 +1959,17 @@ msgstr "Свободная ячейка."
msgid "Game"
msgstr "Игра"
-#: pysollib/stats.py:53
+#: pysollib/stats.py:53 pysollib/tile/tkstats.py:772
+#: pysollib/tile/tkstats.py:773 pysollib/tile/tkstats.py:817
+#: pysollib/tk/tkstats.py:886 pysollib/tk/tkstats.py:887
+#: pysollib/tk/tkstats.py:940
msgid "Played"
msgstr "Играл"
-#: pysollib/stats.py:54 pysollib/stats.py:157
+#: pysollib/stats.py:54 pysollib/stats.py:157 pysollib/tile/tkstats.py:777
+#: pysollib/tile/tkstats.py:778 pysollib/tile/tkstats.py:824
+#: pysollib/tk/tkstats.py:891 pysollib/tk/tkstats.py:892
+#: pysollib/tk/tkstats.py:948
msgid "Won"
msgstr "Выиграл"
@@ -1965,8 +1977,8 @@ msgstr "Выиграл"
msgid "Lost"
msgstr "Проиграл"
-#: pysollib/stats.py:56 pysollib/tile/statusbar.py:147
-#: pysollib/tk/statusbar.py:158 data/glade-translations:25
+#: pysollib/stats.py:56 pysollib/tile/statusbar.py:150
+#: pysollib/tk/statusbar.py:161 data/glade-translations:25
msgid "Playing time"
msgstr "Время игры"
@@ -1974,7 +1986,11 @@ msgstr "Время игры"
msgid "Moves"
msgstr "Ходов"
-#: pysollib/stats.py:58
+#: pysollib/stats.py:58 pysollib/tile/tkstats.py:745
+#: pysollib/tile/tkstats.py:764 pysollib/tile/tkstats.py:782
+#: pysollib/tile/tkstats.py:831 pysollib/tk/tkstats.py:859
+#: pysollib/tk/tkstats.py:878 pysollib/tk/tkstats.py:896
+#: pysollib/tk/tkstats.py:956
msgid "% won"
msgstr "% побед"
@@ -1982,14 +1998,14 @@ msgstr "% побед"
msgid "Status"
msgstr "Статус"
-#: pysollib/stats.py:118 pysollib/tile/statusbar.py:149
-#: pysollib/tile/tkstats.py:552 pysollib/tk/statusbar.py:160
-#: pysollib/tk/tkstats.py:668
+#: pysollib/stats.py:118 pysollib/tile/statusbar.py:152
+#: pysollib/tile/tkstats.py:556 pysollib/tk/statusbar.py:163
+#: pysollib/tk/tkstats.py:670
msgid "Game number"
msgstr "Номер игры"
-#: pysollib/stats.py:118 pysollib/tile/tkstats.py:555
-#: pysollib/tk/tkstats.py:671
+#: pysollib/stats.py:118 pysollib/tile/tkstats.py:559
+#: pysollib/tk/tkstats.py:673
msgid "Started at"
msgstr "Игра начата"
@@ -2017,8 +2033,8 @@ msgstr "Великолепная"
msgid "Demo"
msgstr "Демо"
-#: pysollib/stats.py:215 pysollib/tile/tkstats.py:283
-#: pysollib/tk/tkstats.py:419
+#: pysollib/stats.py:215 pysollib/tile/tkstats.py:287
+#: pysollib/tk/tkstats.py:421
msgid "Total (%d out of %d games)"
msgstr "Всего (%d из %d игр)"
@@ -2030,18 +2046,53 @@ msgstr "Полный лог для "
msgid "Session log for "
msgstr "Лог сессии для "
-#: pysollib/tile/colorsdialog.py:72 pysollib/tk/colorsdialog.py:71
-#: data/glade-translations:41
+#: pysollib/tile/colorsdialog.py:71 pysollib/tk/colorsdialog.py:70
+#: data/glade-translations:56
msgid "Text foreground:"
msgstr "Цвет текста:"
-#: pysollib/tile/colorsdialog.py:77 pysollib/tile/colorsdialog.py:95
-#: pysollib/tile/fontsdialog.py:185 pysollib/tk/colorsdialog.py:76
-#: pysollib/tk/colorsdialog.py:94 pysollib/tk/fontsdialog.py:186
-#: data/glade-translations:49 data/glade-translations:50
-#: data/glade-translations:51 data/glade-translations:52
-#: data/glade-translations:53 data/glade-translations:54
-#: data/glade-translations:55 data/glade-translations:56
+#: pysollib/tile/colorsdialog.py:72 pysollib/tile/timeoutsdialog.py:70
+#: pysollib/tk/colorsdialog.py:71 pysollib/tk/timeoutsdialog.py:68
+#: data/glade-translations:37 data/glade-translations:41
+msgid "Highlight piles:"
+msgstr "Подсветка групп:"
+
+#: pysollib/tile/colorsdialog.py:73 pysollib/tk/colorsdialog.py:72
+#: data/glade-translations:42
+msgid "Highlight cards 1:"
+msgstr "Подсветка карт 1:"
+
+#: pysollib/tile/colorsdialog.py:74 pysollib/tk/colorsdialog.py:73
+#: data/glade-translations:43
+msgid "Highlight cards 2:"
+msgstr "Подсветка карт 2:"
+
+#: pysollib/tile/colorsdialog.py:75 pysollib/tk/colorsdialog.py:74
+#: data/glade-translations:44
+msgid "Highlight same rank 1:"
+msgstr "Подсветка карт одного достоинства 1:"
+
+#: pysollib/tile/colorsdialog.py:76 pysollib/tk/colorsdialog.py:75
+#: data/glade-translations:45
+msgid "Highlight same rank 2:"
+msgstr "Подсветка карт одного достоинства 2:"
+
+#: pysollib/tile/colorsdialog.py:77 pysollib/tk/colorsdialog.py:76
+#: data/glade-translations:46
+msgid "Hint arrow:"
+msgstr "Стрелка подсказки:"
+
+#: pysollib/tile/colorsdialog.py:78 pysollib/tk/colorsdialog.py:77
+#: data/glade-translations:47
+msgid "Highlight not matching:"
+msgstr "Подсветка отсутствия совпадения:"
+
+#: pysollib/tile/colorsdialog.py:85 pysollib/tile/fontsdialog.py:185
+#: pysollib/tk/colorsdialog.py:84 pysollib/tk/fontsdialog.py:186
+#: data/glade-translations:48 data/glade-translations:49
+#: data/glade-translations:50 data/glade-translations:51
+#: data/glade-translations:52 data/glade-translations:53
+#: data/glade-translations:54 data/glade-translations:55
#: data/glade-translations:65 data/glade-translations:66
#: data/glade-translations:67 data/glade-translations:68
#: data/glade-translations:69 data/glade-translations:70
@@ -2049,48 +2100,11 @@ msgstr "Цвет текста:"
msgid "Change..."
msgstr "Изменить..."
-#: pysollib/tile/colorsdialog.py:82 pysollib/tile/timeoutsdialog.py:70
-#: pysollib/tk/colorsdialog.py:81 pysollib/tk/timeoutsdialog.py:68
-#: data/glade-translations:37 data/glade-translations:42
-msgid "Highlight piles:"
-msgstr "Подсветка групп:"
-
-#: pysollib/tile/colorsdialog.py:83 pysollib/tk/colorsdialog.py:82
-#: data/glade-translations:43
-msgid "Highlight cards 1:"
-msgstr "Подсветка карт 1:"
-
-#: pysollib/tile/colorsdialog.py:84 pysollib/tk/colorsdialog.py:83
-#: data/glade-translations:44
-msgid "Highlight cards 2:"
-msgstr "Подсветка карт 2:"
-
-#: pysollib/tile/colorsdialog.py:85 pysollib/tk/colorsdialog.py:84
-#: data/glade-translations:45
-msgid "Highlight same rank 1:"
-msgstr "Подсветка карт одного достоинства 1:"
-
-#: pysollib/tile/colorsdialog.py:86 pysollib/tk/colorsdialog.py:85
-#: data/glade-translations:46
-msgid "Highlight same rank 2:"
-msgstr "Подсветка карт одного достоинства 2:"
-
-#: pysollib/tile/colorsdialog.py:87 pysollib/tk/colorsdialog.py:86
-#: data/glade-translations:47
-msgid "Hint arrow:"
-msgstr "Стрелка подсказки:"
-
-#: pysollib/tile/colorsdialog.py:88 pysollib/tk/colorsdialog.py:87
-#: data/glade-translations:48
-msgid "Highlight not matching:"
-msgstr "Подсветка отсутствия совпадения:"
-
-#: pysollib/tile/colorsdialog.py:115 pysollib/tk/colorsdialog.py:114
+#: pysollib/tile/colorsdialog.py:104 pysollib/tk/colorsdialog.py:103
msgid "Select color"
msgstr "Выбрать цвет"
-#: pysollib/tile/findcarddialog.py:54 pysollib/tile/menubar.py:434
-#: pysollib/tk/findcarddialog.py:54 pysollib/tk/menubar.py:431
+#: pysollib/tile/findcarddialog.py:54 pysollib/tk/findcarddialog.py:54
msgid "Find card"
msgstr "Найти карту"
@@ -2150,507 +2164,524 @@ msgstr "Игровой стол маленький: "
msgid "Select font"
msgstr "Выбрать шрифт"
-#: pysollib/tile/menubar.py:78 pysollib/tk/menubar.py:76
+#: pysollib/tile/menubar.py:81 pysollib/tk/menubar.py:79
msgid "Style"
msgstr "Стиль"
-#: pysollib/tile/menubar.py:86 pysollib/tk/menubar.py:85
+#: pysollib/tile/menubar.py:89 pysollib/tk/menubar.py:88
msgid "Compound"
msgstr "Компоновка"
-#: pysollib/tile/menubar.py:92 pysollib/tk/menubar.py:91
+#: pysollib/tile/menubar.py:95 pysollib/tk/menubar.py:94
msgid "Hide"
msgstr "Спрятать"
-#: pysollib/tile/menubar.py:95 pysollib/tk/menubar.py:94
+#: pysollib/tile/menubar.py:98 pysollib/tk/menubar.py:97
msgid "Top"
msgstr "Сверху"
-#: pysollib/tile/menubar.py:98 pysollib/tk/menubar.py:97
+#: pysollib/tile/menubar.py:101 pysollib/tk/menubar.py:100
msgid "Bottom"
msgstr "Внизу"
-#: pysollib/tile/menubar.py:101 pysollib/tk/menubar.py:100
+#: pysollib/tile/menubar.py:104 pysollib/tk/menubar.py:103
msgid "Left"
msgstr "Слева"
-#: pysollib/tile/menubar.py:104 pysollib/tk/menubar.py:103
+#: pysollib/tile/menubar.py:107 pysollib/tk/menubar.py:106
msgid "Right"
msgstr "Справа"
-#: pysollib/tile/menubar.py:108 pysollib/tk/menubar.py:107
+#: pysollib/tile/menubar.py:111 pysollib/tk/menubar.py:110
msgid "Small icons"
msgstr "Маленькие пиктограммы"
-#: pysollib/tile/menubar.py:111 pysollib/tk/menubar.py:110
+#: pysollib/tile/menubar.py:114 pysollib/tk/menubar.py:113
msgid "Large icons"
msgstr "Большие пиктограммы"
-#: pysollib/tile/menubar.py:117 pysollib/tk/menubar.py:116
+#: pysollib/tile/menubar.py:120 pysollib/tk/menubar.py:119
msgid "Customize toolbar"
msgstr "Настроить панель инструментов"
-#: pysollib/tile/menubar.py:357 pysollib/tk/menubar.py:354
+#: pysollib/tile/menubar.py:354 pysollib/tk/menubar.py:351
#, fuzzy
msgid "apple"
msgstr "Клён"
-#: pysollib/tile/menubar.py:358 pysollib/tile/menubar.py:524
-#: pysollib/tk/menubar.py:355 pysollib/tk/menubar.py:520
+#: pysollib/tile/menubar.py:355 pysollib/tile/menubar.py:528
+#: pysollib/tk/menubar.py:352 pysollib/tk/menubar.py:524
msgid "&About "
msgstr "&О программе "
-#: pysollib/tile/menubar.py:360 pysollib/tk/menubar.py:357
+#: pysollib/tile/menubar.py:357 pysollib/tk/menubar.py:354
msgid "&File"
msgstr "&Файл"
-#: pysollib/tile/menubar.py:362 pysollib/tk/menubar.py:359
+#: pysollib/tile/menubar.py:359 pysollib/tk/menubar.py:356
msgid "R&ecent games"
msgstr "Выбрать н&едавнюю игру"
-#: pysollib/tile/menubar.py:364 pysollib/tk/menubar.py:361
+#: pysollib/tile/menubar.py:361 pysollib/tk/menubar.py:358
msgid "Select &random game"
msgstr "С&лучайная игра"
-#: pysollib/tile/menubar.py:365 pysollib/tk/menubar.py:362
+#: pysollib/tile/menubar.py:362 pysollib/tk/menubar.py:359
msgid "&All games"
msgstr "&Все игры"
-#: pysollib/tile/menubar.py:366 pysollib/tk/menubar.py:363
+#: pysollib/tile/menubar.py:363 pysollib/tk/menubar.py:360
msgid "Games played and &won"
msgstr "&Выигранные игры"
-#: pysollib/tile/menubar.py:367 pysollib/tk/menubar.py:364
+#: pysollib/tile/menubar.py:364 pysollib/tk/menubar.py:361
msgid "Games played and ¬ won"
msgstr "&Невыигранные игры"
-#: pysollib/tile/menubar.py:368 pysollib/tk/menubar.py:365
+#: pysollib/tile/menubar.py:365 pysollib/tk/menubar.py:362
msgid "Games not &played"
msgstr "Не&сыгранные игры"
-#: pysollib/tile/menubar.py:369 pysollib/tk/menubar.py:366
+#: pysollib/tile/menubar.py:366 pysollib/tk/menubar.py:363
msgid "Select game by nu&mber..."
msgstr "Выбрать игру по &номеру..."
-#: pysollib/tile/menubar.py:371 pysollib/tk/menubar.py:368
+#: pysollib/tile/menubar.py:368 pysollib/tk/menubar.py:365
msgid "Fa&vorite games"
msgstr "&Избранные игры"
-#: pysollib/tile/menubar.py:372 pysollib/tk/menubar.py:369
+#: pysollib/tile/menubar.py:369 pysollib/tk/menubar.py:366
msgid "A&dd to favorites"
msgstr "&Добавить в избранное"
-#: pysollib/tile/menubar.py:373 pysollib/tk/menubar.py:370
-msgid "R&emove from favorites"
+#: pysollib/tile/menubar.py:370 pysollib/tk/menubar.py:367
+msgid "Remove &from favorites"
msgstr "&Удалить из избранных"
-#: pysollib/tile/menubar.py:375 pysollib/tk/menubar.py:372
+#: pysollib/tile/menubar.py:372 pysollib/tk/menubar.py:369
msgid "&Open..."
msgstr "&Открыть..."
-#: pysollib/tile/menubar.py:376 pysollib/tk/menubar.py:373
+#: pysollib/tile/menubar.py:373 pysollib/tk/menubar.py:370
msgid "&Save"
msgstr "&Сохранить"
-#: pysollib/tile/menubar.py:377 pysollib/tk/menubar.py:374
+#: pysollib/tile/menubar.py:374 pysollib/tk/menubar.py:371
msgid "Save &as..."
msgstr "Сохранить &как..."
-#: pysollib/tile/menubar.py:379 pysollib/tk/menubar.py:376
+#: pysollib/tile/menubar.py:376 pysollib/tk/menubar.py:373
msgid "&Hold and quit"
msgstr "Со&храниться и выйти"
-#: pysollib/tile/menubar.py:385 pysollib/tile/selectgame.py:398
-#: pysollib/tk/menubar.py:382 pysollib/tk/selectgame.py:407
+#: pysollib/tile/menubar.py:382 pysollib/tile/selectgame.py:398
+#: pysollib/tk/menubar.py:379 pysollib/tk/selectgame.py:407
msgid "&Select"
msgstr "&Выбрать"
-#: pysollib/tile/menubar.py:390 pysollib/tk/menubar.py:387
+#: pysollib/tile/menubar.py:387 pysollib/tk/menubar.py:384
msgid "&Edit"
msgstr "Р&едактировать"
-#: pysollib/tile/menubar.py:391 pysollib/tk/menubar.py:388
+#: pysollib/tile/menubar.py:388 pysollib/tk/menubar.py:385
msgid "&Undo"
msgstr "&Отмена"
-#: pysollib/tile/menubar.py:392 pysollib/tk/menubar.py:389
+#: pysollib/tile/menubar.py:389 pysollib/tk/menubar.py:386
msgid "&Redo"
msgstr "&Повтор"
-#: pysollib/tile/menubar.py:393 pysollib/tk/menubar.py:390
+#: pysollib/tile/menubar.py:390 pysollib/tk/menubar.py:387
msgid "Redo &all"
msgstr "Вернуть все"
-#: pysollib/tile/menubar.py:396 pysollib/tk/menubar.py:393
+#: pysollib/tile/menubar.py:393 pysollib/tk/menubar.py:390
msgid "&Set bookmark"
msgstr "Установить &закладку"
-#: pysollib/tile/menubar.py:398 pysollib/tile/menubar.py:402
-#: pysollib/tk/menubar.py:395 pysollib/tk/menubar.py:399
+#: pysollib/tile/menubar.py:395 pysollib/tile/menubar.py:399
+#: pysollib/tk/menubar.py:392 pysollib/tk/menubar.py:396
msgid "Bookmark %d"
msgstr "Закладка %d"
-#: pysollib/tile/menubar.py:400 pysollib/tk/menubar.py:397
+#: pysollib/tile/menubar.py:397 pysollib/tk/menubar.py:394
msgid "Go&to bookmark"
msgstr "&Перейти к закладке"
-#: pysollib/tile/menubar.py:405 pysollib/tk/menubar.py:402
+#: pysollib/tile/menubar.py:402 pysollib/tk/menubar.py:399
msgid "&Clear bookmarks"
msgstr "О&чистить закладки"
-#: pysollib/tile/menubar.py:408 pysollib/tile/toolbar.py:190
-#: pysollib/tk/menubar.py:405 pysollib/tk/toolbar.py:193
+#: pysollib/tile/menubar.py:405 pysollib/tile/toolbar.py:190
+#: pysollib/tk/menubar.py:402 pysollib/tk/toolbar.py:193
msgid "Restart"
msgstr "Начало"
-#: pysollib/tile/menubar.py:410 pysollib/tk/menubar.py:407
+#: pysollib/tile/menubar.py:407 pysollib/tk/menubar.py:404
msgid "&Game"
msgstr "&Игра"
-#: pysollib/tile/menubar.py:411 pysollib/tk/menubar.py:408
+#: pysollib/tile/menubar.py:408 pysollib/tk/menubar.py:405
msgid "&Deal cards"
msgstr "&Сдать карты"
-#: pysollib/tile/menubar.py:412 pysollib/tk/menubar.py:409
+#: pysollib/tile/menubar.py:409 pysollib/tk/menubar.py:406
msgid "&Auto drop"
msgstr "С&бросить карты"
-#: pysollib/tile/menubar.py:413 pysollib/tk/menubar.py:410
+#: pysollib/tile/menubar.py:410 pysollib/tk/menubar.py:407
msgid "&Pause"
msgstr "&Пауза"
-#: pysollib/tile/menubar.py:416 pysollib/tk/menubar.py:413
+#: pysollib/tile/menubar.py:413 pysollib/tk/menubar.py:410
msgid "S&tatus..."
msgstr "С&татус"
-#: pysollib/tile/menubar.py:417 pysollib/tk/menubar.py:414
+#: pysollib/tile/menubar.py:414 pysollib/tk/menubar.py:411
msgid "&Comments..."
msgstr "&Комментарии..."
-#: pysollib/tile/menubar.py:419 pysollib/tk/menubar.py:416
+#: pysollib/tile/menubar.py:416 pysollib/tk/menubar.py:413
msgid "&Statistics"
msgstr "Ст&атистика"
-#: pysollib/tile/menubar.py:420 pysollib/tile/menubar.py:428
-#: pysollib/tk/menubar.py:417 pysollib/tk/menubar.py:425
+#: pysollib/tile/menubar.py:417 pysollib/tile/menubar.py:426
+#: pysollib/tk/menubar.py:414 pysollib/tk/menubar.py:423
msgid "Current game..."
msgstr "Текущая игра..."
-#: pysollib/tile/menubar.py:421 pysollib/tile/menubar.py:429
-#: pysollib/tk/menubar.py:418 pysollib/tk/menubar.py:426
+#: pysollib/tile/menubar.py:418 pysollib/tile/menubar.py:427
+#: pysollib/tk/menubar.py:415 pysollib/tk/menubar.py:424
msgid "All games..."
msgstr "Все игры..."
-#: pysollib/tile/menubar.py:423 pysollib/tk/menubar.py:420
+#: pysollib/tile/menubar.py:420 pysollib/tk/menubar.py:417
msgid "Session log..."
msgstr "Лог сессии..."
-#: pysollib/tile/menubar.py:424 pysollib/tk/menubar.py:421
+#: pysollib/tile/menubar.py:421 pysollib/tk/menubar.py:418
msgid "Full log..."
msgstr "Полный лог..."
-#: pysollib/tile/menubar.py:427 pysollib/tk/menubar.py:424
+#: pysollib/tile/menubar.py:424 pysollib/tk/menubar.py:421
+msgid "Progression..."
+msgstr "Прогресс..."
+
+#: pysollib/tile/menubar.py:425 pysollib/tk/menubar.py:422
msgid "D&emo statistics"
msgstr "Статистика демо"
-#: pysollib/tile/menubar.py:431 pysollib/tk/menubar.py:428
+#: pysollib/tile/menubar.py:429 pysollib/tk/menubar.py:426
msgid "&Assist"
msgstr "&Подсказка"
-#: pysollib/tile/menubar.py:432 pysollib/tk/menubar.py:429
+#: pysollib/tile/menubar.py:430 pysollib/tk/menubar.py:427
msgid "&Hint"
msgstr "Подсказать &ход"
-#: pysollib/tile/menubar.py:433 pysollib/tk/menubar.py:430
+#: pysollib/tile/menubar.py:431 pysollib/tk/menubar.py:428
msgid "Highlight p&iles"
msgstr "П&оказать группы"
-#: pysollib/tile/menubar.py:436 pysollib/tk/menubar.py:433
+#: pysollib/tile/menubar.py:432 pysollib/tk/menubar.py:429
+msgid "&Find card"
+msgstr "&Найти карту"
+
+#: pysollib/tile/menubar.py:434 pysollib/tk/menubar.py:431
msgid "&Demo"
msgstr "&Демо"
-#: pysollib/tile/menubar.py:437 pysollib/tk/menubar.py:434
+#: pysollib/tile/menubar.py:435 pysollib/tk/menubar.py:432
msgid "Demo (&all games)"
msgstr "Демо (&все игры)"
-#: pysollib/tile/menubar.py:439 pysollib/tk/menubar.py:436
-msgid "Piles description"
-msgstr "Описания ячеек"
+#: pysollib/tile/menubar.py:437 pysollib/tile/menubar.py:439
+#: pysollib/tk/menubar.py:434 pysollib/tk/menubar.py:436
+msgid "&Solver (experimental)"
+msgstr ""
-#: pysollib/tile/menubar.py:443 pysollib/tk/menubar.py:440
+#: pysollib/tile/menubar.py:441 pysollib/tk/menubar.py:438
+msgid "&Piles description"
+msgstr "Описания &ячеек"
+
+#: pysollib/tile/menubar.py:445 pysollib/tk/menubar.py:442
msgid "&Options"
msgstr "&Настройка"
-#: pysollib/tile/menubar.py:444 pysollib/tk/menubar.py:441
+#: pysollib/tile/menubar.py:446 pysollib/tk/menubar.py:443
msgid "&Player options..."
msgstr "Настройки &игрока..."
-#: pysollib/tile/menubar.py:445 pysollib/tk/menubar.py:442
+#: pysollib/tile/menubar.py:447 pysollib/tk/menubar.py:444
msgid "&Automatic play"
msgstr "Настройки &автоматической игры"
-#: pysollib/tile/menubar.py:446 pysollib/tk/menubar.py:443
+#: pysollib/tile/menubar.py:448 pysollib/tk/menubar.py:445
msgid "Auto &face up"
msgstr "Автоматически &переворачивать"
-#: pysollib/tile/menubar.py:447 pysollib/tk/menubar.py:444
+#: pysollib/tile/menubar.py:449 pysollib/tk/menubar.py:446
msgid "A&uto drop"
msgstr "А&втоматически сбрасывать карты"
-#: pysollib/tile/menubar.py:448 pysollib/tk/menubar.py:445
+#: pysollib/tile/menubar.py:450 pysollib/tk/menubar.py:447
msgid "Auto &deal"
msgstr "Автоматически &сдавать карты"
-#: pysollib/tile/menubar.py:450 pysollib/tk/menubar.py:447
+#: pysollib/tile/menubar.py:452 pysollib/tk/menubar.py:449
msgid "&Quick play"
msgstr "&Быстрая игра"
-#: pysollib/tile/menubar.py:451 pysollib/tk/menubar.py:448
+#: pysollib/tile/menubar.py:453 pysollib/tk/menubar.py:450
msgid "Assist &level"
msgstr "&Уровень подсказки"
-#: pysollib/tile/menubar.py:452 pysollib/tk/menubar.py:449
+#: pysollib/tile/menubar.py:454 pysollib/tk/menubar.py:451
msgid "Enable &undo"
msgstr "Разрешить &возврат хода"
-#: pysollib/tile/menubar.py:453 pysollib/tk/menubar.py:450
+#: pysollib/tile/menubar.py:455 pysollib/tk/menubar.py:452
msgid "Enable &bookmarks"
msgstr "Разрешить &закладки"
-#: pysollib/tile/menubar.py:454 pysollib/tk/menubar.py:451
+#: pysollib/tile/menubar.py:456 pysollib/tk/menubar.py:453
msgid "Enable &hint"
msgstr "Разрешить &подсказки"
-#: pysollib/tile/menubar.py:455 pysollib/tk/menubar.py:452
+#: pysollib/tile/menubar.py:457 pysollib/tk/menubar.py:454
msgid "Enable highlight p&iles"
msgstr "Разрешить показывать к&учи"
-#: pysollib/tile/menubar.py:456 pysollib/tk/menubar.py:453
+#: pysollib/tile/menubar.py:458 pysollib/tk/menubar.py:455
msgid "Enable highlight &cards"
msgstr "Разрешить показывать &карты"
-#: pysollib/tile/menubar.py:457 pysollib/tk/menubar.py:454
+#: pysollib/tile/menubar.py:459 pysollib/tk/menubar.py:456
msgid "Enable highlight same &rank"
msgstr "Разрешить показывать карты &одного достоинства"
-#: pysollib/tile/menubar.py:458 pysollib/tk/menubar.py:455
+#: pysollib/tile/menubar.py:460 pysollib/tk/menubar.py:457
msgid "Highlight &no matching"
msgstr "Подсветка отсутствия &совпадения"
-#: pysollib/tile/menubar.py:460 pysollib/tk/menubar.py:457
+#: pysollib/tile/menubar.py:462 pysollib/tk/menubar.py:459
msgid "&Show removed tiles (in Mahjongg games)"
msgstr "Показывать удалённые (в Маджонг)"
-#: pysollib/tile/menubar.py:461 pysollib/tk/menubar.py:458
+#: pysollib/tile/menubar.py:463 pysollib/tk/menubar.py:460
msgid "Show hint &arrow (in Shisen-Sho games)"
msgstr "Показывать стрелку (в Шисен-Сё)"
-#: pysollib/tile/menubar.py:463 pysollib/tk/menubar.py:460
+#: pysollib/tile/menubar.py:465 pysollib/tk/menubar.py:462
msgid "&Sound..."
msgstr "&Звук..."
-#: pysollib/tile/menubar.py:471 pysollib/tk/menubar.py:468
+#: pysollib/tile/menubar.py:473 pysollib/tk/menubar.py:470
msgid "Cards&et..."
msgstr "Коло&да..."
-#: pysollib/tile/menubar.py:472 pysollib/tk/menubar.py:469
+#: pysollib/tile/menubar.py:474 pysollib/tk/menubar.py:471
msgid "Table t&ile..."
msgstr "Игровой &стол..."
-#: pysollib/tile/menubar.py:474 pysollib/tk/menubar.py:471
+#: pysollib/tile/menubar.py:476 pysollib/tk/menubar.py:473
msgid "Card &background"
msgstr "&Рубашка карты"
-#: pysollib/tile/menubar.py:475 pysollib/tk/menubar.py:472
+#: pysollib/tile/menubar.py:477 pysollib/tk/menubar.py:474
msgid "Card &view"
msgstr "&Вид карты"
-#: pysollib/tile/menubar.py:476 pysollib/tk/menubar.py:473
+#: pysollib/tile/menubar.py:478 pysollib/tk/menubar.py:475
msgid "Card shado&w"
msgstr "Тень карты"
-#: pysollib/tile/menubar.py:477 pysollib/tk/menubar.py:474
+#: pysollib/tile/menubar.py:479 pysollib/tk/menubar.py:476
msgid "Shade &legal moves"
msgstr "Подсвечивать &разрешённые ходы"
-#: pysollib/tile/menubar.py:478 pysollib/tk/menubar.py:475
+#: pysollib/tile/menubar.py:480 pysollib/tk/menubar.py:477
msgid "&Negative cards bottom"
msgstr "&Негативные контуры карты"
-#: pysollib/tile/menubar.py:479 pysollib/tk/menubar.py:476
+#: pysollib/tile/menubar.py:481 pysollib/tk/menubar.py:478
msgid "Shrink face-down cards"
msgstr "Сжимать закрытые карты"
-#: pysollib/tile/menubar.py:480 pysollib/tk/menubar.py:477
+#: pysollib/tile/menubar.py:482 pysollib/tk/menubar.py:479
msgid "Shade &filled stacks"
msgstr "Затемнять заполненные ячейки"
-#: pysollib/tile/menubar.py:481 pysollib/tk/menubar.py:478
+#: pysollib/tile/menubar.py:483 pysollib/tk/menubar.py:480
msgid "A&nimations"
msgstr "Анимаци&я"
-#: pysollib/tile/menubar.py:482 pysollib/tk/menubar.py:479
+#: pysollib/tile/menubar.py:484 pysollib/tk/menubar.py:481
msgid "&None"
msgstr "&Нет"
-#: pysollib/tile/menubar.py:483 pysollib/tk/menubar.py:480
-msgid "&Timer based"
-msgstr "Базирующаяся на &таймере"
+#: pysollib/tile/menubar.py:485 pysollib/tk/menubar.py:482
+msgid "&Very fast"
+msgstr "&Очень быстрая"
-#: pysollib/tile/menubar.py:484 pysollib/tk/menubar.py:481
+#: pysollib/tile/menubar.py:486 pysollib/tk/menubar.py:483
msgid "&Fast"
msgstr "&Быстрая"
-#: pysollib/tile/menubar.py:485 pysollib/tk/menubar.py:482
+#: pysollib/tile/menubar.py:487 pysollib/tk/menubar.py:484
+msgid "&Medium"
+msgstr "С&редняя"
+
+#: pysollib/tile/menubar.py:488 pysollib/tk/menubar.py:485
msgid "&Slow"
msgstr "&Медленная"
-#: pysollib/tile/menubar.py:486 pysollib/tk/menubar.py:483
-msgid "&Very slow"
+#: pysollib/tile/menubar.py:489 pysollib/tk/menubar.py:486
+msgid "V&ery slow"
msgstr "&Очень медленная"
-#: pysollib/tile/menubar.py:488 pysollib/tk/menubar.py:485
+#: pysollib/tile/menubar.py:491 pysollib/tk/menubar.py:488
msgid "&Redeal animation"
msgstr "Анимация пере&сдачи"
-#: pysollib/tile/menubar.py:489 pysollib/tk/menubar.py:486
+#: pysollib/tile/menubar.py:493 pysollib/tk/menubar.py:490
msgid "&Winning animation"
msgstr "Анимация &победы"
-#: pysollib/tile/menubar.py:490 pysollib/tk/menubar.py:487
+#: pysollib/tile/menubar.py:494 pysollib/tk/menubar.py:491
msgid "&Mouse"
msgstr "&Мышь"
-#: pysollib/tile/menubar.py:491 pysollib/tk/menubar.py:488
+#: pysollib/tile/menubar.py:495 pysollib/tk/menubar.py:492
msgid "&Drag-and-Drop"
msgstr ""
-#: pysollib/tile/menubar.py:492 pysollib/tk/menubar.py:489
+#: pysollib/tile/menubar.py:496 pysollib/tk/menubar.py:493
msgid "&Point-and-Click"
msgstr ""
-#: pysollib/tile/menubar.py:493 pysollib/tk/menubar.py:490
+#: pysollib/tile/menubar.py:497 pysollib/tk/menubar.py:494
msgid "&Sticky mouse"
msgstr "&Липкая мышь"
-#: pysollib/tile/menubar.py:495 pysollib/tk/menubar.py:492
+#: pysollib/tile/menubar.py:499 pysollib/tk/menubar.py:496
msgid "Use mouse for undo/redo"
-msgstr "Использовать мышь для вперёд/назад"
+msgstr "Использовать мышь для отмены/повтора"
-#: pysollib/tile/menubar.py:497 pysollib/tk/menubar.py:494
+#: pysollib/tile/menubar.py:501 pysollib/tk/menubar.py:498
msgid "&Fonts..."
msgstr "&Шрифты..."
-#: pysollib/tile/menubar.py:498 pysollib/tk/menubar.py:495
+#: pysollib/tile/menubar.py:502 pysollib/tk/menubar.py:499
msgid "&Colors..."
msgstr "&Цвета..."
-#: pysollib/tile/menubar.py:499 pysollib/tk/menubar.py:496
+#: pysollib/tile/menubar.py:503 pysollib/tk/menubar.py:500
msgid "Time&outs..."
msgstr "Тайма&уты..."
-#: pysollib/tile/menubar.py:502 pysollib/tk/menubar.py:498
+#: pysollib/tile/menubar.py:506 pysollib/tk/menubar.py:502
msgid "&Toolbar"
msgstr "Панель и&нструментов"
-#: pysollib/tile/menubar.py:504 pysollib/tk/menubar.py:500
+#: pysollib/tile/menubar.py:508 pysollib/tk/menubar.py:504
msgid "Stat&usbar"
msgstr "Панель с&остояния"
-#: pysollib/tile/menubar.py:505 pysollib/tk/menubar.py:501
+#: pysollib/tile/menubar.py:509 pysollib/tk/menubar.py:505
msgid "Show &statusbar"
msgstr "Показывать панель состояния"
-#: pysollib/tile/menubar.py:506 pysollib/tk/menubar.py:502
+#: pysollib/tile/menubar.py:510 pysollib/tk/menubar.py:506
msgid "Show &number of cards"
msgstr "Показывать количество карт"
-#: pysollib/tile/menubar.py:507 pysollib/tk/menubar.py:503
+#: pysollib/tile/menubar.py:511 pysollib/tk/menubar.py:507
msgid "Show &help bar"
msgstr "Показывать панель помощи"
-#: pysollib/tile/menubar.py:508 pysollib/tk/menubar.py:504
+#: pysollib/tile/menubar.py:512 pysollib/tk/menubar.py:508
msgid "Save games &geometry"
msgstr "Сохранение &геометрии игры"
-#: pysollib/tile/menubar.py:509 pysollib/tk/menubar.py:505
+#: pysollib/tile/menubar.py:513 pysollib/tk/menubar.py:509
msgid "&Demo logo"
msgstr "Д&емо лого"
-#: pysollib/tile/menubar.py:510 pysollib/tk/menubar.py:506
+#: pysollib/tile/menubar.py:514 pysollib/tk/menubar.py:510
msgid "Startup splash sc&reen"
msgstr "О&кно запуска"
-#: pysollib/tile/menubar.py:516 pysollib/tk/menubar.py:512
+#: pysollib/tile/menubar.py:520 pysollib/tk/menubar.py:516
msgid "&Help"
msgstr "&Помощь"
-#: pysollib/tile/menubar.py:517 pysollib/tk/menubar.py:513
+#: pysollib/tile/menubar.py:521 pysollib/tk/menubar.py:517
msgid "&Contents"
msgstr "&Содержание"
-#: pysollib/tile/menubar.py:518 pysollib/tk/menubar.py:514
+#: pysollib/tile/menubar.py:522 pysollib/tk/menubar.py:518
msgid "&How to play"
msgstr "Как &играть"
-#: pysollib/tile/menubar.py:519 pysollib/tk/menubar.py:515
+#: pysollib/tile/menubar.py:523 pysollib/tk/menubar.py:519
msgid "&Rules for this game"
msgstr "&Правила текущей игры"
-#: pysollib/tile/menubar.py:520 pysollib/tk/menubar.py:516
+#: pysollib/tile/menubar.py:524 pysollib/tk/menubar.py:520
msgid "&License terms"
msgstr "&Лицензия"
-#: pysollib/tile/menubar.py:637 pysollib/tk/menubar.py:633
+#: pysollib/tile/menubar.py:641 pysollib/tk/menubar.py:637
msgid "All &games..."
msgstr "&Все игры..."
-#: pysollib/tile/menubar.py:639 pysollib/tk/menubar.py:635
+#: pysollib/tile/menubar.py:643 pysollib/tk/menubar.py:639
msgid "Playable pre&view..."
msgstr "Играемый &предпросмотр..."
-#: pysollib/tile/menubar.py:690 pysollib/tk/menubar.py:686
+#: pysollib/tile/menubar.py:694 pysollib/tk/menubar.py:690
msgid "&Mahjongg games"
msgstr "Игры маджонг"
-#: pysollib/tile/menubar.py:728 pysollib/tk/menubar.py:724
+#: pysollib/tile/menubar.py:732 pysollib/tk/menubar.py:728
msgid "&Popular games"
msgstr "&Популярные игры"
-#: pysollib/tile/menubar.py:736 pysollib/tk/menubar.py:732
+#: pysollib/tile/menubar.py:740 pysollib/tk/menubar.py:736
msgid "&French games"
msgstr "&Классические игры"
-#: pysollib/tile/menubar.py:743 pysollib/tk/menubar.py:739
+#: pysollib/tile/menubar.py:747 pysollib/tk/menubar.py:743
msgid "&Oriental games"
msgstr "&Восточные игры"
-#: pysollib/tile/menubar.py:751 pysollib/tk/menubar.py:747
+#: pysollib/tile/menubar.py:755 pysollib/tk/menubar.py:751
msgid "&Special games"
msgstr "&Особые игры"
-#: pysollib/tile/menubar.py:757 pysollib/tk/menubar.py:753
+#: pysollib/tile/menubar.py:761 pysollib/tk/menubar.py:757
msgid "&All games by name"
msgstr "&Все игры по имени"
-#: pysollib/tile/menubar.py:1030 pysollib/tk/menubar.py:1026
+#: pysollib/tile/menubar.py:1040 pysollib/tk/menubar.py:1036
#: data/glade-translations:72
msgid "Sound settings"
msgstr "Настройка звука"
-#: pysollib/tile/menubar.py:1138 pysollib/tk/menubar.py:1138
+#: pysollib/tile/menubar.py:1148 pysollib/tk/menubar.py:1148
msgid "Select "
msgstr "Выбрать "
-#: pysollib/tile/menubar.py:1185 pysollib/tk/menubar.py:1190
+#: pysollib/tile/menubar.py:1194 pysollib/tk/menubar.py:1195
msgid "Select table background"
msgstr "Выбрать фоновое изображение"
-#: pysollib/tile/menubar.py:1329
+#: pysollib/tile/menubar.py:1339
msgid "Change theme"
msgstr "Изменение темы"
-#: pysollib/tile/menubar.py:1330
+#: pysollib/tile/menubar.py:1340
msgid ""
"This settings will take effect\n"
"the next time you restart "
@@ -2658,33 +2689,33 @@ msgstr ""
"Эта установка вступит в силу\n"
"при следующем запуске "
-#: pysollib/tile/menubar.py:1338
+#: pysollib/tile/menubar.py:1347
msgid "Set t&heme"
msgstr "Установить &тему"
-#: pysollib/tile/menubar.py:1344
+#: pysollib/tile/menubar.py:1353
#, fuzzy
msgid "Default"
msgstr "Сдача"
-#: pysollib/tile/menubar.py:1345
+#: pysollib/tile/menubar.py:1354
#, fuzzy
msgid "Classic"
msgstr "Классический вид"
-#: pysollib/tile/menubar.py:1346
+#: pysollib/tile/menubar.py:1355
msgid "Revitalized"
msgstr ""
-#: pysollib/tile/menubar.py:1347
+#: pysollib/tile/menubar.py:1356
msgid "Windows native"
msgstr ""
-#: pysollib/tile/menubar.py:1348
+#: pysollib/tile/menubar.py:1357
msgid "XP Native"
msgstr ""
-#: pysollib/tile/menubar.py:1349
+#: pysollib/tile/menubar.py:1358
msgid "Aqua"
msgstr ""
@@ -2763,12 +2794,12 @@ msgstr "Большие колоды"
msgid "XLarge cardsets"
msgstr "Очень большие колоды"
-#: pysollib/tile/selectcardset.py:236 pysollib/tk/menubar.py:1135
+#: pysollib/tile/selectcardset.py:236 pysollib/tk/menubar.py:1145
msgid "&Info..."
msgstr "&Информация..."
-#: pysollib/tile/selectcardset.py:237 pysollib/tk/menubar.py:1133
-#: pysollib/tk/menubar.py:1135 pysollib/tk/selectcardset.py:241
+#: pysollib/tile/selectcardset.py:237 pysollib/tk/menubar.py:1143
+#: pysollib/tk/menubar.py:1145 pysollib/tk/selectcardset.py:241
msgid "&Load"
msgstr "&Загрузить"
@@ -3014,28 +3045,28 @@ msgstr "Пересдач:"
msgid "Played:"
msgstr "Играл:"
-#: pysollib/tile/selectgame.py:361 pysollib/tile/tkstats.py:107
-#: pysollib/tile/tkstats.py:159 pysollib/tk/selectgame.py:371
-#: pysollib/tk/tkstats.py:111 pysollib/tk/tkstats.py:163
+#: pysollib/tile/selectgame.py:361 pysollib/tile/tkstats.py:109
+#: pysollib/tile/tkstats.py:161 pysollib/tk/selectgame.py:371
+#: pysollib/tk/tkstats.py:113 pysollib/tk/tkstats.py:165
#: data/glade-translations:9 data/glade-translations:13
msgid "Won:"
msgstr "Выиграл:"
-#: pysollib/tile/selectgame.py:362 pysollib/tile/tkstats.py:108
-#: pysollib/tile/tkstats.py:160 pysollib/tk/selectgame.py:372
-#: pysollib/tk/tkstats.py:112 pysollib/tk/tkstats.py:164
+#: pysollib/tile/selectgame.py:362 pysollib/tile/tkstats.py:110
+#: pysollib/tile/tkstats.py:162 pysollib/tk/selectgame.py:372
+#: pysollib/tk/tkstats.py:114 pysollib/tk/tkstats.py:166
#: data/glade-translations:11 data/glade-translations:14
msgid "Lost:"
msgstr "Проиграл:"
-#: pysollib/tile/selectgame.py:363 pysollib/tile/tkstats.py:622
-#: pysollib/tk/selectgame.py:373 pysollib/tk/tkstats.py:738
+#: pysollib/tile/selectgame.py:363 pysollib/tile/tkstats.py:626
+#: pysollib/tk/selectgame.py:373 pysollib/tk/tkstats.py:740
#: data/glade-translations:18
msgid "Playing time:"
msgstr "Игровое время:"
-#: pysollib/tile/selectgame.py:364 pysollib/tile/tkstats.py:629
-#: pysollib/tk/selectgame.py:374 pysollib/tk/tkstats.py:745
+#: pysollib/tile/selectgame.py:364 pysollib/tile/tkstats.py:633
+#: pysollib/tk/selectgame.py:374 pysollib/tk/tkstats.py:747
#: data/glade-translations:19
msgid "Moves:"
msgstr "Ходов:"
@@ -3092,6 +3123,71 @@ msgstr "М&онотонный цвет..."
msgid "Select table color"
msgstr "Выбрать цвет"
+#: pysollib/tile/solverdialog.py:79 pysollib/tk/solverdialog.py:78
+#: data/glade-translations:8 data/glade-translations:28
+msgid "Game:"
+msgstr "Игра:"
+
+#: pysollib/tile/solverdialog.py:98 pysollib/tk/solverdialog.py:101
+msgid "Solving method:"
+msgstr "Метод решения:"
+
+#: pysollib/tile/solverdialog.py:111 pysollib/tk/solverdialog.py:114
+msgid "Preset:"
+msgstr ""
+
+#: pysollib/tile/solverdialog.py:135 pysollib/tk/solverdialog.py:138
+msgid "Max iterations:"
+msgstr "Максимум итераций:"
+
+#: pysollib/tile/solverdialog.py:145 pysollib/tk/solverdialog.py:148
+msgid "Max depth:"
+msgstr "Максимальная глубина:"
+
+#: pysollib/tile/solverdialog.py:156 pysollib/tk/solverdialog.py:159
+msgid "Show progress"
+msgstr "Показывать прогресс"
+
+#: pysollib/tile/solverdialog.py:160 pysollib/tk/solverdialog.py:163
+msgid "Progress"
+msgstr "Прогресс"
+
+#: pysollib/tile/solverdialog.py:167 pysollib/tk/solverdialog.py:170
+msgid "Iteration:"
+msgstr "Итераций:"
+
+#: pysollib/tile/solverdialog.py:173 pysollib/tk/solverdialog.py:176
+msgid "Depth:"
+msgstr "Глубина:"
+
+#: pysollib/tile/solverdialog.py:179 pysollib/tk/solverdialog.py:182
+msgid "Stored-States:"
+msgstr "Сохранённых состояний: "
+
+#: pysollib/tile/solverdialog.py:202 pysollib/tk/solverdialog.py:205
+msgid "&Close"
+msgstr "&Закрыть"
+
+#: pysollib/tile/solverdialog.py:202 pysollib/tk/solverdialog.py:205
+msgid "&New"
+msgstr "&Новая"
+
+#: pysollib/tile/solverdialog.py:202 pysollib/tk/solverdialog.py:205
+msgid "&Play"
+msgstr "&Играть"
+
+#: pysollib/tile/solverdialog.py:202 pysollib/tk/solverdialog.py:205
+msgid "&Start"
+msgstr "Н&ачать"
+
+#: pysollib/tile/solverdialog.py:272 pysollib/tk/solverdialog.py:275
+msgid "This game is solveable in %s moves."
+msgstr "Игра может быть решена за %s ходов."
+
+#: pysollib/tile/solverdialog.py:275 pysollib/tk/solverdialog.py:278
+msgid "I could not solve this game."
+msgstr "Не смог решить игру."
+
#: pysollib/tile/soundoptionsdialog.py:77 pysollib/tk/soundoptionsdialog.py:75
msgid "Are You Sure"
msgstr "Вы уверены?"
@@ -3211,11 +3307,11 @@ msgstr ""
"Изменения установок DirectX вступят в силу\n"
"при следующем запуске "
-#: pysollib/tile/statusbar.py:148 pysollib/tk/statusbar.py:159
+#: pysollib/tile/statusbar.py:151 pysollib/tk/statusbar.py:162
msgid "Moves/Total moves"
msgstr "Ходов/Всего ходов"
-#: pysollib/tile/statusbar.py:150 pysollib/tk/statusbar.py:161
+#: pysollib/tile/statusbar.py:153 pysollib/tk/statusbar.py:164
msgid "Games played: won/lost"
msgstr "Игр: выиграно/проиграно"
@@ -3297,73 +3393,73 @@ msgstr ""
msgid "Unable to service request:\n"
msgstr "Невозможно выполнить запрос:\n"
-#: pysollib/tile/tkstats.py:79 pysollib/tk/tkstats.py:78
+#: pysollib/tile/tkstats.py:81 pysollib/tk/tkstats.py:80
msgid "Demo games"
msgstr "Демо игры"
-#: pysollib/tile/tkstats.py:91 pysollib/tk/tkstats.py:95
+#: pysollib/tile/tkstats.py:93 pysollib/tk/tkstats.py:97
#: data/glade-translations:16
msgid "Total"
msgstr "Всего"
-#: pysollib/tile/tkstats.py:93 pysollib/tk/tkstats.py:97
+#: pysollib/tile/tkstats.py:95 pysollib/tk/tkstats.py:99
#: data/glade-translations:12
msgid "Current session"
msgstr "Текущая сессия"
-#: pysollib/tile/tkstats.py:109 pysollib/tile/tkstats.py:161
-#: pysollib/tk/tkstats.py:113 pysollib/tk/tkstats.py:165
+#: pysollib/tile/tkstats.py:111 pysollib/tile/tkstats.py:163
+#: pysollib/tk/tkstats.py:115 pysollib/tk/tkstats.py:167
#: data/glade-translations:10 data/glade-translations:15
msgid "Total:"
msgstr "Всего:"
-#: pysollib/tile/tkstats.py:204 pysollib/tk/tkstats.py:278
+#: pysollib/tile/tkstats.py:206 pysollib/tk/tkstats.py:280
msgid "No games"
msgstr "Нет игр"
-#: pysollib/tile/tkstats.py:215 pysollib/tk/tkstats.py:289
+#: pysollib/tile/tkstats.py:217 pysollib/tk/tkstats.py:291
msgid "&All games..."
msgstr "&Все игры..."
-#: pysollib/tile/tkstats.py:217 pysollib/tk/tkstats.py:291
+#: pysollib/tile/tkstats.py:219 pysollib/tk/tkstats.py:293
msgid "&Reset..."
msgstr "О&чистить..."
-#: pysollib/tile/tkstats.py:383
+#: pysollib/tile/tkstats.py:387
msgid "&Play this game"
msgstr "&Выбрать игру"
-#: pysollib/tile/tkstats.py:385 pysollib/tile/tkstats.py:458
-#: pysollib/tile/tkstats.py:481 pysollib/tk/tkstats.py:515
-#: pysollib/tk/tkstats.py:581 pysollib/tk/tkstats.py:596
+#: pysollib/tile/tkstats.py:389 pysollib/tile/tkstats.py:462
+#: pysollib/tile/tkstats.py:485 pysollib/tk/tkstats.py:517
+#: pysollib/tk/tkstats.py:583 pysollib/tk/tkstats.py:598
msgid "&Save to file"
msgstr "&Сохранить в файл"
-#: pysollib/tile/tkstats.py:386 pysollib/tk/tkstats.py:516
+#: pysollib/tile/tkstats.py:390 pysollib/tk/tkstats.py:518
msgid "&Reset all..."
msgstr "О&чистить все..."
-#: pysollib/tile/tkstats.py:457 pysollib/tk/tkstats.py:581
+#: pysollib/tile/tkstats.py:461 pysollib/tk/tkstats.py:583
msgid "Session &log..."
msgstr "&Лог сессии..."
-#: pysollib/tile/tkstats.py:480 pysollib/tk/tkstats.py:596
+#: pysollib/tile/tkstats.py:484 pysollib/tk/tkstats.py:598
msgid "&Full log..."
msgstr "&Полный лог..."
-#: pysollib/tile/tkstats.py:496 pysollib/tk/tkstats.py:611
+#: pysollib/tile/tkstats.py:500 pysollib/tk/tkstats.py:613
msgid "Highlight piles: "
msgstr "Подсветка групп: "
-#: pysollib/tile/tkstats.py:497 pysollib/tk/tkstats.py:612
+#: pysollib/tile/tkstats.py:501 pysollib/tk/tkstats.py:614
msgid "Highlight cards: "
msgstr "Подсветка карт: "
-#: pysollib/tile/tkstats.py:498 pysollib/tk/tkstats.py:613
+#: pysollib/tile/tkstats.py:502 pysollib/tk/tkstats.py:615
msgid "Highlight same rank: "
msgstr "Подсветка карт одного достоинства: "
-#: pysollib/tile/tkstats.py:501 pysollib/tk/tkstats.py:616
+#: pysollib/tile/tkstats.py:505 pysollib/tk/tkstats.py:618
msgid ""
"\n"
"Redeals: "
@@ -3371,7 +3467,7 @@ msgstr ""
"\n"
"Раздач: "
-#: pysollib/tile/tkstats.py:502 pysollib/tk/tkstats.py:617
+#: pysollib/tile/tkstats.py:506 pysollib/tk/tkstats.py:619
msgid ""
"\n"
"Cards in Talon: "
@@ -3379,7 +3475,7 @@ msgstr ""
"\n"
"Карт в колоде: "
-#: pysollib/tile/tkstats.py:504 pysollib/tk/tkstats.py:619
+#: pysollib/tile/tkstats.py:508 pysollib/tk/tkstats.py:621
msgid ""
"\n"
"Cards in Waste: "
@@ -3387,7 +3483,7 @@ msgstr ""
"\n"
"Карт в сбросе: "
-#: pysollib/tile/tkstats.py:506 pysollib/tk/tkstats.py:621
+#: pysollib/tile/tkstats.py:510 pysollib/tk/tkstats.py:623
msgid ""
"\n"
"Cards in Foundations: "
@@ -3395,82 +3491,127 @@ msgstr ""
"\n"
"Карт в игре: "
-#: pysollib/tile/tkstats.py:509 pysollib/tk/tkstats.py:624
+#: pysollib/tile/tkstats.py:513 pysollib/tk/tkstats.py:626
msgid "Game status"
msgstr "Статус игры"
-#: pysollib/tile/tkstats.py:512 pysollib/tk/tkstats.py:627
+#: pysollib/tile/tkstats.py:516 pysollib/tk/tkstats.py:629
msgid "Playing time: "
msgstr "Игровое время: "
-#: pysollib/tile/tkstats.py:513 pysollib/tk/tkstats.py:628
+#: pysollib/tile/tkstats.py:517 pysollib/tk/tkstats.py:630
msgid "Started at: "
msgstr "Игра начата: "
-#: pysollib/tile/tkstats.py:514 pysollib/tk/tkstats.py:629
+#: pysollib/tile/tkstats.py:518 pysollib/tk/tkstats.py:631
msgid "Moves: "
msgstr "Ходов: "
-#: pysollib/tile/tkstats.py:515 pysollib/tk/tkstats.py:630
+#: pysollib/tile/tkstats.py:519 pysollib/tk/tkstats.py:632
msgid "Undo moves: "
msgstr "Отменено ходов: "
-#: pysollib/tile/tkstats.py:516 pysollib/tk/tkstats.py:631
+#: pysollib/tile/tkstats.py:520 pysollib/tk/tkstats.py:633
msgid "Bookmark moves: "
msgstr "Ходов по закладкам: "
-#: pysollib/tile/tkstats.py:517 pysollib/tk/tkstats.py:632
+#: pysollib/tile/tkstats.py:521 pysollib/tk/tkstats.py:634
msgid "Demo moves: "
msgstr "Демо ходов: "
-#: pysollib/tile/tkstats.py:518 pysollib/tk/tkstats.py:633
+#: pysollib/tile/tkstats.py:522 pysollib/tk/tkstats.py:635
msgid "Total player moves: "
msgstr "Всего ходов игрока:"
-#: pysollib/tile/tkstats.py:519 pysollib/tk/tkstats.py:634
+#: pysollib/tile/tkstats.py:523 pysollib/tk/tkstats.py:636
msgid "Total moves in this game: "
msgstr "Всего ходов в этой игре: "
-#: pysollib/tile/tkstats.py:520 pysollib/tk/tkstats.py:635
+#: pysollib/tile/tkstats.py:524 pysollib/tk/tkstats.py:637
msgid "Hints: "
msgstr "Подсказок: "
-#: pysollib/tile/tkstats.py:524 pysollib/tk/tkstats.py:639
+#: pysollib/tile/tkstats.py:528 pysollib/tk/tkstats.py:641
msgid "&Statistics..."
msgstr "&Статистика..."
-#: pysollib/tile/tkstats.py:549 pysollib/tk/tkstats.py:665
+#: pysollib/tile/tkstats.py:553 pysollib/tk/tkstats.py:667
msgid "N"
msgstr "N"
-#: pysollib/tile/tkstats.py:558 pysollib/tk/tkstats.py:674
+#: pysollib/tile/tkstats.py:562 pysollib/tk/tkstats.py:676
msgid "Result"
msgstr "Результат"
-#: pysollib/tile/tkstats.py:614 pysollib/tk/tkstats.py:730
+#: pysollib/tile/tkstats.py:618 pysollib/tk/tkstats.py:732
#: data/glade-translations:21
msgid "Minimum"
msgstr "Минимум"
-#: pysollib/tile/tkstats.py:615 pysollib/tk/tkstats.py:731
+#: pysollib/tile/tkstats.py:619 pysollib/tk/tkstats.py:733
#: data/glade-translations:22
msgid "Maximum"
msgstr "Максимум"
-#: pysollib/tile/tkstats.py:616 pysollib/tk/tkstats.py:732
+#: pysollib/tile/tkstats.py:620 pysollib/tk/tkstats.py:734
#: data/glade-translations:23
msgid "Average"
msgstr "Среднее"
-#: pysollib/tile/tkstats.py:636 pysollib/tk/tkstats.py:752
+#: pysollib/tile/tkstats.py:640 pysollib/tk/tkstats.py:754
#: data/glade-translations:20
msgid "Total moves:"
msgstr "Всего ходов:"
-#: pysollib/tile/tkstats.py:667 pysollib/tk/tkstats.py:783
+#: pysollib/tile/tkstats.py:671 pysollib/tk/tkstats.py:785
msgid "No TOP for this game"
msgstr "TOP для текущей игры отсутствует"
+#: pysollib/tile/tkstats.py:743 pysollib/tile/tkstats.py:759
+#: pysollib/tile/tkstats.py:859 pysollib/tk/tkstats.py:857
+#: pysollib/tk/tkstats.py:873 pysollib/tk/tkstats.py:985
+msgid "Games/day"
+msgstr "Игр за день"
+
+#: pysollib/tile/tkstats.py:744 pysollib/tile/tkstats.py:861
+#: pysollib/tk/tkstats.py:858 pysollib/tk/tkstats.py:987
+msgid "Games/week"
+msgstr "Игр за неделю"
+
+#: pysollib/tile/tkstats.py:789 pysollib/tk/tkstats.py:903
+#: data/glade-translations:30
+msgid "All games"
+msgstr "Все игры"
+
+#: pysollib/tile/tkstats.py:794 pysollib/tk/tkstats.py:909
+#: data/glade-translations:17
+msgid "Current game"
+msgstr "Текущая игра"
+
+#: pysollib/tile/tkstats.py:799 pysollib/tk/tkstats.py:916
+msgid "Statistics for"
+msgstr "Статистика за"
+
+#: pysollib/tile/tkstats.py:804 pysollib/tk/tkstats.py:923
+msgid "Last 7 days"
+msgstr "Последние 7 дней"
+
+#: pysollib/tile/tkstats.py:805 pysollib/tk/tkstats.py:924
+msgid "Last month"
+msgstr "Последний месяц"
+
+#: pysollib/tile/tkstats.py:806 pysollib/tk/tkstats.py:925
+msgid "Last year"
+msgstr "Последний год"
+
+#: pysollib/tile/tkstats.py:807 pysollib/tk/tkstats.py:926
+msgid "All time"
+msgstr "Всё время"
+
+#: pysollib/tile/tkstats.py:813 pysollib/tk/tkstats.py:934
+msgid "Show graphs"
+msgstr "Показывать графики"
+
#: pysollib/tile/toolbar.py:189 pysollib/tk/toolbar.py:192
msgid "New"
msgstr "Новая"
@@ -3551,7 +3692,7 @@ msgstr "Игрок"
msgid "Player options"
msgstr "Установки игрока"
-#: pysollib/tile/toolbar.py:390 pysollib/tk/toolbar.py:431
+#: pysollib/tile/toolbar.py:395 pysollib/tk/toolbar.py:436
msgid "Toolbar"
msgstr "Панель инструментов"
@@ -3599,14 +3740,6 @@ msgstr "набор карт"
msgid "Game Statistics"
msgstr "Статистика игры"
-#: data/glade-translations:8 data/glade-translations:28
-msgid "Game:"
-msgstr "Игра:"
-
-#: data/glade-translations:17
-msgid "Current game"
-msgstr "Текущая игра"
-
#: data/glade-translations:24
msgid "Summary"
msgstr "Сводка"
@@ -3615,14 +3748,13 @@ msgstr "Сводка"
msgid "Total moves"
msgstr "Всего ходов"
-#: data/glade-translations:30
-msgid "All games"
-msgstr "Все игры"
-
#: data/glade-translations:57
msgid "Set font"
msgstr "Настроить шрифт"
+#~ msgid "&Timer based"
+#~ msgstr "Базирующаяся на &таймере"
+
#~ msgid "Relief"
#~ msgstr "Рельеф"
diff --git a/pysollib/actions.py b/pysollib/actions.py
index 44ca65f6..f6229c99 100644
--- a/pysollib/actions.py
+++ b/pysollib/actions.py
@@ -50,6 +50,7 @@ from stats import FileStatsFormatter
from pysoltk import SingleGame_StatsDialog, AllGames_StatsDialog
from pysoltk import FullLog_StatsDialog, SessionLog_StatsDialog
from pysoltk import Status_StatsDialog, Top_StatsDialog
+from pysoltk import ProgressionDialog
from pysoltk import GameInfoDialog
# toolkit imports
@@ -61,6 +62,7 @@ from pysoltk import ColorsDialog
from pysoltk import FontsDialog
from pysoltk import EditTextDialog
from pysoltk import create_find_card_dialog
+from pysoltk import create_solver_dialog
from help import help_about, help_html
gettext = _
@@ -473,11 +475,13 @@ class PysolMenubarActions:
def mDrop(self, *args):
if self._cancelDrag(): return
- self.game.autoPlay(autofaceup=-1, autodrop=1)
+ ##self.game.autoPlay(autofaceup=-1, autodrop=1)
+ self.game.autoDrop(autofaceup=-1)
def mDrop1(self, *args):
if self._cancelDrag(): return
- self.game.autoPlay(autofaceup=1, autodrop=1)
+ ##self.game.autoPlay(autofaceup=1, autodrop=1)
+ self.game.autoDrop(autofaceup=1)
def mStatus(self, *args):
if self._cancelDrag(break_pause=False): return
@@ -495,6 +499,9 @@ class PysolMenubarActions:
create_find_card_dialog(self.game.top, self.game,
self.app.getFindCardImagesDir())
+ def mSolver(self, *args):
+ create_solver_dialog(self.game.top, self.app)
+
def mEditGameComment(self, *args):
if self._cancelDrag(break_pause=False): return
game, gi = self.game, self.game.gameinfo
@@ -593,6 +600,9 @@ class PysolMenubarActions:
elif mode == 106:
header = _("Game Info")
d = GameInfoDialog(self.top, header, self.app)
+ elif mode == 107:
+ header = _("Statistics progression")
+ d = ProgressionDialog(self.top, header, self.app, player, gameid=self.game.id)
elif mode == 202:
# print stats to file
write_method = FileStatsFormatter.writeStats
@@ -698,9 +708,7 @@ class PysolMenubarActions:
if self._cancelDrag(break_pause=False): return
d = ColorsDialog(self.top, _("Set colors"), self.app)
text_color = self.app.opt.colors['text']
- use_default_text_color = self.app.opt.use_default_text_color
if d.status == 0 and d.button == 0:
- self.app.opt.use_default_text_color = d.use_default_color
self.app.opt.colors['text'] = d.text_color
self.app.opt.colors['piles'] = d.piles_color
self.app.opt.colors['cards_1'] = d.cards_1_color
@@ -710,9 +718,8 @@ class PysolMenubarActions:
self.app.opt.colors['hintarrow'] = d.hintarrow_color
self.app.opt.colors['not_matching'] = d.not_matching_color
#
- if (text_color != self.app.opt.colors['text'] or
- use_default_text_color != self.app.opt.use_default_text_color):
- self.app.setTile(self.app.tabletile_index)
+ if text_color != self.app.opt.colors['text']:
+ self.app.setTile(self.app.tabletile_index, force=True)
def mOptFonts(self, *args):
if self._cancelDrag(break_pause=False): return
diff --git a/pysollib/app.py b/pysollib/app.py
index 985f38fc..11a5dc86 100644
--- a/pysollib/app.py
+++ b/pysollib/app.py
@@ -71,6 +71,8 @@ from pysoltk import SelectDialogTreeData
from pysoltk import HTMLViewer
from pysoltk import TOOLBAR_BUTTONS
from pysoltk import destroy_find_card_dialog
+from pysoltk import destroy_solver_dialog
+from pysoltk import connect_game_solver_dialog
from help import help_about, destroy_help_html
gettext = _
@@ -102,7 +104,7 @@ class Options:
self.mahjongg_show_removed = False
self.mahjongg_create_solvable = True
self.shisen_show_hint = True
- self.animations = 2 # default to Timer based
+ self.animations = 2 # default to Fast
self.redeal_animation = True
self.win_animation = True
self.shadow = True
@@ -112,13 +114,14 @@ class Options:
self.demo_logo = True
self.tile_theme = 'default'
if WIN_SYSTEM == 'win32':
- self.tile_theme = 'winnative'
+ self.tile_theme = self.default_tile_theme = 'winnative'
if sys.getwindowsversion() >= (5, 1): # xp
self.tile_theme = 'xpnative'
elif WIN_SYSTEM == 'x11':
- self.tile_theme = 'step'
+ self.tile_theme = 'clam'
+ self.default_tile_theme = 'default'
elif WIN_SYSTEM == 'aqua':
- self.tile_theme = 'aqua'
+ self.tile_theme = self.default_tile_theme = 'aqua'
self.toolbar = 1 # 0 == hide, 1,2,3,4 == top, bottom, lef, right
##self.toolbar_style = 'default'
self.toolbar_style = 'bluecurve'
@@ -190,7 +193,6 @@ class Options:
'hintarrow': '#303030',
'not_matching': '#ff0000',
}
- self.use_default_text_color = False
# delays
self.timeouts = {
'hint': 1.0,
@@ -708,10 +710,12 @@ class Application:
except:
traceback.print_exc()
pass
- # save game geometry
self.wm_save_state()
+ # save game geometry
if self.opt.save_games_geometry and not self.opt.wm_maximized:
- geom = (self.canvas.winfo_width(), self.canvas.winfo_height())
+ w = self.canvas.winfo_width()
+ h = self.canvas.winfo_height()
+ geom = (w-2, h-2) # XXX: subtract canvas borderwidth
self.opt.games_geometry[self.game.id] = geom
self.freeGame()
#
@@ -726,8 +730,9 @@ class Application:
# hide main window
self.wm_withdraw()
#
- destroy_find_card_dialog()
destroy_help_html()
+ destroy_find_card_dialog()
+ destroy_solver_dialog()
# update options
self.opt.last_gameid = id
# save options
@@ -1005,7 +1010,7 @@ class Application:
#from pprint import pprint; pprint(self.opt.cardset)
def loadCardset(self, cs, id=0, update=7, progress=None):
- #print 'loadCardset', cs.ident
+ ##print 'loadCardset', cs.ident
r = 0
if cs is None or cs.error:
return 0
@@ -1049,6 +1054,10 @@ class Application:
elif self.images is not None:
##self.images.destruct()
destruct(self.images)
+ #
+ if self.cardset and self.cardset.ident != cs.ident and \
+ self.cardset.type == cs.type:
+ self.opt.games_geometry = {} # clear saved games geometry
# update
self.images = images
self.subsampled_images = simages
@@ -1381,6 +1390,9 @@ Please select a %s type %s.
names.sort()
return names
+ def getGamesForSolver(self):
+ return self.gdb.getGamesForSolver()
+
#
# plugins
#
@@ -1591,7 +1603,6 @@ Please select a %s type %s.
##print dirs
s = "((\\" + ")|(\\".join(IMAGE_EXTENSIONS) + "))$"
ext_re = re.compile(s, re.I)
- text_color_re = re.compile(r"^(.+)-([0-9A-Fa-f]{6})$")
found, t = [], {}
for dir in dirs:
dir = dir.strip()
@@ -1611,16 +1622,6 @@ Please select a %s type %s.
n = ext_re.sub("", name.strip())
if os.path.split(dir)[-1] == 'stretch':
tile.stretch = 1
- elif n.find('-stretch') > 0:
- # stretch?
- tile.stretch = 1
- n = n.replace('-stretch', '')
- #else:
- # tile.stretch = 0
- m = text_color_re.search(n)
- if m:
- n = m.group(1)
- tile.text_color = "#" + m.group(2).lower()
#n = re.sub("[-_]", " ", n)
n = n.replace('_', ' ')
##n = unicode(n)
diff --git a/pysollib/game.py b/pysollib/game.py
index fe2fe8e3..f49087fc 100644
--- a/pysollib/game.py
+++ b/pysollib/game.py
@@ -37,6 +37,7 @@
# imports
import time
import math
+import md5
from cStringIO import StringIO
# PySol imports
@@ -57,7 +58,9 @@ from pysoltk import after, after_idle, after_cancel
from pysoltk import MfxMessageDialog, MfxExceptionDialog
from pysoltk import MfxCanvasText, MfxCanvasLine, MfxCanvasRectangle
from pysoltk import Card
-from move import AMoveMove, AFlipMove, ATurnStackMove
+from pysoltk import reset_solver_dialog
+from move import AMoveMove, AFlipMove, AFlipAndMoveMove
+from move import ASingleFlipMove, ATurnStackMove
from move import ANextRoundMove, ASaveSeedMove, AShuffleStackMove
from move import AUpdateStackMove, AFlipAllMove, ASaveStateMove
from move import ASingleCardMove
@@ -114,6 +117,8 @@ class Game:
self.cards = []
self.stackmap = {} # dict with (x,y) tuples as key
self.allstacks = []
+ self.sn_groups = [] # snapshot groups; list of list of similar stacks
+ self.snapshots = []
self.stackdesc_list = []
self.demo_logo = None
self.pause_logo = None
@@ -160,6 +165,7 @@ class Game:
# set some defaults
self.sg.openstacks = filter(lambda s: s.cap.max_accept >= s.cap.min_accept, self.sg.openstacks)
self.sg.hp_stacks = filter(lambda s: s.cap.max_move >= 2, self.sg.dropstacks)
+ self.createSnGroups()
# convert stackgroups to tuples (speed)
self.allstacks = tuple(self.allstacks)
self.s.foundations = tuple(self.s.foundations)
@@ -193,15 +199,15 @@ class Game:
# update display properties
self.top.wm_geometry("") # cancel user-specified geometry
self.canvas.setInitialSize(self.width, self.height)
+ if self.app.opt.save_games_geometry and \
+ self.id in self.app.opt.games_geometry:
+ # restore game geometry
+ w, h = self.app.opt.games_geometry[self.id]
+ self.canvas.config(width=w, height=h)
self.top.update_idletasks() # apply geometry now
if DEBUG >= 4:
MfxCanvasRectangle(self.canvas, 0, 0, self.width, self.height,
width=2, fill=None, outline='green')
- # restore game geometry
- if self.app.opt.save_games_geometry:
- w, h = self.app.opt.games_geometry.get(self.id, (0, 0))
- w, h = max(w, self.width), max(h, self.height)
- self.canvas.config(width=w, height=h)
#
self.stats.update_time = time.time()
self.busy = old_busy
@@ -253,10 +259,9 @@ class Game:
bind(self.canvas, "<2>", self.clickHandler)
##bind(self.canvas, "<3>", self.clickHandler)
##bind(self.canvas, "", self.undoHandler)
- ##bind(self.canvas, "", self.undoHandler)
bind(self.canvas, "<1>", self.undoHandler)
bind(self.canvas, "<3>", self.redoHandler)
- bind(self.top, '', self._unmapHandler)
+ bind(self.canvas, '', self._unmapHandler)
def __createCommon(self, app):
self.busy = 1
@@ -324,6 +329,7 @@ class Game:
def reset(self, restart=0):
self.filename = ""
self.demo = None
+ self.solver = None
self.hints = Struct(
list = None, # list of hints for the current move
index = -1,
@@ -337,6 +343,7 @@ class Game:
talon_round = 1,
ncards = 0,
)
+ self.snapshots = []
# local statistics are reset on each game restart
self.stats = Struct(
hints = 0, # number of hints consumed
@@ -434,6 +441,7 @@ class Game:
gamenumber=self.getGameNumber(format=1),
moves=(0, 0),
stats=self.app.stats.getStats(self.app.opt.player, self.id))
+ reset_solver_dialog()
# unhide toplevel when we use a progress bar
if not self.preview:
wm_map(self.top, maximized=self.app.opt.wm_maximized)
@@ -458,6 +466,7 @@ class Game:
self.startMoves()
for stack in self.allstacks:
stack.updateText()
+ self.updateSnapshots()
self.updateText()
self.updateStatus(moves=(0, 0))
self.updateMenus()
@@ -490,6 +499,7 @@ class Game:
self.gsaveinfo = game.gsaveinfo
self.s.talon.round = game.loadinfo.talon_round
self.finished = game.finished
+ self.snapshots = game.snapshots
# 3) move cards to stacks
assert len(self.allstacks) == len(game.loadinfo.stacks)
for i in range(len(self.allstacks)):
@@ -644,6 +654,49 @@ class Game:
def leaveState(self, old_state):
self.moves.state = old_state
+ def getSnapshot(self, hex=False):
+ # generate hash (unique string) of current move
+ sn = []
+ for stack in self.allstacks:
+ s = []
+ for card in stack.cards:
+ s.append('%d%03d%d' % (card.suit, card.rank, card.face_up))
+ sn.append(''.join(s))
+ sn = '-'.join(sn)
+ # make more short string
+ if hex:
+ sn = md5.new(sn).hexdigest()
+ else:
+ sn = md5.new(sn).digest()
+ return sn
+
+ def createSnGroups(self):
+ # group stacks by class and cap
+ sg = {}
+ for s in self.allstacks:
+ for k in sg:
+ if s.__class__ is k.__class__ and \
+ s.cap.__dict__ == k.cap.__dict__:
+ g = sg[k]
+ g.append(s.id)
+ break
+ else:
+ # new group
+ sg[s] = [s.id]
+ sg = sg.values()
+ self.sn_groups = sg
+ ##print sg
+
+
+ def updateSnapshots(self):
+ sn = self.getSnapshot()
+ if sn in self.snapshots:
+ ##self.updateStatus(snapshot=True)
+ pass
+ else:
+ self.snapshots.append(sn)
+ ##self.updateStatus(snapshot=False)
+
#
# card creation & shuffling
@@ -810,8 +863,11 @@ class Game:
if self.demo:
self.stopDemo()
return
- if self.app.opt.mouse_undo and not self.event_handled:
- self.app.menubar.mUndo()
+ if not self.event_handled:
+ if self.drag.stack:
+ self.drag.stack.cancelDrag(event)
+ elif self.app.opt.mouse_undo:
+ self.app.menubar.mUndo()
self.event_handled = False
return EVENT_PROPAGATE
@@ -823,8 +879,11 @@ class Game:
if self.demo:
self.stopDemo()
return
- if self.app.opt.mouse_undo and not self.event_handled:
- self.app.menubar.mRedo()
+ if not self.event_handled:
+ if self.drag.stack:
+ self.drag.stack.cancelDrag(event)
+ elif self.app.opt.mouse_undo:
+ self.app.menubar.mRedo()
self.event_handled = False
return EVENT_PROPAGATE
@@ -895,7 +954,7 @@ class Game:
def _unmapHandler(self, event):
# pause game if root window has been iconified
- if event.widget is self.top and not self.pause:
+ if self.app and not self.pause:
self.app.menubar.mPause()
@@ -925,11 +984,11 @@ class Game:
if a and not self.preview:
self.canvas.update_idletasks()
if self.app.audio and self.app.opt.sound and self.app.opt.sound_samples['deal']:
- if a in (1, 2, 5):
+ if a in (1, 2, 3, 10):
self.playSample("deal01", priority=100, loop=loop)
- elif a == 3:
- self.playSample("deal04", priority=100, loop=loop)
elif a == 4:
+ self.playSample("deal04", priority=100, loop=loop)
+ elif a == 5:
self.playSample("deal08", priority=100, loop=loop)
@@ -960,7 +1019,16 @@ class Game:
bitmap="error")
# main animation method
- def animatedMoveTo(self, from_stack, to_stack, cards, x, y, tkraise=1, frames=-1, shadow=-1):
+ def animatedMoveTo(self, from_stack, to_stack, cards, x, y,
+ tkraise=1, frames=-1, shadow=-1):
+ # available values of app.opt.animations:
+ # 0 - without animations
+ # 1 - very fast (without timer)
+ # 2 - fast (default)
+ # 3 - medium (2/3 of fast speed)
+ # 4 - slow (1/4 of fast speed)
+ # 5 - very slow (1/8 of fast speed)
+ # 10 - used internally in game preview
if self.app.opt.animations == 0 or frames == 0:
return
# init timer - need a high resolution for this to work
@@ -971,13 +1039,16 @@ class Game:
if frames < 0:
frames = 8
assert frames >= 2
- if self.app.opt.animations == 3: # slow
+ if self.app.opt.animations == 3: # medium
+ frames = frames * 3
+ SPF = SPF / 2
+ elif self.app.opt.animations == 4: # slow
frames = frames * 8
SPF = SPF / 2
- elif self.app.opt.animations == 4: # very slow
+ elif self.app.opt.animations == 5: # very slow
frames = frames * 16
SPF = SPF / 2
- elif self.app.opt.animations == 5:
+ elif self.app.opt.animations == 10:
# this is used internally in game preview to speed up
# the initial dealing
if self.moves.state == self.S_INIT and frames > 4:
@@ -986,7 +1057,6 @@ class Game:
shadow = self.app.opt.shadow
shadows = ()
# start animation
- from_stack._unshadeStack()
if tkraise:
for card in cards:
card.tkraise()
@@ -1030,21 +1100,19 @@ class Game:
card.moveBy(dx, dy)
self.canvas.update_idletasks()
- def animatedFlip(self, stack):
- return False
- if self.app.opt.animations == 0:
+
+ def doAnimatedFlipAndMove(self, from_stack, to_stack=None, frames=-1):
+ if self.app.opt.animations == 0 or frames == 0:
+ return False
+ if not from_stack.cards:
return False
if TOOLKIT == 'gtk':
return False
- if not stack.cards:
- return False
if not Image:
return False
- if self.moves.state == self.S_INIT:
- # don't use flip animation for initial dealing
- return False
+
canvas = self.canvas
- card = stack.cards[-1]
+ card = from_stack.cards[-1]
im1 = card._active_image._pil_image
if card.face_up:
im2 = card._back_image._pil_image
@@ -1053,51 +1121,134 @@ class Game:
w, h = im1.size
id = card.item.id
#
- delay = 10
- frames = 3.0 # num frames for each step
- if self.app.opt.animations == 3: # slow
- delay = 10
+ SPF = 0.1/8 # animation speed - seconds per frame
+ frames = 4.0 # num frames for each step
+ if self.app.opt.animations == 3: # medium
+ SPF = 0.1/8
frames = 7.0
- elif self.app.opt.animations == 4: # very slow
- delay = 10
+ elif self.app.opt.animations == 4: # slow
+ SPF = 0.1/8
frames = 12.0
- delta = 2*int(w/frames/2) # should be even for save position
- ddx, ddy = 0, self.app.images.SHADOW_YOFFSET/2 # ascent of the card
- # siep 1
- ww = w
- dx = delta/2
- canvas.move(id, -ddx, -ddy)
- canvas.update_idletasks()
- canvas.after(delay)
- while True:
- if ww-delta <= 0:
- break
- ww -= delta
- tmp = im1.resize((ww, h))
+ elif self.app.opt.animations == 5: # very slow
+ SPF = 0.1/8
+ frames = 24.0
+
+ if to_stack is None:
+ x0, y0 = from_stack.getPositionFor(card)
+ x1, y1 = x0, y0
+ dest_x, dest_y = 0, 0
+ else:
+ x0, y0 = from_stack.getPositionFor(card)
+ x1, y1 = to_stack.getPositionForNextCard()
+ dest_x, dest_y = x1-x0, y1-y0
+
+ if dest_x == 0 and dest_y == 0:
+ # flip
+ #ascent_dx, ascent_dy = 0, self.app.images.SHADOW_YOFFSET/frames
+ ascent_dx, ascent_dy = 0, h/10.0/frames
+ min_size = w/10
+ shrink_dx = (w-min_size) / (frames-1)
+ shrink_dy = 0
+ elif dest_y == 0:
+ # move to left/right waste
+ #ascent_dx, ascent_dy = 0, self.app.images.SHADOW_YOFFSET/frames
+ ascent_dx, ascent_dy = 0, h/10.0/frames
+ min_size = w/10
+ shrink_dx = (w-min_size) / (frames-1)
+ shrink_dy = 0
+ elif dest_x == 0:
+ # move to top/bottom waste
+ return False
+ ascent_dx, ascent_dy = 0, 0
+## min_size = h/10
+## shrink_dx = 0
+## shrink_dy = (h-min_size) / (frames-1)
+ min_size = w/10
+ shrink_dx = (w-min_size) / (frames-1)
+ shrink_dy = 0
+
+ else:
+ # dest_x != 0 and dest_y != 0
+ return False
+
+ move_dx = dest_x / frames / 2
+ move_dy = dest_y / frames / 2
+ xpos, ypos = float(x0), float(y0)
+
+ card.tkraise()
+
+ # step 1
+ d_x = shrink_dx/2+move_dx-ascent_dx
+ d_y = shrink_dy/2+move_dy-ascent_dy
+ nframe = 0
+ while nframe < frames:
+ starttime = uclock()
+ # resize img
+ ww = w - nframe*shrink_dx
+ hh = h - nframe*shrink_dy
+ tmp = im1.resize((int(ww), int(hh)))
tk_tmp = ImageTk.PhotoImage(image=tmp)
canvas.itemconfig(id, image=tk_tmp)
- canvas.move(id, dx, 0)
+ # move img
+ xpos += d_x
+ ypos += d_y
+ card.moveTo(int(round(xpos)), int(round(ypos)))
canvas.update_idletasks()
- canvas.after(delay)
- dx = -dx
+
+ nframe += 1
+ t = (SPF-(uclock()-starttime))*1000 # milliseconds
+ if t > 0:
+ usleep(t/1000)
+## else:
+## nframe += 1
+## xpos += d_x
+## ypos += d_y
+
# step 2
- while True:
- tmp = im2.resize((ww, h))
+ d_x = -shrink_dx/2+move_dx+ascent_dx
+ d_y = -shrink_dy/2+move_dy+ascent_dy
+ nframe = 0
+ while nframe < frames:
+ starttime = uclock()
+ # resize img
+ ww = w - (frames-nframe-1)*shrink_dx
+ hh = h - (frames-nframe-1)*shrink_dy
+ tmp = im2.resize((int(ww), int(hh)))
tk_tmp = ImageTk.PhotoImage(image=tmp)
canvas.itemconfig(id, image=tk_tmp)
- canvas.move(id, dx, 0)
+ # move img
+ xpos += d_x
+ ypos += d_y
+ card.moveTo(int(round(xpos)), int(round(ypos)))
canvas.update_idletasks()
- canvas.after(delay)
- ww += delta
- if ww >= w:
- break
- canvas.move(id, ddx, ddy)
+
+ nframe += 1
+ t = (SPF-(uclock()-starttime))*1000 # milliseconds
+ if t > 0:
+ usleep(t/1000)
+## else:
+## nframe += 1
+## xpos += d_x
+## ypos += d_y
+
+ card.moveTo(x1, y1)
+ #canvas.update_idletasks()
return True
- def doWinAnimation(self):
+ def animatedFlip(self, stack):
+ ##return False
+ return self.doAnimatedFlipAndMove(stack)
+
+ def animatedFlipAndMove(self, from_stack, to_stack, frames=-1):
+ ##return False
+ return self.doAnimatedFlipAndMove(from_stack, to_stack, frames)
+
+
+ def winAnimationEvent(self):
# based on code from pygtk-demo
- FRAME_DELAY = 40
+ FRAME_DELAY = 80
CYCLE_LEN = 60
+ starttime = uclock()
images = self.win_animation.images
saved_images = self.win_animation.saved_images # cached images
canvas = self.canvas
@@ -1107,6 +1258,10 @@ class Game:
x0 = int(int(canvas.cget('width'))*(canvas.xview()[0]))
y0 = int(int(canvas.cget('height'))*(canvas.yview()[0]))
width, height = self.win_animation.width, self.win_animation.height
+ cw = self.canvas.winfo_width()
+ ch = self.canvas.winfo_height()
+ x0 -= (width-cw)/2
+ y0 -= (height-ch)/2
tmp_tk_images = []
raised_images = []
@@ -1143,7 +1298,7 @@ class Game:
if round_k == 100:
tmp = im
else:
- tmp = im.resize(new_size, resample=Image.BICUBIC)
+ tmp = im.resize(new_size, resample=Image.BILINEAR)
tk_tmp = ImageTk.PhotoImage(image=tmp)
saved_images[img_index][round_k] = tk_tmp
@@ -1161,7 +1316,11 @@ class Game:
self.win_animation.tk_images = tmp_tk_images
canvas.update_idletasks()
# loop
- self.win_animation.timer = after(canvas, FRAME_DELAY, self.doWinAnimation)
+ t = FRAME_DELAY-int((uclock()-starttime)*1000)
+ if t > 0:
+ self.win_animation.timer = after(canvas, t, self.winAnimationEvent)
+ else:
+ self.win_animation.timer = after_idle(canvas, self.winAnimationEvent)
def stopWinAnimation(self):
if self.win_animation.timer:
@@ -1178,8 +1337,8 @@ class Game:
def winAnimation(self, perfect=0):
if self.preview:
return
- if not self.app.opt.animations:
- return
+ #if not self.app.opt.animations:
+ # return
if not self.app.opt.win_animation:
return
if TOOLKIT == 'gtk':
@@ -1190,7 +1349,8 @@ class Game:
# select some random cards
cards = self.cards[:]
scards = []
- for i in xrange(10):
+ ncards = min(10, len(cards))
+ for i in xrange(ncards):
c = self.app.miscrandom.choice(cards)
scards.append(c)
cards.remove(c)
@@ -1200,7 +1360,8 @@ class Game:
self.win_animation.width = self.canvas.winfo_width()
self.win_animation.height = self.canvas.winfo_height()
# run win animation in background
- after_idle(self.canvas, self.doWinAnimation)
+ ##after_idle(self.canvas, self.winAnimationEvent)
+ after(self.canvas, 200, self.winAnimationEvent)
return
def redealAnimation(self):
@@ -1208,14 +1369,6 @@ class Game:
return
if not self.app.opt.animations or not self.app.opt.redeal_animation:
return
- self.setCursor(cursor=CURSOR_WATCH)
- self.top.busyUpdate()
- self.canvas.update_idletasks()
- old_a = self.app.opt.animations
- if old_a == 0:
- self.app.opt.animations = 1 # timer based
- elif old_a == 4: # very slow
- self.app.opt.animations = 3 # slow
cards = []
for s in self.allstacks:
if s is not self.s.talon:
@@ -1223,6 +1376,16 @@ class Game:
cards.append((c,s))
if not cards:
return
+ self.setCursor(cursor=CURSOR_WATCH)
+ self.top.busyUpdate()
+ self.canvas.update_idletasks()
+ old_a = self.app.opt.animations
+ if old_a == 0:
+ self.app.opt.animations = 1 # very fast
+ elif old_a == 3: # medium
+ self.app.opt.animations = 2 # fast
+ elif old_a == 4: # very slow
+ self.app.opt.animations = 3 # slow
# select some random cards
acards = []
scards = cards[:]
@@ -1381,6 +1544,7 @@ class Game:
# the actual hint class (or None)
Hint_Class = DefaultHint
+ Solver_Class = None
def getHintClass(self):
return self.Hint_Class
@@ -1625,7 +1789,8 @@ for %d moves.
if s.canFlipCard():
if sound:
self.playSample("autoflip", priority=5)
- s.flipMove()
+ #~s.flipMove()
+ s.flipMove(animation=True)
done_something = 1
# each single flip is undo-able unless opt.autofaceup
self.finishMove()
@@ -1661,6 +1826,13 @@ for %d moves.
return self.dealCards(sound=sound)
return 0
+ def autoDrop(self, autofaceup=-1):
+ old_a = self.app.opt.animations
+ if old_a == 3: # medium
+ self.app.opt.animations = 2 # fast
+ self.autoPlay(autofaceup=autofaceup, autodrop=1)
+ self.app.opt.animations = old_a
+
## for find_card_dialog
def highlightCard(self, suit, rank):
if not self.app:
@@ -1681,11 +1853,12 @@ for %d moves.
return ((self.sg.hp_stacks, 2),)
return ()
- def _highlightCards(self, info, sleep=1.5):
+ def _highlightCards(self, info, sleep=1.5, delta=(1,1,1,1)):
if not info:
return 0
if self.pause:
return 0
+ self.stopWinAnimation()
items = []
for s, c1, c2, color in info:
assert c1 in s.cards and c2 in s.cards
@@ -1741,13 +1914,15 @@ for %d moves.
y2 = y2 + self.app.images.CARDH
tkraise = True
##print c1, c2, x1, y1, x2, y2
+ x1, x2 = x1-delta[0], x2+delta[1]
+ y1, y2 = y1-delta[2], y2+delta[3]
if TOOLKIT == 'tk':
- r = MfxCanvasRectangle(self.canvas, x1-1, y1-1, x2+1, y2+1,
+ r = MfxCanvasRectangle(self.canvas, x1, y1, x2, y2,
width=4, fill=None, outline=color)
if tkraise:
r.tkraise(c2.item)
elif TOOLKIT == 'gtk':
- r = MfxCanvasRectangle(self.canvas, x1-1, y1-1, x2+1, y2+1,
+ r = MfxCanvasRectangle(self.canvas, x1, y1, x2, y2,
width=4, fill=None, outline=color,
group=s.group)
if tkraise:
@@ -1801,7 +1976,7 @@ for %d moves.
for s in si[0]:
pile = s.getPile()
if pile and len(pile) >= si[1]:
- hi.append((s, pile[0], pile[-1], col[1]))
+ hi.append((s, pile[0], pile[-1], col))
if not hi:
self.highlightNotMatching()
return 0
@@ -1855,7 +2030,7 @@ for %d moves.
# for spider-type stacks
if to_stack.cards:
# check suit
- return int(from_stack.cards[-1].suit == to_stack.cards[-1].suit)+1
+ return int(from_stack.cards[-ncards].suit == to_stack.cards[-1].suit)+1
return 0
#
@@ -1896,6 +2071,10 @@ for %d moves.
# compute all hints for the current position
# this is the only method that actually uses class Hint
def getHints(self, level, taken_hint=None):
+ if level == 3:
+ #if self.solver is None:
+ # return None
+ return self.solver.getHints(taken_hint)
hint_class = self.getHintClass()
if hint_class is None:
return None
@@ -1941,10 +2120,17 @@ for %d moves.
# a move move
assert to_stack
assert 1 <= ncards <= len(from_stack.cards)
+ if DEBUG:
+ if not to_stack.acceptsCards(
+ from_stack, from_stack.cards[-ncards:]):
+ print '*fail accepts cards*', from_stack, to_stack, ncards
+ if not from_stack.canMoveCards(from_stack.cards[-ncards:]):
+ print '*fail move cards*', from_stack, ncards
+ ##assert from_stack.canMoveCards(from_stack.cards[-ncards:]) # FIXME: Pyramid
assert to_stack.acceptsCards(from_stack, from_stack.cards[-ncards:])
if sleep <= 0.0:
return h
- info = (level == 1) or (level > 1 and DEBUG >= 3)
+ info = (level == 1) or (level > 1 and DEBUG)
if info and self.app.statusbar and self.app.opt.statusbar:
self.app.statusbar.configLabel("info", text=_("Score %6d") % (score), fg=text_color)
else:
@@ -2000,6 +2186,7 @@ for %d moves.
mixed = mixed,
sleep = self.app.opt.timeouts['demo'],
last_deal = [],
+ snapshots = [],
hint = None,
keypress = None,
start_demo_moves = self.stats.demo_moves,
@@ -2036,6 +2223,8 @@ for %d moves.
timeout = 10000
if 1 and player_moves == 0:
timeout = 5000
+ if self.demo and self.demo.level == 3:
+ timeout = 0
if self.isGameWon():
self.updateTime()
finished = 1
@@ -2044,7 +2233,7 @@ for %d moves.
if not self.top.winfo_ismapped():
status = 2
elif player_moves == 0:
- self.playSample("autopilotwon")
+ self.playSample("autopilotwon", priority=1000)
s = self.app.miscrandom.choice((_("&Great"), _("&Cool"), _("&Yeah"), _("&Wow"))) # ??? accelerators
d = MfxMessageDialog(self.top, title=PACKAGE+_(" Autopilot"),
text=_("\nGame solved in %d moves.\n") % self.moves.index,
@@ -2067,7 +2256,7 @@ for %d moves.
status = 2
else:
if player_moves == 0:
- self.playSample("autopilotlost")
+ self.playSample("autopilotlost", priority=1000)
s = self.app.miscrandom.choice((_("&Oh well"), _("&That's life"), _("&Hmm"))) # ??? accelerators
d = MfxMessageDialog(self.top, title=PACKAGE+_(" Autopilot"),
text=_("\nThis won't come out...\n"),
@@ -2076,7 +2265,7 @@ for %d moves.
status = d.status
if finished:
self.updateStats(demo=1)
- if self.demo and status == 2:
+ if not DEBUG and self.demo and status == 2:
# timeout in dialog
if self.stats.demo_moves > self.demo.start_demo_moves:
# we only increase the splash-screen counter if the last
@@ -2143,29 +2332,38 @@ for %d moves.
score, pos, ncards, from_stack, to_stack, text_color, forced_move = h
if ncards == 0:
# a deal-move
+ # do not let games like Klondike and Canfield deal forever
if self.dealCards() == 0:
return 1
- # do not let games like Klondike and Canfield deal forever
- c = self.s.talon.getCard()
- if c in demo.last_deal:
- # We went through the whole Talon. Give up.
- return 1
- # Note that `None' is a valid entry in last_deal[]
- # (this means that all cards are on the Waste).
- demo.last_deal.append(c)
+ if 0: # old version, based on dealing card
+ c = self.s.talon.getCard()
+ if c in demo.last_deal:
+ # We went through the whole Talon. Give up.
+ return 1
+ # Note that `None' is a valid entry in last_deal[]
+ # (this means that all cards are on the Waste).
+ demo.last_deal.append(c)
+ else: # new version, based on snapshots
+ # check snapshot
+ sn = self.getSnapshot()
+ if sn in demo.snapshots:
+ # not unique
+ return 1
+ demo.snapshots.append(sn)
elif from_stack == to_stack:
# a flip-move
- from_stack.flipMove()
+ from_stack.flipMove(animation=True)
demo.last_deal = []
else:
# a move-move
from_stack.moveMove(ncards, to_stack, frames=-1)
demo.last_deal = []
- ##print self.moves.index
return 0
def createDemoInfoText(self):
## TODO - the text placement is not fully ok
+ if DEBUG:
+ self.showHelp('help', self.getDemoInfoText())
return
if not self.demo or self.demo.info_text or self.preview:
return
@@ -2177,13 +2375,15 @@ for %d moves.
]
ta = self.getDemoInfoTextAttr(tinfo)
if ta:
- font = self.app.getFont("canvas_large")
+ #font = self.app.getFont("canvas_large")
+ font = self.app.getFont("default")
self.demo.info_text = MfxCanvasText(self.canvas, ta[1], ta[2],
anchor=ta[0], font=font,
text=self.getDemoInfoText())
def getDemoInfoText(self):
- return self.gameinfo.short_name
+ h = self.Hint_Class is None and 'None' or self.Hint_Class.__name__
+ return '%s (%s)' % (self.gameinfo.short_name, h)
def getDemoInfoTextAttr(self, tinfo):
items1, items2 = [], []
@@ -2291,6 +2491,21 @@ for %d moves.
am.do(self)
self.hints.list = None
+ def singleFlipMove(self, stack):
+ # flip with animation (without "moveMove" in this move)
+ assert stack
+ am = ASingleFlipMove(stack)
+ self.__storeMove(am)
+ am.do(self)
+ self.hints.list = None
+
+ def flipAndMoveMove(self, from_stack, to_stack, frames=-1):
+ assert from_stack and to_stack and (from_stack is not to_stack)
+ am = AFlipAndMoveMove(from_stack, to_stack, frames)
+ self.__storeMove(am)
+ am.do(self)
+ self.hints.list = None
+
# move type 3
def turnStackMove(self, from_stack, to_stack, update_flags=1):
assert from_stack and to_stack and (from_stack is not to_stack)
@@ -2398,11 +2613,14 @@ for %d moves.
assert moves.index == len(moves.history)
moves.current = []
+ self.updateSnapshots()
# update view
self.updateText()
self.updateStatus(moves=(moves.index, self.stats.total_moves))
self.updateMenus()
self.updatePlayTime(do_after=0)
+ reset_solver_dialog()
+
return 1
@@ -2428,6 +2646,7 @@ for %d moves.
self.stats.undo_moves += 1
self.stats.total_moves += 1
self.hints.list = None
+ self.updateSnapshots()
self.updateText()
self.updateStatus(moves=(self.moves.index, self.stats.total_moves))
self.updateMenus()
@@ -2451,6 +2670,7 @@ for %d moves.
self.stats.redo_moves += 1
self.stats.total_moves += 1
self.hints.list = None
+ self.updateSnapshots()
self.updateText()
self.updateStatus(moves=(self.moves.index, self.stats.total_moves))
self.updateMenus()
@@ -2707,6 +2927,9 @@ in the current implementation.''' % version
moves = pload()
assert isinstance(moves, Struct), err_txt
game.moves.__dict__.update(moves.__dict__)
+ snapshots = p.load()
+ assert isinstance(snapshots, list), err_txt
+ game.snapshots = snapshots
if 0 <= bookmark <= 1:
gstats = pload()
assert isinstance(gstats, Struct), err_txt
@@ -2763,6 +2986,7 @@ in the current implementation.''' % version
p.dump(self.saveinfo)
p.dump(self.gsaveinfo)
p.dump(self.moves)
+ p.dump(self.snapshots)
if 0 <= bookmark <= 1:
if bookmark == 0:
self.gstats.saved = self.gstats.saved + 1
diff --git a/pysollib/gamedb.py b/pysollib/gamedb.py
index 2098c717..6f346226 100644
--- a/pysollib/gamedb.py
+++ b/pysollib/gamedb.py
@@ -339,6 +339,7 @@ class GI:
('fc-0.9.2', tuple(range(441, 466))),
('fc-0.9.3', tuple(range(466, 661))),
('fc-0.9.4', tuple(range(661, 671))),
+ ('fc-1.0', tuple(range(671, 711))),
)
# deprecated - the correct way is to or a GI.GT_XXX flag
@@ -346,8 +347,6 @@ class GI:
_CHILDREN_GAMES = [16, 33, 55, 90, 91, 96, 97, 176, 903,]
_OPEN_GAMES = []
- #_OPEN_GAMES = [ 5, 6, 8, 9, 26, 31, 45, 46, 50, 53, 63, 64, 77, 85, 86,
- # 96, 116, 117, 118, 258, ]
_POPULAR_GAMES = [
1, # Gypsy
@@ -471,6 +470,7 @@ class GameManager:
self.__games_by_altname = None
self.__all_games = {} # includes hidden games
self.__all_gamenames = {} # includes hidden games
+ self.__games_for_solver = []
self.loading_plugin = 0
self.registered_game_types = {}
@@ -536,7 +536,9 @@ class GameManager:
## if gi.id in k: break
## else:
## print gi.id
-
+ if hasattr(gi.gameclass, 'Solver_Class') and \
+ gi.gameclass.Solver_Class is not None:
+ self.__games_for_solver.append(gi.id)
#
# access games database - we do not expose hidden games
@@ -591,6 +593,9 @@ class GameManager:
return gi.id
return None
+ def getGamesForSolver(self):
+ return self.__games_for_solver
+
# /***********************************************************************
# //
diff --git a/pysollib/games/auldlangsyne.py b/pysollib/games/auldlangsyne.py
index 4d1ea94d..56dc23cd 100644
--- a/pysollib/games/auldlangsyne.py
+++ b/pysollib/games/auldlangsyne.py
@@ -42,6 +42,8 @@ from pysollib.layout import Layout
from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint
from pysollib.pysoltk import MfxCanvasText
+from numerica import Numerica_Hint
+
# /***********************************************************************
# // Tam O'Shanter
@@ -159,6 +161,8 @@ class Strategy_RowStack(BasicRowStack):
class Strategy(Game):
+ Hint_Class = Numerica_Hint
+
def createGame(self, rows=8):
# create layout
l, s = Layout(self), self.s
@@ -181,6 +185,7 @@ class Strategy(Game):
# define stack-groups
l.defaultStackGroups()
+ self.sg.dropstacks.append(s.talon)
#
# game overrides
@@ -209,14 +214,16 @@ class StrategyPlus(Strategy):
def fillStack(self, stack):
if stack is self.s.talon and stack.cards:
+ old_state = self.enterState(self.S_FILL)
c = stack.cards[-1]
while c.rank == ACE:
- old_state = self.enterState(self.S_FILL)
self.moveMove(1, stack, self.s.foundations[c.suit])
if stack.canFlipCard():
stack.flipMove()
- self.leaveState(old_state)
+ if not stack.cards:
+ break
c = stack.cards[-1]
+ self.leaveState(old_state)
# /***********************************************************************
@@ -480,6 +487,8 @@ class Amazons_Foundation(AbstractFoundationStack):
rank = 5
if (rank + self.cap.dir) % self.cap.mod != cards[0].rank:
return False
+ if cards[0].rank == QUEEN:
+ return True
i = list(self.game.s.foundations).index(self)
j = list(self.game.s.rows).index(from_stack)
return i == j
diff --git a/pysollib/games/bakersdozen.py b/pysollib/games/bakersdozen.py
index d3958641..6a51593d 100644
--- a/pysollib/games/bakersdozen.py
+++ b/pysollib/games/bakersdozen.py
@@ -42,6 +42,8 @@ from pysollib.stack import *
from pysollib.game import Game
from pysollib.layout import Layout
from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint
+from pysollib.hint import FreeCellSolverWrapper
+
# /***********************************************************************
# // Castles in Spain
@@ -51,8 +53,9 @@ class CastlesInSpain(Game):
Layout_Method = Layout.bakersDozenLayout
Talon_Class = InitialDealTalonStack
Foundation_Class = SS_FoundationStack
- RowStack_Class = AC_RowStack
+ RowStack_Class = SuperMoveAC_RowStack
Hint_Class = CautiousDefaultHint
+ Solver_Class = FreeCellSolverWrapper()
#
# game layout
@@ -69,8 +72,7 @@ class CastlesInSpain(Game):
for r in l.s.foundations:
s.foundations.append(self.Foundation_Class(r.x, r.y, self, suit=r.suit))
for r in l.s.rows:
- s.rows.append(self.RowStack_Class(r.x, r.y, self,
- max_move=1, max_accept=1))
+ s.rows.append(self.RowStack_Class(r.x, r.y, self))
# default
l.defaultAll()
@@ -96,7 +98,8 @@ class Martha_RowStack(AC_RowStack):
class Martha(CastlesInSpain):
- RowStack_Class = FullStackWrapper(Martha_RowStack)
+ Solver_Class = None
+ RowStack_Class = Martha_RowStack
def createGame(self):
CastlesInSpain.createGame(self, rows=12, playcards=13)
@@ -115,7 +118,9 @@ class Martha(CastlesInSpain):
# ************************************************************************/
class BakersDozen(CastlesInSpain):
- RowStack_Class = StackWrapper(RK_RowStack, base_rank=NO_RANK)
+ RowStack_Class = StackWrapper(RK_RowStack, max_move=1, max_accept=1,
+ base_rank=NO_RANK)
+ Solver_Class = FreeCellSolverWrapper(preset='bakers_dozen')
def _shuffleHook(self, cards):
# move Kings to bottom of each stack
@@ -148,16 +153,19 @@ class BakersDozen(CastlesInSpain):
class SpanishPatience(BakersDozen):
Foundation_Class = AC_FoundationStack
+ Solver_Class = None
class PortugueseSolitaire(BakersDozen):
RowStack_Class = StackWrapper(RK_RowStack, base_rank=KING)
+ Solver_Class = FreeCellSolverWrapper(sbb='rank', esf='kings')
def _shuffleHook(self, cards):
return cards
class SpanishPatienceII(PortugueseSolitaire):
RowStack_Class = RK_RowStack
+ Solver_Class = FreeCellSolverWrapper(sbb='rank', sm='unlimited')
# /***********************************************************************
@@ -165,6 +173,8 @@ class SpanishPatienceII(PortugueseSolitaire):
# ************************************************************************/
class GoodMeasure(BakersDozen):
+ Solver_Class = FreeCellSolverWrapper(preset='good_measure')
+
def createGame(self):
CastlesInSpain.createGame(self, rows=10)
@@ -189,8 +199,8 @@ class GoodMeasure(BakersDozen):
class Cruel_Talon(TalonStack):
def canDealCards(self):
## FIXME: this is to avoid loops in the demo
- if self.game.demo and self.game.moves.index >= 100:
- return False
+ #if self.game.demo and self.game.moves.index >= 100:
+ # return False
if self.round == self.max_rounds:
return False
return not self.game.isGameWon()
@@ -238,6 +248,8 @@ class Cruel_Talon(TalonStack):
class Cruel(CastlesInSpain):
Talon_Class = StackWrapper(Cruel_Talon, max_rounds=-1)
RowStack_Class = StackWrapper(SS_RowStack, base_rank=NO_RANK)
+ ##Solver_Class = FreeCellSolverWrapper(preset='cruel')
+ Solver_Class = None
def createGame(self):
CastlesInSpain.createGame(self, rows=12)
@@ -289,6 +301,7 @@ class Indefatigable(Cruel):
class Perseverance(Cruel, BakersDozen):
Talon_Class = StackWrapper(Cruel_Talon, max_rounds=3)
RowStack_Class = StackWrapper(SS_RowStack, base_rank=NO_RANK, dir=-1, max_move=UNLIMITED_MOVES, max_accept=UNLIMITED_ACCEPTS)
+ Solver_Class = None
def _shuffleHook(self, cards):
# move Kings to bottom of each stack (???)
@@ -306,6 +319,7 @@ class Perseverance(Cruel, BakersDozen):
# ************************************************************************/
class RippleFan(CastlesInSpain):
+ Solver_Class = None
def createGame(self):
# create layout
diff --git a/pysollib/games/bakersgame.py b/pysollib/games/bakersgame.py
index ed44831e..0fe714dc 100644
--- a/pysollib/games/bakersgame.py
+++ b/pysollib/games/bakersgame.py
@@ -49,36 +49,12 @@ from pysollib.hint import FreeCellType_Hint, FreeCellSolverWrapper
# // Baker's Game
# ************************************************************************/
-# To simplify playing we also consider the number of free rows.
-# Note that this only is legal if the game.s.rows have a
-# cap.base_rank == ANY_RANK.
-# See also the "SuperMove" section in the FreeCell FAQ.
-class BakersGame_RowStack(SS_RowStack):
- def _getMaxMove(self, to_stack_ncards):
- max_move = getNumberOfFreeStacks(self.game.s.reserves) + 1
- n = getNumberOfFreeStacks(self.game.s.rows)
- if to_stack_ncards == 0:
- n = n - 1
- while n > 0 and max_move < 1000:
- max_move = max_move * 2
- n = n - 1
- return max_move
-
- def canMoveCards(self, cards):
- max_move = self._getMaxMove(1)
- return len(cards) <= max_move and SS_RowStack.canMoveCards(self, cards)
-
- def acceptsCards(self, from_stack, cards):
- max_move = self._getMaxMove(len(self.cards))
- return len(cards) <= max_move and SS_RowStack.acceptsCards(self, from_stack, cards)
-
-
class BakersGame(Game):
Layout_Method = Layout.freeCellLayout
Foundation_Class = SS_FoundationStack
- RowStack_Class = BakersGame_RowStack
- ##Hint_Class = FreeCellType_Hint
- Hint_Class = FreeCellSolverWrapper(FreeCellType_Hint, {'sbb' : "suit" })
+ RowStack_Class = SuperMoveSS_RowStack
+ Hint_Class = FreeCellType_Hint
+ Solver_Class = FreeCellSolverWrapper(sbb='suit')
#
# game layout
@@ -123,7 +99,7 @@ class BakersGame(Game):
class KingOnlyBakersGame(BakersGame):
RowStack_Class = StackWrapper(FreeCell_SS_RowStack, base_rank=KING)
- Hint_Class = FreeCellSolverWrapper(FreeCellType_Hint, {'sbb' : "suit", 'esf' : "kings" })
+ Solver_Class = FreeCellSolverWrapper(sbb='suit', esf='kings')
# /***********************************************************************
@@ -235,19 +211,22 @@ class SeahavenTowers(KingOnlyBakersGame):
class RelaxedSeahavenTowers(SeahavenTowers):
RowStack_Class = KingSS_RowStack
- Hint_Class = FreeCellSolverWrapper(FreeCellType_Hint, {'sbb' : "suit", 'esf' : "kings", 'sm' : "unlimited",})
+ Solver_Class = FreeCellSolverWrapper(sbb='suit', esf='kings', sm='unlimited')
# /***********************************************************************
+# // Tuxedo
# // Penguin
# // Opus
-# // Tuxedo
# ************************************************************************/
class Tuxedo(Game):
- RowStack_Class = SS_RowStack
+ RowStack_Class = StackWrapper(SS_RowStack, mod=13)
+ ReserveStack_Class = ReserveStack
+ Foundation_Class = StackWrapper(SS_FoundationStack, mod=13, max_move=0)
Hint_Class = FreeCellType_Hint
+ Solver_Class = FreeCellSolverWrapper(sbb='suit', sm='unlimited')
def createGame(self, rows=7, reserves=7):
# create layout
@@ -265,16 +244,16 @@ class Tuxedo(Game):
# create stacks
x, y = self.width - l.XS, l.YM
for i in range(4):
- s.foundations.append(SS_FoundationStack(x, y, self, i, mod=13, max_move=0))
+ s.foundations.append(self.Foundation_Class(x, y, self, suit=i))
y = y + l.YS
self.setRegion(s.foundations, (x - l.CW/2, -999, 999999, 999999))
x, y = l.XM + (maxrows-rows)*l.XS/2, l.YM
for i in range(rows):
- s.rows.append(self.RowStack_Class(x, y, self, mod=13))
+ s.rows.append(self.RowStack_Class(x, y, self))
x = x + l.XS
x, y = l.XM + (maxrows-reserves)*l.XS/2, self.height - l.YS
for i in range(reserves):
- s.reserves.append(ReserveStack(x, y, self))
+ s.reserves.append(self.ReserveStack_Class(x, y, self))
x = x + l.XS
self.setRegion(s.reserves, (-999, y - l.CH / 2, 999999, 999999))
s.talon = InitialDealTalonStack(l.XM+1, y, self)
@@ -294,6 +273,7 @@ class Tuxedo(Game):
class Penguin(Tuxedo):
GAME_VERSION = 2
+ Solver_Class = FreeCellSolverWrapper(sbb='suit', esf='kings', sm='unlimited')
def _shuffleHook(self, cards):
# move base cards to top of the Talon (i.e. first cards to be dealt)
diff --git a/pysollib/games/beleagueredcastle.py b/pysollib/games/beleagueredcastle.py
index f05cabca..aaf0ff05 100644
--- a/pysollib/games/beleagueredcastle.py
+++ b/pysollib/games/beleagueredcastle.py
@@ -42,6 +42,7 @@ from pysollib.stack import *
from pysollib.game import Game
from pysollib.layout import Layout
from pysollib.hint import CautiousDefaultHint, FreeCellType_Hint
+from pysollib.hint import FreeCellSolverWrapper
from pysollib.pysoltk import MfxCanvasText
@@ -60,9 +61,11 @@ class BeleagueredCastleType_Hint(CautiousDefaultHint):
class StreetsAndAlleys(Game):
Hint_Class = BeleagueredCastleType_Hint
+ Solver_Class = FreeCellSolverWrapper(preset='streets_and_alleys')
Foundation_Class = SS_FoundationStack
- RowStack_Class = RK_RowStack
+ ##RowStack_Class = RK_RowStack
+ RowStack_Class = SuperMoveRK_RowStack
#
# game layout
@@ -102,7 +105,7 @@ class StreetsAndAlleys(Game):
for x in (x0, x2):
y = l.YM+l.YS*int(reserves!=0)
for i in range(4):
- stack = self.RowStack_Class(x, y, self, max_move=1, max_accept=1)
+ stack = self.RowStack_Class(x, y, self)
stack.CARD_XOFFSET, stack.CARD_YOFFSET = l.XOFFSET, 0
s.rows.append(stack)
y = y + l.YS
@@ -178,6 +181,8 @@ class Citadel(StreetsAndAlleys):
class ExiledKings(Citadel):
+ Hint_Class = BeleagueredCastleType_Hint
+ Solver_Class = FreeCellSolverWrapper(sbb='rank', esf='kings')
RowStack_Class = StackWrapper(RK_RowStack, base_rank=KING)
@@ -432,11 +437,13 @@ class Chessboard(Fortress):
class Stronghold(StreetsAndAlleys):
Hint_Class = FreeCellType_Hint
+ Solver_Class = FreeCellSolverWrapper(sbb='rank')
def createGame(self):
StreetsAndAlleys.createGame(self, reserves=1)
class Fastness(StreetsAndAlleys):
Hint_Class = FreeCellType_Hint
+ Solver_Class = FreeCellSolverWrapper(sbb='rank')
def createGame(self):
StreetsAndAlleys.createGame(self, reserves=2)
@@ -727,6 +734,8 @@ class Rittenhouse(Game):
class Lightweight(StreetsAndAlleys):
DEAL = (7, 1)
RowStack_Class = StackWrapper(RK_RowStack, base_rank=KING)
+ Solver_Class = FreeCellSolverWrapper(sbb='rank', esf='kings',
+ sm='unlimited')
def createGame(self, rows=12, playcards=20):
l, s = Layout(self), self.s
@@ -765,6 +774,7 @@ class Lightweight(StreetsAndAlleys):
class CastleMount(Lightweight):
DEAL = (11, 1)
RowStack_Class = Spider_SS_RowStack
+ Solver_Class = None
shallHighlightMatch = Game._shallHighlightMatch_RK
getQuickPlayScore = Game._getSpiderQuickPlayScore
@@ -786,6 +796,7 @@ class SelectiveCastle_RowStack(RK_RowStack):
class SelectiveCastle(StreetsAndAlleys, Chessboard):
Foundation_Class = Chessboard_Foundation
RowStack_Class = StackWrapper(SelectiveCastle_RowStack, mod=13)
+ Solver_Class = None
def createGame(self):
StreetsAndAlleys.createGame(self, texts=True)
@@ -854,7 +865,7 @@ class Soother(Game):
class PenelopesWeb(StreetsAndAlleys):
RowStack_Class = StackWrapper(RK_RowStack, base_rank=KING)
-
+ Solver_Class = FreeCellSolverWrapper(sbb='rank', esf='kings')
# register the game
diff --git a/pysollib/games/bisley.py b/pysollib/games/bisley.py
index 5748d70c..09fbaa2f 100644
--- a/pysollib/games/bisley.py
+++ b/pysollib/games/bisley.py
@@ -204,7 +204,7 @@ class Realm(Game):
l, s = Layout(self), self.s
# set window
- w, h = l.XM+8*l.XS, l.YM+2*l.YS+15*l.YOFFSET
+ w, h = 3*l.XM+8*l.XS, l.YM+2*l.YS+15*l.YOFFSET
self.setSize(w, h)
# create stacks
diff --git a/pysollib/games/calculation.py b/pysollib/games/calculation.py
index bc45471d..d2645211 100644
--- a/pysollib/games/calculation.py
+++ b/pysollib/games/calculation.py
@@ -368,7 +368,7 @@ class SeniorWrangler_Talon(DealRowTalonStack):
r = self.game.s.rows[self.round-1]
if not r.cards:
self.game.nextRoundMove(self)
- return
+ return 1
if sound:
self.game.startDealSample()
old_state = self.game.enterState(self.game.S_DEAL)
diff --git a/pysollib/games/camelot.py b/pysollib/games/camelot.py
index da28142a..16852daf 100644
--- a/pysollib/games/camelot.py
+++ b/pysollib/games/camelot.py
@@ -379,7 +379,8 @@ class PrincessPatience_RowStack(SS_RowStack):
def canMoveCards(self, cards):
if not SS_RowStack.canMoveCards(self, cards):
return False
- index = list(self.game.s.rows).index(self)
+ ##index = list(self.game.s.rows).index(self) # don't work in demo-mode with cloned stack
+ index = self.id
col = index % 4
row = index / 4
if index < 16: # left
@@ -403,6 +404,7 @@ class PrincessPatience_RowStack(SS_RowStack):
class PrincessPatience(Game):
+ Hint_Class = CautiousDefaultHint
RowStack_Class = PrincessPatience_RowStack
def createGame(self, max_rounds=1):
diff --git a/pysollib/games/capricieuse.py b/pysollib/games/capricieuse.py
index d05f977b..c3635668 100644
--- a/pysollib/games/capricieuse.py
+++ b/pysollib/games/capricieuse.py
@@ -40,7 +40,7 @@ from gypsy import DieRussische_Foundation
# ************************************************************************/
class Capricieuse(Game):
-
+ Hint_Class = CautiousDefaultHint
Talon_Class = StackWrapper(RedealTalonStack, max_rounds=3)
RowStack_Class = UD_SS_RowStack
@@ -113,6 +113,7 @@ class Nationale(Capricieuse):
# ************************************************************************/
class Strata(Game):
+ Hint_Class = CautiousDefaultHint
def createGame(self):
diff --git a/pysollib/games/curdsandwhey.py b/pysollib/games/curdsandwhey.py
index e703f869..947eb6af 100644
--- a/pysollib/games/curdsandwhey.py
+++ b/pysollib/games/curdsandwhey.py
@@ -286,6 +286,7 @@ class Harvestman(Arachnida):
# ************************************************************************/
class GermanPatience(Game):
+ Hint_Class = CautiousDefaultHint
def createGame(self, rows=8):
diff --git a/pysollib/games/fan.py b/pysollib/games/fan.py
index f0005f85..e79e9621 100644
--- a/pysollib/games/fan.py
+++ b/pysollib/games/fan.py
@@ -41,6 +41,7 @@ from pysollib.stack import *
from pysollib.game import Game
from pysollib.layout import Layout
from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint
+from pysollib.hint import FreeCellSolverWrapper
from pysollib.pysoltk import MfxCanvasText
@@ -133,6 +134,10 @@ class Fan(Game):
return ()
+class FanGame(Fan):
+ Solver_Class = FreeCellSolverWrapper(preset='fan')
+
+
# /***********************************************************************
# // Scotch Patience
# ************************************************************************/
@@ -461,6 +466,8 @@ class CloverLeaf_RowStack(UD_SS_RowStack):
if not self.cards:
return cards[0].rank in (ACE, KING)
return True
+ def _getBaseCard(self):
+ return _('Base card - Ace or King.')
class CloverLeaf(Game):
@@ -531,6 +538,8 @@ class CloverLeaf(Game):
# ************************************************************************/
class FreeFan(Fan):
+ RowStack_Class = FullStackWrapper(SuperMoveSS_RowStack, base_rank=KING)
+ Solver_Class = FreeCellSolverWrapper(esf='kings', sbb='suit')
def createGame(self):
Fan.createGame(self, reserves=2, playcards=8)
@@ -542,6 +551,7 @@ class FreeFan(Fan):
class BoxFan(Fan):
RowStack_Class = KingAC_RowStack
+ Solver_Class = FreeCellSolverWrapper(esf='kings')
def createGame(self):
Fan.createGame(self, rows=(4,4,4,4))
@@ -632,7 +642,7 @@ class FascinationFan(Fan):
def redealCards(self):
r0 = r1 = len(self.s.talon.cards)/3
m = len(self.s.talon.cards)%3
- if m >= 1: r2 += 1
+ if m >= 1: r1 += 1
self.s.talon.dealRow(rows=self.s.rows[:r0], flip=0, frames=4)
self.s.talon.dealRow(rows=self.s.rows[:r1], flip=0, frames=4)
self.s.talon.dealRowAvail(frames=4)
@@ -726,7 +736,7 @@ class Crescent(Game):
# register the game
-registerGame(GameInfo(56, Fan, "Fan",
+registerGame(GameInfo(56, FanGame, "Fan",
GI.GT_FAN_TYPE | GI.GT_OPEN, 1, 0, GI.SL_MOSTLY_SKILL))
registerGame(GameInfo(87, ScotchPatience, "Scotch Patience",
GI.GT_FAN_TYPE | GI.GT_OPEN, 1, 0, GI.SL_MOSTLY_SKILL))
diff --git a/pysollib/games/fortythieves.py b/pysollib/games/fortythieves.py
index 70e9a648..bfcf8299 100644
--- a/pysollib/games/fortythieves.py
+++ b/pysollib/games/fortythieves.py
@@ -542,6 +542,7 @@ class Octave_Talon(WasteTalonStack):
class Octave(Game):
+ Hint_Class = CautiousDefaultHint
#
# game layout
diff --git a/pysollib/games/freecell.py b/pysollib/games/freecell.py
index 5386ec17..e6772f64 100644
--- a/pysollib/games/freecell.py
+++ b/pysollib/games/freecell.py
@@ -51,37 +51,14 @@ from spider import Spider_AC_Foundation
# // FreeCell
# ************************************************************************/
-# To simplify playing we also consider the number of free rows.
-# Note that this only is legal if the game.s.rows have a
-# cap.base_rank == ANY_RANK.
-# See also the "SuperMove" section in the FreeCell FAQ.
-class FreeCell_RowStack(AC_RowStack):
- def _getMaxMove(self, to_stack_ncards):
- max_move = getNumberOfFreeStacks(self.game.s.reserves) + 1
- n = getNumberOfFreeStacks(self.game.s.rows)
- if to_stack_ncards == 0:
- n = n - 1
- while n > 0 and max_move < 1000:
- max_move = max_move * 2
- n = n - 1
- return max_move
-
- def canMoveCards(self, cards):
- max_move = self._getMaxMove(1)
- return len(cards) <= max_move and AC_RowStack.canMoveCards(self, cards)
-
- def acceptsCards(self, from_stack, cards):
- max_move = self._getMaxMove(len(self.cards))
- return len(cards) <= max_move and AC_RowStack.acceptsCards(self, from_stack, cards)
-
-
class FreeCell(Game):
Layout_Method = Layout.freeCellLayout
Talon_Class = InitialDealTalonStack
Foundation_Class = SS_FoundationStack
- RowStack_Class = FreeCell_RowStack
+ RowStack_Class = SuperMoveAC_RowStack
ReserveStack_Class = ReserveStack
- Hint_Class = FreeCellSolverWrapper(FreeCellType_Hint, {})
+ Hint_Class = FreeCellType_Hint
+ Solver_Class = FreeCellSolverWrapper()
#
@@ -127,7 +104,7 @@ class FreeCell(Game):
class RelaxedFreeCell(FreeCell):
RowStack_Class = AC_RowStack
- Hint_Class = FreeCellSolverWrapper(FreeCellType_Hint, {'sm' : "unlimited"})
+ Solver_Class = FreeCellSolverWrapper(sm='unlimited')
# /***********************************************************************
@@ -136,7 +113,7 @@ class RelaxedFreeCell(FreeCell):
class ForeCell(FreeCell):
RowStack_Class = StackWrapper(FreeCell_AC_RowStack, base_rank=KING)
- Hint_Class = FreeCellSolverWrapper(FreeCellType_Hint, {'esf' : "kings"})
+ Solver_Class = FreeCellSolverWrapper(esf='kings')
def startGame(self):
for i in range(5):
@@ -159,7 +136,7 @@ class ChallengeFreeCell(FreeCell):
class SuperChallengeFreeCell(ChallengeFreeCell):
RowStack_Class = StackWrapper(FreeCell_AC_RowStack, base_rank=KING)
- Hint_Class = FreeCellSolverWrapper(FreeCellType_Hint, {'esf' : "kings"})
+ Solver_Class = FreeCellSolverWrapper(esf='kings')
# /***********************************************************************
@@ -169,7 +146,7 @@ class SuperChallengeFreeCell(ChallengeFreeCell):
class Stalactites(FreeCell):
Foundation_Class = StackWrapper(RK_FoundationStack, suit=ANY_SUIT, mod=13, min_cards=1)
RowStack_Class = StackWrapper(BasicRowStack, max_move=1, max_accept=0)
- Hint_Class = FreeCellType_Hint
+ Solver_Class = None
def createGame(self):
FreeCell.createGame(self, reserves=2)
@@ -192,7 +169,7 @@ class Stalactites(FreeCell):
# ************************************************************************/
class DoubleFreecell(FreeCell):
- Hint_Class = FreeCellType_Hint
+ Solver_Class = None
#
# game layout
@@ -246,7 +223,7 @@ class DoubleFreecell(FreeCell):
# ************************************************************************/
class TripleFreecell(FreeCell):
- Hint_Class = FreeCellType_Hint
+ Solver_Class = None
#
# game layout
@@ -267,8 +244,8 @@ class TripleFreecell(FreeCell):
s.talon = self.Talon_Class(l.XM, h-l.YS, self)
x, y = l.XM+(max_rows-decks*4)*l.XS/2, l.YM
- for i in range(decks):
- for j in range(4):
+ for j in range(4):
+ for i in range(decks):
s.foundations.append(self.Foundation_Class(x, y, self, suit=j))
x += l.XS
x, y = l.XM+(max_rows-reserves)*l.XS/2, l.YM+l.YS
@@ -323,16 +300,24 @@ class BigCell(TripleFreecell):
# // Spidercells
# ************************************************************************/
-class Spidercells_RowStack(FreeCell_RowStack):
+class Spidercells_RowStack(SuperMoveAC_RowStack):
def canMoveCards(self, cards):
if len(cards) == 13 and isAlternateColorSequence(cards):
return True
- return FreeCell_RowStack.canMoveCards(self, cards)
+ return SuperMoveAC_RowStack.canMoveCards(self, cards)
+ 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)
class Spidercells(FreeCell):
- Hint_Class = FreeCellType_Hint
+ Solver_Class = None
Foundation_Class = Spider_AC_Foundation
RowStack_Class = Spidercells_RowStack
@@ -361,8 +346,6 @@ class Spidercells(FreeCell):
# ************************************************************************/
class SevenByFour(FreeCell):
- Hint_Class = FreeCellSolverWrapper(FreeCellType_Hint, {})
- #Hint_Class = FreeCellType_Hint
def createGame(self):
FreeCell.createGame(self, rows=7)
def startGame(self):
@@ -377,9 +360,8 @@ class SevenByFive(SevenByFour):
FreeCell.createGame(self, rows=7, reserves=5)
class Bath(FreeCell):
- Hint_Class = FreeCellSolverWrapper(FreeCellType_Hint, {'esf' : 'kings'})
- #Hint_Class = FreeCellType_Hint
- RowStack_Class = StackWrapper(FreeCell_RowStack, base_rank=KING)
+ Solver_Class = FreeCellSolverWrapper(esf='kings')
+ RowStack_Class = StackWrapper(SuperMoveAC_RowStack, base_rank=KING)
def createGame(self):
FreeCell.createGame(self, rows=10, reserves=2)
def startGame(self):
@@ -395,7 +377,7 @@ class Bath(FreeCell):
# ************************************************************************/
class Clink(FreeCell):
- Hint_Class = FreeCellType_Hint
+ Solver_Class = None
def createGame(self):
# create layout
@@ -440,7 +422,7 @@ class Clink(FreeCell):
# ************************************************************************/
class Repair(FreeCell):
- Hint_Class = FreeCellType_Hint
+ Solver_Class = FreeCellSolverWrapper(sm='unlimited')
RowStack_Class = AC_RowStack
def createGame(self):
@@ -464,7 +446,7 @@ class FourColours_RowStack(AC_RowStack):
return self.game.app.images.getReserveBottom()
class FourColours(FreeCell):
- Hint_Class = FreeCellType_Hint
+ Solver_Class = None
RowStack_Class = AC_RowStack
def createGame(self):
@@ -507,7 +489,7 @@ class GermanFreeCell_Reserve(ReserveStack):
class GermanFreeCell(SevenByFour):
- Hint_Class = FreeCellType_Hint
+ Solver_Class = None
RowStack_Class = AC_RowStack
ReserveStack_Class = GermanFreeCell_Reserve
@@ -524,7 +506,7 @@ class GermanFreeCell(SevenByFour):
# ************************************************************************/
class OceanTowers(TripleFreecell):
- Hint_Class = FreeCellType_Hint
+ Solver_Class = FreeCellSolverWrapper(esf='kings', sbb='suit')
RowStack_Class = StackWrapper(FreeCell_SS_RowStack, base_rank=KING)
def createGame(self):
@@ -550,7 +532,7 @@ class KingCell_RowStack(RK_RowStack):
return len(cards) <= max_move and RK_RowStack.canMoveCards(self, cards)
class KingCell(FreeCell):
- Hint_Class = FreeCellType_Hint
+ Solver_Class = FreeCellSolverWrapper(esf='kings')
RowStack_Class = StackWrapper(KingCell_RowStack, base_rank=KING)
shallHighlightMatch = Game._shallHighlightMatch_RK
@@ -612,6 +594,7 @@ class Headquarters(Game):
class CanCan(FreeCell):
Hint_Class = DefaultHint
+ Solver_Class = None
RowStack_Class = KingAC_RowStack
ReserveStack_Class = StackWrapper(OpenStack, max_accept=0)
diff --git a/pysollib/games/golf.py b/pysollib/games/golf.py
index a002dea1..eec772cf 100644
--- a/pysollib/games/golf.py
+++ b/pysollib/games/golf.py
@@ -291,6 +291,8 @@ class BlackHole_Foundation(AbstractFoundationStack):
r1, r2 = self.cards[-1].rank, cards[0].rank
return (r1 + 1) % self.cap.mod == r2 or (r2 + 1) % self.cap.mod == r1
return 1
+ def getHelp(self):
+ return _('Foundation. Build up or down regardless of suit.')
class BlackHole_RowStack(ReserveStack):
diff --git a/pysollib/games/grandfathersclock.py b/pysollib/games/grandfathersclock.py
index 95b6772e..d916219c 100644
--- a/pysollib/games/grandfathersclock.py
+++ b/pysollib/games/grandfathersclock.py
@@ -462,9 +462,9 @@ class BigBen(Game):
def startGame(self):
self.startDealSample()
- self.s.talon.dealRow(rows=self.s.foundations)
+ self.s.talon.dealRow(rows=self.s.foundations, frames=4)
for i in range(3):
- self.s.talon.dealRow()
+ self.s.talon.dealRow(frames=4)
def _autoDeal(self, sound=1):
# don't deal a card to the waste if the waste is empty
diff --git a/pysollib/games/harp.py b/pysollib/games/harp.py
index 3b1ea86c..552978ff 100644
--- a/pysollib/games/harp.py
+++ b/pysollib/games/harp.py
@@ -263,6 +263,7 @@ class BigDeal(DoubleKlondike):
# ************************************************************************/
class Delivery(BigDeal):
+ Hint_Class = CautiousDefaultHint
RowStack_Class = StackWrapper(SS_RowStack, max_move=1)
def createGame(self):
diff --git a/pysollib/games/headsandtails.py b/pysollib/games/headsandtails.py
index 343ed117..e99c51fa 100644
--- a/pysollib/games/headsandtails.py
+++ b/pysollib/games/headsandtails.py
@@ -45,6 +45,7 @@ class HeadsAndTails_Reserve(OpenStack):
class HeadsAndTails(Game):
+ Hint_Class = CautiousDefaultHint
#
# game layout
diff --git a/pysollib/games/katzenschwanz.py b/pysollib/games/katzenschwanz.py
index 42b2458b..c75df7ed 100644
--- a/pysollib/games/katzenschwanz.py
+++ b/pysollib/games/katzenschwanz.py
@@ -41,6 +41,7 @@ from pysollib.stack import *
from pysollib.game import Game
from pysollib.layout import Layout
from pysollib.hint import DefaultHint, FreeCellType_Hint, CautiousDefaultHint
+from pysollib.hint import FreeCellSolverWrapper
from pysollib.pysoltk import MfxCanvasText
@@ -63,6 +64,7 @@ class DerKatzenschwanz_Hint(FreeCellType_Hint):
class DerKatzenschwanz(Game):
RowStack_Class = StackWrapper(AC_RowStack, base_rank=NO_RANK)
Hint_Class = DerKatzenschwanz_Hint
+ Solver_Class = FreeCellSolverWrapper(esf='none', sm='unlimited')
#
# game layout
@@ -145,6 +147,7 @@ class DerKatzenschwanz(Game):
class DieSchlange(DerKatzenschwanz):
RowStack_Class = StackWrapper(FreeCell_AC_RowStack, base_rank=NO_RANK)
+ Solver_Class = FreeCellSolverWrapper(esf='none')
def createGame(self):
DerKatzenschwanz.createGame(self, rows=9, reserves=7)
@@ -171,8 +174,8 @@ class DieSchlange(DerKatzenschwanz):
class Kings(DerKatzenschwanz):
- ##RowStack_Class = StackWrapper(AC_RowStack, base_rank=NO_RANK)
- RowStack_Class = StackWrapper(FreeCell_AC_RowStack, base_rank=NO_RANK)
+ RowStack_Class = StackWrapper(AC_RowStack, base_rank=NO_RANK)
+ Solver_Class = FreeCellSolverWrapper(esf='none', sm='unlimited')
def createGame(self):
return DerKatzenschwanz.createGame(self, rows=8, reserves=8)
@@ -192,8 +195,8 @@ class Kings(DerKatzenschwanz):
class Retinue(DieSchlange, Kings):
- ##RowStack_Class = StackWrapper(AC_RowStack, base_rank=NO_RANK)
RowStack_Class = StackWrapper(FreeCell_AC_RowStack, base_rank=NO_RANK)
+ Solver_Class = FreeCellSolverWrapper(esf='none')
def createGame(self):
return DerKatzenschwanz.createGame(self, rows=8, reserves=8)
@@ -249,6 +252,7 @@ class SalicLaw_Talon(OpenTalonStack):
class SalicLaw(DerKatzenschwanz):
Hint_Class = SalicLaw_Hint
+ Solver_Class = None
Foundation_Classes = [
StackWrapper(AbstractFoundationStack, max_cards=1, base_rank=QUEEN),
@@ -340,6 +344,7 @@ class SalicLaw(DerKatzenschwanz):
class Deep(DerKatzenschwanz):
RowStack_Class = StackWrapper(AC_RowStack, base_rank=ANY_RANK)
+ Solver_Class = FreeCellSolverWrapper(sm='unlimited')
def createGame(self):
return DerKatzenschwanz.createGame(self, rows=8, reserves=8)
@@ -499,6 +504,7 @@ class StepUp_RowStack(AC_RowStack):
class StepUp(Game):
+ Hint_Class = CautiousDefaultHint
def createGame(self):
l, s = Layout(self), self.s
diff --git a/pysollib/games/klondike.py b/pysollib/games/klondike.py
index 5c5d47a6..fd556ee3 100644
--- a/pysollib/games/klondike.py
+++ b/pysollib/games/klondike.py
@@ -43,6 +43,7 @@ from pysollib.game import Game
from pysollib.layout import Layout
from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint
from pysollib.hint import KlondikeType_Hint
+from pysollib.hint import FreeCellSolverWrapper
from pysollib.pysoltk import MfxCanvasText
from canfield import CanfieldRush_Talon
@@ -283,15 +284,14 @@ class BlindAlleys(Eastcliff):
# /***********************************************************************
# // Somerset
# // Morehead
-# // Canister
-# // American Canister
-# // British Canister
+# // Usk
# ************************************************************************/
class Somerset(Klondike):
Talon_Class = InitialDealTalonStack
- RowStack_Class = StackWrapper(AC_RowStack, max_move=1)
+ RowStack_Class = SuperMoveAC_RowStack
Hint_Class = CautiousDefaultHint
+ Solver_Class = FreeCellSolverWrapper()
def createGame(self):
Klondike.createGame(self, max_rounds=1, rows=10, waste=0, texts=0)
@@ -306,12 +306,14 @@ class Somerset(Klondike):
class Morehead(Somerset):
RowStack_Class = StackWrapper(BO_RowStack, max_move=1)
+ Solver_Class = None
class Usk(Somerset):
Talon_Class = RedealTalonStack
RowStack_Class = StackWrapper(AC_RowStack, base_rank=KING)
+ Solver_Class = None
def createGame(self):
Klondike.createGame(self, max_rounds=2, rows=10, waste=0, texts=0)
@@ -330,6 +332,8 @@ class Usk(Somerset):
class AmericanCanister(Klondike):
Talon_Class = InitialDealTalonStack
+ RowStack_Class = AC_RowStack
+ Solver_Class = FreeCellSolverWrapper(sm='unlimited')
def createGame(self):
Klondike.createGame(self, max_rounds=1, rows=8, waste=0, texts=0)
@@ -344,11 +348,13 @@ class AmericanCanister(Klondike):
class Canister(AmericanCanister):
RowStack_Class = RK_RowStack
+ Solver_Class = FreeCellSolverWrapper(sbb='rank', sm='unlimited')
shallHighlightMatch = Game._shallHighlightMatch_RK
class BritishCanister(AmericanCanister):
RowStack_Class = StackWrapper(KingAC_RowStack, max_move=1)
+ Solver_Class = FreeCellSolverWrapper(esf='kings')
# /***********************************************************************
@@ -813,6 +819,7 @@ class Alternation(Klondike):
class Lanes(Klondike):
+ Hint_Class = CautiousDefaultHint
Foundation_Class = StackWrapper(SS_FoundationStack, max_move=0)
RowStack_Class = StackWrapper(AC_RowStack, base_rank=ANY_RANK, max_move=1)
@@ -979,6 +986,8 @@ class DoubleDot(Klondike):
def shallHighlightMatch(self, stack1, card1, stack2, card2):
return abs(card1.rank-card2.rank) == 2
+ shallHighlightMatch = Game._shallHighlightMatch_RKW
+
# /***********************************************************************
# // Seven Devils
@@ -1176,6 +1185,7 @@ class GoldMine(Klondike):
# ************************************************************************/
class LuckyThirteen(Game):
+ Hint_Class = CautiousDefaultHint
RowStack_Class = StackWrapper(RK_RowStack, base_rank=NO_RANK)
def createGame(self, xoffset=0, playcards=0):
diff --git a/pysollib/games/mahjongg/mahjongg.py b/pysollib/games/mahjongg/mahjongg.py
index aa05496d..b18dccd4 100644
--- a/pysollib/games/mahjongg/mahjongg.py
+++ b/pysollib/games/mahjongg/mahjongg.py
@@ -95,8 +95,8 @@ class Mahjongg_Foundation(OpenStack):
def basicIsBlocked(self):
return 1
- def initBindings(self):
- pass
+ #def initBindings(self):
+ # pass
def _position(self, card):
#AbstractFoundationStack._position(self, card)
@@ -360,11 +360,13 @@ class AbstractMahjonggGame(Game):
if self.preview:
# Fixme
dx, dy, d_x, d_y = dx/2, dy/2, d_x/2, d_y/2
+ self._delta_x, self._delta_y = dx, -dy
else:
dx = 3
dy = -3
d_x = 0
d_y = 0
+ self._delta_x, self._delta_y = 0, 0
#print dx, dy, d_x, d_y, cs.version
font = self.app.getFont("canvas_default")
@@ -472,7 +474,7 @@ class AbstractMahjonggGame(Game):
y = l.YM+fdyy+j*cardh
else:
if TOOLKIT == 'tk':
- x = -l.XS
+ x = -l.XS-self.canvas.xmargin
y = l.YM+dyy
elif TOOLKIT == 'gtk':
# FIXME
@@ -635,6 +637,10 @@ class AbstractMahjonggGame(Game):
# Mahjongg special: highlight all moveable tiles
return ((self.s.rows, 1),)
+ def _highlightCards(self, info, sleep=1.5, delta=(1,1,1,1)):
+ delta = (-self._delta_x, 0, 0, -self._delta_y)
+ Game._highlightCards(self, info, sleep=sleep, delta=delta)
+
def getCardFaceImage(self, deck, suit, rank):
if suit == 3:
cs = self.app.cardset
@@ -690,6 +696,7 @@ class AbstractMahjonggGame(Game):
return 7 >= card2.rank >= 4
return card1.rank == card2.rank
+
## mahjongg util
def comp_cardset(ncards):
# calc decks, ranks & trumps
diff --git a/pysollib/games/mahjongg/shisensho.py b/pysollib/games/mahjongg/shisensho.py
index de0f1e2e..c47ae9c3 100644
--- a/pysollib/games/mahjongg/shisensho.py
+++ b/pysollib/games/mahjongg/shisensho.py
@@ -254,13 +254,14 @@ class Shisen_RowStack(Mahjongg_RowStack):
cardw = images.CARDW
cardh = images.CARDH
coords = []
+ dx, dy = game._delta_x, game._delta_y
for x, y in path:
if x == 0:
coords.append(6)
elif x == game.L[0]+1:
- coords.append(x0+cardw*(x-1)+10)
+ coords.append(x0+cardw*(x-1)+10+dx)
else:
- coords.append(x0+cardw/2+cardw*(x-1))
+ coords.append(x0+cardw/2+cardw*(x-1)+dx)
if y == 0:
coords.append(6)
elif y == game.L[1]+1:
@@ -308,11 +309,13 @@ class AbstractShisenGame(AbstractMahjonggGame):
dy = -l.YOFFSET
d_x = cs.SHADOW_XOFFSET
d_y = cs.SHADOW_YOFFSET
+ self._delta_x, self._delta_y = dx, -dy
else:
dx = 3
dy = -3
d_x = 0
d_y = 0
+ self._delta_x, self._delta_y = 0, 0
font = self.app.getFont("canvas_default")
@@ -358,10 +361,9 @@ class AbstractShisenGame(AbstractMahjonggGame):
self.texts.info = MfxCanvasText(self.canvas,
self.width - l.XM - ti_width, y,
anchor="nw", font=font)
- x = self.width + l.XS
- y = self.height - l.YS - dxx
# the Talon is invisble
- s.talon = InitialDealTalonStack(x, y + l.YS, self)
+ s.talon = InitialDealTalonStack(-l.XS-self.canvas.xmargin,
+ self.height-dyy, self)
# Define stack groups
l.defaultStackGroups()
diff --git a/pysollib/games/numerica.py b/pysollib/games/numerica.py
index f3320592..d997795a 100644
--- a/pysollib/games/numerica.py
+++ b/pysollib/games/numerica.py
@@ -716,7 +716,7 @@ class Assembly_RowStack(RK_RowStack):
class Assembly(Numerica):
- Hint_Class = DefaultHint
+ Hint_Class = CautiousDefaultHint
Foundation_Class = StackWrapper(RK_FoundationStack, suit=ANY_SUIT)
RowStack_Class = StackWrapper(Assembly_RowStack, max_move=1)
diff --git a/pysollib/games/pyramid.py b/pysollib/games/pyramid.py
index 4a062667..caaf64a2 100644
--- a/pysollib/games/pyramid.py
+++ b/pysollib/games/pyramid.py
@@ -274,7 +274,7 @@ class Pyramid(Game):
def startGame(self):
self.startDealSample()
- self.s.talon.dealRow()
+ self.s.talon.dealRow(frames=4)
self.s.talon.dealCards() # deal first card to WasteStack
def getAutoStacks(self, event=None):
@@ -585,7 +585,7 @@ class Fifteens_RowStack(Elevens_RowStack):
def acceptsCards(self, from_stack, cards):
if not Elevens_RowStack.acceptsCards(self, from_stack, cards):
return False
- return cards[0].rank < 9 and self.cards[0] < 9
+ return cards[0].rank < 9 and self.cards[0].rank < 9
class Fifteens_Reserve(ReserveStack):
@@ -919,7 +919,7 @@ class Apophis(Pharaohs):
def startGame(self):
self.startDealSample()
- self.s.talon.dealRow(frames=3)
+ self.s.talon.dealRow(frames=4)
self.s.talon.dealCards()
def shallHighlightMatch(self, stack1, card1, stack2, card2):
@@ -954,7 +954,7 @@ class Cheops_RowStack(Cheops_StackMethods, Pyramid_RowStack):
class Cheops(Pyramid):
- Foundation_Class = AbstractFoundationStack
+ Foundation_Class = StackWrapper(AbstractFoundationStack, max_accept=0)
Talon_Class = StackWrapper(Cheops_Talon, max_rounds=1, max_accept=1)
RowStack_Class = Cheops_RowStack
WasteStack_Class = Cheops_Waste
diff --git a/pysollib/games/royalcotillion.py b/pysollib/games/royalcotillion.py
index e85fdc98..39e8d0b9 100644
--- a/pysollib/games/royalcotillion.py
+++ b/pysollib/games/royalcotillion.py
@@ -592,6 +592,7 @@ class ThreePirates_Talon(DealRowTalonStack):
class ThreePirates(Game):
+ Hint_Class = CautiousDefaultHint
def createGame(self):
l, s = Layout(self), self.s
@@ -634,6 +635,20 @@ class ThreePirates(Game):
# // Frames
# ************************************************************************/
+class Frames_Hint(CautiousDefaultHint):
+ def computeHints(self):
+ CautiousDefaultHint.computeHints(self)
+ if self.hints:
+ return
+ if not self.game.s.talon.cards:
+ return
+ for s in self.game.s.reserves:
+ if s.cards:
+ for r in self.game.s.rows:
+ if r.acceptsCards(s, s.cards):
+ self.addHint(5000, 1, s, r)
+
+
class Frames_Foundation(UnionSquare_Foundation):
def acceptsCards(self, from_stack, cards):
if not UnionSquare_Foundation.acceptsCards(self, from_stack, cards):
@@ -664,7 +679,7 @@ class Frames_RowStack(UD_SS_RowStack):
class Frames(Game):
- Hint_Class = CautiousDefaultHint
+ Hint_Class = Frames_Hint #CautiousDefaultHint
def createGame(self):
l, s = Layout(self), self.s
@@ -1053,7 +1068,7 @@ class Colonel(Game):
x, y = l.XM+2*l.XS, l.YM
for i in range(8):
- s.foundations.append(SS_FoundationStack(x, y, self, suit=i%4,
+ s.foundations.append(SS_FoundationStack(x, y, self, suit=i/2,
max_move=0))
x += l.XS
@@ -1079,7 +1094,7 @@ class Colonel(Game):
def startGame(self):
self.startDealSample()
- self.s.talon.dealRow()
+ self.s.talon.dealRow(frames=4)
self.s.talon.dealCards()
shallHighlightMatch = Game._shallHighlightMatch_SS
diff --git a/pysollib/games/siebenbisas.py b/pysollib/games/siebenbisas.py
index bf85694c..9b4320be 100644
--- a/pysollib/games/siebenbisas.py
+++ b/pysollib/games/siebenbisas.py
@@ -161,6 +161,19 @@ class SiebenBisAs(Game):
# // Maze
# ************************************************************************/
+class Maze_Hint(SiebenBisAs_Hint):
+ def shallMovePile(self, from_stack, to_stack, pile, rpile):
+ if from_stack is to_stack or not to_stack.acceptsCards(from_stack, pile):
+ return 0
+ # now check for loops
+ rr = self.ClonedStack(from_stack, stackcards=rpile)
+ if rr.acceptsCards(to_stack, pile):
+ # the pile we are going to move could be moved back -
+ # this is dangerous as we can create endless loops...
+ return 0
+ return 1
+
+
class Maze_RowStack(BasicRowStack):
def acceptsCards(self, from_stack, cards):
if not BasicRowStack.acceptsCards(self, from_stack, cards):
@@ -189,7 +202,7 @@ class Maze_RowStack(BasicRowStack):
class Maze(Game):
GAME_VERSION = 2
- Hint_Class = SiebenBisAs_Hint
+ Hint_Class = Maze_Hint #SiebenBisAs_Hint
#
# game layout
diff --git a/pysollib/games/spider.py b/pysollib/games/spider.py
index 953bd3fb..1a283a0b 100644
--- a/pysollib/games/spider.py
+++ b/pysollib/games/spider.py
@@ -42,6 +42,7 @@ from pysollib.game import Game
from pysollib.layout import Layout
from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint
from pysollib.hint import SpiderType_Hint, YukonType_Hint
+from pysollib.hint import FreeCellSolverWrapper
# /***********************************************************************
@@ -112,6 +113,23 @@ class Spider_RowStack(Spider_SS_RowStack):
return (None, 0)
+class SuperMoveSpider_RowStack(SuperMoveStack_StackMethods, Spider_RowStack):
+ def acceptsCards(self, from_stack, cards):
+ if not Spider_RowStack.acceptsCards(self, from_stack, cards):
+ return False
+ num_seq = self._getNumSSSeq(cards)
+ max_move = self._getMaxMove(len(self.cards))
+ return num_seq <= max_move
+ def canMoveCards(self, cards):
+ if not self.basicCanMoveCards(cards):
+ return False
+ if not isRankSequence(cards, self.cap.mod, self.cap.dir):
+ return False
+ num_seq = self._getNumSSSeq(cards)
+ max_move = self._getMaxMove(1)
+ return num_seq <= max_move
+
+
# /***********************************************************************
# // Relaxed Spider
# ************************************************************************/
@@ -282,6 +300,8 @@ class WillOTheWisp(Spiderette):
class SimpleSimon(Spider):
Talon_Class = InitialDealTalonStack
+ RowStack_Class = SuperMoveSpider_RowStack
+ Solver_Class = FreeCellSolverWrapper(preset='simple_simon', base_rank=0)
def createGame(self):
Spider.createGame(self, rows=10, texts=0)
@@ -292,6 +312,12 @@ class SimpleSimon(Spider):
self.startDealSample()
self.s.talon.dealRow()
+class SimpleSimonII(SimpleSimon):
+ Solver_Class = None
+ Foundation_Class = StackWrapper(Spider_SS_Foundation,
+ base_rank=ANY_RANK, mod=13)
+ RowStack_Class = StackWrapper(SuperMoveSpider_RowStack, mod=13)
+
# /***********************************************************************
# // Rachel
@@ -1370,4 +1396,6 @@ registerGame(GameInfo(685, FechtersGame, "Fechter's Game",
GI.GT_SPIDER, 2, 0, GI.SL_MOSTLY_SKILL))
registerGame(GameInfo(710, Bebop, "Bebop",
GI.GT_2DECK_TYPE | GI.GT_ORIGINAL, 2, 0, GI.SL_MOSTLY_SKILL))
+#registerGame(GameInfo(711, SimpleSimonII, "Simple Simon II",
+# GI.GT_SPIDER | GI.GT_OPEN, 1, 0, GI.SL_MOSTLY_SKILL))
diff --git a/pysollib/games/sultan.py b/pysollib/games/sultan.py
index f646cac0..03209c7a 100644
--- a/pysollib/games/sultan.py
+++ b/pysollib/games/sultan.py
@@ -762,13 +762,13 @@ class RoyalAids(Game):
s.waste = WasteStack(x, y, self)
l.createText(s.waste, 'se')
- x, y = l.XM+4*l.XS, l.YM+2*l.YS
+ x, y = l.XM+3.75*l.XS, l.YM+2*l.YS
for i in (0,1):
stack = RoyalAids_RowStack(x, y, self, max_move=1)
s.rows.append(stack)
stack.CARD_XOFFSET, stack.CARD_YOFFSET = 0, 0
x += l.XS
- x, y = l.XM+3*l.XS, l.YM+3*l.YS
+ x, y = l.XM+2.75*l.XS, l.YM+3*l.YS
for i in range(4):
stack = BasicRowStack(x, y, self)
s.reserves.append(stack)
@@ -995,7 +995,7 @@ class Khedive(Game):
def startGame(self):
self.startDealSample()
- self.s.talon.dealRow()
+ self.s.talon.dealRow(frames=4)
self.s.talon.dealCards()
def fillStack(self, stack):
diff --git a/pysollib/games/terrace.py b/pysollib/games/terrace.py
index 37b820e6..6b1c8aeb 100644
--- a/pysollib/games/terrace.py
+++ b/pysollib/games/terrace.py
@@ -365,9 +365,9 @@ class BastilleDay_BastilleStack(Stack):
old_state = self.game.enterState(self.game.S_DEAL)
if sound and not self.game.demo:
self.game.playSample("dealwaste")
- self.flipMove()
- self.moveMove(1, self.game.s.reserves[-1], frames=4, shadow=0)
+ self.game.flipAndMoveMove(self, self.game.s.reserves[-1])
self.game.leaveState(old_state)
+ self.game.finishMove()
return 1
def getHelp(self):
@@ -432,7 +432,7 @@ class BastilleDay(Game):
if self.demo:
r = self.s.reserves[0]
if r.canDealCards():
- self.demo.last_deal = [] # don't check last deal
+ ##self.demo.last_deal = [] # don't check last deal
return r.dealCards(sound=sound)
return Game.dealCards(self, sound=sound)
diff --git a/pysollib/games/windmill.py b/pysollib/games/windmill.py
index 509edaec..4342a3aa 100644
--- a/pysollib/games/windmill.py
+++ b/pysollib/games/windmill.py
@@ -159,6 +159,7 @@ class DutchSolitaire_RowStack(UD_RK_RowStack):
class DutchSolitaire(Windmill):
+ Hint_Class = CautiousDefaultHint
Foundation_Classes = [
StackWrapper(BlackHole_Foundation, suit=ANY_SUIT, mod=13,
max_cards=UNLIMITED_CARDS, min_cards=1),
@@ -270,7 +271,7 @@ class Corners(Game):
self.setSize(5*l.XS+l.XM, 4*l.YS+3*l.YM)
# create stacks
- x, y = l.XM+l.XS, l.YM
+ x, y = l.XM+1.5*l.XS, l.YM
s.talon = WasteTalonStack(x, y, self, max_rounds=max_rounds)
l.createText(s.talon, "sw")
x += l.XS
diff --git a/pysollib/games/yukon.py b/pysollib/games/yukon.py
index b70c453a..80cf31c6 100644
--- a/pysollib/games/yukon.py
+++ b/pysollib/games/yukon.py
@@ -43,6 +43,7 @@ from pysollib.game import Game
from pysollib.layout import Layout
from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint
from pysollib.hint import YukonType_Hint
+from pysollib.hint import FreeCellSolverWrapper
from pysollib.pysoltk import MfxCanvasText
from spider import Spider_SS_Foundation
diff --git a/pysollib/hint.py b/pysollib/hint.py
index 2ab7e2a5..a15a6448 100644
--- a/pysollib/hint.py
+++ b/pysollib/hint.py
@@ -36,8 +36,10 @@
# imports
import os, sys
+import time
# PySol imports
+from settings import USE_FREECELL_SOLVER, FCS_COMMAND
from mfxutil import destruct
from util import KING
@@ -689,289 +691,234 @@ class SpiderType_Hint(DefaultHint):
# /***********************************************************************
-# //
+# // FreeCell-Solver
# ************************************************************************/
-FreecellSolver = None
-## try:
-## import FreecellSolver
-## except:
-## FreecellSolver = None
+class FreeCellSolver_Hint:
-fcs_command = 'fc-solve'
-if os.name == 'nt':
- if sys.path[0] and not os.path.isdir(sys.path[0]): # i.e. library.zip
- fcs_command = os.path.join(os.path.split(sys.path[0])[0], 'fc-solve.exe')
- fcs_command = '"%s"' % fcs_command
+ def __init__(self, game, dialog, **game_type):
+ self.game = game
+ self.dialog = dialog
+ self.game_type = game_type
+ self.options = {
+ 'method': 'dfs',
+ 'max_iters': 10000,
+ 'max_depth': 1000,
+ 'progress': False,
+ 'preset': None,
+ }
+ self.hints = []
+ self.hints_index = 0
-if os.name in ('posix', 'nt'):
- try:
- pin, pout, perr = os.popen3(fcs_command+' --help')
- if pout.readline().startswith('fc-solve'):
- FreecellSolver = True
- del pin, pout, perr
- if os.name == 'posix':
- os.wait()
- except:
- ##traceback.print_exc()
- pass
+ # correct cards rank if foundations.base_rank != 0 (Penguin, Opus)
+ if 'base_rank' in game_type: # (Simple Simon)
+ self.base_rank = game_type['base_rank']
+ else:
+ self.base_rank = game.s.foundations[0].cap.base_rank
+ ##print 'game_type:', game_type
+ ##print 'base_rank:', self.base_rank
+
+ def config(self, **kw):
+ self.options.update(kw)
-class FreeCellSolverWrapper:
- __name__ = 'FreeCellSolverWrapper' # for gameinfodialog
+ def card2str1(self, card):
+ # row and reserves
+ rank = (card.rank-self.base_rank) % 13
+ return "A23456789TJQK"[rank] + "CSHD"[card.suit]
+ def card2str2(self, card):
+ # foundations
+ rank = (card.rank-self.base_rank) % 13
+ return "CSHD"[card.suit] + "-" + "A23456789TJQK"[rank]
- class FreeCellSolver_Hint(AbstractHint):
- def str1(self, card):
- return "A23456789TJQK"[card.rank] + "CSHD"[card.suit]
- def str2(self, card):
- return "CSHD"[card.suit] + "-" + "A23456789TJQK"[card.rank]
+# hard solvable: Freecell #47038300998351211829 (65539 iters)
+ def getHints(self, taken_hint=None):
+ if taken_hint and taken_hint[6]:
+ return [taken_hint[6]]
+ h = self.hints[self.hints_index]
+ #print 'getHints', taken_hint, h
+ if h is None:
+ return None
+ ncards, src, dest = h
+ thint = None
+ if len(src.cards) > ncards and not src.cards[-ncards-1].face_up:
+ # flip card
+ thint = (999999, 0, 1, src, src, None, None)
+ if dest == None: # foundation
+ cards = src.cards[-ncards:]
+ for f in self.game.s.foundations:
+ if f.acceptsCards(src, cards):
+ dest = f
+ break
+ assert dest
+ hint = (999999, 0, ncards, src, dest, None, thint)
+ self.hints_index += 1
+ #print hint
+ return [hint]
- def computeHints(self):
- ##print 'FreeCellSolver_Hint.computeHints'
-
- board = ''
- pboard = {}
- #
+ def computeHints(self):
+ game = self.game
+ game_type = self.game_type
+ progress = self.options['progress']
+ board = ''
+ #
+ #
+ b = ''
+ for s in self.game.s.foundations:
+ if s.cards:
+ ss = self.card2str2(s.cards[-1])
+ b += ' ' + ss
+ if b:
+ board += 'Founds:' + b + '\n'
+ #
+ b = ''
+ for s in self.game.s.reserves:
+ if s.cards:
+ cs = self.card2str1(s.cards[-1])
+ b += ' ' + cs
+ else:
+ b += ' -'
+ if b:
+ board += 'FC:' + b + '\n'
+ #
+ for s in self.game.s.rows:
b = ''
- #l = []
- for s in self.game.s.foundations:
- if s.cards:
- b = b + ' ' + self.str2(s.cards[-1])
- #l.append(self.str2(s.cards[-1]))
- if b:
- board = board + 'Founds:' + b + '\n'
- #pboard['Founds'] = l
- #
- b = ''
- l = []
- for s in self.game.s.reserves:
- if s.cards:
- cs = self.str1(s.cards[-1])
- b = b + ' ' + cs
- l.append(cs)
- else:
- b = b + ' -'
- l.append(None)
- if b:
- board = board + 'FC:' + b + '\n'
- pboard['FC'] = l
- #
- n = 0
- for s in self.game.s.rows:
- b = ''
- l = []
- for c in s.cards:
- cs = self.str1(c)
- if not c.face_up:
- cs = '<%s>' % cs
- b = b + cs + ' '
- l.append(cs)
- board = board + b.strip() + '\n'
- pboard[n] = l
- n += 1
- #
- ##print board
- #
- args = []
- args += ['-sam', '-p', '--display-10-as-t']
- ##args += ['-l', 'good-intentions']
- args += ['--max-iters', 200000]
- args += ['--decks-num', self.fcs_args[0],
- '--stacks-num', self.fcs_args[1],
- '--freecells-num', self.fcs_args[2],
- ]
- #
- game_type = self.fcs_args[3]
- if 'sbb' in game_type:
- args += ['--sequences-are-built-by', game_type['sbb']]
- if 'sm' in game_type:
- args += ['--sequence-move', game_type['sm']]
- if 'esf' in game_type:
- args += ['--empty-stacks-filled-by', game_type['esf']]
- if 'preset' in game_type:
- args += ['--preset', game_type['preset']]
+ for c in s.cards:
+ cs = self.card2str1(c)
+ if not c.face_up:
+ cs = '<%s>' % cs
+ b += cs + ' '
+ board = board + b.strip() + '\n'
+ #
+ ##print '--------------------\n', board, '--------------------'
+ #
+ args = []
+ ##args += ['-sam', '-p', '-opt', '--display-10-as-t']
+ args += ['-m', '-p', '-opt']
+ if self.options['preset'] and self.options['preset'] != 'none':
+ args += ['-l', self.options['preset']]
+ if progress:
+ args += ['--iter-output']
+ ##args += ['-s']
+ args += ['--max-iters', self.options['max_iters'],
+ '--max-depth', self.options['max_depth'],
+ '--method', self.options['method'],
+ '--decks-num', game.gameinfo.decks,
+ '--stacks-num', len(game.s.rows),
+ '--freecells-num', len(game.s.reserves),
+ ]
+ #
+ if 'preset' in game_type:
+ args += ['--preset', game_type['preset']]
+ if 'sbb' in game_type:
+ args += ['--sequences-are-built-by', game_type['sbb']]
+ if 'sm' in game_type:
+ args += ['--sequence-move', game_type['sm']]
+ if 'esf' in game_type:
+ args += ['--empty-stacks-filled-by', game_type['esf']]
- command = fcs_command+' '+' '.join([str(i) for i in args])
- ##print command
- pin, pout, perr = os.popen3(command)
- pin.write(board)
- pin.close()
- #
- stack_types = {
- 'the' : self.game.s.foundations,
- 'stack' : self.game.s.rows,
- 'freecell' : self.game.s.reserves,
- }
- my_hints = []
- pboard_n = 0
- ##print pboard
+ command = FCS_COMMAND+' '+' '.join([str(i) for i in args])
+ ##print command
+ pin, pout, perr = os.popen3(command)
+ pin.write(board)
+ pin.close()
+ #
+ stack_types = {
+ 'the' : game.s.foundations,
+ 'stack' : game.s.rows,
+ 'freecell' : game.s.reserves,
+ }
+ ##start_time = time.time()
+ if progress:
+ # iteration output
+ iter = 0
+ depth = 0
+ states = 0
for s in pout:
##print s,
- if not s.startswith('Move'):
- if s.startswith('Freecells:'):
- ss=s[10:]
- pboard['FC'] = [ss[i:i+4].strip() for i in range(0, len(ss), 4)]
- elif s.startswith(':'):
- pboard[pboard_n] = s.split()[1:]
- pboard_n += 1
- continue
+ if s.startswith('Iter'):
+ #print `s`
+ iter = int(s[10:-1])
+ elif s.startswith('Depth'):
+ #print s,
+ depth = int(s[6:-1])
+ elif s.startswith('Stor'):
+ #print s,
+ states = int(s[14:-1])
+ if iter % 100 == 0:
+ self.dialog.setText(iter=iter, depth=depth,
+ states=states)
+ elif s.startswith('-=-') or \
+ s.startswith('I co'): # "I could not solve this game."
+ break
+ self.dialog.setText(iter=iter, depth=depth, states=states)
- words = s.split()
- ncards = words[1]
- if ncards == 'a':
- ncards = 1
- else:
- ncards = int(ncards)
+ hints = []
+ for s in pout:
+ #print s,
+ # TODO:
+ # Total number of states checked is 6.
+ # This scan generated 6 states.
- sn = int(words[5])
+ if not s.startswith('Move'):
+ continue
+
+ words = s.split()
+ ncards = words[1]
+ if ncards == 'the':
+ # "Move the sequence on top of Stack 1 to the foundations"
+ # (Simple Simon)
+ ncards = 0
+ elif ncards == 'a':
+ ncards = 1
+ else:
+ ncards = int(ncards)
+
+ if ncards:
st = stack_types[words[4]]
- src = st[sn]
-
+ sn = int(words[5])
+ src = st[sn] # source stack
if words[7] == 'the':
# to foundation
- if words[4] == 'stack':
- # from rows
- card = pboard[sn][-1]
- else:
- # from reserves
- card = pboard['FC'][sn]
- suit = 'CSHD'.index(card[1])
- ##dest = self.game.s.foundations[suit]
dest = None
- for f in self.game.s.foundations:
- if f.cap.base_suit == suit:
- dest = f
- break
- assert dest is not None
-
else:
# to rows or reserves
dt = stack_types[words[7]]
dn = int(words[8])
dest = dt[dn]
+ else: # move sequence
+ ncards = 13
+ st = stack_types['stack']
+ sn = int(words[7])
+ src = st[sn]
+ dest = None
- my_hints.append([ncards, src, dest])
- ##print src, dest, ncards
+ hints.append([ncards, src, dest])
+ ##print src, dest, ncards
- pboard = {}
- pboard_n = 0
+ #
+ ##print 'time:', time.time()-start_time
+ ##print perr.read(),
- my_hints.reverse()
- hint = None
- for i in my_hints:
- hint = (999999, 0, i[0], i[1], i[2], None, hint)
- if hint:
- self.hints.append(hint)
- ##print self.hints
- if os.name == 'posix':
- os.wait()
+ self.hints = hints
+ self.hints.append(None) # XXX
+
+ ##print self.hints
+
+ pout.close()
+ perr.close()
+ if os.name == 'posix':
+ os.wait()
- def computeHints_mod(self):
- board = ""
- #
- b = ""
- for s in self.game.s.foundations:
- if s.cards:
- b = b + " " + self.str2(s.cards[-1])
- if b:
- board = board + "Founds:" + b + "\n"
- #
- b = ""
- for s in self.game.s.reserves:
- if s.cards:
- b = b + " " + self.str1(s.cards[-1])
- else:
- b = b + " -"
- if b:
- board = board + "FC:" + b + "\n"
- #
- for s in self.game.s.rows:
- b = ""
- for c in s.cards:
- b = b + self.str1(c) + " "
- board = board + b.strip() + "\n"
- #
- ##print board
- # solver = apply(FreecellSolver.FCSolver, self.fcs_args)
- solver = FreecellSolver.alloc()
- solver.config(["-l", "good-intentions"]);
- solver.config(["--decks-num", self.fcs_args[0],
- "--stacks-num", self.fcs_args[1],
- "--freecells-num", self.fcs_args[2],
- ])
+class FreeCellSolverWrapper:
- game_type = self.fcs_args[3]
- game_type_defaults = {'sbb' : 'alternate_color', 'sm' : 'limited', 'esf': 'all'}
- for k,v in game_type_defaults.items():
- if k not in game_type:
- game_type[k] = v
-
- solver.config(["--sequences-are-built-by", game_type['sbb'],
- "--sequence-move", game_type['sm'],
- "--empty-stacks-filled-by", game_type['esf'],
- ])
+ def __init__(self, **game_type):
+ self.game_type = game_type
- solver.limit_iterations(200000)
-
- h = solver.solve(board)
- if (h == "solved"):
- move = solver.get_next_move()
- hint = None
- founds_map = [2,0,3,1,6,4,7,5]
- my_hints = []
- while (move):
- print move
-
- if (move['type'] == "s2s"):
- ncards = move['num_cards']
- else:
- ncards = 1
-
- src_idx = move['src']
- if move['type'] in ("s2s", "s2found", "s2f"):
- src = self.game.s.rows[src_idx]
- else:
- src = self.game.s.reserves[src_idx]
-
- d_idx = move['dest']
- if move['type'] in ("s2s", "f2s"):
- dest = self.game.s.rows[d_idx]
- elif move['type'] in ("s2f", "f2f"):
- dest = self.game.s.reserves[d_idx]
- else:
- dest = self.game.s.foundations[founds_map[d_idx]]
-
- # hint = (999999, 0, ncards, src, dest, None, 0)
- my_hints.append([ncards, src, dest])
- # self.hints.append(hint)
- move = solver.get_next_move();
- hint = None
- my_hints.reverse()
- for i in my_hints:
- hint = (999999, 0, i[0], i[1], i[2], None, hint)
- self.hints.append(hint)
- #i = len(h)
- #assert i % 3 == 0
- #hint = None
- #map = self.game.s.foundations + self.game.s.rows + self.game.s.reserves
- #while i > 0:
- # i = i - 3
- # v = struct.unpack(">: application has been destroyed"
#root.destroy()
Tkinter._default_root = None
- #
+
+ # check FreeCell-Solver
+ settings.USE_FREECELL_SOLVER = False
+ if os.name == 'nt':
+ if sys.path[0] and not os.path.isdir(sys.path[0]): # i.e. library.zip
+ d = os.path.dirname(sys.path[0])
+ ##d = os.path.join(d, 'freecell-solver')
+ fcs_command = os.path.join('freecell-solver', 'fc-solve.exe')
+ ##fcs_command = '"%s"' % fcs_command # quote command
+ settings.FCS_COMMAND = fcs_command
+ f = os.path.join(d, 'freecell-solver', 'presetrc')
+ os.environ['FREECELL_SOLVER_PRESETRC'] = f
+ os.chdir(d) # for read presets
+ ##print >> file('/fcs.log', 'a'), d
+ ##print >> file('/fcs.log', 'a'), f
+ if os.name in ('posix', 'nt'):
+ try:
+ pin, pout, perr = os.popen3(settings.FCS_COMMAND+' --help')
+ if pout.readline().startswith('fc-solve'):
+ settings.USE_FREECELL_SOLVER = True
+ del pin, pout, perr
+ if os.name == 'posix':
+ os.wait() # kill zombi
+ except:
+ ##traceback.print_exc()
+ pass
+ os.environ['FREECELL_SOLVER_QUIET'] = '1'
+
+ # run app without games menus (more fast start)
if '--no-games-menu' in sys.argv:
sys.argv.remove('--no-games-menu')
settings.SELECT_GAME_MENU = False
diff --git a/pysollib/mfxutil.py b/pysollib/mfxutil.py
index 8a79eb56..8c61ec19 100644
--- a/pysollib/mfxutil.py
+++ b/pysollib/mfxutil.py
@@ -36,6 +36,7 @@
# imports
import os, time, types
+import webbrowser
try:
from cPickle import Pickler, Unpickler, UnpicklingError
@@ -319,10 +320,10 @@ def unpickle(filename):
def openURL(url):
try:
- import webbrowser
webbrowser.open(url)
- return 1
- except ImportError: # FIXME
+ except OSError: # raised on windows if link is unreadable
+ pass
+ except:
return 0
-
+ return 1
diff --git a/pysollib/move.py b/pysollib/move.py
index ad8f56f7..a5dd3bd4 100644
--- a/pysollib/move.py
+++ b/pysollib/move.py
@@ -79,26 +79,31 @@ class AMoveMove(AtomicMove):
self.shadow = shadow
# do the actual move
- def __doMove(self, game, ncards, from_stack, to_stack):
+ def _doMove(self, game, ncards, from_stack, to_stack):
if game.moves.state == game.S_PLAY:
assert to_stack.acceptsCards(from_stack, from_stack.cards[-ncards:])
+ frames = self.frames
+ if frames == -2 and game.moves.state not in (game.S_UNDO, game.S_REDO):
+ # don't use animation for drag-move
+ frames = 0
cards = from_stack.cards[-ncards:]
- if self.frames != 0:
+ if frames != 0:
+ from_stack.unshadeStack()
x, y = to_stack.getPositionForNextCard()
game.animatedMoveTo(from_stack, to_stack, cards, x, y,
- frames=self.frames, shadow=self.shadow)
+ frames=frames, shadow=self.shadow)
for i in range(ncards):
from_stack.removeCard()
for c in cards:
to_stack.addCard(c)
def redo(self, game):
- self.__doMove(game, self.ncards, game.allstacks[self.from_stack_id],
- game.allstacks[self.to_stack_id])
+ self._doMove(game, self.ncards, game.allstacks[self.from_stack_id],
+ game.allstacks[self.to_stack_id])
def undo(self, game):
- self.__doMove(game, self.ncards, game.allstacks[self.to_stack_id],
- game.allstacks[self.from_stack_id])
+ self._doMove(game, self.ncards, game.allstacks[self.to_stack_id],
+ game.allstacks[self.from_stack_id])
def cmpForRedo(self, other):
return (cmp(self.ncards, other.ncards) or
@@ -115,7 +120,26 @@ class AFlipMove(AtomicMove):
self.stack_id = stack.id
# do the actual move
- def __doMove(self, game, stack):
+ def _doMove(self, game, stack):
+ card = stack.cards[-1]
+ ##game.animatedFlip(stack)
+ if card.face_up:
+ card.showBack()
+ else:
+ card.showFace()
+
+ def redo(self, game):
+ self._doMove(game, game.allstacks[self.stack_id])
+
+ def undo(self, game):
+ self._doMove(game, game.allstacks[self.stack_id])
+
+ def cmpForRedo(self, other):
+ return cmp(self.stack_id, other.stack_id)
+
+# flip with animation
+class ASingleFlipMove(AFlipMove):
+ def _doMove(self, game, stack):
card = stack.cards[-1]
game.animatedFlip(stack)
if card.face_up:
@@ -123,14 +147,46 @@ class AFlipMove(AtomicMove):
else:
card.showFace()
+# flip and move one card
+class AFlipAndMoveMove(AtomicMove):
+
+ def __init__(self, from_stack, to_stack, frames):
+ assert from_stack is not to_stack
+ self.from_stack_id = from_stack.id
+ self.to_stack_id = to_stack.id
+ self.frames = frames
+
+ def _doMove(self, game, from_stack, to_stack):
+ if game.moves.state == game.S_PLAY:
+ assert to_stack.acceptsCards(from_stack, from_stack.cards[-1])
+ if self.frames == 0:
+ moved = True
+ else:
+ moved = game.animatedFlipAndMove(from_stack, to_stack, self.frames)
+ c = from_stack.cards[-1]
+ if c.face_up:
+ c.showBack()
+ else:
+ c.showFace()
+ if not moved:
+ cards = from_stack.cards[-1:]
+ x, y = to_stack.getPositionForNextCard()
+ game.animatedMoveTo(from_stack, to_stack, cards, x, y,
+ frames=self.frames, shadow=0)
+ c = from_stack.removeCard()
+ to_stack.addCard(c)
+
def redo(self, game):
- self.__doMove(game, game.allstacks[self.stack_id])
+ self._doMove(game, game.allstacks[self.from_stack_id],
+ game.allstacks[self.to_stack_id])
def undo(self, game):
- self.__doMove(game, game.allstacks[self.stack_id])
+ self._doMove(game, game.allstacks[self.to_stack_id],
+ game.allstacks[self.from_stack_id])
def cmpForRedo(self, other):
- return cmp(self.stack_id, other.stack_id)
+ return (cmp(self.from_stack_id, other.from_stack_id) or
+ cmp(self.to_stack_id, other.to_stack_id))
# /***********************************************************************
@@ -246,7 +302,7 @@ class NEW_ATurnStackMove(AtomicMove):
self.update_flags = update_flags
# do the actual turning move
- def __doMove(self, from_stack, to_stack, show_face):
+ def _doMove(self, from_stack, to_stack, show_face):
assert len(from_stack.cards) > 0
assert len(to_stack.cards) == 0
for card in from_stack.cards:
@@ -272,7 +328,7 @@ class NEW_ATurnStackMove(AtomicMove):
assert to_stack is game.s.talon
assert to_stack.round < to_stack.max_rounds or to_stack.max_rounds < 0
to_stack.round = to_stack.round + 1
- self.__doMove(from_stack, to_stack, 0)
+ self._doMove(from_stack, to_stack, 0)
def undo(self, game):
from_stack = game.allstacks[self.from_stack_id]
@@ -281,7 +337,7 @@ class NEW_ATurnStackMove(AtomicMove):
assert to_stack is game.s.talon
assert to_stack.round > 1
to_stack.round = to_stack.round - 1
- self.__doMove(to_stack, from_stack, 1)
+ self._doMove(to_stack, from_stack, 1)
def cmpForRedo(self, other):
return (cmp(self.from_stack_id, other.from_stack_id) or
@@ -300,7 +356,7 @@ class AUpdateStackMove(AtomicMove):
self.flags = flags
# do the actual move
- def __doMove(self, game, stack, undo):
+ def _doMove(self, game, stack, undo):
if self.flags & 64:
# model
stack.updateModel(undo, self.flags)
@@ -313,11 +369,11 @@ class AUpdateStackMove(AtomicMove):
def redo(self, game):
if (self.flags & 3) in (1, 3):
- self.__doMove(game, game.allstacks[self.stack_id], 0)
+ self._doMove(game, game.allstacks[self.stack_id], 0)
def undo(self, game):
if (self.flags & 3) in (2, 3):
- self.__doMove(game, game.allstacks[self.stack_id], 1)
+ self._doMove(game, game.allstacks[self.stack_id], 1)
def cmpForRedo(self, other):
return cmp(self.stack_id, other.stack_id) or cmp(self.flags, other.flags)
diff --git a/pysollib/pysolgtk/colorsdialog.py b/pysollib/pysolgtk/colorsdialog.py
index ac8e6818..99c55ef4 100644
--- a/pysollib/pysolgtk/colorsdialog.py
+++ b/pysollib/pysolgtk/colorsdialog.py
@@ -59,8 +59,6 @@ class ColorsDialog:
self._setColor(n, app.opt.colors[n])
button = self.widgets_tree.get_widget(n+'_button')
button.connect('clicked', self._changeColor, n)
- checkbutton = self.widgets_tree.get_widget('use_default_checkbutton')
- checkbutton.set_active(not app.opt.use_default_text_color)
self._translateLabels()
@@ -79,7 +77,6 @@ class ColorsDialog:
w = self.widgets_tree.get_widget(n+'_label')
c = w.get_data('user_data')
setattr(self, n+'_color', c)
- self.use_default_color = not checkbutton.get_active()
dialog.destroy()
@@ -125,9 +122,8 @@ class ColorsDialog:
'label51',
'label52',
'label53',
+ 'label79',
):
w = self.widgets_tree.get_widget(n)
w.set_text(gettext(w.get_text()))
- w = self.widgets_tree.get_widget('use_default_checkbutton')
- w.set_label(gettext(w.get_label()))
diff --git a/pysollib/pysolgtk/menubar.py b/pysollib/pysolgtk/menubar.py
index 04b5d2fd..51571d24 100644
--- a/pysollib/pysolgtk/menubar.py
+++ b/pysollib/pysolgtk/menubar.py
@@ -289,10 +289,11 @@ class PysolMenubar(PysolMenubarActions):
#
animations_entries = (
('animationnone', None, ltk2gtk('&None'), None, None, 0),
- ('animationfast', None, ltk2gtk('&Fast'), None, None, 1),
- ('animationtimer', None, ltk2gtk('&Timer based'), None, None, 2),
- ('animationslow', None, ltk2gtk('&Slow'), None, None, 3),
- ('animationveryslow', None, ltk2gtk('&Very slow'), None, None, 4),
+ ('animationveryfast', None, ltk2gtk('&Very fast'), None, None, 1),
+ ('animationfast', None, ltk2gtk('&Fast'), None, None, 2),
+ ('animationmedium', None, ltk2gtk('&Medium'), None, None, 3),
+ ('animationslow', None, ltk2gtk('&Slow'), None, None, 4),
+ ('animationveryslow', None, ltk2gtk('V&ery slow'), None, None, 5),
)
mouse_entries = (
('draganddrop', None, ltk2gtk('&Drag-and-Drop'), None, None, 0),
@@ -388,8 +389,9 @@ class PysolMenubar(PysolMenubarActions):
@@ -831,7 +833,6 @@ class PysolMenubar(PysolMenubarActions):
self._cancelDrag()
self.game.endGame(bookmark=1)
self.game.quitGame(bookmark=1)
- self.app.opt.games_geometry = {} # clear saved games geometry
def mOptToggle(self, w, opt_name, update_game):
diff --git a/pysollib/pysolgtk/selectgame.py b/pysollib/pysolgtk/selectgame.py
index 2ed8bf6a..7b05acbb 100644
--- a/pysollib/pysolgtk/selectgame.py
+++ b/pysollib/pysolgtk/selectgame.py
@@ -389,7 +389,7 @@ class SelectGameDialogWithPreview(MfxDialog):
destruct(self.preview_app)
self.preview_app = None
- def updatePreview(self, gameid, animations=5):
+ def updatePreview(self, gameid, animations=10):
if gameid == self.preview_key:
return
self.deletePreview()
@@ -456,7 +456,7 @@ class SelectGameDialogWithPreview(MfxDialog):
#
self.preview_app.audio = self.app.audio
if self.app.opt.animations:
- self.preview_app.opt.animations = 5
+ self.preview_app.opt.animations = 10
else:
self.preview_app.opt.animations = 0
# save seed
@@ -514,14 +514,13 @@ class SelectGameDialogWithPreview(MfxDialog):
('percent', percent),
):
title_label, text_label = self.info_labels[n]
- if t == '':
+ if t in ('', None):
title_label.hide()
text_label.hide()
else:
title_label.show()
text_label.show()
text_label.set_text(str(t))
- #self.info_labels[n].config(text=t)
def done(self, button):
button = button.get_data("user_data")
diff --git a/pysollib/pysolgtk/solverdialog.py b/pysollib/pysolgtk/solverdialog.py
new file mode 100644
index 00000000..cd6831fd
--- /dev/null
+++ b/pysollib/pysolgtk/solverdialog.py
@@ -0,0 +1,42 @@
+##---------------------------------------------------------------------------##
+##
+## PySol -- a Python Solitaire game
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; see the file COPYING.
+## If not, write to the Free Software Foundation, Inc.,
+## 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+##
+##---------------------------------------------------------------------------##
+
+__all__ = [
+ #'SolverDialog',
+ 'create_solver_dialog',
+ 'connect_game_solver_dialog',
+ 'destroy_solver_dialog',
+ 'reset_solver_dialog',
+ ]
+
+
+solver_dialog = None
+
+def create_solver_dialog(parent, game):
+ pass
+def connect_game_solver_dialog(game):
+ pass
+def destroy_solver_dialog():
+ pass
+ solver_dialog = None
+def reset_solver_dialog():
+ pass
+
diff --git a/pysollib/pysolgtk/tkcanvas.py b/pysollib/pysolgtk/tkcanvas.py
index addf130f..6e505d7c 100644
--- a/pysollib/pysolgtk/tkcanvas.py
+++ b/pysollib/pysolgtk/tkcanvas.py
@@ -445,16 +445,11 @@ class MfxCanvas(gnomecanvas.Canvas):
self.setBackgroundImage(None)
self.configure(bg=tile.color)
##app.top.config(bg=tile.color)
- color = None
else:
self._setTile()
self.configure(bg=self.top_bg)
- color = tile.text_color
- if app.opt.use_default_text_color:
- self.setTextColor(color)
- else:
- self.setTextColor(app.opt.colors['text'])
+ self.setTextColor(app.opt.colors['text'])
return True
diff --git a/pysollib/pysolgtk/tkstats.py b/pysollib/pysolgtk/tkstats.py
index e4664176..be0c2f19 100644
--- a/pysollib/pysolgtk/tkstats.py
+++ b/pysollib/pysolgtk/tkstats.py
@@ -566,7 +566,9 @@ class Status_StatsDialog(MfxMessageDialog): #MfxDialog
)
-
+class ProgressionDialog:
+ # FIXME
+ pass
diff --git a/pysollib/pysoltk.py b/pysollib/pysoltk.py
index 62454e95..e4c75e44 100644
--- a/pysollib/pysoltk.py
+++ b/pysollib/pysoltk.py
@@ -37,6 +37,7 @@ if TOOLKIT == 'tk':
from tile.colorsdialog import *
from tile.fontsdialog import *
from tile.findcarddialog import *
+ from tile.solverdialog import *
from tile.gameinfodialog import *
from tile.toolbar import *
from tile.statusbar import *
@@ -60,6 +61,7 @@ if TOOLKIT == 'tk':
from tk.colorsdialog import *
from tk.fontsdialog import *
from tk.findcarddialog import *
+ from tk.solverdialog import *
from tk.gameinfodialog import *
from tk.toolbar import *
from tk.statusbar import *
@@ -84,6 +86,7 @@ else: # gtk
from pysolgtk.colorsdialog import *
from pysolgtk.fontsdialog import *
from pysolgtk.findcarddialog import *
+ from pysolgtk.solverdialog import *
from pysolgtk.gameinfodialog import *
from pysolgtk.toolbar import *
from pysolgtk.statusbar import *
diff --git a/pysollib/resource.py b/pysollib/resource.py
index 335bb7b2..5a158587 100644
--- a/pysollib/resource.py
+++ b/pysollib/resource.py
@@ -497,7 +497,6 @@ class CardsetManager(ResourceManager):
class Tile(Resource):
def __init__(self, **kw):
kw['color'] = None
- kw['text_color'] = "#000000"
kw['stretch'] = 0
Resource.__init__(self, **kw)
diff --git a/pysollib/settings.py b/pysollib/settings.py
index ab52d525..37ef5b28 100644
--- a/pysollib/settings.py
+++ b/pysollib/settings.py
@@ -29,7 +29,7 @@ PACKAGE = 'PySol'
PACKAGE_URL = 'http://sourceforge.net/projects/pysolfc/'
VERSION = '4.82'
-FC_VERSION = '0.9.5'
+FC_VERSION = '1.0'
VERSION_TUPLE = (4, 82)
# Tk windowing system (auto determine in init.py)
@@ -48,6 +48,11 @@ USE_TILE = 'auto' # or True or False
# 'none' - disable
SOUND_MOD = 'auto'
+# freecell-solver
+USE_FREECELL_SOLVER = True
+FCS_COMMAND = 'fc-solve'
+##FCS_HOME = None # path to fcs presets files
+
# data dirs
DATA_DIRS = []
# you can add your extra directories here
diff --git a/pysollib/stack.py b/pysollib/stack.py
index 13216c0c..237f4328 100644
--- a/pysollib/stack.py
+++ b/pysollib/stack.py
@@ -57,7 +57,7 @@ __all__ = ['cardsFaceUp',
'SS_FoundationStack',
'RK_FoundationStack',
'AC_FoundationStack',
- 'SequenceStack_StackMethods',
+ #'SequenceStack_StackMethods',
'BasicRowStack',
'SequenceRowStack',
'AC_RowStack',
@@ -71,6 +71,7 @@ __all__ = ['cardsFaceUp',
'UD_RK_RowStack',
'FreeCell_AC_RowStack',
'FreeCell_SS_RowStack',
+ 'FreeCell_RK_RowStack',
'Spider_AC_RowStack',
'Spider_SS_RowStack',
'Yukon_AC_RowStack',
@@ -83,6 +84,10 @@ __all__ = ['cardsFaceUp',
'FaceUpWasteTalonStack',
'OpenTalonStack',
'ReserveStack',
+ 'SuperMoveStack_StackMethods',
+ 'SuperMoveSS_RowStack',
+ 'SuperMoveAC_RowStack',
+ 'SuperMoveRK_RowStack',
'InvisibleStack',
'StackWrapper',
'WeakStackWrapper',
@@ -342,6 +347,7 @@ class Stack:
bind(group, "<3>", self.__rightclickEventHandler)
bind(group, "<2>", self.__middleclickEventHandler)
bind(group, "", self.__middleclickEventHandler)
+ ##bind(group, "", self.__controlmiddleclickEventHandler)
##bind(group, "", self.__shiftrightclickEventHandler)
##bind(group, "", "")
bind(group, "", self.__enterEventHandler)
@@ -489,9 +495,8 @@ class Stack:
if update:
view.updateText()
- if self.is_filled:
- self._unshadeStack()
- self.is_filled = False
+ self.unshadeStack()
+ self.is_filled = False
return card
# Get the top card {model}
@@ -652,9 +657,12 @@ class Stack:
# Atomic move actions {model -> view}
#
- def flipMove(self):
+ def flipMove(self, animation=False):
# Flip the top card.
- self.game.flipMove(self)
+ if animation:
+ self.game.singleFlipMove(self)
+ else:
+ self.game.flipMove(self)
def moveMove(self, ncards, to_stack, frames=-1, shadow=-1):
# Move the top n cards.
@@ -671,10 +679,10 @@ class Stack:
# Playing move actions. Better not override.
#
- def playFlipMove(self, sound=1):
+ def playFlipMove(self, sound=1, animation=False):
if sound:
self.game.playSample("flip", 5)
- self.flipMove()
+ self.flipMove(animation=animation)
if not self.game.checkForWin():
self.game.autoPlay()
self.game.finishMove()
@@ -917,6 +925,32 @@ class Stack:
self.game.canvas.update_idletasks()
return 1
+ def controlmiddleclickHandler(self, event):
+ # cheating: show face-down card
+ if not self.is_open:
+ return 0
+ i = self._findCard(event)
+ positions = len(self.cards) - i - 1
+ if i < 0 or positions < 0:
+ return 0
+ ##print self.cards[i]
+ face_up = self.cards[i].face_up
+ if not face_up:
+ self.cards[i].showFace()
+ self.cards[i].item.tkraise()
+ self.game.canvas.update_idletasks()
+ self.game.sleep(self.game.app.opt.timeouts['raise_card'])
+ if not face_up:
+ self.cards[i].showBack()
+ if TOOLKIT == 'tk':
+ if positions > 0:
+ self.cards[i].item.lower(self.cards[i+1].item)
+ elif TOOLKIT == 'gtk':
+ for c in self.cards[i+1:]:
+ c.tkraise()
+ self.game.canvas.update_idletasks()
+ return 1
+
def rightclickHandler(self, event):
return 0
@@ -946,8 +980,25 @@ class Stack:
self.moveCardsBackHandler(event, drag)
def moveCardsBackHandler(self, event, drag):
+ if self.game.app.opt.animations:
+ if drag.cards:
+ c = drag.cards[0]
+ x0, y0 = drag.stack.getPositionFor(c)
+ x1, y1 = c.x, c.y
+ dx, dy = abs(x0-x1), abs(y0-y1)
+ w = self.game.app.images.CARDW
+ h = self.game.app.images.CARDH
+ if dx > 2*w or dy > 2*h:
+ self.game.animatedMoveTo(drag.stack, drag.stack,
+ drag.cards, x0, y0, frames=-1)
+ elif dx > w or dy > h:
+ self.game.animatedMoveTo(drag.stack, drag.stack,
+ drag.cards, x0, y0, frames=4)
for card in drag.cards:
self._position(card)
+ if self.is_filled and self.items.shade_item:
+ self.items.shade_item.show()
+ self.items.shade_item.tkraise()
#
@@ -993,6 +1044,9 @@ class Stack:
def __middleclickEventHandler(self, event):
return self.__defaultClickEventHandler(event, self.middleclickHandler)
+ def __controlmiddleclickEventHandler(self, event):
+ return self.__defaultClickEventHandler(event, self.controlmiddleclickHandler)
+
def __rightclickEventHandler(self, event):
return self.__defaultClickEventHandler(event, self.rightclickHandler)
@@ -1049,8 +1103,11 @@ class Stack:
self.current_cursor = CURSOR_DOWN_ARROW
self.cursor_changed = True
else:
+ help = self.getHelp() ##+' '+self.getBaseCard(),
+ if DEBUG >= 5:
+ help = repr(self)
after_idle(self.canvas, self.game.showHelp,
- 'help', self.getHelp(), ##+' '+self.getBaseCard(),
+ 'help', help,
'info', self.getNumCards())
return EVENT_HANDLED
@@ -1173,7 +1230,7 @@ class Stack:
images = self.game.app.images
cx, cy = cards[0].x, cards[0].y
ddx, ddy = cx-cards[-1].x, cy-cards[-1].y
- if TOOLKIT == 'tk' and Image: # use PIL
+ if 0 and TOOLKIT == 'tk' and Image: # use PIL
c0 = cards[-1]
if self.CARD_XOFFSET[0] < 0: c0 = cards[0]
if self.CARD_YOFFSET[0] < 0: c0 = cards[0]
@@ -1303,7 +1360,7 @@ class Stack:
#item.tkraise()
self.items.shade_item = item
- def _unshadeStack(self):
+ def unshadeStack(self):
if self.items.shade_item:
self.items.shade_item.delete()
self.items.shade_item = None
@@ -1350,19 +1407,19 @@ class Stack:
group=self.group)
drag.shadows.append(im)
- def _setMotionCursor(self, event):
- if not event:
- return
- self.cursor_changed = True
- i = self._findCard(event)
- if i < 0 or not self.canMoveCards(self.cards[i:]):
- if self.current_cursor != CURSOR_NO_MOVE:
- self.game.canvas.config(cursor=CURSOR_NO_MOVE)
- self.current_cursor = CURSOR_NO_MOVE
- else:
- if self.current_cursor != CURSOR_CAN_MOVE:
- self.game.canvas.config(cursor=CURSOR_CAN_MOVE)
- self.current_cursor = CURSOR_CAN_MOVE
+## def _setMotionCursor(self, event):
+## if not event:
+## return
+## self.cursor_changed = True
+## i = self._findCard(event)
+## if i < 0 or not self.canMoveCards(self.cards[i:]):
+## if self.current_cursor != CURSOR_NO_MOVE:
+## self.game.canvas.config(cursor=CURSOR_NO_MOVE)
+## self.current_cursor = CURSOR_NO_MOVE
+## else:
+## if self.current_cursor != CURSOR_CAN_MOVE:
+## self.game.canvas.config(cursor=CURSOR_CAN_MOVE)
+## self.current_cursor = CURSOR_CAN_MOVE
def _stopDrag(self):
drag = self.game.drag
@@ -1376,16 +1433,16 @@ class Stack:
drag.shadows = []
drag.stack = None
drag.cards = []
- if self.is_filled and self.items.shade_item:
- self.items.shade_item.show()
- self.items.shade_item.tkraise()
# finish a drag operation
def finishDrag(self, event=None):
if self.game.app.opt.dragcursor:
self.game.canvas.config(cursor='')
drag = self.game.drag.copy()
- self._stopDrag()
+ if self.game.app.opt.mouse_type == 'point-n-click':
+ drag.stack._stopDrag()
+ else:
+ self._stopDrag()
if drag.cards:
if self.game.app.opt.mouse_type == 'point-n-click':
self.releaseHandler(event, drag)
@@ -1398,7 +1455,10 @@ class Stack:
if self.game.app.opt.dragcursor:
self.game.canvas.config(cursor='')
drag = self.game.drag.copy()
- self._stopDrag()
+ if self.game.app.opt.mouse_type == 'point-n-click':
+ drag.stack._stopDrag()
+ else:
+ self._stopDrag()
if drag.cards:
assert drag.stack is self
self.moveCardsBackHandler(event, drag)
@@ -1410,6 +1470,7 @@ class Stack:
return ''
def _getBaseCard(self):
+ # FIXME: no-french games
if self.cap.max_accept == 0:
return ''
br = self.cap.base_rank
@@ -1424,14 +1485,10 @@ class Stack:
return s
def getNumCards(self):
- if DEBUG >= 5:
- t = repr(self)+' '
- else:
- t = ''
n = len(self.cards)
- if n == 0 : return t+_('No cards')
- elif n == 1 : return t+_('1 card')
- else : return t+str(n)+_(' cards')
+ if n == 0 : return _('No cards')
+ elif n == 1 : return _('1 card')
+ else : return str(n)+_(' cards')
# /***********************************************************************
@@ -1831,8 +1888,9 @@ class OpenStack(Stack):
def clickHandler(self, event):
flipstacks, dropstacks, quickstacks = self.game.getAutoStacks(event)
if self in flipstacks and self.canFlipCard():
- self.playFlipMove()
- return -1 # continue this event (start a drag)
+ self.playFlipMove(animation=True)
+ ##return -1 # continue this event (start a drag)
+ return 1 # break
return 0
def rightclickHandler(self, event):
@@ -1850,7 +1908,7 @@ class OpenStack(Stack):
# flip or drop a card
flipstacks, dropstacks, quickstacks = self.game.getAutoStacks(event)
if self in flipstacks and self.canFlipCard():
- self.playFlipMove()
+ self.playFlipMove(animation=True)
return -1 # continue this event (start a drag)
if self in dropstacks:
to_stack, ncards = self.canDropCards(self.game.s.foundations)
@@ -1870,7 +1928,8 @@ class OpenStack(Stack):
if self.game.app.opt.mouse_type == 'point-n-click':
self.playMoveMove(len(drag.cards), stack, sound=sound)
else:
- self.playMoveMove(len(drag.cards), stack, frames=0, sound=sound)
+ #self.playMoveMove(len(drag.cards), stack, frames=0, sound=sound)
+ self.playMoveMove(len(drag.cards), stack, frames=-2, sound=sound)
def releaseHandler(self, event, drag, sound=1):
cards = drag.cards
@@ -2168,6 +2227,12 @@ class FreeCell_SS_RowStack(SS_RowStack):
max_move = getNumberOfFreeStacks(self.game.s.reserves) + 1
return len(cards) <= max_move and SS_RowStack.canMoveCards(self, cards)
+# A Freecell_Rank_RowStack
+class FreeCell_RK_RowStack(RK_RowStack):
+ def canMoveCards(self, cards):
+ max_move = getNumberOfFreeStacks(self.game.s.reserves) + 1
+ return len(cards) <= max_move and RK_RowStack.canMoveCards(self, cards)
+
# A Spider_AlternateColor_RowStack builds down by rank and alternate color,
# but accepts sequences that match by rank only.
class Spider_AC_RowStack(AC_RowStack):
@@ -2291,6 +2356,73 @@ class UD_RK_RowStack(SequenceRowStack):
+# To simplify playing we also consider the number of free rows.
+# Note that this only is legal if the game.s.rows have a
+# cap.base_rank == ANY_RANK.
+# See also the "SuperMove" section in the FreeCell FAQ.
+class SuperMoveStack_StackMethods:
+ def _getMaxMove(self, to_stack_ncards):
+ max_move = getNumberOfFreeStacks(self.game.s.reserves) + 1
+ if self.cap.base_rank != ANY_RANK:
+ return max_move
+ n = getNumberOfFreeStacks(self.game.s.rows)
+ if to_stack_ncards == 0:
+ n = n - 1
+ max_move = max_move * (2 ** n)
+ return max_move
+ def _getNumSSSeq(self, cards):
+ # num of same-suit sequences (for SuperMoveSpider_RowStack)
+ if not cards:
+ return 0
+ mod = self.cap.mod
+ dir = self.cap.dir
+ n = 1
+ rank = cards[-1].rank
+ suit = cards[-1].suit
+ for c in cards[-2::-1]:
+ if c.suit != suit:
+ suit = c.suit
+ n += 1
+ return n
+
+
+class SuperMoveSS_RowStack(SuperMoveStack_StackMethods, SS_RowStack):
+ def canMoveCards(self, cards):
+ if not SS_RowStack.canMoveCards(self, cards):
+ return False
+ max_move = self._getMaxMove(1)
+ return len(cards) <= max_move
+ def acceptsCards(self, from_stack, cards):
+ if not SS_RowStack.acceptsCards(self, from_stack, cards):
+ return False
+ max_move = self._getMaxMove(len(self.cards))
+ return len(cards) <= max_move
+
+class SuperMoveAC_RowStack(SuperMoveStack_StackMethods, AC_RowStack):
+ def canMoveCards(self, cards):
+ if not AC_RowStack.canMoveCards(self, cards):
+ return False
+ max_move = self._getMaxMove(1)
+ return len(cards) <= max_move
+ def acceptsCards(self, from_stack, cards):
+ if not AC_RowStack.acceptsCards(self, from_stack, cards):
+ return False
+ max_move = self._getMaxMove(len(self.cards))
+ return len(cards) <= max_move
+
+class SuperMoveRK_RowStack(SuperMoveStack_StackMethods, RK_RowStack):
+ def canMoveCards(self, cards):
+ if not RK_RowStack.canMoveCards(self, cards):
+ return False
+ max_move = self._getMaxMove(1)
+ return len(cards) <= max_move
+ def acceptsCards(self, from_stack, cards):
+ if not RK_RowStack.acceptsCards(self, from_stack, cards):
+ return False
+ max_move = self._getMaxMove(len(self.cards))
+ return len(cards) <= max_move
+
+
# /***********************************************************************
# // WasteStack (a helper stack for the Talon, e.g. in Klondike)
@@ -2334,8 +2466,13 @@ class WasteTalonStack(TalonStack):
assert len(waste.cards) + num_cards <= waste.cap.max_cards
for i in range(num_cards):
if not self.cards[-1].face_up:
- self.game.flipMove(self)
- self.game.moveMove(1, self, waste, frames=4, shadow=0)
+ if 1:
+ self.game.flipAndMoveMove(self, waste)
+ else:
+ self.game.flipMove(self)
+ self.game.moveMove(1, self, waste, frames=4, shadow=0)
+ else:
+ self.game.moveMove(1, self, waste, frames=4, shadow=0)
self.fillStack()
elif waste.cards and self.round != self.max_rounds:
if sound:
@@ -2355,6 +2492,11 @@ class FaceUpWasteTalonStack(WasteTalonStack):
self.game.flipMove(self)
self.game.fillStack(self)
+ def dealCards(self, sound=0):
+ WasteTalonStack.dealCards(self, sound=sound)
+ if self.canFlipCard():
+ self.flipMove()
+
class OpenTalonStack(TalonStack, OpenStack):
canMoveCards = OpenStack.canMoveCards
@@ -2460,7 +2602,7 @@ class ArbitraryStack(OpenStack):
# flip or drop a card
flipstacks, dropstacks, quickstacks = self.game.getAutoStacks(event)
if self in flipstacks and self.canFlipCard():
- self.playFlipMove()
+ self.playFlipMove(animation=True)
return -1 # continue this event (start a drag)
if self in dropstacks:
i = self._findCard(event)
diff --git a/pysollib/stats.py b/pysollib/stats.py
index a01066d4..4ad7bed5 100644
--- a/pysollib/stats.py
+++ b/pysollib/stats.py
@@ -243,3 +243,126 @@ class FileStatsFormatter(PysolStatsFormatter):
prev_games = self.app.stats.session_games.get(player)
return self.writeLog(player, header, prev_games)
+
+# /***********************************************************************
+# //
+# ************************************************************************/
+
+class ProgressionFormatter:
+
+ def __init__(self, app, player, gameid):
+
+ all_results = {} # key: (year, month, day); value: [played, won]
+ self.all_results = all_results
+ game_results = {}
+ self.game_results = game_results
+ games = app.stats.prev_games.get(player)
+ if not games:
+ return
+ for g in games:
+ id = g[0]
+ status = g[2]
+ start_time = g[3]
+ t = time.localtime(start_time)[:3]
+ if t not in all_results:
+ all_results[t] = [0,0]
+ all_results[t][0] += 1
+ if status > 0:
+ all_results[t][1] += 1
+ if id == gameid:
+ if t not in game_results:
+ game_results[t] = [0,0]
+ game_results[t][0] += 1
+ if status > 0:
+ game_results[t][1] += 1
+ ##from pprint import pprint; pprint(all_results)
+
+ def norm_time(self, t):
+ if len(t) == 3:
+ t = list(t)+[0,0,0,-1,-1,-1]
+ return list(time.localtime(time.mktime((t))))
+
+ def getResults(self, interval, all_games=True):
+ if all_games:
+ results = self.all_results
+ else:
+ results = self.game_results
+ t = list(time.localtime())
+ if interval == 'week':
+ t[2] -= 7
+ lt = self.norm_time(t)
+ marks = None
+ delta = 1
+ format = '%d.%m'
+ elif interval == 'month':
+ tt = t[:]
+ t[1] -= 1
+ lt = self.norm_time(t)
+ marks = [lt[:3], tt[:3]]
+ tt[2] -= 10
+ marks.append(self.norm_time(tt)[:3])
+ tt[2] -= 10
+ marks.append(self.norm_time(tt)[:3])
+ delta = 1
+ format = '%d.%m'
+ elif interval == 'year':
+ tt = t[:]
+ t[0] -= 1
+ lt = self.norm_time(t)
+ marks = [lt[:3], tt[:3]]
+ for i in xrange(5):
+ tt[1] -= 2
+ marks.append(self.norm_time(tt)[:3])
+ delta = 7
+ format = '%d.%m.%y'
+ else: # all
+ tt = t[:]
+ tt[1] -= 1
+ tt = self.norm_time(tt)
+ if results:
+ lt = self.norm_time(min(results.keys()))
+ lt = min(lt, tt) # min 1 month
+ else:
+ lt = tt
+ dt = time.time()-time.mktime(lt)
+ if dt > 63072000: # 2 years
+ d = 6
+ elif dt > 31536000: # 1 year
+ d = 4
+ elif dt > 10512000: # 4 month
+ d = 2
+ else:
+ d = 1
+ marks = [lt[:3], t[:3]]
+ while t > lt:
+ t[1] -= d
+ t = self.norm_time(t)
+ marks.append(t[:3])
+ delta = 7
+ format = '%d.%m.%y'
+
+ res = []
+ ct = list(time.localtime())
+ while lt <= ct:
+ ##assert type(lt) is type(ct)
+ sum = [0,0]
+ played = 0
+ won = 0
+ text = None
+ for i in xrange(delta):
+ if marks:
+ if ct[:3] in marks:
+ text = time.strftime(format, ct)
+ else:
+ text = time.strftime(format, ct)
+ t = tuple(ct[:3])
+ if t in results:
+ played += results[t][0]
+ won += results[t][1]
+ ct[2] -= 1
+ ct = self.norm_time(ct)
+ res.append((text, played, won))
+ res.reverse()
+ ##from pprint import pprint; pprint(res)
+ return res
+
diff --git a/pysollib/tile/Tile.py b/pysollib/tile/Tile.py
index 521ebd54..72bda8dc 100644
--- a/pysollib/tile/Tile.py
+++ b/pysollib/tile/Tile.py
@@ -17,6 +17,7 @@ Message = Tkinter.Message
Listbox = Tkinter.Listbox
Text = Tkinter.Text
Canvas = Tkinter.Canvas
+Spinbox = Tkinter.Spinbox
PhotoImage = Tkinter.PhotoImage
Event = Tkinter.Event
diff --git a/pysollib/tile/colorsdialog.py b/pysollib/tile/colorsdialog.py
index 8714ee2d..1298c26c 100644
--- a/pysollib/tile/colorsdialog.py
+++ b/pysollib/tile/colorsdialog.py
@@ -49,8 +49,6 @@ class ColorsDialog(MfxDialog):
frame.pack(expand=True, fill='both', padx=5, pady=10)
frame.columnconfigure(0, weight=1)
- self.use_default_var = Tkinter.BooleanVar()
- self.use_default_var.set(not app.opt.use_default_text_color)
self.text_var = Tkinter.StringVar()
self.text_var.set(app.opt.colors['text'])
self.piles_var = Tkinter.StringVar()
@@ -68,17 +66,9 @@ class ColorsDialog(MfxDialog):
self.not_matching_var = Tkinter.StringVar()
self.not_matching_var.set(app.opt.colors['not_matching'])
#
- c = Tkinter.Checkbutton(frame, variable=self.use_default_var,
- text=_("Text foreground:"))
- c.grid(row=0, column=0, sticky='we')
- l = Tk.Label(frame, width=10, height=2,
- bg=self.text_var.get(), textvariable=self.text_var)
- l.grid(row=0, column=1, padx=5)
- b = Tkinter.Button(frame, text=_('Change...'), width=10,
- command=lambda l=l: self.selectColor(l))
- b.grid(row=0, column=2)
- row = 1
+ row = 0
for title, var in (
+ (_('Text foreground:'), self.text_var),
(_('Highlight piles:'), self.piles_var),
(_('Highlight cards 1:'), self.cards_1_var),
(_('Highlight cards 2:'), self.cards_2_var),
@@ -100,7 +90,6 @@ class ColorsDialog(MfxDialog):
focus = self.createButtons(bottom_frame, kw)
self.mainloop(focus, kw.timeout)
#
- self.use_default_color = not self.use_default_var.get()
self.text_color = self.text_var.get()
self.piles_color = self.piles_var.get()
self.cards_1_color = self.cards_1_var.get()
diff --git a/pysollib/tile/findcarddialog.py b/pysollib/tile/findcarddialog.py
index b8430128..ca41191b 100644
--- a/pysollib/tile/findcarddialog.py
+++ b/pysollib/tile/findcarddialog.py
@@ -114,6 +114,7 @@ class FindCardDialog(Tkinter.Toplevel):
self.groups.append(group)
def connectGame(self, game):
+ self.canvas.delete('all')
self.game = game
suits = game.gameinfo.suits
ranks = game.gameinfo.ranks
@@ -133,6 +134,7 @@ class FindCardDialog(Tkinter.Toplevel):
w, h = dx*j+2, dy*i+2
self.canvas.config(width=w, height=h)
self.wm_iconname(PACKAGE + " - " + game.getTitleName())
+ self.wm_geometry('') # cancel user-specified geometry
def enterEvent(self, suit, rank, rect, group):
##print 'enterEvent', suit, rank, self.busy
diff --git a/pysollib/tile/menubar.py b/pysollib/tile/menubar.py
index 29887ade..899929dd 100644
--- a/pysollib/tile/menubar.py
+++ b/pysollib/tile/menubar.py
@@ -48,6 +48,7 @@ from pysollib.util import CARDSET
from pysollib.settings import PACKAGE, WIN_SYSTEM
from pysollib.settings import TOP_TITLE
from pysollib.settings import SELECT_GAME_MENU
+from pysollib.settings import USE_FREECELL_SOLVER
from pysollib.gamedb import GI
from pysollib.actions import PysolMenubarActions
@@ -59,6 +60,7 @@ from soundoptionsdialog import SoundOptionsDialog
from selectcardset import SelectCardsetDialogWithPreview
from selecttile import SelectTileDialogWithPreview
from findcarddialog import connect_game_find_card_dialog, destroy_find_card_dialog
+from solverdialog import connect_game_solver_dialog
from tkwrap import MfxRadioMenuItem, MfxCheckMenuItem, StringVar
from tkwidget import MfxMessageDialog
@@ -139,25 +141,18 @@ class MfxMenubar(Tkinter.Menu):
#print label, type(label)
name = re.sub(r"[^0-9a-zA-Z]", "", label).lower()
label = gettext(label)
- underline = -1
- m = re.search(r"^(.*)\&([^\&].*)$", label)
- if m:
- l1, l2 = m.group(1), m.group(2)
- l1 = re.sub(r"\&\&", "&", l1)
- l2 = re.sub(r"\&\&", "&", l2)
- label = l1 + l2
- underline = len(l1)
+ underline = label.find('&')
+ if underline >= 0:
+ label = label.replace('&', '')
return name, label, underline
def add(self, itemType, cnf={}):
label = cnf.get("label")
if label:
name = cnf.get('name')
- try:
+ if name:
del cnf['name'] # TclError: unknown option "-name"
- except KeyError:
- pass
- if not name:
+ else:
name, label, underline = self.labeltoname(label)
cnf["underline"] = cnf.get("underline", underline)
cnf["label"] = label
@@ -323,6 +318,7 @@ class PysolMenubar(PysolMenubarActions):
connect_game_find_card_dialog(game)
else:
destroy_find_card_dialog()
+ connect_game_solver_dialog(game)
# create a GTK-like path
def _addPath(self, path, menu, index, submenu):
@@ -371,7 +367,7 @@ class PysolMenubar(PysolMenubarActions):
menu.add_separator()
submenu = MfxMenu(menu, label=n_("Fa&vorite games"))
menu.add_command(label=n_("A&dd to favorites"), command=self.mAddFavor)
- menu.add_command(label=n_("R&emove from favorites"), command=self.mDelFavor)
+ menu.add_command(label=n_("Remove &from favorites"), command=self.mDelFavor)
menu.add_separator()
menu.add_command(label=n_("&Open..."), command=self.mOpen, accelerator=m+"O")
menu.add_command(label=n_("&Save"), command=self.mSave, accelerator=m+"S")
@@ -414,7 +410,7 @@ class PysolMenubar(PysolMenubarActions):
menu.add_checkbutton(label=n_("&Pause"), variable=self.tkopt.pause, command=self.mPause, accelerator="P")
#menu.add_command(label=n_("&Pause"), command=self.mPause, accelerator="P")
menu.add_separator()
- menu.add_command(label=n_("S&tatus..."), command=self.mStatus, accelerator="T")
+ menu.add_command(label=n_("S&tatus..."), command=self.mStatus, accelerator=m+"Y")
menu.add_checkbutton(label=n_("&Comments..."), variable=self.tkopt.comment, command=self.mEditGameComment)
menu.add_separator()
submenu = MfxMenu(menu, label=n_("&Statistics"))
@@ -425,6 +421,7 @@ class PysolMenubar(PysolMenubarActions):
submenu.add_command(label=n_("Full log..."), command=lambda self=self: self.mPlayerStats(mode=103))
submenu.add_separator()
submenu.add_command(label=TOP_TITLE+"...", command=self.mTop10, accelerator=m+"T")
+ submenu.add_command(label=n_("Progression..."), command=lambda self=self: self.mPlayerStats(mode=107))
submenu = MfxMenu(menu, label=n_("D&emo statistics"))
submenu.add_command(label=n_("Current game..."), command=lambda self=self: self.mPlayerStats(mode=1101))
submenu.add_command(label=n_("All games..."), command=lambda self=self: self.mPlayerStats(mode=1102))
@@ -432,12 +429,16 @@ class PysolMenubar(PysolMenubarActions):
menu = MfxMenu(self.__menubar, label=n_("&Assist"))
menu.add_command(label=n_("&Hint"), command=self.mHint, accelerator="H")
menu.add_command(label=n_("Highlight p&iles"), command=self.mHighlightPiles, accelerator="I")
- menu.add_command(label=n_("Find card"), command=self.mFindCard, accelerator="F")
+ menu.add_command(label=n_("&Find card"), command=self.mFindCard, accelerator="F3")
menu.add_separator()
menu.add_command(label=n_("&Demo"), command=self.mDemo, accelerator=m+"D")
menu.add_command(label=n_("Demo (&all games)"), command=self.mMixedDemo)
+ if USE_FREECELL_SOLVER:
+ menu.add_command(label=n_("&Solver (experimental)"), command=self.mSolver)
+ else:
+ menu.add_command(label=n_("&Solver (experimental)"), command=self.mSolver, state=Tkinter.DISABLED)
menu.add_separator()
- menu.add_command(label=n_("Piles description"), command=self.mStackDesk, accelerator="F2")
+ menu.add_command(label=n_("&Piles description"), command=self.mStackDesk, accelerator="F2")
if self.progress: self.progress.update(step=1)
@@ -481,10 +482,11 @@ class PysolMenubar(PysolMenubarActions):
submenu.add_checkbutton(label=n_("Shade &filled stacks"), variable=self.tkopt.shade_filled_stacks, command=self.mOptShadeFilledStacks)
submenu = MfxMenu(menu, label=n_("A&nimations"))
submenu.add_radiobutton(label=n_("&None"), variable=self.tkopt.animations, value=0, command=self.mOptAnimations)
- submenu.add_radiobutton(label=n_("&Timer based"), variable=self.tkopt.animations, value=2, command=self.mOptAnimations)
- submenu.add_radiobutton(label=n_("&Fast"), variable=self.tkopt.animations, value=1, command=self.mOptAnimations)
- submenu.add_radiobutton(label=n_("&Slow"), variable=self.tkopt.animations, value=3, command=self.mOptAnimations)
- submenu.add_radiobutton(label=n_("&Very slow"), variable=self.tkopt.animations, value=4, command=self.mOptAnimations)
+ submenu.add_radiobutton(label=n_("&Very fast"), variable=self.tkopt.animations, value=1, command=self.mOptAnimations)
+ submenu.add_radiobutton(label=n_("&Fast"), variable=self.tkopt.animations, value=2, command=self.mOptAnimations)
+ submenu.add_radiobutton(label=n_("&Medium"), variable=self.tkopt.animations, value=3, command=self.mOptAnimations)
+ submenu.add_radiobutton(label=n_("&Slow"), variable=self.tkopt.animations, value=4, command=self.mOptAnimations)
+ submenu.add_radiobutton(label=n_("V&ery slow"), variable=self.tkopt.animations, value=5, command=self.mOptAnimations)
submenu.add_separator()
submenu.add_checkbutton(label=n_("&Redeal animation"), variable=self.tkopt.redeal_animation, command=self.mRedealAnimation)
if Image:
@@ -531,15 +533,13 @@ class PysolMenubar(PysolMenubarActions):
ctrl = "Control-"
if sys.platform == "darwin": ctrl = "Command-"
self._bindKey("", "n", self.mNewGame)
- self._bindKey("", "g", self.mSelectGameDialog)
- self._bindKey("", "v", self.mSelectGameDialogWithPreview)
+ self._bindKey(ctrl, "w", self.mSelectGameDialog)
+ self._bindKey(ctrl, "v", self.mSelectGameDialogWithPreview)
self._bindKey(ctrl, "r", lambda e, self=self: self.mSelectRandomGame())
self._bindKey(ctrl, "m", self.mSelectGameById)
self._bindKey(ctrl, "n", self.mNewGameWithNextId)
self._bindKey(ctrl, "o", self.mOpen)
- ##self._bindKey("", "F3", self.mOpen) # undocumented
self._bindKey(ctrl, "s", self.mSave)
- ##self._bindKey("", "F2", self.mSaveAs) # undocumented
self._bindKey(ctrl, "q", self.mQuit)
self._bindKey("", "z", self.mUndo)
self._bindKey("", "BackSpace", self.mUndo) # undocumented
@@ -547,14 +547,14 @@ class PysolMenubar(PysolMenubarActions):
self._bindKey("", "r", self.mRedo)
self._bindKey(ctrl, "g", self.mRestart)
self._bindKey("", "space", self.mDeal) # undocumented
- self._bindKey("", "t", self.mStatus)
+ self._bindKey(ctrl, "y", self.mStatus)
self._bindKey(ctrl, "t", self.mTop10)
self._bindKey("", "h", self.mHint)
self._bindKey(ctrl, "h", self.mHint1) # undocumented
##self._bindKey("", "Shift_L", self.mHighlightPiles)
##self._bindKey("", "Shift_R", self.mHighlightPiles)
self._bindKey("", "i", self.mHighlightPiles)
- self._bindKey("", "f", self.mFindCard)
+ self._bindKey("", "F3", self.mFindCard)
self._bindKey(ctrl, "d", self.mDemo)
self._bindKey(ctrl, "e", self.mSelectCardsetDialog)
self._bindKey(ctrl, "b", self.mOptChangeCardback) # undocumented
@@ -597,19 +597,19 @@ class PysolMenubar(PysolMenubarActions):
#
def _bindKey(self, modifier, key, func):
- if 0 and not modifier and len(key) == 1:
- self.__keybindings[key.lower()] = func
- self.__keybindings[key.upper()] = func
- return
+## if 0 and not modifier and len(key) == 1:
+## self.__keybindings[key.lower()] = func
+## self.__keybindings[key.upper()] = func
+## return
+ if not modifier and len(key) == 1:
+ # ignore Ctrl/Shift/Alt
+ func = lambda e, func=func: e.state == 0 and func(e)
sequence = "<" + modifier + "KeyPress-" + key + ">"
- try:
+ bind(self.top, sequence, func)
+ if len(key) == 1 and key != key.upper():
+ key = key.upper()
+ sequence = "<" + modifier + "KeyPress-" + key + ">"
bind(self.top, sequence, func)
- if len(key) == 1 and key != key.upper():
- key = key.upper()
- sequence = "<" + modifier + "KeyPress-" + key + ">"
- bind(self.top, sequence, func)
- except:
- raise
def _keyPressHandler(self, event):
@@ -621,10 +621,10 @@ class PysolMenubar(PysolMenubarActions):
if event.char: # ignore Ctrl/Shift/etc.
self.game.demo.keypress = event.char
r = EVENT_HANDLED
- func = self.__keybindings.get(event.char)
- if func and (event.state & ~2) == 0:
- func(event)
- r = EVENT_HANDLED
+## func = self.__keybindings.get(event.char)
+## if func and (event.state & ~2) == 0:
+## func(event)
+## r = EVENT_HANDLED
return r
#
@@ -636,9 +636,11 @@ class PysolMenubar(PysolMenubarActions):
games = map(self.app.gdb.get, self.app.gdb.getGamesIdSortedByName())
##games = tuple(games)
###menu = MfxMenu(menu, label="Select &game")
- menu.add_command(label=n_("All &games..."), accelerator="G",
+ m = "Ctrl-"
+ if sys.platform == "darwin": m = "Cmd-"
+ menu.add_command(label=n_("All &games..."), accelerator=m+"W",
command=self.mSelectGameDialog)
- menu.add_command(label=n_("Playable pre&view..."), accelerator="V",
+ menu.add_command(label=n_("Playable pre&view..."), accelerator=m+"V",
command=self.mSelectGameDialogWithPreview)
if not SELECT_GAME_MENU:
return
@@ -783,13 +785,19 @@ class PysolMenubar(PysolMenubarActions):
gi = games[i]
columnbreak = i > 0 and (i % cb) == 0
if short_name:
- label = gi.short_name
+ label = gettext(gi.short_name)
else:
- label = gi.name
- menu.add_radiobutton(command=command, variable=variable,
- columnbreak=columnbreak,
- value=gi.id, label=label, name=None)
-
+ label = gettext(gi.name)
+## menu.add_radiobutton(command=command, variable=variable,
+## columnbreak=columnbreak,
+## value=gi.id, label=label, name=None)
+ # optimized by inlining
+ menu.tk.call((menu._w, 'add', 'radiobutton') +
+ menu._options({'command': command,
+ 'variable': variable,
+ 'columnbreak': columnbreak,
+ 'value': gi.id,
+ 'label': label}))
#
# Select Game menu actions
@@ -1148,7 +1156,6 @@ class PysolMenubar(PysolMenubarActions):
self._cancelDrag()
self.game.endGame(bookmark=1)
self.game.quitGame(bookmark=1)
- self.app.opt.games_geometry = {} # clear saved games geometry
def _mOptCardback(self, index):
if self._cancelDrag(break_pause=False): return
@@ -1328,13 +1335,13 @@ class PysolMenubar(PysolMenubarActions):
def mOptTheme(self, *event):
theme = self.tkopt.theme.get()
+ self.app.opt.tile_theme = theme
d = MfxMessageDialog(self.top, title=_("Change theme"),
text=_("""\
This settings will take effect
the next time you restart """)+PACKAGE,
bitmap="warning",
default=0, strings=(_("&OK"),))
- self.app.opt.tile_theme = theme
def createThemesMenu(self, menu):
submenu = MfxMenu(menu, label=n_("Set t&heme"))
@@ -1343,12 +1350,12 @@ the next time you restart """)+PACKAGE,
all_themes.sort()
#
tn = {
- 'default': _('Default'),
- 'classic': _('Classic'),
- 'alt': _('Revitalized'),
- 'winnative': _('Windows native'),
- 'xpnative': _('XP Native'),
- 'aqua': _('Aqua'),
+ 'default': n_('Default'),
+ 'classic': n_('Classic'),
+ 'alt': n_('Revitalized'),
+ 'winnative': n_('Windows native'),
+ 'xpnative': n_('XP Native'),
+ 'aqua': n_('Aqua'),
}
for t in all_themes:
try:
@@ -1357,4 +1364,3 @@ the next time you restart """)+PACKAGE,
n = t.capitalize()
submenu.add_radiobutton(label=n, variable=self.tkopt.theme,
value=t, command=self.mOptTheme)
-
diff --git a/pysollib/tile/selectgame.py b/pysollib/tile/selectgame.py
index 148d498a..a76c8e2f 100644
--- a/pysollib/tile/selectgame.py
+++ b/pysollib/tile/selectgame.py
@@ -428,7 +428,7 @@ class SelectGameDialogWithPreview(SelectGameDialog):
destruct(self.preview_app)
self.preview_app = None
- def updatePreview(self, gameid, animations=5):
+ def updatePreview(self, gameid, animations=10):
if gameid == self.preview_key:
return
self.deletePreview()
@@ -495,7 +495,7 @@ class SelectGameDialogWithPreview(SelectGameDialog):
#
self.preview_app.audio = self.app.audio
if self.app.opt.animations:
- self.preview_app.opt.animations = 5
+ self.preview_app.opt.animations = 10
else:
self.preview_app.opt.animations = 0
# save seed
@@ -553,13 +553,12 @@ class SelectGameDialogWithPreview(SelectGameDialog):
('percent', percent),
):
title_label, text_label = self.info_labels[n]
- if t == '':
+ if t in ('', None):
title_label.grid_remove()
text_label.grid_remove()
else:
title_label.grid()
text_label.grid()
text_label.config(text=t)
- #self.info_labels[n].config(text=t)
diff --git a/pysollib/tile/solverdialog.py b/pysollib/tile/solverdialog.py
new file mode 100644
index 00000000..c3cbcc99
--- /dev/null
+++ b/pysollib/tile/solverdialog.py
@@ -0,0 +1,330 @@
+##---------------------------------------------------------------------------##
+##
+## PySol -- a Python Solitaire game
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; see the file COPYING.
+## If not, write to the Free Software Foundation, Inc.,
+## 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+##
+##---------------------------------------------------------------------------##
+
+__all__ = [
+ #'SolverDialog',
+ 'create_solver_dialog',
+ 'connect_game_solver_dialog',
+ 'destroy_solver_dialog',
+ 'reset_solver_dialog',
+ ]
+
+# imports
+import os, sys
+import Tile as Tkinter
+import traceback
+
+# PySol imports
+from pysollib.mfxutil import destruct, kwdefault, KwStruct, Struct
+from pysollib.settings import PACKAGE
+
+# Toolkit imports
+from tkconst import EVENT_HANDLED, EVENT_PROPAGATE
+from tkwidget import MfxDialog
+from tkwidget import PysolScale
+from tkutil import bind, unbind_destroy
+
+gettext = _
+
+
+# /***********************************************************************
+# //
+# ************************************************************************/
+
+class SolverDialog(MfxDialog):
+
+ def __init__(self, parent, app, **kw):
+ self.parent = parent
+ self.app = app
+ title = PACKAGE+' - FreeCell Solver'
+ kw = self.initKw(kw)
+ MfxDialog.__init__(self, parent, title, kw.resizable, kw.default)
+ top_frame, bottom_frame = self.createFrames(kw)
+ self.createBitmaps(top_frame, kw)
+ #
+ self.solving_methods = {
+ 'A*': 'a-star',
+ 'Breadth-First Search': 'bfs',
+ 'Depth-First Search': 'dfs', # default
+ 'A randomized DFS': 'random-dfs',
+ '"Soft" DFS': 'soft-dfs',
+ }
+ self.games = {} # key: gamename; value: gameid
+
+ #
+ frame = Tkinter.Frame(top_frame)
+ frame.pack(expand=True, fill='both', padx=4, pady=4)
+ frame.columnconfigure(1, weight=1)
+
+ #
+ row = 0
+ Tkinter.Label(frame, text=_('Game:'), anchor='w'
+ ).grid(row=row, column=0, sticky='ew', padx=2, pady=2)
+ games = app.getGamesForSolver()
+ gamenames = ['']
+ for id in games:
+ name = app.getGameTitleName(id)
+ name = gettext(name)
+ gamenames.append(name)
+ self.games[name] = id
+ gamenames.sort()
+ self.gamenames = gamenames
+ cb = Tkinter.Combobox(frame, values=tuple(gamenames),
+ state='readonly', width=40)
+ cb.grid(row=row, column=1, sticky='ew', padx=2, pady=2)
+ bind(cb, '<>', self.gameSelected)
+ self.games_var = cb
+
+ #
+ row += 1
+ Tkinter.Label(frame, text=_('Solving method:'), anchor='w'
+ ).grid(row=row, column=0, sticky='ew', padx=2, pady=2)
+ ##sm = self.solving_methods.values()
+ ##sm.sort()
+ sm = ['A*', 'Breadth-First Search', 'Depth-First Search',
+ 'A randomized DFS', '"Soft" DFS']
+ cb = Tkinter.Combobox(frame, values=tuple(sm), state='readonly')
+ cb.grid(row=row, column=1, sticky='ew', padx=2, pady=2)
+ cb.current(sm.index('Depth-First Search'))
+ self.solving_method_var = cb
+
+ #
+ row += 1
+ Tkinter.Label(frame, text=_('Preset:'), anchor='w'
+ ).grid(row=row, column=0, sticky='ew', padx=2, pady=2)
+ presets = [
+ 'none',
+ 'abra-kadabra',
+ 'cool-jives',
+ 'crooked-nose',
+ 'fools-gold',
+ 'good-intentions',
+ 'hello-world',
+ 'john-galt-line',
+ 'rin-tin-tin',
+ 'yellow-brick-road',
+ ]
+ self.presets = presets
+ cb = Tkinter.Combobox(frame, values=tuple(presets), state='readonly')
+ cb.grid(row=row, column=1, sticky='ew', padx=2, pady=2)
+ cb.current(0)
+ self.preset_var = cb
+
+ #
+ row += 1
+ self.max_iters_var = Tkinter.IntVar()
+ self.max_iters_var.set(10e4)
+ Tkinter.Label(frame, text=_('Max iterations:'), anchor='w'
+ ).grid(row=row, column=0, sticky='ew', padx=2, pady=2)
+ spin = Tkinter.Spinbox(frame, bg='white', from_=1000, to=10e6,
+ increment=1000, textvariable=self.max_iters_var)
+ spin.grid(row=row, column=1, sticky='w', padx=2, pady=2)
+
+ #
+ row += 1
+ self.max_depth_var = Tkinter.IntVar()
+ self.max_depth_var.set(1000)
+ Tkinter.Label(frame, text=_('Max depth:'), anchor='w'
+ ).grid(row=row, column=0, sticky='ew', padx=2, pady=2)
+ spin = Tkinter.Spinbox(frame, bg='white', from_=100, to=10000,
+ increment=100, textvariable=self.max_depth_var)
+ spin.grid(row=row, column=1, sticky='w', padx=2, pady=2)
+
+ #
+ row += 1
+ self.progress_var = Tkinter.BooleanVar()
+ self.progress_var.set(True)
+ w = Tkinter.Checkbutton(frame, variable=self.progress_var,
+ text=_('Show progress'))
+ w.grid(row=row, column=0, columnspan=2, sticky='ew', padx=2, pady=2)
+
+ #
+ label_frame = Tkinter.LabelFrame(top_frame, text=_('Progress'))
+ label_frame.pack(expand=True, fill='both', padx=6, pady=2)
+ #label_frame.columnconfigure(0, weight=1)
+ label_frame.columnconfigure(1, weight=1)
+
+ #
+ frow = 0
+ Tkinter.Label(label_frame, text=_('Iteration:'), anchor='w'
+ ).grid(row=frow, column=0, sticky='ew', padx=4, pady=2)
+ lb = Tkinter.Label(label_frame, anchor='w')
+ lb.grid(row=frow, column=1, sticky='ew', padx=4, pady=2)
+ self.iter_label = lb
+ frow += 1
+ Tkinter.Label(label_frame, text=_('Depth:'), anchor='w'
+ ).grid(row=frow, column=0, sticky='ew', padx=4, pady=2)
+ lb = Tkinter.Label(label_frame, anchor='w')
+ lb.grid(row=frow, column=1, sticky='ew', padx=4, pady=2)
+ self.depth_label = lb
+ frow += 1
+ Tkinter.Label(label_frame, text=_('Stored-States:'), anchor='w'
+ ).grid(row=frow, column=0, sticky='ew', padx=4, pady=2)
+ lb = Tkinter.Label(label_frame, anchor='w')
+ lb.grid(row=frow, column=1, sticky='ew', padx=4, pady=2)
+ self.states_label = lb
+
+ #
+ lb = Tkinter.Label(top_frame, anchor='w')
+ lb.pack(expand=True, fill='x', padx=6, pady=4)
+ self.result_label = lb
+
+ #
+ focus = self.createButtons(bottom_frame, kw)
+ self.mainloop(focus, kw.timeout, transient=False)
+
+ self.start_button = self.buttons[0]
+ self.play_button = self.buttons[1]
+
+ #
+ self._reset()
+ self.connectGame(self.app.game)
+
+ def initKw(self, kw):
+ strings=[_('&Start'), _('&Play'), _('&New'), 'sep', _('&Close'),]
+ kw = KwStruct(kw,
+ strings=strings,
+ default=0,
+ )
+ return MfxDialog.initKw(self, kw)
+
+ def mDone(self, button):
+ if button == 0:
+ self.startSolving()
+ elif button == 1:
+ self.startPlay()
+ elif button == 2:
+ self.app.menubar.mNewGame()
+ elif button == 3:
+ global solver_dialog
+ solver_dialog = None
+ self.destroy()
+ return EVENT_HANDLED
+
+ def mCancel(self, *event):
+ return self.mDone(3)
+
+ def wmDeleteWindow(self, *event):
+ return self.mDone(3)
+
+ def gameSelected(self, *event):
+ name = self.games_var.get()
+ if not name:
+ return
+ id = self.games[name]
+ self.app.menubar._mSelectGame(id)
+
+ def connectGame(self, game):
+ name = self.app.getGameTitleName(game.id)
+ name = gettext(name)
+ if name in self.gamenames:
+ self.start_button.config(state='normal')
+ i = self.gamenames.index(name)
+ self.games_var.current(i)
+ else:
+ self.start_button.config(state='disabled')
+ self.games_var.current(0)
+ self.play_button.config(state='disabled')
+
+ def _reset(self):
+ self.play_button.config(state='disabled')
+ self.setText(iter='', depth='', states='')
+ self.result_label['text'] = ''
+ self.top.update_idletasks()
+
+ def reset(self):
+ self.play_button.config(state='disabled')
+
+ def startSolving(self):
+ self._reset()
+ game = self.app.game
+ solver = game.Solver_Class(game, self) # create solver instance
+ game.solver = solver
+ method = self.solving_method_var.get()
+ method = self.solving_methods[method]
+ preset = self.preset_var.get()
+ max_iters = self.max_iters_var.get()
+ max_depth = self.max_depth_var.get()
+ progress = self.progress_var.get()
+ solver.config(method=method, preset=preset, max_iters=max_iters,
+ max_depth=max_depth, progress=progress)
+ solver.computeHints()
+ hints_len = len(solver.hints)-1
+ if hints_len > 0:
+ self.result_label['text'] = _('This game is solveable in %s moves.') % hints_len
+ self.play_button.config(state='normal')
+ else:
+ self.result_label['text'] = _('I could not solve this game.')
+ self.play_button.config(state='disabled')
+
+ def startPlay(self):
+ self.play_button.config(state='disabled')
+ self.app.top.tkraise()
+ self.app.top.update_idletasks()
+ self.app.top.update()
+ self.app.game.startDemo(level=3)
+
+ def setText(self, **kw):
+ if 'iter' in kw:
+ self.iter_label['text'] = kw['iter']
+ if 'depth' in kw:
+ self.depth_label['text'] = kw['depth']
+ if 'states' in kw:
+ self.states_label['text'] = kw['states']
+ self.top.update_idletasks()
+
+
+
+solver_dialog = None
+
+def create_solver_dialog(parent, game):
+ global solver_dialog
+ try:
+ solver_dialog.top.wm_deiconify()
+ solver_dialog.top.tkraise()
+ except:
+ ##traceback.print_exc()
+ solver_dialog = SolverDialog(parent, game)
+
+def connect_game_solver_dialog(game):
+ try:
+ solver_dialog.connectGame(game)
+ except:
+ pass
+
+def destroy_solver_dialog():
+ global solver_dialog
+ try:
+ solver_dialog.destroy()
+ except:
+ ##traceback.print_exc()
+ pass
+ solver_dialog = None
+
+
+def reset_solver_dialog():
+ if solver_dialog:
+ try:
+ solver_dialog.reset()
+ except:
+ ##traceback.print_exc()
+ pass
+
diff --git a/pysollib/tile/statusbar.py b/pysollib/tile/statusbar.py
index 8a75378d..7ce4a16a 100644
--- a/pysollib/tile/statusbar.py
+++ b/pysollib/tile/statusbar.py
@@ -102,8 +102,11 @@ class MfxStatusbar:
def updateText(self, **kw):
for k, v in kw.items():
label = getattr(self, k + "_label")
- #label["text"] = str(v)
- label["text"] = unicode(v)
+ text = unicode(v)
+ width = label['width']
+ if width and len(text) > width:
+ label['width'] = len(text)
+ label["text"] = text
def configLabel(self, name, **kw):
if 'fg' in kw:
diff --git a/pysollib/tile/tkcanvas.py b/pysollib/tile/tkcanvas.py
index e67a8e70..12cfeec2 100644
--- a/pysollib/tile/tkcanvas.py
+++ b/pysollib/tile/tkcanvas.py
@@ -371,21 +371,14 @@ class MfxCanvas(Tkinter.Canvas):
def _substitute(self, *args):
e = Tkinter.Event()
- e.x = int(args[0])
- e.y = int(args[1])
+ try:
+ # Tk changed behavior in 8.4.2, returning "??" rather more often.
+ e.x = int(args[0])
+ except ValueError:
+ e.x = args[0]
+ try:
+ e.y = int(args[1])
+ except ValueError:
+ e.y = args[1]
return (e,)
-
- #
- # debug
- #
-
- def update(self):
- ##import mfxutil; print mfxutil.callername()
- # ??????
- Tkinter.Canvas.update(self)
-
- def update_idletasks(self):
- ##import mfxutil; print mfxutil.callername()
- Tkinter.Canvas.update_idletasks(self)
-
diff --git a/pysollib/tile/tkstats.py b/pysollib/tile/tkstats.py
index 8ba2341d..a3eaff30 100644
--- a/pysollib/tile/tkstats.py
+++ b/pysollib/tile/tkstats.py
@@ -38,10 +38,12 @@ __all__ = ['SingleGame_StatsDialog',
'FullLog_StatsDialog',
'SessionLog_StatsDialog',
'Status_StatsDialog',
- 'Top_StatsDialog']
+ 'Top_StatsDialog',
+ 'ProgressionDialog',
+ ]
# imports
-import os, string, sys, types
+import os
import time
import Tile as Tkinter
import tkFont
@@ -50,7 +52,7 @@ import tkFont
from pysollib.mfxutil import destruct, Struct, kwdefault, KwStruct
from pysollib.mfxutil import format_time
##from pysollib.util import *
-from pysollib.stats import PysolStatsFormatter
+from pysollib.stats import PysolStatsFormatter, ProgressionFormatter
from pysollib.settings import TOP_TITLE
# Toolkit imports
@@ -233,6 +235,7 @@ class TreeFormatter(PysolStatsFormatter):
self.tree = tree
self.parent_window = parent_window
self.font = font
+ self.tkfont = tkFont.Font(tree, font)
self.gameid = None
self.gamenumber = None
self._tabs = None
@@ -246,7 +249,8 @@ class TreeFormatter(PysolStatsFormatter):
tw = 20*self.w
##tw = 160
self._tabs = [tw]
- font = tkFont.Font(self.tree, self.font)
+ #font = tkFont.Font(self.tree, self.font)
+ font = self.tkfont
for t in arg[1:]:
tw = font.measure(t)+20
self._tabs.append(tw)
@@ -604,7 +608,7 @@ class Top_StatsDialog(MfxDialog):
self.createBitmaps(top_frame, kw)
frame = Tkinter.Frame(top_frame)
- frame.pack(expand=Tkinter.YES, fill=Tkinter.BOTH, padx=5, pady=10)
+ frame.pack(expand=Tkinter.YES, fill=Tkinter.BOTH, padx=10, pady=10)
frame.columnconfigure(0, weight=1)
if (player in app.stats.games_stats and
@@ -681,3 +685,255 @@ class Top_StatsDialog(MfxDialog):
separatorwidth=2,
)
return MfxDialog.initKw(self, kw)
+
+
+# /***********************************************************************
+# //
+# ************************************************************************/
+
+class ProgressionDialog(MfxDialog):
+ def __init__(self, parent, title, app, player, gameid, **kw):
+
+ font_name = app.getFont('default')
+ font = tkFont.Font(parent, font_name)
+ tkfont = tkFont.Font(parent, font)
+ font_metrics = font.metrics()
+ measure = tkfont.measure
+ self.text_height = font_metrics['linespace']
+ self.text_width = measure('XX.XX.XX')
+
+ self.items = []
+ self.formatter = ProgressionFormatter(app, player, gameid)
+
+ kw = self.initKw(kw)
+ MfxDialog.__init__(self, parent, title, kw.resizable, kw.default)
+ top_frame, bottom_frame = self.createFrames(kw)
+ self.createBitmaps(top_frame, kw)
+
+ frame = Tkinter.Frame(top_frame)
+ frame.pack(expand=True, fill='both', padx=5, pady=10)
+ frame.columnconfigure(0, weight=1)
+
+ # constants
+ self.canvas_width, self.canvas_height = 600, 250
+ if parent.winfo_screenwidth() < 800 or \
+ parent.winfo_screenheight() < 600:
+ self.canvas_width, self.canvas_height = 400, 200
+ self.xmargin, self.ymargin = 10, 10
+ self.graph_dx, self.graph_dy = 10, 10
+ self.played_color = '#ff7ee9'
+ self.won_color = '#00dc28'
+ self.percent_color = 'blue'
+ # create canvas
+ self.canvas = canvas = Tkinter.Canvas(frame, bg='#dfe8ff', bd=0,
+ highlightthickness=1,
+ highlightbackground='black',
+ width=self.canvas_width,
+ height=self.canvas_height)
+ canvas.pack(side='left', padx=5)
+ #
+ dir = os.path.join('images', 'stats')
+ try:
+ fn = app.dataloader.findImage('progression', dir)
+ self.bg_image = loadImage(fn)
+ canvas.create_image(0, 0, image=self.bg_image, anchor='nw')
+ except:
+ pass
+ #
+ tw = max(measure(_('Games/day')),
+ measure(_('Games/week')),
+ measure(_('% won')))
+ self.left_margin = self.xmargin+tw/2
+ self.right_margin = self.xmargin+tw/2
+ self.top_margin = 15+self.text_height
+ self.bottom_margin = 15+self.text_height+10+self.text_height
+ #
+ x0, y0 = self.left_margin, self.canvas_height-self.bottom_margin
+ x1, y1 = self.canvas_width-self.right_margin, self.top_margin
+ canvas.create_rectangle(x0, y0, x1, y1, fill='white')
+ # horizontal axis
+ canvas.create_line(x0, y0, x1, y0, width=3)
+
+ # left vertical axis
+ canvas.create_line(x0, y0, x0, y1, width=3)
+ t = _('Games/day')
+ self.games_text_id = canvas.create_text(x0-4, y1-4, anchor='s', text=t)
+
+ # right vertical axis
+ canvas.create_line(x1, y0, x1, y1, width=3)
+ canvas.create_text(x1+4, y1-4, anchor='s', text=_('% won'))
+
+ # caption
+ d = self.text_height
+ x, y = self.xmargin, self.canvas_height-self.ymargin
+ id = canvas.create_rectangle(x, y, x+d, y-d, outline='black',
+ fill=self.played_color)
+ x += d+5
+ canvas.create_text(x, y, anchor='sw', text=_('Played'))
+ x += measure(_('Played'))+20
+ id = canvas.create_rectangle(x, y, x+d, y-d, outline='black',
+ fill=self.won_color)
+ x += d+5
+ canvas.create_text(x, y, anchor='sw', text=_('Won'))
+ x += measure(_('Won'))+20
+ id = canvas.create_rectangle(x, y, x+d, y-d, outline='black',
+ fill=self.percent_color)
+ x += d+5
+ canvas.create_text(x, y, anchor='sw', text=_('% won'))
+
+ # right frame
+ right_frame = Tkinter.Frame(frame)
+ right_frame.pack(side='left', fill='x', padx=5)
+ self.all_games_variable = var = Tkinter.StringVar()
+ var.set('all')
+ b = Tkinter.Radiobutton(right_frame, text=_('All games'),
+ variable=var, value='all',
+ command=self.updateGraph,
+ )
+ b.pack(fill='x', expand=True, padx=3, pady=1)
+ b = Tkinter.Radiobutton(right_frame, text=_('Current game'),
+ variable=var, value='current',
+ command=self.updateGraph,
+ )
+ b.pack(fill='x', expand=True, padx=3, pady=1)
+ label_frame = Tkinter.LabelFrame(right_frame, text=_('Statistics for'))
+ label_frame.pack(side='top', fill='x', pady=10)
+ self.variable = var = Tkinter.StringVar()
+ var.set('week')
+ for v, t in (
+ ('week', _('Last 7 days')),
+ ('month', _('Last month')),
+ ('year', _('Last year')),
+ ('all', _('All time')),
+ ):
+ b = Tkinter.Radiobutton(label_frame, text=t, variable=var, value=v,
+ command=self.updateGraph,
+ )
+ b.pack(fill='x', expand=True, padx=3, pady=1)
+ label_frame = Tkinter.LabelFrame(right_frame, text=_('Show graphs'))
+ label_frame.pack(side='top', fill='x')
+ self.played_graph_var = Tkinter.BooleanVar()
+ self.played_graph_var.set(True)
+ b = Tkinter.Checkbutton(label_frame, text=_('Played'),
+ command=self.updateGraph,
+ variable=self.played_graph_var,
+ )
+ b.pack(fill='x', expand=True, padx=3, pady=1)
+ self.won_graph_var = Tkinter.BooleanVar()
+ self.won_graph_var.set(True)
+ b = Tkinter.Checkbutton(label_frame, text=_('Won'),
+ command=self.updateGraph,
+ variable=self.won_graph_var,
+ )
+ b.pack(fill='x', expand=True, padx=3, pady=1)
+ self.percent_graph_var = Tkinter.BooleanVar()
+ self.percent_graph_var.set(True)
+ b = Tkinter.Checkbutton(label_frame, text=_('% won'),
+ command=self.updateGraph,
+ variable=self.percent_graph_var,
+ )
+ b.pack(fill='x', expand=True, padx=3, pady=1)
+
+ self.updateGraph()
+
+ focus = self.createButtons(bottom_frame, kw)
+ self.mainloop(focus, kw.timeout)
+
+
+ def initKw(self, kw):
+ kw = KwStruct(kw, strings=(_('&OK'),), default=0, separatorwidth=2)
+ return MfxDialog.initKw(self, kw)
+
+
+ def updateGraph(self, *args):
+ interval = self.variable.get()
+ canvas = self.canvas
+ if self.items:
+ canvas.delete(*self.items)
+ self.items = []
+
+ all_games = (self.all_games_variable.get() == 'all')
+ result = self.formatter.getResults(interval, all_games)
+
+ if interval in ('week', 'month'):
+ t = _('Games/day')
+ else:
+ t = _('Games/week')
+ canvas.itemconfig(self.games_text_id, text=t)
+
+ graph_width = self.canvas_width-self.left_margin-self.right_margin
+ graph_height = self.canvas_height-self.top_margin-self.bottom_margin
+ dx = (graph_width-2*self.graph_dx)/(len(result)-1)
+ graph_dx = (graph_width-(len(result)-1)*dx)/2
+ dy = (graph_height-self.graph_dy)/5
+ x0, y0 = self.left_margin, self.canvas_height-self.bottom_margin
+ x1, y1 = self.canvas_width-self.right_margin, self.top_margin
+ td = self.text_height/2
+
+ # vertical scale
+ x = x0+graph_dx
+ xx = -100 # coord. of prev. text
+ for res in result:
+ if res[0] is not None and x > xx+self.text_width+4:
+ ##id = canvas.create_line(x, y0, x, y0-5, width=3)
+ ##self.items.append(id)
+ id = canvas.create_line(x, y0, x, y1, stipple='gray50')
+ self.items.append(id)
+ id = canvas.create_text(x, y0+td, anchor='n', text=res[0])
+ self.items.append(id)
+ xx = x
+ else:
+ id = canvas.create_line(x, y0, x, y0-3, width=1)
+ self.items.append(id)
+ x += dx
+
+ # horizontal scale
+ max_games = max([i[1] for i in result])
+ games_delta = max_games/5+1
+ percent = 0
+ games = 0
+ for y in range(y0, y1, -dy):
+ if y != y0:
+ id = canvas.create_line(x0, y, x1, y, stipple='gray50')
+ self.items.append(id)
+ id = canvas.create_text(x0-td, y, anchor='e', text=str(games))
+ self.items.append(id)
+ id = canvas.create_text(x1+td, y, anchor='w', text=str(percent))
+ self.items.append(id)
+ games += games_delta
+ percent += 20
+
+ # draw result
+ games_resolution = float(dy)/games_delta
+ percent_resolution = float(dy)/20
+ played_coords = []
+ won_coords = []
+ percent_coords = []
+ x = x0+graph_dx
+ for res in result:
+ played, won = res[1], res[2]
+ y = y0 - int(games_resolution*played)
+ played_coords += [x,y]
+ y = y0 - int(games_resolution*won)
+ won_coords += [x,y]
+ if played > 0:
+ percent = int(100.*won/played)
+ else:
+ percent = 0
+ y = y0 - int(percent_resolution*percent)
+ percent_coords += [x,y]
+ x += dx
+ if self.played_graph_var.get():
+ id = canvas.create_line(fill=self.played_color, width=3,
+ *played_coords)
+ self.items.append(id)
+ if self.won_graph_var.get():
+ id = canvas.create_line(fill=self.won_color, width=3,
+ *won_coords)
+ self.items.append(id)
+ if self.percent_graph_var.get():
+ id = canvas.create_line(fill=self.percent_color, width=3,
+ *percent_coords)
+ self.items.append(id)
+
+
diff --git a/pysollib/tile/tkwidget.py b/pysollib/tile/tkwidget.py
index 2c2b49cf..887ab1a7 100644
--- a/pysollib/tile/tkwidget.py
+++ b/pysollib/tile/tkwidget.py
@@ -507,16 +507,11 @@ class MfxScrolledCanvas:
if i == 0:
self.canvas.config(bg=tile.color)
##app.top.config(bg=tile.color)
- color = None
else:
self.canvas.config(bg=app.top_bg)
##app.top.config(bg=app.top_bg)
- color = tile.text_color
- if app.opt.use_default_text_color:
- self.canvas.setTextColor(color)
- else:
- self.canvas.setTextColor(app.opt.colors['text'])
+ self.canvas.setTextColor(app.opt.colors['text'])
return True
@@ -533,7 +528,7 @@ class MfxScrolledCanvas:
def createFrame(self, kw):
width = kw.get("width")
height = kw.get("height")
- self.frame = Tkinter.Frame(self.parent, width=width, height=height, bg=None)
+ self.frame = Tkinter.Frame(self.parent, width=width, height=height)
def createCanvas(self, kw):
self.canvas = MfxCanvas(self.frame, **kw)
diff --git a/pysollib/tile/toolbar.py b/pysollib/tile/toolbar.py
index e8aba7fb..f2aee2a5 100644
--- a/pysollib/tile/toolbar.py
+++ b/pysollib/tile/toolbar.py
@@ -33,7 +33,7 @@
##
##---------------------------------------------------------------------------##
-__all__ = ['PysolToolbar'] #, 'TOOLBAR_BUTTONS']
+__all__ = ['PysolToolbar']
# imports
import os
@@ -240,6 +240,7 @@ class PysolToolbar(PysolToolbarActions):
widget.hide()
#
prev_visible = None
+ last_visible = None
for w in self._widgets:
if isinstance(w, ToolbarSeparator):
if prev_visible is None or isinstance(prev_visible, ToolbarSeparator):
@@ -248,6 +249,10 @@ class PysolToolbar(PysolToolbarActions):
w.show(orient=self.orient)
if w.visible:
prev_visible = w
+ if not isinstance(w, ToolbarLabel):
+ last_visible = w
+ if isinstance(last_visible, ToolbarSeparator):
+ last_visible.hide()
# util
def _loadImage(self, name):
diff --git a/pysollib/tk/colorsdialog.py b/pysollib/tk/colorsdialog.py
index e01f74ee..b9b20719 100644
--- a/pysollib/tk/colorsdialog.py
+++ b/pysollib/tk/colorsdialog.py
@@ -48,8 +48,6 @@ class ColorsDialog(MfxDialog):
frame.pack(expand=True, fill='both', padx=5, pady=10)
frame.columnconfigure(0, weight=1)
- self.use_default_var = Tkinter.BooleanVar()
- self.use_default_var.set(not app.opt.use_default_text_color)
self.text_var = Tkinter.StringVar()
self.text_var.set(app.opt.colors['text'])
self.piles_var = Tkinter.StringVar()
@@ -67,17 +65,9 @@ class ColorsDialog(MfxDialog):
self.not_matching_var = Tkinter.StringVar()
self.not_matching_var.set(app.opt.colors['not_matching'])
#
- c = Tkinter.Checkbutton(frame, variable=self.use_default_var,
- text=_("Text foreground:"), anchor='w')
- c.grid(row=0, column=0, sticky='we')
- l = Tkinter.Label(frame, width=10, height=2,
- bg=self.text_var.get(), textvariable=self.text_var)
- l.grid(row=0, column=1, padx=5)
- b = Tkinter.Button(frame, text=_('Change...'), width=10,
- command=lambda l=l: self.selectColor(l))
- b.grid(row=0, column=2)
- row = 1
+ row = 0
for title, var in (
+ (_('Text foreground:'), self.text_var),
(_('Highlight piles:'), self.piles_var),
(_('Highlight cards 1:'), self.cards_1_var),
(_('Highlight cards 2:'), self.cards_2_var),
@@ -86,7 +76,7 @@ class ColorsDialog(MfxDialog):
(_('Hint arrow:'), self.hintarrow_var),
(_('Highlight not matching:'), self.not_matching_var),
):
- Tkinter.Label(frame, text=title, anchor='w'
+ Tkinter.Label(frame, text=title, anchor='w',
).grid(row=row, column=0, sticky='we')
l = Tkinter.Label(frame, width=10, height=2,
bg=var.get(), textvariable=var)
@@ -99,7 +89,6 @@ class ColorsDialog(MfxDialog):
focus = self.createButtons(bottom_frame, kw)
self.mainloop(focus, kw.timeout)
#
- self.use_default_color = not self.use_default_var.get()
self.text_color = self.text_var.get()
self.piles_color = self.piles_var.get()
self.cards_1_color = self.cards_1_var.get()
diff --git a/pysollib/tk/findcarddialog.py b/pysollib/tk/findcarddialog.py
index b8430128..ca41191b 100644
--- a/pysollib/tk/findcarddialog.py
+++ b/pysollib/tk/findcarddialog.py
@@ -114,6 +114,7 @@ class FindCardDialog(Tkinter.Toplevel):
self.groups.append(group)
def connectGame(self, game):
+ self.canvas.delete('all')
self.game = game
suits = game.gameinfo.suits
ranks = game.gameinfo.ranks
@@ -133,6 +134,7 @@ class FindCardDialog(Tkinter.Toplevel):
w, h = dx*j+2, dy*i+2
self.canvas.config(width=w, height=h)
self.wm_iconname(PACKAGE + " - " + game.getTitleName())
+ self.wm_geometry('') # cancel user-specified geometry
def enterEvent(self, suit, rank, rect, group):
##print 'enterEvent', suit, rank, self.busy
diff --git a/pysollib/tk/menubar.py b/pysollib/tk/menubar.py
index 777ff840..1ee8450f 100644
--- a/pysollib/tk/menubar.py
+++ b/pysollib/tk/menubar.py
@@ -47,6 +47,7 @@ from pysollib.util import CARDSET
from pysollib.settings import PACKAGE, WIN_SYSTEM
from pysollib.settings import TOP_TITLE
from pysollib.settings import SELECT_GAME_MENU
+from pysollib.settings import USE_FREECELL_SOLVER
from pysollib.gamedb import GI
from pysollib.actions import PysolMenubarActions
@@ -58,6 +59,7 @@ from soundoptionsdialog import SoundOptionsDialog
from selectcardset import SelectCardsetDialogWithPreview
from selecttile import SelectTileDialogWithPreview
from findcarddialog import connect_game_find_card_dialog, destroy_find_card_dialog
+from solverdialog import connect_game_solver_dialog
from tkwrap import MfxRadioMenuItem, MfxCheckMenuItem, StringVar
#from toolbar import TOOLBAR_BUTTONS
@@ -138,25 +140,18 @@ class MfxMenubar(Tkinter.Menu):
#print label, type(label)
name = re.sub(r"[^0-9a-zA-Z]", "", label).lower()
label = gettext(label)
- underline = -1
- m = re.search(r"^(.*)\&([^\&].*)$", label)
- if m:
- l1, l2 = m.group(1), m.group(2)
- l1 = re.sub(r"\&\&", "&", l1)
- l2 = re.sub(r"\&\&", "&", l2)
- label = l1 + l2
- underline = len(l1)
+ underline = label.find('&')
+ if underline >= 0:
+ label = label.replace('&', '')
return name, label, underline
def add(self, itemType, cnf={}):
label = cnf.get("label")
if label:
name = cnf.get('name')
- try:
+ if name:
del cnf['name'] # TclError: unknown option "-name"
- except KeyError:
- pass
- if not name:
+ else:
name, label, underline = self.labeltoname(label)
cnf["underline"] = cnf.get("underline", underline)
cnf["label"] = label
@@ -320,6 +315,7 @@ class PysolMenubar(PysolMenubarActions):
connect_game_find_card_dialog(game)
else:
destroy_find_card_dialog()
+ connect_game_solver_dialog(game)
# create a GTK-like path
def _addPath(self, path, menu, index, submenu):
@@ -368,7 +364,7 @@ class PysolMenubar(PysolMenubarActions):
menu.add_separator()
submenu = MfxMenu(menu, label=n_("Fa&vorite games"))
menu.add_command(label=n_("A&dd to favorites"), command=self.mAddFavor)
- menu.add_command(label=n_("R&emove from favorites"), command=self.mDelFavor)
+ menu.add_command(label=n_("Remove &from favorites"), command=self.mDelFavor)
menu.add_separator()
menu.add_command(label=n_("&Open..."), command=self.mOpen, accelerator=m+"O")
menu.add_command(label=n_("&Save"), command=self.mSave, accelerator=m+"S")
@@ -411,7 +407,7 @@ class PysolMenubar(PysolMenubarActions):
menu.add_checkbutton(label=n_("&Pause"), variable=self.tkopt.pause, command=self.mPause, accelerator="P")
#menu.add_command(label=n_("&Pause"), command=self.mPause, accelerator="P")
menu.add_separator()
- menu.add_command(label=n_("S&tatus..."), command=self.mStatus, accelerator="T")
+ menu.add_command(label=n_("S&tatus..."), command=self.mStatus, accelerator=m+"Y")
menu.add_checkbutton(label=n_("&Comments..."), variable=self.tkopt.comment, command=self.mEditGameComment)
menu.add_separator()
submenu = MfxMenu(menu, label=n_("&Statistics"))
@@ -422,6 +418,7 @@ class PysolMenubar(PysolMenubarActions):
submenu.add_command(label=n_("Full log..."), command=lambda self=self: self.mPlayerStats(mode=103))
submenu.add_separator()
submenu.add_command(label=TOP_TITLE+"...", command=self.mTop10, accelerator=m+"T")
+ submenu.add_command(label=n_("Progression..."), command=lambda self=self: self.mPlayerStats(mode=107))
submenu = MfxMenu(menu, label=n_("D&emo statistics"))
submenu.add_command(label=n_("Current game..."), command=lambda self=self: self.mPlayerStats(mode=1101))
submenu.add_command(label=n_("All games..."), command=lambda self=self: self.mPlayerStats(mode=1102))
@@ -429,12 +426,16 @@ class PysolMenubar(PysolMenubarActions):
menu = MfxMenu(self.__menubar, label=n_("&Assist"))
menu.add_command(label=n_("&Hint"), command=self.mHint, accelerator="H")
menu.add_command(label=n_("Highlight p&iles"), command=self.mHighlightPiles, accelerator="I")
- menu.add_command(label=n_("Find card"), command=self.mFindCard, accelerator="F")
+ menu.add_command(label=n_("&Find card"), command=self.mFindCard, accelerator="F3")
menu.add_separator()
menu.add_command(label=n_("&Demo"), command=self.mDemo, accelerator=m+"D")
menu.add_command(label=n_("Demo (&all games)"), command=self.mMixedDemo)
+ if USE_FREECELL_SOLVER:
+ menu.add_command(label=n_("&Solver (experimental)"), command=self.mSolver)
+ else:
+ menu.add_command(label=n_("&Solver (experimental)"), command=self.mSolver, state=Tkinter.DISABLED)
menu.add_separator()
- menu.add_command(label=n_("Piles description"), command=self.mStackDesk, accelerator="F2")
+ menu.add_command(label=n_("&Piles description"), command=self.mStackDesk, accelerator="F2")
if self.progress: self.progress.update(step=1)
@@ -478,10 +479,11 @@ class PysolMenubar(PysolMenubarActions):
submenu.add_checkbutton(label=n_("Shade &filled stacks"), variable=self.tkopt.shade_filled_stacks, command=self.mOptShadeFilledStacks)
submenu = MfxMenu(menu, label=n_("A&nimations"))
submenu.add_radiobutton(label=n_("&None"), variable=self.tkopt.animations, value=0, command=self.mOptAnimations)
- submenu.add_radiobutton(label=n_("&Timer based"), variable=self.tkopt.animations, value=2, command=self.mOptAnimations)
- submenu.add_radiobutton(label=n_("&Fast"), variable=self.tkopt.animations, value=1, command=self.mOptAnimations)
- submenu.add_radiobutton(label=n_("&Slow"), variable=self.tkopt.animations, value=3, command=self.mOptAnimations)
- submenu.add_radiobutton(label=n_("&Very slow"), variable=self.tkopt.animations, value=4, command=self.mOptAnimations)
+ submenu.add_radiobutton(label=n_("&Very fast"), variable=self.tkopt.animations, value=1, command=self.mOptAnimations)
+ submenu.add_radiobutton(label=n_("&Fast"), variable=self.tkopt.animations, value=2, command=self.mOptAnimations)
+ submenu.add_radiobutton(label=n_("&Medium"), variable=self.tkopt.animations, value=3, command=self.mOptAnimations)
+ submenu.add_radiobutton(label=n_("&Slow"), variable=self.tkopt.animations, value=4, command=self.mOptAnimations)
+ submenu.add_radiobutton(label=n_("V&ery slow"), variable=self.tkopt.animations, value=5, command=self.mOptAnimations)
submenu.add_separator()
submenu.add_checkbutton(label=n_("&Redeal animation"), variable=self.tkopt.redeal_animation, command=self.mRedealAnimation)
if Image:
@@ -527,15 +529,13 @@ class PysolMenubar(PysolMenubarActions):
ctrl = "Control-"
if sys.platform == "darwin": ctrl = "Command-"
self._bindKey("", "n", self.mNewGame)
- self._bindKey("", "g", self.mSelectGameDialog)
- self._bindKey("", "v", self.mSelectGameDialogWithPreview)
+ self._bindKey(ctrl, "w", self.mSelectGameDialog)
+ self._bindKey(ctrl, "v", self.mSelectGameDialogWithPreview)
self._bindKey(ctrl, "r", lambda e, self=self: self.mSelectRandomGame())
self._bindKey(ctrl, "m", self.mSelectGameById)
self._bindKey(ctrl, "n", self.mNewGameWithNextId)
self._bindKey(ctrl, "o", self.mOpen)
- ##self._bindKey("", "F3", self.mOpen) # undocumented
self._bindKey(ctrl, "s", self.mSave)
- ##self._bindKey("", "F2", self.mSaveAs) # undocumented
self._bindKey(ctrl, "q", self.mQuit)
self._bindKey("", "z", self.mUndo)
self._bindKey("", "BackSpace", self.mUndo) # undocumented
@@ -543,14 +543,14 @@ class PysolMenubar(PysolMenubarActions):
self._bindKey("", "r", self.mRedo)
self._bindKey(ctrl, "g", self.mRestart)
self._bindKey("", "space", self.mDeal) # undocumented
- self._bindKey("", "t", self.mStatus)
+ self._bindKey(ctrl, "y", self.mStatus)
self._bindKey(ctrl, "t", self.mTop10)
self._bindKey("", "h", self.mHint)
self._bindKey(ctrl, "h", self.mHint1) # undocumented
##self._bindKey("", "Shift_L", self.mHighlightPiles)
##self._bindKey("", "Shift_R", self.mHighlightPiles)
self._bindKey("", "i", self.mHighlightPiles)
- self._bindKey("", "f", self.mFindCard)
+ self._bindKey("", "F3", self.mFindCard)
self._bindKey(ctrl, "d", self.mDemo)
self._bindKey(ctrl, "e", self.mSelectCardsetDialog)
self._bindKey(ctrl, "b", self.mOptChangeCardback) # undocumented
@@ -593,19 +593,19 @@ class PysolMenubar(PysolMenubarActions):
#
def _bindKey(self, modifier, key, func):
- if 0 and not modifier and len(key) == 1:
- self.__keybindings[key.lower()] = func
- self.__keybindings[key.upper()] = func
- return
+## if 0 and not modifier and len(key) == 1:
+## self.__keybindings[key.lower()] = func
+## self.__keybindings[key.upper()] = func
+## return
+ if not modifier and len(key) == 1:
+ # ignore Ctrl/Shift/Alt
+ func = lambda e, func=func: e.state == 0 and func(e)
sequence = "<" + modifier + "KeyPress-" + key + ">"
- try:
+ bind(self.top, sequence, func)
+ if len(key) == 1 and key != key.upper():
+ key = key.upper()
+ sequence = "<" + modifier + "KeyPress-" + key + ">"
bind(self.top, sequence, func)
- if len(key) == 1 and key != key.upper():
- key = key.upper()
- sequence = "<" + modifier + "KeyPress-" + key + ">"
- bind(self.top, sequence, func)
- except:
- raise
def _keyPressHandler(self, event):
@@ -617,10 +617,10 @@ class PysolMenubar(PysolMenubarActions):
if event.char: # ignore Ctrl/Shift/etc.
self.game.demo.keypress = event.char
r = EVENT_HANDLED
- func = self.__keybindings.get(event.char)
- if func and (event.state & ~2) == 0:
- func(event)
- r = EVENT_HANDLED
+## func = self.__keybindings.get(event.char)
+## if func and (event.state & ~2) == 0:
+## func(event)
+## r = EVENT_HANDLED
return r
#
@@ -632,9 +632,11 @@ class PysolMenubar(PysolMenubarActions):
games = map(self.app.gdb.get, self.app.gdb.getGamesIdSortedByName())
##games = tuple(games)
###menu = MfxMenu(menu, label="Select &game")
- menu.add_command(label=n_("All &games..."), accelerator="G",
+ m = "Ctrl-"
+ if sys.platform == "darwin": m = "Cmd-"
+ menu.add_command(label=n_("All &games..."), accelerator=m+"W",
command=self.mSelectGameDialog)
- menu.add_command(label=n_("Playable pre&view..."), accelerator="V",
+ menu.add_command(label=n_("Playable pre&view..."), accelerator=m+"V",
command=self.mSelectGameDialogWithPreview)
if not SELECT_GAME_MENU:
return
@@ -779,13 +781,19 @@ class PysolMenubar(PysolMenubarActions):
gi = games[i]
columnbreak = i > 0 and (i % cb) == 0
if short_name:
- label = gi.short_name
+ label = gettext(gi.short_name)
else:
- label = gi.name
- menu.add_radiobutton(command=command, variable=variable,
- columnbreak=columnbreak,
- value=gi.id, label=label, name=None)
-
+ label = gettext(gi.name)
+## menu.add_radiobutton(command=command, variable=variable,
+## columnbreak=columnbreak,
+## value=gi.id, label=label, name=None)
+ # optimized by inlining
+ menu.tk.call((menu._w, 'add', 'radiobutton') +
+ menu._options({'command': command,
+ 'variable': variable,
+ 'columnbreak': columnbreak,
+ 'value': gi.id,
+ 'label': label}))
#
# Select Game menu actions
@@ -1149,7 +1157,6 @@ class PysolMenubar(PysolMenubarActions):
self._cancelDrag()
self.game.endGame(bookmark=1)
self.game.quitGame(bookmark=1)
- self.app.opt.games_geometry = {} # clear saved games geometry
def _mOptCardback(self, index):
if self._cancelDrag(break_pause=False): return
@@ -1171,10 +1178,6 @@ class PysolMenubar(PysolMenubarActions):
def mOptChangeCardback(self, *event):
self._mOptCardback(self.app.cardset.backindex + 1)
-## def mOptTableTile(self, *event):
-## if self._cancelDrag(break_pause=False): return
-## self._mOptTableTile(self.tkopt.tabletile.get())
-
def mOptChangeTableTile(self, *event):
if self._cancelDrag(break_pause=False): return
n = self.app.tabletile_manager.len()
@@ -1337,4 +1340,3 @@ class PysolMenubar(PysolMenubarActions):
else:
if self._cancelDrag(break_pause=True): return
self.game.showStackDesc()
-
diff --git a/pysollib/tk/selectgame.py b/pysollib/tk/selectgame.py
index da46e929..8eb95fd1 100644
--- a/pysollib/tk/selectgame.py
+++ b/pysollib/tk/selectgame.py
@@ -437,7 +437,7 @@ class SelectGameDialogWithPreview(SelectGameDialog):
destruct(self.preview_app)
self.preview_app = None
- def updatePreview(self, gameid, animations=5):
+ def updatePreview(self, gameid, animations=10):
if gameid == self.preview_key:
return
self.deletePreview()
@@ -504,7 +504,7 @@ class SelectGameDialogWithPreview(SelectGameDialog):
#
self.preview_app.audio = self.app.audio
if self.app.opt.animations:
- self.preview_app.opt.animations = 5
+ self.preview_app.opt.animations = 10
else:
self.preview_app.opt.animations = 0
# save seed
@@ -562,13 +562,12 @@ class SelectGameDialogWithPreview(SelectGameDialog):
('percent', percent),
):
title_label, text_label = self.info_labels[n]
- if t == '':
+ if t in ('', None):
title_label.grid_remove()
text_label.grid_remove()
else:
title_label.grid()
text_label.grid()
text_label.config(text=t)
- #self.info_labels[n].config(text=t)
diff --git a/pysollib/tk/solverdialog.py b/pysollib/tk/solverdialog.py
new file mode 100644
index 00000000..d7a7b9c9
--- /dev/null
+++ b/pysollib/tk/solverdialog.py
@@ -0,0 +1,333 @@
+##---------------------------------------------------------------------------##
+##
+## PySol -- a Python Solitaire game
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; see the file COPYING.
+## If not, write to the Free Software Foundation, Inc.,
+## 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+##
+##---------------------------------------------------------------------------##
+
+__all__ = [
+ #'SolverDialog',
+ 'create_solver_dialog',
+ 'connect_game_solver_dialog',
+ 'destroy_solver_dialog',
+ 'reset_solver_dialog',
+ ]
+
+# imports
+import os, sys
+import Tkinter
+import traceback
+
+# PySol imports
+from pysollib.mfxutil import destruct, kwdefault, KwStruct, Struct
+from pysollib.settings import PACKAGE
+
+# Toolkit imports
+from tkconst import EVENT_HANDLED, EVENT_PROPAGATE
+from tkwidget import MfxDialog
+from tkutil import bind, unbind_destroy
+
+gettext = _
+
+
+# /***********************************************************************
+# //
+# ************************************************************************/
+
+class SolverDialog(MfxDialog):
+
+ def __init__(self, parent, app, **kw):
+ self.parent = parent
+ self.app = app
+ title = PACKAGE+' - FreeCell Solver'
+ kw = self.initKw(kw)
+ MfxDialog.__init__(self, parent, title, kw.resizable, kw.default)
+ top_frame, bottom_frame = self.createFrames(kw)
+ self.createBitmaps(top_frame, kw)
+ #
+ self.solving_methods = {
+ 'A*': 'a-star',
+ 'Breadth-First Search': 'bfs',
+ 'Depth-First Search': 'dfs', # default
+ 'A randomized DFS': 'random-dfs',
+ '"Soft" DFS': 'soft-dfs',
+ }
+ self.games = {} # key: gamename; value: gameid
+
+ #
+ frame = Tkinter.Frame(top_frame)
+ frame.pack(expand=True, fill='both', padx=4, pady=4)
+ frame.columnconfigure(1, weight=1)
+
+ #
+ row = 0
+ Tkinter.Label(frame, text=_('Game:'), anchor='w'
+ ).grid(row=row, column=0, sticky='ew', padx=2, pady=2)
+ games = app.getGamesForSolver()
+ gamenames = ['']
+ for id in games:
+ name = app.getGameTitleName(id)
+ name = gettext(name)
+ gamenames.append(name)
+ self.games[name] = id
+ gamenames.sort()
+ self.gamenames = gamenames
+ self.games_var = var = Tkinter.StringVar()
+ om = Tkinter.OptionMenu(frame, var, command=self.gameSelected,
+ *gamenames)
+ om.grid(row=row, column=1, sticky='ew', padx=2, pady=2)
+ n = len(gamenames)
+ cb_max = int(self.top.winfo_screenheight()/23)
+ cb_max = n / (n/cb_max+1)
+ for i in xrange(cb_max, n, cb_max):
+ om['menu'].entryconfig(i, columnbreak=True)
+
+ #
+ row += 1
+ Tkinter.Label(frame, text=_('Solving method:'), anchor='w'
+ ).grid(row=row, column=0, sticky='ew', padx=2, pady=2)
+ ##sm = self.solving_methods.values()
+ ##sm.sort()
+ sm = ['A*', 'Breadth-First Search', 'Depth-First Search',
+ 'A randomized DFS', '"Soft" DFS']
+ self.solving_method_var = var = Tkinter.StringVar()
+ var.set('Depth-First Search')
+ om = Tkinter.OptionMenu(frame, var, *sm)
+ om.grid(row=row, column=1, sticky='ew', padx=2, pady=2)
+
+ #
+ row += 1
+ Tkinter.Label(frame, text=_('Preset:'), anchor='w'
+ ).grid(row=row, column=0, sticky='ew', padx=2, pady=2)
+ presets = [
+ 'none',
+ 'abra-kadabra',
+ 'cool-jives',
+ 'crooked-nose',
+ 'fools-gold',
+ 'good-intentions',
+ 'hello-world',
+ 'john-galt-line',
+ 'rin-tin-tin',
+ 'yellow-brick-road',
+ ]
+ self.presets = presets
+ self.preset_var = var = Tkinter.StringVar()
+ var.set('none')
+ om = Tkinter.OptionMenu(frame, var, *presets)
+ om.grid(row=row, column=1, sticky='ew', padx=2, pady=2)
+
+ #
+ row += 1
+ self.max_iters_var = Tkinter.IntVar()
+ self.max_iters_var.set(10e4)
+ Tkinter.Label(frame, text=_('Max iterations:'), anchor='w'
+ ).grid(row=row, column=0, sticky='ew', padx=2, pady=2)
+ spin = Tkinter.Spinbox(frame, bg='white', from_=1000, to=10e6,
+ increment=1000, textvariable=self.max_iters_var)
+ spin.grid(row=row, column=1, sticky='w', padx=2, pady=2)
+
+ #
+ row += 1
+ self.max_depth_var = Tkinter.IntVar()
+ self.max_depth_var.set(1000)
+ Tkinter.Label(frame, text=_('Max depth:'), anchor='w'
+ ).grid(row=row, column=0, sticky='ew', padx=2, pady=2)
+ spin = Tkinter.Spinbox(frame, bg='white', from_=100, to=10000,
+ increment=100, textvariable=self.max_depth_var)
+ spin.grid(row=row, column=1, sticky='w', padx=2, pady=2)
+
+ #
+ row += 1
+ self.progress_var = Tkinter.BooleanVar()
+ self.progress_var.set(True)
+ w = Tkinter.Checkbutton(frame, variable=self.progress_var,
+ text=_('Show progress'), anchor='w')
+ w.grid(row=row, column=0, columnspan=2, sticky='ew', padx=2, pady=2)
+
+ #
+ label_frame = Tkinter.LabelFrame(top_frame, text=_('Progress'))
+ label_frame.pack(expand=True, fill='both', padx=6, pady=2)
+ #label_frame.columnconfigure(0, weight=1)
+ label_frame.columnconfigure(1, weight=1)
+
+ #
+ frow = 0
+ Tkinter.Label(label_frame, text=_('Iteration:'), anchor='w'
+ ).grid(row=frow, column=0, sticky='ew', padx=4, pady=2)
+ lb = Tkinter.Label(label_frame, anchor='w')
+ lb.grid(row=frow, column=1, sticky='ew', padx=4, pady=2)
+ self.iter_label = lb
+ frow += 1
+ Tkinter.Label(label_frame, text=_('Depth:'), anchor='w'
+ ).grid(row=frow, column=0, sticky='ew', padx=4, pady=2)
+ lb = Tkinter.Label(label_frame, anchor='w')
+ lb.grid(row=frow, column=1, sticky='ew', padx=4, pady=2)
+ self.depth_label = lb
+ frow += 1
+ Tkinter.Label(label_frame, text=_('Stored-States:'), anchor='w'
+ ).grid(row=frow, column=0, sticky='ew', padx=4, pady=2)
+ lb = Tkinter.Label(label_frame, anchor='w')
+ lb.grid(row=frow, column=1, sticky='ew', padx=4, pady=2)
+ self.states_label = lb
+
+ #
+ lb = Tkinter.Label(top_frame, anchor='w')
+ lb.pack(expand=True, fill='x', padx=6, pady=4)
+ self.result_label = lb
+
+ #
+ focus = self.createButtons(bottom_frame, kw)
+ self.mainloop(focus, kw.timeout, transient=False)
+
+ self.start_button = self.buttons[0]
+ self.play_button = self.buttons[1]
+
+ #
+ self._reset()
+ self.connectGame(self.app.game)
+
+ def initKw(self, kw):
+ strings=[_('&Start'), _('&Play'), _('&New'), _('&Close'),]
+ kw = KwStruct(kw,
+ strings=strings,
+ default=0,
+ )
+ return MfxDialog.initKw(self, kw)
+
+ def mDone(self, button):
+ if button == 0:
+ self.startSolving()
+ elif button == 1:
+ self.startPlay()
+ elif button == 2:
+ self.app.menubar.mNewGame()
+ elif button == 3:
+ global solver_dialog
+ solver_dialog = None
+ self.destroy()
+ return EVENT_HANDLED
+
+ def mCancel(self, *event):
+ return self.mDone(3)
+
+ def wmDeleteWindow(self, *event):
+ return self.mDone(3)
+
+ def gameSelected(self, *event):
+ name = self.games_var.get()
+ if not name:
+ return
+ id = self.games[name]
+ self.app.menubar._mSelectGame(id)
+
+ def connectGame(self, game):
+ name = self.app.getGameTitleName(game.id)
+ name = gettext(name)
+ if name in self.gamenames:
+ self.start_button.config(state='normal')
+ i = self.gamenames.index(name)
+ self.games_var.set(name)
+ else:
+ self.start_button.config(state='disabled')
+ self.games_var.set('')
+ self.play_button.config(state='disabled')
+
+ def _reset(self):
+ self.play_button.config(state='disabled')
+ self.setText(iter='', depth='', states='')
+ self.result_label['text'] = ''
+ self.top.update_idletasks()
+
+ def reset(self):
+ self.play_button.config(state='disabled')
+
+ def startSolving(self):
+ self._reset()
+ game = self.app.game
+ solver = game.Solver_Class(game, self) # create solver instance
+ game.solver = solver
+ method = self.solving_method_var.get()
+ method = self.solving_methods[method]
+ preset = self.preset_var.get()
+ max_iters = self.max_iters_var.get()
+ max_depth = self.max_depth_var.get()
+ progress = self.progress_var.get()
+ solver.config(method=method, preset=preset, max_iters=max_iters,
+ max_depth=max_depth, progress=progress)
+ solver.computeHints()
+ hints_len = len(solver.hints)-1
+ if hints_len > 0:
+ self.result_label['text'] = _('This game is solveable in %s moves.') % hints_len
+ self.play_button.config(state='normal')
+ else:
+ self.result_label['text'] = _('I could not solve this game.')
+ self.play_button.config(state='disabled')
+
+ def startPlay(self):
+ self.play_button.config(state='disabled')
+ self.app.top.tkraise()
+ self.app.top.update_idletasks()
+ self.app.top.update()
+ self.app.game.startDemo(level=3)
+
+ def setText(self, **kw):
+ if 'iter' in kw:
+ self.iter_label['text'] = kw['iter']
+ if 'depth' in kw:
+ self.depth_label['text'] = kw['depth']
+ if 'states' in kw:
+ self.states_label['text'] = kw['states']
+ self.top.update_idletasks()
+
+
+
+solver_dialog = None
+
+def create_solver_dialog(parent, game):
+ global solver_dialog
+ try:
+ solver_dialog.top.wm_deiconify()
+ solver_dialog.top.tkraise()
+ except:
+ ##traceback.print_exc()
+ solver_dialog = SolverDialog(parent, game)
+
+def connect_game_solver_dialog(game):
+ try:
+ solver_dialog.connectGame(game)
+ except:
+ pass
+
+def destroy_solver_dialog():
+ global solver_dialog
+ try:
+ solver_dialog.destroy()
+ except:
+ ##traceback.print_exc()
+ pass
+ solver_dialog = None
+
+
+def reset_solver_dialog():
+ if solver_dialog:
+ try:
+ solver_dialog.reset()
+ except:
+ ##traceback.print_exc()
+ pass
+
diff --git a/pysollib/tk/soundoptionsdialog.py b/pysollib/tk/soundoptionsdialog.py
index da9fdcc6..dca2ecea 100644
--- a/pysollib/tk/soundoptionsdialog.py
+++ b/pysollib/tk/soundoptionsdialog.py
@@ -121,7 +121,7 @@ class SoundOptionsDialog(MfxDialog):
if app.audio.CAN_PLAY_MUSIC: # and app.startup_opt.sound_mode > 0:
row += 1
w = Tkinter.Label(frame, text=_('Sample volume:'))
- w.grid(row=row, column=0, sticky='ew')
+ w.grid(row=row, column=0, sticky='w', padx=5)
w = Tkinter.Scale(frame, from_=0, to=128, resolution=1,
orient='horizontal', takefocus=0,
length="3i", #label=_('Sample volume'),
@@ -134,7 +134,7 @@ class SoundOptionsDialog(MfxDialog):
orient='horizontal', takefocus=0,
length="3i", #label=_('Music volume'),
variable=self.music_volume)
- w.grid(row=row, column=1, sticky='w', padx=5)
+ w.grid(row=row, column=1, sticky='ew', padx=5)
else:
# remove "Apply" button
diff --git a/pysollib/tk/statusbar.py b/pysollib/tk/statusbar.py
index 89dfdc87..c8c80910 100644
--- a/pysollib/tk/statusbar.py
+++ b/pysollib/tk/statusbar.py
@@ -116,8 +116,11 @@ class MfxStatusbar:
def updateText(self, **kw):
for k, v in kw.items():
label = getattr(self, k + "_label")
- #label["text"] = str(v)
- label["text"] = unicode(v)
+ text = unicode(v)
+ width = label['width']
+ if width and len(text) > width:
+ label['width'] = len(text)
+ label["text"] = text
def configLabel(self, name, **kw):
label = getattr(self, name + "_label")
diff --git a/pysollib/tk/tkcanvas.py b/pysollib/tk/tkcanvas.py
index 2c9e7856..1e33f44a 100644
--- a/pysollib/tk/tkcanvas.py
+++ b/pysollib/tk/tkcanvas.py
@@ -370,21 +370,14 @@ class MfxCanvas(Tkinter.Canvas):
def _substitute(self, *args):
e = Tkinter.Event()
- e.x = int(args[0])
- e.y = int(args[1])
+ try:
+ # Tk changed behavior in 8.4.2, returning "??" rather more often.
+ e.x = int(args[0])
+ except ValueError:
+ e.x = args[0]
+ try:
+ e.y = int(args[1])
+ except ValueError:
+ e.y = args[1]
return (e,)
-
- #
- # debug
- #
-
- def update(self):
- ##import mfxutil; print mfxutil.callername()
- # ??????
- Tkinter.Canvas.update(self)
-
- def update_idletasks(self):
- ##import mfxutil; print mfxutil.callername()
- Tkinter.Canvas.update_idletasks(self)
-
diff --git a/pysollib/tk/tkstats.py b/pysollib/tk/tkstats.py
index cb1d14d3..f3b4fb15 100644
--- a/pysollib/tk/tkstats.py
+++ b/pysollib/tk/tkstats.py
@@ -38,7 +38,9 @@ __all__ = ['SingleGame_StatsDialog',
'FullLog_StatsDialog',
'SessionLog_StatsDialog',
'Status_StatsDialog',
- 'Top_StatsDialog']
+ 'Top_StatsDialog',
+ 'ProgressionDialog',
+ ]
# imports
import os, string, sys, types
@@ -49,7 +51,7 @@ import Tkinter, tkFont
from pysollib.mfxutil import destruct, Struct, kwdefault, KwStruct
from pysollib.mfxutil import format_time
##from pysollib.util import *
-from pysollib.stats import PysolStatsFormatter
+from pysollib.stats import PysolStatsFormatter, ProgressionFormatter
from pysollib.settings import TOP_TITLE
# Toolkit imports
@@ -720,7 +722,7 @@ class Top_StatsDialog(MfxDialog):
self.createBitmaps(top_frame, kw)
frame = Tkinter.Frame(top_frame)
- frame.pack(expand=Tkinter.YES, fill=Tkinter.BOTH, padx=5, pady=10)
+ frame.pack(expand=Tkinter.YES, fill=Tkinter.BOTH, padx=10, pady=10)
frame.columnconfigure(0, weight=1)
if (player in app.stats.games_stats and
@@ -797,3 +799,266 @@ class Top_StatsDialog(MfxDialog):
separatorwidth=2,
)
return MfxDialog.initKw(self, kw)
+
+
+# /***********************************************************************
+# //
+# ************************************************************************/
+
+class ProgressionDialog(MfxDialog):
+ def __init__(self, parent, title, app, player, gameid, **kw):
+
+ font_name = app.getFont('default')
+ font = tkFont.Font(parent, font_name)
+ tkfont = tkFont.Font(parent, font)
+ font_metrics = font.metrics()
+ measure = tkfont.measure
+ self.text_height = font_metrics['linespace']
+ self.text_width = measure('XX.XX.XX')
+
+ self.items = []
+ self.formatter = ProgressionFormatter(app, player, gameid)
+
+ kw = self.initKw(kw)
+ MfxDialog.__init__(self, parent, title, kw.resizable, kw.default)
+ top_frame, bottom_frame = self.createFrames(kw)
+ self.createBitmaps(top_frame, kw)
+
+ frame = Tkinter.Frame(top_frame)
+ frame.pack(expand=True, fill='both', padx=5, pady=10)
+ frame.columnconfigure(0, weight=1)
+
+ # constants
+ self.canvas_width, self.canvas_height = 600, 250
+ if parent.winfo_screenwidth() < 800 or \
+ parent.winfo_screenheight() < 600:
+ self.canvas_width, self.canvas_height = 400, 200
+ self.xmargin, self.ymargin = 10, 10
+ self.graph_dx, self.graph_dy = 10, 10
+ self.played_color = '#ff7ee9'
+ self.won_color = '#00dc28'
+ self.percent_color = 'blue'
+ # create canvas
+ self.canvas = canvas = Tkinter.Canvas(frame, bg='#dfe8ff',
+ highlightthickness=1,
+ highlightbackground='black',
+ width=self.canvas_width,
+ height=self.canvas_height)
+ canvas.pack(side='left', padx=5)
+ #
+ dir = os.path.join('images', 'stats')
+ try:
+ fn = app.dataloader.findImage('progression', dir)
+ self.bg_image = loadImage(fn)
+ canvas.create_image(0, 0, image=self.bg_image, anchor='nw')
+ except:
+ pass
+ #
+ tw = max(measure(_('Games/day')),
+ measure(_('Games/week')),
+ measure(_('% won')))
+ self.left_margin = self.xmargin+tw/2
+ self.right_margin = self.xmargin+tw/2
+ self.top_margin = 15+self.text_height
+ self.bottom_margin = 15+self.text_height+10+self.text_height
+ #
+ x0, y0 = self.left_margin, self.canvas_height-self.bottom_margin
+ x1, y1 = self.canvas_width-self.right_margin, self.top_margin
+ canvas.create_rectangle(x0, y0, x1, y1, fill='white')
+ # horizontal axis
+ canvas.create_line(x0, y0, x1, y0, width=3)
+
+ # left vertical axis
+ canvas.create_line(x0, y0, x0, y1, width=3)
+ t = _('Games/day')
+ self.games_text_id = canvas.create_text(x0-4, y1-4, anchor='s', text=t)
+
+ # right vertical axis
+ canvas.create_line(x1, y0, x1, y1, width=3)
+ canvas.create_text(x1+4, y1-4, anchor='s', text=_('% won'))
+
+ # caption
+ d = self.text_height
+ x, y = self.xmargin, self.canvas_height-self.ymargin
+ id = canvas.create_rectangle(x, y, x+d, y-d, outline='black',
+ fill=self.played_color)
+ x += d+5
+ canvas.create_text(x, y, anchor='sw', text=_('Played'))
+ x += measure(_('Played'))+20
+ id = canvas.create_rectangle(x, y, x+d, y-d, outline='black',
+ fill=self.won_color)
+ x += d+5
+ canvas.create_text(x, y, anchor='sw', text=_('Won'))
+ x += measure(_('Won'))+20
+ id = canvas.create_rectangle(x, y, x+d, y-d, outline='black',
+ fill=self.percent_color)
+ x += d+5
+ canvas.create_text(x, y, anchor='sw', text=_('% won'))
+
+ # right frame
+ right_frame = Tkinter.Frame(frame)
+ right_frame.pack(side='left', fill='x', padx=5)
+ self.all_games_variable = var = Tkinter.StringVar()
+ var.set('all')
+ b = Tkinter.Radiobutton(right_frame, text=_('All games'),
+ variable=var, value='all',
+ command=self.updateGraph,
+ justify='left', anchor='w'
+ )
+ b.pack(fill='x', expand=True, padx=3, pady=1)
+ b = Tkinter.Radiobutton(right_frame, text=_('Current game'),
+ variable=var, value='current',
+ command=self.updateGraph,
+ justify='left', anchor='w'
+ )
+ b.pack(fill='x', expand=True, padx=3, pady=1)
+ if Tkinter.TkVersion >= 8.4:
+ label_frame = Tkinter.LabelFrame(right_frame, text=_('Statistics for'))
+ else:
+ label_frame = Tkinter.Frame(right_frame)
+ label_frame.pack(side='top', fill='x', pady=10)
+ self.variable = var = Tkinter.StringVar()
+ var.set('week')
+ for v, t in (
+ ('week', _('Last 7 days')),
+ ('month', _('Last month')),
+ ('year', _('Last year')),
+ ('all', _('All time')),
+ ):
+ b = Tkinter.Radiobutton(label_frame, text=t, variable=var, value=v,
+ command=self.updateGraph,
+ justify='left', anchor='w'
+ )
+ b.pack(fill='x', expand=True, padx=3, pady=1)
+ if Tkinter.TkVersion >= 8.4:
+ label_frame = Tkinter.LabelFrame(right_frame, text=_('Show graphs'))
+ else:
+ label_frame = Tkinter.Frame(right_frame)
+ label_frame.pack(side='top', fill='x')
+ self.played_graph_var = Tkinter.BooleanVar()
+ self.played_graph_var.set(True)
+ b = Tkinter.Checkbutton(label_frame, text=_('Played'),
+ command=self.updateGraph,
+ variable=self.played_graph_var,
+ justify='left', anchor='w'
+ )
+ b.pack(fill='x', expand=True, padx=3, pady=1)
+ self.won_graph_var = Tkinter.BooleanVar()
+ self.won_graph_var.set(True)
+ b = Tkinter.Checkbutton(label_frame, text=_('Won'),
+ command=self.updateGraph,
+ variable=self.won_graph_var,
+ justify='left', anchor='w'
+ )
+ b.pack(fill='x', expand=True, padx=3, pady=1)
+ self.percent_graph_var = Tkinter.BooleanVar()
+ self.percent_graph_var.set(True)
+ b = Tkinter.Checkbutton(label_frame, text=_('% won'),
+ command=self.updateGraph,
+ variable=self.percent_graph_var,
+ justify='left', anchor='w'
+ )
+ b.pack(fill='x', expand=True, padx=3, pady=1)
+
+ self.updateGraph()
+
+ focus = self.createButtons(bottom_frame, kw)
+ self.mainloop(focus, kw.timeout)
+
+
+ def initKw(self, kw):
+ kw = KwStruct(kw, strings=(_('&OK'),), default=0, separatorwidth=2)
+ return MfxDialog.initKw(self, kw)
+
+
+ def updateGraph(self, *args):
+ interval = self.variable.get()
+ canvas = self.canvas
+ if self.items:
+ canvas.delete(*self.items)
+ self.items = []
+
+ all_games = (self.all_games_variable.get() == 'all')
+ result = self.formatter.getResults(interval, all_games)
+
+ if interval in ('week', 'month'):
+ t = _('Games/day')
+ else:
+ t = _('Games/week')
+ canvas.itemconfig(self.games_text_id, text=t)
+
+ graph_width = self.canvas_width-self.left_margin-self.right_margin
+ graph_height = self.canvas_height-self.top_margin-self.bottom_margin
+ dx = (graph_width-2*self.graph_dx)/(len(result)-1)
+ graph_dx = (graph_width-(len(result)-1)*dx)/2
+ dy = (graph_height-self.graph_dy)/5
+ x0, y0 = self.left_margin, self.canvas_height-self.bottom_margin
+ x1, y1 = self.canvas_width-self.right_margin, self.top_margin
+ td = self.text_height/2
+
+ # vertical scale
+ x = x0+graph_dx
+ xx = -100
+ for res in result:
+ if res[0] is not None and x > xx+self.text_width+4:
+ ##id = canvas.create_line(x, y0, x, y0-5, width=3)
+ ##self.items.append(id)
+ id = canvas.create_line(x, y0, x, y1, stipple='gray50')
+ self.items.append(id)
+ id = canvas.create_text(x, y0+td, anchor='n', text=res[0])
+ self.items.append(id)
+ xx = x
+ else:
+ id = canvas.create_line(x, y0, x, y0-3, width=1)
+ self.items.append(id)
+ x += dx
+
+ # horizontal scale
+ max_games = max([i[1] for i in result])
+ games_delta = max_games/5+1
+ percent = 0
+ games = 0
+ for y in range(y0, y1, -dy):
+ if y != y0:
+ id = canvas.create_line(x0, y, x1, y, stipple='gray50')
+ self.items.append(id)
+ id = canvas.create_text(x0-td, y, anchor='e', text=str(games))
+ self.items.append(id)
+ id = canvas.create_text(x1+td, y, anchor='w', text=str(percent))
+ self.items.append(id)
+ games += games_delta
+ percent += 20
+
+ # draw result
+ games_resolution = float(dy)/games_delta
+ percent_resolution = float(dy)/20
+ played_coords = []
+ won_coords = []
+ percent_coords = []
+ x = x0+graph_dx
+ for res in result:
+ played, won = res[1], res[2]
+ y = y0 - int(games_resolution*played)
+ played_coords += [x,y]
+ y = y0 - int(games_resolution*won)
+ won_coords += [x,y]
+ if played > 0:
+ percent = int(100.*won/played)
+ else:
+ percent = 0
+ y = y0 - int(percent_resolution*percent)
+ percent_coords += [x,y]
+ x += dx
+ if self.played_graph_var.get():
+ id = canvas.create_line(fill=self.played_color, width=3,
+ *played_coords)
+ self.items.append(id)
+ if self.won_graph_var.get():
+ id = canvas.create_line(fill=self.won_color, width=3,
+ *won_coords)
+ self.items.append(id)
+ if self.percent_graph_var.get():
+ id = canvas.create_line(fill=self.percent_color, width=3,
+ *percent_coords)
+ self.items.append(id)
+
diff --git a/pysollib/tk/tkwidget.py b/pysollib/tk/tkwidget.py
index 289d40d7..dfc116ad 100644
--- a/pysollib/tk/tkwidget.py
+++ b/pysollib/tk/tkwidget.py
@@ -507,16 +507,11 @@ class MfxScrolledCanvas:
if i == 0:
self.canvas.config(bg=tile.color)
##app.top.config(bg=tile.color)
- color = None
else:
self.canvas.config(bg=app.top_bg)
##app.top.config(bg=app.top_bg)
- color = tile.text_color
- if app.opt.use_default_text_color:
- self.canvas.setTextColor(color)
- else:
- self.canvas.setTextColor(app.opt.colors['text'])
+ self.canvas.setTextColor(app.opt.colors['text'])
return True
diff --git a/pysollib/tk/toolbar.py b/pysollib/tk/toolbar.py
index 1d74927d..c6aafbed 100644
--- a/pysollib/tk/toolbar.py
+++ b/pysollib/tk/toolbar.py
@@ -33,7 +33,7 @@
##
##---------------------------------------------------------------------------##
-__all__ = ['PysolToolbar'] #, 'TOOLBAR_BUTTONS']
+__all__ = ['PysolToolbar']
# imports
import os, sys, types, Tkinter
@@ -245,6 +245,7 @@ class PysolToolbar(PysolToolbarActions):
widget.hide()
#
prev_visible = None
+ last_visible = None
for w in self._widgets:
if w.__class__ is ToolbarSeparator:
if prev_visible is None or prev_visible.__class__ is ToolbarSeparator:
@@ -256,6 +257,10 @@ class PysolToolbar(PysolToolbarActions):
prev_visible.hide()
if w.visible:
prev_visible = w
+ if not isinstance(w, ToolbarLabel):
+ last_visible = w
+ if isinstance(last_visible, ToolbarSeparator):
+ last_visible.hide()
# util
def _loadImage(self, name):
diff --git a/pysollib/winsystems/common.py b/pysollib/winsystems/common.py
index a2bb6f42..4397d313 100644
--- a/pysollib/winsystems/common.py
+++ b/pysollib/winsystems/common.py
@@ -42,13 +42,13 @@ def init_tile(app, top, theme):
traceback.print_exc()
pass
-def set_theme(top, theme):
+def set_theme(app, top, theme):
# set theme
style = Tile.Style(top)
all_themes = style.theme_names()
if theme not in all_themes:
print >> sys.stderr, 'WARNING: invalid theme name:', theme
- theme = 'default'
+ theme = app.opt.default_tile_theme
style.theme_use(theme)
@@ -91,7 +91,7 @@ class baseInitRootWindow:
elif USE_TILE:
theme = app.opt.tile_theme
init_tile(app, root, theme)
- set_theme(root, theme)
+ set_theme(app, root, theme)
else:
pass
diff --git a/pysollib/winsystems/x11.py b/pysollib/winsystems/x11.py
index 1a5ac612..183e1c0c 100644
--- a/pysollib/winsystems/x11.py
+++ b/pysollib/winsystems/x11.py
@@ -72,6 +72,8 @@ class initRootWindow(baseInitRootWindow):
app.opt.fonts['default'] = fn
root.option_add('*Menu.borderWidth', 1, 60)
root.option_add('*Menu.activeBorderWidth', 1, 60)
+ if app.opt.tile_theme == 'clam':
+ root.wm_minsize(550, 360)
#
else:
root.option_add('*Entry.background', 'white', 60)
diff --git a/scripts/all_games.py b/scripts/all_games.py
index de790a3a..9d4ca690 100755
--- a/scripts/all_games.py
+++ b/scripts/all_games.py
@@ -140,8 +140,8 @@ def all_games(sort_by='id'):
gt = 'French (%s)' % GAME_BY_TYPE[gi.si.game_type]
name = gi.name.encode('utf-8')
altnames = '
'.join(gi.altnames).encode('utf-8')
- if 1 and os.path.exists(os.path.join(rules_dir, rules_fn)):
- fn = '../data/html/rules/'+rules_fn
+ fn = os.path.join(rules_dir, rules_fn)
+ if 1 and os.path.exists(fn):
print '''%s |
%s
| %s | %s |
@@ -152,13 +152,19 @@ def all_games(sort_by='id'):
print ''
def create_html(sort_by):
- print ''
+ print '''
+
+ PySolFC - List of solitaire games
+
+
+
+'''
print 'Total games: %d' % len(GAME_DB.getGamesIdSortedById())
print 'Categories
'
by_category()
print 'Types
'
by_type()
- print 'All games
'
+ #print 'All games
'
all_games(sort_by)
print ''
@@ -220,6 +226,9 @@ def plain_text():
gi = GAME_DB.get(id)
if gi.category == GI.GC_FRENCH:
##print str(gi.gameclass)
+ ##gc = gi.gameclass
+ ##h = gc.Hint_Class is None and 'None' or gc.Hint_Class.__name__
+ ##print gi.name.encode('utf-8'), h
print gi.name.encode('utf-8')
for n in gi.altnames:
print n.encode('utf-8')
@@ -233,6 +242,8 @@ if len(sys.argv) < 2 or sys.argv[1] == 'html':
sort_by = 'id'
if len(sys.argv) > 2:
sort_by = sys.argv[2]
+ if len(sys.argv) > 3:
+ rules_dir = sys.argv[3]
create_html(sort_by)
elif sys.argv[1] == 'gettext':
get_text()
diff --git a/scripts/build.bat b/scripts/build.bat
index 084532b7..f44b92db 100755
--- a/scripts/build.bat
+++ b/scripts/build.bat
@@ -4,7 +4,7 @@ cd ..
rm -rf dist
mkdir dist
cp -r locale dist
-cp fc-solve.exe dist
+cp -r freecell-solver dist
cp smpeg.dll ogg.dll vorbis.dll vorbisfile.dll dist
python setup.py py2exe
cp -r d:\Python\tcl\tile0.7.8 dist\tcl
diff --git a/setup.py b/setup.py
index 1625e726..266a7a6b 100644
--- a/setup.py
+++ b/setup.py
@@ -4,6 +4,7 @@
import os
from distutils.core import setup
from pysollib.settings import FC_VERSION as VERSION
+from pysollib.settings import PACKAGE_URL
if os.name == 'nt':
import py2exe
@@ -14,7 +15,7 @@ elif os.name == 'nt':
else:
data_dir = 'data'
-datas = [
+ddirs = [
'html',
'images',
'sound',
@@ -25,11 +26,11 @@ datas = [
]
for s in file('MANIFEST.in'):
if s.startswith('graft data/cardset-'):
- datas.append(s[11:].strip())
+ ddirs.append(s[11:].strip())
data_files = []
-for d in datas:
+for d in ddirs:
for root, dirs, files in os.walk(os.path.join('data', d)):
if root.find('.svn') >= 0:
continue
@@ -57,7 +58,7 @@ integrated HTML help browser, and it\'s free Open Source software.
kw = {
'name' : 'PySolFC',
'version' : VERSION,
- 'url' : 'http://sourceforge.net/projects/pysolfc/',
+ 'url' : PACKAGE_URL,
'author' : 'Skomoroh',
'author_email' : 'skomoroh@gmail.com',
'description' : 'PySol - a solitaire game collection',