+ 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: file:///home/shlomif/Backup/svn-dumps/PySolFC/svnsync-repos/pysolfc/PySolFC/trunk@138 efabe8c0-fbe8-4139-b769-b5e6d273206e
|
@ -24,7 +24,7 @@ graft html-src
|
|||
## data - images
|
||||
##
|
||||
#graft data/images
|
||||
recursive-include data/images *.gif *.png
|
||||
recursive-include data/images *.gif *.png *.jpg
|
||||
graft data/tiles
|
||||
include data/pysol.xbm data/pysol.xpm data/pysol.ico
|
||||
##
|
||||
|
|
|
@ -38,7 +38,6 @@ gchar *s = N_("Highlight piles:");
|
|||
gchar *s = N_("Highlight cards:");
|
||||
gchar *s = N_("Highlight same rank:");
|
||||
gchar *s = N_("Set colors");
|
||||
gchar *s = N_("Text foreground:");
|
||||
gchar *s = N_("Highlight piles:");
|
||||
gchar *s = N_("Highlight cards 1:");
|
||||
gchar *s = N_("Highlight cards 2:");
|
||||
|
@ -54,6 +53,7 @@ gchar *s = N_("Change...");
|
|||
gchar *s = N_("Change...");
|
||||
gchar *s = N_("Change...");
|
||||
gchar *s = N_("Change...");
|
||||
gchar *s = N_("Text foreground:");
|
||||
gchar *s = N_("Set font");
|
||||
gchar *s = N_("HTML: ");
|
||||
gchar *s = N_("Small: ");
|
||||
|
|
BIN
data/images/stats/progression.jpg
Normal file
After Width: | Height: | Size: 4.6 KiB |
|
@ -2051,30 +2051,6 @@
|
|||
<property name="row_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>
|
||||
<widget class="GtkLabel" id="label31">
|
||||
<property name="visible">True</property>
|
||||
|
@ -3128,6 +3104,32 @@
|
|||
<property name="y_options">fill</property>
|
||||
</packing>
|
||||
</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>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
|
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 6 KiB After Width: | Height: | Size: 6 KiB |
Before Width: | Height: | Size: 170 KiB After Width: | Height: | Size: 170 KiB |
Before Width: | Height: | Size: 206 KiB After Width: | Height: | Size: 206 KiB |
|
@ -1,5 +1,4 @@
|
|||
<h1>PySol - a Solitaire Game Collection</h1>
|
||||
<hr>
|
||||
|
||||
<p> <a href="intro.html">Introduction</a>
|
||||
<p> <a href="install.html">Installation</a>
|
||||
|
|
|
@ -26,6 +26,3 @@ by court artisans. No cards from this pack are known to still exist.
|
|||
<h3>Strategy</h3>
|
||||
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.
|
||||
<p>
|
||||
<br>
|
||||
<a href="../ganjifa.html">General Ganjifa Rules</a>
|
||||
|
|
|
@ -15,5 +15,5 @@ and the number of cards you can move as a sequence is not restricted.
|
|||
<p>
|
||||
All cards are dealt to 9 piles at the start of the game, each Raja or King
|
||||
starting a new pile. Rows build down in rank and alternate color.
|
||||
Refer to the general <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.
|
||||
|
|
|
@ -8,7 +8,7 @@ Move all cards to the foundations.
|
|||
|
||||
<h3>Quick Description</h3>
|
||||
<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.
|
||||
|
||||
<h3>Rules</h3>
|
||||
|
@ -19,7 +19,3 @@ 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.
|
||||
|
|
16
html-src/rules/newbritishconstitution.html
Normal 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>
|
25
html-src/rules/retinue.html
Normal 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.
|
|
@ -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.
|
||||
<p>
|
||||
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.
|
||||
<p>
|
||||
The aim is to move all the cards into thirteen-card sequences on the goal
|
||||
|
|
11
po/games.pot
|
@ -5,7 +5,7 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PySol 0.0.1\n"
|
||||
"POT-Creation-Date: Fri Jan 12 13:34:09 2007\n"
|
||||
"POT-Creation-Date: Thu Feb 15 18:35:01 2007\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
@ -216,6 +216,9 @@ msgstr ""
|
|||
msgid "Baroness"
|
||||
msgstr ""
|
||||
|
||||
msgid "Barrier"
|
||||
msgstr ""
|
||||
|
||||
msgid "Bastille Day"
|
||||
msgstr ""
|
||||
|
||||
|
@ -243,6 +246,9 @@ msgstr ""
|
|||
msgid "Beatle"
|
||||
msgstr ""
|
||||
|
||||
msgid "Bebop"
|
||||
msgstr ""
|
||||
|
||||
msgid "Beetle"
|
||||
msgstr ""
|
||||
|
||||
|
@ -3717,6 +3723,9 @@ msgstr ""
|
|||
msgid "Wasp"
|
||||
msgstr ""
|
||||
|
||||
msgid "Waterfall"
|
||||
msgstr ""
|
||||
|
||||
msgid "Waterloo"
|
||||
msgstr ""
|
||||
|
||||
|
|
1086
po/pysol.pot
|
@ -5,8 +5,8 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PySol 0.0.1\n"
|
||||
"POT-Creation-Date: Fri Jan 12 13:34:09 2007\n"
|
||||
"PO-Revision-Date: 2007-01-17 19:12+0300\n"
|
||||
"POT-Creation-Date: Thu Feb 15 18:35:01 2007\n"
|
||||
"PO-Revision-Date: 2007-02-12 19:08+0300\n"
|
||||
"Last-Translator: Скоморох <skomoroh@gmail.com>\n"
|
||||
"Language-Team: Russian <ru@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
|
@ -217,6 +217,10 @@ msgstr "Баларама"
|
|||
msgid "Baroness"
|
||||
msgstr "Баронесса"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Barrier"
|
||||
msgstr "Причудливый"
|
||||
|
||||
msgid "Bastille Day"
|
||||
msgstr "День Бастилии"
|
||||
|
||||
|
@ -244,6 +248,9 @@ msgstr "Клюв и ласты"
|
|||
msgid "Beatle"
|
||||
msgstr "Жук"
|
||||
|
||||
msgid "Bebop"
|
||||
msgstr ""
|
||||
|
||||
msgid "Beetle"
|
||||
msgstr "Жук"
|
||||
|
||||
|
@ -3772,6 +3779,10 @@ msgstr "Фаворит Вашингтона"
|
|||
msgid "Wasp"
|
||||
msgstr "Оса"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Waterfall"
|
||||
msgstr "Ватерлоо"
|
||||
|
||||
msgid "Waterloo"
|
||||
msgstr "Ватерлоо"
|
||||
|
||||
|
|
1062
po/ru_pysol.po
|
@ -50,6 +50,7 @@ from stats import FileStatsFormatter
|
|||
from pysoltk import SingleGame_StatsDialog, AllGames_StatsDialog
|
||||
from pysoltk import FullLog_StatsDialog, SessionLog_StatsDialog
|
||||
from pysoltk import Status_StatsDialog, Top_StatsDialog
|
||||
from pysoltk import ProgressionDialog
|
||||
from pysoltk import GameInfoDialog
|
||||
|
||||
# toolkit imports
|
||||
|
@ -61,6 +62,7 @@ from pysoltk import ColorsDialog
|
|||
from pysoltk import FontsDialog
|
||||
from pysoltk import EditTextDialog
|
||||
from pysoltk import create_find_card_dialog
|
||||
from pysoltk import create_solver_dialog
|
||||
from help import help_about, help_html
|
||||
|
||||
gettext = _
|
||||
|
@ -473,11 +475,13 @@ class PysolMenubarActions:
|
|||
|
||||
def mDrop(self, *args):
|
||||
if self._cancelDrag(): return
|
||||
self.game.autoPlay(autofaceup=-1, autodrop=1)
|
||||
##self.game.autoPlay(autofaceup=-1, autodrop=1)
|
||||
self.game.autoDrop(autofaceup=-1)
|
||||
|
||||
def mDrop1(self, *args):
|
||||
if self._cancelDrag(): return
|
||||
self.game.autoPlay(autofaceup=1, autodrop=1)
|
||||
##self.game.autoPlay(autofaceup=1, autodrop=1)
|
||||
self.game.autoDrop(autofaceup=1)
|
||||
|
||||
def mStatus(self, *args):
|
||||
if self._cancelDrag(break_pause=False): return
|
||||
|
@ -495,6 +499,9 @@ class PysolMenubarActions:
|
|||
create_find_card_dialog(self.game.top, self.game,
|
||||
self.app.getFindCardImagesDir())
|
||||
|
||||
def mSolver(self, *args):
|
||||
create_solver_dialog(self.game.top, self.app)
|
||||
|
||||
def mEditGameComment(self, *args):
|
||||
if self._cancelDrag(break_pause=False): return
|
||||
game, gi = self.game, self.game.gameinfo
|
||||
|
@ -593,6 +600,9 @@ class PysolMenubarActions:
|
|||
elif mode == 106:
|
||||
header = _("Game Info")
|
||||
d = GameInfoDialog(self.top, header, self.app)
|
||||
elif mode == 107:
|
||||
header = _("Statistics progression")
|
||||
d = ProgressionDialog(self.top, header, self.app, player, gameid=self.game.id)
|
||||
elif mode == 202:
|
||||
# print stats to file
|
||||
write_method = FileStatsFormatter.writeStats
|
||||
|
@ -698,9 +708,7 @@ class PysolMenubarActions:
|
|||
if self._cancelDrag(break_pause=False): return
|
||||
d = ColorsDialog(self.top, _("Set colors"), self.app)
|
||||
text_color = self.app.opt.colors['text']
|
||||
use_default_text_color = self.app.opt.use_default_text_color
|
||||
if d.status == 0 and d.button == 0:
|
||||
self.app.opt.use_default_text_color = d.use_default_color
|
||||
self.app.opt.colors['text'] = d.text_color
|
||||
self.app.opt.colors['piles'] = d.piles_color
|
||||
self.app.opt.colors['cards_1'] = d.cards_1_color
|
||||
|
@ -710,9 +718,8 @@ class PysolMenubarActions:
|
|||
self.app.opt.colors['hintarrow'] = d.hintarrow_color
|
||||
self.app.opt.colors['not_matching'] = d.not_matching_color
|
||||
#
|
||||
if (text_color != self.app.opt.colors['text'] or
|
||||
use_default_text_color != self.app.opt.use_default_text_color):
|
||||
self.app.setTile(self.app.tabletile_index)
|
||||
if text_color != self.app.opt.colors['text']:
|
||||
self.app.setTile(self.app.tabletile_index, force=True)
|
||||
|
||||
def mOptFonts(self, *args):
|
||||
if self._cancelDrag(break_pause=False): return
|
||||
|
|
|
@ -71,6 +71,8 @@ from pysoltk import SelectDialogTreeData
|
|||
from pysoltk import HTMLViewer
|
||||
from pysoltk import TOOLBAR_BUTTONS
|
||||
from pysoltk import destroy_find_card_dialog
|
||||
from pysoltk import destroy_solver_dialog
|
||||
from pysoltk import connect_game_solver_dialog
|
||||
from help import help_about, destroy_help_html
|
||||
|
||||
gettext = _
|
||||
|
@ -102,7 +104,7 @@ class Options:
|
|||
self.mahjongg_show_removed = False
|
||||
self.mahjongg_create_solvable = True
|
||||
self.shisen_show_hint = True
|
||||
self.animations = 2 # default to Timer based
|
||||
self.animations = 2 # default to Fast
|
||||
self.redeal_animation = True
|
||||
self.win_animation = True
|
||||
self.shadow = True
|
||||
|
@ -112,13 +114,14 @@ class Options:
|
|||
self.demo_logo = True
|
||||
self.tile_theme = 'default'
|
||||
if WIN_SYSTEM == 'win32':
|
||||
self.tile_theme = 'winnative'
|
||||
self.tile_theme = self.default_tile_theme = 'winnative'
|
||||
if sys.getwindowsversion() >= (5, 1): # xp
|
||||
self.tile_theme = 'xpnative'
|
||||
elif WIN_SYSTEM == 'x11':
|
||||
self.tile_theme = 'step'
|
||||
self.tile_theme = 'clam'
|
||||
self.default_tile_theme = 'default'
|
||||
elif WIN_SYSTEM == 'aqua':
|
||||
self.tile_theme = 'aqua'
|
||||
self.tile_theme = self.default_tile_theme = 'aqua'
|
||||
self.toolbar = 1 # 0 == hide, 1,2,3,4 == top, bottom, lef, right
|
||||
##self.toolbar_style = 'default'
|
||||
self.toolbar_style = 'bluecurve'
|
||||
|
@ -190,7 +193,6 @@ class Options:
|
|||
'hintarrow': '#303030',
|
||||
'not_matching': '#ff0000',
|
||||
}
|
||||
self.use_default_text_color = False
|
||||
# delays
|
||||
self.timeouts = {
|
||||
'hint': 1.0,
|
||||
|
@ -708,10 +710,12 @@ class Application:
|
|||
except:
|
||||
traceback.print_exc()
|
||||
pass
|
||||
# save game geometry
|
||||
self.wm_save_state()
|
||||
# save game geometry
|
||||
if self.opt.save_games_geometry and not self.opt.wm_maximized:
|
||||
geom = (self.canvas.winfo_width(), self.canvas.winfo_height())
|
||||
w = self.canvas.winfo_width()
|
||||
h = self.canvas.winfo_height()
|
||||
geom = (w-2, h-2) # XXX: subtract canvas borderwidth
|
||||
self.opt.games_geometry[self.game.id] = geom
|
||||
self.freeGame()
|
||||
#
|
||||
|
@ -726,8 +730,9 @@ class Application:
|
|||
# hide main window
|
||||
self.wm_withdraw()
|
||||
#
|
||||
destroy_find_card_dialog()
|
||||
destroy_help_html()
|
||||
destroy_find_card_dialog()
|
||||
destroy_solver_dialog()
|
||||
# update options
|
||||
self.opt.last_gameid = id
|
||||
# save options
|
||||
|
@ -1005,7 +1010,7 @@ class Application:
|
|||
#from pprint import pprint; pprint(self.opt.cardset)
|
||||
|
||||
def loadCardset(self, cs, id=0, update=7, progress=None):
|
||||
#print 'loadCardset', cs.ident
|
||||
##print 'loadCardset', cs.ident
|
||||
r = 0
|
||||
if cs is None or cs.error:
|
||||
return 0
|
||||
|
@ -1049,6 +1054,10 @@ class Application:
|
|||
elif self.images is not None:
|
||||
##self.images.destruct()
|
||||
destruct(self.images)
|
||||
#
|
||||
if self.cardset and self.cardset.ident != cs.ident and \
|
||||
self.cardset.type == cs.type:
|
||||
self.opt.games_geometry = {} # clear saved games geometry
|
||||
# update
|
||||
self.images = images
|
||||
self.subsampled_images = simages
|
||||
|
@ -1381,6 +1390,9 @@ Please select a %s type %s.
|
|||
names.sort()
|
||||
return names
|
||||
|
||||
def getGamesForSolver(self):
|
||||
return self.gdb.getGamesForSolver()
|
||||
|
||||
#
|
||||
# plugins
|
||||
#
|
||||
|
@ -1591,7 +1603,6 @@ Please select a %s type %s.
|
|||
##print dirs
|
||||
s = "((\\" + ")|(\\".join(IMAGE_EXTENSIONS) + "))$"
|
||||
ext_re = re.compile(s, re.I)
|
||||
text_color_re = re.compile(r"^(.+)-([0-9A-Fa-f]{6})$")
|
||||
found, t = [], {}
|
||||
for dir in dirs:
|
||||
dir = dir.strip()
|
||||
|
@ -1611,16 +1622,6 @@ Please select a %s type %s.
|
|||
n = ext_re.sub("", name.strip())
|
||||
if os.path.split(dir)[-1] == 'stretch':
|
||||
tile.stretch = 1
|
||||
elif n.find('-stretch') > 0:
|
||||
# stretch?
|
||||
tile.stretch = 1
|
||||
n = n.replace('-stretch', '')
|
||||
#else:
|
||||
# tile.stretch = 0
|
||||
m = text_color_re.search(n)
|
||||
if m:
|
||||
n = m.group(1)
|
||||
tile.text_color = "#" + m.group(2).lower()
|
||||
#n = re.sub("[-_]", " ", n)
|
||||
n = n.replace('_', ' ')
|
||||
##n = unicode(n)
|
||||
|
|
420
pysollib/game.py
|
@ -37,6 +37,7 @@
|
|||
# imports
|
||||
import time
|
||||
import math
|
||||
import md5
|
||||
from cStringIO import StringIO
|
||||
|
||||
# PySol imports
|
||||
|
@ -57,7 +58,9 @@ from pysoltk import after, after_idle, after_cancel
|
|||
from pysoltk import MfxMessageDialog, MfxExceptionDialog
|
||||
from pysoltk import MfxCanvasText, MfxCanvasLine, MfxCanvasRectangle
|
||||
from pysoltk import Card
|
||||
from move import AMoveMove, AFlipMove, ATurnStackMove
|
||||
from pysoltk import reset_solver_dialog
|
||||
from move import AMoveMove, AFlipMove, AFlipAndMoveMove
|
||||
from move import ASingleFlipMove, ATurnStackMove
|
||||
from move import ANextRoundMove, ASaveSeedMove, AShuffleStackMove
|
||||
from move import AUpdateStackMove, AFlipAllMove, ASaveStateMove
|
||||
from move import ASingleCardMove
|
||||
|
@ -114,6 +117,8 @@ class Game:
|
|||
self.cards = []
|
||||
self.stackmap = {} # dict with (x,y) tuples as key
|
||||
self.allstacks = []
|
||||
self.sn_groups = [] # snapshot groups; list of list of similar stacks
|
||||
self.snapshots = []
|
||||
self.stackdesc_list = []
|
||||
self.demo_logo = None
|
||||
self.pause_logo = None
|
||||
|
@ -160,6 +165,7 @@ class Game:
|
|||
# set some defaults
|
||||
self.sg.openstacks = filter(lambda s: s.cap.max_accept >= s.cap.min_accept, self.sg.openstacks)
|
||||
self.sg.hp_stacks = filter(lambda s: s.cap.max_move >= 2, self.sg.dropstacks)
|
||||
self.createSnGroups()
|
||||
# convert stackgroups to tuples (speed)
|
||||
self.allstacks = tuple(self.allstacks)
|
||||
self.s.foundations = tuple(self.s.foundations)
|
||||
|
@ -193,15 +199,15 @@ class Game:
|
|||
# update display properties
|
||||
self.top.wm_geometry("") # cancel user-specified geometry
|
||||
self.canvas.setInitialSize(self.width, self.height)
|
||||
if self.app.opt.save_games_geometry and \
|
||||
self.id in self.app.opt.games_geometry:
|
||||
# restore game geometry
|
||||
w, h = self.app.opt.games_geometry[self.id]
|
||||
self.canvas.config(width=w, height=h)
|
||||
self.top.update_idletasks() # apply geometry now
|
||||
if DEBUG >= 4:
|
||||
MfxCanvasRectangle(self.canvas, 0, 0, self.width, self.height,
|
||||
width=2, fill=None, outline='green')
|
||||
# restore game geometry
|
||||
if self.app.opt.save_games_geometry:
|
||||
w, h = self.app.opt.games_geometry.get(self.id, (0, 0))
|
||||
w, h = max(w, self.width), max(h, self.height)
|
||||
self.canvas.config(width=w, height=h)
|
||||
#
|
||||
self.stats.update_time = time.time()
|
||||
self.busy = old_busy
|
||||
|
@ -253,10 +259,9 @@ class Game:
|
|||
bind(self.canvas, "<2>", self.clickHandler)
|
||||
##bind(self.canvas, "<3>", self.clickHandler)
|
||||
##bind(self.canvas, "<Double-1>", self.undoHandler)
|
||||
##bind(self.canvas, "<Double-1>", self.undoHandler)
|
||||
bind(self.canvas, "<1>", self.undoHandler)
|
||||
bind(self.canvas, "<3>", self.redoHandler)
|
||||
bind(self.top, '<Unmap>', self._unmapHandler)
|
||||
bind(self.canvas, '<Unmap>', self._unmapHandler)
|
||||
|
||||
def __createCommon(self, app):
|
||||
self.busy = 1
|
||||
|
@ -324,6 +329,7 @@ class Game:
|
|||
def reset(self, restart=0):
|
||||
self.filename = ""
|
||||
self.demo = None
|
||||
self.solver = None
|
||||
self.hints = Struct(
|
||||
list = None, # list of hints for the current move
|
||||
index = -1,
|
||||
|
@ -337,6 +343,7 @@ class Game:
|
|||
talon_round = 1,
|
||||
ncards = 0,
|
||||
)
|
||||
self.snapshots = []
|
||||
# local statistics are reset on each game restart
|
||||
self.stats = Struct(
|
||||
hints = 0, # number of hints consumed
|
||||
|
@ -434,6 +441,7 @@ class Game:
|
|||
gamenumber=self.getGameNumber(format=1),
|
||||
moves=(0, 0),
|
||||
stats=self.app.stats.getStats(self.app.opt.player, self.id))
|
||||
reset_solver_dialog()
|
||||
# unhide toplevel when we use a progress bar
|
||||
if not self.preview:
|
||||
wm_map(self.top, maximized=self.app.opt.wm_maximized)
|
||||
|
@ -458,6 +466,7 @@ class Game:
|
|||
self.startMoves()
|
||||
for stack in self.allstacks:
|
||||
stack.updateText()
|
||||
self.updateSnapshots()
|
||||
self.updateText()
|
||||
self.updateStatus(moves=(0, 0))
|
||||
self.updateMenus()
|
||||
|
@ -490,6 +499,7 @@ class Game:
|
|||
self.gsaveinfo = game.gsaveinfo
|
||||
self.s.talon.round = game.loadinfo.talon_round
|
||||
self.finished = game.finished
|
||||
self.snapshots = game.snapshots
|
||||
# 3) move cards to stacks
|
||||
assert len(self.allstacks) == len(game.loadinfo.stacks)
|
||||
for i in range(len(self.allstacks)):
|
||||
|
@ -644,6 +654,49 @@ class Game:
|
|||
def leaveState(self, old_state):
|
||||
self.moves.state = old_state
|
||||
|
||||
def getSnapshot(self, hex=False):
|
||||
# generate hash (unique string) of current move
|
||||
sn = []
|
||||
for stack in self.allstacks:
|
||||
s = []
|
||||
for card in stack.cards:
|
||||
s.append('%d%03d%d' % (card.suit, card.rank, card.face_up))
|
||||
sn.append(''.join(s))
|
||||
sn = '-'.join(sn)
|
||||
# make more short string
|
||||
if hex:
|
||||
sn = md5.new(sn).hexdigest()
|
||||
else:
|
||||
sn = md5.new(sn).digest()
|
||||
return sn
|
||||
|
||||
def createSnGroups(self):
|
||||
# group stacks by class and cap
|
||||
sg = {}
|
||||
for s in self.allstacks:
|
||||
for k in sg:
|
||||
if s.__class__ is k.__class__ and \
|
||||
s.cap.__dict__ == k.cap.__dict__:
|
||||
g = sg[k]
|
||||
g.append(s.id)
|
||||
break
|
||||
else:
|
||||
# new group
|
||||
sg[s] = [s.id]
|
||||
sg = sg.values()
|
||||
self.sn_groups = sg
|
||||
##print sg
|
||||
|
||||
|
||||
def updateSnapshots(self):
|
||||
sn = self.getSnapshot()
|
||||
if sn in self.snapshots:
|
||||
##self.updateStatus(snapshot=True)
|
||||
pass
|
||||
else:
|
||||
self.snapshots.append(sn)
|
||||
##self.updateStatus(snapshot=False)
|
||||
|
||||
|
||||
#
|
||||
# card creation & shuffling
|
||||
|
@ -810,8 +863,11 @@ class Game:
|
|||
if self.demo:
|
||||
self.stopDemo()
|
||||
return
|
||||
if self.app.opt.mouse_undo and not self.event_handled:
|
||||
self.app.menubar.mUndo()
|
||||
if not self.event_handled:
|
||||
if self.drag.stack:
|
||||
self.drag.stack.cancelDrag(event)
|
||||
elif self.app.opt.mouse_undo:
|
||||
self.app.menubar.mUndo()
|
||||
self.event_handled = False
|
||||
return EVENT_PROPAGATE
|
||||
|
||||
|
@ -823,8 +879,11 @@ class Game:
|
|||
if self.demo:
|
||||
self.stopDemo()
|
||||
return
|
||||
if self.app.opt.mouse_undo and not self.event_handled:
|
||||
self.app.menubar.mRedo()
|
||||
if not self.event_handled:
|
||||
if self.drag.stack:
|
||||
self.drag.stack.cancelDrag(event)
|
||||
elif self.app.opt.mouse_undo:
|
||||
self.app.menubar.mRedo()
|
||||
self.event_handled = False
|
||||
return EVENT_PROPAGATE
|
||||
|
||||
|
@ -895,7 +954,7 @@ class Game:
|
|||
|
||||
def _unmapHandler(self, event):
|
||||
# pause game if root window has been iconified
|
||||
if event.widget is self.top and not self.pause:
|
||||
if self.app and not self.pause:
|
||||
self.app.menubar.mPause()
|
||||
|
||||
|
||||
|
@ -925,11 +984,11 @@ class Game:
|
|||
if a and not self.preview:
|
||||
self.canvas.update_idletasks()
|
||||
if self.app.audio and self.app.opt.sound and self.app.opt.sound_samples['deal']:
|
||||
if a in (1, 2, 5):
|
||||
if a in (1, 2, 3, 10):
|
||||
self.playSample("deal01", priority=100, loop=loop)
|
||||
elif a == 3:
|
||||
self.playSample("deal04", priority=100, loop=loop)
|
||||
elif a == 4:
|
||||
self.playSample("deal04", priority=100, loop=loop)
|
||||
elif a == 5:
|
||||
self.playSample("deal08", priority=100, loop=loop)
|
||||
|
||||
|
||||
|
@ -960,7 +1019,16 @@ class Game:
|
|||
bitmap="error")
|
||||
|
||||
# main animation method
|
||||
def animatedMoveTo(self, from_stack, to_stack, cards, x, y, tkraise=1, frames=-1, shadow=-1):
|
||||
def animatedMoveTo(self, from_stack, to_stack, cards, x, y,
|
||||
tkraise=1, frames=-1, shadow=-1):
|
||||
# available values of app.opt.animations:
|
||||
# 0 - without animations
|
||||
# 1 - very fast (without timer)
|
||||
# 2 - fast (default)
|
||||
# 3 - medium (2/3 of fast speed)
|
||||
# 4 - slow (1/4 of fast speed)
|
||||
# 5 - very slow (1/8 of fast speed)
|
||||
# 10 - used internally in game preview
|
||||
if self.app.opt.animations == 0 or frames == 0:
|
||||
return
|
||||
# init timer - need a high resolution for this to work
|
||||
|
@ -971,13 +1039,16 @@ class Game:
|
|||
if frames < 0:
|
||||
frames = 8
|
||||
assert frames >= 2
|
||||
if self.app.opt.animations == 3: # slow
|
||||
if self.app.opt.animations == 3: # medium
|
||||
frames = frames * 3
|
||||
SPF = SPF / 2
|
||||
elif self.app.opt.animations == 4: # slow
|
||||
frames = frames * 8
|
||||
SPF = SPF / 2
|
||||
elif self.app.opt.animations == 4: # very slow
|
||||
elif self.app.opt.animations == 5: # very slow
|
||||
frames = frames * 16
|
||||
SPF = SPF / 2
|
||||
elif self.app.opt.animations == 5:
|
||||
elif self.app.opt.animations == 10:
|
||||
# this is used internally in game preview to speed up
|
||||
# the initial dealing
|
||||
if self.moves.state == self.S_INIT and frames > 4:
|
||||
|
@ -986,7 +1057,6 @@ class Game:
|
|||
shadow = self.app.opt.shadow
|
||||
shadows = ()
|
||||
# start animation
|
||||
from_stack._unshadeStack()
|
||||
if tkraise:
|
||||
for card in cards:
|
||||
card.tkraise()
|
||||
|
@ -1030,21 +1100,19 @@ class Game:
|
|||
card.moveBy(dx, dy)
|
||||
self.canvas.update_idletasks()
|
||||
|
||||
def animatedFlip(self, stack):
|
||||
return False
|
||||
if self.app.opt.animations == 0:
|
||||
|
||||
def doAnimatedFlipAndMove(self, from_stack, to_stack=None, frames=-1):
|
||||
if self.app.opt.animations == 0 or frames == 0:
|
||||
return False
|
||||
if not from_stack.cards:
|
||||
return False
|
||||
if TOOLKIT == 'gtk':
|
||||
return False
|
||||
if not stack.cards:
|
||||
return False
|
||||
if not Image:
|
||||
return False
|
||||
if self.moves.state == self.S_INIT:
|
||||
# don't use flip animation for initial dealing
|
||||
return False
|
||||
|
||||
canvas = self.canvas
|
||||
card = stack.cards[-1]
|
||||
card = from_stack.cards[-1]
|
||||
im1 = card._active_image._pil_image
|
||||
if card.face_up:
|
||||
im2 = card._back_image._pil_image
|
||||
|
@ -1053,51 +1121,134 @@ class Game:
|
|||
w, h = im1.size
|
||||
id = card.item.id
|
||||
#
|
||||
delay = 10
|
||||
frames = 3.0 # num frames for each step
|
||||
if self.app.opt.animations == 3: # slow
|
||||
delay = 10
|
||||
SPF = 0.1/8 # animation speed - seconds per frame
|
||||
frames = 4.0 # num frames for each step
|
||||
if self.app.opt.animations == 3: # medium
|
||||
SPF = 0.1/8
|
||||
frames = 7.0
|
||||
elif self.app.opt.animations == 4: # very slow
|
||||
delay = 10
|
||||
elif self.app.opt.animations == 4: # slow
|
||||
SPF = 0.1/8
|
||||
frames = 12.0
|
||||
delta = 2*int(w/frames/2) # should be even for save position
|
||||
ddx, ddy = 0, self.app.images.SHADOW_YOFFSET/2 # ascent of the card
|
||||
# siep 1
|
||||
ww = w
|
||||
dx = delta/2
|
||||
canvas.move(id, -ddx, -ddy)
|
||||
canvas.update_idletasks()
|
||||
canvas.after(delay)
|
||||
while True:
|
||||
if ww-delta <= 0:
|
||||
break
|
||||
ww -= delta
|
||||
tmp = im1.resize((ww, h))
|
||||
elif self.app.opt.animations == 5: # very slow
|
||||
SPF = 0.1/8
|
||||
frames = 24.0
|
||||
|
||||
if to_stack is None:
|
||||
x0, y0 = from_stack.getPositionFor(card)
|
||||
x1, y1 = x0, y0
|
||||
dest_x, dest_y = 0, 0
|
||||
else:
|
||||
x0, y0 = from_stack.getPositionFor(card)
|
||||
x1, y1 = to_stack.getPositionForNextCard()
|
||||
dest_x, dest_y = x1-x0, y1-y0
|
||||
|
||||
if dest_x == 0 and dest_y == 0:
|
||||
# flip
|
||||
#ascent_dx, ascent_dy = 0, self.app.images.SHADOW_YOFFSET/frames
|
||||
ascent_dx, ascent_dy = 0, h/10.0/frames
|
||||
min_size = w/10
|
||||
shrink_dx = (w-min_size) / (frames-1)
|
||||
shrink_dy = 0
|
||||
elif dest_y == 0:
|
||||
# move to left/right waste
|
||||
#ascent_dx, ascent_dy = 0, self.app.images.SHADOW_YOFFSET/frames
|
||||
ascent_dx, ascent_dy = 0, h/10.0/frames
|
||||
min_size = w/10
|
||||
shrink_dx = (w-min_size) / (frames-1)
|
||||
shrink_dy = 0
|
||||
elif dest_x == 0:
|
||||
# move to top/bottom waste
|
||||
return False
|
||||
ascent_dx, ascent_dy = 0, 0
|
||||
## min_size = h/10
|
||||
## shrink_dx = 0
|
||||
## shrink_dy = (h-min_size) / (frames-1)
|
||||
min_size = w/10
|
||||
shrink_dx = (w-min_size) / (frames-1)
|
||||
shrink_dy = 0
|
||||
|
||||
else:
|
||||
# dest_x != 0 and dest_y != 0
|
||||
return False
|
||||
|
||||
move_dx = dest_x / frames / 2
|
||||
move_dy = dest_y / frames / 2
|
||||
xpos, ypos = float(x0), float(y0)
|
||||
|
||||
card.tkraise()
|
||||
|
||||
# step 1
|
||||
d_x = shrink_dx/2+move_dx-ascent_dx
|
||||
d_y = shrink_dy/2+move_dy-ascent_dy
|
||||
nframe = 0
|
||||
while nframe < frames:
|
||||
starttime = uclock()
|
||||
# resize img
|
||||
ww = w - nframe*shrink_dx
|
||||
hh = h - nframe*shrink_dy
|
||||
tmp = im1.resize((int(ww), int(hh)))
|
||||
tk_tmp = ImageTk.PhotoImage(image=tmp)
|
||||
canvas.itemconfig(id, image=tk_tmp)
|
||||
canvas.move(id, dx, 0)
|
||||
# move img
|
||||
xpos += d_x
|
||||
ypos += d_y
|
||||
card.moveTo(int(round(xpos)), int(round(ypos)))
|
||||
canvas.update_idletasks()
|
||||
canvas.after(delay)
|
||||
dx = -dx
|
||||
|
||||
nframe += 1
|
||||
t = (SPF-(uclock()-starttime))*1000 # milliseconds
|
||||
if t > 0:
|
||||
usleep(t/1000)
|
||||
## else:
|
||||
## nframe += 1
|
||||
## xpos += d_x
|
||||
## ypos += d_y
|
||||
|
||||
# step 2
|
||||
while True:
|
||||
tmp = im2.resize((ww, h))
|
||||
d_x = -shrink_dx/2+move_dx+ascent_dx
|
||||
d_y = -shrink_dy/2+move_dy+ascent_dy
|
||||
nframe = 0
|
||||
while nframe < frames:
|
||||
starttime = uclock()
|
||||
# resize img
|
||||
ww = w - (frames-nframe-1)*shrink_dx
|
||||
hh = h - (frames-nframe-1)*shrink_dy
|
||||
tmp = im2.resize((int(ww), int(hh)))
|
||||
tk_tmp = ImageTk.PhotoImage(image=tmp)
|
||||
canvas.itemconfig(id, image=tk_tmp)
|
||||
canvas.move(id, dx, 0)
|
||||
# move img
|
||||
xpos += d_x
|
||||
ypos += d_y
|
||||
card.moveTo(int(round(xpos)), int(round(ypos)))
|
||||
canvas.update_idletasks()
|
||||
canvas.after(delay)
|
||||
ww += delta
|
||||
if ww >= w:
|
||||
break
|
||||
canvas.move(id, ddx, ddy)
|
||||
|
||||
nframe += 1
|
||||
t = (SPF-(uclock()-starttime))*1000 # milliseconds
|
||||
if t > 0:
|
||||
usleep(t/1000)
|
||||
## else:
|
||||
## nframe += 1
|
||||
## xpos += d_x
|
||||
## ypos += d_y
|
||||
|
||||
card.moveTo(x1, y1)
|
||||
#canvas.update_idletasks()
|
||||
return True
|
||||
|
||||
def doWinAnimation(self):
|
||||
def animatedFlip(self, stack):
|
||||
##return False
|
||||
return self.doAnimatedFlipAndMove(stack)
|
||||
|
||||
def animatedFlipAndMove(self, from_stack, to_stack, frames=-1):
|
||||
##return False
|
||||
return self.doAnimatedFlipAndMove(from_stack, to_stack, frames)
|
||||
|
||||
|
||||
def winAnimationEvent(self):
|
||||
# based on code from pygtk-demo
|
||||
FRAME_DELAY = 40
|
||||
FRAME_DELAY = 80
|
||||
CYCLE_LEN = 60
|
||||
starttime = uclock()
|
||||
images = self.win_animation.images
|
||||
saved_images = self.win_animation.saved_images # cached images
|
||||
canvas = self.canvas
|
||||
|
@ -1107,6 +1258,10 @@ class Game:
|
|||
x0 = int(int(canvas.cget('width'))*(canvas.xview()[0]))
|
||||
y0 = int(int(canvas.cget('height'))*(canvas.yview()[0]))
|
||||
width, height = self.win_animation.width, self.win_animation.height
|
||||
cw = self.canvas.winfo_width()
|
||||
ch = self.canvas.winfo_height()
|
||||
x0 -= (width-cw)/2
|
||||
y0 -= (height-ch)/2
|
||||
|
||||
tmp_tk_images = []
|
||||
raised_images = []
|
||||
|
@ -1143,7 +1298,7 @@ class Game:
|
|||
if round_k == 100:
|
||||
tmp = im
|
||||
else:
|
||||
tmp = im.resize(new_size, resample=Image.BICUBIC)
|
||||
tmp = im.resize(new_size, resample=Image.BILINEAR)
|
||||
tk_tmp = ImageTk.PhotoImage(image=tmp)
|
||||
saved_images[img_index][round_k] = tk_tmp
|
||||
|
||||
|
@ -1161,7 +1316,11 @@ class Game:
|
|||
self.win_animation.tk_images = tmp_tk_images
|
||||
canvas.update_idletasks()
|
||||
# loop
|
||||
self.win_animation.timer = after(canvas, FRAME_DELAY, self.doWinAnimation)
|
||||
t = FRAME_DELAY-int((uclock()-starttime)*1000)
|
||||
if t > 0:
|
||||
self.win_animation.timer = after(canvas, t, self.winAnimationEvent)
|
||||
else:
|
||||
self.win_animation.timer = after_idle(canvas, self.winAnimationEvent)
|
||||
|
||||
def stopWinAnimation(self):
|
||||
if self.win_animation.timer:
|
||||
|
@ -1178,8 +1337,8 @@ class Game:
|
|||
def winAnimation(self, perfect=0):
|
||||
if self.preview:
|
||||
return
|
||||
if not self.app.opt.animations:
|
||||
return
|
||||
#if not self.app.opt.animations:
|
||||
# return
|
||||
if not self.app.opt.win_animation:
|
||||
return
|
||||
if TOOLKIT == 'gtk':
|
||||
|
@ -1190,7 +1349,8 @@ class Game:
|
|||
# select some random cards
|
||||
cards = self.cards[:]
|
||||
scards = []
|
||||
for i in xrange(10):
|
||||
ncards = min(10, len(cards))
|
||||
for i in xrange(ncards):
|
||||
c = self.app.miscrandom.choice(cards)
|
||||
scards.append(c)
|
||||
cards.remove(c)
|
||||
|
@ -1200,7 +1360,8 @@ class Game:
|
|||
self.win_animation.width = self.canvas.winfo_width()
|
||||
self.win_animation.height = self.canvas.winfo_height()
|
||||
# run win animation in background
|
||||
after_idle(self.canvas, self.doWinAnimation)
|
||||
##after_idle(self.canvas, self.winAnimationEvent)
|
||||
after(self.canvas, 200, self.winAnimationEvent)
|
||||
return
|
||||
|
||||
def redealAnimation(self):
|
||||
|
@ -1208,14 +1369,6 @@ class Game:
|
|||
return
|
||||
if not self.app.opt.animations or not self.app.opt.redeal_animation:
|
||||
return
|
||||
self.setCursor(cursor=CURSOR_WATCH)
|
||||
self.top.busyUpdate()
|
||||
self.canvas.update_idletasks()
|
||||
old_a = self.app.opt.animations
|
||||
if old_a == 0:
|
||||
self.app.opt.animations = 1 # timer based
|
||||
elif old_a == 4: # very slow
|
||||
self.app.opt.animations = 3 # slow
|
||||
cards = []
|
||||
for s in self.allstacks:
|
||||
if s is not self.s.talon:
|
||||
|
@ -1223,6 +1376,16 @@ class Game:
|
|||
cards.append((c,s))
|
||||
if not cards:
|
||||
return
|
||||
self.setCursor(cursor=CURSOR_WATCH)
|
||||
self.top.busyUpdate()
|
||||
self.canvas.update_idletasks()
|
||||
old_a = self.app.opt.animations
|
||||
if old_a == 0:
|
||||
self.app.opt.animations = 1 # very fast
|
||||
elif old_a == 3: # medium
|
||||
self.app.opt.animations = 2 # fast
|
||||
elif old_a == 4: # very slow
|
||||
self.app.opt.animations = 3 # slow
|
||||
# select some random cards
|
||||
acards = []
|
||||
scards = cards[:]
|
||||
|
@ -1381,6 +1544,7 @@ class Game:
|
|||
|
||||
# the actual hint class (or None)
|
||||
Hint_Class = DefaultHint
|
||||
Solver_Class = None
|
||||
|
||||
def getHintClass(self):
|
||||
return self.Hint_Class
|
||||
|
@ -1625,7 +1789,8 @@ for %d moves.
|
|||
if s.canFlipCard():
|
||||
if sound:
|
||||
self.playSample("autoflip", priority=5)
|
||||
s.flipMove()
|
||||
#~s.flipMove()
|
||||
s.flipMove(animation=True)
|
||||
done_something = 1
|
||||
# each single flip is undo-able unless opt.autofaceup
|
||||
self.finishMove()
|
||||
|
@ -1661,6 +1826,13 @@ for %d moves.
|
|||
return self.dealCards(sound=sound)
|
||||
return 0
|
||||
|
||||
def autoDrop(self, autofaceup=-1):
|
||||
old_a = self.app.opt.animations
|
||||
if old_a == 3: # medium
|
||||
self.app.opt.animations = 2 # fast
|
||||
self.autoPlay(autofaceup=autofaceup, autodrop=1)
|
||||
self.app.opt.animations = old_a
|
||||
|
||||
## for find_card_dialog
|
||||
def highlightCard(self, suit, rank):
|
||||
if not self.app:
|
||||
|
@ -1681,11 +1853,12 @@ for %d moves.
|
|||
return ((self.sg.hp_stacks, 2),)
|
||||
return ()
|
||||
|
||||
def _highlightCards(self, info, sleep=1.5):
|
||||
def _highlightCards(self, info, sleep=1.5, delta=(1,1,1,1)):
|
||||
if not info:
|
||||
return 0
|
||||
if self.pause:
|
||||
return 0
|
||||
self.stopWinAnimation()
|
||||
items = []
|
||||
for s, c1, c2, color in info:
|
||||
assert c1 in s.cards and c2 in s.cards
|
||||
|
@ -1741,13 +1914,15 @@ for %d moves.
|
|||
y2 = y2 + self.app.images.CARDH
|
||||
tkraise = True
|
||||
##print c1, c2, x1, y1, x2, y2
|
||||
x1, x2 = x1-delta[0], x2+delta[1]
|
||||
y1, y2 = y1-delta[2], y2+delta[3]
|
||||
if TOOLKIT == 'tk':
|
||||
r = MfxCanvasRectangle(self.canvas, x1-1, y1-1, x2+1, y2+1,
|
||||
r = MfxCanvasRectangle(self.canvas, x1, y1, x2, y2,
|
||||
width=4, fill=None, outline=color)
|
||||
if tkraise:
|
||||
r.tkraise(c2.item)
|
||||
elif TOOLKIT == 'gtk':
|
||||
r = MfxCanvasRectangle(self.canvas, x1-1, y1-1, x2+1, y2+1,
|
||||
r = MfxCanvasRectangle(self.canvas, x1, y1, x2, y2,
|
||||
width=4, fill=None, outline=color,
|
||||
group=s.group)
|
||||
if tkraise:
|
||||
|
@ -1801,7 +1976,7 @@ for %d moves.
|
|||
for s in si[0]:
|
||||
pile = s.getPile()
|
||||
if pile and len(pile) >= si[1]:
|
||||
hi.append((s, pile[0], pile[-1], col[1]))
|
||||
hi.append((s, pile[0], pile[-1], col))
|
||||
if not hi:
|
||||
self.highlightNotMatching()
|
||||
return 0
|
||||
|
@ -1855,7 +2030,7 @@ for %d moves.
|
|||
# for spider-type stacks
|
||||
if to_stack.cards:
|
||||
# check suit
|
||||
return int(from_stack.cards[-1].suit == to_stack.cards[-1].suit)+1
|
||||
return int(from_stack.cards[-ncards].suit == to_stack.cards[-1].suit)+1
|
||||
return 0
|
||||
|
||||
#
|
||||
|
@ -1896,6 +2071,10 @@ for %d moves.
|
|||
# compute all hints for the current position
|
||||
# this is the only method that actually uses class Hint
|
||||
def getHints(self, level, taken_hint=None):
|
||||
if level == 3:
|
||||
#if self.solver is None:
|
||||
# return None
|
||||
return self.solver.getHints(taken_hint)
|
||||
hint_class = self.getHintClass()
|
||||
if hint_class is None:
|
||||
return None
|
||||
|
@ -1941,10 +2120,17 @@ for %d moves.
|
|||
# a move move
|
||||
assert to_stack
|
||||
assert 1 <= ncards <= len(from_stack.cards)
|
||||
if DEBUG:
|
||||
if not to_stack.acceptsCards(
|
||||
from_stack, from_stack.cards[-ncards:]):
|
||||
print '*fail accepts cards*', from_stack, to_stack, ncards
|
||||
if not from_stack.canMoveCards(from_stack.cards[-ncards:]):
|
||||
print '*fail move cards*', from_stack, ncards
|
||||
##assert from_stack.canMoveCards(from_stack.cards[-ncards:]) # FIXME: Pyramid
|
||||
assert to_stack.acceptsCards(from_stack, from_stack.cards[-ncards:])
|
||||
if sleep <= 0.0:
|
||||
return h
|
||||
info = (level == 1) or (level > 1 and DEBUG >= 3)
|
||||
info = (level == 1) or (level > 1 and DEBUG)
|
||||
if info and self.app.statusbar and self.app.opt.statusbar:
|
||||
self.app.statusbar.configLabel("info", text=_("Score %6d") % (score), fg=text_color)
|
||||
else:
|
||||
|
@ -2000,6 +2186,7 @@ for %d moves.
|
|||
mixed = mixed,
|
||||
sleep = self.app.opt.timeouts['demo'],
|
||||
last_deal = [],
|
||||
snapshots = [],
|
||||
hint = None,
|
||||
keypress = None,
|
||||
start_demo_moves = self.stats.demo_moves,
|
||||
|
@ -2036,6 +2223,8 @@ for %d moves.
|
|||
timeout = 10000
|
||||
if 1 and player_moves == 0:
|
||||
timeout = 5000
|
||||
if self.demo and self.demo.level == 3:
|
||||
timeout = 0
|
||||
if self.isGameWon():
|
||||
self.updateTime()
|
||||
finished = 1
|
||||
|
@ -2044,7 +2233,7 @@ for %d moves.
|
|||
if not self.top.winfo_ismapped():
|
||||
status = 2
|
||||
elif player_moves == 0:
|
||||
self.playSample("autopilotwon")
|
||||
self.playSample("autopilotwon", priority=1000)
|
||||
s = self.app.miscrandom.choice((_("&Great"), _("&Cool"), _("&Yeah"), _("&Wow"))) # ??? accelerators
|
||||
d = MfxMessageDialog(self.top, title=PACKAGE+_(" Autopilot"),
|
||||
text=_("\nGame solved in %d moves.\n") % self.moves.index,
|
||||
|
@ -2067,7 +2256,7 @@ for %d moves.
|
|||
status = 2
|
||||
else:
|
||||
if player_moves == 0:
|
||||
self.playSample("autopilotlost")
|
||||
self.playSample("autopilotlost", priority=1000)
|
||||
s = self.app.miscrandom.choice((_("&Oh well"), _("&That's life"), _("&Hmm"))) # ??? accelerators
|
||||
d = MfxMessageDialog(self.top, title=PACKAGE+_(" Autopilot"),
|
||||
text=_("\nThis won't come out...\n"),
|
||||
|
@ -2076,7 +2265,7 @@ for %d moves.
|
|||
status = d.status
|
||||
if finished:
|
||||
self.updateStats(demo=1)
|
||||
if self.demo and status == 2:
|
||||
if not DEBUG and self.demo and status == 2:
|
||||
# timeout in dialog
|
||||
if self.stats.demo_moves > self.demo.start_demo_moves:
|
||||
# we only increase the splash-screen counter if the last
|
||||
|
@ -2143,29 +2332,38 @@ for %d moves.
|
|||
score, pos, ncards, from_stack, to_stack, text_color, forced_move = h
|
||||
if ncards == 0:
|
||||
# a deal-move
|
||||
# do not let games like Klondike and Canfield deal forever
|
||||
if self.dealCards() == 0:
|
||||
return 1
|
||||
# do not let games like Klondike and Canfield deal forever
|
||||
c = self.s.talon.getCard()
|
||||
if c in demo.last_deal:
|
||||
# We went through the whole Talon. Give up.
|
||||
return 1
|
||||
# Note that `None' is a valid entry in last_deal[]
|
||||
# (this means that all cards are on the Waste).
|
||||
demo.last_deal.append(c)
|
||||
if 0: # old version, based on dealing card
|
||||
c = self.s.talon.getCard()
|
||||
if c in demo.last_deal:
|
||||
# We went through the whole Talon. Give up.
|
||||
return 1
|
||||
# Note that `None' is a valid entry in last_deal[]
|
||||
# (this means that all cards are on the Waste).
|
||||
demo.last_deal.append(c)
|
||||
else: # new version, based on snapshots
|
||||
# check snapshot
|
||||
sn = self.getSnapshot()
|
||||
if sn in demo.snapshots:
|
||||
# not unique
|
||||
return 1
|
||||
demo.snapshots.append(sn)
|
||||
elif from_stack == to_stack:
|
||||
# a flip-move
|
||||
from_stack.flipMove()
|
||||
from_stack.flipMove(animation=True)
|
||||
demo.last_deal = []
|
||||
else:
|
||||
# a move-move
|
||||
from_stack.moveMove(ncards, to_stack, frames=-1)
|
||||
demo.last_deal = []
|
||||
##print self.moves.index
|
||||
return 0
|
||||
|
||||
def createDemoInfoText(self):
|
||||
## TODO - the text placement is not fully ok
|
||||
if DEBUG:
|
||||
self.showHelp('help', self.getDemoInfoText())
|
||||
return
|
||||
if not self.demo or self.demo.info_text or self.preview:
|
||||
return
|
||||
|
@ -2177,13 +2375,15 @@ for %d moves.
|
|||
]
|
||||
ta = self.getDemoInfoTextAttr(tinfo)
|
||||
if ta:
|
||||
font = self.app.getFont("canvas_large")
|
||||
#font = self.app.getFont("canvas_large")
|
||||
font = self.app.getFont("default")
|
||||
self.demo.info_text = MfxCanvasText(self.canvas, ta[1], ta[2],
|
||||
anchor=ta[0], font=font,
|
||||
text=self.getDemoInfoText())
|
||||
|
||||
def getDemoInfoText(self):
|
||||
return self.gameinfo.short_name
|
||||
h = self.Hint_Class is None and 'None' or self.Hint_Class.__name__
|
||||
return '%s (%s)' % (self.gameinfo.short_name, h)
|
||||
|
||||
def getDemoInfoTextAttr(self, tinfo):
|
||||
items1, items2 = [], []
|
||||
|
@ -2291,6 +2491,21 @@ for %d moves.
|
|||
am.do(self)
|
||||
self.hints.list = None
|
||||
|
||||
def singleFlipMove(self, stack):
|
||||
# flip with animation (without "moveMove" in this move)
|
||||
assert stack
|
||||
am = ASingleFlipMove(stack)
|
||||
self.__storeMove(am)
|
||||
am.do(self)
|
||||
self.hints.list = None
|
||||
|
||||
def flipAndMoveMove(self, from_stack, to_stack, frames=-1):
|
||||
assert from_stack and to_stack and (from_stack is not to_stack)
|
||||
am = AFlipAndMoveMove(from_stack, to_stack, frames)
|
||||
self.__storeMove(am)
|
||||
am.do(self)
|
||||
self.hints.list = None
|
||||
|
||||
# move type 3
|
||||
def turnStackMove(self, from_stack, to_stack, update_flags=1):
|
||||
assert from_stack and to_stack and (from_stack is not to_stack)
|
||||
|
@ -2398,11 +2613,14 @@ for %d moves.
|
|||
assert moves.index == len(moves.history)
|
||||
|
||||
moves.current = []
|
||||
self.updateSnapshots()
|
||||
# update view
|
||||
self.updateText()
|
||||
self.updateStatus(moves=(moves.index, self.stats.total_moves))
|
||||
self.updateMenus()
|
||||
self.updatePlayTime(do_after=0)
|
||||
reset_solver_dialog()
|
||||
|
||||
|
||||
return 1
|
||||
|
||||
|
@ -2428,6 +2646,7 @@ for %d moves.
|
|||
self.stats.undo_moves += 1
|
||||
self.stats.total_moves += 1
|
||||
self.hints.list = None
|
||||
self.updateSnapshots()
|
||||
self.updateText()
|
||||
self.updateStatus(moves=(self.moves.index, self.stats.total_moves))
|
||||
self.updateMenus()
|
||||
|
@ -2451,6 +2670,7 @@ for %d moves.
|
|||
self.stats.redo_moves += 1
|
||||
self.stats.total_moves += 1
|
||||
self.hints.list = None
|
||||
self.updateSnapshots()
|
||||
self.updateText()
|
||||
self.updateStatus(moves=(self.moves.index, self.stats.total_moves))
|
||||
self.updateMenus()
|
||||
|
@ -2707,6 +2927,9 @@ in the current implementation.''' % version
|
|||
moves = pload()
|
||||
assert isinstance(moves, Struct), err_txt
|
||||
game.moves.__dict__.update(moves.__dict__)
|
||||
snapshots = p.load()
|
||||
assert isinstance(snapshots, list), err_txt
|
||||
game.snapshots = snapshots
|
||||
if 0 <= bookmark <= 1:
|
||||
gstats = pload()
|
||||
assert isinstance(gstats, Struct), err_txt
|
||||
|
@ -2763,6 +2986,7 @@ in the current implementation.''' % version
|
|||
p.dump(self.saveinfo)
|
||||
p.dump(self.gsaveinfo)
|
||||
p.dump(self.moves)
|
||||
p.dump(self.snapshots)
|
||||
if 0 <= bookmark <= 1:
|
||||
if bookmark == 0:
|
||||
self.gstats.saved = self.gstats.saved + 1
|
||||
|
|
|
@ -339,6 +339,7 @@ class GI:
|
|||
('fc-0.9.2', tuple(range(441, 466))),
|
||||
('fc-0.9.3', tuple(range(466, 661))),
|
||||
('fc-0.9.4', tuple(range(661, 671))),
|
||||
('fc-1.0', tuple(range(671, 711))),
|
||||
)
|
||||
|
||||
# deprecated - the correct way is to or a GI.GT_XXX flag
|
||||
|
@ -346,8 +347,6 @@ class GI:
|
|||
_CHILDREN_GAMES = [16, 33, 55, 90, 91, 96, 97, 176, 903,]
|
||||
|
||||
_OPEN_GAMES = []
|
||||
#_OPEN_GAMES = [ 5, 6, 8, 9, 26, 31, 45, 46, 50, 53, 63, 64, 77, 85, 86,
|
||||
# 96, 116, 117, 118, 258, ]
|
||||
|
||||
_POPULAR_GAMES = [
|
||||
1, # Gypsy
|
||||
|
@ -471,6 +470,7 @@ class GameManager:
|
|||
self.__games_by_altname = None
|
||||
self.__all_games = {} # includes hidden games
|
||||
self.__all_gamenames = {} # includes hidden games
|
||||
self.__games_for_solver = []
|
||||
self.loading_plugin = 0
|
||||
self.registered_game_types = {}
|
||||
|
||||
|
@ -536,7 +536,9 @@ class GameManager:
|
|||
## if gi.id in k: break
|
||||
## else:
|
||||
## print gi.id
|
||||
|
||||
if hasattr(gi.gameclass, 'Solver_Class') and \
|
||||
gi.gameclass.Solver_Class is not None:
|
||||
self.__games_for_solver.append(gi.id)
|
||||
|
||||
#
|
||||
# access games database - we do not expose hidden games
|
||||
|
@ -591,6 +593,9 @@ class GameManager:
|
|||
return gi.id
|
||||
return None
|
||||
|
||||
def getGamesForSolver(self):
|
||||
return self.__games_for_solver
|
||||
|
||||
|
||||
# /***********************************************************************
|
||||
# //
|
||||
|
|
|
@ -42,6 +42,8 @@ from pysollib.layout import Layout
|
|||
from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint
|
||||
from pysollib.pysoltk import MfxCanvasText
|
||||
|
||||
from numerica import Numerica_Hint
|
||||
|
||||
|
||||
# /***********************************************************************
|
||||
# // Tam O'Shanter
|
||||
|
@ -159,6 +161,8 @@ class Strategy_RowStack(BasicRowStack):
|
|||
|
||||
|
||||
class Strategy(Game):
|
||||
Hint_Class = Numerica_Hint
|
||||
|
||||
def createGame(self, rows=8):
|
||||
# create layout
|
||||
l, s = Layout(self), self.s
|
||||
|
@ -181,6 +185,7 @@ class Strategy(Game):
|
|||
|
||||
# define stack-groups
|
||||
l.defaultStackGroups()
|
||||
self.sg.dropstacks.append(s.talon)
|
||||
|
||||
#
|
||||
# game overrides
|
||||
|
@ -209,14 +214,16 @@ class StrategyPlus(Strategy):
|
|||
|
||||
def fillStack(self, stack):
|
||||
if stack is self.s.talon and stack.cards:
|
||||
old_state = self.enterState(self.S_FILL)
|
||||
c = stack.cards[-1]
|
||||
while c.rank == ACE:
|
||||
old_state = self.enterState(self.S_FILL)
|
||||
self.moveMove(1, stack, self.s.foundations[c.suit])
|
||||
if stack.canFlipCard():
|
||||
stack.flipMove()
|
||||
self.leaveState(old_state)
|
||||
if not stack.cards:
|
||||
break
|
||||
c = stack.cards[-1]
|
||||
self.leaveState(old_state)
|
||||
|
||||
|
||||
# /***********************************************************************
|
||||
|
@ -480,6 +487,8 @@ class Amazons_Foundation(AbstractFoundationStack):
|
|||
rank = 5
|
||||
if (rank + self.cap.dir) % self.cap.mod != cards[0].rank:
|
||||
return False
|
||||
if cards[0].rank == QUEEN:
|
||||
return True
|
||||
i = list(self.game.s.foundations).index(self)
|
||||
j = list(self.game.s.rows).index(from_stack)
|
||||
return i == j
|
||||
|
|
|
@ -42,6 +42,8 @@ from pysollib.stack import *
|
|||
from pysollib.game import Game
|
||||
from pysollib.layout import Layout
|
||||
from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint
|
||||
from pysollib.hint import FreeCellSolverWrapper
|
||||
|
||||
|
||||
# /***********************************************************************
|
||||
# // Castles in Spain
|
||||
|
@ -51,8 +53,9 @@ class CastlesInSpain(Game):
|
|||
Layout_Method = Layout.bakersDozenLayout
|
||||
Talon_Class = InitialDealTalonStack
|
||||
Foundation_Class = SS_FoundationStack
|
||||
RowStack_Class = AC_RowStack
|
||||
RowStack_Class = SuperMoveAC_RowStack
|
||||
Hint_Class = CautiousDefaultHint
|
||||
Solver_Class = FreeCellSolverWrapper()
|
||||
|
||||
#
|
||||
# game layout
|
||||
|
@ -69,8 +72,7 @@ class CastlesInSpain(Game):
|
|||
for r in l.s.foundations:
|
||||
s.foundations.append(self.Foundation_Class(r.x, r.y, self, suit=r.suit))
|
||||
for r in l.s.rows:
|
||||
s.rows.append(self.RowStack_Class(r.x, r.y, self,
|
||||
max_move=1, max_accept=1))
|
||||
s.rows.append(self.RowStack_Class(r.x, r.y, self))
|
||||
# default
|
||||
l.defaultAll()
|
||||
|
||||
|
@ -96,7 +98,8 @@ class Martha_RowStack(AC_RowStack):
|
|||
|
||||
|
||||
class Martha(CastlesInSpain):
|
||||
RowStack_Class = FullStackWrapper(Martha_RowStack)
|
||||
Solver_Class = None
|
||||
RowStack_Class = Martha_RowStack
|
||||
|
||||
def createGame(self):
|
||||
CastlesInSpain.createGame(self, rows=12, playcards=13)
|
||||
|
@ -115,7 +118,9 @@ class Martha(CastlesInSpain):
|
|||
# ************************************************************************/
|
||||
|
||||
class BakersDozen(CastlesInSpain):
|
||||
RowStack_Class = StackWrapper(RK_RowStack, base_rank=NO_RANK)
|
||||
RowStack_Class = StackWrapper(RK_RowStack, max_move=1, max_accept=1,
|
||||
base_rank=NO_RANK)
|
||||
Solver_Class = FreeCellSolverWrapper(preset='bakers_dozen')
|
||||
|
||||
def _shuffleHook(self, cards):
|
||||
# move Kings to bottom of each stack
|
||||
|
@ -148,16 +153,19 @@ class BakersDozen(CastlesInSpain):
|
|||
|
||||
class SpanishPatience(BakersDozen):
|
||||
Foundation_Class = AC_FoundationStack
|
||||
Solver_Class = None
|
||||
|
||||
|
||||
class PortugueseSolitaire(BakersDozen):
|
||||
RowStack_Class = StackWrapper(RK_RowStack, base_rank=KING)
|
||||
Solver_Class = FreeCellSolverWrapper(sbb='rank', esf='kings')
|
||||
def _shuffleHook(self, cards):
|
||||
return cards
|
||||
|
||||
|
||||
class SpanishPatienceII(PortugueseSolitaire):
|
||||
RowStack_Class = RK_RowStack
|
||||
Solver_Class = FreeCellSolverWrapper(sbb='rank', sm='unlimited')
|
||||
|
||||
|
||||
# /***********************************************************************
|
||||
|
@ -165,6 +173,8 @@ class SpanishPatienceII(PortugueseSolitaire):
|
|||
# ************************************************************************/
|
||||
|
||||
class GoodMeasure(BakersDozen):
|
||||
Solver_Class = FreeCellSolverWrapper(preset='good_measure')
|
||||
|
||||
def createGame(self):
|
||||
CastlesInSpain.createGame(self, rows=10)
|
||||
|
||||
|
@ -189,8 +199,8 @@ class GoodMeasure(BakersDozen):
|
|||
class Cruel_Talon(TalonStack):
|
||||
def canDealCards(self):
|
||||
## FIXME: this is to avoid loops in the demo
|
||||
if self.game.demo and self.game.moves.index >= 100:
|
||||
return False
|
||||
#if self.game.demo and self.game.moves.index >= 100:
|
||||
# return False
|
||||
if self.round == self.max_rounds:
|
||||
return False
|
||||
return not self.game.isGameWon()
|
||||
|
@ -238,6 +248,8 @@ class Cruel_Talon(TalonStack):
|
|||
class Cruel(CastlesInSpain):
|
||||
Talon_Class = StackWrapper(Cruel_Talon, max_rounds=-1)
|
||||
RowStack_Class = StackWrapper(SS_RowStack, base_rank=NO_RANK)
|
||||
##Solver_Class = FreeCellSolverWrapper(preset='cruel')
|
||||
Solver_Class = None
|
||||
|
||||
def createGame(self):
|
||||
CastlesInSpain.createGame(self, rows=12)
|
||||
|
@ -289,6 +301,7 @@ class Indefatigable(Cruel):
|
|||
class Perseverance(Cruel, BakersDozen):
|
||||
Talon_Class = StackWrapper(Cruel_Talon, max_rounds=3)
|
||||
RowStack_Class = StackWrapper(SS_RowStack, base_rank=NO_RANK, dir=-1, max_move=UNLIMITED_MOVES, max_accept=UNLIMITED_ACCEPTS)
|
||||
Solver_Class = None
|
||||
|
||||
def _shuffleHook(self, cards):
|
||||
# move Kings to bottom of each stack (???)
|
||||
|
@ -306,6 +319,7 @@ class Perseverance(Cruel, BakersDozen):
|
|||
# ************************************************************************/
|
||||
|
||||
class RippleFan(CastlesInSpain):
|
||||
Solver_Class = None
|
||||
|
||||
def createGame(self):
|
||||
# create layout
|
||||
|
|
|
@ -49,36 +49,12 @@ from pysollib.hint import FreeCellType_Hint, FreeCellSolverWrapper
|
|||
# // Baker's Game
|
||||
# ************************************************************************/
|
||||
|
||||
# To simplify playing we also consider the number of free rows.
|
||||
# Note that this only is legal if the game.s.rows have a
|
||||
# cap.base_rank == ANY_RANK.
|
||||
# See also the "SuperMove" section in the FreeCell FAQ.
|
||||
class BakersGame_RowStack(SS_RowStack):
|
||||
def _getMaxMove(self, to_stack_ncards):
|
||||
max_move = getNumberOfFreeStacks(self.game.s.reserves) + 1
|
||||
n = getNumberOfFreeStacks(self.game.s.rows)
|
||||
if to_stack_ncards == 0:
|
||||
n = n - 1
|
||||
while n > 0 and max_move < 1000:
|
||||
max_move = max_move * 2
|
||||
n = n - 1
|
||||
return max_move
|
||||
|
||||
def canMoveCards(self, cards):
|
||||
max_move = self._getMaxMove(1)
|
||||
return len(cards) <= max_move and SS_RowStack.canMoveCards(self, cards)
|
||||
|
||||
def acceptsCards(self, from_stack, cards):
|
||||
max_move = self._getMaxMove(len(self.cards))
|
||||
return len(cards) <= max_move and SS_RowStack.acceptsCards(self, from_stack, cards)
|
||||
|
||||
|
||||
class BakersGame(Game):
|
||||
Layout_Method = Layout.freeCellLayout
|
||||
Foundation_Class = SS_FoundationStack
|
||||
RowStack_Class = BakersGame_RowStack
|
||||
##Hint_Class = FreeCellType_Hint
|
||||
Hint_Class = FreeCellSolverWrapper(FreeCellType_Hint, {'sbb' : "suit" })
|
||||
RowStack_Class = SuperMoveSS_RowStack
|
||||
Hint_Class = FreeCellType_Hint
|
||||
Solver_Class = FreeCellSolverWrapper(sbb='suit')
|
||||
|
||||
#
|
||||
# game layout
|
||||
|
@ -123,7 +99,7 @@ class BakersGame(Game):
|
|||
|
||||
class KingOnlyBakersGame(BakersGame):
|
||||
RowStack_Class = StackWrapper(FreeCell_SS_RowStack, base_rank=KING)
|
||||
Hint_Class = FreeCellSolverWrapper(FreeCellType_Hint, {'sbb' : "suit", 'esf' : "kings" })
|
||||
Solver_Class = FreeCellSolverWrapper(sbb='suit', esf='kings')
|
||||
|
||||
|
||||
# /***********************************************************************
|
||||
|
@ -235,19 +211,22 @@ class SeahavenTowers(KingOnlyBakersGame):
|
|||
|
||||
class RelaxedSeahavenTowers(SeahavenTowers):
|
||||
RowStack_Class = KingSS_RowStack
|
||||
Hint_Class = FreeCellSolverWrapper(FreeCellType_Hint, {'sbb' : "suit", 'esf' : "kings", 'sm' : "unlimited",})
|
||||
Solver_Class = FreeCellSolverWrapper(sbb='suit', esf='kings', sm='unlimited')
|
||||
|
||||
|
||||
# /***********************************************************************
|
||||
# // Tuxedo
|
||||
# // Penguin
|
||||
# // Opus
|
||||
# // Tuxedo
|
||||
# ************************************************************************/
|
||||
|
||||
class Tuxedo(Game):
|
||||
|
||||
RowStack_Class = SS_RowStack
|
||||
RowStack_Class = StackWrapper(SS_RowStack, mod=13)
|
||||
ReserveStack_Class = ReserveStack
|
||||
Foundation_Class = StackWrapper(SS_FoundationStack, mod=13, max_move=0)
|
||||
Hint_Class = FreeCellType_Hint
|
||||
Solver_Class = FreeCellSolverWrapper(sbb='suit', sm='unlimited')
|
||||
|
||||
def createGame(self, rows=7, reserves=7):
|
||||
# create layout
|
||||
|
@ -265,16 +244,16 @@ class Tuxedo(Game):
|
|||
# create stacks
|
||||
x, y = self.width - l.XS, l.YM
|
||||
for i in range(4):
|
||||
s.foundations.append(SS_FoundationStack(x, y, self, i, mod=13, max_move=0))
|
||||
s.foundations.append(self.Foundation_Class(x, y, self, suit=i))
|
||||
y = y + l.YS
|
||||
self.setRegion(s.foundations, (x - l.CW/2, -999, 999999, 999999))
|
||||
x, y = l.XM + (maxrows-rows)*l.XS/2, l.YM
|
||||
for i in range(rows):
|
||||
s.rows.append(self.RowStack_Class(x, y, self, mod=13))
|
||||
s.rows.append(self.RowStack_Class(x, y, self))
|
||||
x = x + l.XS
|
||||
x, y = l.XM + (maxrows-reserves)*l.XS/2, self.height - l.YS
|
||||
for i in range(reserves):
|
||||
s.reserves.append(ReserveStack(x, y, self))
|
||||
s.reserves.append(self.ReserveStack_Class(x, y, self))
|
||||
x = x + l.XS
|
||||
self.setRegion(s.reserves, (-999, y - l.CH / 2, 999999, 999999))
|
||||
s.talon = InitialDealTalonStack(l.XM+1, y, self)
|
||||
|
@ -294,6 +273,7 @@ class Tuxedo(Game):
|
|||
|
||||
class Penguin(Tuxedo):
|
||||
GAME_VERSION = 2
|
||||
Solver_Class = FreeCellSolverWrapper(sbb='suit', esf='kings', sm='unlimited')
|
||||
|
||||
def _shuffleHook(self, cards):
|
||||
# move base cards to top of the Talon (i.e. first cards to be dealt)
|
||||
|
|
|
@ -42,6 +42,7 @@ from pysollib.stack import *
|
|||
from pysollib.game import Game
|
||||
from pysollib.layout import Layout
|
||||
from pysollib.hint import CautiousDefaultHint, FreeCellType_Hint
|
||||
from pysollib.hint import FreeCellSolverWrapper
|
||||
from pysollib.pysoltk import MfxCanvasText
|
||||
|
||||
|
||||
|
@ -60,9 +61,11 @@ class BeleagueredCastleType_Hint(CautiousDefaultHint):
|
|||
|
||||
class StreetsAndAlleys(Game):
|
||||
Hint_Class = BeleagueredCastleType_Hint
|
||||
Solver_Class = FreeCellSolverWrapper(preset='streets_and_alleys')
|
||||
|
||||
Foundation_Class = SS_FoundationStack
|
||||
RowStack_Class = RK_RowStack
|
||||
##RowStack_Class = RK_RowStack
|
||||
RowStack_Class = SuperMoveRK_RowStack
|
||||
|
||||
#
|
||||
# game layout
|
||||
|
@ -102,7 +105,7 @@ class StreetsAndAlleys(Game):
|
|||
for x in (x0, x2):
|
||||
y = l.YM+l.YS*int(reserves!=0)
|
||||
for i in range(4):
|
||||
stack = self.RowStack_Class(x, y, self, max_move=1, max_accept=1)
|
||||
stack = self.RowStack_Class(x, y, self)
|
||||
stack.CARD_XOFFSET, stack.CARD_YOFFSET = l.XOFFSET, 0
|
||||
s.rows.append(stack)
|
||||
y = y + l.YS
|
||||
|
@ -178,6 +181,8 @@ class Citadel(StreetsAndAlleys):
|
|||
|
||||
|
||||
class ExiledKings(Citadel):
|
||||
Hint_Class = BeleagueredCastleType_Hint
|
||||
Solver_Class = FreeCellSolverWrapper(sbb='rank', esf='kings')
|
||||
RowStack_Class = StackWrapper(RK_RowStack, base_rank=KING)
|
||||
|
||||
|
||||
|
@ -432,11 +437,13 @@ class Chessboard(Fortress):
|
|||
|
||||
class Stronghold(StreetsAndAlleys):
|
||||
Hint_Class = FreeCellType_Hint
|
||||
Solver_Class = FreeCellSolverWrapper(sbb='rank')
|
||||
def createGame(self):
|
||||
StreetsAndAlleys.createGame(self, reserves=1)
|
||||
|
||||
class Fastness(StreetsAndAlleys):
|
||||
Hint_Class = FreeCellType_Hint
|
||||
Solver_Class = FreeCellSolverWrapper(sbb='rank')
|
||||
def createGame(self):
|
||||
StreetsAndAlleys.createGame(self, reserves=2)
|
||||
|
||||
|
@ -727,6 +734,8 @@ class Rittenhouse(Game):
|
|||
class Lightweight(StreetsAndAlleys):
|
||||
DEAL = (7, 1)
|
||||
RowStack_Class = StackWrapper(RK_RowStack, base_rank=KING)
|
||||
Solver_Class = FreeCellSolverWrapper(sbb='rank', esf='kings',
|
||||
sm='unlimited')
|
||||
|
||||
def createGame(self, rows=12, playcards=20):
|
||||
l, s = Layout(self), self.s
|
||||
|
@ -765,6 +774,7 @@ class Lightweight(StreetsAndAlleys):
|
|||
class CastleMount(Lightweight):
|
||||
DEAL = (11, 1)
|
||||
RowStack_Class = Spider_SS_RowStack
|
||||
Solver_Class = None
|
||||
|
||||
shallHighlightMatch = Game._shallHighlightMatch_RK
|
||||
getQuickPlayScore = Game._getSpiderQuickPlayScore
|
||||
|
@ -786,6 +796,7 @@ class SelectiveCastle_RowStack(RK_RowStack):
|
|||
class SelectiveCastle(StreetsAndAlleys, Chessboard):
|
||||
Foundation_Class = Chessboard_Foundation
|
||||
RowStack_Class = StackWrapper(SelectiveCastle_RowStack, mod=13)
|
||||
Solver_Class = None
|
||||
|
||||
def createGame(self):
|
||||
StreetsAndAlleys.createGame(self, texts=True)
|
||||
|
@ -854,7 +865,7 @@ class Soother(Game):
|
|||
|
||||
class PenelopesWeb(StreetsAndAlleys):
|
||||
RowStack_Class = StackWrapper(RK_RowStack, base_rank=KING)
|
||||
|
||||
Solver_Class = FreeCellSolverWrapper(sbb='rank', esf='kings')
|
||||
|
||||
|
||||
# register the game
|
||||
|
|
|
@ -204,7 +204,7 @@ class Realm(Game):
|
|||
l, s = Layout(self), self.s
|
||||
|
||||
# set window
|
||||
w, h = l.XM+8*l.XS, l.YM+2*l.YS+15*l.YOFFSET
|
||||
w, h = 3*l.XM+8*l.XS, l.YM+2*l.YS+15*l.YOFFSET
|
||||
self.setSize(w, h)
|
||||
|
||||
# create stacks
|
||||
|
|
|
@ -368,7 +368,7 @@ class SeniorWrangler_Talon(DealRowTalonStack):
|
|||
r = self.game.s.rows[self.round-1]
|
||||
if not r.cards:
|
||||
self.game.nextRoundMove(self)
|
||||
return
|
||||
return 1
|
||||
if sound:
|
||||
self.game.startDealSample()
|
||||
old_state = self.game.enterState(self.game.S_DEAL)
|
||||
|
|
|
@ -379,7 +379,8 @@ class PrincessPatience_RowStack(SS_RowStack):
|
|||
def canMoveCards(self, cards):
|
||||
if not SS_RowStack.canMoveCards(self, cards):
|
||||
return False
|
||||
index = list(self.game.s.rows).index(self)
|
||||
##index = list(self.game.s.rows).index(self) # don't work in demo-mode with cloned stack
|
||||
index = self.id
|
||||
col = index % 4
|
||||
row = index / 4
|
||||
if index < 16: # left
|
||||
|
@ -403,6 +404,7 @@ class PrincessPatience_RowStack(SS_RowStack):
|
|||
|
||||
|
||||
class PrincessPatience(Game):
|
||||
Hint_Class = CautiousDefaultHint
|
||||
RowStack_Class = PrincessPatience_RowStack
|
||||
|
||||
def createGame(self, max_rounds=1):
|
||||
|
|
|
@ -40,7 +40,7 @@ from gypsy import DieRussische_Foundation
|
|||
# ************************************************************************/
|
||||
|
||||
class Capricieuse(Game):
|
||||
|
||||
Hint_Class = CautiousDefaultHint
|
||||
Talon_Class = StackWrapper(RedealTalonStack, max_rounds=3)
|
||||
RowStack_Class = UD_SS_RowStack
|
||||
|
||||
|
@ -113,6 +113,7 @@ class Nationale(Capricieuse):
|
|||
# ************************************************************************/
|
||||
|
||||
class Strata(Game):
|
||||
Hint_Class = CautiousDefaultHint
|
||||
|
||||
def createGame(self):
|
||||
|
||||
|
|
|
@ -286,6 +286,7 @@ class Harvestman(Arachnida):
|
|||
# ************************************************************************/
|
||||
|
||||
class GermanPatience(Game):
|
||||
Hint_Class = CautiousDefaultHint
|
||||
|
||||
def createGame(self, rows=8):
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ from pysollib.stack import *
|
|||
from pysollib.game import Game
|
||||
from pysollib.layout import Layout
|
||||
from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint
|
||||
from pysollib.hint import FreeCellSolverWrapper
|
||||
from pysollib.pysoltk import MfxCanvasText
|
||||
|
||||
|
||||
|
@ -133,6 +134,10 @@ class Fan(Game):
|
|||
return ()
|
||||
|
||||
|
||||
class FanGame(Fan):
|
||||
Solver_Class = FreeCellSolverWrapper(preset='fan')
|
||||
|
||||
|
||||
# /***********************************************************************
|
||||
# // Scotch Patience
|
||||
# ************************************************************************/
|
||||
|
@ -461,6 +466,8 @@ class CloverLeaf_RowStack(UD_SS_RowStack):
|
|||
if not self.cards:
|
||||
return cards[0].rank in (ACE, KING)
|
||||
return True
|
||||
def _getBaseCard(self):
|
||||
return _('Base card - Ace or King.')
|
||||
|
||||
|
||||
class CloverLeaf(Game):
|
||||
|
@ -531,6 +538,8 @@ class CloverLeaf(Game):
|
|||
# ************************************************************************/
|
||||
|
||||
class FreeFan(Fan):
|
||||
RowStack_Class = FullStackWrapper(SuperMoveSS_RowStack, base_rank=KING)
|
||||
Solver_Class = FreeCellSolverWrapper(esf='kings', sbb='suit')
|
||||
def createGame(self):
|
||||
Fan.createGame(self, reserves=2, playcards=8)
|
||||
|
||||
|
@ -542,6 +551,7 @@ class FreeFan(Fan):
|
|||
class BoxFan(Fan):
|
||||
|
||||
RowStack_Class = KingAC_RowStack
|
||||
Solver_Class = FreeCellSolverWrapper(esf='kings')
|
||||
|
||||
def createGame(self):
|
||||
Fan.createGame(self, rows=(4,4,4,4))
|
||||
|
@ -632,7 +642,7 @@ class FascinationFan(Fan):
|
|||
def redealCards(self):
|
||||
r0 = r1 = len(self.s.talon.cards)/3
|
||||
m = len(self.s.talon.cards)%3
|
||||
if m >= 1: r2 += 1
|
||||
if m >= 1: r1 += 1
|
||||
self.s.talon.dealRow(rows=self.s.rows[:r0], flip=0, frames=4)
|
||||
self.s.talon.dealRow(rows=self.s.rows[:r1], flip=0, frames=4)
|
||||
self.s.talon.dealRowAvail(frames=4)
|
||||
|
@ -726,7 +736,7 @@ class Crescent(Game):
|
|||
|
||||
|
||||
# register the game
|
||||
registerGame(GameInfo(56, Fan, "Fan",
|
||||
registerGame(GameInfo(56, FanGame, "Fan",
|
||||
GI.GT_FAN_TYPE | GI.GT_OPEN, 1, 0, GI.SL_MOSTLY_SKILL))
|
||||
registerGame(GameInfo(87, ScotchPatience, "Scotch Patience",
|
||||
GI.GT_FAN_TYPE | GI.GT_OPEN, 1, 0, GI.SL_MOSTLY_SKILL))
|
||||
|
|
|
@ -542,6 +542,7 @@ class Octave_Talon(WasteTalonStack):
|
|||
|
||||
|
||||
class Octave(Game):
|
||||
Hint_Class = CautiousDefaultHint
|
||||
|
||||
#
|
||||
# game layout
|
||||
|
|
|
@ -51,37 +51,14 @@ from spider import Spider_AC_Foundation
|
|||
# // FreeCell
|
||||
# ************************************************************************/
|
||||
|
||||
# To simplify playing we also consider the number of free rows.
|
||||
# Note that this only is legal if the game.s.rows have a
|
||||
# cap.base_rank == ANY_RANK.
|
||||
# See also the "SuperMove" section in the FreeCell FAQ.
|
||||
class FreeCell_RowStack(AC_RowStack):
|
||||
def _getMaxMove(self, to_stack_ncards):
|
||||
max_move = getNumberOfFreeStacks(self.game.s.reserves) + 1
|
||||
n = getNumberOfFreeStacks(self.game.s.rows)
|
||||
if to_stack_ncards == 0:
|
||||
n = n - 1
|
||||
while n > 0 and max_move < 1000:
|
||||
max_move = max_move * 2
|
||||
n = n - 1
|
||||
return max_move
|
||||
|
||||
def canMoveCards(self, cards):
|
||||
max_move = self._getMaxMove(1)
|
||||
return len(cards) <= max_move and AC_RowStack.canMoveCards(self, cards)
|
||||
|
||||
def acceptsCards(self, from_stack, cards):
|
||||
max_move = self._getMaxMove(len(self.cards))
|
||||
return len(cards) <= max_move and AC_RowStack.acceptsCards(self, from_stack, cards)
|
||||
|
||||
|
||||
class FreeCell(Game):
|
||||
Layout_Method = Layout.freeCellLayout
|
||||
Talon_Class = InitialDealTalonStack
|
||||
Foundation_Class = SS_FoundationStack
|
||||
RowStack_Class = FreeCell_RowStack
|
||||
RowStack_Class = SuperMoveAC_RowStack
|
||||
ReserveStack_Class = ReserveStack
|
||||
Hint_Class = FreeCellSolverWrapper(FreeCellType_Hint, {})
|
||||
Hint_Class = FreeCellType_Hint
|
||||
Solver_Class = FreeCellSolverWrapper()
|
||||
|
||||
|
||||
#
|
||||
|
@ -127,7 +104,7 @@ class FreeCell(Game):
|
|||
|
||||
class RelaxedFreeCell(FreeCell):
|
||||
RowStack_Class = AC_RowStack
|
||||
Hint_Class = FreeCellSolverWrapper(FreeCellType_Hint, {'sm' : "unlimited"})
|
||||
Solver_Class = FreeCellSolverWrapper(sm='unlimited')
|
||||
|
||||
|
||||
# /***********************************************************************
|
||||
|
@ -136,7 +113,7 @@ class RelaxedFreeCell(FreeCell):
|
|||
|
||||
class ForeCell(FreeCell):
|
||||
RowStack_Class = StackWrapper(FreeCell_AC_RowStack, base_rank=KING)
|
||||
Hint_Class = FreeCellSolverWrapper(FreeCellType_Hint, {'esf' : "kings"})
|
||||
Solver_Class = FreeCellSolverWrapper(esf='kings')
|
||||
|
||||
def startGame(self):
|
||||
for i in range(5):
|
||||
|
@ -159,7 +136,7 @@ class ChallengeFreeCell(FreeCell):
|
|||
|
||||
class SuperChallengeFreeCell(ChallengeFreeCell):
|
||||
RowStack_Class = StackWrapper(FreeCell_AC_RowStack, base_rank=KING)
|
||||
Hint_Class = FreeCellSolverWrapper(FreeCellType_Hint, {'esf' : "kings"})
|
||||
Solver_Class = FreeCellSolverWrapper(esf='kings')
|
||||
|
||||
|
||||
# /***********************************************************************
|
||||
|
@ -169,7 +146,7 @@ class SuperChallengeFreeCell(ChallengeFreeCell):
|
|||
class Stalactites(FreeCell):
|
||||
Foundation_Class = StackWrapper(RK_FoundationStack, suit=ANY_SUIT, mod=13, min_cards=1)
|
||||
RowStack_Class = StackWrapper(BasicRowStack, max_move=1, max_accept=0)
|
||||
Hint_Class = FreeCellType_Hint
|
||||
Solver_Class = None
|
||||
|
||||
def createGame(self):
|
||||
FreeCell.createGame(self, reserves=2)
|
||||
|
@ -192,7 +169,7 @@ class Stalactites(FreeCell):
|
|||
# ************************************************************************/
|
||||
|
||||
class DoubleFreecell(FreeCell):
|
||||
Hint_Class = FreeCellType_Hint
|
||||
Solver_Class = None
|
||||
|
||||
#
|
||||
# game layout
|
||||
|
@ -246,7 +223,7 @@ class DoubleFreecell(FreeCell):
|
|||
# ************************************************************************/
|
||||
|
||||
class TripleFreecell(FreeCell):
|
||||
Hint_Class = FreeCellType_Hint
|
||||
Solver_Class = None
|
||||
|
||||
#
|
||||
# game layout
|
||||
|
@ -267,8 +244,8 @@ class TripleFreecell(FreeCell):
|
|||
s.talon = self.Talon_Class(l.XM, h-l.YS, self)
|
||||
|
||||
x, y = l.XM+(max_rows-decks*4)*l.XS/2, l.YM
|
||||
for i in range(decks):
|
||||
for j in range(4):
|
||||
for j in range(4):
|
||||
for i in range(decks):
|
||||
s.foundations.append(self.Foundation_Class(x, y, self, suit=j))
|
||||
x += l.XS
|
||||
x, y = l.XM+(max_rows-reserves)*l.XS/2, l.YM+l.YS
|
||||
|
@ -323,16 +300,24 @@ class BigCell(TripleFreecell):
|
|||
# // Spidercells
|
||||
# ************************************************************************/
|
||||
|
||||
class Spidercells_RowStack(FreeCell_RowStack):
|
||||
class Spidercells_RowStack(SuperMoveAC_RowStack):
|
||||
def canMoveCards(self, cards):
|
||||
if len(cards) == 13 and isAlternateColorSequence(cards):
|
||||
return True
|
||||
return FreeCell_RowStack.canMoveCards(self, cards)
|
||||
return SuperMoveAC_RowStack.canMoveCards(self, cards)
|
||||
def canDropCards(self, stacks):
|
||||
if len(self.cards) < 13:
|
||||
return (None, 0)
|
||||
cards = self.cards[-13:]
|
||||
for s in stacks:
|
||||
if s is not self and s.acceptsCards(self, cards):
|
||||
return (s, 13)
|
||||
return (None, 0)
|
||||
|
||||
|
||||
class Spidercells(FreeCell):
|
||||
|
||||
Hint_Class = FreeCellType_Hint
|
||||
Solver_Class = None
|
||||
Foundation_Class = Spider_AC_Foundation
|
||||
RowStack_Class = Spidercells_RowStack
|
||||
|
||||
|
@ -361,8 +346,6 @@ class Spidercells(FreeCell):
|
|||
# ************************************************************************/
|
||||
|
||||
class SevenByFour(FreeCell):
|
||||
Hint_Class = FreeCellSolverWrapper(FreeCellType_Hint, {})
|
||||
#Hint_Class = FreeCellType_Hint
|
||||
def createGame(self):
|
||||
FreeCell.createGame(self, rows=7)
|
||||
def startGame(self):
|
||||
|
@ -377,9 +360,8 @@ class SevenByFive(SevenByFour):
|
|||
FreeCell.createGame(self, rows=7, reserves=5)
|
||||
|
||||
class Bath(FreeCell):
|
||||
Hint_Class = FreeCellSolverWrapper(FreeCellType_Hint, {'esf' : 'kings'})
|
||||
#Hint_Class = FreeCellType_Hint
|
||||
RowStack_Class = StackWrapper(FreeCell_RowStack, base_rank=KING)
|
||||
Solver_Class = FreeCellSolverWrapper(esf='kings')
|
||||
RowStack_Class = StackWrapper(SuperMoveAC_RowStack, base_rank=KING)
|
||||
def createGame(self):
|
||||
FreeCell.createGame(self, rows=10, reserves=2)
|
||||
def startGame(self):
|
||||
|
@ -395,7 +377,7 @@ class Bath(FreeCell):
|
|||
# ************************************************************************/
|
||||
|
||||
class Clink(FreeCell):
|
||||
Hint_Class = FreeCellType_Hint
|
||||
Solver_Class = None
|
||||
|
||||
def createGame(self):
|
||||
# create layout
|
||||
|
@ -440,7 +422,7 @@ class Clink(FreeCell):
|
|||
# ************************************************************************/
|
||||
|
||||
class Repair(FreeCell):
|
||||
Hint_Class = FreeCellType_Hint
|
||||
Solver_Class = FreeCellSolverWrapper(sm='unlimited')
|
||||
RowStack_Class = AC_RowStack
|
||||
|
||||
def createGame(self):
|
||||
|
@ -464,7 +446,7 @@ class FourColours_RowStack(AC_RowStack):
|
|||
return self.game.app.images.getReserveBottom()
|
||||
|
||||
class FourColours(FreeCell):
|
||||
Hint_Class = FreeCellType_Hint
|
||||
Solver_Class = None
|
||||
RowStack_Class = AC_RowStack
|
||||
|
||||
def createGame(self):
|
||||
|
@ -507,7 +489,7 @@ class GermanFreeCell_Reserve(ReserveStack):
|
|||
|
||||
|
||||
class GermanFreeCell(SevenByFour):
|
||||
Hint_Class = FreeCellType_Hint
|
||||
Solver_Class = None
|
||||
RowStack_Class = AC_RowStack
|
||||
ReserveStack_Class = GermanFreeCell_Reserve
|
||||
|
||||
|
@ -524,7 +506,7 @@ class GermanFreeCell(SevenByFour):
|
|||
# ************************************************************************/
|
||||
|
||||
class OceanTowers(TripleFreecell):
|
||||
Hint_Class = FreeCellType_Hint
|
||||
Solver_Class = FreeCellSolverWrapper(esf='kings', sbb='suit')
|
||||
RowStack_Class = StackWrapper(FreeCell_SS_RowStack, base_rank=KING)
|
||||
|
||||
def createGame(self):
|
||||
|
@ -550,7 +532,7 @@ class KingCell_RowStack(RK_RowStack):
|
|||
return len(cards) <= max_move and RK_RowStack.canMoveCards(self, cards)
|
||||
|
||||
class KingCell(FreeCell):
|
||||
Hint_Class = FreeCellType_Hint
|
||||
Solver_Class = FreeCellSolverWrapper(esf='kings')
|
||||
RowStack_Class = StackWrapper(KingCell_RowStack, base_rank=KING)
|
||||
|
||||
shallHighlightMatch = Game._shallHighlightMatch_RK
|
||||
|
@ -612,6 +594,7 @@ class Headquarters(Game):
|
|||
|
||||
class CanCan(FreeCell):
|
||||
Hint_Class = DefaultHint
|
||||
Solver_Class = None
|
||||
RowStack_Class = KingAC_RowStack
|
||||
ReserveStack_Class = StackWrapper(OpenStack, max_accept=0)
|
||||
|
||||
|
|
|
@ -291,6 +291,8 @@ class BlackHole_Foundation(AbstractFoundationStack):
|
|||
r1, r2 = self.cards[-1].rank, cards[0].rank
|
||||
return (r1 + 1) % self.cap.mod == r2 or (r2 + 1) % self.cap.mod == r1
|
||||
return 1
|
||||
def getHelp(self):
|
||||
return _('Foundation. Build up or down regardless of suit.')
|
||||
|
||||
|
||||
class BlackHole_RowStack(ReserveStack):
|
||||
|
|
|
@ -462,9 +462,9 @@ class BigBen(Game):
|
|||
|
||||
def startGame(self):
|
||||
self.startDealSample()
|
||||
self.s.talon.dealRow(rows=self.s.foundations)
|
||||
self.s.talon.dealRow(rows=self.s.foundations, frames=4)
|
||||
for i in range(3):
|
||||
self.s.talon.dealRow()
|
||||
self.s.talon.dealRow(frames=4)
|
||||
|
||||
def _autoDeal(self, sound=1):
|
||||
# don't deal a card to the waste if the waste is empty
|
||||
|
|
|
@ -263,6 +263,7 @@ class BigDeal(DoubleKlondike):
|
|||
# ************************************************************************/
|
||||
|
||||
class Delivery(BigDeal):
|
||||
Hint_Class = CautiousDefaultHint
|
||||
RowStack_Class = StackWrapper(SS_RowStack, max_move=1)
|
||||
|
||||
def createGame(self):
|
||||
|
|
|
@ -45,6 +45,7 @@ class HeadsAndTails_Reserve(OpenStack):
|
|||
|
||||
|
||||
class HeadsAndTails(Game):
|
||||
Hint_Class = CautiousDefaultHint
|
||||
|
||||
#
|
||||
# game layout
|
||||
|
|
|
@ -41,6 +41,7 @@ from pysollib.stack import *
|
|||
from pysollib.game import Game
|
||||
from pysollib.layout import Layout
|
||||
from pysollib.hint import DefaultHint, FreeCellType_Hint, CautiousDefaultHint
|
||||
from pysollib.hint import FreeCellSolverWrapper
|
||||
from pysollib.pysoltk import MfxCanvasText
|
||||
|
||||
|
||||
|
@ -63,6 +64,7 @@ class DerKatzenschwanz_Hint(FreeCellType_Hint):
|
|||
class DerKatzenschwanz(Game):
|
||||
RowStack_Class = StackWrapper(AC_RowStack, base_rank=NO_RANK)
|
||||
Hint_Class = DerKatzenschwanz_Hint
|
||||
Solver_Class = FreeCellSolverWrapper(esf='none', sm='unlimited')
|
||||
|
||||
#
|
||||
# game layout
|
||||
|
@ -145,6 +147,7 @@ class DerKatzenschwanz(Game):
|
|||
class DieSchlange(DerKatzenschwanz):
|
||||
|
||||
RowStack_Class = StackWrapper(FreeCell_AC_RowStack, base_rank=NO_RANK)
|
||||
Solver_Class = FreeCellSolverWrapper(esf='none')
|
||||
|
||||
def createGame(self):
|
||||
DerKatzenschwanz.createGame(self, rows=9, reserves=7)
|
||||
|
@ -171,8 +174,8 @@ class DieSchlange(DerKatzenschwanz):
|
|||
|
||||
class Kings(DerKatzenschwanz):
|
||||
|
||||
##RowStack_Class = StackWrapper(AC_RowStack, base_rank=NO_RANK)
|
||||
RowStack_Class = StackWrapper(FreeCell_AC_RowStack, base_rank=NO_RANK)
|
||||
RowStack_Class = StackWrapper(AC_RowStack, base_rank=NO_RANK)
|
||||
Solver_Class = FreeCellSolverWrapper(esf='none', sm='unlimited')
|
||||
|
||||
def createGame(self):
|
||||
return DerKatzenschwanz.createGame(self, rows=8, reserves=8)
|
||||
|
@ -192,8 +195,8 @@ class Kings(DerKatzenschwanz):
|
|||
|
||||
class Retinue(DieSchlange, Kings):
|
||||
|
||||
##RowStack_Class = StackWrapper(AC_RowStack, base_rank=NO_RANK)
|
||||
RowStack_Class = StackWrapper(FreeCell_AC_RowStack, base_rank=NO_RANK)
|
||||
Solver_Class = FreeCellSolverWrapper(esf='none')
|
||||
|
||||
def createGame(self):
|
||||
return DerKatzenschwanz.createGame(self, rows=8, reserves=8)
|
||||
|
@ -249,6 +252,7 @@ class SalicLaw_Talon(OpenTalonStack):
|
|||
class SalicLaw(DerKatzenschwanz):
|
||||
|
||||
Hint_Class = SalicLaw_Hint
|
||||
Solver_Class = None
|
||||
|
||||
Foundation_Classes = [
|
||||
StackWrapper(AbstractFoundationStack, max_cards=1, base_rank=QUEEN),
|
||||
|
@ -340,6 +344,7 @@ class SalicLaw(DerKatzenschwanz):
|
|||
|
||||
class Deep(DerKatzenschwanz):
|
||||
RowStack_Class = StackWrapper(AC_RowStack, base_rank=ANY_RANK)
|
||||
Solver_Class = FreeCellSolverWrapper(sm='unlimited')
|
||||
|
||||
def createGame(self):
|
||||
return DerKatzenschwanz.createGame(self, rows=8, reserves=8)
|
||||
|
@ -499,6 +504,7 @@ class StepUp_RowStack(AC_RowStack):
|
|||
|
||||
|
||||
class StepUp(Game):
|
||||
Hint_Class = CautiousDefaultHint
|
||||
|
||||
def createGame(self):
|
||||
l, s = Layout(self), self.s
|
||||
|
|
|
@ -43,6 +43,7 @@ from pysollib.game import Game
|
|||
from pysollib.layout import Layout
|
||||
from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint
|
||||
from pysollib.hint import KlondikeType_Hint
|
||||
from pysollib.hint import FreeCellSolverWrapper
|
||||
from pysollib.pysoltk import MfxCanvasText
|
||||
|
||||
from canfield import CanfieldRush_Talon
|
||||
|
@ -283,15 +284,14 @@ class BlindAlleys(Eastcliff):
|
|||
# /***********************************************************************
|
||||
# // Somerset
|
||||
# // Morehead
|
||||
# // Canister
|
||||
# // American Canister
|
||||
# // British Canister
|
||||
# // Usk
|
||||
# ************************************************************************/
|
||||
|
||||
class Somerset(Klondike):
|
||||
Talon_Class = InitialDealTalonStack
|
||||
RowStack_Class = StackWrapper(AC_RowStack, max_move=1)
|
||||
RowStack_Class = SuperMoveAC_RowStack
|
||||
Hint_Class = CautiousDefaultHint
|
||||
Solver_Class = FreeCellSolverWrapper()
|
||||
|
||||
def createGame(self):
|
||||
Klondike.createGame(self, max_rounds=1, rows=10, waste=0, texts=0)
|
||||
|
@ -306,12 +306,14 @@ class Somerset(Klondike):
|
|||
|
||||
class Morehead(Somerset):
|
||||
RowStack_Class = StackWrapper(BO_RowStack, max_move=1)
|
||||
Solver_Class = None
|
||||
|
||||
|
||||
class Usk(Somerset):
|
||||
|
||||
Talon_Class = RedealTalonStack
|
||||
RowStack_Class = StackWrapper(AC_RowStack, base_rank=KING)
|
||||
Solver_Class = None
|
||||
|
||||
def createGame(self):
|
||||
Klondike.createGame(self, max_rounds=2, rows=10, waste=0, texts=0)
|
||||
|
@ -330,6 +332,8 @@ class Usk(Somerset):
|
|||
|
||||
class AmericanCanister(Klondike):
|
||||
Talon_Class = InitialDealTalonStack
|
||||
RowStack_Class = AC_RowStack
|
||||
Solver_Class = FreeCellSolverWrapper(sm='unlimited')
|
||||
|
||||
def createGame(self):
|
||||
Klondike.createGame(self, max_rounds=1, rows=8, waste=0, texts=0)
|
||||
|
@ -344,11 +348,13 @@ class AmericanCanister(Klondike):
|
|||
|
||||
class Canister(AmericanCanister):
|
||||
RowStack_Class = RK_RowStack
|
||||
Solver_Class = FreeCellSolverWrapper(sbb='rank', sm='unlimited')
|
||||
shallHighlightMatch = Game._shallHighlightMatch_RK
|
||||
|
||||
|
||||
class BritishCanister(AmericanCanister):
|
||||
RowStack_Class = StackWrapper(KingAC_RowStack, max_move=1)
|
||||
Solver_Class = FreeCellSolverWrapper(esf='kings')
|
||||
|
||||
|
||||
# /***********************************************************************
|
||||
|
@ -813,6 +819,7 @@ class Alternation(Klondike):
|
|||
|
||||
class Lanes(Klondike):
|
||||
|
||||
Hint_Class = CautiousDefaultHint
|
||||
Foundation_Class = StackWrapper(SS_FoundationStack, max_move=0)
|
||||
RowStack_Class = StackWrapper(AC_RowStack, base_rank=ANY_RANK, max_move=1)
|
||||
|
||||
|
@ -979,6 +986,8 @@ class DoubleDot(Klondike):
|
|||
def shallHighlightMatch(self, stack1, card1, stack2, card2):
|
||||
return abs(card1.rank-card2.rank) == 2
|
||||
|
||||
shallHighlightMatch = Game._shallHighlightMatch_RKW
|
||||
|
||||
|
||||
# /***********************************************************************
|
||||
# // Seven Devils
|
||||
|
@ -1176,6 +1185,7 @@ class GoldMine(Klondike):
|
|||
# ************************************************************************/
|
||||
|
||||
class LuckyThirteen(Game):
|
||||
Hint_Class = CautiousDefaultHint
|
||||
RowStack_Class = StackWrapper(RK_RowStack, base_rank=NO_RANK)
|
||||
|
||||
def createGame(self, xoffset=0, playcards=0):
|
||||
|
|
|
@ -95,8 +95,8 @@ class Mahjongg_Foundation(OpenStack):
|
|||
def basicIsBlocked(self):
|
||||
return 1
|
||||
|
||||
def initBindings(self):
|
||||
pass
|
||||
#def initBindings(self):
|
||||
# pass
|
||||
|
||||
def _position(self, card):
|
||||
#AbstractFoundationStack._position(self, card)
|
||||
|
@ -360,11 +360,13 @@ class AbstractMahjonggGame(Game):
|
|||
if self.preview:
|
||||
# Fixme
|
||||
dx, dy, d_x, d_y = dx/2, dy/2, d_x/2, d_y/2
|
||||
self._delta_x, self._delta_y = dx, -dy
|
||||
else:
|
||||
dx = 3
|
||||
dy = -3
|
||||
d_x = 0
|
||||
d_y = 0
|
||||
self._delta_x, self._delta_y = 0, 0
|
||||
#print dx, dy, d_x, d_y, cs.version
|
||||
|
||||
font = self.app.getFont("canvas_default")
|
||||
|
@ -472,7 +474,7 @@ class AbstractMahjonggGame(Game):
|
|||
y = l.YM+fdyy+j*cardh
|
||||
else:
|
||||
if TOOLKIT == 'tk':
|
||||
x = -l.XS
|
||||
x = -l.XS-self.canvas.xmargin
|
||||
y = l.YM+dyy
|
||||
elif TOOLKIT == 'gtk':
|
||||
# FIXME
|
||||
|
@ -635,6 +637,10 @@ class AbstractMahjonggGame(Game):
|
|||
# Mahjongg special: highlight all moveable tiles
|
||||
return ((self.s.rows, 1),)
|
||||
|
||||
def _highlightCards(self, info, sleep=1.5, delta=(1,1,1,1)):
|
||||
delta = (-self._delta_x, 0, 0, -self._delta_y)
|
||||
Game._highlightCards(self, info, sleep=sleep, delta=delta)
|
||||
|
||||
def getCardFaceImage(self, deck, suit, rank):
|
||||
if suit == 3:
|
||||
cs = self.app.cardset
|
||||
|
@ -690,6 +696,7 @@ class AbstractMahjonggGame(Game):
|
|||
return 7 >= card2.rank >= 4
|
||||
return card1.rank == card2.rank
|
||||
|
||||
|
||||
## mahjongg util
|
||||
def comp_cardset(ncards):
|
||||
# calc decks, ranks & trumps
|
||||
|
|
|
@ -254,13 +254,14 @@ class Shisen_RowStack(Mahjongg_RowStack):
|
|||
cardw = images.CARDW
|
||||
cardh = images.CARDH
|
||||
coords = []
|
||||
dx, dy = game._delta_x, game._delta_y
|
||||
for x, y in path:
|
||||
if x == 0:
|
||||
coords.append(6)
|
||||
elif x == game.L[0]+1:
|
||||
coords.append(x0+cardw*(x-1)+10)
|
||||
coords.append(x0+cardw*(x-1)+10+dx)
|
||||
else:
|
||||
coords.append(x0+cardw/2+cardw*(x-1))
|
||||
coords.append(x0+cardw/2+cardw*(x-1)+dx)
|
||||
if y == 0:
|
||||
coords.append(6)
|
||||
elif y == game.L[1]+1:
|
||||
|
@ -308,11 +309,13 @@ class AbstractShisenGame(AbstractMahjonggGame):
|
|||
dy = -l.YOFFSET
|
||||
d_x = cs.SHADOW_XOFFSET
|
||||
d_y = cs.SHADOW_YOFFSET
|
||||
self._delta_x, self._delta_y = dx, -dy
|
||||
else:
|
||||
dx = 3
|
||||
dy = -3
|
||||
d_x = 0
|
||||
d_y = 0
|
||||
self._delta_x, self._delta_y = 0, 0
|
||||
|
||||
font = self.app.getFont("canvas_default")
|
||||
|
||||
|
@ -358,10 +361,9 @@ class AbstractShisenGame(AbstractMahjonggGame):
|
|||
self.texts.info = MfxCanvasText(self.canvas,
|
||||
self.width - l.XM - ti_width, y,
|
||||
anchor="nw", font=font)
|
||||
x = self.width + l.XS
|
||||
y = self.height - l.YS - dxx
|
||||
# the Talon is invisble
|
||||
s.talon = InitialDealTalonStack(x, y + l.YS, self)
|
||||
s.talon = InitialDealTalonStack(-l.XS-self.canvas.xmargin,
|
||||
self.height-dyy, self)
|
||||
|
||||
# Define stack groups
|
||||
l.defaultStackGroups()
|
||||
|
|
|
@ -716,7 +716,7 @@ class Assembly_RowStack(RK_RowStack):
|
|||
|
||||
|
||||
class Assembly(Numerica):
|
||||
Hint_Class = DefaultHint
|
||||
Hint_Class = CautiousDefaultHint
|
||||
|
||||
Foundation_Class = StackWrapper(RK_FoundationStack, suit=ANY_SUIT)
|
||||
RowStack_Class = StackWrapper(Assembly_RowStack, max_move=1)
|
||||
|
|
|
@ -274,7 +274,7 @@ class Pyramid(Game):
|
|||
|
||||
def startGame(self):
|
||||
self.startDealSample()
|
||||
self.s.talon.dealRow()
|
||||
self.s.talon.dealRow(frames=4)
|
||||
self.s.talon.dealCards() # deal first card to WasteStack
|
||||
|
||||
def getAutoStacks(self, event=None):
|
||||
|
@ -585,7 +585,7 @@ class Fifteens_RowStack(Elevens_RowStack):
|
|||
def acceptsCards(self, from_stack, cards):
|
||||
if not Elevens_RowStack.acceptsCards(self, from_stack, cards):
|
||||
return False
|
||||
return cards[0].rank < 9 and self.cards[0] < 9
|
||||
return cards[0].rank < 9 and self.cards[0].rank < 9
|
||||
|
||||
|
||||
class Fifteens_Reserve(ReserveStack):
|
||||
|
@ -919,7 +919,7 @@ class Apophis(Pharaohs):
|
|||
|
||||
def startGame(self):
|
||||
self.startDealSample()
|
||||
self.s.talon.dealRow(frames=3)
|
||||
self.s.talon.dealRow(frames=4)
|
||||
self.s.talon.dealCards()
|
||||
|
||||
def shallHighlightMatch(self, stack1, card1, stack2, card2):
|
||||
|
@ -954,7 +954,7 @@ class Cheops_RowStack(Cheops_StackMethods, Pyramid_RowStack):
|
|||
|
||||
class Cheops(Pyramid):
|
||||
|
||||
Foundation_Class = AbstractFoundationStack
|
||||
Foundation_Class = StackWrapper(AbstractFoundationStack, max_accept=0)
|
||||
Talon_Class = StackWrapper(Cheops_Talon, max_rounds=1, max_accept=1)
|
||||
RowStack_Class = Cheops_RowStack
|
||||
WasteStack_Class = Cheops_Waste
|
||||
|
|
|
@ -592,6 +592,7 @@ class ThreePirates_Talon(DealRowTalonStack):
|
|||
|
||||
|
||||
class ThreePirates(Game):
|
||||
Hint_Class = CautiousDefaultHint
|
||||
|
||||
def createGame(self):
|
||||
l, s = Layout(self), self.s
|
||||
|
@ -634,6 +635,20 @@ class ThreePirates(Game):
|
|||
# // Frames
|
||||
# ************************************************************************/
|
||||
|
||||
class Frames_Hint(CautiousDefaultHint):
|
||||
def computeHints(self):
|
||||
CautiousDefaultHint.computeHints(self)
|
||||
if self.hints:
|
||||
return
|
||||
if not self.game.s.talon.cards:
|
||||
return
|
||||
for s in self.game.s.reserves:
|
||||
if s.cards:
|
||||
for r in self.game.s.rows:
|
||||
if r.acceptsCards(s, s.cards):
|
||||
self.addHint(5000, 1, s, r)
|
||||
|
||||
|
||||
class Frames_Foundation(UnionSquare_Foundation):
|
||||
def acceptsCards(self, from_stack, cards):
|
||||
if not UnionSquare_Foundation.acceptsCards(self, from_stack, cards):
|
||||
|
@ -664,7 +679,7 @@ class Frames_RowStack(UD_SS_RowStack):
|
|||
|
||||
|
||||
class Frames(Game):
|
||||
Hint_Class = CautiousDefaultHint
|
||||
Hint_Class = Frames_Hint #CautiousDefaultHint
|
||||
|
||||
def createGame(self):
|
||||
l, s = Layout(self), self.s
|
||||
|
@ -1053,7 +1068,7 @@ class Colonel(Game):
|
|||
|
||||
x, y = l.XM+2*l.XS, l.YM
|
||||
for i in range(8):
|
||||
s.foundations.append(SS_FoundationStack(x, y, self, suit=i%4,
|
||||
s.foundations.append(SS_FoundationStack(x, y, self, suit=i/2,
|
||||
max_move=0))
|
||||
x += l.XS
|
||||
|
||||
|
@ -1079,7 +1094,7 @@ class Colonel(Game):
|
|||
|
||||
def startGame(self):
|
||||
self.startDealSample()
|
||||
self.s.talon.dealRow()
|
||||
self.s.talon.dealRow(frames=4)
|
||||
self.s.talon.dealCards()
|
||||
|
||||
shallHighlightMatch = Game._shallHighlightMatch_SS
|
||||
|
|
|
@ -161,6 +161,19 @@ class SiebenBisAs(Game):
|
|||
# // Maze
|
||||
# ************************************************************************/
|
||||
|
||||
class Maze_Hint(SiebenBisAs_Hint):
|
||||
def shallMovePile(self, from_stack, to_stack, pile, rpile):
|
||||
if from_stack is to_stack or not to_stack.acceptsCards(from_stack, pile):
|
||||
return 0
|
||||
# now check for loops
|
||||
rr = self.ClonedStack(from_stack, stackcards=rpile)
|
||||
if rr.acceptsCards(to_stack, pile):
|
||||
# the pile we are going to move could be moved back -
|
||||
# this is dangerous as we can create endless loops...
|
||||
return 0
|
||||
return 1
|
||||
|
||||
|
||||
class Maze_RowStack(BasicRowStack):
|
||||
def acceptsCards(self, from_stack, cards):
|
||||
if not BasicRowStack.acceptsCards(self, from_stack, cards):
|
||||
|
@ -189,7 +202,7 @@ class Maze_RowStack(BasicRowStack):
|
|||
class Maze(Game):
|
||||
GAME_VERSION = 2
|
||||
|
||||
Hint_Class = SiebenBisAs_Hint
|
||||
Hint_Class = Maze_Hint #SiebenBisAs_Hint
|
||||
|
||||
#
|
||||
# game layout
|
||||
|
|
|
@ -42,6 +42,7 @@ from pysollib.game import Game
|
|||
from pysollib.layout import Layout
|
||||
from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint
|
||||
from pysollib.hint import SpiderType_Hint, YukonType_Hint
|
||||
from pysollib.hint import FreeCellSolverWrapper
|
||||
|
||||
|
||||
# /***********************************************************************
|
||||
|
@ -112,6 +113,23 @@ class Spider_RowStack(Spider_SS_RowStack):
|
|||
return (None, 0)
|
||||
|
||||
|
||||
class SuperMoveSpider_RowStack(SuperMoveStack_StackMethods, Spider_RowStack):
|
||||
def acceptsCards(self, from_stack, cards):
|
||||
if not Spider_RowStack.acceptsCards(self, from_stack, cards):
|
||||
return False
|
||||
num_seq = self._getNumSSSeq(cards)
|
||||
max_move = self._getMaxMove(len(self.cards))
|
||||
return num_seq <= max_move
|
||||
def canMoveCards(self, cards):
|
||||
if not self.basicCanMoveCards(cards):
|
||||
return False
|
||||
if not isRankSequence(cards, self.cap.mod, self.cap.dir):
|
||||
return False
|
||||
num_seq = self._getNumSSSeq(cards)
|
||||
max_move = self._getMaxMove(1)
|
||||
return num_seq <= max_move
|
||||
|
||||
|
||||
# /***********************************************************************
|
||||
# // Relaxed Spider
|
||||
# ************************************************************************/
|
||||
|
@ -282,6 +300,8 @@ class WillOTheWisp(Spiderette):
|
|||
|
||||
class SimpleSimon(Spider):
|
||||
Talon_Class = InitialDealTalonStack
|
||||
RowStack_Class = SuperMoveSpider_RowStack
|
||||
Solver_Class = FreeCellSolverWrapper(preset='simple_simon', base_rank=0)
|
||||
|
||||
def createGame(self):
|
||||
Spider.createGame(self, rows=10, texts=0)
|
||||
|
@ -292,6 +312,12 @@ class SimpleSimon(Spider):
|
|||
self.startDealSample()
|
||||
self.s.talon.dealRow()
|
||||
|
||||
class SimpleSimonII(SimpleSimon):
|
||||
Solver_Class = None
|
||||
Foundation_Class = StackWrapper(Spider_SS_Foundation,
|
||||
base_rank=ANY_RANK, mod=13)
|
||||
RowStack_Class = StackWrapper(SuperMoveSpider_RowStack, mod=13)
|
||||
|
||||
|
||||
# /***********************************************************************
|
||||
# // Rachel
|
||||
|
@ -1370,4 +1396,6 @@ registerGame(GameInfo(685, FechtersGame, "Fechter's Game",
|
|||
GI.GT_SPIDER, 2, 0, GI.SL_MOSTLY_SKILL))
|
||||
registerGame(GameInfo(710, Bebop, "Bebop",
|
||||
GI.GT_2DECK_TYPE | GI.GT_ORIGINAL, 2, 0, GI.SL_MOSTLY_SKILL))
|
||||
#registerGame(GameInfo(711, SimpleSimonII, "Simple Simon II",
|
||||
# GI.GT_SPIDER | GI.GT_OPEN, 1, 0, GI.SL_MOSTLY_SKILL))
|
||||
|
||||
|
|
|
@ -762,13 +762,13 @@ class RoyalAids(Game):
|
|||
s.waste = WasteStack(x, y, self)
|
||||
l.createText(s.waste, 'se')
|
||||
|
||||
x, y = l.XM+4*l.XS, l.YM+2*l.YS
|
||||
x, y = l.XM+3.75*l.XS, l.YM+2*l.YS
|
||||
for i in (0,1):
|
||||
stack = RoyalAids_RowStack(x, y, self, max_move=1)
|
||||
s.rows.append(stack)
|
||||
stack.CARD_XOFFSET, stack.CARD_YOFFSET = 0, 0
|
||||
x += l.XS
|
||||
x, y = l.XM+3*l.XS, l.YM+3*l.YS
|
||||
x, y = l.XM+2.75*l.XS, l.YM+3*l.YS
|
||||
for i in range(4):
|
||||
stack = BasicRowStack(x, y, self)
|
||||
s.reserves.append(stack)
|
||||
|
@ -995,7 +995,7 @@ class Khedive(Game):
|
|||
|
||||
def startGame(self):
|
||||
self.startDealSample()
|
||||
self.s.talon.dealRow()
|
||||
self.s.talon.dealRow(frames=4)
|
||||
self.s.talon.dealCards()
|
||||
|
||||
def fillStack(self, stack):
|
||||
|
|
|
@ -365,9 +365,9 @@ class BastilleDay_BastilleStack(Stack):
|
|||
old_state = self.game.enterState(self.game.S_DEAL)
|
||||
if sound and not self.game.demo:
|
||||
self.game.playSample("dealwaste")
|
||||
self.flipMove()
|
||||
self.moveMove(1, self.game.s.reserves[-1], frames=4, shadow=0)
|
||||
self.game.flipAndMoveMove(self, self.game.s.reserves[-1])
|
||||
self.game.leaveState(old_state)
|
||||
self.game.finishMove()
|
||||
return 1
|
||||
|
||||
def getHelp(self):
|
||||
|
@ -432,7 +432,7 @@ class BastilleDay(Game):
|
|||
if self.demo:
|
||||
r = self.s.reserves[0]
|
||||
if r.canDealCards():
|
||||
self.demo.last_deal = [] # don't check last deal
|
||||
##self.demo.last_deal = [] # don't check last deal
|
||||
return r.dealCards(sound=sound)
|
||||
return Game.dealCards(self, sound=sound)
|
||||
|
||||
|
|
|
@ -159,6 +159,7 @@ class DutchSolitaire_RowStack(UD_RK_RowStack):
|
|||
|
||||
|
||||
class DutchSolitaire(Windmill):
|
||||
Hint_Class = CautiousDefaultHint
|
||||
Foundation_Classes = [
|
||||
StackWrapper(BlackHole_Foundation, suit=ANY_SUIT, mod=13,
|
||||
max_cards=UNLIMITED_CARDS, min_cards=1),
|
||||
|
@ -270,7 +271,7 @@ class Corners(Game):
|
|||
self.setSize(5*l.XS+l.XM, 4*l.YS+3*l.YM)
|
||||
|
||||
# create stacks
|
||||
x, y = l.XM+l.XS, l.YM
|
||||
x, y = l.XM+1.5*l.XS, l.YM
|
||||
s.talon = WasteTalonStack(x, y, self, max_rounds=max_rounds)
|
||||
l.createText(s.talon, "sw")
|
||||
x += l.XS
|
||||
|
|
|
@ -43,6 +43,7 @@ from pysollib.game import Game
|
|||
from pysollib.layout import Layout
|
||||
from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint
|
||||
from pysollib.hint import YukonType_Hint
|
||||
from pysollib.hint import FreeCellSolverWrapper
|
||||
from pysollib.pysoltk import MfxCanvasText
|
||||
|
||||
from spider import Spider_SS_Foundation
|
||||
|
|
451
pysollib/hint.py
|
@ -36,8 +36,10 @@
|
|||
|
||||
# imports
|
||||
import os, sys
|
||||
import time
|
||||
|
||||
# PySol imports
|
||||
from settings import USE_FREECELL_SOLVER, FCS_COMMAND
|
||||
from mfxutil import destruct
|
||||
from util import KING
|
||||
|
||||
|
@ -689,289 +691,234 @@ class SpiderType_Hint(DefaultHint):
|
|||
|
||||
|
||||
# /***********************************************************************
|
||||
# //
|
||||
# // FreeCell-Solver
|
||||
# ************************************************************************/
|
||||
|
||||
FreecellSolver = None
|
||||
|
||||
## try:
|
||||
## import FreecellSolver
|
||||
## except:
|
||||
## FreecellSolver = None
|
||||
class FreeCellSolver_Hint:
|
||||
|
||||
fcs_command = 'fc-solve'
|
||||
if os.name == 'nt':
|
||||
if sys.path[0] and not os.path.isdir(sys.path[0]): # i.e. library.zip
|
||||
fcs_command = os.path.join(os.path.split(sys.path[0])[0], 'fc-solve.exe')
|
||||
fcs_command = '"%s"' % fcs_command
|
||||
def __init__(self, game, dialog, **game_type):
|
||||
self.game = game
|
||||
self.dialog = dialog
|
||||
self.game_type = game_type
|
||||
self.options = {
|
||||
'method': 'dfs',
|
||||
'max_iters': 10000,
|
||||
'max_depth': 1000,
|
||||
'progress': False,
|
||||
'preset': None,
|
||||
}
|
||||
self.hints = []
|
||||
self.hints_index = 0
|
||||
|
||||
if os.name in ('posix', 'nt'):
|
||||
try:
|
||||
pin, pout, perr = os.popen3(fcs_command+' --help')
|
||||
if pout.readline().startswith('fc-solve'):
|
||||
FreecellSolver = True
|
||||
del pin, pout, perr
|
||||
if os.name == 'posix':
|
||||
os.wait()
|
||||
except:
|
||||
##traceback.print_exc()
|
||||
pass
|
||||
# correct cards rank if foundations.base_rank != 0 (Penguin, Opus)
|
||||
if 'base_rank' in game_type: # (Simple Simon)
|
||||
self.base_rank = game_type['base_rank']
|
||||
else:
|
||||
self.base_rank = game.s.foundations[0].cap.base_rank
|
||||
##print 'game_type:', game_type
|
||||
##print 'base_rank:', self.base_rank
|
||||
|
||||
def config(self, **kw):
|
||||
self.options.update(kw)
|
||||
|
||||
|
||||
class FreeCellSolverWrapper:
|
||||
__name__ = 'FreeCellSolverWrapper' # for gameinfodialog
|
||||
def card2str1(self, card):
|
||||
# row and reserves
|
||||
rank = (card.rank-self.base_rank) % 13
|
||||
return "A23456789TJQK"[rank] + "CSHD"[card.suit]
|
||||
def card2str2(self, card):
|
||||
# foundations
|
||||
rank = (card.rank-self.base_rank) % 13
|
||||
return "CSHD"[card.suit] + "-" + "A23456789TJQK"[rank]
|
||||
|
||||
class FreeCellSolver_Hint(AbstractHint):
|
||||
def str1(self, card):
|
||||
return "A23456789TJQK"[card.rank] + "CSHD"[card.suit]
|
||||
def str2(self, card):
|
||||
return "CSHD"[card.suit] + "-" + "A23456789TJQK"[card.rank]
|
||||
# hard solvable: Freecell #47038300998351211829 (65539 iters)
|
||||
|
||||
def getHints(self, taken_hint=None):
|
||||
if taken_hint and taken_hint[6]:
|
||||
return [taken_hint[6]]
|
||||
h = self.hints[self.hints_index]
|
||||
#print 'getHints', taken_hint, h
|
||||
if h is None:
|
||||
return None
|
||||
ncards, src, dest = h
|
||||
thint = None
|
||||
if len(src.cards) > ncards and not src.cards[-ncards-1].face_up:
|
||||
# flip card
|
||||
thint = (999999, 0, 1, src, src, None, None)
|
||||
if dest == None: # foundation
|
||||
cards = src.cards[-ncards:]
|
||||
for f in self.game.s.foundations:
|
||||
if f.acceptsCards(src, cards):
|
||||
dest = f
|
||||
break
|
||||
assert dest
|
||||
hint = (999999, 0, ncards, src, dest, None, thint)
|
||||
self.hints_index += 1
|
||||
#print hint
|
||||
return [hint]
|
||||
|
||||
def computeHints(self):
|
||||
##print 'FreeCellSolver_Hint.computeHints'
|
||||
|
||||
board = ''
|
||||
pboard = {}
|
||||
#
|
||||
def computeHints(self):
|
||||
game = self.game
|
||||
game_type = self.game_type
|
||||
progress = self.options['progress']
|
||||
board = ''
|
||||
#
|
||||
#
|
||||
b = ''
|
||||
for s in self.game.s.foundations:
|
||||
if s.cards:
|
||||
ss = self.card2str2(s.cards[-1])
|
||||
b += ' ' + ss
|
||||
if b:
|
||||
board += 'Founds:' + b + '\n'
|
||||
#
|
||||
b = ''
|
||||
for s in self.game.s.reserves:
|
||||
if s.cards:
|
||||
cs = self.card2str1(s.cards[-1])
|
||||
b += ' ' + cs
|
||||
else:
|
||||
b += ' -'
|
||||
if b:
|
||||
board += 'FC:' + b + '\n'
|
||||
#
|
||||
for s in self.game.s.rows:
|
||||
b = ''
|
||||
#l = []
|
||||
for s in self.game.s.foundations:
|
||||
if s.cards:
|
||||
b = b + ' ' + self.str2(s.cards[-1])
|
||||
#l.append(self.str2(s.cards[-1]))
|
||||
if b:
|
||||
board = board + 'Founds:' + b + '\n'
|
||||
#pboard['Founds'] = l
|
||||
#
|
||||
b = ''
|
||||
l = []
|
||||
for s in self.game.s.reserves:
|
||||
if s.cards:
|
||||
cs = self.str1(s.cards[-1])
|
||||
b = b + ' ' + cs
|
||||
l.append(cs)
|
||||
else:
|
||||
b = b + ' -'
|
||||
l.append(None)
|
||||
if b:
|
||||
board = board + 'FC:' + b + '\n'
|
||||
pboard['FC'] = l
|
||||
#
|
||||
n = 0
|
||||
for s in self.game.s.rows:
|
||||
b = ''
|
||||
l = []
|
||||
for c in s.cards:
|
||||
cs = self.str1(c)
|
||||
if not c.face_up:
|
||||
cs = '<%s>' % cs
|
||||
b = b + cs + ' '
|
||||
l.append(cs)
|
||||
board = board + b.strip() + '\n'
|
||||
pboard[n] = l
|
||||
n += 1
|
||||
#
|
||||
##print board
|
||||
#
|
||||
args = []
|
||||
args += ['-sam', '-p', '--display-10-as-t']
|
||||
##args += ['-l', 'good-intentions']
|
||||
args += ['--max-iters', 200000]
|
||||
args += ['--decks-num', self.fcs_args[0],
|
||||
'--stacks-num', self.fcs_args[1],
|
||||
'--freecells-num', self.fcs_args[2],
|
||||
]
|
||||
#
|
||||
game_type = self.fcs_args[3]
|
||||
if 'sbb' in game_type:
|
||||
args += ['--sequences-are-built-by', game_type['sbb']]
|
||||
if 'sm' in game_type:
|
||||
args += ['--sequence-move', game_type['sm']]
|
||||
if 'esf' in game_type:
|
||||
args += ['--empty-stacks-filled-by', game_type['esf']]
|
||||
if 'preset' in game_type:
|
||||
args += ['--preset', game_type['preset']]
|
||||
for c in s.cards:
|
||||
cs = self.card2str1(c)
|
||||
if not c.face_up:
|
||||
cs = '<%s>' % cs
|
||||
b += cs + ' '
|
||||
board = board + b.strip() + '\n'
|
||||
#
|
||||
##print '--------------------\n', board, '--------------------'
|
||||
#
|
||||
args = []
|
||||
##args += ['-sam', '-p', '-opt', '--display-10-as-t']
|
||||
args += ['-m', '-p', '-opt']
|
||||
if self.options['preset'] and self.options['preset'] != 'none':
|
||||
args += ['-l', self.options['preset']]
|
||||
if progress:
|
||||
args += ['--iter-output']
|
||||
##args += ['-s']
|
||||
args += ['--max-iters', self.options['max_iters'],
|
||||
'--max-depth', self.options['max_depth'],
|
||||
'--method', self.options['method'],
|
||||
'--decks-num', game.gameinfo.decks,
|
||||
'--stacks-num', len(game.s.rows),
|
||||
'--freecells-num', len(game.s.reserves),
|
||||
]
|
||||
#
|
||||
if 'preset' in game_type:
|
||||
args += ['--preset', game_type['preset']]
|
||||
if 'sbb' in game_type:
|
||||
args += ['--sequences-are-built-by', game_type['sbb']]
|
||||
if 'sm' in game_type:
|
||||
args += ['--sequence-move', game_type['sm']]
|
||||
if 'esf' in game_type:
|
||||
args += ['--empty-stacks-filled-by', game_type['esf']]
|
||||
|
||||
command = fcs_command+' '+' '.join([str(i) for i in args])
|
||||
##print command
|
||||
pin, pout, perr = os.popen3(command)
|
||||
pin.write(board)
|
||||
pin.close()
|
||||
#
|
||||
stack_types = {
|
||||
'the' : self.game.s.foundations,
|
||||
'stack' : self.game.s.rows,
|
||||
'freecell' : self.game.s.reserves,
|
||||
}
|
||||
my_hints = []
|
||||
pboard_n = 0
|
||||
##print pboard
|
||||
command = FCS_COMMAND+' '+' '.join([str(i) for i in args])
|
||||
##print command
|
||||
pin, pout, perr = os.popen3(command)
|
||||
pin.write(board)
|
||||
pin.close()
|
||||
#
|
||||
stack_types = {
|
||||
'the' : game.s.foundations,
|
||||
'stack' : game.s.rows,
|
||||
'freecell' : game.s.reserves,
|
||||
}
|
||||
##start_time = time.time()
|
||||
if progress:
|
||||
# iteration output
|
||||
iter = 0
|
||||
depth = 0
|
||||
states = 0
|
||||
for s in pout:
|
||||
##print s,
|
||||
if not s.startswith('Move'):
|
||||
if s.startswith('Freecells:'):
|
||||
ss=s[10:]
|
||||
pboard['FC'] = [ss[i:i+4].strip() for i in range(0, len(ss), 4)]
|
||||
elif s.startswith(':'):
|
||||
pboard[pboard_n] = s.split()[1:]
|
||||
pboard_n += 1
|
||||
continue
|
||||
if s.startswith('Iter'):
|
||||
#print `s`
|
||||
iter = int(s[10:-1])
|
||||
elif s.startswith('Depth'):
|
||||
#print s,
|
||||
depth = int(s[6:-1])
|
||||
elif s.startswith('Stor'):
|
||||
#print s,
|
||||
states = int(s[14:-1])
|
||||
if iter % 100 == 0:
|
||||
self.dialog.setText(iter=iter, depth=depth,
|
||||
states=states)
|
||||
elif s.startswith('-=-') or \
|
||||
s.startswith('I co'): # "I could not solve this game."
|
||||
break
|
||||
self.dialog.setText(iter=iter, depth=depth, states=states)
|
||||
|
||||
words = s.split()
|
||||
ncards = words[1]
|
||||
if ncards == 'a':
|
||||
ncards = 1
|
||||
else:
|
||||
ncards = int(ncards)
|
||||
hints = []
|
||||
for s in pout:
|
||||
#print s,
|
||||
# TODO:
|
||||
# Total number of states checked is 6.
|
||||
# This scan generated 6 states.
|
||||
|
||||
sn = int(words[5])
|
||||
if not s.startswith('Move'):
|
||||
continue
|
||||
|
||||
words = s.split()
|
||||
ncards = words[1]
|
||||
if ncards == 'the':
|
||||
# "Move the sequence on top of Stack 1 to the foundations"
|
||||
# (Simple Simon)
|
||||
ncards = 0
|
||||
elif ncards == 'a':
|
||||
ncards = 1
|
||||
else:
|
||||
ncards = int(ncards)
|
||||
|
||||
if ncards:
|
||||
st = stack_types[words[4]]
|
||||
src = st[sn]
|
||||
|
||||
sn = int(words[5])
|
||||
src = st[sn] # source stack
|
||||
if words[7] == 'the':
|
||||
# to foundation
|
||||
if words[4] == 'stack':
|
||||
# from rows
|
||||
card = pboard[sn][-1]
|
||||
else:
|
||||
# from reserves
|
||||
card = pboard['FC'][sn]
|
||||
suit = 'CSHD'.index(card[1])
|
||||
##dest = self.game.s.foundations[suit]
|
||||
dest = None
|
||||
for f in self.game.s.foundations:
|
||||
if f.cap.base_suit == suit:
|
||||
dest = f
|
||||
break
|
||||
assert dest is not None
|
||||
|
||||
else:
|
||||
# to rows or reserves
|
||||
dt = stack_types[words[7]]
|
||||
dn = int(words[8])
|
||||
dest = dt[dn]
|
||||
else: # move sequence
|
||||
ncards = 13
|
||||
st = stack_types['stack']
|
||||
sn = int(words[7])
|
||||
src = st[sn]
|
||||
dest = None
|
||||
|
||||
my_hints.append([ncards, src, dest])
|
||||
##print src, dest, ncards
|
||||
hints.append([ncards, src, dest])
|
||||
##print src, dest, ncards
|
||||
|
||||
pboard = {}
|
||||
pboard_n = 0
|
||||
#
|
||||
##print 'time:', time.time()-start_time
|
||||
##print perr.read(),
|
||||
|
||||
my_hints.reverse()
|
||||
hint = None
|
||||
for i in my_hints:
|
||||
hint = (999999, 0, i[0], i[1], i[2], None, hint)
|
||||
if hint:
|
||||
self.hints.append(hint)
|
||||
##print self.hints
|
||||
if os.name == 'posix':
|
||||
os.wait()
|
||||
self.hints = hints
|
||||
self.hints.append(None) # XXX
|
||||
|
||||
##print self.hints
|
||||
|
||||
pout.close()
|
||||
perr.close()
|
||||
if os.name == 'posix':
|
||||
os.wait()
|
||||
|
||||
|
||||
def computeHints_mod(self):
|
||||
board = ""
|
||||
#
|
||||
b = ""
|
||||
for s in self.game.s.foundations:
|
||||
if s.cards:
|
||||
b = b + " " + self.str2(s.cards[-1])
|
||||
if b:
|
||||
board = board + "Founds:" + b + "\n"
|
||||
#
|
||||
b = ""
|
||||
for s in self.game.s.reserves:
|
||||
if s.cards:
|
||||
b = b + " " + self.str1(s.cards[-1])
|
||||
else:
|
||||
b = b + " -"
|
||||
if b:
|
||||
board = board + "FC:" + b + "\n"
|
||||
#
|
||||
for s in self.game.s.rows:
|
||||
b = ""
|
||||
for c in s.cards:
|
||||
b = b + self.str1(c) + " "
|
||||
board = board + b.strip() + "\n"
|
||||
#
|
||||
##print board
|
||||
# solver = apply(FreecellSolver.FCSolver, self.fcs_args)
|
||||
solver = FreecellSolver.alloc()
|
||||
solver.config(["-l", "good-intentions"]);
|
||||
solver.config(["--decks-num", self.fcs_args[0],
|
||||
"--stacks-num", self.fcs_args[1],
|
||||
"--freecells-num", self.fcs_args[2],
|
||||
])
|
||||
class FreeCellSolverWrapper:
|
||||
|
||||
game_type = self.fcs_args[3]
|
||||
game_type_defaults = {'sbb' : 'alternate_color', 'sm' : 'limited', 'esf': 'all'}
|
||||
for k,v in game_type_defaults.items():
|
||||
if k not in game_type:
|
||||
game_type[k] = v
|
||||
|
||||
solver.config(["--sequences-are-built-by", game_type['sbb'],
|
||||
"--sequence-move", game_type['sm'],
|
||||
"--empty-stacks-filled-by", game_type['esf'],
|
||||
])
|
||||
def __init__(self, **game_type):
|
||||
self.game_type = game_type
|
||||
|
||||
solver.limit_iterations(200000)
|
||||
|
||||
h = solver.solve(board)
|
||||
if (h == "solved"):
|
||||
move = solver.get_next_move()
|
||||
hint = None
|
||||
founds_map = [2,0,3,1,6,4,7,5]
|
||||
my_hints = []
|
||||
while (move):
|
||||
print move
|
||||
|
||||
if (move['type'] == "s2s"):
|
||||
ncards = move['num_cards']
|
||||
else:
|
||||
ncards = 1
|
||||
|
||||
src_idx = move['src']
|
||||
if move['type'] in ("s2s", "s2found", "s2f"):
|
||||
src = self.game.s.rows[src_idx]
|
||||
else:
|
||||
src = self.game.s.reserves[src_idx]
|
||||
|
||||
d_idx = move['dest']
|
||||
if move['type'] in ("s2s", "f2s"):
|
||||
dest = self.game.s.rows[d_idx]
|
||||
elif move['type'] in ("s2f", "f2f"):
|
||||
dest = self.game.s.reserves[d_idx]
|
||||
else:
|
||||
dest = self.game.s.foundations[founds_map[d_idx]]
|
||||
|
||||
# hint = (999999, 0, ncards, src, dest, None, 0)
|
||||
my_hints.append([ncards, src, dest])
|
||||
# self.hints.append(hint)
|
||||
move = solver.get_next_move();
|
||||
hint = None
|
||||
my_hints.reverse()
|
||||
for i in my_hints:
|
||||
hint = (999999, 0, i[0], i[1], i[2], None, hint)
|
||||
self.hints.append(hint)
|
||||
#i = len(h)
|
||||
#assert i % 3 == 0
|
||||
#hint = None
|
||||
#map = self.game.s.foundations + self.game.s.rows + self.game.s.reserves
|
||||
#while i > 0:
|
||||
# i = i - 3
|
||||
# v = struct.unpack("<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
|
||||
def __call__(self, game, dialog):
|
||||
hint = FreeCellSolver_Hint(game, dialog, **self.game_type)
|
||||
return hint
|
||||
|
||||
|
|
|
@ -206,7 +206,7 @@ class Images:
|
|||
pass
|
||||
if progress: progress.update(step=pstep)
|
||||
# shadow
|
||||
if TOOLKIT == 'tk' and Image:
|
||||
if 0 and TOOLKIT == 'tk' and Image:
|
||||
fn = self.d.findImage('shadow', 'images')
|
||||
self._pil_shadow_image = Image.open(fn).convert('RGBA')
|
||||
else:
|
||||
|
@ -302,7 +302,6 @@ class Images:
|
|||
y0, y1 = min(y1, y0), max(y1, y0)
|
||||
x1 = x1 + self.CARDW
|
||||
y1 = y1 + self.CARDH
|
||||
#xx0, yy0 = x0, y0
|
||||
w, h = x1-x0, y1-y0
|
||||
if (w,h) in self._pil_shadow:
|
||||
return self._pil_shadow[(w,h)]
|
||||
|
|
|
@ -76,7 +76,7 @@ def init():
|
|||
if settings.TOOLKIT == 'tk':
|
||||
import Tkinter
|
||||
from Tkinter import TclError
|
||||
root = Tkinter.Tk()
|
||||
root = Tkinter.Tk(className='PySol')
|
||||
settings.WIN_SYSTEM = root.tk.call('tk', 'windowingsystem')
|
||||
if settings.WIN_SYSTEM == 'aqua':
|
||||
# TkAqua displays the console automatically in application
|
||||
|
@ -97,7 +97,35 @@ def init():
|
|||
# "can't invoke event <<ThemeChanged>>: application has been destroyed"
|
||||
#root.destroy()
|
||||
Tkinter._default_root = None
|
||||
#
|
||||
|
||||
# check FreeCell-Solver
|
||||
settings.USE_FREECELL_SOLVER = False
|
||||
if os.name == 'nt':
|
||||
if sys.path[0] and not os.path.isdir(sys.path[0]): # i.e. library.zip
|
||||
d = os.path.dirname(sys.path[0])
|
||||
##d = os.path.join(d, 'freecell-solver')
|
||||
fcs_command = os.path.join('freecell-solver', 'fc-solve.exe')
|
||||
##fcs_command = '"%s"' % fcs_command # quote command
|
||||
settings.FCS_COMMAND = fcs_command
|
||||
f = os.path.join(d, 'freecell-solver', 'presetrc')
|
||||
os.environ['FREECELL_SOLVER_PRESETRC'] = f
|
||||
os.chdir(d) # for read presets
|
||||
##print >> file('/fcs.log', 'a'), d
|
||||
##print >> file('/fcs.log', 'a'), f
|
||||
if os.name in ('posix', 'nt'):
|
||||
try:
|
||||
pin, pout, perr = os.popen3(settings.FCS_COMMAND+' --help')
|
||||
if pout.readline().startswith('fc-solve'):
|
||||
settings.USE_FREECELL_SOLVER = True
|
||||
del pin, pout, perr
|
||||
if os.name == 'posix':
|
||||
os.wait() # kill zombi
|
||||
except:
|
||||
##traceback.print_exc()
|
||||
pass
|
||||
os.environ['FREECELL_SOLVER_QUIET'] = '1'
|
||||
|
||||
# run app without games menus (more fast start)
|
||||
if '--no-games-menu' in sys.argv:
|
||||
sys.argv.remove('--no-games-menu')
|
||||
settings.SELECT_GAME_MENU = False
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
|
||||
# imports
|
||||
import os, time, types
|
||||
import webbrowser
|
||||
|
||||
try:
|
||||
from cPickle import Pickler, Unpickler, UnpicklingError
|
||||
|
@ -319,10 +320,10 @@ def unpickle(filename):
|
|||
|
||||
def openURL(url):
|
||||
try:
|
||||
import webbrowser
|
||||
webbrowser.open(url)
|
||||
return 1
|
||||
except ImportError: # FIXME
|
||||
except OSError: # raised on windows if link is unreadable
|
||||
pass
|
||||
except:
|
||||
return 0
|
||||
|
||||
return 1
|
||||
|
||||
|
|
|
@ -79,26 +79,31 @@ class AMoveMove(AtomicMove):
|
|||
self.shadow = shadow
|
||||
|
||||
# do the actual move
|
||||
def __doMove(self, game, ncards, from_stack, to_stack):
|
||||
def _doMove(self, game, ncards, from_stack, to_stack):
|
||||
if game.moves.state == game.S_PLAY:
|
||||
assert to_stack.acceptsCards(from_stack, from_stack.cards[-ncards:])
|
||||
frames = self.frames
|
||||
if frames == -2 and game.moves.state not in (game.S_UNDO, game.S_REDO):
|
||||
# don't use animation for drag-move
|
||||
frames = 0
|
||||
cards = from_stack.cards[-ncards:]
|
||||
if self.frames != 0:
|
||||
if frames != 0:
|
||||
from_stack.unshadeStack()
|
||||
x, y = to_stack.getPositionForNextCard()
|
||||
game.animatedMoveTo(from_stack, to_stack, cards, x, y,
|
||||
frames=self.frames, shadow=self.shadow)
|
||||
frames=frames, shadow=self.shadow)
|
||||
for i in range(ncards):
|
||||
from_stack.removeCard()
|
||||
for c in cards:
|
||||
to_stack.addCard(c)
|
||||
|
||||
def redo(self, game):
|
||||
self.__doMove(game, self.ncards, game.allstacks[self.from_stack_id],
|
||||
game.allstacks[self.to_stack_id])
|
||||
self._doMove(game, self.ncards, game.allstacks[self.from_stack_id],
|
||||
game.allstacks[self.to_stack_id])
|
||||
|
||||
def undo(self, game):
|
||||
self.__doMove(game, self.ncards, game.allstacks[self.to_stack_id],
|
||||
game.allstacks[self.from_stack_id])
|
||||
self._doMove(game, self.ncards, game.allstacks[self.to_stack_id],
|
||||
game.allstacks[self.from_stack_id])
|
||||
|
||||
def cmpForRedo(self, other):
|
||||
return (cmp(self.ncards, other.ncards) or
|
||||
|
@ -115,7 +120,26 @@ class AFlipMove(AtomicMove):
|
|||
self.stack_id = stack.id
|
||||
|
||||
# do the actual move
|
||||
def __doMove(self, game, stack):
|
||||
def _doMove(self, game, stack):
|
||||
card = stack.cards[-1]
|
||||
##game.animatedFlip(stack)
|
||||
if card.face_up:
|
||||
card.showBack()
|
||||
else:
|
||||
card.showFace()
|
||||
|
||||
def redo(self, game):
|
||||
self._doMove(game, game.allstacks[self.stack_id])
|
||||
|
||||
def undo(self, game):
|
||||
self._doMove(game, game.allstacks[self.stack_id])
|
||||
|
||||
def cmpForRedo(self, other):
|
||||
return cmp(self.stack_id, other.stack_id)
|
||||
|
||||
# flip with animation
|
||||
class ASingleFlipMove(AFlipMove):
|
||||
def _doMove(self, game, stack):
|
||||
card = stack.cards[-1]
|
||||
game.animatedFlip(stack)
|
||||
if card.face_up:
|
||||
|
@ -123,14 +147,46 @@ class AFlipMove(AtomicMove):
|
|||
else:
|
||||
card.showFace()
|
||||
|
||||
# flip and move one card
|
||||
class AFlipAndMoveMove(AtomicMove):
|
||||
|
||||
def __init__(self, from_stack, to_stack, frames):
|
||||
assert from_stack is not to_stack
|
||||
self.from_stack_id = from_stack.id
|
||||
self.to_stack_id = to_stack.id
|
||||
self.frames = frames
|
||||
|
||||
def _doMove(self, game, from_stack, to_stack):
|
||||
if game.moves.state == game.S_PLAY:
|
||||
assert to_stack.acceptsCards(from_stack, from_stack.cards[-1])
|
||||
if self.frames == 0:
|
||||
moved = True
|
||||
else:
|
||||
moved = game.animatedFlipAndMove(from_stack, to_stack, self.frames)
|
||||
c = from_stack.cards[-1]
|
||||
if c.face_up:
|
||||
c.showBack()
|
||||
else:
|
||||
c.showFace()
|
||||
if not moved:
|
||||
cards = from_stack.cards[-1:]
|
||||
x, y = to_stack.getPositionForNextCard()
|
||||
game.animatedMoveTo(from_stack, to_stack, cards, x, y,
|
||||
frames=self.frames, shadow=0)
|
||||
c = from_stack.removeCard()
|
||||
to_stack.addCard(c)
|
||||
|
||||
def redo(self, game):
|
||||
self.__doMove(game, game.allstacks[self.stack_id])
|
||||
self._doMove(game, game.allstacks[self.from_stack_id],
|
||||
game.allstacks[self.to_stack_id])
|
||||
|
||||
def undo(self, game):
|
||||
self.__doMove(game, game.allstacks[self.stack_id])
|
||||
self._doMove(game, game.allstacks[self.to_stack_id],
|
||||
game.allstacks[self.from_stack_id])
|
||||
|
||||
def cmpForRedo(self, other):
|
||||
return cmp(self.stack_id, other.stack_id)
|
||||
return (cmp(self.from_stack_id, other.from_stack_id) or
|
||||
cmp(self.to_stack_id, other.to_stack_id))
|
||||
|
||||
|
||||
# /***********************************************************************
|
||||
|
@ -246,7 +302,7 @@ class NEW_ATurnStackMove(AtomicMove):
|
|||
self.update_flags = update_flags
|
||||
|
||||
# do the actual turning move
|
||||
def __doMove(self, from_stack, to_stack, show_face):
|
||||
def _doMove(self, from_stack, to_stack, show_face):
|
||||
assert len(from_stack.cards) > 0
|
||||
assert len(to_stack.cards) == 0
|
||||
for card in from_stack.cards:
|
||||
|
@ -272,7 +328,7 @@ class NEW_ATurnStackMove(AtomicMove):
|
|||
assert to_stack is game.s.talon
|
||||
assert to_stack.round < to_stack.max_rounds or to_stack.max_rounds < 0
|
||||
to_stack.round = to_stack.round + 1
|
||||
self.__doMove(from_stack, to_stack, 0)
|
||||
self._doMove(from_stack, to_stack, 0)
|
||||
|
||||
def undo(self, game):
|
||||
from_stack = game.allstacks[self.from_stack_id]
|
||||
|
@ -281,7 +337,7 @@ class NEW_ATurnStackMove(AtomicMove):
|
|||
assert to_stack is game.s.talon
|
||||
assert to_stack.round > 1
|
||||
to_stack.round = to_stack.round - 1
|
||||
self.__doMove(to_stack, from_stack, 1)
|
||||
self._doMove(to_stack, from_stack, 1)
|
||||
|
||||
def cmpForRedo(self, other):
|
||||
return (cmp(self.from_stack_id, other.from_stack_id) or
|
||||
|
@ -300,7 +356,7 @@ class AUpdateStackMove(AtomicMove):
|
|||
self.flags = flags
|
||||
|
||||
# do the actual move
|
||||
def __doMove(self, game, stack, undo):
|
||||
def _doMove(self, game, stack, undo):
|
||||
if self.flags & 64:
|
||||
# model
|
||||
stack.updateModel(undo, self.flags)
|
||||
|
@ -313,11 +369,11 @@ class AUpdateStackMove(AtomicMove):
|
|||
|
||||
def redo(self, game):
|
||||
if (self.flags & 3) in (1, 3):
|
||||
self.__doMove(game, game.allstacks[self.stack_id], 0)
|
||||
self._doMove(game, game.allstacks[self.stack_id], 0)
|
||||
|
||||
def undo(self, game):
|
||||
if (self.flags & 3) in (2, 3):
|
||||
self.__doMove(game, game.allstacks[self.stack_id], 1)
|
||||
self._doMove(game, game.allstacks[self.stack_id], 1)
|
||||
|
||||
def cmpForRedo(self, other):
|
||||
return cmp(self.stack_id, other.stack_id) or cmp(self.flags, other.flags)
|
||||
|
|
|
@ -59,8 +59,6 @@ class ColorsDialog:
|
|||
self._setColor(n, app.opt.colors[n])
|
||||
button = self.widgets_tree.get_widget(n+'_button')
|
||||
button.connect('clicked', self._changeColor, n)
|
||||
checkbutton = self.widgets_tree.get_widget('use_default_checkbutton')
|
||||
checkbutton.set_active(not app.opt.use_default_text_color)
|
||||
|
||||
self._translateLabels()
|
||||
|
||||
|
@ -79,7 +77,6 @@ class ColorsDialog:
|
|||
w = self.widgets_tree.get_widget(n+'_label')
|
||||
c = w.get_data('user_data')
|
||||
setattr(self, n+'_color', c)
|
||||
self.use_default_color = not checkbutton.get_active()
|
||||
|
||||
dialog.destroy()
|
||||
|
||||
|
@ -125,9 +122,8 @@ class ColorsDialog:
|
|||
'label51',
|
||||
'label52',
|
||||
'label53',
|
||||
'label79',
|
||||
):
|
||||
w = self.widgets_tree.get_widget(n)
|
||||
w.set_text(gettext(w.get_text()))
|
||||
w = self.widgets_tree.get_widget('use_default_checkbutton')
|
||||
w.set_label(gettext(w.get_label()))
|
||||
|
||||
|
|
|
@ -289,10 +289,11 @@ class PysolMenubar(PysolMenubarActions):
|
|||
#
|
||||
animations_entries = (
|
||||
('animationnone', None, ltk2gtk('&None'), None, None, 0),
|
||||
('animationfast', None, ltk2gtk('&Fast'), None, None, 1),
|
||||
('animationtimer', None, ltk2gtk('&Timer based'), None, None, 2),
|
||||
('animationslow', None, ltk2gtk('&Slow'), None, None, 3),
|
||||
('animationveryslow', None, ltk2gtk('&Very slow'), None, None, 4),
|
||||
('animationveryfast', None, ltk2gtk('&Very fast'), None, None, 1),
|
||||
('animationfast', None, ltk2gtk('&Fast'), None, None, 2),
|
||||
('animationmedium', None, ltk2gtk('&Medium'), None, None, 3),
|
||||
('animationslow', None, ltk2gtk('&Slow'), None, None, 4),
|
||||
('animationveryslow', None, ltk2gtk('V&ery slow'), None, None, 5),
|
||||
)
|
||||
mouse_entries = (
|
||||
('draganddrop', None, ltk2gtk('&Drag-and-Drop'), None, None, 0),
|
||||
|
@ -388,8 +389,9 @@ class PysolMenubar(PysolMenubarActions):
|
|||
<menuitem action='tabletile'/>
|
||||
<menu action='animations'>
|
||||
<menuitem action='animationnone'/>
|
||||
<menuitem action='animationtimer'/>
|
||||
<menuitem action='animationveryfast'/>
|
||||
<menuitem action='animationfast'/>
|
||||
<menuitem action='animationmedium'/>
|
||||
<menuitem action='animationslow'/>
|
||||
<menuitem action='animationveryslow'/>
|
||||
</menu>
|
||||
|
@ -831,7 +833,6 @@ class PysolMenubar(PysolMenubarActions):
|
|||
self._cancelDrag()
|
||||
self.game.endGame(bookmark=1)
|
||||
self.game.quitGame(bookmark=1)
|
||||
self.app.opt.games_geometry = {} # clear saved games geometry
|
||||
|
||||
|
||||
def mOptToggle(self, w, opt_name, update_game):
|
||||
|
|
|
@ -389,7 +389,7 @@ class SelectGameDialogWithPreview(MfxDialog):
|
|||
destruct(self.preview_app)
|
||||
self.preview_app = None
|
||||
|
||||
def updatePreview(self, gameid, animations=5):
|
||||
def updatePreview(self, gameid, animations=10):
|
||||
if gameid == self.preview_key:
|
||||
return
|
||||
self.deletePreview()
|
||||
|
@ -456,7 +456,7 @@ class SelectGameDialogWithPreview(MfxDialog):
|
|||
#
|
||||
self.preview_app.audio = self.app.audio
|
||||
if self.app.opt.animations:
|
||||
self.preview_app.opt.animations = 5
|
||||
self.preview_app.opt.animations = 10
|
||||
else:
|
||||
self.preview_app.opt.animations = 0
|
||||
# save seed
|
||||
|
@ -514,14 +514,13 @@ class SelectGameDialogWithPreview(MfxDialog):
|
|||
('percent', percent),
|
||||
):
|
||||
title_label, text_label = self.info_labels[n]
|
||||
if t == '':
|
||||
if t in ('', None):
|
||||
title_label.hide()
|
||||
text_label.hide()
|
||||
else:
|
||||
title_label.show()
|
||||
text_label.show()
|
||||
text_label.set_text(str(t))
|
||||
#self.info_labels[n].config(text=t)
|
||||
|
||||
def done(self, button):
|
||||
button = button.get_data("user_data")
|
||||
|
|
42
pysollib/pysolgtk/solverdialog.py
Normal 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
|
||||
|
|
@ -445,16 +445,11 @@ class MfxCanvas(gnomecanvas.Canvas):
|
|||
self.setBackgroundImage(None)
|
||||
self.configure(bg=tile.color)
|
||||
##app.top.config(bg=tile.color)
|
||||
color = None
|
||||
else:
|
||||
self._setTile()
|
||||
self.configure(bg=self.top_bg)
|
||||
color = tile.text_color
|
||||
|
||||
if app.opt.use_default_text_color:
|
||||
self.setTextColor(color)
|
||||
else:
|
||||
self.setTextColor(app.opt.colors['text'])
|
||||
self.setTextColor(app.opt.colors['text'])
|
||||
|
||||
return True
|
||||
|
||||
|
|
|
@ -566,7 +566,9 @@ class Status_StatsDialog(MfxMessageDialog): #MfxDialog
|
|||
)
|
||||
|
||||
|
||||
|
||||
class ProgressionDialog:
|
||||
# FIXME
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ if TOOLKIT == 'tk':
|
|||
from tile.colorsdialog import *
|
||||
from tile.fontsdialog import *
|
||||
from tile.findcarddialog import *
|
||||
from tile.solverdialog import *
|
||||
from tile.gameinfodialog import *
|
||||
from tile.toolbar import *
|
||||
from tile.statusbar import *
|
||||
|
@ -60,6 +61,7 @@ if TOOLKIT == 'tk':
|
|||
from tk.colorsdialog import *
|
||||
from tk.fontsdialog import *
|
||||
from tk.findcarddialog import *
|
||||
from tk.solverdialog import *
|
||||
from tk.gameinfodialog import *
|
||||
from tk.toolbar import *
|
||||
from tk.statusbar import *
|
||||
|
@ -84,6 +86,7 @@ else: # gtk
|
|||
from pysolgtk.colorsdialog import *
|
||||
from pysolgtk.fontsdialog import *
|
||||
from pysolgtk.findcarddialog import *
|
||||
from pysolgtk.solverdialog import *
|
||||
from pysolgtk.gameinfodialog import *
|
||||
from pysolgtk.toolbar import *
|
||||
from pysolgtk.statusbar import *
|
||||
|
|
|
@ -497,7 +497,6 @@ class CardsetManager(ResourceManager):
|
|||
class Tile(Resource):
|
||||
def __init__(self, **kw):
|
||||
kw['color'] = None
|
||||
kw['text_color'] = "#000000"
|
||||
kw['stretch'] = 0
|
||||
Resource.__init__(self, **kw)
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ PACKAGE = 'PySol'
|
|||
PACKAGE_URL = 'http://sourceforge.net/projects/pysolfc/'
|
||||
|
||||
VERSION = '4.82'
|
||||
FC_VERSION = '0.9.5'
|
||||
FC_VERSION = '1.0'
|
||||
VERSION_TUPLE = (4, 82)
|
||||
|
||||
# Tk windowing system (auto determine in init.py)
|
||||
|
@ -48,6 +48,11 @@ USE_TILE = 'auto' # or True or False
|
|||
# 'none' - disable
|
||||
SOUND_MOD = 'auto'
|
||||
|
||||
# freecell-solver
|
||||
USE_FREECELL_SOLVER = True
|
||||
FCS_COMMAND = 'fc-solve'
|
||||
##FCS_HOME = None # path to fcs presets files
|
||||
|
||||
# data dirs
|
||||
DATA_DIRS = []
|
||||
# you can add your extra directories here
|
||||
|
|
|
@ -57,7 +57,7 @@ __all__ = ['cardsFaceUp',
|
|||
'SS_FoundationStack',
|
||||
'RK_FoundationStack',
|
||||
'AC_FoundationStack',
|
||||
'SequenceStack_StackMethods',
|
||||
#'SequenceStack_StackMethods',
|
||||
'BasicRowStack',
|
||||
'SequenceRowStack',
|
||||
'AC_RowStack',
|
||||
|
@ -71,6 +71,7 @@ __all__ = ['cardsFaceUp',
|
|||
'UD_RK_RowStack',
|
||||
'FreeCell_AC_RowStack',
|
||||
'FreeCell_SS_RowStack',
|
||||
'FreeCell_RK_RowStack',
|
||||
'Spider_AC_RowStack',
|
||||
'Spider_SS_RowStack',
|
||||
'Yukon_AC_RowStack',
|
||||
|
@ -83,6 +84,10 @@ __all__ = ['cardsFaceUp',
|
|||
'FaceUpWasteTalonStack',
|
||||
'OpenTalonStack',
|
||||
'ReserveStack',
|
||||
'SuperMoveStack_StackMethods',
|
||||
'SuperMoveSS_RowStack',
|
||||
'SuperMoveAC_RowStack',
|
||||
'SuperMoveRK_RowStack',
|
||||
'InvisibleStack',
|
||||
'StackWrapper',
|
||||
'WeakStackWrapper',
|
||||
|
@ -342,6 +347,7 @@ class Stack:
|
|||
bind(group, "<3>", self.__rightclickEventHandler)
|
||||
bind(group, "<2>", self.__middleclickEventHandler)
|
||||
bind(group, "<Control-3>", self.__middleclickEventHandler)
|
||||
##bind(group, "<Control-2>", self.__controlmiddleclickEventHandler)
|
||||
##bind(group, "<Shift-3>", self.__shiftrightclickEventHandler)
|
||||
##bind(group, "<Double-2>", "")
|
||||
bind(group, "<Enter>", self.__enterEventHandler)
|
||||
|
@ -489,9 +495,8 @@ class Stack:
|
|||
|
||||
if update:
|
||||
view.updateText()
|
||||
if self.is_filled:
|
||||
self._unshadeStack()
|
||||
self.is_filled = False
|
||||
self.unshadeStack()
|
||||
self.is_filled = False
|
||||
return card
|
||||
|
||||
# Get the top card {model}
|
||||
|
@ -652,9 +657,12 @@ class Stack:
|
|||
# Atomic move actions {model -> view}
|
||||
#
|
||||
|
||||
def flipMove(self):
|
||||
def flipMove(self, animation=False):
|
||||
# Flip the top card.
|
||||
self.game.flipMove(self)
|
||||
if animation:
|
||||
self.game.singleFlipMove(self)
|
||||
else:
|
||||
self.game.flipMove(self)
|
||||
|
||||
def moveMove(self, ncards, to_stack, frames=-1, shadow=-1):
|
||||
# Move the top n cards.
|
||||
|
@ -671,10 +679,10 @@ class Stack:
|
|||
# Playing move actions. Better not override.
|
||||
#
|
||||
|
||||
def playFlipMove(self, sound=1):
|
||||
def playFlipMove(self, sound=1, animation=False):
|
||||
if sound:
|
||||
self.game.playSample("flip", 5)
|
||||
self.flipMove()
|
||||
self.flipMove(animation=animation)
|
||||
if not self.game.checkForWin():
|
||||
self.game.autoPlay()
|
||||
self.game.finishMove()
|
||||
|
@ -917,6 +925,32 @@ class Stack:
|
|||
self.game.canvas.update_idletasks()
|
||||
return 1
|
||||
|
||||
def controlmiddleclickHandler(self, event):
|
||||
# cheating: show face-down card
|
||||
if not self.is_open:
|
||||
return 0
|
||||
i = self._findCard(event)
|
||||
positions = len(self.cards) - i - 1
|
||||
if i < 0 or positions < 0:
|
||||
return 0
|
||||
##print self.cards[i]
|
||||
face_up = self.cards[i].face_up
|
||||
if not face_up:
|
||||
self.cards[i].showFace()
|
||||
self.cards[i].item.tkraise()
|
||||
self.game.canvas.update_idletasks()
|
||||
self.game.sleep(self.game.app.opt.timeouts['raise_card'])
|
||||
if not face_up:
|
||||
self.cards[i].showBack()
|
||||
if TOOLKIT == 'tk':
|
||||
if positions > 0:
|
||||
self.cards[i].item.lower(self.cards[i+1].item)
|
||||
elif TOOLKIT == 'gtk':
|
||||
for c in self.cards[i+1:]:
|
||||
c.tkraise()
|
||||
self.game.canvas.update_idletasks()
|
||||
return 1
|
||||
|
||||
def rightclickHandler(self, event):
|
||||
return 0
|
||||
|
||||
|
@ -946,8 +980,25 @@ class Stack:
|
|||
self.moveCardsBackHandler(event, drag)
|
||||
|
||||
def moveCardsBackHandler(self, event, drag):
|
||||
if self.game.app.opt.animations:
|
||||
if drag.cards:
|
||||
c = drag.cards[0]
|
||||
x0, y0 = drag.stack.getPositionFor(c)
|
||||
x1, y1 = c.x, c.y
|
||||
dx, dy = abs(x0-x1), abs(y0-y1)
|
||||
w = self.game.app.images.CARDW
|
||||
h = self.game.app.images.CARDH
|
||||
if dx > 2*w or dy > 2*h:
|
||||
self.game.animatedMoveTo(drag.stack, drag.stack,
|
||||
drag.cards, x0, y0, frames=-1)
|
||||
elif dx > w or dy > h:
|
||||
self.game.animatedMoveTo(drag.stack, drag.stack,
|
||||
drag.cards, x0, y0, frames=4)
|
||||
for card in drag.cards:
|
||||
self._position(card)
|
||||
if self.is_filled and self.items.shade_item:
|
||||
self.items.shade_item.show()
|
||||
self.items.shade_item.tkraise()
|
||||
|
||||
|
||||
#
|
||||
|
@ -993,6 +1044,9 @@ class Stack:
|
|||
def __middleclickEventHandler(self, event):
|
||||
return self.__defaultClickEventHandler(event, self.middleclickHandler)
|
||||
|
||||
def __controlmiddleclickEventHandler(self, event):
|
||||
return self.__defaultClickEventHandler(event, self.controlmiddleclickHandler)
|
||||
|
||||
def __rightclickEventHandler(self, event):
|
||||
return self.__defaultClickEventHandler(event, self.rightclickHandler)
|
||||
|
||||
|
@ -1049,8 +1103,11 @@ class Stack:
|
|||
self.current_cursor = CURSOR_DOWN_ARROW
|
||||
self.cursor_changed = True
|
||||
else:
|
||||
help = self.getHelp() ##+' '+self.getBaseCard(),
|
||||
if DEBUG >= 5:
|
||||
help = repr(self)
|
||||
after_idle(self.canvas, self.game.showHelp,
|
||||
'help', self.getHelp(), ##+' '+self.getBaseCard(),
|
||||
'help', help,
|
||||
'info', self.getNumCards())
|
||||
return EVENT_HANDLED
|
||||
|
||||
|
@ -1173,7 +1230,7 @@ class Stack:
|
|||
images = self.game.app.images
|
||||
cx, cy = cards[0].x, cards[0].y
|
||||
ddx, ddy = cx-cards[-1].x, cy-cards[-1].y
|
||||
if TOOLKIT == 'tk' and Image: # use PIL
|
||||
if 0 and TOOLKIT == 'tk' and Image: # use PIL
|
||||
c0 = cards[-1]
|
||||
if self.CARD_XOFFSET[0] < 0: c0 = cards[0]
|
||||
if self.CARD_YOFFSET[0] < 0: c0 = cards[0]
|
||||
|
@ -1303,7 +1360,7 @@ class Stack:
|
|||
#item.tkraise()
|
||||
self.items.shade_item = item
|
||||
|
||||
def _unshadeStack(self):
|
||||
def unshadeStack(self):
|
||||
if self.items.shade_item:
|
||||
self.items.shade_item.delete()
|
||||
self.items.shade_item = None
|
||||
|
@ -1350,19 +1407,19 @@ class Stack:
|
|||
group=self.group)
|
||||
drag.shadows.append(im)
|
||||
|
||||
def _setMotionCursor(self, event):
|
||||
if not event:
|
||||
return
|
||||
self.cursor_changed = True
|
||||
i = self._findCard(event)
|
||||
if i < 0 or not self.canMoveCards(self.cards[i:]):
|
||||
if self.current_cursor != CURSOR_NO_MOVE:
|
||||
self.game.canvas.config(cursor=CURSOR_NO_MOVE)
|
||||
self.current_cursor = CURSOR_NO_MOVE
|
||||
else:
|
||||
if self.current_cursor != CURSOR_CAN_MOVE:
|
||||
self.game.canvas.config(cursor=CURSOR_CAN_MOVE)
|
||||
self.current_cursor = CURSOR_CAN_MOVE
|
||||
## def _setMotionCursor(self, event):
|
||||
## if not event:
|
||||
## return
|
||||
## self.cursor_changed = True
|
||||
## i = self._findCard(event)
|
||||
## if i < 0 or not self.canMoveCards(self.cards[i:]):
|
||||
## if self.current_cursor != CURSOR_NO_MOVE:
|
||||
## self.game.canvas.config(cursor=CURSOR_NO_MOVE)
|
||||
## self.current_cursor = CURSOR_NO_MOVE
|
||||
## else:
|
||||
## if self.current_cursor != CURSOR_CAN_MOVE:
|
||||
## self.game.canvas.config(cursor=CURSOR_CAN_MOVE)
|
||||
## self.current_cursor = CURSOR_CAN_MOVE
|
||||
|
||||
def _stopDrag(self):
|
||||
drag = self.game.drag
|
||||
|
@ -1376,16 +1433,16 @@ class Stack:
|
|||
drag.shadows = []
|
||||
drag.stack = None
|
||||
drag.cards = []
|
||||
if self.is_filled and self.items.shade_item:
|
||||
self.items.shade_item.show()
|
||||
self.items.shade_item.tkraise()
|
||||
|
||||
# finish a drag operation
|
||||
def finishDrag(self, event=None):
|
||||
if self.game.app.opt.dragcursor:
|
||||
self.game.canvas.config(cursor='')
|
||||
drag = self.game.drag.copy()
|
||||
self._stopDrag()
|
||||
if self.game.app.opt.mouse_type == 'point-n-click':
|
||||
drag.stack._stopDrag()
|
||||
else:
|
||||
self._stopDrag()
|
||||
if drag.cards:
|
||||
if self.game.app.opt.mouse_type == 'point-n-click':
|
||||
self.releaseHandler(event, drag)
|
||||
|
@ -1398,7 +1455,10 @@ class Stack:
|
|||
if self.game.app.opt.dragcursor:
|
||||
self.game.canvas.config(cursor='')
|
||||
drag = self.game.drag.copy()
|
||||
self._stopDrag()
|
||||
if self.game.app.opt.mouse_type == 'point-n-click':
|
||||
drag.stack._stopDrag()
|
||||
else:
|
||||
self._stopDrag()
|
||||
if drag.cards:
|
||||
assert drag.stack is self
|
||||
self.moveCardsBackHandler(event, drag)
|
||||
|
@ -1410,6 +1470,7 @@ class Stack:
|
|||
return ''
|
||||
|
||||
def _getBaseCard(self):
|
||||
# FIXME: no-french games
|
||||
if self.cap.max_accept == 0:
|
||||
return ''
|
||||
br = self.cap.base_rank
|
||||
|
@ -1424,14 +1485,10 @@ class Stack:
|
|||
return s
|
||||
|
||||
def getNumCards(self):
|
||||
if DEBUG >= 5:
|
||||
t = repr(self)+' '
|
||||
else:
|
||||
t = ''
|
||||
n = len(self.cards)
|
||||
if n == 0 : return t+_('No cards')
|
||||
elif n == 1 : return t+_('1 card')
|
||||
else : return t+str(n)+_(' cards')
|
||||
if n == 0 : return _('No cards')
|
||||
elif n == 1 : return _('1 card')
|
||||
else : return str(n)+_(' cards')
|
||||
|
||||
|
||||
# /***********************************************************************
|
||||
|
@ -1831,8 +1888,9 @@ class OpenStack(Stack):
|
|||
def clickHandler(self, event):
|
||||
flipstacks, dropstacks, quickstacks = self.game.getAutoStacks(event)
|
||||
if self in flipstacks and self.canFlipCard():
|
||||
self.playFlipMove()
|
||||
return -1 # continue this event (start a drag)
|
||||
self.playFlipMove(animation=True)
|
||||
##return -1 # continue this event (start a drag)
|
||||
return 1 # break
|
||||
return 0
|
||||
|
||||
def rightclickHandler(self, event):
|
||||
|
@ -1850,7 +1908,7 @@ class OpenStack(Stack):
|
|||
# flip or drop a card
|
||||
flipstacks, dropstacks, quickstacks = self.game.getAutoStacks(event)
|
||||
if self in flipstacks and self.canFlipCard():
|
||||
self.playFlipMove()
|
||||
self.playFlipMove(animation=True)
|
||||
return -1 # continue this event (start a drag)
|
||||
if self in dropstacks:
|
||||
to_stack, ncards = self.canDropCards(self.game.s.foundations)
|
||||
|
@ -1870,7 +1928,8 @@ class OpenStack(Stack):
|
|||
if self.game.app.opt.mouse_type == 'point-n-click':
|
||||
self.playMoveMove(len(drag.cards), stack, sound=sound)
|
||||
else:
|
||||
self.playMoveMove(len(drag.cards), stack, frames=0, sound=sound)
|
||||
#self.playMoveMove(len(drag.cards), stack, frames=0, sound=sound)
|
||||
self.playMoveMove(len(drag.cards), stack, frames=-2, sound=sound)
|
||||
|
||||
def releaseHandler(self, event, drag, sound=1):
|
||||
cards = drag.cards
|
||||
|
@ -2168,6 +2227,12 @@ class FreeCell_SS_RowStack(SS_RowStack):
|
|||
max_move = getNumberOfFreeStacks(self.game.s.reserves) + 1
|
||||
return len(cards) <= max_move and SS_RowStack.canMoveCards(self, cards)
|
||||
|
||||
# A Freecell_Rank_RowStack
|
||||
class FreeCell_RK_RowStack(RK_RowStack):
|
||||
def canMoveCards(self, cards):
|
||||
max_move = getNumberOfFreeStacks(self.game.s.reserves) + 1
|
||||
return len(cards) <= max_move and RK_RowStack.canMoveCards(self, cards)
|
||||
|
||||
# A Spider_AlternateColor_RowStack builds down by rank and alternate color,
|
||||
# but accepts sequences that match by rank only.
|
||||
class Spider_AC_RowStack(AC_RowStack):
|
||||
|
@ -2291,6 +2356,73 @@ class UD_RK_RowStack(SequenceRowStack):
|
|||
|
||||
|
||||
|
||||
# To simplify playing we also consider the number of free rows.
|
||||
# Note that this only is legal if the game.s.rows have a
|
||||
# cap.base_rank == ANY_RANK.
|
||||
# See also the "SuperMove" section in the FreeCell FAQ.
|
||||
class SuperMoveStack_StackMethods:
|
||||
def _getMaxMove(self, to_stack_ncards):
|
||||
max_move = getNumberOfFreeStacks(self.game.s.reserves) + 1
|
||||
if self.cap.base_rank != ANY_RANK:
|
||||
return max_move
|
||||
n = getNumberOfFreeStacks(self.game.s.rows)
|
||||
if to_stack_ncards == 0:
|
||||
n = n - 1
|
||||
max_move = max_move * (2 ** n)
|
||||
return max_move
|
||||
def _getNumSSSeq(self, cards):
|
||||
# num of same-suit sequences (for SuperMoveSpider_RowStack)
|
||||
if not cards:
|
||||
return 0
|
||||
mod = self.cap.mod
|
||||
dir = self.cap.dir
|
||||
n = 1
|
||||
rank = cards[-1].rank
|
||||
suit = cards[-1].suit
|
||||
for c in cards[-2::-1]:
|
||||
if c.suit != suit:
|
||||
suit = c.suit
|
||||
n += 1
|
||||
return n
|
||||
|
||||
|
||||
class SuperMoveSS_RowStack(SuperMoveStack_StackMethods, SS_RowStack):
|
||||
def canMoveCards(self, cards):
|
||||
if not SS_RowStack.canMoveCards(self, cards):
|
||||
return False
|
||||
max_move = self._getMaxMove(1)
|
||||
return len(cards) <= max_move
|
||||
def acceptsCards(self, from_stack, cards):
|
||||
if not SS_RowStack.acceptsCards(self, from_stack, cards):
|
||||
return False
|
||||
max_move = self._getMaxMove(len(self.cards))
|
||||
return len(cards) <= max_move
|
||||
|
||||
class SuperMoveAC_RowStack(SuperMoveStack_StackMethods, AC_RowStack):
|
||||
def canMoveCards(self, cards):
|
||||
if not AC_RowStack.canMoveCards(self, cards):
|
||||
return False
|
||||
max_move = self._getMaxMove(1)
|
||||
return len(cards) <= max_move
|
||||
def acceptsCards(self, from_stack, cards):
|
||||
if not AC_RowStack.acceptsCards(self, from_stack, cards):
|
||||
return False
|
||||
max_move = self._getMaxMove(len(self.cards))
|
||||
return len(cards) <= max_move
|
||||
|
||||
class SuperMoveRK_RowStack(SuperMoveStack_StackMethods, RK_RowStack):
|
||||
def canMoveCards(self, cards):
|
||||
if not RK_RowStack.canMoveCards(self, cards):
|
||||
return False
|
||||
max_move = self._getMaxMove(1)
|
||||
return len(cards) <= max_move
|
||||
def acceptsCards(self, from_stack, cards):
|
||||
if not RK_RowStack.acceptsCards(self, from_stack, cards):
|
||||
return False
|
||||
max_move = self._getMaxMove(len(self.cards))
|
||||
return len(cards) <= max_move
|
||||
|
||||
|
||||
|
||||
# /***********************************************************************
|
||||
# // WasteStack (a helper stack for the Talon, e.g. in Klondike)
|
||||
|
@ -2334,8 +2466,13 @@ class WasteTalonStack(TalonStack):
|
|||
assert len(waste.cards) + num_cards <= waste.cap.max_cards
|
||||
for i in range(num_cards):
|
||||
if not self.cards[-1].face_up:
|
||||
self.game.flipMove(self)
|
||||
self.game.moveMove(1, self, waste, frames=4, shadow=0)
|
||||
if 1:
|
||||
self.game.flipAndMoveMove(self, waste)
|
||||
else:
|
||||
self.game.flipMove(self)
|
||||
self.game.moveMove(1, self, waste, frames=4, shadow=0)
|
||||
else:
|
||||
self.game.moveMove(1, self, waste, frames=4, shadow=0)
|
||||
self.fillStack()
|
||||
elif waste.cards and self.round != self.max_rounds:
|
||||
if sound:
|
||||
|
@ -2355,6 +2492,11 @@ class FaceUpWasteTalonStack(WasteTalonStack):
|
|||
self.game.flipMove(self)
|
||||
self.game.fillStack(self)
|
||||
|
||||
def dealCards(self, sound=0):
|
||||
WasteTalonStack.dealCards(self, sound=sound)
|
||||
if self.canFlipCard():
|
||||
self.flipMove()
|
||||
|
||||
|
||||
class OpenTalonStack(TalonStack, OpenStack):
|
||||
canMoveCards = OpenStack.canMoveCards
|
||||
|
@ -2460,7 +2602,7 @@ class ArbitraryStack(OpenStack):
|
|||
# flip or drop a card
|
||||
flipstacks, dropstacks, quickstacks = self.game.getAutoStacks(event)
|
||||
if self in flipstacks and self.canFlipCard():
|
||||
self.playFlipMove()
|
||||
self.playFlipMove(animation=True)
|
||||
return -1 # continue this event (start a drag)
|
||||
if self in dropstacks:
|
||||
i = self._findCard(event)
|
||||
|
|
|
@ -243,3 +243,126 @@ class FileStatsFormatter(PysolStatsFormatter):
|
|||
prev_games = self.app.stats.session_games.get(player)
|
||||
return self.writeLog(player, header, prev_games)
|
||||
|
||||
|
||||
# /***********************************************************************
|
||||
# //
|
||||
# ************************************************************************/
|
||||
|
||||
class ProgressionFormatter:
|
||||
|
||||
def __init__(self, app, player, gameid):
|
||||
|
||||
all_results = {} # key: (year, month, day); value: [played, won]
|
||||
self.all_results = all_results
|
||||
game_results = {}
|
||||
self.game_results = game_results
|
||||
games = app.stats.prev_games.get(player)
|
||||
if not games:
|
||||
return
|
||||
for g in games:
|
||||
id = g[0]
|
||||
status = g[2]
|
||||
start_time = g[3]
|
||||
t = time.localtime(start_time)[:3]
|
||||
if t not in all_results:
|
||||
all_results[t] = [0,0]
|
||||
all_results[t][0] += 1
|
||||
if status > 0:
|
||||
all_results[t][1] += 1
|
||||
if id == gameid:
|
||||
if t not in game_results:
|
||||
game_results[t] = [0,0]
|
||||
game_results[t][0] += 1
|
||||
if status > 0:
|
||||
game_results[t][1] += 1
|
||||
##from pprint import pprint; pprint(all_results)
|
||||
|
||||
def norm_time(self, t):
|
||||
if len(t) == 3:
|
||||
t = list(t)+[0,0,0,-1,-1,-1]
|
||||
return list(time.localtime(time.mktime((t))))
|
||||
|
||||
def getResults(self, interval, all_games=True):
|
||||
if all_games:
|
||||
results = self.all_results
|
||||
else:
|
||||
results = self.game_results
|
||||
t = list(time.localtime())
|
||||
if interval == 'week':
|
||||
t[2] -= 7
|
||||
lt = self.norm_time(t)
|
||||
marks = None
|
||||
delta = 1
|
||||
format = '%d.%m'
|
||||
elif interval == 'month':
|
||||
tt = t[:]
|
||||
t[1] -= 1
|
||||
lt = self.norm_time(t)
|
||||
marks = [lt[:3], tt[:3]]
|
||||
tt[2] -= 10
|
||||
marks.append(self.norm_time(tt)[:3])
|
||||
tt[2] -= 10
|
||||
marks.append(self.norm_time(tt)[:3])
|
||||
delta = 1
|
||||
format = '%d.%m'
|
||||
elif interval == 'year':
|
||||
tt = t[:]
|
||||
t[0] -= 1
|
||||
lt = self.norm_time(t)
|
||||
marks = [lt[:3], tt[:3]]
|
||||
for i in xrange(5):
|
||||
tt[1] -= 2
|
||||
marks.append(self.norm_time(tt)[:3])
|
||||
delta = 7
|
||||
format = '%d.%m.%y'
|
||||
else: # all
|
||||
tt = t[:]
|
||||
tt[1] -= 1
|
||||
tt = self.norm_time(tt)
|
||||
if results:
|
||||
lt = self.norm_time(min(results.keys()))
|
||||
lt = min(lt, tt) # min 1 month
|
||||
else:
|
||||
lt = tt
|
||||
dt = time.time()-time.mktime(lt)
|
||||
if dt > 63072000: # 2 years
|
||||
d = 6
|
||||
elif dt > 31536000: # 1 year
|
||||
d = 4
|
||||
elif dt > 10512000: # 4 month
|
||||
d = 2
|
||||
else:
|
||||
d = 1
|
||||
marks = [lt[:3], t[:3]]
|
||||
while t > lt:
|
||||
t[1] -= d
|
||||
t = self.norm_time(t)
|
||||
marks.append(t[:3])
|
||||
delta = 7
|
||||
format = '%d.%m.%y'
|
||||
|
||||
res = []
|
||||
ct = list(time.localtime())
|
||||
while lt <= ct:
|
||||
##assert type(lt) is type(ct)
|
||||
sum = [0,0]
|
||||
played = 0
|
||||
won = 0
|
||||
text = None
|
||||
for i in xrange(delta):
|
||||
if marks:
|
||||
if ct[:3] in marks:
|
||||
text = time.strftime(format, ct)
|
||||
else:
|
||||
text = time.strftime(format, ct)
|
||||
t = tuple(ct[:3])
|
||||
if t in results:
|
||||
played += results[t][0]
|
||||
won += results[t][1]
|
||||
ct[2] -= 1
|
||||
ct = self.norm_time(ct)
|
||||
res.append((text, played, won))
|
||||
res.reverse()
|
||||
##from pprint import pprint; pprint(res)
|
||||
return res
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ Message = Tkinter.Message
|
|||
Listbox = Tkinter.Listbox
|
||||
Text = Tkinter.Text
|
||||
Canvas = Tkinter.Canvas
|
||||
Spinbox = Tkinter.Spinbox
|
||||
|
||||
PhotoImage = Tkinter.PhotoImage
|
||||
Event = Tkinter.Event
|
||||
|
|
|
@ -49,8 +49,6 @@ class ColorsDialog(MfxDialog):
|
|||
frame.pack(expand=True, fill='both', padx=5, pady=10)
|
||||
frame.columnconfigure(0, weight=1)
|
||||
|
||||
self.use_default_var = Tkinter.BooleanVar()
|
||||
self.use_default_var.set(not app.opt.use_default_text_color)
|
||||
self.text_var = Tkinter.StringVar()
|
||||
self.text_var.set(app.opt.colors['text'])
|
||||
self.piles_var = Tkinter.StringVar()
|
||||
|
@ -68,17 +66,9 @@ class ColorsDialog(MfxDialog):
|
|||
self.not_matching_var = Tkinter.StringVar()
|
||||
self.not_matching_var.set(app.opt.colors['not_matching'])
|
||||
#
|
||||
c = Tkinter.Checkbutton(frame, variable=self.use_default_var,
|
||||
text=_("Text foreground:"))
|
||||
c.grid(row=0, column=0, sticky='we')
|
||||
l = Tk.Label(frame, width=10, height=2,
|
||||
bg=self.text_var.get(), textvariable=self.text_var)
|
||||
l.grid(row=0, column=1, padx=5)
|
||||
b = Tkinter.Button(frame, text=_('Change...'), width=10,
|
||||
command=lambda l=l: self.selectColor(l))
|
||||
b.grid(row=0, column=2)
|
||||
row = 1
|
||||
row = 0
|
||||
for title, var in (
|
||||
(_('Text foreground:'), self.text_var),
|
||||
(_('Highlight piles:'), self.piles_var),
|
||||
(_('Highlight cards 1:'), self.cards_1_var),
|
||||
(_('Highlight cards 2:'), self.cards_2_var),
|
||||
|
@ -100,7 +90,6 @@ class ColorsDialog(MfxDialog):
|
|||
focus = self.createButtons(bottom_frame, kw)
|
||||
self.mainloop(focus, kw.timeout)
|
||||
#
|
||||
self.use_default_color = not self.use_default_var.get()
|
||||
self.text_color = self.text_var.get()
|
||||
self.piles_color = self.piles_var.get()
|
||||
self.cards_1_color = self.cards_1_var.get()
|
||||
|
|
|
@ -114,6 +114,7 @@ class FindCardDialog(Tkinter.Toplevel):
|
|||
self.groups.append(group)
|
||||
|
||||
def connectGame(self, game):
|
||||
self.canvas.delete('all')
|
||||
self.game = game
|
||||
suits = game.gameinfo.suits
|
||||
ranks = game.gameinfo.ranks
|
||||
|
@ -133,6 +134,7 @@ class FindCardDialog(Tkinter.Toplevel):
|
|||
w, h = dx*j+2, dy*i+2
|
||||
self.canvas.config(width=w, height=h)
|
||||
self.wm_iconname(PACKAGE + " - " + game.getTitleName())
|
||||
self.wm_geometry('') # cancel user-specified geometry
|
||||
|
||||
def enterEvent(self, suit, rank, rect, group):
|
||||
##print 'enterEvent', suit, rank, self.busy
|
||||
|
|
|
@ -48,6 +48,7 @@ from pysollib.util import CARDSET
|
|||
from pysollib.settings import PACKAGE, WIN_SYSTEM
|
||||
from pysollib.settings import TOP_TITLE
|
||||
from pysollib.settings import SELECT_GAME_MENU
|
||||
from pysollib.settings import USE_FREECELL_SOLVER
|
||||
from pysollib.gamedb import GI
|
||||
from pysollib.actions import PysolMenubarActions
|
||||
|
||||
|
@ -59,6 +60,7 @@ from soundoptionsdialog import SoundOptionsDialog
|
|||
from selectcardset import SelectCardsetDialogWithPreview
|
||||
from selecttile import SelectTileDialogWithPreview
|
||||
from findcarddialog import connect_game_find_card_dialog, destroy_find_card_dialog
|
||||
from solverdialog import connect_game_solver_dialog
|
||||
from tkwrap import MfxRadioMenuItem, MfxCheckMenuItem, StringVar
|
||||
from tkwidget import MfxMessageDialog
|
||||
|
||||
|
@ -139,25 +141,18 @@ class MfxMenubar(Tkinter.Menu):
|
|||
#print label, type(label)
|
||||
name = re.sub(r"[^0-9a-zA-Z]", "", label).lower()
|
||||
label = gettext(label)
|
||||
underline = -1
|
||||
m = re.search(r"^(.*)\&([^\&].*)$", label)
|
||||
if m:
|
||||
l1, l2 = m.group(1), m.group(2)
|
||||
l1 = re.sub(r"\&\&", "&", l1)
|
||||
l2 = re.sub(r"\&\&", "&", l2)
|
||||
label = l1 + l2
|
||||
underline = len(l1)
|
||||
underline = label.find('&')
|
||||
if underline >= 0:
|
||||
label = label.replace('&', '')
|
||||
return name, label, underline
|
||||
|
||||
def add(self, itemType, cnf={}):
|
||||
label = cnf.get("label")
|
||||
if label:
|
||||
name = cnf.get('name')
|
||||
try:
|
||||
if name:
|
||||
del cnf['name'] # TclError: unknown option "-name"
|
||||
except KeyError:
|
||||
pass
|
||||
if not name:
|
||||
else:
|
||||
name, label, underline = self.labeltoname(label)
|
||||
cnf["underline"] = cnf.get("underline", underline)
|
||||
cnf["label"] = label
|
||||
|
@ -323,6 +318,7 @@ class PysolMenubar(PysolMenubarActions):
|
|||
connect_game_find_card_dialog(game)
|
||||
else:
|
||||
destroy_find_card_dialog()
|
||||
connect_game_solver_dialog(game)
|
||||
|
||||
# create a GTK-like path
|
||||
def _addPath(self, path, menu, index, submenu):
|
||||
|
@ -371,7 +367,7 @@ class PysolMenubar(PysolMenubarActions):
|
|||
menu.add_separator()
|
||||
submenu = MfxMenu(menu, label=n_("Fa&vorite games"))
|
||||
menu.add_command(label=n_("A&dd to favorites"), command=self.mAddFavor)
|
||||
menu.add_command(label=n_("R&emove from favorites"), command=self.mDelFavor)
|
||||
menu.add_command(label=n_("Remove &from favorites"), command=self.mDelFavor)
|
||||
menu.add_separator()
|
||||
menu.add_command(label=n_("&Open..."), command=self.mOpen, accelerator=m+"O")
|
||||
menu.add_command(label=n_("&Save"), command=self.mSave, accelerator=m+"S")
|
||||
|
@ -414,7 +410,7 @@ class PysolMenubar(PysolMenubarActions):
|
|||
menu.add_checkbutton(label=n_("&Pause"), variable=self.tkopt.pause, command=self.mPause, accelerator="P")
|
||||
#menu.add_command(label=n_("&Pause"), command=self.mPause, accelerator="P")
|
||||
menu.add_separator()
|
||||
menu.add_command(label=n_("S&tatus..."), command=self.mStatus, accelerator="T")
|
||||
menu.add_command(label=n_("S&tatus..."), command=self.mStatus, accelerator=m+"Y")
|
||||
menu.add_checkbutton(label=n_("&Comments..."), variable=self.tkopt.comment, command=self.mEditGameComment)
|
||||
menu.add_separator()
|
||||
submenu = MfxMenu(menu, label=n_("&Statistics"))
|
||||
|
@ -425,6 +421,7 @@ class PysolMenubar(PysolMenubarActions):
|
|||
submenu.add_command(label=n_("Full log..."), command=lambda self=self: self.mPlayerStats(mode=103))
|
||||
submenu.add_separator()
|
||||
submenu.add_command(label=TOP_TITLE+"...", command=self.mTop10, accelerator=m+"T")
|
||||
submenu.add_command(label=n_("Progression..."), command=lambda self=self: self.mPlayerStats(mode=107))
|
||||
submenu = MfxMenu(menu, label=n_("D&emo statistics"))
|
||||
submenu.add_command(label=n_("Current game..."), command=lambda self=self: self.mPlayerStats(mode=1101))
|
||||
submenu.add_command(label=n_("All games..."), command=lambda self=self: self.mPlayerStats(mode=1102))
|
||||
|
@ -432,12 +429,16 @@ class PysolMenubar(PysolMenubarActions):
|
|||
menu = MfxMenu(self.__menubar, label=n_("&Assist"))
|
||||
menu.add_command(label=n_("&Hint"), command=self.mHint, accelerator="H")
|
||||
menu.add_command(label=n_("Highlight p&iles"), command=self.mHighlightPiles, accelerator="I")
|
||||
menu.add_command(label=n_("Find card"), command=self.mFindCard, accelerator="F")
|
||||
menu.add_command(label=n_("&Find card"), command=self.mFindCard, accelerator="F3")
|
||||
menu.add_separator()
|
||||
menu.add_command(label=n_("&Demo"), command=self.mDemo, accelerator=m+"D")
|
||||
menu.add_command(label=n_("Demo (&all games)"), command=self.mMixedDemo)
|
||||
if USE_FREECELL_SOLVER:
|
||||
menu.add_command(label=n_("&Solver (experimental)"), command=self.mSolver)
|
||||
else:
|
||||
menu.add_command(label=n_("&Solver (experimental)"), command=self.mSolver, state=Tkinter.DISABLED)
|
||||
menu.add_separator()
|
||||
menu.add_command(label=n_("Piles description"), command=self.mStackDesk, accelerator="F2")
|
||||
menu.add_command(label=n_("&Piles description"), command=self.mStackDesk, accelerator="F2")
|
||||
|
||||
if self.progress: self.progress.update(step=1)
|
||||
|
||||
|
@ -481,10 +482,11 @@ class PysolMenubar(PysolMenubarActions):
|
|||
submenu.add_checkbutton(label=n_("Shade &filled stacks"), variable=self.tkopt.shade_filled_stacks, command=self.mOptShadeFilledStacks)
|
||||
submenu = MfxMenu(menu, label=n_("A&nimations"))
|
||||
submenu.add_radiobutton(label=n_("&None"), variable=self.tkopt.animations, value=0, command=self.mOptAnimations)
|
||||
submenu.add_radiobutton(label=n_("&Timer based"), variable=self.tkopt.animations, value=2, command=self.mOptAnimations)
|
||||
submenu.add_radiobutton(label=n_("&Fast"), variable=self.tkopt.animations, value=1, command=self.mOptAnimations)
|
||||
submenu.add_radiobutton(label=n_("&Slow"), variable=self.tkopt.animations, value=3, command=self.mOptAnimations)
|
||||
submenu.add_radiobutton(label=n_("&Very slow"), variable=self.tkopt.animations, value=4, command=self.mOptAnimations)
|
||||
submenu.add_radiobutton(label=n_("&Very fast"), variable=self.tkopt.animations, value=1, command=self.mOptAnimations)
|
||||
submenu.add_radiobutton(label=n_("&Fast"), variable=self.tkopt.animations, value=2, command=self.mOptAnimations)
|
||||
submenu.add_radiobutton(label=n_("&Medium"), variable=self.tkopt.animations, value=3, command=self.mOptAnimations)
|
||||
submenu.add_radiobutton(label=n_("&Slow"), variable=self.tkopt.animations, value=4, command=self.mOptAnimations)
|
||||
submenu.add_radiobutton(label=n_("V&ery slow"), variable=self.tkopt.animations, value=5, command=self.mOptAnimations)
|
||||
submenu.add_separator()
|
||||
submenu.add_checkbutton(label=n_("&Redeal animation"), variable=self.tkopt.redeal_animation, command=self.mRedealAnimation)
|
||||
if Image:
|
||||
|
@ -531,15 +533,13 @@ class PysolMenubar(PysolMenubarActions):
|
|||
ctrl = "Control-"
|
||||
if sys.platform == "darwin": ctrl = "Command-"
|
||||
self._bindKey("", "n", self.mNewGame)
|
||||
self._bindKey("", "g", self.mSelectGameDialog)
|
||||
self._bindKey("", "v", self.mSelectGameDialogWithPreview)
|
||||
self._bindKey(ctrl, "w", self.mSelectGameDialog)
|
||||
self._bindKey(ctrl, "v", self.mSelectGameDialogWithPreview)
|
||||
self._bindKey(ctrl, "r", lambda e, self=self: self.mSelectRandomGame())
|
||||
self._bindKey(ctrl, "m", self.mSelectGameById)
|
||||
self._bindKey(ctrl, "n", self.mNewGameWithNextId)
|
||||
self._bindKey(ctrl, "o", self.mOpen)
|
||||
##self._bindKey("", "F3", self.mOpen) # undocumented
|
||||
self._bindKey(ctrl, "s", self.mSave)
|
||||
##self._bindKey("", "F2", self.mSaveAs) # undocumented
|
||||
self._bindKey(ctrl, "q", self.mQuit)
|
||||
self._bindKey("", "z", self.mUndo)
|
||||
self._bindKey("", "BackSpace", self.mUndo) # undocumented
|
||||
|
@ -547,14 +547,14 @@ class PysolMenubar(PysolMenubarActions):
|
|||
self._bindKey("", "r", self.mRedo)
|
||||
self._bindKey(ctrl, "g", self.mRestart)
|
||||
self._bindKey("", "space", self.mDeal) # undocumented
|
||||
self._bindKey("", "t", self.mStatus)
|
||||
self._bindKey(ctrl, "y", self.mStatus)
|
||||
self._bindKey(ctrl, "t", self.mTop10)
|
||||
self._bindKey("", "h", self.mHint)
|
||||
self._bindKey(ctrl, "h", self.mHint1) # undocumented
|
||||
##self._bindKey("", "Shift_L", self.mHighlightPiles)
|
||||
##self._bindKey("", "Shift_R", self.mHighlightPiles)
|
||||
self._bindKey("", "i", self.mHighlightPiles)
|
||||
self._bindKey("", "f", self.mFindCard)
|
||||
self._bindKey("", "F3", self.mFindCard)
|
||||
self._bindKey(ctrl, "d", self.mDemo)
|
||||
self._bindKey(ctrl, "e", self.mSelectCardsetDialog)
|
||||
self._bindKey(ctrl, "b", self.mOptChangeCardback) # undocumented
|
||||
|
@ -597,19 +597,19 @@ class PysolMenubar(PysolMenubarActions):
|
|||
#
|
||||
|
||||
def _bindKey(self, modifier, key, func):
|
||||
if 0 and not modifier and len(key) == 1:
|
||||
self.__keybindings[key.lower()] = func
|
||||
self.__keybindings[key.upper()] = func
|
||||
return
|
||||
## if 0 and not modifier and len(key) == 1:
|
||||
## self.__keybindings[key.lower()] = func
|
||||
## self.__keybindings[key.upper()] = func
|
||||
## return
|
||||
if not modifier and len(key) == 1:
|
||||
# ignore Ctrl/Shift/Alt
|
||||
func = lambda e, func=func: e.state == 0 and func(e)
|
||||
sequence = "<" + modifier + "KeyPress-" + key + ">"
|
||||
try:
|
||||
bind(self.top, sequence, func)
|
||||
if len(key) == 1 and key != key.upper():
|
||||
key = key.upper()
|
||||
sequence = "<" + modifier + "KeyPress-" + key + ">"
|
||||
bind(self.top, sequence, func)
|
||||
if len(key) == 1 and key != key.upper():
|
||||
key = key.upper()
|
||||
sequence = "<" + modifier + "KeyPress-" + key + ">"
|
||||
bind(self.top, sequence, func)
|
||||
except:
|
||||
raise
|
||||
|
||||
|
||||
def _keyPressHandler(self, event):
|
||||
|
@ -621,10 +621,10 @@ class PysolMenubar(PysolMenubarActions):
|
|||
if event.char: # ignore Ctrl/Shift/etc.
|
||||
self.game.demo.keypress = event.char
|
||||
r = EVENT_HANDLED
|
||||
func = self.__keybindings.get(event.char)
|
||||
if func and (event.state & ~2) == 0:
|
||||
func(event)
|
||||
r = EVENT_HANDLED
|
||||
## func = self.__keybindings.get(event.char)
|
||||
## if func and (event.state & ~2) == 0:
|
||||
## func(event)
|
||||
## r = EVENT_HANDLED
|
||||
return r
|
||||
|
||||
#
|
||||
|
@ -636,9 +636,11 @@ class PysolMenubar(PysolMenubarActions):
|
|||
games = map(self.app.gdb.get, self.app.gdb.getGamesIdSortedByName())
|
||||
##games = tuple(games)
|
||||
###menu = MfxMenu(menu, label="Select &game")
|
||||
menu.add_command(label=n_("All &games..."), accelerator="G",
|
||||
m = "Ctrl-"
|
||||
if sys.platform == "darwin": m = "Cmd-"
|
||||
menu.add_command(label=n_("All &games..."), accelerator=m+"W",
|
||||
command=self.mSelectGameDialog)
|
||||
menu.add_command(label=n_("Playable pre&view..."), accelerator="V",
|
||||
menu.add_command(label=n_("Playable pre&view..."), accelerator=m+"V",
|
||||
command=self.mSelectGameDialogWithPreview)
|
||||
if not SELECT_GAME_MENU:
|
||||
return
|
||||
|
@ -783,13 +785,19 @@ class PysolMenubar(PysolMenubarActions):
|
|||
gi = games[i]
|
||||
columnbreak = i > 0 and (i % cb) == 0
|
||||
if short_name:
|
||||
label = gi.short_name
|
||||
label = gettext(gi.short_name)
|
||||
else:
|
||||
label = gi.name
|
||||
menu.add_radiobutton(command=command, variable=variable,
|
||||
columnbreak=columnbreak,
|
||||
value=gi.id, label=label, name=None)
|
||||
|
||||
label = gettext(gi.name)
|
||||
## menu.add_radiobutton(command=command, variable=variable,
|
||||
## columnbreak=columnbreak,
|
||||
## value=gi.id, label=label, name=None)
|
||||
# optimized by inlining
|
||||
menu.tk.call((menu._w, 'add', 'radiobutton') +
|
||||
menu._options({'command': command,
|
||||
'variable': variable,
|
||||
'columnbreak': columnbreak,
|
||||
'value': gi.id,
|
||||
'label': label}))
|
||||
|
||||
#
|
||||
# Select Game menu actions
|
||||
|
@ -1148,7 +1156,6 @@ class PysolMenubar(PysolMenubarActions):
|
|||
self._cancelDrag()
|
||||
self.game.endGame(bookmark=1)
|
||||
self.game.quitGame(bookmark=1)
|
||||
self.app.opt.games_geometry = {} # clear saved games geometry
|
||||
|
||||
def _mOptCardback(self, index):
|
||||
if self._cancelDrag(break_pause=False): return
|
||||
|
@ -1328,13 +1335,13 @@ class PysolMenubar(PysolMenubarActions):
|
|||
|
||||
def mOptTheme(self, *event):
|
||||
theme = self.tkopt.theme.get()
|
||||
self.app.opt.tile_theme = theme
|
||||
d = MfxMessageDialog(self.top, title=_("Change theme"),
|
||||
text=_("""\
|
||||
This settings will take effect
|
||||
the next time you restart """)+PACKAGE,
|
||||
bitmap="warning",
|
||||
default=0, strings=(_("&OK"),))
|
||||
self.app.opt.tile_theme = theme
|
||||
|
||||
def createThemesMenu(self, menu):
|
||||
submenu = MfxMenu(menu, label=n_("Set t&heme"))
|
||||
|
@ -1343,12 +1350,12 @@ the next time you restart """)+PACKAGE,
|
|||
all_themes.sort()
|
||||
#
|
||||
tn = {
|
||||
'default': _('Default'),
|
||||
'classic': _('Classic'),
|
||||
'alt': _('Revitalized'),
|
||||
'winnative': _('Windows native'),
|
||||
'xpnative': _('XP Native'),
|
||||
'aqua': _('Aqua'),
|
||||
'default': n_('Default'),
|
||||
'classic': n_('Classic'),
|
||||
'alt': n_('Revitalized'),
|
||||
'winnative': n_('Windows native'),
|
||||
'xpnative': n_('XP Native'),
|
||||
'aqua': n_('Aqua'),
|
||||
}
|
||||
for t in all_themes:
|
||||
try:
|
||||
|
@ -1357,4 +1364,3 @@ the next time you restart """)+PACKAGE,
|
|||
n = t.capitalize()
|
||||
submenu.add_radiobutton(label=n, variable=self.tkopt.theme,
|
||||
value=t, command=self.mOptTheme)
|
||||
|
||||
|
|
|
@ -428,7 +428,7 @@ class SelectGameDialogWithPreview(SelectGameDialog):
|
|||
destruct(self.preview_app)
|
||||
self.preview_app = None
|
||||
|
||||
def updatePreview(self, gameid, animations=5):
|
||||
def updatePreview(self, gameid, animations=10):
|
||||
if gameid == self.preview_key:
|
||||
return
|
||||
self.deletePreview()
|
||||
|
@ -495,7 +495,7 @@ class SelectGameDialogWithPreview(SelectGameDialog):
|
|||
#
|
||||
self.preview_app.audio = self.app.audio
|
||||
if self.app.opt.animations:
|
||||
self.preview_app.opt.animations = 5
|
||||
self.preview_app.opt.animations = 10
|
||||
else:
|
||||
self.preview_app.opt.animations = 0
|
||||
# save seed
|
||||
|
@ -553,13 +553,12 @@ class SelectGameDialogWithPreview(SelectGameDialog):
|
|||
('percent', percent),
|
||||
):
|
||||
title_label, text_label = self.info_labels[n]
|
||||
if t == '':
|
||||
if t in ('', None):
|
||||
title_label.grid_remove()
|
||||
text_label.grid_remove()
|
||||
else:
|
||||
title_label.grid()
|
||||
text_label.grid()
|
||||
text_label.config(text=t)
|
||||
#self.info_labels[n].config(text=t)
|
||||
|
||||
|
||||
|
|
330
pysollib/tile/solverdialog.py
Normal 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
|
||||
|
|
@ -102,8 +102,11 @@ class MfxStatusbar:
|
|||
def updateText(self, **kw):
|
||||
for k, v in kw.items():
|
||||
label = getattr(self, k + "_label")
|
||||
#label["text"] = str(v)
|
||||
label["text"] = unicode(v)
|
||||
text = unicode(v)
|
||||
width = label['width']
|
||||
if width and len(text) > width:
|
||||
label['width'] = len(text)
|
||||
label["text"] = text
|
||||
|
||||
def configLabel(self, name, **kw):
|
||||
if 'fg' in kw:
|
||||
|
|
|
@ -371,21 +371,14 @@ class MfxCanvas(Tkinter.Canvas):
|
|||
|
||||
def _substitute(self, *args):
|
||||
e = Tkinter.Event()
|
||||
e.x = int(args[0])
|
||||
e.y = int(args[1])
|
||||
try:
|
||||
# Tk changed behavior in 8.4.2, returning "??" rather more often.
|
||||
e.x = int(args[0])
|
||||
except ValueError:
|
||||
e.x = args[0]
|
||||
try:
|
||||
e.y = int(args[1])
|
||||
except ValueError:
|
||||
e.y = args[1]
|
||||
return (e,)
|
||||
|
||||
|
||||
#
|
||||
# debug
|
||||
#
|
||||
|
||||
def update(self):
|
||||
##import mfxutil; print mfxutil.callername()
|
||||
# ??????
|
||||
Tkinter.Canvas.update(self)
|
||||
|
||||
def update_idletasks(self):
|
||||
##import mfxutil; print mfxutil.callername()
|
||||
Tkinter.Canvas.update_idletasks(self)
|
||||
|
||||
|
|
|
@ -38,10 +38,12 @@ __all__ = ['SingleGame_StatsDialog',
|
|||
'FullLog_StatsDialog',
|
||||
'SessionLog_StatsDialog',
|
||||
'Status_StatsDialog',
|
||||
'Top_StatsDialog']
|
||||
'Top_StatsDialog',
|
||||
'ProgressionDialog',
|
||||
]
|
||||
|
||||
# imports
|
||||
import os, string, sys, types
|
||||
import os
|
||||
import time
|
||||
import Tile as Tkinter
|
||||
import tkFont
|
||||
|
@ -50,7 +52,7 @@ import tkFont
|
|||
from pysollib.mfxutil import destruct, Struct, kwdefault, KwStruct
|
||||
from pysollib.mfxutil import format_time
|
||||
##from pysollib.util import *
|
||||
from pysollib.stats import PysolStatsFormatter
|
||||
from pysollib.stats import PysolStatsFormatter, ProgressionFormatter
|
||||
from pysollib.settings import TOP_TITLE
|
||||
|
||||
# Toolkit imports
|
||||
|
@ -233,6 +235,7 @@ class TreeFormatter(PysolStatsFormatter):
|
|||
self.tree = tree
|
||||
self.parent_window = parent_window
|
||||
self.font = font
|
||||
self.tkfont = tkFont.Font(tree, font)
|
||||
self.gameid = None
|
||||
self.gamenumber = None
|
||||
self._tabs = None
|
||||
|
@ -246,7 +249,8 @@ class TreeFormatter(PysolStatsFormatter):
|
|||
tw = 20*self.w
|
||||
##tw = 160
|
||||
self._tabs = [tw]
|
||||
font = tkFont.Font(self.tree, self.font)
|
||||
#font = tkFont.Font(self.tree, self.font)
|
||||
font = self.tkfont
|
||||
for t in arg[1:]:
|
||||
tw = font.measure(t)+20
|
||||
self._tabs.append(tw)
|
||||
|
@ -604,7 +608,7 @@ class Top_StatsDialog(MfxDialog):
|
|||
self.createBitmaps(top_frame, kw)
|
||||
|
||||
frame = Tkinter.Frame(top_frame)
|
||||
frame.pack(expand=Tkinter.YES, fill=Tkinter.BOTH, padx=5, pady=10)
|
||||
frame.pack(expand=Tkinter.YES, fill=Tkinter.BOTH, padx=10, pady=10)
|
||||
frame.columnconfigure(0, weight=1)
|
||||
|
||||
if (player in app.stats.games_stats and
|
||||
|
@ -681,3 +685,255 @@ class Top_StatsDialog(MfxDialog):
|
|||
separatorwidth=2,
|
||||
)
|
||||
return MfxDialog.initKw(self, kw)
|
||||
|
||||
|
||||
# /***********************************************************************
|
||||
# //
|
||||
# ************************************************************************/
|
||||
|
||||
class ProgressionDialog(MfxDialog):
|
||||
def __init__(self, parent, title, app, player, gameid, **kw):
|
||||
|
||||
font_name = app.getFont('default')
|
||||
font = tkFont.Font(parent, font_name)
|
||||
tkfont = tkFont.Font(parent, font)
|
||||
font_metrics = font.metrics()
|
||||
measure = tkfont.measure
|
||||
self.text_height = font_metrics['linespace']
|
||||
self.text_width = measure('XX.XX.XX')
|
||||
|
||||
self.items = []
|
||||
self.formatter = ProgressionFormatter(app, player, gameid)
|
||||
|
||||
kw = self.initKw(kw)
|
||||
MfxDialog.__init__(self, parent, title, kw.resizable, kw.default)
|
||||
top_frame, bottom_frame = self.createFrames(kw)
|
||||
self.createBitmaps(top_frame, kw)
|
||||
|
||||
frame = Tkinter.Frame(top_frame)
|
||||
frame.pack(expand=True, fill='both', padx=5, pady=10)
|
||||
frame.columnconfigure(0, weight=1)
|
||||
|
||||
# constants
|
||||
self.canvas_width, self.canvas_height = 600, 250
|
||||
if parent.winfo_screenwidth() < 800 or \
|
||||
parent.winfo_screenheight() < 600:
|
||||
self.canvas_width, self.canvas_height = 400, 200
|
||||
self.xmargin, self.ymargin = 10, 10
|
||||
self.graph_dx, self.graph_dy = 10, 10
|
||||
self.played_color = '#ff7ee9'
|
||||
self.won_color = '#00dc28'
|
||||
self.percent_color = 'blue'
|
||||
# create canvas
|
||||
self.canvas = canvas = Tkinter.Canvas(frame, bg='#dfe8ff', bd=0,
|
||||
highlightthickness=1,
|
||||
highlightbackground='black',
|
||||
width=self.canvas_width,
|
||||
height=self.canvas_height)
|
||||
canvas.pack(side='left', padx=5)
|
||||
#
|
||||
dir = os.path.join('images', 'stats')
|
||||
try:
|
||||
fn = app.dataloader.findImage('progression', dir)
|
||||
self.bg_image = loadImage(fn)
|
||||
canvas.create_image(0, 0, image=self.bg_image, anchor='nw')
|
||||
except:
|
||||
pass
|
||||
#
|
||||
tw = max(measure(_('Games/day')),
|
||||
measure(_('Games/week')),
|
||||
measure(_('% won')))
|
||||
self.left_margin = self.xmargin+tw/2
|
||||
self.right_margin = self.xmargin+tw/2
|
||||
self.top_margin = 15+self.text_height
|
||||
self.bottom_margin = 15+self.text_height+10+self.text_height
|
||||
#
|
||||
x0, y0 = self.left_margin, self.canvas_height-self.bottom_margin
|
||||
x1, y1 = self.canvas_width-self.right_margin, self.top_margin
|
||||
canvas.create_rectangle(x0, y0, x1, y1, fill='white')
|
||||
# horizontal axis
|
||||
canvas.create_line(x0, y0, x1, y0, width=3)
|
||||
|
||||
# left vertical axis
|
||||
canvas.create_line(x0, y0, x0, y1, width=3)
|
||||
t = _('Games/day')
|
||||
self.games_text_id = canvas.create_text(x0-4, y1-4, anchor='s', text=t)
|
||||
|
||||
# right vertical axis
|
||||
canvas.create_line(x1, y0, x1, y1, width=3)
|
||||
canvas.create_text(x1+4, y1-4, anchor='s', text=_('% won'))
|
||||
|
||||
# caption
|
||||
d = self.text_height
|
||||
x, y = self.xmargin, self.canvas_height-self.ymargin
|
||||
id = canvas.create_rectangle(x, y, x+d, y-d, outline='black',
|
||||
fill=self.played_color)
|
||||
x += d+5
|
||||
canvas.create_text(x, y, anchor='sw', text=_('Played'))
|
||||
x += measure(_('Played'))+20
|
||||
id = canvas.create_rectangle(x, y, x+d, y-d, outline='black',
|
||||
fill=self.won_color)
|
||||
x += d+5
|
||||
canvas.create_text(x, y, anchor='sw', text=_('Won'))
|
||||
x += measure(_('Won'))+20
|
||||
id = canvas.create_rectangle(x, y, x+d, y-d, outline='black',
|
||||
fill=self.percent_color)
|
||||
x += d+5
|
||||
canvas.create_text(x, y, anchor='sw', text=_('% won'))
|
||||
|
||||
# right frame
|
||||
right_frame = Tkinter.Frame(frame)
|
||||
right_frame.pack(side='left', fill='x', padx=5)
|
||||
self.all_games_variable = var = Tkinter.StringVar()
|
||||
var.set('all')
|
||||
b = Tkinter.Radiobutton(right_frame, text=_('All games'),
|
||||
variable=var, value='all',
|
||||
command=self.updateGraph,
|
||||
)
|
||||
b.pack(fill='x', expand=True, padx=3, pady=1)
|
||||
b = Tkinter.Radiobutton(right_frame, text=_('Current game'),
|
||||
variable=var, value='current',
|
||||
command=self.updateGraph,
|
||||
)
|
||||
b.pack(fill='x', expand=True, padx=3, pady=1)
|
||||
label_frame = Tkinter.LabelFrame(right_frame, text=_('Statistics for'))
|
||||
label_frame.pack(side='top', fill='x', pady=10)
|
||||
self.variable = var = Tkinter.StringVar()
|
||||
var.set('week')
|
||||
for v, t in (
|
||||
('week', _('Last 7 days')),
|
||||
('month', _('Last month')),
|
||||
('year', _('Last year')),
|
||||
('all', _('All time')),
|
||||
):
|
||||
b = Tkinter.Radiobutton(label_frame, text=t, variable=var, value=v,
|
||||
command=self.updateGraph,
|
||||
)
|
||||
b.pack(fill='x', expand=True, padx=3, pady=1)
|
||||
label_frame = Tkinter.LabelFrame(right_frame, text=_('Show graphs'))
|
||||
label_frame.pack(side='top', fill='x')
|
||||
self.played_graph_var = Tkinter.BooleanVar()
|
||||
self.played_graph_var.set(True)
|
||||
b = Tkinter.Checkbutton(label_frame, text=_('Played'),
|
||||
command=self.updateGraph,
|
||||
variable=self.played_graph_var,
|
||||
)
|
||||
b.pack(fill='x', expand=True, padx=3, pady=1)
|
||||
self.won_graph_var = Tkinter.BooleanVar()
|
||||
self.won_graph_var.set(True)
|
||||
b = Tkinter.Checkbutton(label_frame, text=_('Won'),
|
||||
command=self.updateGraph,
|
||||
variable=self.won_graph_var,
|
||||
)
|
||||
b.pack(fill='x', expand=True, padx=3, pady=1)
|
||||
self.percent_graph_var = Tkinter.BooleanVar()
|
||||
self.percent_graph_var.set(True)
|
||||
b = Tkinter.Checkbutton(label_frame, text=_('% won'),
|
||||
command=self.updateGraph,
|
||||
variable=self.percent_graph_var,
|
||||
)
|
||||
b.pack(fill='x', expand=True, padx=3, pady=1)
|
||||
|
||||
self.updateGraph()
|
||||
|
||||
focus = self.createButtons(bottom_frame, kw)
|
||||
self.mainloop(focus, kw.timeout)
|
||||
|
||||
|
||||
def initKw(self, kw):
|
||||
kw = KwStruct(kw, strings=(_('&OK'),), default=0, separatorwidth=2)
|
||||
return MfxDialog.initKw(self, kw)
|
||||
|
||||
|
||||
def updateGraph(self, *args):
|
||||
interval = self.variable.get()
|
||||
canvas = self.canvas
|
||||
if self.items:
|
||||
canvas.delete(*self.items)
|
||||
self.items = []
|
||||
|
||||
all_games = (self.all_games_variable.get() == 'all')
|
||||
result = self.formatter.getResults(interval, all_games)
|
||||
|
||||
if interval in ('week', 'month'):
|
||||
t = _('Games/day')
|
||||
else:
|
||||
t = _('Games/week')
|
||||
canvas.itemconfig(self.games_text_id, text=t)
|
||||
|
||||
graph_width = self.canvas_width-self.left_margin-self.right_margin
|
||||
graph_height = self.canvas_height-self.top_margin-self.bottom_margin
|
||||
dx = (graph_width-2*self.graph_dx)/(len(result)-1)
|
||||
graph_dx = (graph_width-(len(result)-1)*dx)/2
|
||||
dy = (graph_height-self.graph_dy)/5
|
||||
x0, y0 = self.left_margin, self.canvas_height-self.bottom_margin
|
||||
x1, y1 = self.canvas_width-self.right_margin, self.top_margin
|
||||
td = self.text_height/2
|
||||
|
||||
# vertical scale
|
||||
x = x0+graph_dx
|
||||
xx = -100 # coord. of prev. text
|
||||
for res in result:
|
||||
if res[0] is not None and x > xx+self.text_width+4:
|
||||
##id = canvas.create_line(x, y0, x, y0-5, width=3)
|
||||
##self.items.append(id)
|
||||
id = canvas.create_line(x, y0, x, y1, stipple='gray50')
|
||||
self.items.append(id)
|
||||
id = canvas.create_text(x, y0+td, anchor='n', text=res[0])
|
||||
self.items.append(id)
|
||||
xx = x
|
||||
else:
|
||||
id = canvas.create_line(x, y0, x, y0-3, width=1)
|
||||
self.items.append(id)
|
||||
x += dx
|
||||
|
||||
# horizontal scale
|
||||
max_games = max([i[1] for i in result])
|
||||
games_delta = max_games/5+1
|
||||
percent = 0
|
||||
games = 0
|
||||
for y in range(y0, y1, -dy):
|
||||
if y != y0:
|
||||
id = canvas.create_line(x0, y, x1, y, stipple='gray50')
|
||||
self.items.append(id)
|
||||
id = canvas.create_text(x0-td, y, anchor='e', text=str(games))
|
||||
self.items.append(id)
|
||||
id = canvas.create_text(x1+td, y, anchor='w', text=str(percent))
|
||||
self.items.append(id)
|
||||
games += games_delta
|
||||
percent += 20
|
||||
|
||||
# draw result
|
||||
games_resolution = float(dy)/games_delta
|
||||
percent_resolution = float(dy)/20
|
||||
played_coords = []
|
||||
won_coords = []
|
||||
percent_coords = []
|
||||
x = x0+graph_dx
|
||||
for res in result:
|
||||
played, won = res[1], res[2]
|
||||
y = y0 - int(games_resolution*played)
|
||||
played_coords += [x,y]
|
||||
y = y0 - int(games_resolution*won)
|
||||
won_coords += [x,y]
|
||||
if played > 0:
|
||||
percent = int(100.*won/played)
|
||||
else:
|
||||
percent = 0
|
||||
y = y0 - int(percent_resolution*percent)
|
||||
percent_coords += [x,y]
|
||||
x += dx
|
||||
if self.played_graph_var.get():
|
||||
id = canvas.create_line(fill=self.played_color, width=3,
|
||||
*played_coords)
|
||||
self.items.append(id)
|
||||
if self.won_graph_var.get():
|
||||
id = canvas.create_line(fill=self.won_color, width=3,
|
||||
*won_coords)
|
||||
self.items.append(id)
|
||||
if self.percent_graph_var.get():
|
||||
id = canvas.create_line(fill=self.percent_color, width=3,
|
||||
*percent_coords)
|
||||
self.items.append(id)
|
||||
|
||||
|
||||
|
|
|
@ -507,16 +507,11 @@ class MfxScrolledCanvas:
|
|||
if i == 0:
|
||||
self.canvas.config(bg=tile.color)
|
||||
##app.top.config(bg=tile.color)
|
||||
color = None
|
||||
else:
|
||||
self.canvas.config(bg=app.top_bg)
|
||||
##app.top.config(bg=app.top_bg)
|
||||
color = tile.text_color
|
||||
|
||||
if app.opt.use_default_text_color:
|
||||
self.canvas.setTextColor(color)
|
||||
else:
|
||||
self.canvas.setTextColor(app.opt.colors['text'])
|
||||
self.canvas.setTextColor(app.opt.colors['text'])
|
||||
|
||||
return True
|
||||
|
||||
|
@ -533,7 +528,7 @@ class MfxScrolledCanvas:
|
|||
def createFrame(self, kw):
|
||||
width = kw.get("width")
|
||||
height = kw.get("height")
|
||||
self.frame = Tkinter.Frame(self.parent, width=width, height=height, bg=None)
|
||||
self.frame = Tkinter.Frame(self.parent, width=width, height=height)
|
||||
|
||||
def createCanvas(self, kw):
|
||||
self.canvas = MfxCanvas(self.frame, **kw)
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
##
|
||||
##---------------------------------------------------------------------------##
|
||||
|
||||
__all__ = ['PysolToolbar'] #, 'TOOLBAR_BUTTONS']
|
||||
__all__ = ['PysolToolbar']
|
||||
|
||||
# imports
|
||||
import os
|
||||
|
@ -240,6 +240,7 @@ class PysolToolbar(PysolToolbarActions):
|
|||
widget.hide()
|
||||
#
|
||||
prev_visible = None
|
||||
last_visible = None
|
||||
for w in self._widgets:
|
||||
if isinstance(w, ToolbarSeparator):
|
||||
if prev_visible is None or isinstance(prev_visible, ToolbarSeparator):
|
||||
|
@ -248,6 +249,10 @@ class PysolToolbar(PysolToolbarActions):
|
|||
w.show(orient=self.orient)
|
||||
if w.visible:
|
||||
prev_visible = w
|
||||
if not isinstance(w, ToolbarLabel):
|
||||
last_visible = w
|
||||
if isinstance(last_visible, ToolbarSeparator):
|
||||
last_visible.hide()
|
||||
|
||||
# util
|
||||
def _loadImage(self, name):
|
||||
|
|
|
@ -48,8 +48,6 @@ class ColorsDialog(MfxDialog):
|
|||
frame.pack(expand=True, fill='both', padx=5, pady=10)
|
||||
frame.columnconfigure(0, weight=1)
|
||||
|
||||
self.use_default_var = Tkinter.BooleanVar()
|
||||
self.use_default_var.set(not app.opt.use_default_text_color)
|
||||
self.text_var = Tkinter.StringVar()
|
||||
self.text_var.set(app.opt.colors['text'])
|
||||
self.piles_var = Tkinter.StringVar()
|
||||
|
@ -67,17 +65,9 @@ class ColorsDialog(MfxDialog):
|
|||
self.not_matching_var = Tkinter.StringVar()
|
||||
self.not_matching_var.set(app.opt.colors['not_matching'])
|
||||
#
|
||||
c = Tkinter.Checkbutton(frame, variable=self.use_default_var,
|
||||
text=_("Text foreground:"), anchor='w')
|
||||
c.grid(row=0, column=0, sticky='we')
|
||||
l = Tkinter.Label(frame, width=10, height=2,
|
||||
bg=self.text_var.get(), textvariable=self.text_var)
|
||||
l.grid(row=0, column=1, padx=5)
|
||||
b = Tkinter.Button(frame, text=_('Change...'), width=10,
|
||||
command=lambda l=l: self.selectColor(l))
|
||||
b.grid(row=0, column=2)
|
||||
row = 1
|
||||
row = 0
|
||||
for title, var in (
|
||||
(_('Text foreground:'), self.text_var),
|
||||
(_('Highlight piles:'), self.piles_var),
|
||||
(_('Highlight cards 1:'), self.cards_1_var),
|
||||
(_('Highlight cards 2:'), self.cards_2_var),
|
||||
|
@ -86,7 +76,7 @@ class ColorsDialog(MfxDialog):
|
|||
(_('Hint arrow:'), self.hintarrow_var),
|
||||
(_('Highlight not matching:'), self.not_matching_var),
|
||||
):
|
||||
Tkinter.Label(frame, text=title, anchor='w'
|
||||
Tkinter.Label(frame, text=title, anchor='w',
|
||||
).grid(row=row, column=0, sticky='we')
|
||||
l = Tkinter.Label(frame, width=10, height=2,
|
||||
bg=var.get(), textvariable=var)
|
||||
|
@ -99,7 +89,6 @@ class ColorsDialog(MfxDialog):
|
|||
focus = self.createButtons(bottom_frame, kw)
|
||||
self.mainloop(focus, kw.timeout)
|
||||
#
|
||||
self.use_default_color = not self.use_default_var.get()
|
||||
self.text_color = self.text_var.get()
|
||||
self.piles_color = self.piles_var.get()
|
||||
self.cards_1_color = self.cards_1_var.get()
|
||||
|
|
|
@ -114,6 +114,7 @@ class FindCardDialog(Tkinter.Toplevel):
|
|||
self.groups.append(group)
|
||||
|
||||
def connectGame(self, game):
|
||||
self.canvas.delete('all')
|
||||
self.game = game
|
||||
suits = game.gameinfo.suits
|
||||
ranks = game.gameinfo.ranks
|
||||
|
@ -133,6 +134,7 @@ class FindCardDialog(Tkinter.Toplevel):
|
|||
w, h = dx*j+2, dy*i+2
|
||||
self.canvas.config(width=w, height=h)
|
||||
self.wm_iconname(PACKAGE + " - " + game.getTitleName())
|
||||
self.wm_geometry('') # cancel user-specified geometry
|
||||
|
||||
def enterEvent(self, suit, rank, rect, group):
|
||||
##print 'enterEvent', suit, rank, self.busy
|
||||
|
|
|
@ -47,6 +47,7 @@ from pysollib.util import CARDSET
|
|||
from pysollib.settings import PACKAGE, WIN_SYSTEM
|
||||
from pysollib.settings import TOP_TITLE
|
||||
from pysollib.settings import SELECT_GAME_MENU
|
||||
from pysollib.settings import USE_FREECELL_SOLVER
|
||||
from pysollib.gamedb import GI
|
||||
from pysollib.actions import PysolMenubarActions
|
||||
|
||||
|
@ -58,6 +59,7 @@ from soundoptionsdialog import SoundOptionsDialog
|
|||
from selectcardset import SelectCardsetDialogWithPreview
|
||||
from selecttile import SelectTileDialogWithPreview
|
||||
from findcarddialog import connect_game_find_card_dialog, destroy_find_card_dialog
|
||||
from solverdialog import connect_game_solver_dialog
|
||||
from tkwrap import MfxRadioMenuItem, MfxCheckMenuItem, StringVar
|
||||
|
||||
#from toolbar import TOOLBAR_BUTTONS
|
||||
|
@ -138,25 +140,18 @@ class MfxMenubar(Tkinter.Menu):
|
|||
#print label, type(label)
|
||||
name = re.sub(r"[^0-9a-zA-Z]", "", label).lower()
|
||||
label = gettext(label)
|
||||
underline = -1
|
||||
m = re.search(r"^(.*)\&([^\&].*)$", label)
|
||||
if m:
|
||||
l1, l2 = m.group(1), m.group(2)
|
||||
l1 = re.sub(r"\&\&", "&", l1)
|
||||
l2 = re.sub(r"\&\&", "&", l2)
|
||||
label = l1 + l2
|
||||
underline = len(l1)
|
||||
underline = label.find('&')
|
||||
if underline >= 0:
|
||||
label = label.replace('&', '')
|
||||
return name, label, underline
|
||||
|
||||
def add(self, itemType, cnf={}):
|
||||
label = cnf.get("label")
|
||||
if label:
|
||||
name = cnf.get('name')
|
||||
try:
|
||||
if name:
|
||||
del cnf['name'] # TclError: unknown option "-name"
|
||||
except KeyError:
|
||||
pass
|
||||
if not name:
|
||||
else:
|
||||
name, label, underline = self.labeltoname(label)
|
||||
cnf["underline"] = cnf.get("underline", underline)
|
||||
cnf["label"] = label
|
||||
|
@ -320,6 +315,7 @@ class PysolMenubar(PysolMenubarActions):
|
|||
connect_game_find_card_dialog(game)
|
||||
else:
|
||||
destroy_find_card_dialog()
|
||||
connect_game_solver_dialog(game)
|
||||
|
||||
# create a GTK-like path
|
||||
def _addPath(self, path, menu, index, submenu):
|
||||
|
@ -368,7 +364,7 @@ class PysolMenubar(PysolMenubarActions):
|
|||
menu.add_separator()
|
||||
submenu = MfxMenu(menu, label=n_("Fa&vorite games"))
|
||||
menu.add_command(label=n_("A&dd to favorites"), command=self.mAddFavor)
|
||||
menu.add_command(label=n_("R&emove from favorites"), command=self.mDelFavor)
|
||||
menu.add_command(label=n_("Remove &from favorites"), command=self.mDelFavor)
|
||||
menu.add_separator()
|
||||
menu.add_command(label=n_("&Open..."), command=self.mOpen, accelerator=m+"O")
|
||||
menu.add_command(label=n_("&Save"), command=self.mSave, accelerator=m+"S")
|
||||
|
@ -411,7 +407,7 @@ class PysolMenubar(PysolMenubarActions):
|
|||
menu.add_checkbutton(label=n_("&Pause"), variable=self.tkopt.pause, command=self.mPause, accelerator="P")
|
||||
#menu.add_command(label=n_("&Pause"), command=self.mPause, accelerator="P")
|
||||
menu.add_separator()
|
||||
menu.add_command(label=n_("S&tatus..."), command=self.mStatus, accelerator="T")
|
||||
menu.add_command(label=n_("S&tatus..."), command=self.mStatus, accelerator=m+"Y")
|
||||
menu.add_checkbutton(label=n_("&Comments..."), variable=self.tkopt.comment, command=self.mEditGameComment)
|
||||
menu.add_separator()
|
||||
submenu = MfxMenu(menu, label=n_("&Statistics"))
|
||||
|
@ -422,6 +418,7 @@ class PysolMenubar(PysolMenubarActions):
|
|||
submenu.add_command(label=n_("Full log..."), command=lambda self=self: self.mPlayerStats(mode=103))
|
||||
submenu.add_separator()
|
||||
submenu.add_command(label=TOP_TITLE+"...", command=self.mTop10, accelerator=m+"T")
|
||||
submenu.add_command(label=n_("Progression..."), command=lambda self=self: self.mPlayerStats(mode=107))
|
||||
submenu = MfxMenu(menu, label=n_("D&emo statistics"))
|
||||
submenu.add_command(label=n_("Current game..."), command=lambda self=self: self.mPlayerStats(mode=1101))
|
||||
submenu.add_command(label=n_("All games..."), command=lambda self=self: self.mPlayerStats(mode=1102))
|
||||
|
@ -429,12 +426,16 @@ class PysolMenubar(PysolMenubarActions):
|
|||
menu = MfxMenu(self.__menubar, label=n_("&Assist"))
|
||||
menu.add_command(label=n_("&Hint"), command=self.mHint, accelerator="H")
|
||||
menu.add_command(label=n_("Highlight p&iles"), command=self.mHighlightPiles, accelerator="I")
|
||||
menu.add_command(label=n_("Find card"), command=self.mFindCard, accelerator="F")
|
||||
menu.add_command(label=n_("&Find card"), command=self.mFindCard, accelerator="F3")
|
||||
menu.add_separator()
|
||||
menu.add_command(label=n_("&Demo"), command=self.mDemo, accelerator=m+"D")
|
||||
menu.add_command(label=n_("Demo (&all games)"), command=self.mMixedDemo)
|
||||
if USE_FREECELL_SOLVER:
|
||||
menu.add_command(label=n_("&Solver (experimental)"), command=self.mSolver)
|
||||
else:
|
||||
menu.add_command(label=n_("&Solver (experimental)"), command=self.mSolver, state=Tkinter.DISABLED)
|
||||
menu.add_separator()
|
||||
menu.add_command(label=n_("Piles description"), command=self.mStackDesk, accelerator="F2")
|
||||
menu.add_command(label=n_("&Piles description"), command=self.mStackDesk, accelerator="F2")
|
||||
|
||||
if self.progress: self.progress.update(step=1)
|
||||
|
||||
|
@ -478,10 +479,11 @@ class PysolMenubar(PysolMenubarActions):
|
|||
submenu.add_checkbutton(label=n_("Shade &filled stacks"), variable=self.tkopt.shade_filled_stacks, command=self.mOptShadeFilledStacks)
|
||||
submenu = MfxMenu(menu, label=n_("A&nimations"))
|
||||
submenu.add_radiobutton(label=n_("&None"), variable=self.tkopt.animations, value=0, command=self.mOptAnimations)
|
||||
submenu.add_radiobutton(label=n_("&Timer based"), variable=self.tkopt.animations, value=2, command=self.mOptAnimations)
|
||||
submenu.add_radiobutton(label=n_("&Fast"), variable=self.tkopt.animations, value=1, command=self.mOptAnimations)
|
||||
submenu.add_radiobutton(label=n_("&Slow"), variable=self.tkopt.animations, value=3, command=self.mOptAnimations)
|
||||
submenu.add_radiobutton(label=n_("&Very slow"), variable=self.tkopt.animations, value=4, command=self.mOptAnimations)
|
||||
submenu.add_radiobutton(label=n_("&Very fast"), variable=self.tkopt.animations, value=1, command=self.mOptAnimations)
|
||||
submenu.add_radiobutton(label=n_("&Fast"), variable=self.tkopt.animations, value=2, command=self.mOptAnimations)
|
||||
submenu.add_radiobutton(label=n_("&Medium"), variable=self.tkopt.animations, value=3, command=self.mOptAnimations)
|
||||
submenu.add_radiobutton(label=n_("&Slow"), variable=self.tkopt.animations, value=4, command=self.mOptAnimations)
|
||||
submenu.add_radiobutton(label=n_("V&ery slow"), variable=self.tkopt.animations, value=5, command=self.mOptAnimations)
|
||||
submenu.add_separator()
|
||||
submenu.add_checkbutton(label=n_("&Redeal animation"), variable=self.tkopt.redeal_animation, command=self.mRedealAnimation)
|
||||
if Image:
|
||||
|
@ -527,15 +529,13 @@ class PysolMenubar(PysolMenubarActions):
|
|||
ctrl = "Control-"
|
||||
if sys.platform == "darwin": ctrl = "Command-"
|
||||
self._bindKey("", "n", self.mNewGame)
|
||||
self._bindKey("", "g", self.mSelectGameDialog)
|
||||
self._bindKey("", "v", self.mSelectGameDialogWithPreview)
|
||||
self._bindKey(ctrl, "w", self.mSelectGameDialog)
|
||||
self._bindKey(ctrl, "v", self.mSelectGameDialogWithPreview)
|
||||
self._bindKey(ctrl, "r", lambda e, self=self: self.mSelectRandomGame())
|
||||
self._bindKey(ctrl, "m", self.mSelectGameById)
|
||||
self._bindKey(ctrl, "n", self.mNewGameWithNextId)
|
||||
self._bindKey(ctrl, "o", self.mOpen)
|
||||
##self._bindKey("", "F3", self.mOpen) # undocumented
|
||||
self._bindKey(ctrl, "s", self.mSave)
|
||||
##self._bindKey("", "F2", self.mSaveAs) # undocumented
|
||||
self._bindKey(ctrl, "q", self.mQuit)
|
||||
self._bindKey("", "z", self.mUndo)
|
||||
self._bindKey("", "BackSpace", self.mUndo) # undocumented
|
||||
|
@ -543,14 +543,14 @@ class PysolMenubar(PysolMenubarActions):
|
|||
self._bindKey("", "r", self.mRedo)
|
||||
self._bindKey(ctrl, "g", self.mRestart)
|
||||
self._bindKey("", "space", self.mDeal) # undocumented
|
||||
self._bindKey("", "t", self.mStatus)
|
||||
self._bindKey(ctrl, "y", self.mStatus)
|
||||
self._bindKey(ctrl, "t", self.mTop10)
|
||||
self._bindKey("", "h", self.mHint)
|
||||
self._bindKey(ctrl, "h", self.mHint1) # undocumented
|
||||
##self._bindKey("", "Shift_L", self.mHighlightPiles)
|
||||
##self._bindKey("", "Shift_R", self.mHighlightPiles)
|
||||
self._bindKey("", "i", self.mHighlightPiles)
|
||||
self._bindKey("", "f", self.mFindCard)
|
||||
self._bindKey("", "F3", self.mFindCard)
|
||||
self._bindKey(ctrl, "d", self.mDemo)
|
||||
self._bindKey(ctrl, "e", self.mSelectCardsetDialog)
|
||||
self._bindKey(ctrl, "b", self.mOptChangeCardback) # undocumented
|
||||
|
@ -593,19 +593,19 @@ class PysolMenubar(PysolMenubarActions):
|
|||
#
|
||||
|
||||
def _bindKey(self, modifier, key, func):
|
||||
if 0 and not modifier and len(key) == 1:
|
||||
self.__keybindings[key.lower()] = func
|
||||
self.__keybindings[key.upper()] = func
|
||||
return
|
||||
## if 0 and not modifier and len(key) == 1:
|
||||
## self.__keybindings[key.lower()] = func
|
||||
## self.__keybindings[key.upper()] = func
|
||||
## return
|
||||
if not modifier and len(key) == 1:
|
||||
# ignore Ctrl/Shift/Alt
|
||||
func = lambda e, func=func: e.state == 0 and func(e)
|
||||
sequence = "<" + modifier + "KeyPress-" + key + ">"
|
||||
try:
|
||||
bind(self.top, sequence, func)
|
||||
if len(key) == 1 and key != key.upper():
|
||||
key = key.upper()
|
||||
sequence = "<" + modifier + "KeyPress-" + key + ">"
|
||||
bind(self.top, sequence, func)
|
||||
if len(key) == 1 and key != key.upper():
|
||||
key = key.upper()
|
||||
sequence = "<" + modifier + "KeyPress-" + key + ">"
|
||||
bind(self.top, sequence, func)
|
||||
except:
|
||||
raise
|
||||
|
||||
|
||||
def _keyPressHandler(self, event):
|
||||
|
@ -617,10 +617,10 @@ class PysolMenubar(PysolMenubarActions):
|
|||
if event.char: # ignore Ctrl/Shift/etc.
|
||||
self.game.demo.keypress = event.char
|
||||
r = EVENT_HANDLED
|
||||
func = self.__keybindings.get(event.char)
|
||||
if func and (event.state & ~2) == 0:
|
||||
func(event)
|
||||
r = EVENT_HANDLED
|
||||
## func = self.__keybindings.get(event.char)
|
||||
## if func and (event.state & ~2) == 0:
|
||||
## func(event)
|
||||
## r = EVENT_HANDLED
|
||||
return r
|
||||
|
||||
#
|
||||
|
@ -632,9 +632,11 @@ class PysolMenubar(PysolMenubarActions):
|
|||
games = map(self.app.gdb.get, self.app.gdb.getGamesIdSortedByName())
|
||||
##games = tuple(games)
|
||||
###menu = MfxMenu(menu, label="Select &game")
|
||||
menu.add_command(label=n_("All &games..."), accelerator="G",
|
||||
m = "Ctrl-"
|
||||
if sys.platform == "darwin": m = "Cmd-"
|
||||
menu.add_command(label=n_("All &games..."), accelerator=m+"W",
|
||||
command=self.mSelectGameDialog)
|
||||
menu.add_command(label=n_("Playable pre&view..."), accelerator="V",
|
||||
menu.add_command(label=n_("Playable pre&view..."), accelerator=m+"V",
|
||||
command=self.mSelectGameDialogWithPreview)
|
||||
if not SELECT_GAME_MENU:
|
||||
return
|
||||
|
@ -779,13 +781,19 @@ class PysolMenubar(PysolMenubarActions):
|
|||
gi = games[i]
|
||||
columnbreak = i > 0 and (i % cb) == 0
|
||||
if short_name:
|
||||
label = gi.short_name
|
||||
label = gettext(gi.short_name)
|
||||
else:
|
||||
label = gi.name
|
||||
menu.add_radiobutton(command=command, variable=variable,
|
||||
columnbreak=columnbreak,
|
||||
value=gi.id, label=label, name=None)
|
||||
|
||||
label = gettext(gi.name)
|
||||
## menu.add_radiobutton(command=command, variable=variable,
|
||||
## columnbreak=columnbreak,
|
||||
## value=gi.id, label=label, name=None)
|
||||
# optimized by inlining
|
||||
menu.tk.call((menu._w, 'add', 'radiobutton') +
|
||||
menu._options({'command': command,
|
||||
'variable': variable,
|
||||
'columnbreak': columnbreak,
|
||||
'value': gi.id,
|
||||
'label': label}))
|
||||
|
||||
#
|
||||
# Select Game menu actions
|
||||
|
@ -1149,7 +1157,6 @@ class PysolMenubar(PysolMenubarActions):
|
|||
self._cancelDrag()
|
||||
self.game.endGame(bookmark=1)
|
||||
self.game.quitGame(bookmark=1)
|
||||
self.app.opt.games_geometry = {} # clear saved games geometry
|
||||
|
||||
def _mOptCardback(self, index):
|
||||
if self._cancelDrag(break_pause=False): return
|
||||
|
@ -1171,10 +1178,6 @@ class PysolMenubar(PysolMenubarActions):
|
|||
def mOptChangeCardback(self, *event):
|
||||
self._mOptCardback(self.app.cardset.backindex + 1)
|
||||
|
||||
## def mOptTableTile(self, *event):
|
||||
## if self._cancelDrag(break_pause=False): return
|
||||
## self._mOptTableTile(self.tkopt.tabletile.get())
|
||||
|
||||
def mOptChangeTableTile(self, *event):
|
||||
if self._cancelDrag(break_pause=False): return
|
||||
n = self.app.tabletile_manager.len()
|
||||
|
@ -1337,4 +1340,3 @@ class PysolMenubar(PysolMenubarActions):
|
|||
else:
|
||||
if self._cancelDrag(break_pause=True): return
|
||||
self.game.showStackDesc()
|
||||
|
||||
|
|
|
@ -437,7 +437,7 @@ class SelectGameDialogWithPreview(SelectGameDialog):
|
|||
destruct(self.preview_app)
|
||||
self.preview_app = None
|
||||
|
||||
def updatePreview(self, gameid, animations=5):
|
||||
def updatePreview(self, gameid, animations=10):
|
||||
if gameid == self.preview_key:
|
||||
return
|
||||
self.deletePreview()
|
||||
|
@ -504,7 +504,7 @@ class SelectGameDialogWithPreview(SelectGameDialog):
|
|||
#
|
||||
self.preview_app.audio = self.app.audio
|
||||
if self.app.opt.animations:
|
||||
self.preview_app.opt.animations = 5
|
||||
self.preview_app.opt.animations = 10
|
||||
else:
|
||||
self.preview_app.opt.animations = 0
|
||||
# save seed
|
||||
|
@ -562,13 +562,12 @@ class SelectGameDialogWithPreview(SelectGameDialog):
|
|||
('percent', percent),
|
||||
):
|
||||
title_label, text_label = self.info_labels[n]
|
||||
if t == '':
|
||||
if t in ('', None):
|
||||
title_label.grid_remove()
|
||||
text_label.grid_remove()
|
||||
else:
|
||||
title_label.grid()
|
||||
text_label.grid()
|
||||
text_label.config(text=t)
|
||||
#self.info_labels[n].config(text=t)
|
||||
|
||||
|
||||
|
|
333
pysollib/tk/solverdialog.py
Normal 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
|
||||
|
|
@ -121,7 +121,7 @@ class SoundOptionsDialog(MfxDialog):
|
|||
if app.audio.CAN_PLAY_MUSIC: # and app.startup_opt.sound_mode > 0:
|
||||
row += 1
|
||||
w = Tkinter.Label(frame, text=_('Sample volume:'))
|
||||
w.grid(row=row, column=0, sticky='ew')
|
||||
w.grid(row=row, column=0, sticky='w', padx=5)
|
||||
w = Tkinter.Scale(frame, from_=0, to=128, resolution=1,
|
||||
orient='horizontal', takefocus=0,
|
||||
length="3i", #label=_('Sample volume'),
|
||||
|
@ -134,7 +134,7 @@ class SoundOptionsDialog(MfxDialog):
|
|||
orient='horizontal', takefocus=0,
|
||||
length="3i", #label=_('Music volume'),
|
||||
variable=self.music_volume)
|
||||
w.grid(row=row, column=1, sticky='w', padx=5)
|
||||
w.grid(row=row, column=1, sticky='ew', padx=5)
|
||||
|
||||
else:
|
||||
# remove "Apply" button
|
||||
|
|
|
@ -116,8 +116,11 @@ class MfxStatusbar:
|
|||
def updateText(self, **kw):
|
||||
for k, v in kw.items():
|
||||
label = getattr(self, k + "_label")
|
||||
#label["text"] = str(v)
|
||||
label["text"] = unicode(v)
|
||||
text = unicode(v)
|
||||
width = label['width']
|
||||
if width and len(text) > width:
|
||||
label['width'] = len(text)
|
||||
label["text"] = text
|
||||
|
||||
def configLabel(self, name, **kw):
|
||||
label = getattr(self, name + "_label")
|
||||
|
|
|
@ -370,21 +370,14 @@ class MfxCanvas(Tkinter.Canvas):
|
|||
|
||||
def _substitute(self, *args):
|
||||
e = Tkinter.Event()
|
||||
e.x = int(args[0])
|
||||
e.y = int(args[1])
|
||||
try:
|
||||
# Tk changed behavior in 8.4.2, returning "??" rather more often.
|
||||
e.x = int(args[0])
|
||||
except ValueError:
|
||||
e.x = args[0]
|
||||
try:
|
||||
e.y = int(args[1])
|
||||
except ValueError:
|
||||
e.y = args[1]
|
||||
return (e,)
|
||||
|
||||
|
||||
#
|
||||
# debug
|
||||
#
|
||||
|
||||
def update(self):
|
||||
##import mfxutil; print mfxutil.callername()
|
||||
# ??????
|
||||
Tkinter.Canvas.update(self)
|
||||
|
||||
def update_idletasks(self):
|
||||
##import mfxutil; print mfxutil.callername()
|
||||
Tkinter.Canvas.update_idletasks(self)
|
||||
|
||||
|
|
|
@ -38,7 +38,9 @@ __all__ = ['SingleGame_StatsDialog',
|
|||
'FullLog_StatsDialog',
|
||||
'SessionLog_StatsDialog',
|
||||
'Status_StatsDialog',
|
||||
'Top_StatsDialog']
|
||||
'Top_StatsDialog',
|
||||
'ProgressionDialog',
|
||||
]
|
||||
|
||||
# imports
|
||||
import os, string, sys, types
|
||||
|
@ -49,7 +51,7 @@ import Tkinter, tkFont
|
|||
from pysollib.mfxutil import destruct, Struct, kwdefault, KwStruct
|
||||
from pysollib.mfxutil import format_time
|
||||
##from pysollib.util import *
|
||||
from pysollib.stats import PysolStatsFormatter
|
||||
from pysollib.stats import PysolStatsFormatter, ProgressionFormatter
|
||||
from pysollib.settings import TOP_TITLE
|
||||
|
||||
# Toolkit imports
|
||||
|
@ -720,7 +722,7 @@ class Top_StatsDialog(MfxDialog):
|
|||
self.createBitmaps(top_frame, kw)
|
||||
|
||||
frame = Tkinter.Frame(top_frame)
|
||||
frame.pack(expand=Tkinter.YES, fill=Tkinter.BOTH, padx=5, pady=10)
|
||||
frame.pack(expand=Tkinter.YES, fill=Tkinter.BOTH, padx=10, pady=10)
|
||||
frame.columnconfigure(0, weight=1)
|
||||
|
||||
if (player in app.stats.games_stats and
|
||||
|
@ -797,3 +799,266 @@ class Top_StatsDialog(MfxDialog):
|
|||
separatorwidth=2,
|
||||
)
|
||||
return MfxDialog.initKw(self, kw)
|
||||
|
||||
|
||||
# /***********************************************************************
|
||||
# //
|
||||
# ************************************************************************/
|
||||
|
||||
class ProgressionDialog(MfxDialog):
|
||||
def __init__(self, parent, title, app, player, gameid, **kw):
|
||||
|
||||
font_name = app.getFont('default')
|
||||
font = tkFont.Font(parent, font_name)
|
||||
tkfont = tkFont.Font(parent, font)
|
||||
font_metrics = font.metrics()
|
||||
measure = tkfont.measure
|
||||
self.text_height = font_metrics['linespace']
|
||||
self.text_width = measure('XX.XX.XX')
|
||||
|
||||
self.items = []
|
||||
self.formatter = ProgressionFormatter(app, player, gameid)
|
||||
|
||||
kw = self.initKw(kw)
|
||||
MfxDialog.__init__(self, parent, title, kw.resizable, kw.default)
|
||||
top_frame, bottom_frame = self.createFrames(kw)
|
||||
self.createBitmaps(top_frame, kw)
|
||||
|
||||
frame = Tkinter.Frame(top_frame)
|
||||
frame.pack(expand=True, fill='both', padx=5, pady=10)
|
||||
frame.columnconfigure(0, weight=1)
|
||||
|
||||
# constants
|
||||
self.canvas_width, self.canvas_height = 600, 250
|
||||
if parent.winfo_screenwidth() < 800 or \
|
||||
parent.winfo_screenheight() < 600:
|
||||
self.canvas_width, self.canvas_height = 400, 200
|
||||
self.xmargin, self.ymargin = 10, 10
|
||||
self.graph_dx, self.graph_dy = 10, 10
|
||||
self.played_color = '#ff7ee9'
|
||||
self.won_color = '#00dc28'
|
||||
self.percent_color = 'blue'
|
||||
# create canvas
|
||||
self.canvas = canvas = Tkinter.Canvas(frame, bg='#dfe8ff',
|
||||
highlightthickness=1,
|
||||
highlightbackground='black',
|
||||
width=self.canvas_width,
|
||||
height=self.canvas_height)
|
||||
canvas.pack(side='left', padx=5)
|
||||
#
|
||||
dir = os.path.join('images', 'stats')
|
||||
try:
|
||||
fn = app.dataloader.findImage('progression', dir)
|
||||
self.bg_image = loadImage(fn)
|
||||
canvas.create_image(0, 0, image=self.bg_image, anchor='nw')
|
||||
except:
|
||||
pass
|
||||
#
|
||||
tw = max(measure(_('Games/day')),
|
||||
measure(_('Games/week')),
|
||||
measure(_('% won')))
|
||||
self.left_margin = self.xmargin+tw/2
|
||||
self.right_margin = self.xmargin+tw/2
|
||||
self.top_margin = 15+self.text_height
|
||||
self.bottom_margin = 15+self.text_height+10+self.text_height
|
||||
#
|
||||
x0, y0 = self.left_margin, self.canvas_height-self.bottom_margin
|
||||
x1, y1 = self.canvas_width-self.right_margin, self.top_margin
|
||||
canvas.create_rectangle(x0, y0, x1, y1, fill='white')
|
||||
# horizontal axis
|
||||
canvas.create_line(x0, y0, x1, y0, width=3)
|
||||
|
||||
# left vertical axis
|
||||
canvas.create_line(x0, y0, x0, y1, width=3)
|
||||
t = _('Games/day')
|
||||
self.games_text_id = canvas.create_text(x0-4, y1-4, anchor='s', text=t)
|
||||
|
||||
# right vertical axis
|
||||
canvas.create_line(x1, y0, x1, y1, width=3)
|
||||
canvas.create_text(x1+4, y1-4, anchor='s', text=_('% won'))
|
||||
|
||||
# caption
|
||||
d = self.text_height
|
||||
x, y = self.xmargin, self.canvas_height-self.ymargin
|
||||
id = canvas.create_rectangle(x, y, x+d, y-d, outline='black',
|
||||
fill=self.played_color)
|
||||
x += d+5
|
||||
canvas.create_text(x, y, anchor='sw', text=_('Played'))
|
||||
x += measure(_('Played'))+20
|
||||
id = canvas.create_rectangle(x, y, x+d, y-d, outline='black',
|
||||
fill=self.won_color)
|
||||
x += d+5
|
||||
canvas.create_text(x, y, anchor='sw', text=_('Won'))
|
||||
x += measure(_('Won'))+20
|
||||
id = canvas.create_rectangle(x, y, x+d, y-d, outline='black',
|
||||
fill=self.percent_color)
|
||||
x += d+5
|
||||
canvas.create_text(x, y, anchor='sw', text=_('% won'))
|
||||
|
||||
# right frame
|
||||
right_frame = Tkinter.Frame(frame)
|
||||
right_frame.pack(side='left', fill='x', padx=5)
|
||||
self.all_games_variable = var = Tkinter.StringVar()
|
||||
var.set('all')
|
||||
b = Tkinter.Radiobutton(right_frame, text=_('All games'),
|
||||
variable=var, value='all',
|
||||
command=self.updateGraph,
|
||||
justify='left', anchor='w'
|
||||
)
|
||||
b.pack(fill='x', expand=True, padx=3, pady=1)
|
||||
b = Tkinter.Radiobutton(right_frame, text=_('Current game'),
|
||||
variable=var, value='current',
|
||||
command=self.updateGraph,
|
||||
justify='left', anchor='w'
|
||||
)
|
||||
b.pack(fill='x', expand=True, padx=3, pady=1)
|
||||
if Tkinter.TkVersion >= 8.4:
|
||||
label_frame = Tkinter.LabelFrame(right_frame, text=_('Statistics for'))
|
||||
else:
|
||||
label_frame = Tkinter.Frame(right_frame)
|
||||
label_frame.pack(side='top', fill='x', pady=10)
|
||||
self.variable = var = Tkinter.StringVar()
|
||||
var.set('week')
|
||||
for v, t in (
|
||||
('week', _('Last 7 days')),
|
||||
('month', _('Last month')),
|
||||
('year', _('Last year')),
|
||||
('all', _('All time')),
|
||||
):
|
||||
b = Tkinter.Radiobutton(label_frame, text=t, variable=var, value=v,
|
||||
command=self.updateGraph,
|
||||
justify='left', anchor='w'
|
||||
)
|
||||
b.pack(fill='x', expand=True, padx=3, pady=1)
|
||||
if Tkinter.TkVersion >= 8.4:
|
||||
label_frame = Tkinter.LabelFrame(right_frame, text=_('Show graphs'))
|
||||
else:
|
||||
label_frame = Tkinter.Frame(right_frame)
|
||||
label_frame.pack(side='top', fill='x')
|
||||
self.played_graph_var = Tkinter.BooleanVar()
|
||||
self.played_graph_var.set(True)
|
||||
b = Tkinter.Checkbutton(label_frame, text=_('Played'),
|
||||
command=self.updateGraph,
|
||||
variable=self.played_graph_var,
|
||||
justify='left', anchor='w'
|
||||
)
|
||||
b.pack(fill='x', expand=True, padx=3, pady=1)
|
||||
self.won_graph_var = Tkinter.BooleanVar()
|
||||
self.won_graph_var.set(True)
|
||||
b = Tkinter.Checkbutton(label_frame, text=_('Won'),
|
||||
command=self.updateGraph,
|
||||
variable=self.won_graph_var,
|
||||
justify='left', anchor='w'
|
||||
)
|
||||
b.pack(fill='x', expand=True, padx=3, pady=1)
|
||||
self.percent_graph_var = Tkinter.BooleanVar()
|
||||
self.percent_graph_var.set(True)
|
||||
b = Tkinter.Checkbutton(label_frame, text=_('% won'),
|
||||
command=self.updateGraph,
|
||||
variable=self.percent_graph_var,
|
||||
justify='left', anchor='w'
|
||||
)
|
||||
b.pack(fill='x', expand=True, padx=3, pady=1)
|
||||
|
||||
self.updateGraph()
|
||||
|
||||
focus = self.createButtons(bottom_frame, kw)
|
||||
self.mainloop(focus, kw.timeout)
|
||||
|
||||
|
||||
def initKw(self, kw):
|
||||
kw = KwStruct(kw, strings=(_('&OK'),), default=0, separatorwidth=2)
|
||||
return MfxDialog.initKw(self, kw)
|
||||
|
||||
|
||||
def updateGraph(self, *args):
|
||||
interval = self.variable.get()
|
||||
canvas = self.canvas
|
||||
if self.items:
|
||||
canvas.delete(*self.items)
|
||||
self.items = []
|
||||
|
||||
all_games = (self.all_games_variable.get() == 'all')
|
||||
result = self.formatter.getResults(interval, all_games)
|
||||
|
||||
if interval in ('week', 'month'):
|
||||
t = _('Games/day')
|
||||
else:
|
||||
t = _('Games/week')
|
||||
canvas.itemconfig(self.games_text_id, text=t)
|
||||
|
||||
graph_width = self.canvas_width-self.left_margin-self.right_margin
|
||||
graph_height = self.canvas_height-self.top_margin-self.bottom_margin
|
||||
dx = (graph_width-2*self.graph_dx)/(len(result)-1)
|
||||
graph_dx = (graph_width-(len(result)-1)*dx)/2
|
||||
dy = (graph_height-self.graph_dy)/5
|
||||
x0, y0 = self.left_margin, self.canvas_height-self.bottom_margin
|
||||
x1, y1 = self.canvas_width-self.right_margin, self.top_margin
|
||||
td = self.text_height/2
|
||||
|
||||
# vertical scale
|
||||
x = x0+graph_dx
|
||||
xx = -100
|
||||
for res in result:
|
||||
if res[0] is not None and x > xx+self.text_width+4:
|
||||
##id = canvas.create_line(x, y0, x, y0-5, width=3)
|
||||
##self.items.append(id)
|
||||
id = canvas.create_line(x, y0, x, y1, stipple='gray50')
|
||||
self.items.append(id)
|
||||
id = canvas.create_text(x, y0+td, anchor='n', text=res[0])
|
||||
self.items.append(id)
|
||||
xx = x
|
||||
else:
|
||||
id = canvas.create_line(x, y0, x, y0-3, width=1)
|
||||
self.items.append(id)
|
||||
x += dx
|
||||
|
||||
# horizontal scale
|
||||
max_games = max([i[1] for i in result])
|
||||
games_delta = max_games/5+1
|
||||
percent = 0
|
||||
games = 0
|
||||
for y in range(y0, y1, -dy):
|
||||
if y != y0:
|
||||
id = canvas.create_line(x0, y, x1, y, stipple='gray50')
|
||||
self.items.append(id)
|
||||
id = canvas.create_text(x0-td, y, anchor='e', text=str(games))
|
||||
self.items.append(id)
|
||||
id = canvas.create_text(x1+td, y, anchor='w', text=str(percent))
|
||||
self.items.append(id)
|
||||
games += games_delta
|
||||
percent += 20
|
||||
|
||||
# draw result
|
||||
games_resolution = float(dy)/games_delta
|
||||
percent_resolution = float(dy)/20
|
||||
played_coords = []
|
||||
won_coords = []
|
||||
percent_coords = []
|
||||
x = x0+graph_dx
|
||||
for res in result:
|
||||
played, won = res[1], res[2]
|
||||
y = y0 - int(games_resolution*played)
|
||||
played_coords += [x,y]
|
||||
y = y0 - int(games_resolution*won)
|
||||
won_coords += [x,y]
|
||||
if played > 0:
|
||||
percent = int(100.*won/played)
|
||||
else:
|
||||
percent = 0
|
||||
y = y0 - int(percent_resolution*percent)
|
||||
percent_coords += [x,y]
|
||||
x += dx
|
||||
if self.played_graph_var.get():
|
||||
id = canvas.create_line(fill=self.played_color, width=3,
|
||||
*played_coords)
|
||||
self.items.append(id)
|
||||
if self.won_graph_var.get():
|
||||
id = canvas.create_line(fill=self.won_color, width=3,
|
||||
*won_coords)
|
||||
self.items.append(id)
|
||||
if self.percent_graph_var.get():
|
||||
id = canvas.create_line(fill=self.percent_color, width=3,
|
||||
*percent_coords)
|
||||
self.items.append(id)
|
||||
|
||||
|
|
|
@ -507,16 +507,11 @@ class MfxScrolledCanvas:
|
|||
if i == 0:
|
||||
self.canvas.config(bg=tile.color)
|
||||
##app.top.config(bg=tile.color)
|
||||
color = None
|
||||
else:
|
||||
self.canvas.config(bg=app.top_bg)
|
||||
##app.top.config(bg=app.top_bg)
|
||||
color = tile.text_color
|
||||
|
||||
if app.opt.use_default_text_color:
|
||||
self.canvas.setTextColor(color)
|
||||
else:
|
||||
self.canvas.setTextColor(app.opt.colors['text'])
|
||||
self.canvas.setTextColor(app.opt.colors['text'])
|
||||
|
||||
return True
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
##
|
||||
##---------------------------------------------------------------------------##
|
||||
|
||||
__all__ = ['PysolToolbar'] #, 'TOOLBAR_BUTTONS']
|
||||
__all__ = ['PysolToolbar']
|
||||
|
||||
# imports
|
||||
import os, sys, types, Tkinter
|
||||
|
@ -245,6 +245,7 @@ class PysolToolbar(PysolToolbarActions):
|
|||
widget.hide()
|
||||
#
|
||||
prev_visible = None
|
||||
last_visible = None
|
||||
for w in self._widgets:
|
||||
if w.__class__ is ToolbarSeparator:
|
||||
if prev_visible is None or prev_visible.__class__ is ToolbarSeparator:
|
||||
|
@ -256,6 +257,10 @@ class PysolToolbar(PysolToolbarActions):
|
|||
prev_visible.hide()
|
||||
if w.visible:
|
||||
prev_visible = w
|
||||
if not isinstance(w, ToolbarLabel):
|
||||
last_visible = w
|
||||
if isinstance(last_visible, ToolbarSeparator):
|
||||
last_visible.hide()
|
||||
|
||||
# util
|
||||
def _loadImage(self, name):
|
||||
|
|
|
@ -42,13 +42,13 @@ def init_tile(app, top, theme):
|
|||
traceback.print_exc()
|
||||
pass
|
||||
|
||||
def set_theme(top, theme):
|
||||
def set_theme(app, top, theme):
|
||||
# set theme
|
||||
style = Tile.Style(top)
|
||||
all_themes = style.theme_names()
|
||||
if theme not in all_themes:
|
||||
print >> sys.stderr, 'WARNING: invalid theme name:', theme
|
||||
theme = 'default'
|
||||
theme = app.opt.default_tile_theme
|
||||
style.theme_use(theme)
|
||||
|
||||
|
||||
|
@ -91,7 +91,7 @@ class baseInitRootWindow:
|
|||
elif USE_TILE:
|
||||
theme = app.opt.tile_theme
|
||||
init_tile(app, root, theme)
|
||||
set_theme(root, theme)
|
||||
set_theme(app, root, theme)
|
||||
else:
|
||||
pass
|
||||
|
||||
|
|
|
@ -72,6 +72,8 @@ class initRootWindow(baseInitRootWindow):
|
|||
app.opt.fonts['default'] = fn
|
||||
root.option_add('*Menu.borderWidth', 1, 60)
|
||||
root.option_add('*Menu.activeBorderWidth', 1, 60)
|
||||
if app.opt.tile_theme == 'clam':
|
||||
root.wm_minsize(550, 360)
|
||||
#
|
||||
else:
|
||||
root.option_add('*Entry.background', 'white', 60)
|
||||
|
|
|
@ -140,8 +140,8 @@ def all_games(sort_by='id'):
|
|||
gt = 'French (%s)' % GAME_BY_TYPE[gi.si.game_type]
|
||||
name = gi.name.encode('utf-8')
|
||||
altnames = '<br>'.join(gi.altnames).encode('utf-8')
|
||||
if 1 and os.path.exists(os.path.join(rules_dir, rules_fn)):
|
||||
fn = '../data/html/rules/'+rules_fn
|
||||
fn = os.path.join(rules_dir, rules_fn)
|
||||
if 1 and os.path.exists(fn):
|
||||
print '''<tr><td>%s</td><td>
|
||||
<a href="%s" title="Rules for this game">%s</a>
|
||||
</td><td>%s</td><td>%s</td></tr>
|
||||
|
@ -152,13 +152,19 @@ def all_games(sort_by='id'):
|
|||
print '</table>'
|
||||
|
||||
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 '<h2>Categories</h2>'
|
||||
by_category()
|
||||
print '<h2>Types</h2>'
|
||||
by_type()
|
||||
print '<h2>All games</h2>'
|
||||
#print '<h2>All games</h2>'
|
||||
all_games(sort_by)
|
||||
print '</body></html>'
|
||||
|
||||
|
@ -220,6 +226,9 @@ def plain_text():
|
|||
gi = GAME_DB.get(id)
|
||||
if gi.category == GI.GC_FRENCH:
|
||||
##print str(gi.gameclass)
|
||||
##gc = gi.gameclass
|
||||
##h = gc.Hint_Class is None and 'None' or gc.Hint_Class.__name__
|
||||
##print gi.name.encode('utf-8'), h
|
||||
print gi.name.encode('utf-8')
|
||||
for n in gi.altnames:
|
||||
print n.encode('utf-8')
|
||||
|
@ -233,6 +242,8 @@ if len(sys.argv) < 2 or sys.argv[1] == 'html':
|
|||
sort_by = 'id'
|
||||
if len(sys.argv) > 2:
|
||||
sort_by = sys.argv[2]
|
||||
if len(sys.argv) > 3:
|
||||
rules_dir = sys.argv[3]
|
||||
create_html(sort_by)
|
||||
elif sys.argv[1] == 'gettext':
|
||||
get_text()
|
||||
|
|
|
@ -4,7 +4,7 @@ cd ..
|
|||
rm -rf dist
|
||||
mkdir dist
|
||||
cp -r locale dist
|
||||
cp fc-solve.exe dist
|
||||
cp -r freecell-solver dist
|
||||
cp smpeg.dll ogg.dll vorbis.dll vorbisfile.dll dist
|
||||
python setup.py py2exe
|
||||
cp -r d:\Python\tcl\tile0.7.8 dist\tcl
|
||||
|
|
9
setup.py
|
@ -4,6 +4,7 @@
|
|||
import os
|
||||
from distutils.core import setup
|
||||
from pysollib.settings import FC_VERSION as VERSION
|
||||
from pysollib.settings import PACKAGE_URL
|
||||
if os.name == 'nt':
|
||||
import py2exe
|
||||
|
||||
|
@ -14,7 +15,7 @@ elif os.name == 'nt':
|
|||
else:
|
||||
data_dir = 'data'
|
||||
|
||||
datas = [
|
||||
ddirs = [
|
||||
'html',
|
||||
'images',
|
||||
'sound',
|
||||
|
@ -25,11 +26,11 @@ datas = [
|
|||
]
|
||||
for s in file('MANIFEST.in'):
|
||||
if s.startswith('graft data/cardset-'):
|
||||
datas.append(s[11:].strip())
|
||||
ddirs.append(s[11:].strip())
|
||||
|
||||
data_files = []
|
||||
|
||||
for d in datas:
|
||||
for d in ddirs:
|
||||
for root, dirs, files in os.walk(os.path.join('data', d)):
|
||||
if root.find('.svn') >= 0:
|
||||
continue
|
||||
|
@ -57,7 +58,7 @@ integrated HTML help browser, and it\'s free Open Source software.
|
|||
kw = {
|
||||
'name' : 'PySolFC',
|
||||
'version' : VERSION,
|
||||
'url' : 'http://sourceforge.net/projects/pysolfc/',
|
||||
'url' : PACKAGE_URL,
|
||||
'author' : 'Skomoroh',
|
||||
'author_email' : 'skomoroh@gmail.com',
|
||||
'description' : 'PySol - a solitaire game collection',
|
||||
|
|