diff --git a/MANIFEST.in b/MANIFEST.in index a156c3a0..676d073d 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -24,7 +24,7 @@ graft html-src ## data - images ## #graft data/images -recursive-include data/images *.gif *.png +recursive-include data/images *.gif *.png *.jpg graft data/tiles include data/pysol.xbm data/pysol.xpm data/pysol.ico ## diff --git a/data/glade-translations b/data/glade-translations index 0d636dd9..22f37f1d 100644 --- a/data/glade-translations +++ b/data/glade-translations @@ -38,7 +38,6 @@ gchar *s = N_("Highlight piles:"); gchar *s = N_("Highlight cards:"); gchar *s = N_("Highlight same rank:"); gchar *s = N_("Set colors"); -gchar *s = N_("Text foreground:"); gchar *s = N_("Highlight piles:"); gchar *s = N_("Highlight cards 1:"); gchar *s = N_("Highlight cards 2:"); @@ -54,6 +53,7 @@ gchar *s = N_("Change..."); gchar *s = N_("Change..."); gchar *s = N_("Change..."); gchar *s = N_("Change..."); +gchar *s = N_("Text foreground:"); gchar *s = N_("Set font"); gchar *s = N_("HTML: "); gchar *s = N_("Small: "); diff --git a/data/images/stats/progression.jpg b/data/images/stats/progression.jpg new file mode 100644 index 00000000..336d1f1c Binary files /dev/null and b/data/images/stats/progression.jpg differ diff --git a/data/pysolfc.glade b/data/pysolfc.glade index 39103dba..70c54275 100644 --- a/data/pysolfc.glade +++ b/data/pysolfc.glade @@ -2051,30 +2051,6 @@ 0 0 - - - True - True - Text foreground: - True - GTK_RELIEF_NORMAL - True - False - False - True - - - 0 - 1 - 0 - 1 - 4 - 4 - fill - - - - True @@ -3128,6 +3104,32 @@ fill + + + + True + Text foreground: + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + 0 + 1 + 0 + 1 + 4 + 4 + fill + + + 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',