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

+ separated demo-mode and solver; solver dialog

+ added statistics progression; statistics progression dialog
+ new animation speed: `medium'; renamed `timer based' to `fast'
+ added flip animation (animatedFlip)
+ added move to waste animation (animatedFlipAndMove)
+ added cancel-drag animation (moveCardsBackHandler)
* improved demo game (snapshots based check for loop)
- removed setting text color from file name
- removed auto generation shadows (too slow)
* optimized menu creation
* changed some keybindings
* updated russian translation
* many bugfixes


git-svn-id: https://pysolfc.svn.sourceforge.net/svnroot/pysolfc/PySolFC/trunk@138 39dd0a4e-7c14-0410-91b3-c4f2d318f732
This commit is contained in:
skomoroh 2007-02-16 23:20:02 +00:00
parent a5af8f15fa
commit 971148fa27
95 changed files with 3978 additions and 1810 deletions

View file

@ -24,7 +24,7 @@ graft html-src
## data - images ## data - images
## ##
#graft data/images #graft data/images
recursive-include data/images *.gif *.png recursive-include data/images *.gif *.png *.jpg
graft data/tiles graft data/tiles
include data/pysol.xbm data/pysol.xpm data/pysol.ico include data/pysol.xbm data/pysol.xpm data/pysol.ico
## ##

View file

@ -38,7 +38,6 @@ gchar *s = N_("Highlight piles:");
gchar *s = N_("Highlight cards:"); gchar *s = N_("Highlight cards:");
gchar *s = N_("Highlight same rank:"); gchar *s = N_("Highlight same rank:");
gchar *s = N_("Set colors"); gchar *s = N_("Set colors");
gchar *s = N_("Text foreground:");
gchar *s = N_("Highlight piles:"); gchar *s = N_("Highlight piles:");
gchar *s = N_("Highlight cards 1:"); gchar *s = N_("Highlight cards 1:");
gchar *s = N_("Highlight cards 2:"); 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_("Change...");
gchar *s = N_("Change..."); gchar *s = N_("Change...");
gchar *s = N_("Text foreground:");
gchar *s = N_("Set font"); gchar *s = N_("Set font");
gchar *s = N_("HTML: "); gchar *s = N_("HTML: ");
gchar *s = N_("Small: "); gchar *s = N_("Small: ");

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

View file

@ -2051,30 +2051,6 @@
<property name="row_spacing">0</property> <property name="row_spacing">0</property>
<property name="column_spacing">0</property> <property name="column_spacing">0</property>
<child>
<widget class="GtkCheckButton" id="use_default_checkbutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Text foreground:</property>
<property name="use_underline">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<property name="active">False</property>
<property name="inconsistent">False</property>
<property name="draw_indicator">True</property>
</widget>
<packing>
<property name="left_attach">0</property>
<property name="right_attach">1</property>
<property name="top_attach">0</property>
<property name="bottom_attach">1</property>
<property name="x_padding">4</property>
<property name="y_padding">4</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
<child> <child>
<widget class="GtkLabel" id="label31"> <widget class="GtkLabel" id="label31">
<property name="visible">True</property> <property name="visible">True</property>
@ -3128,6 +3104,32 @@
<property name="y_options">fill</property> <property name="y_options">fill</property>
</packing> </packing>
</child> </child>
<child>
<widget class="GtkLabel" id="label79">
<property name="visible">True</property>
<property name="label" translatable="yes">Text foreground:</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
<packing>
<property name="left_attach">0</property>
<property name="right_attach">1</property>
<property name="top_attach">0</property>
<property name="bottom_attach">1</property>
<property name="x_padding">4</property>
<property name="y_padding">4</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
</widget> </widget>
<packing> <packing>
<property name="padding">0</property> <property name="padding">0</property>

View file

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View file

Before

Width:  |  Height:  |  Size: 6 KiB

After

Width:  |  Height:  |  Size: 6 KiB

View file

Before

Width:  |  Height:  |  Size: 170 KiB

After

Width:  |  Height:  |  Size: 170 KiB

View file

Before

Width:  |  Height:  |  Size: 206 KiB

After

Width:  |  Height:  |  Size: 206 KiB

View file

@ -1,5 +1,4 @@
<h1>PySol - a Solitaire Game Collection</h1> <h1>PySol - a Solitaire Game Collection</h1>
<hr>
<p> <a href="intro.html">Introduction</a> <p> <a href="intro.html">Introduction</a>
<p> <a href="install.html">Installation</a> <p> <a href="install.html">Installation</a>

View file

@ -26,6 +26,3 @@ by court artisans. No cards from this pack are known to still exist.
<h3>Strategy</h3> <h3>Strategy</h3>
Build sequences on the rows that will play when the correct card turns 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. over from the talon. This game type requires careful strategy to win.
<p>
<br>
<a href="../ganjifa.html">General Ganjifa Rules</a>

View file

@ -15,5 +15,5 @@ and the number of cards you can move as a sequence is not restricted.
<p> <p>
All cards are dealt to 9 piles at the start of the game, each Raja or King 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. starting a new pile. Rows build down in rank and alternate color.
Refer to the general <a href="ganjifa.html">Ganjifa</a> page. Refer to the general <a href="../ganjifa.html">Ganjifa</a> page.
Empty rows cannot be filled. The eight free cells will hold one card each. Empty rows cannot be filled. The eight free cells will hold one card each.

View file

@ -8,7 +8,7 @@ Move all cards to the foundations.
<h3>Quick Description</h3> <h3>Quick Description</h3>
<p> <p>
Like <a href="freecell.html">FreeCell</a>, Like <a href="relaxedfreecell.html">Relaxed FreeCell</a>,
but with 2 decks, and empty rows are not filled. but with 2 decks, and empty rows are not filled.
<h3>Rules</h3> <h3>Rules</h3>
@ -19,7 +19,3 @@ To compensate for this there are 8 free cells which can hold any
- and just one - card. - and just one - card.
<p> <p>
Piles build down by alternate color, and an empty space cannot be filled. Piles build down by alternate color, and an empty space cannot be filled.
<p>
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.

View file

@ -0,0 +1,16 @@
<h1>New British Constitution</h1>
<p>
Two-Deck game type. 2 decks. No redeals.
<h3>Object</h3>
<p>
Move all cards to the foundations.
<h3>Quick Description</h3>
<p>
Like <a href="britishconstitution.html">British Constitution</a>,
but only Jacks on empty spaces and the piles build down by rank ignoring suit.
<h3>Rules</h3>
<p>
<i>[To be written]</i>

View file

@ -0,0 +1,25 @@
<h1>Retinue</h1>
<p>
FreeCell type. 2 decks. No redeal.
<h3>Object</h3>
<p>
Move all cards to the foundations.
<h3>Quick Description</h3>
<p>
Like <a href="freecell.html">FreeCell</a>,
but with 2 decks, and empty rows are not filled.
<h3>Rules</h3>
<p>
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.
<p>
Piles build down by alternate color, and an empty space cannot be filled.
<p>
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.

View file

@ -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. two pack game widely available as a computer version.
<p> <p>
28 cards are dealt out to seven diminishing columns with the bottom card of 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. dealt face up to the right of the columns.
<p> <p>
The aim is to move all the cards into thirteen-card sequences on the goal The aim is to move all the cards into thirteen-card sequences on the goal

View file

@ -5,7 +5,7 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PySol 0.0.1\n" "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" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -216,6 +216,9 @@ msgstr ""
msgid "Baroness" msgid "Baroness"
msgstr "" msgstr ""
msgid "Barrier"
msgstr ""
msgid "Bastille Day" msgid "Bastille Day"
msgstr "" msgstr ""
@ -243,6 +246,9 @@ msgstr ""
msgid "Beatle" msgid "Beatle"
msgstr "" msgstr ""
msgid "Bebop"
msgstr ""
msgid "Beetle" msgid "Beetle"
msgstr "" msgstr ""
@ -3717,6 +3723,9 @@ msgstr ""
msgid "Wasp" msgid "Wasp"
msgstr "" msgstr ""
msgid "Waterfall"
msgstr ""
msgid "Waterloo" msgid "Waterloo"
msgstr "" msgstr ""

File diff suppressed because it is too large Load diff

View file

@ -5,8 +5,8 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PySol 0.0.1\n" "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: 2007-01-17 19:12+0300\n" "PO-Revision-Date: 2007-02-12 19:08+0300\n"
"Last-Translator: Скоморох <skomoroh@gmail.com>\n" "Last-Translator: Скоморох <skomoroh@gmail.com>\n"
"Language-Team: Russian <ru@li.org>\n" "Language-Team: Russian <ru@li.org>\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@ -217,6 +217,10 @@ msgstr "Баларама"
msgid "Baroness" msgid "Baroness"
msgstr "Баронесса" msgstr "Баронесса"
#, fuzzy
msgid "Barrier"
msgstr "Причудливый"
msgid "Bastille Day" msgid "Bastille Day"
msgstr "День Бастилии" msgstr "День Бастилии"
@ -244,6 +248,9 @@ msgstr "Клюв и ласты"
msgid "Beatle" msgid "Beatle"
msgstr "Жук" msgstr "Жук"
msgid "Bebop"
msgstr ""
msgid "Beetle" msgid "Beetle"
msgstr "Жук" msgstr "Жук"
@ -3772,6 +3779,10 @@ msgstr "Фаворит Вашингтона"
msgid "Wasp" msgid "Wasp"
msgstr "Оса" msgstr "Оса"
#, fuzzy
msgid "Waterfall"
msgstr "Ватерлоо"
msgid "Waterloo" msgid "Waterloo"
msgstr "Ватерлоо" msgstr "Ватерлоо"

File diff suppressed because it is too large Load diff

View file

@ -50,6 +50,7 @@ from stats import FileStatsFormatter
from pysoltk import SingleGame_StatsDialog, AllGames_StatsDialog from pysoltk import SingleGame_StatsDialog, AllGames_StatsDialog
from pysoltk import FullLog_StatsDialog, SessionLog_StatsDialog from pysoltk import FullLog_StatsDialog, SessionLog_StatsDialog
from pysoltk import Status_StatsDialog, Top_StatsDialog from pysoltk import Status_StatsDialog, Top_StatsDialog
from pysoltk import ProgressionDialog
from pysoltk import GameInfoDialog from pysoltk import GameInfoDialog
# toolkit imports # toolkit imports
@ -61,6 +62,7 @@ from pysoltk import ColorsDialog
from pysoltk import FontsDialog from pysoltk import FontsDialog
from pysoltk import EditTextDialog from pysoltk import EditTextDialog
from pysoltk import create_find_card_dialog from pysoltk import create_find_card_dialog
from pysoltk import create_solver_dialog
from help import help_about, help_html from help import help_about, help_html
gettext = _ gettext = _
@ -473,11 +475,13 @@ class PysolMenubarActions:
def mDrop(self, *args): def mDrop(self, *args):
if self._cancelDrag(): return 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): def mDrop1(self, *args):
if self._cancelDrag(): return 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): def mStatus(self, *args):
if self._cancelDrag(break_pause=False): return if self._cancelDrag(break_pause=False): return
@ -495,6 +499,9 @@ class PysolMenubarActions:
create_find_card_dialog(self.game.top, self.game, create_find_card_dialog(self.game.top, self.game,
self.app.getFindCardImagesDir()) self.app.getFindCardImagesDir())
def mSolver(self, *args):
create_solver_dialog(self.game.top, self.app)
def mEditGameComment(self, *args): def mEditGameComment(self, *args):
if self._cancelDrag(break_pause=False): return if self._cancelDrag(break_pause=False): return
game, gi = self.game, self.game.gameinfo game, gi = self.game, self.game.gameinfo
@ -593,6 +600,9 @@ class PysolMenubarActions:
elif mode == 106: elif mode == 106:
header = _("Game Info") header = _("Game Info")
d = GameInfoDialog(self.top, header, self.app) 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: elif mode == 202:
# print stats to file # print stats to file
write_method = FileStatsFormatter.writeStats write_method = FileStatsFormatter.writeStats
@ -698,9 +708,7 @@ class PysolMenubarActions:
if self._cancelDrag(break_pause=False): return if self._cancelDrag(break_pause=False): return
d = ColorsDialog(self.top, _("Set colors"), self.app) d = ColorsDialog(self.top, _("Set colors"), self.app)
text_color = self.app.opt.colors['text'] 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: 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['text'] = d.text_color
self.app.opt.colors['piles'] = d.piles_color self.app.opt.colors['piles'] = d.piles_color
self.app.opt.colors['cards_1'] = d.cards_1_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['hintarrow'] = d.hintarrow_color
self.app.opt.colors['not_matching'] = d.not_matching_color self.app.opt.colors['not_matching'] = d.not_matching_color
# #
if (text_color != self.app.opt.colors['text'] or if text_color != self.app.opt.colors['text']:
use_default_text_color != self.app.opt.use_default_text_color): self.app.setTile(self.app.tabletile_index, force=True)
self.app.setTile(self.app.tabletile_index)
def mOptFonts(self, *args): def mOptFonts(self, *args):
if self._cancelDrag(break_pause=False): return if self._cancelDrag(break_pause=False): return

View file

@ -71,6 +71,8 @@ from pysoltk import SelectDialogTreeData
from pysoltk import HTMLViewer from pysoltk import HTMLViewer
from pysoltk import TOOLBAR_BUTTONS from pysoltk import TOOLBAR_BUTTONS
from pysoltk import destroy_find_card_dialog 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 from help import help_about, destroy_help_html
gettext = _ gettext = _
@ -102,7 +104,7 @@ class Options:
self.mahjongg_show_removed = False self.mahjongg_show_removed = False
self.mahjongg_create_solvable = True self.mahjongg_create_solvable = True
self.shisen_show_hint = True self.shisen_show_hint = True
self.animations = 2 # default to Timer based self.animations = 2 # default to Fast
self.redeal_animation = True self.redeal_animation = True
self.win_animation = True self.win_animation = True
self.shadow = True self.shadow = True
@ -112,13 +114,14 @@ class Options:
self.demo_logo = True self.demo_logo = True
self.tile_theme = 'default' self.tile_theme = 'default'
if WIN_SYSTEM == 'win32': if WIN_SYSTEM == 'win32':
self.tile_theme = 'winnative' self.tile_theme = self.default_tile_theme = 'winnative'
if sys.getwindowsversion() >= (5, 1): # xp if sys.getwindowsversion() >= (5, 1): # xp
self.tile_theme = 'xpnative' self.tile_theme = 'xpnative'
elif WIN_SYSTEM == 'x11': elif WIN_SYSTEM == 'x11':
self.tile_theme = 'step' self.tile_theme = 'clam'
self.default_tile_theme = 'default'
elif WIN_SYSTEM == 'aqua': 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 = 1 # 0 == hide, 1,2,3,4 == top, bottom, lef, right
##self.toolbar_style = 'default' ##self.toolbar_style = 'default'
self.toolbar_style = 'bluecurve' self.toolbar_style = 'bluecurve'
@ -190,7 +193,6 @@ class Options:
'hintarrow': '#303030', 'hintarrow': '#303030',
'not_matching': '#ff0000', 'not_matching': '#ff0000',
} }
self.use_default_text_color = False
# delays # delays
self.timeouts = { self.timeouts = {
'hint': 1.0, 'hint': 1.0,
@ -708,10 +710,12 @@ class Application:
except: except:
traceback.print_exc() traceback.print_exc()
pass pass
# save game geometry
self.wm_save_state() self.wm_save_state()
# save game geometry
if self.opt.save_games_geometry and not self.opt.wm_maximized: 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.opt.games_geometry[self.game.id] = geom
self.freeGame() self.freeGame()
# #
@ -726,8 +730,9 @@ class Application:
# hide main window # hide main window
self.wm_withdraw() self.wm_withdraw()
# #
destroy_find_card_dialog()
destroy_help_html() destroy_help_html()
destroy_find_card_dialog()
destroy_solver_dialog()
# update options # update options
self.opt.last_gameid = id self.opt.last_gameid = id
# save options # save options
@ -1005,7 +1010,7 @@ class Application:
#from pprint import pprint; pprint(self.opt.cardset) #from pprint import pprint; pprint(self.opt.cardset)
def loadCardset(self, cs, id=0, update=7, progress=None): def loadCardset(self, cs, id=0, update=7, progress=None):
#print 'loadCardset', cs.ident ##print 'loadCardset', cs.ident
r = 0 r = 0
if cs is None or cs.error: if cs is None or cs.error:
return 0 return 0
@ -1049,6 +1054,10 @@ class Application:
elif self.images is not None: elif self.images is not None:
##self.images.destruct() ##self.images.destruct()
destruct(self.images) 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 # update
self.images = images self.images = images
self.subsampled_images = simages self.subsampled_images = simages
@ -1381,6 +1390,9 @@ Please select a %s type %s.
names.sort() names.sort()
return names return names
def getGamesForSolver(self):
return self.gdb.getGamesForSolver()
# #
# plugins # plugins
# #
@ -1591,7 +1603,6 @@ Please select a %s type %s.
##print dirs ##print dirs
s = "((\\" + ")|(\\".join(IMAGE_EXTENSIONS) + "))$" s = "((\\" + ")|(\\".join(IMAGE_EXTENSIONS) + "))$"
ext_re = re.compile(s, re.I) ext_re = re.compile(s, re.I)
text_color_re = re.compile(r"^(.+)-([0-9A-Fa-f]{6})$")
found, t = [], {} found, t = [], {}
for dir in dirs: for dir in dirs:
dir = dir.strip() dir = dir.strip()
@ -1611,16 +1622,6 @@ Please select a %s type %s.
n = ext_re.sub("", name.strip()) n = ext_re.sub("", name.strip())
if os.path.split(dir)[-1] == 'stretch': if os.path.split(dir)[-1] == 'stretch':
tile.stretch = 1 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 = re.sub("[-_]", " ", n)
n = n.replace('_', ' ') n = n.replace('_', ' ')
##n = unicode(n) ##n = unicode(n)

View file

@ -37,6 +37,7 @@
# imports # imports
import time import time
import math import math
import md5
from cStringIO import StringIO from cStringIO import StringIO
# PySol imports # PySol imports
@ -57,7 +58,9 @@ from pysoltk import after, after_idle, after_cancel
from pysoltk import MfxMessageDialog, MfxExceptionDialog from pysoltk import MfxMessageDialog, MfxExceptionDialog
from pysoltk import MfxCanvasText, MfxCanvasLine, MfxCanvasRectangle from pysoltk import MfxCanvasText, MfxCanvasLine, MfxCanvasRectangle
from pysoltk import Card 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 ANextRoundMove, ASaveSeedMove, AShuffleStackMove
from move import AUpdateStackMove, AFlipAllMove, ASaveStateMove from move import AUpdateStackMove, AFlipAllMove, ASaveStateMove
from move import ASingleCardMove from move import ASingleCardMove
@ -114,6 +117,8 @@ class Game:
self.cards = [] self.cards = []
self.stackmap = {} # dict with (x,y) tuples as key self.stackmap = {} # dict with (x,y) tuples as key
self.allstacks = [] self.allstacks = []
self.sn_groups = [] # snapshot groups; list of list of similar stacks
self.snapshots = []
self.stackdesc_list = [] self.stackdesc_list = []
self.demo_logo = None self.demo_logo = None
self.pause_logo = None self.pause_logo = None
@ -160,6 +165,7 @@ class Game:
# set some defaults # set some defaults
self.sg.openstacks = filter(lambda s: s.cap.max_accept >= s.cap.min_accept, self.sg.openstacks) 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.sg.hp_stacks = filter(lambda s: s.cap.max_move >= 2, self.sg.dropstacks)
self.createSnGroups()
# convert stackgroups to tuples (speed) # convert stackgroups to tuples (speed)
self.allstacks = tuple(self.allstacks) self.allstacks = tuple(self.allstacks)
self.s.foundations = tuple(self.s.foundations) self.s.foundations = tuple(self.s.foundations)
@ -193,15 +199,15 @@ class Game:
# update display properties # update display properties
self.top.wm_geometry("") # cancel user-specified geometry self.top.wm_geometry("") # cancel user-specified geometry
self.canvas.setInitialSize(self.width, self.height) 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 self.top.update_idletasks() # apply geometry now
if DEBUG >= 4: if DEBUG >= 4:
MfxCanvasRectangle(self.canvas, 0, 0, self.width, self.height, MfxCanvasRectangle(self.canvas, 0, 0, self.width, self.height,
width=2, fill=None, outline='green') 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.stats.update_time = time.time()
self.busy = old_busy self.busy = old_busy
@ -253,10 +259,9 @@ class Game:
bind(self.canvas, "<2>", self.clickHandler) bind(self.canvas, "<2>", self.clickHandler)
##bind(self.canvas, "<3>", self.clickHandler) ##bind(self.canvas, "<3>", self.clickHandler)
##bind(self.canvas, "<Double-1>", self.undoHandler) ##bind(self.canvas, "<Double-1>", self.undoHandler)
##bind(self.canvas, "<Double-1>", self.undoHandler)
bind(self.canvas, "<1>", self.undoHandler) bind(self.canvas, "<1>", self.undoHandler)
bind(self.canvas, "<3>", self.redoHandler) bind(self.canvas, "<3>", self.redoHandler)
bind(self.top, '<Unmap>', self._unmapHandler) bind(self.canvas, '<Unmap>', self._unmapHandler)
def __createCommon(self, app): def __createCommon(self, app):
self.busy = 1 self.busy = 1
@ -324,6 +329,7 @@ class Game:
def reset(self, restart=0): def reset(self, restart=0):
self.filename = "" self.filename = ""
self.demo = None self.demo = None
self.solver = None
self.hints = Struct( self.hints = Struct(
list = None, # list of hints for the current move list = None, # list of hints for the current move
index = -1, index = -1,
@ -337,6 +343,7 @@ class Game:
talon_round = 1, talon_round = 1,
ncards = 0, ncards = 0,
) )
self.snapshots = []
# local statistics are reset on each game restart # local statistics are reset on each game restart
self.stats = Struct( self.stats = Struct(
hints = 0, # number of hints consumed hints = 0, # number of hints consumed
@ -434,6 +441,7 @@ class Game:
gamenumber=self.getGameNumber(format=1), gamenumber=self.getGameNumber(format=1),
moves=(0, 0), moves=(0, 0),
stats=self.app.stats.getStats(self.app.opt.player, self.id)) stats=self.app.stats.getStats(self.app.opt.player, self.id))
reset_solver_dialog()
# unhide toplevel when we use a progress bar # unhide toplevel when we use a progress bar
if not self.preview: if not self.preview:
wm_map(self.top, maximized=self.app.opt.wm_maximized) wm_map(self.top, maximized=self.app.opt.wm_maximized)
@ -458,6 +466,7 @@ class Game:
self.startMoves() self.startMoves()
for stack in self.allstacks: for stack in self.allstacks:
stack.updateText() stack.updateText()
self.updateSnapshots()
self.updateText() self.updateText()
self.updateStatus(moves=(0, 0)) self.updateStatus(moves=(0, 0))
self.updateMenus() self.updateMenus()
@ -490,6 +499,7 @@ class Game:
self.gsaveinfo = game.gsaveinfo self.gsaveinfo = game.gsaveinfo
self.s.talon.round = game.loadinfo.talon_round self.s.talon.round = game.loadinfo.talon_round
self.finished = game.finished self.finished = game.finished
self.snapshots = game.snapshots
# 3) move cards to stacks # 3) move cards to stacks
assert len(self.allstacks) == len(game.loadinfo.stacks) assert len(self.allstacks) == len(game.loadinfo.stacks)
for i in range(len(self.allstacks)): for i in range(len(self.allstacks)):
@ -644,6 +654,49 @@ class Game:
def leaveState(self, old_state): def leaveState(self, old_state):
self.moves.state = 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 # card creation & shuffling
@ -810,7 +863,10 @@ class Game:
if self.demo: if self.demo:
self.stopDemo() self.stopDemo()
return return
if self.app.opt.mouse_undo and not self.event_handled: 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.app.menubar.mUndo()
self.event_handled = False self.event_handled = False
return EVENT_PROPAGATE return EVENT_PROPAGATE
@ -823,7 +879,10 @@ class Game:
if self.demo: if self.demo:
self.stopDemo() self.stopDemo()
return return
if self.app.opt.mouse_undo and not self.event_handled: 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.app.menubar.mRedo()
self.event_handled = False self.event_handled = False
return EVENT_PROPAGATE return EVENT_PROPAGATE
@ -895,7 +954,7 @@ class Game:
def _unmapHandler(self, event): def _unmapHandler(self, event):
# pause game if root window has been iconified # 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() self.app.menubar.mPause()
@ -925,11 +984,11 @@ class Game:
if a and not self.preview: if a and not self.preview:
self.canvas.update_idletasks() self.canvas.update_idletasks()
if self.app.audio and self.app.opt.sound and self.app.opt.sound_samples['deal']: 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) self.playSample("deal01", priority=100, loop=loop)
elif a == 3:
self.playSample("deal04", priority=100, loop=loop)
elif a == 4: elif a == 4:
self.playSample("deal04", priority=100, loop=loop)
elif a == 5:
self.playSample("deal08", priority=100, loop=loop) self.playSample("deal08", priority=100, loop=loop)
@ -960,7 +1019,16 @@ class Game:
bitmap="error") bitmap="error")
# main animation method # 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: if self.app.opt.animations == 0 or frames == 0:
return return
# init timer - need a high resolution for this to work # init timer - need a high resolution for this to work
@ -971,13 +1039,16 @@ class Game:
if frames < 0: if frames < 0:
frames = 8 frames = 8
assert frames >= 2 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 frames = frames * 8
SPF = SPF / 2 SPF = SPF / 2
elif self.app.opt.animations == 4: # very slow elif self.app.opt.animations == 5: # very slow
frames = frames * 16 frames = frames * 16
SPF = SPF / 2 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 # this is used internally in game preview to speed up
# the initial dealing # the initial dealing
if self.moves.state == self.S_INIT and frames > 4: if self.moves.state == self.S_INIT and frames > 4:
@ -986,7 +1057,6 @@ class Game:
shadow = self.app.opt.shadow shadow = self.app.opt.shadow
shadows = () shadows = ()
# start animation # start animation
from_stack._unshadeStack()
if tkraise: if tkraise:
for card in cards: for card in cards:
card.tkraise() card.tkraise()
@ -1030,21 +1100,19 @@ class Game:
card.moveBy(dx, dy) card.moveBy(dx, dy)
self.canvas.update_idletasks() self.canvas.update_idletasks()
def animatedFlip(self, stack):
def doAnimatedFlipAndMove(self, from_stack, to_stack=None, frames=-1):
if self.app.opt.animations == 0 or frames == 0:
return False return False
if self.app.opt.animations == 0: if not from_stack.cards:
return False return False
if TOOLKIT == 'gtk': if TOOLKIT == 'gtk':
return False return False
if not stack.cards:
return False
if not Image: if not Image:
return False return False
if self.moves.state == self.S_INIT:
# don't use flip animation for initial dealing
return False
canvas = self.canvas canvas = self.canvas
card = stack.cards[-1] card = from_stack.cards[-1]
im1 = card._active_image._pil_image im1 = card._active_image._pil_image
if card.face_up: if card.face_up:
im2 = card._back_image._pil_image im2 = card._back_image._pil_image
@ -1053,51 +1121,134 @@ class Game:
w, h = im1.size w, h = im1.size
id = card.item.id id = card.item.id
# #
delay = 10 SPF = 0.1/8 # animation speed - seconds per frame
frames = 3.0 # num frames for each step frames = 4.0 # num frames for each step
if self.app.opt.animations == 3: # slow if self.app.opt.animations == 3: # medium
delay = 10 SPF = 0.1/8
frames = 7.0 frames = 7.0
elif self.app.opt.animations == 4: # very slow elif self.app.opt.animations == 4: # slow
delay = 10 SPF = 0.1/8
frames = 12.0 frames = 12.0
delta = 2*int(w/frames/2) # should be even for save position elif self.app.opt.animations == 5: # very slow
ddx, ddy = 0, self.app.images.SHADOW_YOFFSET/2 # ascent of the card SPF = 0.1/8
# siep 1 frames = 24.0
ww = w
dx = delta/2 if to_stack is None:
canvas.move(id, -ddx, -ddy) x0, y0 = from_stack.getPositionFor(card)
canvas.update_idletasks() x1, y1 = x0, y0
canvas.after(delay) dest_x, dest_y = 0, 0
while True: else:
if ww-delta <= 0: x0, y0 = from_stack.getPositionFor(card)
break x1, y1 = to_stack.getPositionForNextCard()
ww -= delta dest_x, dest_y = x1-x0, y1-y0
tmp = im1.resize((ww, h))
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) tk_tmp = ImageTk.PhotoImage(image=tmp)
canvas.itemconfig(id, image=tk_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.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 # step 2
while True: d_x = -shrink_dx/2+move_dx+ascent_dx
tmp = im2.resize((ww, h)) 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) tk_tmp = ImageTk.PhotoImage(image=tmp)
canvas.itemconfig(id, image=tk_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.update_idletasks()
canvas.after(delay)
ww += delta nframe += 1
if ww >= w: t = (SPF-(uclock()-starttime))*1000 # milliseconds
break if t > 0:
canvas.move(id, ddx, ddy) usleep(t/1000)
## else:
## nframe += 1
## xpos += d_x
## ypos += d_y
card.moveTo(x1, y1)
#canvas.update_idletasks()
return True 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 # based on code from pygtk-demo
FRAME_DELAY = 40 FRAME_DELAY = 80
CYCLE_LEN = 60 CYCLE_LEN = 60
starttime = uclock()
images = self.win_animation.images images = self.win_animation.images
saved_images = self.win_animation.saved_images # cached images saved_images = self.win_animation.saved_images # cached images
canvas = self.canvas canvas = self.canvas
@ -1107,6 +1258,10 @@ class Game:
x0 = int(int(canvas.cget('width'))*(canvas.xview()[0])) x0 = int(int(canvas.cget('width'))*(canvas.xview()[0]))
y0 = int(int(canvas.cget('height'))*(canvas.yview()[0])) y0 = int(int(canvas.cget('height'))*(canvas.yview()[0]))
width, height = self.win_animation.width, self.win_animation.height 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 = [] tmp_tk_images = []
raised_images = [] raised_images = []
@ -1143,7 +1298,7 @@ class Game:
if round_k == 100: if round_k == 100:
tmp = im tmp = im
else: else:
tmp = im.resize(new_size, resample=Image.BICUBIC) tmp = im.resize(new_size, resample=Image.BILINEAR)
tk_tmp = ImageTk.PhotoImage(image=tmp) tk_tmp = ImageTk.PhotoImage(image=tmp)
saved_images[img_index][round_k] = tk_tmp saved_images[img_index][round_k] = tk_tmp
@ -1161,7 +1316,11 @@ class Game:
self.win_animation.tk_images = tmp_tk_images self.win_animation.tk_images = tmp_tk_images
canvas.update_idletasks() canvas.update_idletasks()
# loop # 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): def stopWinAnimation(self):
if self.win_animation.timer: if self.win_animation.timer:
@ -1178,8 +1337,8 @@ class Game:
def winAnimation(self, perfect=0): def winAnimation(self, perfect=0):
if self.preview: if self.preview:
return return
if not self.app.opt.animations: #if not self.app.opt.animations:
return # return
if not self.app.opt.win_animation: if not self.app.opt.win_animation:
return return
if TOOLKIT == 'gtk': if TOOLKIT == 'gtk':
@ -1190,7 +1349,8 @@ class Game:
# select some random cards # select some random cards
cards = self.cards[:] cards = self.cards[:]
scards = [] scards = []
for i in xrange(10): ncards = min(10, len(cards))
for i in xrange(ncards):
c = self.app.miscrandom.choice(cards) c = self.app.miscrandom.choice(cards)
scards.append(c) scards.append(c)
cards.remove(c) cards.remove(c)
@ -1200,7 +1360,8 @@ class Game:
self.win_animation.width = self.canvas.winfo_width() self.win_animation.width = self.canvas.winfo_width()
self.win_animation.height = self.canvas.winfo_height() self.win_animation.height = self.canvas.winfo_height()
# run win animation in background # run win animation in background
after_idle(self.canvas, self.doWinAnimation) ##after_idle(self.canvas, self.winAnimationEvent)
after(self.canvas, 200, self.winAnimationEvent)
return return
def redealAnimation(self): def redealAnimation(self):
@ -1208,14 +1369,6 @@ class Game:
return return
if not self.app.opt.animations or not self.app.opt.redeal_animation: if not self.app.opt.animations or not self.app.opt.redeal_animation:
return 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 = [] cards = []
for s in self.allstacks: for s in self.allstacks:
if s is not self.s.talon: if s is not self.s.talon:
@ -1223,6 +1376,16 @@ class Game:
cards.append((c,s)) cards.append((c,s))
if not cards: if not cards:
return 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 # select some random cards
acards = [] acards = []
scards = cards[:] scards = cards[:]
@ -1381,6 +1544,7 @@ class Game:
# the actual hint class (or None) # the actual hint class (or None)
Hint_Class = DefaultHint Hint_Class = DefaultHint
Solver_Class = None
def getHintClass(self): def getHintClass(self):
return self.Hint_Class return self.Hint_Class
@ -1625,7 +1789,8 @@ for %d moves.
if s.canFlipCard(): if s.canFlipCard():
if sound: if sound:
self.playSample("autoflip", priority=5) self.playSample("autoflip", priority=5)
s.flipMove() #~s.flipMove()
s.flipMove(animation=True)
done_something = 1 done_something = 1
# each single flip is undo-able unless opt.autofaceup # each single flip is undo-able unless opt.autofaceup
self.finishMove() self.finishMove()
@ -1661,6 +1826,13 @@ for %d moves.
return self.dealCards(sound=sound) return self.dealCards(sound=sound)
return 0 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 ## for find_card_dialog
def highlightCard(self, suit, rank): def highlightCard(self, suit, rank):
if not self.app: if not self.app:
@ -1681,11 +1853,12 @@ for %d moves.
return ((self.sg.hp_stacks, 2),) return ((self.sg.hp_stacks, 2),)
return () return ()
def _highlightCards(self, info, sleep=1.5): def _highlightCards(self, info, sleep=1.5, delta=(1,1,1,1)):
if not info: if not info:
return 0 return 0
if self.pause: if self.pause:
return 0 return 0
self.stopWinAnimation()
items = [] items = []
for s, c1, c2, color in info: for s, c1, c2, color in info:
assert c1 in s.cards and c2 in s.cards assert c1 in s.cards and c2 in s.cards
@ -1741,13 +1914,15 @@ for %d moves.
y2 = y2 + self.app.images.CARDH y2 = y2 + self.app.images.CARDH
tkraise = True tkraise = True
##print c1, c2, x1, y1, x2, y2 ##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': 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) width=4, fill=None, outline=color)
if tkraise: if tkraise:
r.tkraise(c2.item) r.tkraise(c2.item)
elif TOOLKIT == 'gtk': 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, width=4, fill=None, outline=color,
group=s.group) group=s.group)
if tkraise: if tkraise:
@ -1801,7 +1976,7 @@ for %d moves.
for s in si[0]: for s in si[0]:
pile = s.getPile() pile = s.getPile()
if pile and len(pile) >= si[1]: 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: if not hi:
self.highlightNotMatching() self.highlightNotMatching()
return 0 return 0
@ -1855,7 +2030,7 @@ for %d moves.
# for spider-type stacks # for spider-type stacks
if to_stack.cards: if to_stack.cards:
# check suit # 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 return 0
# #
@ -1896,6 +2071,10 @@ for %d moves.
# compute all hints for the current position # compute all hints for the current position
# this is the only method that actually uses class Hint # this is the only method that actually uses class Hint
def getHints(self, level, taken_hint=None): 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() hint_class = self.getHintClass()
if hint_class is None: if hint_class is None:
return None return None
@ -1941,10 +2120,17 @@ for %d moves.
# a move move # a move move
assert to_stack assert to_stack
assert 1 <= ncards <= len(from_stack.cards) 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:]) assert to_stack.acceptsCards(from_stack, from_stack.cards[-ncards:])
if sleep <= 0.0: if sleep <= 0.0:
return h 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: if info and self.app.statusbar and self.app.opt.statusbar:
self.app.statusbar.configLabel("info", text=_("Score %6d") % (score), fg=text_color) self.app.statusbar.configLabel("info", text=_("Score %6d") % (score), fg=text_color)
else: else:
@ -2000,6 +2186,7 @@ for %d moves.
mixed = mixed, mixed = mixed,
sleep = self.app.opt.timeouts['demo'], sleep = self.app.opt.timeouts['demo'],
last_deal = [], last_deal = [],
snapshots = [],
hint = None, hint = None,
keypress = None, keypress = None,
start_demo_moves = self.stats.demo_moves, start_demo_moves = self.stats.demo_moves,
@ -2036,6 +2223,8 @@ for %d moves.
timeout = 10000 timeout = 10000
if 1 and player_moves == 0: if 1 and player_moves == 0:
timeout = 5000 timeout = 5000
if self.demo and self.demo.level == 3:
timeout = 0
if self.isGameWon(): if self.isGameWon():
self.updateTime() self.updateTime()
finished = 1 finished = 1
@ -2044,7 +2233,7 @@ for %d moves.
if not self.top.winfo_ismapped(): if not self.top.winfo_ismapped():
status = 2 status = 2
elif player_moves == 0: elif player_moves == 0:
self.playSample("autopilotwon") self.playSample("autopilotwon", priority=1000)
s = self.app.miscrandom.choice((_("&Great"), _("&Cool"), _("&Yeah"), _("&Wow"))) # ??? accelerators s = self.app.miscrandom.choice((_("&Great"), _("&Cool"), _("&Yeah"), _("&Wow"))) # ??? accelerators
d = MfxMessageDialog(self.top, title=PACKAGE+_(" Autopilot"), d = MfxMessageDialog(self.top, title=PACKAGE+_(" Autopilot"),
text=_("\nGame solved in %d moves.\n") % self.moves.index, text=_("\nGame solved in %d moves.\n") % self.moves.index,
@ -2067,7 +2256,7 @@ for %d moves.
status = 2 status = 2
else: else:
if player_moves == 0: if player_moves == 0:
self.playSample("autopilotlost") self.playSample("autopilotlost", priority=1000)
s = self.app.miscrandom.choice((_("&Oh well"), _("&That's life"), _("&Hmm"))) # ??? accelerators s = self.app.miscrandom.choice((_("&Oh well"), _("&That's life"), _("&Hmm"))) # ??? accelerators
d = MfxMessageDialog(self.top, title=PACKAGE+_(" Autopilot"), d = MfxMessageDialog(self.top, title=PACKAGE+_(" Autopilot"),
text=_("\nThis won't come out...\n"), text=_("\nThis won't come out...\n"),
@ -2076,7 +2265,7 @@ for %d moves.
status = d.status status = d.status
if finished: if finished:
self.updateStats(demo=1) self.updateStats(demo=1)
if self.demo and status == 2: if not DEBUG and self.demo and status == 2:
# timeout in dialog # timeout in dialog
if self.stats.demo_moves > self.demo.start_demo_moves: if self.stats.demo_moves > self.demo.start_demo_moves:
# we only increase the splash-screen counter if the last # we only increase the splash-screen counter if the last
@ -2143,9 +2332,10 @@ for %d moves.
score, pos, ncards, from_stack, to_stack, text_color, forced_move = h score, pos, ncards, from_stack, to_stack, text_color, forced_move = h
if ncards == 0: if ncards == 0:
# a deal-move # a deal-move
# do not let games like Klondike and Canfield deal forever
if self.dealCards() == 0: if self.dealCards() == 0:
return 1 return 1
# do not let games like Klondike and Canfield deal forever if 0: # old version, based on dealing card
c = self.s.talon.getCard() c = self.s.talon.getCard()
if c in demo.last_deal: if c in demo.last_deal:
# We went through the whole Talon. Give up. # We went through the whole Talon. Give up.
@ -2153,19 +2343,27 @@ for %d moves.
# Note that `None' is a valid entry in last_deal[] # Note that `None' is a valid entry in last_deal[]
# (this means that all cards are on the Waste). # (this means that all cards are on the Waste).
demo.last_deal.append(c) 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: elif from_stack == to_stack:
# a flip-move # a flip-move
from_stack.flipMove() from_stack.flipMove(animation=True)
demo.last_deal = [] demo.last_deal = []
else: else:
# a move-move # a move-move
from_stack.moveMove(ncards, to_stack, frames=-1) from_stack.moveMove(ncards, to_stack, frames=-1)
demo.last_deal = [] demo.last_deal = []
##print self.moves.index
return 0 return 0
def createDemoInfoText(self): def createDemoInfoText(self):
## TODO - the text placement is not fully ok ## TODO - the text placement is not fully ok
if DEBUG:
self.showHelp('help', self.getDemoInfoText())
return return
if not self.demo or self.demo.info_text or self.preview: if not self.demo or self.demo.info_text or self.preview:
return return
@ -2177,13 +2375,15 @@ for %d moves.
] ]
ta = self.getDemoInfoTextAttr(tinfo) ta = self.getDemoInfoTextAttr(tinfo)
if ta: 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], self.demo.info_text = MfxCanvasText(self.canvas, ta[1], ta[2],
anchor=ta[0], font=font, anchor=ta[0], font=font,
text=self.getDemoInfoText()) text=self.getDemoInfoText())
def getDemoInfoText(self): 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): def getDemoInfoTextAttr(self, tinfo):
items1, items2 = [], [] items1, items2 = [], []
@ -2291,6 +2491,21 @@ for %d moves.
am.do(self) am.do(self)
self.hints.list = None 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 # move type 3
def turnStackMove(self, from_stack, to_stack, update_flags=1): def turnStackMove(self, from_stack, to_stack, update_flags=1):
assert from_stack and to_stack and (from_stack is not to_stack) 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) assert moves.index == len(moves.history)
moves.current = [] moves.current = []
self.updateSnapshots()
# update view # update view
self.updateText() self.updateText()
self.updateStatus(moves=(moves.index, self.stats.total_moves)) self.updateStatus(moves=(moves.index, self.stats.total_moves))
self.updateMenus() self.updateMenus()
self.updatePlayTime(do_after=0) self.updatePlayTime(do_after=0)
reset_solver_dialog()
return 1 return 1
@ -2428,6 +2646,7 @@ for %d moves.
self.stats.undo_moves += 1 self.stats.undo_moves += 1
self.stats.total_moves += 1 self.stats.total_moves += 1
self.hints.list = None self.hints.list = None
self.updateSnapshots()
self.updateText() self.updateText()
self.updateStatus(moves=(self.moves.index, self.stats.total_moves)) self.updateStatus(moves=(self.moves.index, self.stats.total_moves))
self.updateMenus() self.updateMenus()
@ -2451,6 +2670,7 @@ for %d moves.
self.stats.redo_moves += 1 self.stats.redo_moves += 1
self.stats.total_moves += 1 self.stats.total_moves += 1
self.hints.list = None self.hints.list = None
self.updateSnapshots()
self.updateText() self.updateText()
self.updateStatus(moves=(self.moves.index, self.stats.total_moves)) self.updateStatus(moves=(self.moves.index, self.stats.total_moves))
self.updateMenus() self.updateMenus()
@ -2707,6 +2927,9 @@ in the current implementation.''' % version
moves = pload() moves = pload()
assert isinstance(moves, Struct), err_txt assert isinstance(moves, Struct), err_txt
game.moves.__dict__.update(moves.__dict__) game.moves.__dict__.update(moves.__dict__)
snapshots = p.load()
assert isinstance(snapshots, list), err_txt
game.snapshots = snapshots
if 0 <= bookmark <= 1: if 0 <= bookmark <= 1:
gstats = pload() gstats = pload()
assert isinstance(gstats, Struct), err_txt assert isinstance(gstats, Struct), err_txt
@ -2763,6 +2986,7 @@ in the current implementation.''' % version
p.dump(self.saveinfo) p.dump(self.saveinfo)
p.dump(self.gsaveinfo) p.dump(self.gsaveinfo)
p.dump(self.moves) p.dump(self.moves)
p.dump(self.snapshots)
if 0 <= bookmark <= 1: if 0 <= bookmark <= 1:
if bookmark == 0: if bookmark == 0:
self.gstats.saved = self.gstats.saved + 1 self.gstats.saved = self.gstats.saved + 1

View file

@ -339,6 +339,7 @@ class GI:
('fc-0.9.2', tuple(range(441, 466))), ('fc-0.9.2', tuple(range(441, 466))),
('fc-0.9.3', tuple(range(466, 661))), ('fc-0.9.3', tuple(range(466, 661))),
('fc-0.9.4', tuple(range(661, 671))), ('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 # 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,] _CHILDREN_GAMES = [16, 33, 55, 90, 91, 96, 97, 176, 903,]
_OPEN_GAMES = [] _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 = [ _POPULAR_GAMES = [
1, # Gypsy 1, # Gypsy
@ -471,6 +470,7 @@ class GameManager:
self.__games_by_altname = None self.__games_by_altname = None
self.__all_games = {} # includes hidden games self.__all_games = {} # includes hidden games
self.__all_gamenames = {} # includes hidden games self.__all_gamenames = {} # includes hidden games
self.__games_for_solver = []
self.loading_plugin = 0 self.loading_plugin = 0
self.registered_game_types = {} self.registered_game_types = {}
@ -536,7 +536,9 @@ class GameManager:
## if gi.id in k: break ## if gi.id in k: break
## else: ## else:
## print gi.id ## 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 # access games database - we do not expose hidden games
@ -591,6 +593,9 @@ class GameManager:
return gi.id return gi.id
return None return None
def getGamesForSolver(self):
return self.__games_for_solver
# /*********************************************************************** # /***********************************************************************
# // # //

View file

@ -42,6 +42,8 @@ from pysollib.layout import Layout
from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint
from pysollib.pysoltk import MfxCanvasText from pysollib.pysoltk import MfxCanvasText
from numerica import Numerica_Hint
# /*********************************************************************** # /***********************************************************************
# // Tam O'Shanter # // Tam O'Shanter
@ -159,6 +161,8 @@ class Strategy_RowStack(BasicRowStack):
class Strategy(Game): class Strategy(Game):
Hint_Class = Numerica_Hint
def createGame(self, rows=8): def createGame(self, rows=8):
# create layout # create layout
l, s = Layout(self), self.s l, s = Layout(self), self.s
@ -181,6 +185,7 @@ class Strategy(Game):
# define stack-groups # define stack-groups
l.defaultStackGroups() l.defaultStackGroups()
self.sg.dropstacks.append(s.talon)
# #
# game overrides # game overrides
@ -209,14 +214,16 @@ class StrategyPlus(Strategy):
def fillStack(self, stack): def fillStack(self, stack):
if stack is self.s.talon and stack.cards: if stack is self.s.talon and stack.cards:
old_state = self.enterState(self.S_FILL)
c = stack.cards[-1] c = stack.cards[-1]
while c.rank == ACE: while c.rank == ACE:
old_state = self.enterState(self.S_FILL)
self.moveMove(1, stack, self.s.foundations[c.suit]) self.moveMove(1, stack, self.s.foundations[c.suit])
if stack.canFlipCard(): if stack.canFlipCard():
stack.flipMove() stack.flipMove()
self.leaveState(old_state) if not stack.cards:
break
c = stack.cards[-1] c = stack.cards[-1]
self.leaveState(old_state)
# /*********************************************************************** # /***********************************************************************
@ -480,6 +487,8 @@ class Amazons_Foundation(AbstractFoundationStack):
rank = 5 rank = 5
if (rank + self.cap.dir) % self.cap.mod != cards[0].rank: if (rank + self.cap.dir) % self.cap.mod != cards[0].rank:
return False return False
if cards[0].rank == QUEEN:
return True
i = list(self.game.s.foundations).index(self) i = list(self.game.s.foundations).index(self)
j = list(self.game.s.rows).index(from_stack) j = list(self.game.s.rows).index(from_stack)
return i == j return i == j

View file

@ -42,6 +42,8 @@ from pysollib.stack import *
from pysollib.game import Game from pysollib.game import Game
from pysollib.layout import Layout from pysollib.layout import Layout
from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint
from pysollib.hint import FreeCellSolverWrapper
# /*********************************************************************** # /***********************************************************************
# // Castles in Spain # // Castles in Spain
@ -51,8 +53,9 @@ class CastlesInSpain(Game):
Layout_Method = Layout.bakersDozenLayout Layout_Method = Layout.bakersDozenLayout
Talon_Class = InitialDealTalonStack Talon_Class = InitialDealTalonStack
Foundation_Class = SS_FoundationStack Foundation_Class = SS_FoundationStack
RowStack_Class = AC_RowStack RowStack_Class = SuperMoveAC_RowStack
Hint_Class = CautiousDefaultHint Hint_Class = CautiousDefaultHint
Solver_Class = FreeCellSolverWrapper()
# #
# game layout # game layout
@ -69,8 +72,7 @@ class CastlesInSpain(Game):
for r in l.s.foundations: for r in l.s.foundations:
s.foundations.append(self.Foundation_Class(r.x, r.y, self, suit=r.suit)) s.foundations.append(self.Foundation_Class(r.x, r.y, self, suit=r.suit))
for r in l.s.rows: for r in l.s.rows:
s.rows.append(self.RowStack_Class(r.x, r.y, self, s.rows.append(self.RowStack_Class(r.x, r.y, self))
max_move=1, max_accept=1))
# default # default
l.defaultAll() l.defaultAll()
@ -96,7 +98,8 @@ class Martha_RowStack(AC_RowStack):
class Martha(CastlesInSpain): class Martha(CastlesInSpain):
RowStack_Class = FullStackWrapper(Martha_RowStack) Solver_Class = None
RowStack_Class = Martha_RowStack
def createGame(self): def createGame(self):
CastlesInSpain.createGame(self, rows=12, playcards=13) CastlesInSpain.createGame(self, rows=12, playcards=13)
@ -115,7 +118,9 @@ class Martha(CastlesInSpain):
# ************************************************************************/ # ************************************************************************/
class BakersDozen(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): def _shuffleHook(self, cards):
# move Kings to bottom of each stack # move Kings to bottom of each stack
@ -148,16 +153,19 @@ class BakersDozen(CastlesInSpain):
class SpanishPatience(BakersDozen): class SpanishPatience(BakersDozen):
Foundation_Class = AC_FoundationStack Foundation_Class = AC_FoundationStack
Solver_Class = None
class PortugueseSolitaire(BakersDozen): class PortugueseSolitaire(BakersDozen):
RowStack_Class = StackWrapper(RK_RowStack, base_rank=KING) RowStack_Class = StackWrapper(RK_RowStack, base_rank=KING)
Solver_Class = FreeCellSolverWrapper(sbb='rank', esf='kings')
def _shuffleHook(self, cards): def _shuffleHook(self, cards):
return cards return cards
class SpanishPatienceII(PortugueseSolitaire): class SpanishPatienceII(PortugueseSolitaire):
RowStack_Class = RK_RowStack RowStack_Class = RK_RowStack
Solver_Class = FreeCellSolverWrapper(sbb='rank', sm='unlimited')
# /*********************************************************************** # /***********************************************************************
@ -165,6 +173,8 @@ class SpanishPatienceII(PortugueseSolitaire):
# ************************************************************************/ # ************************************************************************/
class GoodMeasure(BakersDozen): class GoodMeasure(BakersDozen):
Solver_Class = FreeCellSolverWrapper(preset='good_measure')
def createGame(self): def createGame(self):
CastlesInSpain.createGame(self, rows=10) CastlesInSpain.createGame(self, rows=10)
@ -189,8 +199,8 @@ class GoodMeasure(BakersDozen):
class Cruel_Talon(TalonStack): class Cruel_Talon(TalonStack):
def canDealCards(self): def canDealCards(self):
## FIXME: this is to avoid loops in the demo ## FIXME: this is to avoid loops in the demo
if self.game.demo and self.game.moves.index >= 100: #if self.game.demo and self.game.moves.index >= 100:
return False # return False
if self.round == self.max_rounds: if self.round == self.max_rounds:
return False return False
return not self.game.isGameWon() return not self.game.isGameWon()
@ -238,6 +248,8 @@ class Cruel_Talon(TalonStack):
class Cruel(CastlesInSpain): class Cruel(CastlesInSpain):
Talon_Class = StackWrapper(Cruel_Talon, max_rounds=-1) Talon_Class = StackWrapper(Cruel_Talon, max_rounds=-1)
RowStack_Class = StackWrapper(SS_RowStack, base_rank=NO_RANK) RowStack_Class = StackWrapper(SS_RowStack, base_rank=NO_RANK)
##Solver_Class = FreeCellSolverWrapper(preset='cruel')
Solver_Class = None
def createGame(self): def createGame(self):
CastlesInSpain.createGame(self, rows=12) CastlesInSpain.createGame(self, rows=12)
@ -289,6 +301,7 @@ class Indefatigable(Cruel):
class Perseverance(Cruel, BakersDozen): class Perseverance(Cruel, BakersDozen):
Talon_Class = StackWrapper(Cruel_Talon, max_rounds=3) 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) 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): def _shuffleHook(self, cards):
# move Kings to bottom of each stack (???) # move Kings to bottom of each stack (???)
@ -306,6 +319,7 @@ class Perseverance(Cruel, BakersDozen):
# ************************************************************************/ # ************************************************************************/
class RippleFan(CastlesInSpain): class RippleFan(CastlesInSpain):
Solver_Class = None
def createGame(self): def createGame(self):
# create layout # create layout

View file

@ -49,36 +49,12 @@ from pysollib.hint import FreeCellType_Hint, FreeCellSolverWrapper
# // Baker's Game # // 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): class BakersGame(Game):
Layout_Method = Layout.freeCellLayout Layout_Method = Layout.freeCellLayout
Foundation_Class = SS_FoundationStack Foundation_Class = SS_FoundationStack
RowStack_Class = BakersGame_RowStack RowStack_Class = SuperMoveSS_RowStack
##Hint_Class = FreeCellType_Hint Hint_Class = FreeCellType_Hint
Hint_Class = FreeCellSolverWrapper(FreeCellType_Hint, {'sbb' : "suit" }) Solver_Class = FreeCellSolverWrapper(sbb='suit')
# #
# game layout # game layout
@ -123,7 +99,7 @@ class BakersGame(Game):
class KingOnlyBakersGame(BakersGame): class KingOnlyBakersGame(BakersGame):
RowStack_Class = StackWrapper(FreeCell_SS_RowStack, base_rank=KING) 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): class RelaxedSeahavenTowers(SeahavenTowers):
RowStack_Class = KingSS_RowStack 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 # // Penguin
# // Opus # // Opus
# // Tuxedo
# ************************************************************************/ # ************************************************************************/
class Tuxedo(Game): 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 Hint_Class = FreeCellType_Hint
Solver_Class = FreeCellSolverWrapper(sbb='suit', sm='unlimited')
def createGame(self, rows=7, reserves=7): def createGame(self, rows=7, reserves=7):
# create layout # create layout
@ -265,16 +244,16 @@ class Tuxedo(Game):
# create stacks # create stacks
x, y = self.width - l.XS, l.YM x, y = self.width - l.XS, l.YM
for i in range(4): 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 y = y + l.YS
self.setRegion(s.foundations, (x - l.CW/2, -999, 999999, 999999)) self.setRegion(s.foundations, (x - l.CW/2, -999, 999999, 999999))
x, y = l.XM + (maxrows-rows)*l.XS/2, l.YM x, y = l.XM + (maxrows-rows)*l.XS/2, l.YM
for i in range(rows): 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 = x + l.XS
x, y = l.XM + (maxrows-reserves)*l.XS/2, self.height - l.YS x, y = l.XM + (maxrows-reserves)*l.XS/2, self.height - l.YS
for i in range(reserves): 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 x = x + l.XS
self.setRegion(s.reserves, (-999, y - l.CH / 2, 999999, 999999)) self.setRegion(s.reserves, (-999, y - l.CH / 2, 999999, 999999))
s.talon = InitialDealTalonStack(l.XM+1, y, self) s.talon = InitialDealTalonStack(l.XM+1, y, self)
@ -294,6 +273,7 @@ class Tuxedo(Game):
class Penguin(Tuxedo): class Penguin(Tuxedo):
GAME_VERSION = 2 GAME_VERSION = 2
Solver_Class = FreeCellSolverWrapper(sbb='suit', esf='kings', sm='unlimited')
def _shuffleHook(self, cards): def _shuffleHook(self, cards):
# move base cards to top of the Talon (i.e. first cards to be dealt) # move base cards to top of the Talon (i.e. first cards to be dealt)

View file

@ -42,6 +42,7 @@ from pysollib.stack import *
from pysollib.game import Game from pysollib.game import Game
from pysollib.layout import Layout from pysollib.layout import Layout
from pysollib.hint import CautiousDefaultHint, FreeCellType_Hint from pysollib.hint import CautiousDefaultHint, FreeCellType_Hint
from pysollib.hint import FreeCellSolverWrapper
from pysollib.pysoltk import MfxCanvasText from pysollib.pysoltk import MfxCanvasText
@ -60,9 +61,11 @@ class BeleagueredCastleType_Hint(CautiousDefaultHint):
class StreetsAndAlleys(Game): class StreetsAndAlleys(Game):
Hint_Class = BeleagueredCastleType_Hint Hint_Class = BeleagueredCastleType_Hint
Solver_Class = FreeCellSolverWrapper(preset='streets_and_alleys')
Foundation_Class = SS_FoundationStack Foundation_Class = SS_FoundationStack
RowStack_Class = RK_RowStack ##RowStack_Class = RK_RowStack
RowStack_Class = SuperMoveRK_RowStack
# #
# game layout # game layout
@ -102,7 +105,7 @@ class StreetsAndAlleys(Game):
for x in (x0, x2): for x in (x0, x2):
y = l.YM+l.YS*int(reserves!=0) y = l.YM+l.YS*int(reserves!=0)
for i in range(4): 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 stack.CARD_XOFFSET, stack.CARD_YOFFSET = l.XOFFSET, 0
s.rows.append(stack) s.rows.append(stack)
y = y + l.YS y = y + l.YS
@ -178,6 +181,8 @@ class Citadel(StreetsAndAlleys):
class ExiledKings(Citadel): class ExiledKings(Citadel):
Hint_Class = BeleagueredCastleType_Hint
Solver_Class = FreeCellSolverWrapper(sbb='rank', esf='kings')
RowStack_Class = StackWrapper(RK_RowStack, base_rank=KING) RowStack_Class = StackWrapper(RK_RowStack, base_rank=KING)
@ -432,11 +437,13 @@ class Chessboard(Fortress):
class Stronghold(StreetsAndAlleys): class Stronghold(StreetsAndAlleys):
Hint_Class = FreeCellType_Hint Hint_Class = FreeCellType_Hint
Solver_Class = FreeCellSolverWrapper(sbb='rank')
def createGame(self): def createGame(self):
StreetsAndAlleys.createGame(self, reserves=1) StreetsAndAlleys.createGame(self, reserves=1)
class Fastness(StreetsAndAlleys): class Fastness(StreetsAndAlleys):
Hint_Class = FreeCellType_Hint Hint_Class = FreeCellType_Hint
Solver_Class = FreeCellSolverWrapper(sbb='rank')
def createGame(self): def createGame(self):
StreetsAndAlleys.createGame(self, reserves=2) StreetsAndAlleys.createGame(self, reserves=2)
@ -727,6 +734,8 @@ class Rittenhouse(Game):
class Lightweight(StreetsAndAlleys): class Lightweight(StreetsAndAlleys):
DEAL = (7, 1) DEAL = (7, 1)
RowStack_Class = StackWrapper(RK_RowStack, base_rank=KING) RowStack_Class = StackWrapper(RK_RowStack, base_rank=KING)
Solver_Class = FreeCellSolverWrapper(sbb='rank', esf='kings',
sm='unlimited')
def createGame(self, rows=12, playcards=20): def createGame(self, rows=12, playcards=20):
l, s = Layout(self), self.s l, s = Layout(self), self.s
@ -765,6 +774,7 @@ class Lightweight(StreetsAndAlleys):
class CastleMount(Lightweight): class CastleMount(Lightweight):
DEAL = (11, 1) DEAL = (11, 1)
RowStack_Class = Spider_SS_RowStack RowStack_Class = Spider_SS_RowStack
Solver_Class = None
shallHighlightMatch = Game._shallHighlightMatch_RK shallHighlightMatch = Game._shallHighlightMatch_RK
getQuickPlayScore = Game._getSpiderQuickPlayScore getQuickPlayScore = Game._getSpiderQuickPlayScore
@ -786,6 +796,7 @@ class SelectiveCastle_RowStack(RK_RowStack):
class SelectiveCastle(StreetsAndAlleys, Chessboard): class SelectiveCastle(StreetsAndAlleys, Chessboard):
Foundation_Class = Chessboard_Foundation Foundation_Class = Chessboard_Foundation
RowStack_Class = StackWrapper(SelectiveCastle_RowStack, mod=13) RowStack_Class = StackWrapper(SelectiveCastle_RowStack, mod=13)
Solver_Class = None
def createGame(self): def createGame(self):
StreetsAndAlleys.createGame(self, texts=True) StreetsAndAlleys.createGame(self, texts=True)
@ -854,7 +865,7 @@ class Soother(Game):
class PenelopesWeb(StreetsAndAlleys): class PenelopesWeb(StreetsAndAlleys):
RowStack_Class = StackWrapper(RK_RowStack, base_rank=KING) RowStack_Class = StackWrapper(RK_RowStack, base_rank=KING)
Solver_Class = FreeCellSolverWrapper(sbb='rank', esf='kings')
# register the game # register the game

View file

@ -204,7 +204,7 @@ class Realm(Game):
l, s = Layout(self), self.s l, s = Layout(self), self.s
# set window # 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) self.setSize(w, h)
# create stacks # create stacks

View file

@ -368,7 +368,7 @@ class SeniorWrangler_Talon(DealRowTalonStack):
r = self.game.s.rows[self.round-1] r = self.game.s.rows[self.round-1]
if not r.cards: if not r.cards:
self.game.nextRoundMove(self) self.game.nextRoundMove(self)
return return 1
if sound: if sound:
self.game.startDealSample() self.game.startDealSample()
old_state = self.game.enterState(self.game.S_DEAL) old_state = self.game.enterState(self.game.S_DEAL)

View file

@ -379,7 +379,8 @@ class PrincessPatience_RowStack(SS_RowStack):
def canMoveCards(self, cards): def canMoveCards(self, cards):
if not SS_RowStack.canMoveCards(self, cards): if not SS_RowStack.canMoveCards(self, cards):
return False 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 col = index % 4
row = index / 4 row = index / 4
if index < 16: # left if index < 16: # left
@ -403,6 +404,7 @@ class PrincessPatience_RowStack(SS_RowStack):
class PrincessPatience(Game): class PrincessPatience(Game):
Hint_Class = CautiousDefaultHint
RowStack_Class = PrincessPatience_RowStack RowStack_Class = PrincessPatience_RowStack
def createGame(self, max_rounds=1): def createGame(self, max_rounds=1):

View file

@ -40,7 +40,7 @@ from gypsy import DieRussische_Foundation
# ************************************************************************/ # ************************************************************************/
class Capricieuse(Game): class Capricieuse(Game):
Hint_Class = CautiousDefaultHint
Talon_Class = StackWrapper(RedealTalonStack, max_rounds=3) Talon_Class = StackWrapper(RedealTalonStack, max_rounds=3)
RowStack_Class = UD_SS_RowStack RowStack_Class = UD_SS_RowStack
@ -113,6 +113,7 @@ class Nationale(Capricieuse):
# ************************************************************************/ # ************************************************************************/
class Strata(Game): class Strata(Game):
Hint_Class = CautiousDefaultHint
def createGame(self): def createGame(self):

View file

@ -286,6 +286,7 @@ class Harvestman(Arachnida):
# ************************************************************************/ # ************************************************************************/
class GermanPatience(Game): class GermanPatience(Game):
Hint_Class = CautiousDefaultHint
def createGame(self, rows=8): def createGame(self, rows=8):

View file

@ -41,6 +41,7 @@ from pysollib.stack import *
from pysollib.game import Game from pysollib.game import Game
from pysollib.layout import Layout from pysollib.layout import Layout
from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint
from pysollib.hint import FreeCellSolverWrapper
from pysollib.pysoltk import MfxCanvasText from pysollib.pysoltk import MfxCanvasText
@ -133,6 +134,10 @@ class Fan(Game):
return () return ()
class FanGame(Fan):
Solver_Class = FreeCellSolverWrapper(preset='fan')
# /*********************************************************************** # /***********************************************************************
# // Scotch Patience # // Scotch Patience
# ************************************************************************/ # ************************************************************************/
@ -461,6 +466,8 @@ class CloverLeaf_RowStack(UD_SS_RowStack):
if not self.cards: if not self.cards:
return cards[0].rank in (ACE, KING) return cards[0].rank in (ACE, KING)
return True return True
def _getBaseCard(self):
return _('Base card - Ace or King.')
class CloverLeaf(Game): class CloverLeaf(Game):
@ -531,6 +538,8 @@ class CloverLeaf(Game):
# ************************************************************************/ # ************************************************************************/
class FreeFan(Fan): class FreeFan(Fan):
RowStack_Class = FullStackWrapper(SuperMoveSS_RowStack, base_rank=KING)
Solver_Class = FreeCellSolverWrapper(esf='kings', sbb='suit')
def createGame(self): def createGame(self):
Fan.createGame(self, reserves=2, playcards=8) Fan.createGame(self, reserves=2, playcards=8)
@ -542,6 +551,7 @@ class FreeFan(Fan):
class BoxFan(Fan): class BoxFan(Fan):
RowStack_Class = KingAC_RowStack RowStack_Class = KingAC_RowStack
Solver_Class = FreeCellSolverWrapper(esf='kings')
def createGame(self): def createGame(self):
Fan.createGame(self, rows=(4,4,4,4)) Fan.createGame(self, rows=(4,4,4,4))
@ -632,7 +642,7 @@ class FascinationFan(Fan):
def redealCards(self): def redealCards(self):
r0 = r1 = len(self.s.talon.cards)/3 r0 = r1 = len(self.s.talon.cards)/3
m = 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[:r0], flip=0, frames=4)
self.s.talon.dealRow(rows=self.s.rows[:r1], flip=0, frames=4) self.s.talon.dealRow(rows=self.s.rows[:r1], flip=0, frames=4)
self.s.talon.dealRowAvail(frames=4) self.s.talon.dealRowAvail(frames=4)
@ -726,7 +736,7 @@ class Crescent(Game):
# register the 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)) GI.GT_FAN_TYPE | GI.GT_OPEN, 1, 0, GI.SL_MOSTLY_SKILL))
registerGame(GameInfo(87, ScotchPatience, "Scotch Patience", registerGame(GameInfo(87, ScotchPatience, "Scotch Patience",
GI.GT_FAN_TYPE | GI.GT_OPEN, 1, 0, GI.SL_MOSTLY_SKILL)) GI.GT_FAN_TYPE | GI.GT_OPEN, 1, 0, GI.SL_MOSTLY_SKILL))

View file

@ -542,6 +542,7 @@ class Octave_Talon(WasteTalonStack):
class Octave(Game): class Octave(Game):
Hint_Class = CautiousDefaultHint
# #
# game layout # game layout

View file

@ -51,37 +51,14 @@ from spider import Spider_AC_Foundation
# // FreeCell # // 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): class FreeCell(Game):
Layout_Method = Layout.freeCellLayout Layout_Method = Layout.freeCellLayout
Talon_Class = InitialDealTalonStack Talon_Class = InitialDealTalonStack
Foundation_Class = SS_FoundationStack Foundation_Class = SS_FoundationStack
RowStack_Class = FreeCell_RowStack RowStack_Class = SuperMoveAC_RowStack
ReserveStack_Class = ReserveStack 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): class RelaxedFreeCell(FreeCell):
RowStack_Class = AC_RowStack 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): class ForeCell(FreeCell):
RowStack_Class = StackWrapper(FreeCell_AC_RowStack, base_rank=KING) RowStack_Class = StackWrapper(FreeCell_AC_RowStack, base_rank=KING)
Hint_Class = FreeCellSolverWrapper(FreeCellType_Hint, {'esf' : "kings"}) Solver_Class = FreeCellSolverWrapper(esf='kings')
def startGame(self): def startGame(self):
for i in range(5): for i in range(5):
@ -159,7 +136,7 @@ class ChallengeFreeCell(FreeCell):
class SuperChallengeFreeCell(ChallengeFreeCell): class SuperChallengeFreeCell(ChallengeFreeCell):
RowStack_Class = StackWrapper(FreeCell_AC_RowStack, base_rank=KING) 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): class Stalactites(FreeCell):
Foundation_Class = StackWrapper(RK_FoundationStack, suit=ANY_SUIT, mod=13, min_cards=1) Foundation_Class = StackWrapper(RK_FoundationStack, suit=ANY_SUIT, mod=13, min_cards=1)
RowStack_Class = StackWrapper(BasicRowStack, max_move=1, max_accept=0) RowStack_Class = StackWrapper(BasicRowStack, max_move=1, max_accept=0)
Hint_Class = FreeCellType_Hint Solver_Class = None
def createGame(self): def createGame(self):
FreeCell.createGame(self, reserves=2) FreeCell.createGame(self, reserves=2)
@ -192,7 +169,7 @@ class Stalactites(FreeCell):
# ************************************************************************/ # ************************************************************************/
class DoubleFreecell(FreeCell): class DoubleFreecell(FreeCell):
Hint_Class = FreeCellType_Hint Solver_Class = None
# #
# game layout # game layout
@ -246,7 +223,7 @@ class DoubleFreecell(FreeCell):
# ************************************************************************/ # ************************************************************************/
class TripleFreecell(FreeCell): class TripleFreecell(FreeCell):
Hint_Class = FreeCellType_Hint Solver_Class = None
# #
# game layout # game layout
@ -267,8 +244,8 @@ class TripleFreecell(FreeCell):
s.talon = self.Talon_Class(l.XM, h-l.YS, self) s.talon = self.Talon_Class(l.XM, h-l.YS, self)
x, y = l.XM+(max_rows-decks*4)*l.XS/2, l.YM 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)) s.foundations.append(self.Foundation_Class(x, y, self, suit=j))
x += l.XS x += l.XS
x, y = l.XM+(max_rows-reserves)*l.XS/2, l.YM+l.YS x, y = l.XM+(max_rows-reserves)*l.XS/2, l.YM+l.YS
@ -323,16 +300,24 @@ class BigCell(TripleFreecell):
# // Spidercells # // Spidercells
# ************************************************************************/ # ************************************************************************/
class Spidercells_RowStack(FreeCell_RowStack): class Spidercells_RowStack(SuperMoveAC_RowStack):
def canMoveCards(self, cards): def canMoveCards(self, cards):
if len(cards) == 13 and isAlternateColorSequence(cards): if len(cards) == 13 and isAlternateColorSequence(cards):
return True 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): class Spidercells(FreeCell):
Hint_Class = FreeCellType_Hint Solver_Class = None
Foundation_Class = Spider_AC_Foundation Foundation_Class = Spider_AC_Foundation
RowStack_Class = Spidercells_RowStack RowStack_Class = Spidercells_RowStack
@ -361,8 +346,6 @@ class Spidercells(FreeCell):
# ************************************************************************/ # ************************************************************************/
class SevenByFour(FreeCell): class SevenByFour(FreeCell):
Hint_Class = FreeCellSolverWrapper(FreeCellType_Hint, {})
#Hint_Class = FreeCellType_Hint
def createGame(self): def createGame(self):
FreeCell.createGame(self, rows=7) FreeCell.createGame(self, rows=7)
def startGame(self): def startGame(self):
@ -377,9 +360,8 @@ class SevenByFive(SevenByFour):
FreeCell.createGame(self, rows=7, reserves=5) FreeCell.createGame(self, rows=7, reserves=5)
class Bath(FreeCell): class Bath(FreeCell):
Hint_Class = FreeCellSolverWrapper(FreeCellType_Hint, {'esf' : 'kings'}) Solver_Class = FreeCellSolverWrapper(esf='kings')
#Hint_Class = FreeCellType_Hint RowStack_Class = StackWrapper(SuperMoveAC_RowStack, base_rank=KING)
RowStack_Class = StackWrapper(FreeCell_RowStack, base_rank=KING)
def createGame(self): def createGame(self):
FreeCell.createGame(self, rows=10, reserves=2) FreeCell.createGame(self, rows=10, reserves=2)
def startGame(self): def startGame(self):
@ -395,7 +377,7 @@ class Bath(FreeCell):
# ************************************************************************/ # ************************************************************************/
class Clink(FreeCell): class Clink(FreeCell):
Hint_Class = FreeCellType_Hint Solver_Class = None
def createGame(self): def createGame(self):
# create layout # create layout
@ -440,7 +422,7 @@ class Clink(FreeCell):
# ************************************************************************/ # ************************************************************************/
class Repair(FreeCell): class Repair(FreeCell):
Hint_Class = FreeCellType_Hint Solver_Class = FreeCellSolverWrapper(sm='unlimited')
RowStack_Class = AC_RowStack RowStack_Class = AC_RowStack
def createGame(self): def createGame(self):
@ -464,7 +446,7 @@ class FourColours_RowStack(AC_RowStack):
return self.game.app.images.getReserveBottom() return self.game.app.images.getReserveBottom()
class FourColours(FreeCell): class FourColours(FreeCell):
Hint_Class = FreeCellType_Hint Solver_Class = None
RowStack_Class = AC_RowStack RowStack_Class = AC_RowStack
def createGame(self): def createGame(self):
@ -507,7 +489,7 @@ class GermanFreeCell_Reserve(ReserveStack):
class GermanFreeCell(SevenByFour): class GermanFreeCell(SevenByFour):
Hint_Class = FreeCellType_Hint Solver_Class = None
RowStack_Class = AC_RowStack RowStack_Class = AC_RowStack
ReserveStack_Class = GermanFreeCell_Reserve ReserveStack_Class = GermanFreeCell_Reserve
@ -524,7 +506,7 @@ class GermanFreeCell(SevenByFour):
# ************************************************************************/ # ************************************************************************/
class OceanTowers(TripleFreecell): class OceanTowers(TripleFreecell):
Hint_Class = FreeCellType_Hint Solver_Class = FreeCellSolverWrapper(esf='kings', sbb='suit')
RowStack_Class = StackWrapper(FreeCell_SS_RowStack, base_rank=KING) RowStack_Class = StackWrapper(FreeCell_SS_RowStack, base_rank=KING)
def createGame(self): def createGame(self):
@ -550,7 +532,7 @@ class KingCell_RowStack(RK_RowStack):
return len(cards) <= max_move and RK_RowStack.canMoveCards(self, cards) return len(cards) <= max_move and RK_RowStack.canMoveCards(self, cards)
class KingCell(FreeCell): class KingCell(FreeCell):
Hint_Class = FreeCellType_Hint Solver_Class = FreeCellSolverWrapper(esf='kings')
RowStack_Class = StackWrapper(KingCell_RowStack, base_rank=KING) RowStack_Class = StackWrapper(KingCell_RowStack, base_rank=KING)
shallHighlightMatch = Game._shallHighlightMatch_RK shallHighlightMatch = Game._shallHighlightMatch_RK
@ -612,6 +594,7 @@ class Headquarters(Game):
class CanCan(FreeCell): class CanCan(FreeCell):
Hint_Class = DefaultHint Hint_Class = DefaultHint
Solver_Class = None
RowStack_Class = KingAC_RowStack RowStack_Class = KingAC_RowStack
ReserveStack_Class = StackWrapper(OpenStack, max_accept=0) ReserveStack_Class = StackWrapper(OpenStack, max_accept=0)

View file

@ -291,6 +291,8 @@ class BlackHole_Foundation(AbstractFoundationStack):
r1, r2 = self.cards[-1].rank, cards[0].rank r1, r2 = self.cards[-1].rank, cards[0].rank
return (r1 + 1) % self.cap.mod == r2 or (r2 + 1) % self.cap.mod == r1 return (r1 + 1) % self.cap.mod == r2 or (r2 + 1) % self.cap.mod == r1
return 1 return 1
def getHelp(self):
return _('Foundation. Build up or down regardless of suit.')
class BlackHole_RowStack(ReserveStack): class BlackHole_RowStack(ReserveStack):

View file

@ -462,9 +462,9 @@ class BigBen(Game):
def startGame(self): def startGame(self):
self.startDealSample() 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): for i in range(3):
self.s.talon.dealRow() self.s.talon.dealRow(frames=4)
def _autoDeal(self, sound=1): def _autoDeal(self, sound=1):
# don't deal a card to the waste if the waste is empty # don't deal a card to the waste if the waste is empty

View file

@ -263,6 +263,7 @@ class BigDeal(DoubleKlondike):
# ************************************************************************/ # ************************************************************************/
class Delivery(BigDeal): class Delivery(BigDeal):
Hint_Class = CautiousDefaultHint
RowStack_Class = StackWrapper(SS_RowStack, max_move=1) RowStack_Class = StackWrapper(SS_RowStack, max_move=1)
def createGame(self): def createGame(self):

View file

@ -45,6 +45,7 @@ class HeadsAndTails_Reserve(OpenStack):
class HeadsAndTails(Game): class HeadsAndTails(Game):
Hint_Class = CautiousDefaultHint
# #
# game layout # game layout

View file

@ -41,6 +41,7 @@ from pysollib.stack import *
from pysollib.game import Game from pysollib.game import Game
from pysollib.layout import Layout from pysollib.layout import Layout
from pysollib.hint import DefaultHint, FreeCellType_Hint, CautiousDefaultHint from pysollib.hint import DefaultHint, FreeCellType_Hint, CautiousDefaultHint
from pysollib.hint import FreeCellSolverWrapper
from pysollib.pysoltk import MfxCanvasText from pysollib.pysoltk import MfxCanvasText
@ -63,6 +64,7 @@ class DerKatzenschwanz_Hint(FreeCellType_Hint):
class DerKatzenschwanz(Game): class DerKatzenschwanz(Game):
RowStack_Class = StackWrapper(AC_RowStack, base_rank=NO_RANK) RowStack_Class = StackWrapper(AC_RowStack, base_rank=NO_RANK)
Hint_Class = DerKatzenschwanz_Hint Hint_Class = DerKatzenschwanz_Hint
Solver_Class = FreeCellSolverWrapper(esf='none', sm='unlimited')
# #
# game layout # game layout
@ -145,6 +147,7 @@ class DerKatzenschwanz(Game):
class DieSchlange(DerKatzenschwanz): class DieSchlange(DerKatzenschwanz):
RowStack_Class = StackWrapper(FreeCell_AC_RowStack, base_rank=NO_RANK) RowStack_Class = StackWrapper(FreeCell_AC_RowStack, base_rank=NO_RANK)
Solver_Class = FreeCellSolverWrapper(esf='none')
def createGame(self): def createGame(self):
DerKatzenschwanz.createGame(self, rows=9, reserves=7) DerKatzenschwanz.createGame(self, rows=9, reserves=7)
@ -171,8 +174,8 @@ class DieSchlange(DerKatzenschwanz):
class Kings(DerKatzenschwanz): class Kings(DerKatzenschwanz):
##RowStack_Class = StackWrapper(AC_RowStack, base_rank=NO_RANK) RowStack_Class = StackWrapper(AC_RowStack, base_rank=NO_RANK)
RowStack_Class = StackWrapper(FreeCell_AC_RowStack, base_rank=NO_RANK) Solver_Class = FreeCellSolverWrapper(esf='none', sm='unlimited')
def createGame(self): def createGame(self):
return DerKatzenschwanz.createGame(self, rows=8, reserves=8) return DerKatzenschwanz.createGame(self, rows=8, reserves=8)
@ -192,8 +195,8 @@ class Kings(DerKatzenschwanz):
class Retinue(DieSchlange, Kings): class Retinue(DieSchlange, Kings):
##RowStack_Class = StackWrapper(AC_RowStack, base_rank=NO_RANK)
RowStack_Class = StackWrapper(FreeCell_AC_RowStack, base_rank=NO_RANK) RowStack_Class = StackWrapper(FreeCell_AC_RowStack, base_rank=NO_RANK)
Solver_Class = FreeCellSolverWrapper(esf='none')
def createGame(self): def createGame(self):
return DerKatzenschwanz.createGame(self, rows=8, reserves=8) return DerKatzenschwanz.createGame(self, rows=8, reserves=8)
@ -249,6 +252,7 @@ class SalicLaw_Talon(OpenTalonStack):
class SalicLaw(DerKatzenschwanz): class SalicLaw(DerKatzenschwanz):
Hint_Class = SalicLaw_Hint Hint_Class = SalicLaw_Hint
Solver_Class = None
Foundation_Classes = [ Foundation_Classes = [
StackWrapper(AbstractFoundationStack, max_cards=1, base_rank=QUEEN), StackWrapper(AbstractFoundationStack, max_cards=1, base_rank=QUEEN),
@ -340,6 +344,7 @@ class SalicLaw(DerKatzenschwanz):
class Deep(DerKatzenschwanz): class Deep(DerKatzenschwanz):
RowStack_Class = StackWrapper(AC_RowStack, base_rank=ANY_RANK) RowStack_Class = StackWrapper(AC_RowStack, base_rank=ANY_RANK)
Solver_Class = FreeCellSolverWrapper(sm='unlimited')
def createGame(self): def createGame(self):
return DerKatzenschwanz.createGame(self, rows=8, reserves=8) return DerKatzenschwanz.createGame(self, rows=8, reserves=8)
@ -499,6 +504,7 @@ class StepUp_RowStack(AC_RowStack):
class StepUp(Game): class StepUp(Game):
Hint_Class = CautiousDefaultHint
def createGame(self): def createGame(self):
l, s = Layout(self), self.s l, s = Layout(self), self.s

View file

@ -43,6 +43,7 @@ from pysollib.game import Game
from pysollib.layout import Layout from pysollib.layout import Layout
from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint
from pysollib.hint import KlondikeType_Hint from pysollib.hint import KlondikeType_Hint
from pysollib.hint import FreeCellSolverWrapper
from pysollib.pysoltk import MfxCanvasText from pysollib.pysoltk import MfxCanvasText
from canfield import CanfieldRush_Talon from canfield import CanfieldRush_Talon
@ -283,15 +284,14 @@ class BlindAlleys(Eastcliff):
# /*********************************************************************** # /***********************************************************************
# // Somerset # // Somerset
# // Morehead # // Morehead
# // Canister # // Usk
# // American Canister
# // British Canister
# ************************************************************************/ # ************************************************************************/
class Somerset(Klondike): class Somerset(Klondike):
Talon_Class = InitialDealTalonStack Talon_Class = InitialDealTalonStack
RowStack_Class = StackWrapper(AC_RowStack, max_move=1) RowStack_Class = SuperMoveAC_RowStack
Hint_Class = CautiousDefaultHint Hint_Class = CautiousDefaultHint
Solver_Class = FreeCellSolverWrapper()
def createGame(self): def createGame(self):
Klondike.createGame(self, max_rounds=1, rows=10, waste=0, texts=0) Klondike.createGame(self, max_rounds=1, rows=10, waste=0, texts=0)
@ -306,12 +306,14 @@ class Somerset(Klondike):
class Morehead(Somerset): class Morehead(Somerset):
RowStack_Class = StackWrapper(BO_RowStack, max_move=1) RowStack_Class = StackWrapper(BO_RowStack, max_move=1)
Solver_Class = None
class Usk(Somerset): class Usk(Somerset):
Talon_Class = RedealTalonStack Talon_Class = RedealTalonStack
RowStack_Class = StackWrapper(AC_RowStack, base_rank=KING) RowStack_Class = StackWrapper(AC_RowStack, base_rank=KING)
Solver_Class = None
def createGame(self): def createGame(self):
Klondike.createGame(self, max_rounds=2, rows=10, waste=0, texts=0) Klondike.createGame(self, max_rounds=2, rows=10, waste=0, texts=0)
@ -330,6 +332,8 @@ class Usk(Somerset):
class AmericanCanister(Klondike): class AmericanCanister(Klondike):
Talon_Class = InitialDealTalonStack Talon_Class = InitialDealTalonStack
RowStack_Class = AC_RowStack
Solver_Class = FreeCellSolverWrapper(sm='unlimited')
def createGame(self): def createGame(self):
Klondike.createGame(self, max_rounds=1, rows=8, waste=0, texts=0) Klondike.createGame(self, max_rounds=1, rows=8, waste=0, texts=0)
@ -344,11 +348,13 @@ class AmericanCanister(Klondike):
class Canister(AmericanCanister): class Canister(AmericanCanister):
RowStack_Class = RK_RowStack RowStack_Class = RK_RowStack
Solver_Class = FreeCellSolverWrapper(sbb='rank', sm='unlimited')
shallHighlightMatch = Game._shallHighlightMatch_RK shallHighlightMatch = Game._shallHighlightMatch_RK
class BritishCanister(AmericanCanister): class BritishCanister(AmericanCanister):
RowStack_Class = StackWrapper(KingAC_RowStack, max_move=1) RowStack_Class = StackWrapper(KingAC_RowStack, max_move=1)
Solver_Class = FreeCellSolverWrapper(esf='kings')
# /*********************************************************************** # /***********************************************************************
@ -813,6 +819,7 @@ class Alternation(Klondike):
class Lanes(Klondike): class Lanes(Klondike):
Hint_Class = CautiousDefaultHint
Foundation_Class = StackWrapper(SS_FoundationStack, max_move=0) Foundation_Class = StackWrapper(SS_FoundationStack, max_move=0)
RowStack_Class = StackWrapper(AC_RowStack, base_rank=ANY_RANK, max_move=1) 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): def shallHighlightMatch(self, stack1, card1, stack2, card2):
return abs(card1.rank-card2.rank) == 2 return abs(card1.rank-card2.rank) == 2
shallHighlightMatch = Game._shallHighlightMatch_RKW
# /*********************************************************************** # /***********************************************************************
# // Seven Devils # // Seven Devils
@ -1176,6 +1185,7 @@ class GoldMine(Klondike):
# ************************************************************************/ # ************************************************************************/
class LuckyThirteen(Game): class LuckyThirteen(Game):
Hint_Class = CautiousDefaultHint
RowStack_Class = StackWrapper(RK_RowStack, base_rank=NO_RANK) RowStack_Class = StackWrapper(RK_RowStack, base_rank=NO_RANK)
def createGame(self, xoffset=0, playcards=0): def createGame(self, xoffset=0, playcards=0):

View file

@ -95,8 +95,8 @@ class Mahjongg_Foundation(OpenStack):
def basicIsBlocked(self): def basicIsBlocked(self):
return 1 return 1
def initBindings(self): #def initBindings(self):
pass # pass
def _position(self, card): def _position(self, card):
#AbstractFoundationStack._position(self, card) #AbstractFoundationStack._position(self, card)
@ -360,11 +360,13 @@ class AbstractMahjonggGame(Game):
if self.preview: if self.preview:
# Fixme # Fixme
dx, dy, d_x, d_y = dx/2, dy/2, d_x/2, d_y/2 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: else:
dx = 3 dx = 3
dy = -3 dy = -3
d_x = 0 d_x = 0
d_y = 0 d_y = 0
self._delta_x, self._delta_y = 0, 0
#print dx, dy, d_x, d_y, cs.version #print dx, dy, d_x, d_y, cs.version
font = self.app.getFont("canvas_default") font = self.app.getFont("canvas_default")
@ -472,7 +474,7 @@ class AbstractMahjonggGame(Game):
y = l.YM+fdyy+j*cardh y = l.YM+fdyy+j*cardh
else: else:
if TOOLKIT == 'tk': if TOOLKIT == 'tk':
x = -l.XS x = -l.XS-self.canvas.xmargin
y = l.YM+dyy y = l.YM+dyy
elif TOOLKIT == 'gtk': elif TOOLKIT == 'gtk':
# FIXME # FIXME
@ -635,6 +637,10 @@ class AbstractMahjonggGame(Game):
# Mahjongg special: highlight all moveable tiles # Mahjongg special: highlight all moveable tiles
return ((self.s.rows, 1),) 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): def getCardFaceImage(self, deck, suit, rank):
if suit == 3: if suit == 3:
cs = self.app.cardset cs = self.app.cardset
@ -690,6 +696,7 @@ class AbstractMahjonggGame(Game):
return 7 >= card2.rank >= 4 return 7 >= card2.rank >= 4
return card1.rank == card2.rank return card1.rank == card2.rank
## mahjongg util ## mahjongg util
def comp_cardset(ncards): def comp_cardset(ncards):
# calc decks, ranks & trumps # calc decks, ranks & trumps

View file

@ -254,13 +254,14 @@ class Shisen_RowStack(Mahjongg_RowStack):
cardw = images.CARDW cardw = images.CARDW
cardh = images.CARDH cardh = images.CARDH
coords = [] coords = []
dx, dy = game._delta_x, game._delta_y
for x, y in path: for x, y in path:
if x == 0: if x == 0:
coords.append(6) coords.append(6)
elif x == game.L[0]+1: elif x == game.L[0]+1:
coords.append(x0+cardw*(x-1)+10) coords.append(x0+cardw*(x-1)+10+dx)
else: else:
coords.append(x0+cardw/2+cardw*(x-1)) coords.append(x0+cardw/2+cardw*(x-1)+dx)
if y == 0: if y == 0:
coords.append(6) coords.append(6)
elif y == game.L[1]+1: elif y == game.L[1]+1:
@ -308,11 +309,13 @@ class AbstractShisenGame(AbstractMahjonggGame):
dy = -l.YOFFSET dy = -l.YOFFSET
d_x = cs.SHADOW_XOFFSET d_x = cs.SHADOW_XOFFSET
d_y = cs.SHADOW_YOFFSET d_y = cs.SHADOW_YOFFSET
self._delta_x, self._delta_y = dx, -dy
else: else:
dx = 3 dx = 3
dy = -3 dy = -3
d_x = 0 d_x = 0
d_y = 0 d_y = 0
self._delta_x, self._delta_y = 0, 0
font = self.app.getFont("canvas_default") font = self.app.getFont("canvas_default")
@ -358,10 +361,9 @@ class AbstractShisenGame(AbstractMahjonggGame):
self.texts.info = MfxCanvasText(self.canvas, self.texts.info = MfxCanvasText(self.canvas,
self.width - l.XM - ti_width, y, self.width - l.XM - ti_width, y,
anchor="nw", font=font) anchor="nw", font=font)
x = self.width + l.XS
y = self.height - l.YS - dxx
# the Talon is invisble # 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 # Define stack groups
l.defaultStackGroups() l.defaultStackGroups()

View file

@ -716,7 +716,7 @@ class Assembly_RowStack(RK_RowStack):
class Assembly(Numerica): class Assembly(Numerica):
Hint_Class = DefaultHint Hint_Class = CautiousDefaultHint
Foundation_Class = StackWrapper(RK_FoundationStack, suit=ANY_SUIT) Foundation_Class = StackWrapper(RK_FoundationStack, suit=ANY_SUIT)
RowStack_Class = StackWrapper(Assembly_RowStack, max_move=1) RowStack_Class = StackWrapper(Assembly_RowStack, max_move=1)

View file

@ -274,7 +274,7 @@ class Pyramid(Game):
def startGame(self): def startGame(self):
self.startDealSample() self.startDealSample()
self.s.talon.dealRow() self.s.talon.dealRow(frames=4)
self.s.talon.dealCards() # deal first card to WasteStack self.s.talon.dealCards() # deal first card to WasteStack
def getAutoStacks(self, event=None): def getAutoStacks(self, event=None):
@ -585,7 +585,7 @@ class Fifteens_RowStack(Elevens_RowStack):
def acceptsCards(self, from_stack, cards): def acceptsCards(self, from_stack, cards):
if not Elevens_RowStack.acceptsCards(self, from_stack, cards): if not Elevens_RowStack.acceptsCards(self, from_stack, cards):
return False 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): class Fifteens_Reserve(ReserveStack):
@ -919,7 +919,7 @@ class Apophis(Pharaohs):
def startGame(self): def startGame(self):
self.startDealSample() self.startDealSample()
self.s.talon.dealRow(frames=3) self.s.talon.dealRow(frames=4)
self.s.talon.dealCards() self.s.talon.dealCards()
def shallHighlightMatch(self, stack1, card1, stack2, card2): def shallHighlightMatch(self, stack1, card1, stack2, card2):
@ -954,7 +954,7 @@ class Cheops_RowStack(Cheops_StackMethods, Pyramid_RowStack):
class Cheops(Pyramid): class Cheops(Pyramid):
Foundation_Class = AbstractFoundationStack Foundation_Class = StackWrapper(AbstractFoundationStack, max_accept=0)
Talon_Class = StackWrapper(Cheops_Talon, max_rounds=1, max_accept=1) Talon_Class = StackWrapper(Cheops_Talon, max_rounds=1, max_accept=1)
RowStack_Class = Cheops_RowStack RowStack_Class = Cheops_RowStack
WasteStack_Class = Cheops_Waste WasteStack_Class = Cheops_Waste

View file

@ -592,6 +592,7 @@ class ThreePirates_Talon(DealRowTalonStack):
class ThreePirates(Game): class ThreePirates(Game):
Hint_Class = CautiousDefaultHint
def createGame(self): def createGame(self):
l, s = Layout(self), self.s l, s = Layout(self), self.s
@ -634,6 +635,20 @@ class ThreePirates(Game):
# // Frames # // 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): class Frames_Foundation(UnionSquare_Foundation):
def acceptsCards(self, from_stack, cards): def acceptsCards(self, from_stack, cards):
if not UnionSquare_Foundation.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): class Frames(Game):
Hint_Class = CautiousDefaultHint Hint_Class = Frames_Hint #CautiousDefaultHint
def createGame(self): def createGame(self):
l, s = Layout(self), self.s l, s = Layout(self), self.s
@ -1053,7 +1068,7 @@ class Colonel(Game):
x, y = l.XM+2*l.XS, l.YM x, y = l.XM+2*l.XS, l.YM
for i in range(8): 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)) max_move=0))
x += l.XS x += l.XS
@ -1079,7 +1094,7 @@ class Colonel(Game):
def startGame(self): def startGame(self):
self.startDealSample() self.startDealSample()
self.s.talon.dealRow() self.s.talon.dealRow(frames=4)
self.s.talon.dealCards() self.s.talon.dealCards()
shallHighlightMatch = Game._shallHighlightMatch_SS shallHighlightMatch = Game._shallHighlightMatch_SS

View file

@ -161,6 +161,19 @@ class SiebenBisAs(Game):
# // Maze # // 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): class Maze_RowStack(BasicRowStack):
def acceptsCards(self, from_stack, cards): def acceptsCards(self, from_stack, cards):
if not BasicRowStack.acceptsCards(self, from_stack, cards): if not BasicRowStack.acceptsCards(self, from_stack, cards):
@ -189,7 +202,7 @@ class Maze_RowStack(BasicRowStack):
class Maze(Game): class Maze(Game):
GAME_VERSION = 2 GAME_VERSION = 2
Hint_Class = SiebenBisAs_Hint Hint_Class = Maze_Hint #SiebenBisAs_Hint
# #
# game layout # game layout

View file

@ -42,6 +42,7 @@ from pysollib.game import Game
from pysollib.layout import Layout from pysollib.layout import Layout
from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint
from pysollib.hint import SpiderType_Hint, YukonType_Hint 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) 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 # // Relaxed Spider
# ************************************************************************/ # ************************************************************************/
@ -282,6 +300,8 @@ class WillOTheWisp(Spiderette):
class SimpleSimon(Spider): class SimpleSimon(Spider):
Talon_Class = InitialDealTalonStack Talon_Class = InitialDealTalonStack
RowStack_Class = SuperMoveSpider_RowStack
Solver_Class = FreeCellSolverWrapper(preset='simple_simon', base_rank=0)
def createGame(self): def createGame(self):
Spider.createGame(self, rows=10, texts=0) Spider.createGame(self, rows=10, texts=0)
@ -292,6 +312,12 @@ class SimpleSimon(Spider):
self.startDealSample() self.startDealSample()
self.s.talon.dealRow() 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 # // Rachel
@ -1370,4 +1396,6 @@ registerGame(GameInfo(685, FechtersGame, "Fechter's Game",
GI.GT_SPIDER, 2, 0, GI.SL_MOSTLY_SKILL)) GI.GT_SPIDER, 2, 0, GI.SL_MOSTLY_SKILL))
registerGame(GameInfo(710, Bebop, "Bebop", registerGame(GameInfo(710, Bebop, "Bebop",
GI.GT_2DECK_TYPE | GI.GT_ORIGINAL, 2, 0, GI.SL_MOSTLY_SKILL)) 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))

View file

@ -762,13 +762,13 @@ class RoyalAids(Game):
s.waste = WasteStack(x, y, self) s.waste = WasteStack(x, y, self)
l.createText(s.waste, 'se') 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): for i in (0,1):
stack = RoyalAids_RowStack(x, y, self, max_move=1) stack = RoyalAids_RowStack(x, y, self, max_move=1)
s.rows.append(stack) s.rows.append(stack)
stack.CARD_XOFFSET, stack.CARD_YOFFSET = 0, 0 stack.CARD_XOFFSET, stack.CARD_YOFFSET = 0, 0
x += l.XS 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): for i in range(4):
stack = BasicRowStack(x, y, self) stack = BasicRowStack(x, y, self)
s.reserves.append(stack) s.reserves.append(stack)
@ -995,7 +995,7 @@ class Khedive(Game):
def startGame(self): def startGame(self):
self.startDealSample() self.startDealSample()
self.s.talon.dealRow() self.s.talon.dealRow(frames=4)
self.s.talon.dealCards() self.s.talon.dealCards()
def fillStack(self, stack): def fillStack(self, stack):

View file

@ -365,9 +365,9 @@ class BastilleDay_BastilleStack(Stack):
old_state = self.game.enterState(self.game.S_DEAL) old_state = self.game.enterState(self.game.S_DEAL)
if sound and not self.game.demo: if sound and not self.game.demo:
self.game.playSample("dealwaste") self.game.playSample("dealwaste")
self.flipMove() self.game.flipAndMoveMove(self, self.game.s.reserves[-1])
self.moveMove(1, self.game.s.reserves[-1], frames=4, shadow=0)
self.game.leaveState(old_state) self.game.leaveState(old_state)
self.game.finishMove()
return 1 return 1
def getHelp(self): def getHelp(self):
@ -432,7 +432,7 @@ class BastilleDay(Game):
if self.demo: if self.demo:
r = self.s.reserves[0] r = self.s.reserves[0]
if r.canDealCards(): 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 r.dealCards(sound=sound)
return Game.dealCards(self, sound=sound) return Game.dealCards(self, sound=sound)

View file

@ -159,6 +159,7 @@ class DutchSolitaire_RowStack(UD_RK_RowStack):
class DutchSolitaire(Windmill): class DutchSolitaire(Windmill):
Hint_Class = CautiousDefaultHint
Foundation_Classes = [ Foundation_Classes = [
StackWrapper(BlackHole_Foundation, suit=ANY_SUIT, mod=13, StackWrapper(BlackHole_Foundation, suit=ANY_SUIT, mod=13,
max_cards=UNLIMITED_CARDS, min_cards=1), 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) self.setSize(5*l.XS+l.XM, 4*l.YS+3*l.YM)
# create stacks # 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) s.talon = WasteTalonStack(x, y, self, max_rounds=max_rounds)
l.createText(s.talon, "sw") l.createText(s.talon, "sw")
x += l.XS x += l.XS

View file

@ -43,6 +43,7 @@ from pysollib.game import Game
from pysollib.layout import Layout from pysollib.layout import Layout
from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint
from pysollib.hint import YukonType_Hint from pysollib.hint import YukonType_Hint
from pysollib.hint import FreeCellSolverWrapper
from pysollib.pysoltk import MfxCanvasText from pysollib.pysoltk import MfxCanvasText
from spider import Spider_SS_Foundation from spider import Spider_SS_Foundation

View file

@ -36,8 +36,10 @@
# imports # imports
import os, sys import os, sys
import time
# PySol imports # PySol imports
from settings import USE_FREECELL_SOLVER, FCS_COMMAND
from mfxutil import destruct from mfxutil import destruct
from util import KING from util import KING
@ -689,289 +691,234 @@ class SpiderType_Hint(DefaultHint):
# /*********************************************************************** # /***********************************************************************
# // # // FreeCell-Solver
# ************************************************************************/ # ************************************************************************/
FreecellSolver = None
## try: class FreeCellSolver_Hint:
## import FreecellSolver
## except:
## FreecellSolver = None
fcs_command = 'fc-solve' def __init__(self, game, dialog, **game_type):
if os.name == 'nt': self.game = game
if sys.path[0] and not os.path.isdir(sys.path[0]): # i.e. library.zip self.dialog = dialog
fcs_command = os.path.join(os.path.split(sys.path[0])[0], 'fc-solve.exe') self.game_type = game_type
fcs_command = '"%s"' % fcs_command 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'): # correct cards rank if foundations.base_rank != 0 (Penguin, Opus)
try: if 'base_rank' in game_type: # (Simple Simon)
pin, pout, perr = os.popen3(fcs_command+' --help') self.base_rank = game_type['base_rank']
if pout.readline().startswith('fc-solve'): else:
FreecellSolver = True self.base_rank = game.s.foundations[0].cap.base_rank
del pin, pout, perr ##print 'game_type:', game_type
if os.name == 'posix': ##print 'base_rank:', self.base_rank
os.wait()
except: def config(self, **kw):
##traceback.print_exc() self.options.update(kw)
pass
class FreeCellSolverWrapper: def card2str1(self, card):
__name__ = 'FreeCellSolverWrapper' # for gameinfodialog # 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): # hard solvable: Freecell #47038300998351211829 (65539 iters)
def str1(self, card):
return "A23456789TJQK"[card.rank] + "CSHD"[card.suit]
def str2(self, card):
return "CSHD"[card.suit] + "-" + "A23456789TJQK"[card.rank]
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): def computeHints(self):
##print 'FreeCellSolver_Hint.computeHints' game = self.game
game_type = self.game_type
progress = self.options['progress']
board = '' board = ''
pboard = {} #
# #
b = '' b = ''
#l = []
for s in self.game.s.foundations: for s in self.game.s.foundations:
if s.cards: if s.cards:
b = b + ' ' + self.str2(s.cards[-1]) ss = self.card2str2(s.cards[-1])
#l.append(self.str2(s.cards[-1])) b += ' ' + ss
if b: if b:
board = board + 'Founds:' + b + '\n' board += 'Founds:' + b + '\n'
#pboard['Founds'] = l
# #
b = '' b = ''
l = []
for s in self.game.s.reserves: for s in self.game.s.reserves:
if s.cards: if s.cards:
cs = self.str1(s.cards[-1]) cs = self.card2str1(s.cards[-1])
b = b + ' ' + cs b += ' ' + cs
l.append(cs)
else: else:
b = b + ' -' b += ' -'
l.append(None)
if b: if b:
board = board + 'FC:' + b + '\n' board += 'FC:' + b + '\n'
pboard['FC'] = l
# #
n = 0
for s in self.game.s.rows: for s in self.game.s.rows:
b = '' b = ''
l = []
for c in s.cards: for c in s.cards:
cs = self.str1(c) cs = self.card2str1(c)
if not c.face_up: if not c.face_up:
cs = '<%s>' % cs cs = '<%s>' % cs
b = b + cs + ' ' b += cs + ' '
l.append(cs)
board = board + b.strip() + '\n' board = board + b.strip() + '\n'
pboard[n] = l
n += 1
# #
##print board ##print '--------------------\n', board, '--------------------'
# #
args = [] args = []
args += ['-sam', '-p', '--display-10-as-t'] ##args += ['-sam', '-p', '-opt', '--display-10-as-t']
##args += ['-l', 'good-intentions'] args += ['-m', '-p', '-opt']
args += ['--max-iters', 200000] if self.options['preset'] and self.options['preset'] != 'none':
args += ['--decks-num', self.fcs_args[0], args += ['-l', self.options['preset']]
'--stacks-num', self.fcs_args[1], if progress:
'--freecells-num', self.fcs_args[2], 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),
] ]
# #
game_type = self.fcs_args[3] if 'preset' in game_type:
args += ['--preset', game_type['preset']]
if 'sbb' in game_type: if 'sbb' in game_type:
args += ['--sequences-are-built-by', game_type['sbb']] args += ['--sequences-are-built-by', game_type['sbb']]
if 'sm' in game_type: if 'sm' in game_type:
args += ['--sequence-move', game_type['sm']] args += ['--sequence-move', game_type['sm']]
if 'esf' in game_type: if 'esf' in game_type:
args += ['--empty-stacks-filled-by', game_type['esf']] args += ['--empty-stacks-filled-by', game_type['esf']]
if 'preset' in game_type:
args += ['--preset', game_type['preset']]
command = fcs_command+' '+' '.join([str(i) for i in args]) command = FCS_COMMAND+' '+' '.join([str(i) for i in args])
##print command ##print command
pin, pout, perr = os.popen3(command) pin, pout, perr = os.popen3(command)
pin.write(board) pin.write(board)
pin.close() pin.close()
# #
stack_types = { stack_types = {
'the' : self.game.s.foundations, 'the' : game.s.foundations,
'stack' : self.game.s.rows, 'stack' : game.s.rows,
'freecell' : self.game.s.reserves, 'freecell' : game.s.reserves,
} }
my_hints = [] ##start_time = time.time()
pboard_n = 0 if progress:
##print pboard # iteration output
iter = 0
depth = 0
states = 0
for s in pout: for s in pout:
##print s, ##print s,
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)
hints = []
for s in pout:
#print s,
# TODO:
# Total number of states checked is 6.
# This scan generated 6 states.
if not s.startswith('Move'): 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 continue
words = s.split() words = s.split()
ncards = words[1] ncards = words[1]
if ncards == 'a': if ncards == 'the':
# "Move the sequence on top of Stack 1 to the foundations"
# (Simple Simon)
ncards = 0
elif ncards == 'a':
ncards = 1 ncards = 1
else: else:
ncards = int(ncards) ncards = int(ncards)
sn = int(words[5]) if ncards:
st = stack_types[words[4]] st = stack_types[words[4]]
src = st[sn] sn = int(words[5])
src = st[sn] # source stack
if words[7] == 'the': if words[7] == 'the':
# to foundation # 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 dest = None
for f in self.game.s.foundations:
if f.cap.base_suit == suit:
dest = f
break
assert dest is not None
else: else:
# to rows or reserves # to rows or reserves
dt = stack_types[words[7]] dt = stack_types[words[7]]
dn = int(words[8]) dn = int(words[8])
dest = dt[dn] 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]) hints.append([ncards, src, dest])
##print src, dest, ncards ##print src, dest, ncards
pboard = {} #
pboard_n = 0 ##print 'time:', time.time()-start_time
##print perr.read(),
self.hints = hints
self.hints.append(None) # XXX
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 ##print self.hints
pout.close()
perr.close()
if os.name == 'posix': if os.name == 'posix':
os.wait() os.wait()
def computeHints_mod(self): class FreeCellSolverWrapper:
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],
])
game_type = self.fcs_args[3] def __init__(self, **game_type):
game_type_defaults = {'sbb' : 'alternate_color', 'sm' : 'limited', 'esf': 'all'} self.game_type = game_type
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'], def __call__(self, game, dialog):
"--sequence-move", game_type['sm'], hint = FreeCellSolver_Hint(game, dialog, **self.game_type)
"--empty-stacks-filled-by", game_type['esf'],
])
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("<BBB", h[i:i+3])
# hint = (999999, 0, v[0], map[v[1]], map[v[2]], None, hint)
#self.hints.append(hint)
def __init__(self, Fallback_Class, *args):
self.Fallback_Class = Fallback_Class
self.args = args
def __call__(self, game, level):
if FreecellSolver is None:
hint = self.Fallback_Class(game, level)
else:
hint = self.FreeCellSolver_Hint(game, level)
hint.fcs_args = (game.gameinfo.decks, len(game.s.rows), len(game.s.reserves)) + self.args
return hint return hint

View file

@ -206,7 +206,7 @@ class Images:
pass pass
if progress: progress.update(step=pstep) if progress: progress.update(step=pstep)
# shadow # shadow
if TOOLKIT == 'tk' and Image: if 0 and TOOLKIT == 'tk' and Image:
fn = self.d.findImage('shadow', 'images') fn = self.d.findImage('shadow', 'images')
self._pil_shadow_image = Image.open(fn).convert('RGBA') self._pil_shadow_image = Image.open(fn).convert('RGBA')
else: else:
@ -302,7 +302,6 @@ class Images:
y0, y1 = min(y1, y0), max(y1, y0) y0, y1 = min(y1, y0), max(y1, y0)
x1 = x1 + self.CARDW x1 = x1 + self.CARDW
y1 = y1 + self.CARDH y1 = y1 + self.CARDH
#xx0, yy0 = x0, y0
w, h = x1-x0, y1-y0 w, h = x1-x0, y1-y0
if (w,h) in self._pil_shadow: if (w,h) in self._pil_shadow:
return self._pil_shadow[(w,h)] return self._pil_shadow[(w,h)]

View file

@ -76,7 +76,7 @@ def init():
if settings.TOOLKIT == 'tk': if settings.TOOLKIT == 'tk':
import Tkinter import Tkinter
from Tkinter import TclError from Tkinter import TclError
root = Tkinter.Tk() root = Tkinter.Tk(className='PySol')
settings.WIN_SYSTEM = root.tk.call('tk', 'windowingsystem') settings.WIN_SYSTEM = root.tk.call('tk', 'windowingsystem')
if settings.WIN_SYSTEM == 'aqua': if settings.WIN_SYSTEM == 'aqua':
# TkAqua displays the console automatically in application # TkAqua displays the console automatically in application
@ -97,7 +97,35 @@ def init():
# "can't invoke event <<ThemeChanged>>: application has been destroyed" # "can't invoke event <<ThemeChanged>>: application has been destroyed"
#root.destroy() #root.destroy()
Tkinter._default_root = None 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: if '--no-games-menu' in sys.argv:
sys.argv.remove('--no-games-menu') sys.argv.remove('--no-games-menu')
settings.SELECT_GAME_MENU = False settings.SELECT_GAME_MENU = False

View file

@ -36,6 +36,7 @@
# imports # imports
import os, time, types import os, time, types
import webbrowser
try: try:
from cPickle import Pickler, Unpickler, UnpicklingError from cPickle import Pickler, Unpickler, UnpicklingError
@ -319,10 +320,10 @@ def unpickle(filename):
def openURL(url): def openURL(url):
try: try:
import webbrowser
webbrowser.open(url) webbrowser.open(url)
return 1 except OSError: # raised on windows if link is unreadable
except ImportError: # FIXME pass
except:
return 0 return 0
return 1

View file

@ -79,25 +79,30 @@ class AMoveMove(AtomicMove):
self.shadow = shadow self.shadow = shadow
# do the actual move # 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: if game.moves.state == game.S_PLAY:
assert to_stack.acceptsCards(from_stack, from_stack.cards[-ncards:]) 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:] cards = from_stack.cards[-ncards:]
if self.frames != 0: if frames != 0:
from_stack.unshadeStack()
x, y = to_stack.getPositionForNextCard() x, y = to_stack.getPositionForNextCard()
game.animatedMoveTo(from_stack, to_stack, cards, x, y, 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): for i in range(ncards):
from_stack.removeCard() from_stack.removeCard()
for c in cards: for c in cards:
to_stack.addCard(c) to_stack.addCard(c)
def redo(self, game): def redo(self, game):
self.__doMove(game, self.ncards, game.allstacks[self.from_stack_id], self._doMove(game, self.ncards, game.allstacks[self.from_stack_id],
game.allstacks[self.to_stack_id]) game.allstacks[self.to_stack_id])
def undo(self, game): def undo(self, game):
self.__doMove(game, self.ncards, game.allstacks[self.to_stack_id], self._doMove(game, self.ncards, game.allstacks[self.to_stack_id],
game.allstacks[self.from_stack_id]) game.allstacks[self.from_stack_id])
def cmpForRedo(self, other): def cmpForRedo(self, other):
@ -115,7 +120,26 @@ class AFlipMove(AtomicMove):
self.stack_id = stack.id self.stack_id = stack.id
# do the actual move # 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] card = stack.cards[-1]
game.animatedFlip(stack) game.animatedFlip(stack)
if card.face_up: if card.face_up:
@ -123,14 +147,46 @@ class AFlipMove(AtomicMove):
else: else:
card.showFace() 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): 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): 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): 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 self.update_flags = update_flags
# do the actual turning move # 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(from_stack.cards) > 0
assert len(to_stack.cards) == 0 assert len(to_stack.cards) == 0
for card in from_stack.cards: for card in from_stack.cards:
@ -272,7 +328,7 @@ class NEW_ATurnStackMove(AtomicMove):
assert to_stack is game.s.talon assert to_stack is game.s.talon
assert to_stack.round < to_stack.max_rounds or to_stack.max_rounds < 0 assert to_stack.round < to_stack.max_rounds or to_stack.max_rounds < 0
to_stack.round = to_stack.round + 1 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): def undo(self, game):
from_stack = game.allstacks[self.from_stack_id] 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 is game.s.talon
assert to_stack.round > 1 assert to_stack.round > 1
to_stack.round = 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): def cmpForRedo(self, other):
return (cmp(self.from_stack_id, other.from_stack_id) or return (cmp(self.from_stack_id, other.from_stack_id) or
@ -300,7 +356,7 @@ class AUpdateStackMove(AtomicMove):
self.flags = flags self.flags = flags
# do the actual move # do the actual move
def __doMove(self, game, stack, undo): def _doMove(self, game, stack, undo):
if self.flags & 64: if self.flags & 64:
# model # model
stack.updateModel(undo, self.flags) stack.updateModel(undo, self.flags)
@ -313,11 +369,11 @@ class AUpdateStackMove(AtomicMove):
def redo(self, game): def redo(self, game):
if (self.flags & 3) in (1, 3): 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): def undo(self, game):
if (self.flags & 3) in (2, 3): 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): def cmpForRedo(self, other):
return cmp(self.stack_id, other.stack_id) or cmp(self.flags, other.flags) return cmp(self.stack_id, other.stack_id) or cmp(self.flags, other.flags)

View file

@ -59,8 +59,6 @@ class ColorsDialog:
self._setColor(n, app.opt.colors[n]) self._setColor(n, app.opt.colors[n])
button = self.widgets_tree.get_widget(n+'_button') button = self.widgets_tree.get_widget(n+'_button')
button.connect('clicked', self._changeColor, n) 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() self._translateLabels()
@ -79,7 +77,6 @@ class ColorsDialog:
w = self.widgets_tree.get_widget(n+'_label') w = self.widgets_tree.get_widget(n+'_label')
c = w.get_data('user_data') c = w.get_data('user_data')
setattr(self, n+'_color', c) setattr(self, n+'_color', c)
self.use_default_color = not checkbutton.get_active()
dialog.destroy() dialog.destroy()
@ -125,9 +122,8 @@ class ColorsDialog:
'label51', 'label51',
'label52', 'label52',
'label53', 'label53',
'label79',
): ):
w = self.widgets_tree.get_widget(n) w = self.widgets_tree.get_widget(n)
w.set_text(gettext(w.get_text())) w.set_text(gettext(w.get_text()))
w = self.widgets_tree.get_widget('use_default_checkbutton')
w.set_label(gettext(w.get_label()))

View file

@ -289,10 +289,11 @@ class PysolMenubar(PysolMenubarActions):
# #
animations_entries = ( animations_entries = (
('animationnone', None, ltk2gtk('&None'), None, None, 0), ('animationnone', None, ltk2gtk('&None'), None, None, 0),
('animationfast', None, ltk2gtk('&Fast'), None, None, 1), ('animationveryfast', None, ltk2gtk('&Very fast'), None, None, 1),
('animationtimer', None, ltk2gtk('&Timer based'), None, None, 2), ('animationfast', None, ltk2gtk('&Fast'), None, None, 2),
('animationslow', None, ltk2gtk('&Slow'), None, None, 3), ('animationmedium', None, ltk2gtk('&Medium'), None, None, 3),
('animationveryslow', None, ltk2gtk('&Very slow'), None, None, 4), ('animationslow', None, ltk2gtk('&Slow'), None, None, 4),
('animationveryslow', None, ltk2gtk('V&ery slow'), None, None, 5),
) )
mouse_entries = ( mouse_entries = (
('draganddrop', None, ltk2gtk('&Drag-and-Drop'), None, None, 0), ('draganddrop', None, ltk2gtk('&Drag-and-Drop'), None, None, 0),
@ -388,8 +389,9 @@ class PysolMenubar(PysolMenubarActions):
<menuitem action='tabletile'/> <menuitem action='tabletile'/>
<menu action='animations'> <menu action='animations'>
<menuitem action='animationnone'/> <menuitem action='animationnone'/>
<menuitem action='animationtimer'/> <menuitem action='animationveryfast'/>
<menuitem action='animationfast'/> <menuitem action='animationfast'/>
<menuitem action='animationmedium'/>
<menuitem action='animationslow'/> <menuitem action='animationslow'/>
<menuitem action='animationveryslow'/> <menuitem action='animationveryslow'/>
</menu> </menu>
@ -831,7 +833,6 @@ class PysolMenubar(PysolMenubarActions):
self._cancelDrag() self._cancelDrag()
self.game.endGame(bookmark=1) self.game.endGame(bookmark=1)
self.game.quitGame(bookmark=1) self.game.quitGame(bookmark=1)
self.app.opt.games_geometry = {} # clear saved games geometry
def mOptToggle(self, w, opt_name, update_game): def mOptToggle(self, w, opt_name, update_game):

View file

@ -389,7 +389,7 @@ class SelectGameDialogWithPreview(MfxDialog):
destruct(self.preview_app) destruct(self.preview_app)
self.preview_app = None self.preview_app = None
def updatePreview(self, gameid, animations=5): def updatePreview(self, gameid, animations=10):
if gameid == self.preview_key: if gameid == self.preview_key:
return return
self.deletePreview() self.deletePreview()
@ -456,7 +456,7 @@ class SelectGameDialogWithPreview(MfxDialog):
# #
self.preview_app.audio = self.app.audio self.preview_app.audio = self.app.audio
if self.app.opt.animations: if self.app.opt.animations:
self.preview_app.opt.animations = 5 self.preview_app.opt.animations = 10
else: else:
self.preview_app.opt.animations = 0 self.preview_app.opt.animations = 0
# save seed # save seed
@ -514,14 +514,13 @@ class SelectGameDialogWithPreview(MfxDialog):
('percent', percent), ('percent', percent),
): ):
title_label, text_label = self.info_labels[n] title_label, text_label = self.info_labels[n]
if t == '': if t in ('', None):
title_label.hide() title_label.hide()
text_label.hide() text_label.hide()
else: else:
title_label.show() title_label.show()
text_label.show() text_label.show()
text_label.set_text(str(t)) text_label.set_text(str(t))
#self.info_labels[n].config(text=t)
def done(self, button): def done(self, button):
button = button.get_data("user_data") button = button.get_data("user_data")

View file

@ -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

View file

@ -445,15 +445,10 @@ class MfxCanvas(gnomecanvas.Canvas):
self.setBackgroundImage(None) self.setBackgroundImage(None)
self.configure(bg=tile.color) self.configure(bg=tile.color)
##app.top.config(bg=tile.color) ##app.top.config(bg=tile.color)
color = None
else: else:
self._setTile() self._setTile()
self.configure(bg=self.top_bg) 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 return True

View file

@ -566,7 +566,9 @@ class Status_StatsDialog(MfxMessageDialog): #MfxDialog
) )
class ProgressionDialog:
# FIXME
pass

View file

@ -37,6 +37,7 @@ if TOOLKIT == 'tk':
from tile.colorsdialog import * from tile.colorsdialog import *
from tile.fontsdialog import * from tile.fontsdialog import *
from tile.findcarddialog import * from tile.findcarddialog import *
from tile.solverdialog import *
from tile.gameinfodialog import * from tile.gameinfodialog import *
from tile.toolbar import * from tile.toolbar import *
from tile.statusbar import * from tile.statusbar import *
@ -60,6 +61,7 @@ if TOOLKIT == 'tk':
from tk.colorsdialog import * from tk.colorsdialog import *
from tk.fontsdialog import * from tk.fontsdialog import *
from tk.findcarddialog import * from tk.findcarddialog import *
from tk.solverdialog import *
from tk.gameinfodialog import * from tk.gameinfodialog import *
from tk.toolbar import * from tk.toolbar import *
from tk.statusbar import * from tk.statusbar import *
@ -84,6 +86,7 @@ else: # gtk
from pysolgtk.colorsdialog import * from pysolgtk.colorsdialog import *
from pysolgtk.fontsdialog import * from pysolgtk.fontsdialog import *
from pysolgtk.findcarddialog import * from pysolgtk.findcarddialog import *
from pysolgtk.solverdialog import *
from pysolgtk.gameinfodialog import * from pysolgtk.gameinfodialog import *
from pysolgtk.toolbar import * from pysolgtk.toolbar import *
from pysolgtk.statusbar import * from pysolgtk.statusbar import *

View file

@ -497,7 +497,6 @@ class CardsetManager(ResourceManager):
class Tile(Resource): class Tile(Resource):
def __init__(self, **kw): def __init__(self, **kw):
kw['color'] = None kw['color'] = None
kw['text_color'] = "#000000"
kw['stretch'] = 0 kw['stretch'] = 0
Resource.__init__(self, **kw) Resource.__init__(self, **kw)

View file

@ -29,7 +29,7 @@ PACKAGE = 'PySol'
PACKAGE_URL = 'http://sourceforge.net/projects/pysolfc/' PACKAGE_URL = 'http://sourceforge.net/projects/pysolfc/'
VERSION = '4.82' VERSION = '4.82'
FC_VERSION = '0.9.5' FC_VERSION = '1.0'
VERSION_TUPLE = (4, 82) VERSION_TUPLE = (4, 82)
# Tk windowing system (auto determine in init.py) # Tk windowing system (auto determine in init.py)
@ -48,6 +48,11 @@ USE_TILE = 'auto' # or True or False
# 'none' - disable # 'none' - disable
SOUND_MOD = 'auto' 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
DATA_DIRS = [] DATA_DIRS = []
# you can add your extra directories here # you can add your extra directories here

View file

@ -57,7 +57,7 @@ __all__ = ['cardsFaceUp',
'SS_FoundationStack', 'SS_FoundationStack',
'RK_FoundationStack', 'RK_FoundationStack',
'AC_FoundationStack', 'AC_FoundationStack',
'SequenceStack_StackMethods', #'SequenceStack_StackMethods',
'BasicRowStack', 'BasicRowStack',
'SequenceRowStack', 'SequenceRowStack',
'AC_RowStack', 'AC_RowStack',
@ -71,6 +71,7 @@ __all__ = ['cardsFaceUp',
'UD_RK_RowStack', 'UD_RK_RowStack',
'FreeCell_AC_RowStack', 'FreeCell_AC_RowStack',
'FreeCell_SS_RowStack', 'FreeCell_SS_RowStack',
'FreeCell_RK_RowStack',
'Spider_AC_RowStack', 'Spider_AC_RowStack',
'Spider_SS_RowStack', 'Spider_SS_RowStack',
'Yukon_AC_RowStack', 'Yukon_AC_RowStack',
@ -83,6 +84,10 @@ __all__ = ['cardsFaceUp',
'FaceUpWasteTalonStack', 'FaceUpWasteTalonStack',
'OpenTalonStack', 'OpenTalonStack',
'ReserveStack', 'ReserveStack',
'SuperMoveStack_StackMethods',
'SuperMoveSS_RowStack',
'SuperMoveAC_RowStack',
'SuperMoveRK_RowStack',
'InvisibleStack', 'InvisibleStack',
'StackWrapper', 'StackWrapper',
'WeakStackWrapper', 'WeakStackWrapper',
@ -342,6 +347,7 @@ class Stack:
bind(group, "<3>", self.__rightclickEventHandler) bind(group, "<3>", self.__rightclickEventHandler)
bind(group, "<2>", self.__middleclickEventHandler) bind(group, "<2>", self.__middleclickEventHandler)
bind(group, "<Control-3>", self.__middleclickEventHandler) bind(group, "<Control-3>", self.__middleclickEventHandler)
##bind(group, "<Control-2>", self.__controlmiddleclickEventHandler)
##bind(group, "<Shift-3>", self.__shiftrightclickEventHandler) ##bind(group, "<Shift-3>", self.__shiftrightclickEventHandler)
##bind(group, "<Double-2>", "") ##bind(group, "<Double-2>", "")
bind(group, "<Enter>", self.__enterEventHandler) bind(group, "<Enter>", self.__enterEventHandler)
@ -489,8 +495,7 @@ class Stack:
if update: if update:
view.updateText() view.updateText()
if self.is_filled: self.unshadeStack()
self._unshadeStack()
self.is_filled = False self.is_filled = False
return card return card
@ -652,8 +657,11 @@ class Stack:
# Atomic move actions {model -> view} # Atomic move actions {model -> view}
# #
def flipMove(self): def flipMove(self, animation=False):
# Flip the top card. # Flip the top card.
if animation:
self.game.singleFlipMove(self)
else:
self.game.flipMove(self) self.game.flipMove(self)
def moveMove(self, ncards, to_stack, frames=-1, shadow=-1): def moveMove(self, ncards, to_stack, frames=-1, shadow=-1):
@ -671,10 +679,10 @@ class Stack:
# Playing move actions. Better not override. # Playing move actions. Better not override.
# #
def playFlipMove(self, sound=1): def playFlipMove(self, sound=1, animation=False):
if sound: if sound:
self.game.playSample("flip", 5) self.game.playSample("flip", 5)
self.flipMove() self.flipMove(animation=animation)
if not self.game.checkForWin(): if not self.game.checkForWin():
self.game.autoPlay() self.game.autoPlay()
self.game.finishMove() self.game.finishMove()
@ -917,6 +925,32 @@ class Stack:
self.game.canvas.update_idletasks() self.game.canvas.update_idletasks()
return 1 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): def rightclickHandler(self, event):
return 0 return 0
@ -946,8 +980,25 @@ class Stack:
self.moveCardsBackHandler(event, drag) self.moveCardsBackHandler(event, drag)
def moveCardsBackHandler(self, 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: for card in drag.cards:
self._position(card) 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): def __middleclickEventHandler(self, event):
return self.__defaultClickEventHandler(event, self.middleclickHandler) return self.__defaultClickEventHandler(event, self.middleclickHandler)
def __controlmiddleclickEventHandler(self, event):
return self.__defaultClickEventHandler(event, self.controlmiddleclickHandler)
def __rightclickEventHandler(self, event): def __rightclickEventHandler(self, event):
return self.__defaultClickEventHandler(event, self.rightclickHandler) return self.__defaultClickEventHandler(event, self.rightclickHandler)
@ -1049,8 +1103,11 @@ class Stack:
self.current_cursor = CURSOR_DOWN_ARROW self.current_cursor = CURSOR_DOWN_ARROW
self.cursor_changed = True self.cursor_changed = True
else: else:
help = self.getHelp() ##+' '+self.getBaseCard(),
if DEBUG >= 5:
help = repr(self)
after_idle(self.canvas, self.game.showHelp, after_idle(self.canvas, self.game.showHelp,
'help', self.getHelp(), ##+' '+self.getBaseCard(), 'help', help,
'info', self.getNumCards()) 'info', self.getNumCards())
return EVENT_HANDLED return EVENT_HANDLED
@ -1173,7 +1230,7 @@ class Stack:
images = self.game.app.images images = self.game.app.images
cx, cy = cards[0].x, cards[0].y cx, cy = cards[0].x, cards[0].y
ddx, ddy = cx-cards[-1].x, cy-cards[-1].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] c0 = cards[-1]
if self.CARD_XOFFSET[0] < 0: c0 = cards[0] if self.CARD_XOFFSET[0] < 0: c0 = cards[0]
if self.CARD_YOFFSET[0] < 0: c0 = cards[0] if self.CARD_YOFFSET[0] < 0: c0 = cards[0]
@ -1303,7 +1360,7 @@ class Stack:
#item.tkraise() #item.tkraise()
self.items.shade_item = item self.items.shade_item = item
def _unshadeStack(self): def unshadeStack(self):
if self.items.shade_item: if self.items.shade_item:
self.items.shade_item.delete() self.items.shade_item.delete()
self.items.shade_item = None self.items.shade_item = None
@ -1350,19 +1407,19 @@ class Stack:
group=self.group) group=self.group)
drag.shadows.append(im) drag.shadows.append(im)
def _setMotionCursor(self, event): ## def _setMotionCursor(self, event):
if not event: ## if not event:
return ## return
self.cursor_changed = True ## self.cursor_changed = True
i = self._findCard(event) ## i = self._findCard(event)
if i < 0 or not self.canMoveCards(self.cards[i:]): ## if i < 0 or not self.canMoveCards(self.cards[i:]):
if self.current_cursor != CURSOR_NO_MOVE: ## if self.current_cursor != CURSOR_NO_MOVE:
self.game.canvas.config(cursor=CURSOR_NO_MOVE) ## self.game.canvas.config(cursor=CURSOR_NO_MOVE)
self.current_cursor = CURSOR_NO_MOVE ## self.current_cursor = CURSOR_NO_MOVE
else: ## else:
if self.current_cursor != CURSOR_CAN_MOVE: ## if self.current_cursor != CURSOR_CAN_MOVE:
self.game.canvas.config(cursor=CURSOR_CAN_MOVE) ## self.game.canvas.config(cursor=CURSOR_CAN_MOVE)
self.current_cursor = CURSOR_CAN_MOVE ## self.current_cursor = CURSOR_CAN_MOVE
def _stopDrag(self): def _stopDrag(self):
drag = self.game.drag drag = self.game.drag
@ -1376,15 +1433,15 @@ class Stack:
drag.shadows = [] drag.shadows = []
drag.stack = None drag.stack = None
drag.cards = [] 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 # finish a drag operation
def finishDrag(self, event=None): def finishDrag(self, event=None):
if self.game.app.opt.dragcursor: if self.game.app.opt.dragcursor:
self.game.canvas.config(cursor='') self.game.canvas.config(cursor='')
drag = self.game.drag.copy() drag = self.game.drag.copy()
if self.game.app.opt.mouse_type == 'point-n-click':
drag.stack._stopDrag()
else:
self._stopDrag() self._stopDrag()
if drag.cards: if drag.cards:
if self.game.app.opt.mouse_type == 'point-n-click': if self.game.app.opt.mouse_type == 'point-n-click':
@ -1398,6 +1455,9 @@ class Stack:
if self.game.app.opt.dragcursor: if self.game.app.opt.dragcursor:
self.game.canvas.config(cursor='') self.game.canvas.config(cursor='')
drag = self.game.drag.copy() drag = self.game.drag.copy()
if self.game.app.opt.mouse_type == 'point-n-click':
drag.stack._stopDrag()
else:
self._stopDrag() self._stopDrag()
if drag.cards: if drag.cards:
assert drag.stack is self assert drag.stack is self
@ -1410,6 +1470,7 @@ class Stack:
return '' return ''
def _getBaseCard(self): def _getBaseCard(self):
# FIXME: no-french games
if self.cap.max_accept == 0: if self.cap.max_accept == 0:
return '' return ''
br = self.cap.base_rank br = self.cap.base_rank
@ -1424,14 +1485,10 @@ class Stack:
return s return s
def getNumCards(self): def getNumCards(self):
if DEBUG >= 5:
t = repr(self)+' '
else:
t = ''
n = len(self.cards) n = len(self.cards)
if n == 0 : return t+_('No cards') if n == 0 : return _('No cards')
elif n == 1 : return t+_('1 card') elif n == 1 : return _('1 card')
else : return t+str(n)+_(' cards') else : return str(n)+_(' cards')
# /*********************************************************************** # /***********************************************************************
@ -1831,8 +1888,9 @@ class OpenStack(Stack):
def clickHandler(self, event): def clickHandler(self, event):
flipstacks, dropstacks, quickstacks = self.game.getAutoStacks(event) flipstacks, dropstacks, quickstacks = self.game.getAutoStacks(event)
if self in flipstacks and self.canFlipCard(): if self in flipstacks and self.canFlipCard():
self.playFlipMove() self.playFlipMove(animation=True)
return -1 # continue this event (start a drag) ##return -1 # continue this event (start a drag)
return 1 # break
return 0 return 0
def rightclickHandler(self, event): def rightclickHandler(self, event):
@ -1850,7 +1908,7 @@ class OpenStack(Stack):
# flip or drop a card # flip or drop a card
flipstacks, dropstacks, quickstacks = self.game.getAutoStacks(event) flipstacks, dropstacks, quickstacks = self.game.getAutoStacks(event)
if self in flipstacks and self.canFlipCard(): if self in flipstacks and self.canFlipCard():
self.playFlipMove() self.playFlipMove(animation=True)
return -1 # continue this event (start a drag) return -1 # continue this event (start a drag)
if self in dropstacks: if self in dropstacks:
to_stack, ncards = self.canDropCards(self.game.s.foundations) 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': if self.game.app.opt.mouse_type == 'point-n-click':
self.playMoveMove(len(drag.cards), stack, sound=sound) self.playMoveMove(len(drag.cards), stack, sound=sound)
else: 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): def releaseHandler(self, event, drag, sound=1):
cards = drag.cards cards = drag.cards
@ -2168,6 +2227,12 @@ class FreeCell_SS_RowStack(SS_RowStack):
max_move = getNumberOfFreeStacks(self.game.s.reserves) + 1 max_move = getNumberOfFreeStacks(self.game.s.reserves) + 1
return len(cards) <= max_move and SS_RowStack.canMoveCards(self, cards) 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, # A Spider_AlternateColor_RowStack builds down by rank and alternate color,
# but accepts sequences that match by rank only. # but accepts sequences that match by rank only.
class Spider_AC_RowStack(AC_RowStack): 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) # // 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 assert len(waste.cards) + num_cards <= waste.cap.max_cards
for i in range(num_cards): for i in range(num_cards):
if not self.cards[-1].face_up: if not self.cards[-1].face_up:
if 1:
self.game.flipAndMoveMove(self, waste)
else:
self.game.flipMove(self) self.game.flipMove(self)
self.game.moveMove(1, self, waste, frames=4, shadow=0) self.game.moveMove(1, self, waste, frames=4, shadow=0)
else:
self.game.moveMove(1, self, waste, frames=4, shadow=0)
self.fillStack() self.fillStack()
elif waste.cards and self.round != self.max_rounds: elif waste.cards and self.round != self.max_rounds:
if sound: if sound:
@ -2355,6 +2492,11 @@ class FaceUpWasteTalonStack(WasteTalonStack):
self.game.flipMove(self) self.game.flipMove(self)
self.game.fillStack(self) self.game.fillStack(self)
def dealCards(self, sound=0):
WasteTalonStack.dealCards(self, sound=sound)
if self.canFlipCard():
self.flipMove()
class OpenTalonStack(TalonStack, OpenStack): class OpenTalonStack(TalonStack, OpenStack):
canMoveCards = OpenStack.canMoveCards canMoveCards = OpenStack.canMoveCards
@ -2460,7 +2602,7 @@ class ArbitraryStack(OpenStack):
# flip or drop a card # flip or drop a card
flipstacks, dropstacks, quickstacks = self.game.getAutoStacks(event) flipstacks, dropstacks, quickstacks = self.game.getAutoStacks(event)
if self in flipstacks and self.canFlipCard(): if self in flipstacks and self.canFlipCard():
self.playFlipMove() self.playFlipMove(animation=True)
return -1 # continue this event (start a drag) return -1 # continue this event (start a drag)
if self in dropstacks: if self in dropstacks:
i = self._findCard(event) i = self._findCard(event)

View file

@ -243,3 +243,126 @@ class FileStatsFormatter(PysolStatsFormatter):
prev_games = self.app.stats.session_games.get(player) prev_games = self.app.stats.session_games.get(player)
return self.writeLog(player, header, prev_games) 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

View file

@ -17,6 +17,7 @@ Message = Tkinter.Message
Listbox = Tkinter.Listbox Listbox = Tkinter.Listbox
Text = Tkinter.Text Text = Tkinter.Text
Canvas = Tkinter.Canvas Canvas = Tkinter.Canvas
Spinbox = Tkinter.Spinbox
PhotoImage = Tkinter.PhotoImage PhotoImage = Tkinter.PhotoImage
Event = Tkinter.Event Event = Tkinter.Event

View file

@ -49,8 +49,6 @@ class ColorsDialog(MfxDialog):
frame.pack(expand=True, fill='both', padx=5, pady=10) frame.pack(expand=True, fill='both', padx=5, pady=10)
frame.columnconfigure(0, weight=1) 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 = Tkinter.StringVar()
self.text_var.set(app.opt.colors['text']) self.text_var.set(app.opt.colors['text'])
self.piles_var = Tkinter.StringVar() self.piles_var = Tkinter.StringVar()
@ -68,17 +66,9 @@ class ColorsDialog(MfxDialog):
self.not_matching_var = Tkinter.StringVar() self.not_matching_var = Tkinter.StringVar()
self.not_matching_var.set(app.opt.colors['not_matching']) self.not_matching_var.set(app.opt.colors['not_matching'])
# #
c = Tkinter.Checkbutton(frame, variable=self.use_default_var, row = 0
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
for title, var in ( for title, var in (
(_('Text foreground:'), self.text_var),
(_('Highlight piles:'), self.piles_var), (_('Highlight piles:'), self.piles_var),
(_('Highlight cards 1:'), self.cards_1_var), (_('Highlight cards 1:'), self.cards_1_var),
(_('Highlight cards 2:'), self.cards_2_var), (_('Highlight cards 2:'), self.cards_2_var),
@ -100,7 +90,6 @@ class ColorsDialog(MfxDialog):
focus = self.createButtons(bottom_frame, kw) focus = self.createButtons(bottom_frame, kw)
self.mainloop(focus, kw.timeout) self.mainloop(focus, kw.timeout)
# #
self.use_default_color = not self.use_default_var.get()
self.text_color = self.text_var.get() self.text_color = self.text_var.get()
self.piles_color = self.piles_var.get() self.piles_color = self.piles_var.get()
self.cards_1_color = self.cards_1_var.get() self.cards_1_color = self.cards_1_var.get()

View file

@ -114,6 +114,7 @@ class FindCardDialog(Tkinter.Toplevel):
self.groups.append(group) self.groups.append(group)
def connectGame(self, game): def connectGame(self, game):
self.canvas.delete('all')
self.game = game self.game = game
suits = game.gameinfo.suits suits = game.gameinfo.suits
ranks = game.gameinfo.ranks ranks = game.gameinfo.ranks
@ -133,6 +134,7 @@ class FindCardDialog(Tkinter.Toplevel):
w, h = dx*j+2, dy*i+2 w, h = dx*j+2, dy*i+2
self.canvas.config(width=w, height=h) self.canvas.config(width=w, height=h)
self.wm_iconname(PACKAGE + " - " + game.getTitleName()) self.wm_iconname(PACKAGE + " - " + game.getTitleName())
self.wm_geometry('') # cancel user-specified geometry
def enterEvent(self, suit, rank, rect, group): def enterEvent(self, suit, rank, rect, group):
##print 'enterEvent', suit, rank, self.busy ##print 'enterEvent', suit, rank, self.busy

View file

@ -48,6 +48,7 @@ from pysollib.util import CARDSET
from pysollib.settings import PACKAGE, WIN_SYSTEM from pysollib.settings import PACKAGE, WIN_SYSTEM
from pysollib.settings import TOP_TITLE from pysollib.settings import TOP_TITLE
from pysollib.settings import SELECT_GAME_MENU from pysollib.settings import SELECT_GAME_MENU
from pysollib.settings import USE_FREECELL_SOLVER
from pysollib.gamedb import GI from pysollib.gamedb import GI
from pysollib.actions import PysolMenubarActions from pysollib.actions import PysolMenubarActions
@ -59,6 +60,7 @@ from soundoptionsdialog import SoundOptionsDialog
from selectcardset import SelectCardsetDialogWithPreview from selectcardset import SelectCardsetDialogWithPreview
from selecttile import SelectTileDialogWithPreview from selecttile import SelectTileDialogWithPreview
from findcarddialog import connect_game_find_card_dialog, destroy_find_card_dialog 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 tkwrap import MfxRadioMenuItem, MfxCheckMenuItem, StringVar
from tkwidget import MfxMessageDialog from tkwidget import MfxMessageDialog
@ -139,25 +141,18 @@ class MfxMenubar(Tkinter.Menu):
#print label, type(label) #print label, type(label)
name = re.sub(r"[^0-9a-zA-Z]", "", label).lower() name = re.sub(r"[^0-9a-zA-Z]", "", label).lower()
label = gettext(label) label = gettext(label)
underline = -1 underline = label.find('&')
m = re.search(r"^(.*)\&([^\&].*)$", label) if underline >= 0:
if m: label = label.replace('&', '')
l1, l2 = m.group(1), m.group(2)
l1 = re.sub(r"\&\&", "&", l1)
l2 = re.sub(r"\&\&", "&", l2)
label = l1 + l2
underline = len(l1)
return name, label, underline return name, label, underline
def add(self, itemType, cnf={}): def add(self, itemType, cnf={}):
label = cnf.get("label") label = cnf.get("label")
if label: if label:
name = cnf.get('name') name = cnf.get('name')
try: if name:
del cnf['name'] # TclError: unknown option "-name" del cnf['name'] # TclError: unknown option "-name"
except KeyError: else:
pass
if not name:
name, label, underline = self.labeltoname(label) name, label, underline = self.labeltoname(label)
cnf["underline"] = cnf.get("underline", underline) cnf["underline"] = cnf.get("underline", underline)
cnf["label"] = label cnf["label"] = label
@ -323,6 +318,7 @@ class PysolMenubar(PysolMenubarActions):
connect_game_find_card_dialog(game) connect_game_find_card_dialog(game)
else: else:
destroy_find_card_dialog() destroy_find_card_dialog()
connect_game_solver_dialog(game)
# create a GTK-like path # create a GTK-like path
def _addPath(self, path, menu, index, submenu): def _addPath(self, path, menu, index, submenu):
@ -371,7 +367,7 @@ class PysolMenubar(PysolMenubarActions):
menu.add_separator() menu.add_separator()
submenu = MfxMenu(menu, label=n_("Fa&vorite games")) 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_("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_separator()
menu.add_command(label=n_("&Open..."), command=self.mOpen, accelerator=m+"O") menu.add_command(label=n_("&Open..."), command=self.mOpen, accelerator=m+"O")
menu.add_command(label=n_("&Save"), command=self.mSave, accelerator=m+"S") 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_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_command(label=n_("&Pause"), command=self.mPause, accelerator="P")
menu.add_separator() 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_checkbutton(label=n_("&Comments..."), variable=self.tkopt.comment, command=self.mEditGameComment)
menu.add_separator() menu.add_separator()
submenu = MfxMenu(menu, label=n_("&Statistics")) 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_command(label=n_("Full log..."), command=lambda self=self: self.mPlayerStats(mode=103))
submenu.add_separator() submenu.add_separator()
submenu.add_command(label=TOP_TITLE+"...", command=self.mTop10, accelerator=m+"T") 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 = 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_("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)) 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 = MfxMenu(self.__menubar, label=n_("&Assist"))
menu.add_command(label=n_("&Hint"), command=self.mHint, accelerator="H") 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_("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_separator()
menu.add_command(label=n_("&Demo"), command=self.mDemo, accelerator=m+"D") menu.add_command(label=n_("&Demo"), command=self.mDemo, accelerator=m+"D")
menu.add_command(label=n_("Demo (&all games)"), command=self.mMixedDemo) 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_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) 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.add_checkbutton(label=n_("Shade &filled stacks"), variable=self.tkopt.shade_filled_stacks, command=self.mOptShadeFilledStacks)
submenu = MfxMenu(menu, label=n_("A&nimations")) 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_("&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_("&Very fast"), variable=self.tkopt.animations, value=1, command=self.mOptAnimations)
submenu.add_radiobutton(label=n_("&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_("&Slow"), variable=self.tkopt.animations, value=3, command=self.mOptAnimations) submenu.add_radiobutton(label=n_("&Medium"), 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_("&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_separator()
submenu.add_checkbutton(label=n_("&Redeal animation"), variable=self.tkopt.redeal_animation, command=self.mRedealAnimation) submenu.add_checkbutton(label=n_("&Redeal animation"), variable=self.tkopt.redeal_animation, command=self.mRedealAnimation)
if Image: if Image:
@ -531,15 +533,13 @@ class PysolMenubar(PysolMenubarActions):
ctrl = "Control-" ctrl = "Control-"
if sys.platform == "darwin": ctrl = "Command-" if sys.platform == "darwin": ctrl = "Command-"
self._bindKey("", "n", self.mNewGame) self._bindKey("", "n", self.mNewGame)
self._bindKey("", "g", self.mSelectGameDialog) self._bindKey(ctrl, "w", self.mSelectGameDialog)
self._bindKey("", "v", self.mSelectGameDialogWithPreview) self._bindKey(ctrl, "v", self.mSelectGameDialogWithPreview)
self._bindKey(ctrl, "r", lambda e, self=self: self.mSelectRandomGame()) self._bindKey(ctrl, "r", lambda e, self=self: self.mSelectRandomGame())
self._bindKey(ctrl, "m", self.mSelectGameById) self._bindKey(ctrl, "m", self.mSelectGameById)
self._bindKey(ctrl, "n", self.mNewGameWithNextId) self._bindKey(ctrl, "n", self.mNewGameWithNextId)
self._bindKey(ctrl, "o", self.mOpen) self._bindKey(ctrl, "o", self.mOpen)
##self._bindKey("", "F3", self.mOpen) # undocumented
self._bindKey(ctrl, "s", self.mSave) self._bindKey(ctrl, "s", self.mSave)
##self._bindKey("", "F2", self.mSaveAs) # undocumented
self._bindKey(ctrl, "q", self.mQuit) self._bindKey(ctrl, "q", self.mQuit)
self._bindKey("", "z", self.mUndo) self._bindKey("", "z", self.mUndo)
self._bindKey("", "BackSpace", self.mUndo) # undocumented self._bindKey("", "BackSpace", self.mUndo) # undocumented
@ -547,14 +547,14 @@ class PysolMenubar(PysolMenubarActions):
self._bindKey("", "r", self.mRedo) self._bindKey("", "r", self.mRedo)
self._bindKey(ctrl, "g", self.mRestart) self._bindKey(ctrl, "g", self.mRestart)
self._bindKey("", "space", self.mDeal) # undocumented 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(ctrl, "t", self.mTop10)
self._bindKey("", "h", self.mHint) self._bindKey("", "h", self.mHint)
self._bindKey(ctrl, "h", self.mHint1) # undocumented self._bindKey(ctrl, "h", self.mHint1) # undocumented
##self._bindKey("", "Shift_L", self.mHighlightPiles) ##self._bindKey("", "Shift_L", self.mHighlightPiles)
##self._bindKey("", "Shift_R", self.mHighlightPiles) ##self._bindKey("", "Shift_R", self.mHighlightPiles)
self._bindKey("", "i", 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, "d", self.mDemo)
self._bindKey(ctrl, "e", self.mSelectCardsetDialog) self._bindKey(ctrl, "e", self.mSelectCardsetDialog)
self._bindKey(ctrl, "b", self.mOptChangeCardback) # undocumented self._bindKey(ctrl, "b", self.mOptChangeCardback) # undocumented
@ -597,19 +597,19 @@ class PysolMenubar(PysolMenubarActions):
# #
def _bindKey(self, modifier, key, func): def _bindKey(self, modifier, key, func):
if 0 and not modifier and len(key) == 1: ## if 0 and not modifier and len(key) == 1:
self.__keybindings[key.lower()] = func ## self.__keybindings[key.lower()] = func
self.__keybindings[key.upper()] = func ## self.__keybindings[key.upper()] = func
return ## 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 + ">" sequence = "<" + modifier + "KeyPress-" + key + ">"
try:
bind(self.top, sequence, func) bind(self.top, sequence, func)
if len(key) == 1 and key != key.upper(): if len(key) == 1 and key != key.upper():
key = key.upper() key = key.upper()
sequence = "<" + modifier + "KeyPress-" + key + ">" sequence = "<" + modifier + "KeyPress-" + key + ">"
bind(self.top, sequence, func) bind(self.top, sequence, func)
except:
raise
def _keyPressHandler(self, event): def _keyPressHandler(self, event):
@ -621,10 +621,10 @@ class PysolMenubar(PysolMenubarActions):
if event.char: # ignore Ctrl/Shift/etc. if event.char: # ignore Ctrl/Shift/etc.
self.game.demo.keypress = event.char self.game.demo.keypress = event.char
r = EVENT_HANDLED r = EVENT_HANDLED
func = self.__keybindings.get(event.char) ## func = self.__keybindings.get(event.char)
if func and (event.state & ~2) == 0: ## if func and (event.state & ~2) == 0:
func(event) ## func(event)
r = EVENT_HANDLED ## r = EVENT_HANDLED
return r return r
# #
@ -636,9 +636,11 @@ class PysolMenubar(PysolMenubarActions):
games = map(self.app.gdb.get, self.app.gdb.getGamesIdSortedByName()) games = map(self.app.gdb.get, self.app.gdb.getGamesIdSortedByName())
##games = tuple(games) ##games = tuple(games)
###menu = MfxMenu(menu, label="Select &game") ###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) 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) command=self.mSelectGameDialogWithPreview)
if not SELECT_GAME_MENU: if not SELECT_GAME_MENU:
return return
@ -783,13 +785,19 @@ class PysolMenubar(PysolMenubarActions):
gi = games[i] gi = games[i]
columnbreak = i > 0 and (i % cb) == 0 columnbreak = i > 0 and (i % cb) == 0
if short_name: if short_name:
label = gi.short_name label = gettext(gi.short_name)
else: else:
label = gi.name label = gettext(gi.name)
menu.add_radiobutton(command=command, variable=variable, ## menu.add_radiobutton(command=command, variable=variable,
columnbreak=columnbreak, ## columnbreak=columnbreak,
value=gi.id, label=label, name=None) ## 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 # Select Game menu actions
@ -1148,7 +1156,6 @@ class PysolMenubar(PysolMenubarActions):
self._cancelDrag() self._cancelDrag()
self.game.endGame(bookmark=1) self.game.endGame(bookmark=1)
self.game.quitGame(bookmark=1) self.game.quitGame(bookmark=1)
self.app.opt.games_geometry = {} # clear saved games geometry
def _mOptCardback(self, index): def _mOptCardback(self, index):
if self._cancelDrag(break_pause=False): return if self._cancelDrag(break_pause=False): return
@ -1328,13 +1335,13 @@ class PysolMenubar(PysolMenubarActions):
def mOptTheme(self, *event): def mOptTheme(self, *event):
theme = self.tkopt.theme.get() theme = self.tkopt.theme.get()
self.app.opt.tile_theme = theme
d = MfxMessageDialog(self.top, title=_("Change theme"), d = MfxMessageDialog(self.top, title=_("Change theme"),
text=_("""\ text=_("""\
This settings will take effect This settings will take effect
the next time you restart """)+PACKAGE, the next time you restart """)+PACKAGE,
bitmap="warning", bitmap="warning",
default=0, strings=(_("&OK"),)) default=0, strings=(_("&OK"),))
self.app.opt.tile_theme = theme
def createThemesMenu(self, menu): def createThemesMenu(self, menu):
submenu = MfxMenu(menu, label=n_("Set t&heme")) submenu = MfxMenu(menu, label=n_("Set t&heme"))
@ -1343,12 +1350,12 @@ the next time you restart """)+PACKAGE,
all_themes.sort() all_themes.sort()
# #
tn = { tn = {
'default': _('Default'), 'default': n_('Default'),
'classic': _('Classic'), 'classic': n_('Classic'),
'alt': _('Revitalized'), 'alt': n_('Revitalized'),
'winnative': _('Windows native'), 'winnative': n_('Windows native'),
'xpnative': _('XP Native'), 'xpnative': n_('XP Native'),
'aqua': _('Aqua'), 'aqua': n_('Aqua'),
} }
for t in all_themes: for t in all_themes:
try: try:
@ -1357,4 +1364,3 @@ the next time you restart """)+PACKAGE,
n = t.capitalize() n = t.capitalize()
submenu.add_radiobutton(label=n, variable=self.tkopt.theme, submenu.add_radiobutton(label=n, variable=self.tkopt.theme,
value=t, command=self.mOptTheme) value=t, command=self.mOptTheme)

View file

@ -428,7 +428,7 @@ class SelectGameDialogWithPreview(SelectGameDialog):
destruct(self.preview_app) destruct(self.preview_app)
self.preview_app = None self.preview_app = None
def updatePreview(self, gameid, animations=5): def updatePreview(self, gameid, animations=10):
if gameid == self.preview_key: if gameid == self.preview_key:
return return
self.deletePreview() self.deletePreview()
@ -495,7 +495,7 @@ class SelectGameDialogWithPreview(SelectGameDialog):
# #
self.preview_app.audio = self.app.audio self.preview_app.audio = self.app.audio
if self.app.opt.animations: if self.app.opt.animations:
self.preview_app.opt.animations = 5 self.preview_app.opt.animations = 10
else: else:
self.preview_app.opt.animations = 0 self.preview_app.opt.animations = 0
# save seed # save seed
@ -553,13 +553,12 @@ class SelectGameDialogWithPreview(SelectGameDialog):
('percent', percent), ('percent', percent),
): ):
title_label, text_label = self.info_labels[n] title_label, text_label = self.info_labels[n]
if t == '': if t in ('', None):
title_label.grid_remove() title_label.grid_remove()
text_label.grid_remove() text_label.grid_remove()
else: else:
title_label.grid() title_label.grid()
text_label.grid() text_label.grid()
text_label.config(text=t) text_label.config(text=t)
#self.info_labels[n].config(text=t)

View file

@ -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, '<<ComboboxSelected>>', 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

View file

@ -102,8 +102,11 @@ class MfxStatusbar:
def updateText(self, **kw): def updateText(self, **kw):
for k, v in kw.items(): for k, v in kw.items():
label = getattr(self, k + "_label") label = getattr(self, k + "_label")
#label["text"] = str(v) text = unicode(v)
label["text"] = unicode(v) width = label['width']
if width and len(text) > width:
label['width'] = len(text)
label["text"] = text
def configLabel(self, name, **kw): def configLabel(self, name, **kw):
if 'fg' in kw: if 'fg' in kw:

View file

@ -371,21 +371,14 @@ class MfxCanvas(Tkinter.Canvas):
def _substitute(self, *args): def _substitute(self, *args):
e = Tkinter.Event() e = Tkinter.Event()
try:
# Tk changed behavior in 8.4.2, returning "??" rather more often.
e.x = int(args[0]) e.x = int(args[0])
except ValueError:
e.x = args[0]
try:
e.y = int(args[1]) e.y = int(args[1])
except ValueError:
e.y = args[1]
return (e,) 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)

View file

@ -38,10 +38,12 @@ __all__ = ['SingleGame_StatsDialog',
'FullLog_StatsDialog', 'FullLog_StatsDialog',
'SessionLog_StatsDialog', 'SessionLog_StatsDialog',
'Status_StatsDialog', 'Status_StatsDialog',
'Top_StatsDialog'] 'Top_StatsDialog',
'ProgressionDialog',
]
# imports # imports
import os, string, sys, types import os
import time import time
import Tile as Tkinter import Tile as Tkinter
import tkFont import tkFont
@ -50,7 +52,7 @@ import tkFont
from pysollib.mfxutil import destruct, Struct, kwdefault, KwStruct from pysollib.mfxutil import destruct, Struct, kwdefault, KwStruct
from pysollib.mfxutil import format_time from pysollib.mfxutil import format_time
##from pysollib.util import * ##from pysollib.util import *
from pysollib.stats import PysolStatsFormatter from pysollib.stats import PysolStatsFormatter, ProgressionFormatter
from pysollib.settings import TOP_TITLE from pysollib.settings import TOP_TITLE
# Toolkit imports # Toolkit imports
@ -233,6 +235,7 @@ class TreeFormatter(PysolStatsFormatter):
self.tree = tree self.tree = tree
self.parent_window = parent_window self.parent_window = parent_window
self.font = font self.font = font
self.tkfont = tkFont.Font(tree, font)
self.gameid = None self.gameid = None
self.gamenumber = None self.gamenumber = None
self._tabs = None self._tabs = None
@ -246,7 +249,8 @@ class TreeFormatter(PysolStatsFormatter):
tw = 20*self.w tw = 20*self.w
##tw = 160 ##tw = 160
self._tabs = [tw] 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:]: for t in arg[1:]:
tw = font.measure(t)+20 tw = font.measure(t)+20
self._tabs.append(tw) self._tabs.append(tw)
@ -604,7 +608,7 @@ class Top_StatsDialog(MfxDialog):
self.createBitmaps(top_frame, kw) self.createBitmaps(top_frame, kw)
frame = Tkinter.Frame(top_frame) 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) frame.columnconfigure(0, weight=1)
if (player in app.stats.games_stats and if (player in app.stats.games_stats and
@ -681,3 +685,255 @@ class Top_StatsDialog(MfxDialog):
separatorwidth=2, separatorwidth=2,
) )
return MfxDialog.initKw(self, kw) 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)

View file

@ -507,15 +507,10 @@ class MfxScrolledCanvas:
if i == 0: if i == 0:
self.canvas.config(bg=tile.color) self.canvas.config(bg=tile.color)
##app.top.config(bg=tile.color) ##app.top.config(bg=tile.color)
color = None
else: else:
self.canvas.config(bg=app.top_bg) self.canvas.config(bg=app.top_bg)
##app.top.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 return True
@ -533,7 +528,7 @@ class MfxScrolledCanvas:
def createFrame(self, kw): def createFrame(self, kw):
width = kw.get("width") width = kw.get("width")
height = kw.get("height") 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): def createCanvas(self, kw):
self.canvas = MfxCanvas(self.frame, **kw) self.canvas = MfxCanvas(self.frame, **kw)

View file

@ -33,7 +33,7 @@
## ##
##---------------------------------------------------------------------------## ##---------------------------------------------------------------------------##
__all__ = ['PysolToolbar'] #, 'TOOLBAR_BUTTONS'] __all__ = ['PysolToolbar']
# imports # imports
import os import os
@ -240,6 +240,7 @@ class PysolToolbar(PysolToolbarActions):
widget.hide() widget.hide()
# #
prev_visible = None prev_visible = None
last_visible = None
for w in self._widgets: for w in self._widgets:
if isinstance(w, ToolbarSeparator): if isinstance(w, ToolbarSeparator):
if prev_visible is None or isinstance(prev_visible, ToolbarSeparator): if prev_visible is None or isinstance(prev_visible, ToolbarSeparator):
@ -248,6 +249,10 @@ class PysolToolbar(PysolToolbarActions):
w.show(orient=self.orient) w.show(orient=self.orient)
if w.visible: if w.visible:
prev_visible = w prev_visible = w
if not isinstance(w, ToolbarLabel):
last_visible = w
if isinstance(last_visible, ToolbarSeparator):
last_visible.hide()
# util # util
def _loadImage(self, name): def _loadImage(self, name):

View file

@ -48,8 +48,6 @@ class ColorsDialog(MfxDialog):
frame.pack(expand=True, fill='both', padx=5, pady=10) frame.pack(expand=True, fill='both', padx=5, pady=10)
frame.columnconfigure(0, weight=1) 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 = Tkinter.StringVar()
self.text_var.set(app.opt.colors['text']) self.text_var.set(app.opt.colors['text'])
self.piles_var = Tkinter.StringVar() self.piles_var = Tkinter.StringVar()
@ -67,17 +65,9 @@ class ColorsDialog(MfxDialog):
self.not_matching_var = Tkinter.StringVar() self.not_matching_var = Tkinter.StringVar()
self.not_matching_var.set(app.opt.colors['not_matching']) self.not_matching_var.set(app.opt.colors['not_matching'])
# #
c = Tkinter.Checkbutton(frame, variable=self.use_default_var, row = 0
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
for title, var in ( for title, var in (
(_('Text foreground:'), self.text_var),
(_('Highlight piles:'), self.piles_var), (_('Highlight piles:'), self.piles_var),
(_('Highlight cards 1:'), self.cards_1_var), (_('Highlight cards 1:'), self.cards_1_var),
(_('Highlight cards 2:'), self.cards_2_var), (_('Highlight cards 2:'), self.cards_2_var),
@ -86,7 +76,7 @@ class ColorsDialog(MfxDialog):
(_('Hint arrow:'), self.hintarrow_var), (_('Hint arrow:'), self.hintarrow_var),
(_('Highlight not matching:'), self.not_matching_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') ).grid(row=row, column=0, sticky='we')
l = Tkinter.Label(frame, width=10, height=2, l = Tkinter.Label(frame, width=10, height=2,
bg=var.get(), textvariable=var) bg=var.get(), textvariable=var)
@ -99,7 +89,6 @@ class ColorsDialog(MfxDialog):
focus = self.createButtons(bottom_frame, kw) focus = self.createButtons(bottom_frame, kw)
self.mainloop(focus, kw.timeout) self.mainloop(focus, kw.timeout)
# #
self.use_default_color = not self.use_default_var.get()
self.text_color = self.text_var.get() self.text_color = self.text_var.get()
self.piles_color = self.piles_var.get() self.piles_color = self.piles_var.get()
self.cards_1_color = self.cards_1_var.get() self.cards_1_color = self.cards_1_var.get()

View file

@ -114,6 +114,7 @@ class FindCardDialog(Tkinter.Toplevel):
self.groups.append(group) self.groups.append(group)
def connectGame(self, game): def connectGame(self, game):
self.canvas.delete('all')
self.game = game self.game = game
suits = game.gameinfo.suits suits = game.gameinfo.suits
ranks = game.gameinfo.ranks ranks = game.gameinfo.ranks
@ -133,6 +134,7 @@ class FindCardDialog(Tkinter.Toplevel):
w, h = dx*j+2, dy*i+2 w, h = dx*j+2, dy*i+2
self.canvas.config(width=w, height=h) self.canvas.config(width=w, height=h)
self.wm_iconname(PACKAGE + " - " + game.getTitleName()) self.wm_iconname(PACKAGE + " - " + game.getTitleName())
self.wm_geometry('') # cancel user-specified geometry
def enterEvent(self, suit, rank, rect, group): def enterEvent(self, suit, rank, rect, group):
##print 'enterEvent', suit, rank, self.busy ##print 'enterEvent', suit, rank, self.busy

View file

@ -47,6 +47,7 @@ from pysollib.util import CARDSET
from pysollib.settings import PACKAGE, WIN_SYSTEM from pysollib.settings import PACKAGE, WIN_SYSTEM
from pysollib.settings import TOP_TITLE from pysollib.settings import TOP_TITLE
from pysollib.settings import SELECT_GAME_MENU from pysollib.settings import SELECT_GAME_MENU
from pysollib.settings import USE_FREECELL_SOLVER
from pysollib.gamedb import GI from pysollib.gamedb import GI
from pysollib.actions import PysolMenubarActions from pysollib.actions import PysolMenubarActions
@ -58,6 +59,7 @@ from soundoptionsdialog import SoundOptionsDialog
from selectcardset import SelectCardsetDialogWithPreview from selectcardset import SelectCardsetDialogWithPreview
from selecttile import SelectTileDialogWithPreview from selecttile import SelectTileDialogWithPreview
from findcarddialog import connect_game_find_card_dialog, destroy_find_card_dialog 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 tkwrap import MfxRadioMenuItem, MfxCheckMenuItem, StringVar
#from toolbar import TOOLBAR_BUTTONS #from toolbar import TOOLBAR_BUTTONS
@ -138,25 +140,18 @@ class MfxMenubar(Tkinter.Menu):
#print label, type(label) #print label, type(label)
name = re.sub(r"[^0-9a-zA-Z]", "", label).lower() name = re.sub(r"[^0-9a-zA-Z]", "", label).lower()
label = gettext(label) label = gettext(label)
underline = -1 underline = label.find('&')
m = re.search(r"^(.*)\&([^\&].*)$", label) if underline >= 0:
if m: label = label.replace('&', '')
l1, l2 = m.group(1), m.group(2)
l1 = re.sub(r"\&\&", "&", l1)
l2 = re.sub(r"\&\&", "&", l2)
label = l1 + l2
underline = len(l1)
return name, label, underline return name, label, underline
def add(self, itemType, cnf={}): def add(self, itemType, cnf={}):
label = cnf.get("label") label = cnf.get("label")
if label: if label:
name = cnf.get('name') name = cnf.get('name')
try: if name:
del cnf['name'] # TclError: unknown option "-name" del cnf['name'] # TclError: unknown option "-name"
except KeyError: else:
pass
if not name:
name, label, underline = self.labeltoname(label) name, label, underline = self.labeltoname(label)
cnf["underline"] = cnf.get("underline", underline) cnf["underline"] = cnf.get("underline", underline)
cnf["label"] = label cnf["label"] = label
@ -320,6 +315,7 @@ class PysolMenubar(PysolMenubarActions):
connect_game_find_card_dialog(game) connect_game_find_card_dialog(game)
else: else:
destroy_find_card_dialog() destroy_find_card_dialog()
connect_game_solver_dialog(game)
# create a GTK-like path # create a GTK-like path
def _addPath(self, path, menu, index, submenu): def _addPath(self, path, menu, index, submenu):
@ -368,7 +364,7 @@ class PysolMenubar(PysolMenubarActions):
menu.add_separator() menu.add_separator()
submenu = MfxMenu(menu, label=n_("Fa&vorite games")) 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_("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_separator()
menu.add_command(label=n_("&Open..."), command=self.mOpen, accelerator=m+"O") menu.add_command(label=n_("&Open..."), command=self.mOpen, accelerator=m+"O")
menu.add_command(label=n_("&Save"), command=self.mSave, accelerator=m+"S") 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_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_command(label=n_("&Pause"), command=self.mPause, accelerator="P")
menu.add_separator() 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_checkbutton(label=n_("&Comments..."), variable=self.tkopt.comment, command=self.mEditGameComment)
menu.add_separator() menu.add_separator()
submenu = MfxMenu(menu, label=n_("&Statistics")) 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_command(label=n_("Full log..."), command=lambda self=self: self.mPlayerStats(mode=103))
submenu.add_separator() submenu.add_separator()
submenu.add_command(label=TOP_TITLE+"...", command=self.mTop10, accelerator=m+"T") 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 = 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_("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)) 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 = MfxMenu(self.__menubar, label=n_("&Assist"))
menu.add_command(label=n_("&Hint"), command=self.mHint, accelerator="H") 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_("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_separator()
menu.add_command(label=n_("&Demo"), command=self.mDemo, accelerator=m+"D") menu.add_command(label=n_("&Demo"), command=self.mDemo, accelerator=m+"D")
menu.add_command(label=n_("Demo (&all games)"), command=self.mMixedDemo) 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_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) 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.add_checkbutton(label=n_("Shade &filled stacks"), variable=self.tkopt.shade_filled_stacks, command=self.mOptShadeFilledStacks)
submenu = MfxMenu(menu, label=n_("A&nimations")) 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_("&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_("&Very fast"), variable=self.tkopt.animations, value=1, command=self.mOptAnimations)
submenu.add_radiobutton(label=n_("&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_("&Slow"), variable=self.tkopt.animations, value=3, command=self.mOptAnimations) submenu.add_radiobutton(label=n_("&Medium"), 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_("&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_separator()
submenu.add_checkbutton(label=n_("&Redeal animation"), variable=self.tkopt.redeal_animation, command=self.mRedealAnimation) submenu.add_checkbutton(label=n_("&Redeal animation"), variable=self.tkopt.redeal_animation, command=self.mRedealAnimation)
if Image: if Image:
@ -527,15 +529,13 @@ class PysolMenubar(PysolMenubarActions):
ctrl = "Control-" ctrl = "Control-"
if sys.platform == "darwin": ctrl = "Command-" if sys.platform == "darwin": ctrl = "Command-"
self._bindKey("", "n", self.mNewGame) self._bindKey("", "n", self.mNewGame)
self._bindKey("", "g", self.mSelectGameDialog) self._bindKey(ctrl, "w", self.mSelectGameDialog)
self._bindKey("", "v", self.mSelectGameDialogWithPreview) self._bindKey(ctrl, "v", self.mSelectGameDialogWithPreview)
self._bindKey(ctrl, "r", lambda e, self=self: self.mSelectRandomGame()) self._bindKey(ctrl, "r", lambda e, self=self: self.mSelectRandomGame())
self._bindKey(ctrl, "m", self.mSelectGameById) self._bindKey(ctrl, "m", self.mSelectGameById)
self._bindKey(ctrl, "n", self.mNewGameWithNextId) self._bindKey(ctrl, "n", self.mNewGameWithNextId)
self._bindKey(ctrl, "o", self.mOpen) self._bindKey(ctrl, "o", self.mOpen)
##self._bindKey("", "F3", self.mOpen) # undocumented
self._bindKey(ctrl, "s", self.mSave) self._bindKey(ctrl, "s", self.mSave)
##self._bindKey("", "F2", self.mSaveAs) # undocumented
self._bindKey(ctrl, "q", self.mQuit) self._bindKey(ctrl, "q", self.mQuit)
self._bindKey("", "z", self.mUndo) self._bindKey("", "z", self.mUndo)
self._bindKey("", "BackSpace", self.mUndo) # undocumented self._bindKey("", "BackSpace", self.mUndo) # undocumented
@ -543,14 +543,14 @@ class PysolMenubar(PysolMenubarActions):
self._bindKey("", "r", self.mRedo) self._bindKey("", "r", self.mRedo)
self._bindKey(ctrl, "g", self.mRestart) self._bindKey(ctrl, "g", self.mRestart)
self._bindKey("", "space", self.mDeal) # undocumented 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(ctrl, "t", self.mTop10)
self._bindKey("", "h", self.mHint) self._bindKey("", "h", self.mHint)
self._bindKey(ctrl, "h", self.mHint1) # undocumented self._bindKey(ctrl, "h", self.mHint1) # undocumented
##self._bindKey("", "Shift_L", self.mHighlightPiles) ##self._bindKey("", "Shift_L", self.mHighlightPiles)
##self._bindKey("", "Shift_R", self.mHighlightPiles) ##self._bindKey("", "Shift_R", self.mHighlightPiles)
self._bindKey("", "i", 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, "d", self.mDemo)
self._bindKey(ctrl, "e", self.mSelectCardsetDialog) self._bindKey(ctrl, "e", self.mSelectCardsetDialog)
self._bindKey(ctrl, "b", self.mOptChangeCardback) # undocumented self._bindKey(ctrl, "b", self.mOptChangeCardback) # undocumented
@ -593,19 +593,19 @@ class PysolMenubar(PysolMenubarActions):
# #
def _bindKey(self, modifier, key, func): def _bindKey(self, modifier, key, func):
if 0 and not modifier and len(key) == 1: ## if 0 and not modifier and len(key) == 1:
self.__keybindings[key.lower()] = func ## self.__keybindings[key.lower()] = func
self.__keybindings[key.upper()] = func ## self.__keybindings[key.upper()] = func
return ## 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 + ">" sequence = "<" + modifier + "KeyPress-" + key + ">"
try:
bind(self.top, sequence, func) bind(self.top, sequence, func)
if len(key) == 1 and key != key.upper(): if len(key) == 1 and key != key.upper():
key = key.upper() key = key.upper()
sequence = "<" + modifier + "KeyPress-" + key + ">" sequence = "<" + modifier + "KeyPress-" + key + ">"
bind(self.top, sequence, func) bind(self.top, sequence, func)
except:
raise
def _keyPressHandler(self, event): def _keyPressHandler(self, event):
@ -617,10 +617,10 @@ class PysolMenubar(PysolMenubarActions):
if event.char: # ignore Ctrl/Shift/etc. if event.char: # ignore Ctrl/Shift/etc.
self.game.demo.keypress = event.char self.game.demo.keypress = event.char
r = EVENT_HANDLED r = EVENT_HANDLED
func = self.__keybindings.get(event.char) ## func = self.__keybindings.get(event.char)
if func and (event.state & ~2) == 0: ## if func and (event.state & ~2) == 0:
func(event) ## func(event)
r = EVENT_HANDLED ## r = EVENT_HANDLED
return r return r
# #
@ -632,9 +632,11 @@ class PysolMenubar(PysolMenubarActions):
games = map(self.app.gdb.get, self.app.gdb.getGamesIdSortedByName()) games = map(self.app.gdb.get, self.app.gdb.getGamesIdSortedByName())
##games = tuple(games) ##games = tuple(games)
###menu = MfxMenu(menu, label="Select &game") ###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) 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) command=self.mSelectGameDialogWithPreview)
if not SELECT_GAME_MENU: if not SELECT_GAME_MENU:
return return
@ -779,13 +781,19 @@ class PysolMenubar(PysolMenubarActions):
gi = games[i] gi = games[i]
columnbreak = i > 0 and (i % cb) == 0 columnbreak = i > 0 and (i % cb) == 0
if short_name: if short_name:
label = gi.short_name label = gettext(gi.short_name)
else: else:
label = gi.name label = gettext(gi.name)
menu.add_radiobutton(command=command, variable=variable, ## menu.add_radiobutton(command=command, variable=variable,
columnbreak=columnbreak, ## columnbreak=columnbreak,
value=gi.id, label=label, name=None) ## 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 # Select Game menu actions
@ -1149,7 +1157,6 @@ class PysolMenubar(PysolMenubarActions):
self._cancelDrag() self._cancelDrag()
self.game.endGame(bookmark=1) self.game.endGame(bookmark=1)
self.game.quitGame(bookmark=1) self.game.quitGame(bookmark=1)
self.app.opt.games_geometry = {} # clear saved games geometry
def _mOptCardback(self, index): def _mOptCardback(self, index):
if self._cancelDrag(break_pause=False): return if self._cancelDrag(break_pause=False): return
@ -1171,10 +1178,6 @@ class PysolMenubar(PysolMenubarActions):
def mOptChangeCardback(self, *event): def mOptChangeCardback(self, *event):
self._mOptCardback(self.app.cardset.backindex + 1) 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): def mOptChangeTableTile(self, *event):
if self._cancelDrag(break_pause=False): return if self._cancelDrag(break_pause=False): return
n = self.app.tabletile_manager.len() n = self.app.tabletile_manager.len()
@ -1337,4 +1340,3 @@ class PysolMenubar(PysolMenubarActions):
else: else:
if self._cancelDrag(break_pause=True): return if self._cancelDrag(break_pause=True): return
self.game.showStackDesc() self.game.showStackDesc()

View file

@ -437,7 +437,7 @@ class SelectGameDialogWithPreview(SelectGameDialog):
destruct(self.preview_app) destruct(self.preview_app)
self.preview_app = None self.preview_app = None
def updatePreview(self, gameid, animations=5): def updatePreview(self, gameid, animations=10):
if gameid == self.preview_key: if gameid == self.preview_key:
return return
self.deletePreview() self.deletePreview()
@ -504,7 +504,7 @@ class SelectGameDialogWithPreview(SelectGameDialog):
# #
self.preview_app.audio = self.app.audio self.preview_app.audio = self.app.audio
if self.app.opt.animations: if self.app.opt.animations:
self.preview_app.opt.animations = 5 self.preview_app.opt.animations = 10
else: else:
self.preview_app.opt.animations = 0 self.preview_app.opt.animations = 0
# save seed # save seed
@ -562,13 +562,12 @@ class SelectGameDialogWithPreview(SelectGameDialog):
('percent', percent), ('percent', percent),
): ):
title_label, text_label = self.info_labels[n] title_label, text_label = self.info_labels[n]
if t == '': if t in ('', None):
title_label.grid_remove() title_label.grid_remove()
text_label.grid_remove() text_label.grid_remove()
else: else:
title_label.grid() title_label.grid()
text_label.grid() text_label.grid()
text_label.config(text=t) text_label.config(text=t)
#self.info_labels[n].config(text=t)

333
pysollib/tk/solverdialog.py Normal file
View file

@ -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

View file

@ -121,7 +121,7 @@ class SoundOptionsDialog(MfxDialog):
if app.audio.CAN_PLAY_MUSIC: # and app.startup_opt.sound_mode > 0: if app.audio.CAN_PLAY_MUSIC: # and app.startup_opt.sound_mode > 0:
row += 1 row += 1
w = Tkinter.Label(frame, text=_('Sample volume:')) 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, w = Tkinter.Scale(frame, from_=0, to=128, resolution=1,
orient='horizontal', takefocus=0, orient='horizontal', takefocus=0,
length="3i", #label=_('Sample volume'), length="3i", #label=_('Sample volume'),
@ -134,7 +134,7 @@ class SoundOptionsDialog(MfxDialog):
orient='horizontal', takefocus=0, orient='horizontal', takefocus=0,
length="3i", #label=_('Music volume'), length="3i", #label=_('Music volume'),
variable=self.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: else:
# remove "Apply" button # remove "Apply" button

View file

@ -116,8 +116,11 @@ class MfxStatusbar:
def updateText(self, **kw): def updateText(self, **kw):
for k, v in kw.items(): for k, v in kw.items():
label = getattr(self, k + "_label") label = getattr(self, k + "_label")
#label["text"] = str(v) text = unicode(v)
label["text"] = unicode(v) width = label['width']
if width and len(text) > width:
label['width'] = len(text)
label["text"] = text
def configLabel(self, name, **kw): def configLabel(self, name, **kw):
label = getattr(self, name + "_label") label = getattr(self, name + "_label")

View file

@ -370,21 +370,14 @@ class MfxCanvas(Tkinter.Canvas):
def _substitute(self, *args): def _substitute(self, *args):
e = Tkinter.Event() e = Tkinter.Event()
try:
# Tk changed behavior in 8.4.2, returning "??" rather more often.
e.x = int(args[0]) e.x = int(args[0])
except ValueError:
e.x = args[0]
try:
e.y = int(args[1]) e.y = int(args[1])
except ValueError:
e.y = args[1]
return (e,) 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)

View file

@ -38,7 +38,9 @@ __all__ = ['SingleGame_StatsDialog',
'FullLog_StatsDialog', 'FullLog_StatsDialog',
'SessionLog_StatsDialog', 'SessionLog_StatsDialog',
'Status_StatsDialog', 'Status_StatsDialog',
'Top_StatsDialog'] 'Top_StatsDialog',
'ProgressionDialog',
]
# imports # imports
import os, string, sys, types import os, string, sys, types
@ -49,7 +51,7 @@ import Tkinter, tkFont
from pysollib.mfxutil import destruct, Struct, kwdefault, KwStruct from pysollib.mfxutil import destruct, Struct, kwdefault, KwStruct
from pysollib.mfxutil import format_time from pysollib.mfxutil import format_time
##from pysollib.util import * ##from pysollib.util import *
from pysollib.stats import PysolStatsFormatter from pysollib.stats import PysolStatsFormatter, ProgressionFormatter
from pysollib.settings import TOP_TITLE from pysollib.settings import TOP_TITLE
# Toolkit imports # Toolkit imports
@ -720,7 +722,7 @@ class Top_StatsDialog(MfxDialog):
self.createBitmaps(top_frame, kw) self.createBitmaps(top_frame, kw)
frame = Tkinter.Frame(top_frame) 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) frame.columnconfigure(0, weight=1)
if (player in app.stats.games_stats and if (player in app.stats.games_stats and
@ -797,3 +799,266 @@ class Top_StatsDialog(MfxDialog):
separatorwidth=2, separatorwidth=2,
) )
return MfxDialog.initKw(self, kw) 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)

View file

@ -507,15 +507,10 @@ class MfxScrolledCanvas:
if i == 0: if i == 0:
self.canvas.config(bg=tile.color) self.canvas.config(bg=tile.color)
##app.top.config(bg=tile.color) ##app.top.config(bg=tile.color)
color = None
else: else:
self.canvas.config(bg=app.top_bg) self.canvas.config(bg=app.top_bg)
##app.top.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 return True

View file

@ -33,7 +33,7 @@
## ##
##---------------------------------------------------------------------------## ##---------------------------------------------------------------------------##
__all__ = ['PysolToolbar'] #, 'TOOLBAR_BUTTONS'] __all__ = ['PysolToolbar']
# imports # imports
import os, sys, types, Tkinter import os, sys, types, Tkinter
@ -245,6 +245,7 @@ class PysolToolbar(PysolToolbarActions):
widget.hide() widget.hide()
# #
prev_visible = None prev_visible = None
last_visible = None
for w in self._widgets: for w in self._widgets:
if w.__class__ is ToolbarSeparator: if w.__class__ is ToolbarSeparator:
if prev_visible is None or prev_visible.__class__ is ToolbarSeparator: if prev_visible is None or prev_visible.__class__ is ToolbarSeparator:
@ -256,6 +257,10 @@ class PysolToolbar(PysolToolbarActions):
prev_visible.hide() prev_visible.hide()
if w.visible: if w.visible:
prev_visible = w prev_visible = w
if not isinstance(w, ToolbarLabel):
last_visible = w
if isinstance(last_visible, ToolbarSeparator):
last_visible.hide()
# util # util
def _loadImage(self, name): def _loadImage(self, name):

View file

@ -42,13 +42,13 @@ def init_tile(app, top, theme):
traceback.print_exc() traceback.print_exc()
pass pass
def set_theme(top, theme): def set_theme(app, top, theme):
# set theme # set theme
style = Tile.Style(top) style = Tile.Style(top)
all_themes = style.theme_names() all_themes = style.theme_names()
if theme not in all_themes: if theme not in all_themes:
print >> sys.stderr, 'WARNING: invalid theme name:', theme print >> sys.stderr, 'WARNING: invalid theme name:', theme
theme = 'default' theme = app.opt.default_tile_theme
style.theme_use(theme) style.theme_use(theme)
@ -91,7 +91,7 @@ class baseInitRootWindow:
elif USE_TILE: elif USE_TILE:
theme = app.opt.tile_theme theme = app.opt.tile_theme
init_tile(app, root, theme) init_tile(app, root, theme)
set_theme(root, theme) set_theme(app, root, theme)
else: else:
pass pass

View file

@ -72,6 +72,8 @@ class initRootWindow(baseInitRootWindow):
app.opt.fonts['default'] = fn app.opt.fonts['default'] = fn
root.option_add('*Menu.borderWidth', 1, 60) root.option_add('*Menu.borderWidth', 1, 60)
root.option_add('*Menu.activeBorderWidth', 1, 60) root.option_add('*Menu.activeBorderWidth', 1, 60)
if app.opt.tile_theme == 'clam':
root.wm_minsize(550, 360)
# #
else: else:
root.option_add('*Entry.background', 'white', 60) root.option_add('*Entry.background', 'white', 60)

View file

@ -140,8 +140,8 @@ def all_games(sort_by='id'):
gt = 'French (%s)' % GAME_BY_TYPE[gi.si.game_type] gt = 'French (%s)' % GAME_BY_TYPE[gi.si.game_type]
name = gi.name.encode('utf-8') name = gi.name.encode('utf-8')
altnames = '<br>'.join(gi.altnames).encode('utf-8') altnames = '<br>'.join(gi.altnames).encode('utf-8')
if 1 and os.path.exists(os.path.join(rules_dir, rules_fn)): fn = os.path.join(rules_dir, rules_fn)
fn = '../data/html/rules/'+rules_fn if 1 and os.path.exists(fn):
print '''<tr><td>%s</td><td> print '''<tr><td>%s</td><td>
<a href="%s" title="Rules for this game">%s</a> <a href="%s" title="Rules for this game">%s</a>
</td><td>%s</td><td>%s</td></tr> </td><td>%s</td><td>%s</td></tr>
@ -152,13 +152,19 @@ def all_games(sort_by='id'):
print '</table>' print '</table>'
def create_html(sort_by): def create_html(sort_by):
print '<html><body>' print '''<html>
<head>
<title>PySolFC - List of solitaire games</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
'''
print '<b>Total games: %d</b>' % len(GAME_DB.getGamesIdSortedById()) print '<b>Total games: %d</b>' % len(GAME_DB.getGamesIdSortedById())
print '<h2>Categories</h2>' print '<h2>Categories</h2>'
by_category() by_category()
print '<h2>Types</h2>' print '<h2>Types</h2>'
by_type() by_type()
print '<h2>All games</h2>' #print '<h2>All games</h2>'
all_games(sort_by) all_games(sort_by)
print '</body></html>' print '</body></html>'
@ -220,6 +226,9 @@ def plain_text():
gi = GAME_DB.get(id) gi = GAME_DB.get(id)
if gi.category == GI.GC_FRENCH: if gi.category == GI.GC_FRENCH:
##print str(gi.gameclass) ##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') print gi.name.encode('utf-8')
for n in gi.altnames: for n in gi.altnames:
print n.encode('utf-8') print n.encode('utf-8')
@ -233,6 +242,8 @@ if len(sys.argv) < 2 or sys.argv[1] == 'html':
sort_by = 'id' sort_by = 'id'
if len(sys.argv) > 2: if len(sys.argv) > 2:
sort_by = sys.argv[2] sort_by = sys.argv[2]
if len(sys.argv) > 3:
rules_dir = sys.argv[3]
create_html(sort_by) create_html(sort_by)
elif sys.argv[1] == 'gettext': elif sys.argv[1] == 'gettext':
get_text() get_text()

View file

@ -4,7 +4,7 @@ cd ..
rm -rf dist rm -rf dist
mkdir dist mkdir dist
cp -r locale 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 cp smpeg.dll ogg.dll vorbis.dll vorbisfile.dll dist
python setup.py py2exe python setup.py py2exe
cp -r d:\Python\tcl\tile0.7.8 dist\tcl cp -r d:\Python\tcl\tile0.7.8 dist\tcl

View file

@ -4,6 +4,7 @@
import os import os
from distutils.core import setup from distutils.core import setup
from pysollib.settings import FC_VERSION as VERSION from pysollib.settings import FC_VERSION as VERSION
from pysollib.settings import PACKAGE_URL
if os.name == 'nt': if os.name == 'nt':
import py2exe import py2exe
@ -14,7 +15,7 @@ elif os.name == 'nt':
else: else:
data_dir = 'data' data_dir = 'data'
datas = [ ddirs = [
'html', 'html',
'images', 'images',
'sound', 'sound',
@ -25,11 +26,11 @@ datas = [
] ]
for s in file('MANIFEST.in'): for s in file('MANIFEST.in'):
if s.startswith('graft data/cardset-'): if s.startswith('graft data/cardset-'):
datas.append(s[11:].strip()) ddirs.append(s[11:].strip())
data_files = [] data_files = []
for d in datas: for d in ddirs:
for root, dirs, files in os.walk(os.path.join('data', d)): for root, dirs, files in os.walk(os.path.join('data', d)):
if root.find('.svn') >= 0: if root.find('.svn') >= 0:
continue continue
@ -57,7 +58,7 @@ integrated HTML help browser, and it\'s free Open Source software.
kw = { kw = {
'name' : 'PySolFC', 'name' : 'PySolFC',
'version' : VERSION, 'version' : VERSION,
'url' : 'http://sourceforge.net/projects/pysolfc/', 'url' : PACKAGE_URL,
'author' : 'Skomoroh', 'author' : 'Skomoroh',
'author_email' : 'skomoroh@gmail.com', 'author_email' : 'skomoroh@gmail.com',
'description' : 'PySol - a solitaire game collection', 'description' : 'PySol - a solitaire game collection',