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

+ new feature: Solitaire Wizard (tile only yet)

* misc. minor improvements


git-svn-id: https://pysolfc.svn.sourceforge.net/svnroot/pysolfc/PySolFC/trunk@157 39dd0a4e-7c14-0410-91b3-c4f2d318f732
This commit is contained in:
skomoroh 2007-05-03 21:21:33 +00:00
parent 9c826858fa
commit fb3b59bf3d
15 changed files with 817 additions and 53 deletions

8
README
View file

@ -28,6 +28,14 @@ or just run from the source directory:
$ python pysol.py
** Freecell Solver **
If you want to use Solver, you should configure freecell-solver with following
options:
--enable-max-num-freecells=8
--enable-max-num-stacks=20
--enable-max-num-initial-cards-per-stack=60
(or edit config.h)
Install Extras.
---------------

View file

@ -44,6 +44,7 @@ from pysolrandom import constructRandom
from settings import PACKAGE, PACKAGE_URL
from settings import TOP_TITLE
from settings import DEBUG
from gamedb import GI
# stats imports
from stats import FileStatsFormatter
@ -95,6 +96,7 @@ class PysolMenubarActions:
find_card = 0,
rules = 0,
pause = 0,
custom_game = 0,
)
def connectGame(self, game):
@ -192,6 +194,8 @@ class PysolMenubarActions:
ms.rules = 1
if not game.finished:
ms.pause = 1
if game.gameinfo.si.game_type == GI.GT_CUSTOM:
ms.custom_game = 1
# update menu items and toolbar
def _updateMenus(self):
@ -208,6 +212,7 @@ class PysolMenubarActions:
self.setMenuState(ms.redo, "edit.redoall")
self.updateBookmarkMenuState()
self.setMenuState(ms.restart, "edit.restart")
self.setMenuState(ms.custom_game, "edit.editcurrentgame")
# Game menu
self.setMenuState(ms.deal, "game.dealcards")
self.setMenuState(ms.autodrop, "game.autodrop")
@ -264,9 +269,9 @@ class PysolMenubarActions:
self.game.endGame()
self.game.quitGame(self.game.id)
def _mSelectGame(self, id, random=None):
def _mSelectGame(self, id, random=None, force=False):
if self._cancelDrag(): return
if self.game.id == id:
if not force and self.game.id == id:
return
if self.changed():
if not self.game.areYouSure(_("Select game")):

View file

@ -1420,12 +1420,12 @@ Please select a %s type %s.
m = re.search(r"^(.+)\.py$", name)
n = os.path.join(dir, name)
if m and os.path.isfile(n):
p = sys.path[:]
try:
loadGame(m.group(1), n)
except Exception, ex:
if DEBUG:
traceback.print_exc()
print_err(_("error loading plugin %s: %s") % (n, ex))
sys.path = p
#

170
pysollib/customgame.py Normal file
View file

@ -0,0 +1,170 @@
##---------------------------------------------------------------------------##
##
## 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.
##
##---------------------------------------------------------------------------##
from gamedb import registerGame, GameInfo, GI
from util import *
from stack import *
from game import Game
from layout import Layout
from hint import AbstractHint, DefaultHint, CautiousDefaultHint
#from pysoltk import MfxCanvasText
from wizardutil import WizardWidgets
# /***********************************************************************
# //
# ************************************************************************/
class CustomGame(Game):
def createGame(self):
ss = self.SETTINGS
s = {}
for w in WizardWidgets:
if isinstance(w, basestring):
continue
if w.widget == 'menu':
v = dict(w.values_map)[ss[w.var_name]]
else:
v = ss[w.var_name]
s[w.var_name] = v
##from pprint import pprint; pprint(s)
foundation = StackWrapper(
s['found_type'],
base_rank=s['found_base_card'],
dir=s['found_dir'],
max_move=s['found_max_move'],
)
max_rounds = s['redeals']
if max_rounds >= 0:
max_rounds += 1
talon = StackWrapper(
s['talon'],
max_rounds=max_rounds,
)
row = StackWrapper(
s['rows_type'],
base_rank=s['rows_base_card'],
dir=s['rows_dir'],
max_move=s['rows_max_move'],
)
kw = {'rows' : s['rows_num'],
'waste' : False,
'texts' : True,
}
if s['talon'] is InitialDealTalonStack:
kw['texts'] = False
if s['talon'] is WasteTalonStack:
kw['waste'] = True
kw['waste_class'] = WasteStack
if int(s['reserves_num']):
kw['reserves'] = s['reserves_num']
kw['reserve_class'] = s['reserves_type']
kw['playcards'] = 12+s['deal_to_rows']
Layout(self).createGame(layout_method = s['layout'],
talon_class = talon,
foundation_class = foundation,
row_class = row,
**kw
)
for c, f in (
((AC_RowStack, UD_AC_RowStack),
self._shallHighlightMatch_AC),
((SS_RowStack, UD_SS_RowStack),
self._shallHighlightMatch_SS),
((RK_RowStack, UD_RK_RowStack),
self._shallHighlightMatch_RK),
):
if s['rows_type'] in c:
self.shallHighlightMatch = f
break
def startGame(self):
frames = 0
# deal to reserves
n = self.SETTINGS['deal_to_reserves']
for i in range(n):
self.s.talon.dealRowAvail(rows=self.s.reserves,
flip=True, frames=frames)
if frames == 0 and len(self.s.talon.cards) < 16:
frames = -1
self.startDealSample()
# deal to rows
flip = self.SETTINGS['deal_faceup'] == 'All cards'
max_rows = self.SETTINGS['deal_to_rows']
if self.SETTINGS['deal_type'] == 'Triangle':
# triangle
for i in range(1, len(self.s.rows)):
self.s.talon.dealRowAvail(rows=self.s.rows[i:],
flip=flip, frames=frames)
max_rows -= 1
if max_rows == 1:
break
if frames == 0 and len(self.s.talon.cards) < 16:
frames = -1
self.startDealSample()
else:
# rectangle
for i in range(max_rows-1):
self.s.talon.dealRowAvail(rows=self.s.rows,
flip=flip, frames=frames)
if frames == 0 and len(self.s.talon.cards) < 16:
frames = -1
self.startDealSample()
if frames == 0:
self.startDealSample()
self.s.talon.dealRowAvail()
# deal to waste
if self.s.waste:
self.s.talon.dealCards()
def registerCustomGame(gameclass):
s = gameclass.SETTINGS
for w in WizardWidgets:
if isinstance(w, basestring):
continue
if w.var_name == 'decks':
v = s['decks']
decks = dict(w.values_map)[v]
if w.var_name == 'redeals':
v = s['redeals']
redeals = dict(w.values_map)[v]
if w.var_name == 'skill_level':
v = s['skill_level']
skill_level = dict(w.values_map)[v]
gameid = s['gameid']
registerGame(GameInfo(gameid, gameclass, s['name'],
GI.GT_CUSTOM | GI.GT_ORIGINAL,
decks, redeals, skill_level))

View file

@ -98,6 +98,7 @@ class GI:
GT_TERRACE = 32
GT_YUKON = 33
GT_SHISEN_SHO = 34
GT_CUSTOM = 40
# extra flags
GT_BETA = 1 << 12 # beta version of game driver
GT_CHILDREN = 1 << 13 # *not used*
@ -173,6 +174,7 @@ class GI:
(n_("Two-Deck games"),lambda gi, gt=GT_2DECK_TYPE: gi.si.game_type == gt),
(n_("Three-Deck games"),lambda gi, gt=GT_3DECK_TYPE: gi.si.game_type == gt),
(n_("Four-Deck games"),lambda gi, gt=GT_4DECK_TYPE: gi.si.game_type == gt),
(n_("Cusom games"), lambda gi, gt=GT_CUSTOM: gi.si.game_type == gt),
)
SELECT_ORIGINAL_GAME_BY_TYPE = (
@ -471,7 +473,8 @@ class GameManager:
self.__all_games = {} # includes hidden games
self.__all_gamenames = {} # includes hidden games
self.__games_for_solver = []
self.loading_plugin = 0
self.loading_plugin = False
self.check_game = True
self.registered_game_types = {}
def getSelected(self):
@ -506,12 +509,12 @@ class GameManager:
raise GameInfoException("duplicate game altname %s: %s" %
(gi.id, n))
def register(self, gi):
def register(self, gi, check_game=True):
##print gi.id, gi.short_name.encode('utf-8')
if not isinstance(gi, GameInfo):
raise GameInfoException("wrong GameInfo class")
gi.plugin = self.loading_plugin
if self.loading_plugin or CHECK_GAMES:
if self.check_game and (self.loading_plugin or CHECK_GAMES):
self._check_game(gi)
##if 0 and gi.si.game_flags & GI.GT_XORIGINAL:
## return
@ -610,9 +613,10 @@ def registerGame(gameinfo):
return gameinfo
def loadGame(modname, filename, plugin=1):
def loadGame(modname, filename, plugin=True, check_game=True):
##print "load game", modname, filename
GAME_DB.loading_plugin = plugin
GAME_DB.check_game = check_game
module = imp.load_source(modname, filename)
##execfile(filename, globals(), globals())

View file

@ -39,7 +39,7 @@ import os, sys
import time
# PySol imports
from settings import USE_FREECELL_SOLVER, FCS_COMMAND
from settings import DEBUG, USE_FREECELL_SOLVER, FCS_COMMAND
from mfxutil import destruct
from util import KING
@ -702,7 +702,7 @@ class FreeCellSolver_Hint:
self.dialog = dialog
self.game_type = game_type
self.options = {
'method': 'dfs',
'method': 'soft-dfs',
'max_iters': 10000,
'max_depth': 1000,
'progress': False,
@ -768,7 +768,10 @@ class FreeCellSolver_Hint:
b = ''
for s in self.game.s.foundations:
if s.cards:
ss = self.card2str2(s.cards[-1])
if 'preset' in game_type and game_type['preset'] == 'simple_simon':
ss = self.card2str2(s.cards[0])
else:
ss = self.card2str2(s.cards[-1])
b += ' ' + ss
if b:
board += 'Founds:' + b + '\n'
@ -792,16 +795,18 @@ class FreeCellSolver_Hint:
b += cs + ' '
board = board + b.strip() + '\n'
#
##print '--------------------\n', board, '--------------------'
if DEBUG:
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']
if DEBUG:
args += ['-s']
if self.options['preset'] and self.options['preset'] != 'none':
args += ['--load-config', self.options['preset']]
args += ['--max-iters', self.options['max_iters'],
'--max-depth', self.options['max_depth'],
'--method', self.options['method'],
@ -820,7 +825,8 @@ class FreeCellSolver_Hint:
args += ['--empty-stacks-filled-by', game_type['esf']]
command = FCS_COMMAND+' '+' '.join([str(i) for i in args])
##print command
if DEBUG:
print command
pin, pout, perr = os.popen3(command)
pin.write(board)
pin.close()
@ -830,14 +836,16 @@ class FreeCellSolver_Hint:
'stack' : game.s.rows,
'freecell' : game.s.reserves,
}
##start_time = time.time()
if DEBUG:
start_time = time.time()
if progress:
# iteration output
iter = 0
depth = 0
states = 0
for s in pout:
##print s,
if DEBUG >= 5:
print s,
if s.startswith('Iter'):
#print `s`
iter = int(s[10:-1])
@ -857,7 +865,8 @@ class FreeCellSolver_Hint:
hints = []
for s in pout:
#print s,
if DEBUG:
print s,
# TODO:
# Total number of states checked is 6.
# This scan generated 6 states.
@ -899,7 +908,8 @@ class FreeCellSolver_Hint:
##print src, dest, ncards
#
##print 'time:', time.time()-start_time
if DEBUG:
print 'time:', time.time()-start_time
##print perr.read(),
self.hints = hints

View file

@ -330,10 +330,10 @@ class Layout:
# FreeCell layout
# - top: free cells, foundations
# - below: rows
# - left bottom: talon
# - left bottom: talon, waste
#
def freeCellLayout(self, rows, reserves, texts=0, playcards=18):
def freeCellLayout(self, rows, reserves, waste=0, texts=0, playcards=18):
S = self.__createStack
CW, CH = self.CW, self.CH
XM, YM = self.XM, self.YM
@ -371,8 +371,18 @@ class Layout:
x, y = XM, h - YS
self.s.talon = s = S(x, y)
if texts:
# place text right of stack
self._setText(s, anchor="se")
if waste:
# place text top of stack
self._setText(s, anchor="n")
else:
# place text right of stack
self._setText(s, anchor="se")
if waste:
x += XS
self.s.waste = s = S(x, y)
if texts:
# place text top of stack
self._setText(s, anchor="n")
# set window
self.size = (w, h)
@ -394,6 +404,7 @@ class Layout:
decks = self.game.gameinfo.decks
suits = len(self.game.gameinfo.suits) + bool(self.game.gameinfo.trumps)
w = XM + max(rows+decks, reserves+2+waste)*XS
if reserves:
h = YS+(playcards-1)*self.YOFFSET+YS
else:
@ -409,6 +420,7 @@ class Layout:
self.setRegion(self.s.rows, (-999, -999, x - CW / 2, 999999))
# create foundations
x = w - decks*XS
for suit in range(suits):
for i in range(decks):
self.s.foundations.append(S(x+i*XS, y, suit=suit))
@ -435,16 +447,16 @@ class Layout:
x += XS
# set window
self.size = (XM + (max(rows, reserves)+decks)*XS, h)
self.size = (w, h)
#
# Harp layout
# - top: rows
# - top: reserves, rows
# - bottom: foundations, waste, talon
#
def harpLayout(self, rows, waste, texts=1, playcards=19):
def harpLayout(self, rows, waste, reserves=0, texts=1, playcards=19):
S = self.__createStack
CW, CH = self.CW, self.CH
XM, YM = self.XM, self.YM
@ -453,19 +465,29 @@ class Layout:
decks = self.game.gameinfo.decks
suits = len(self.game.gameinfo.suits) + bool(self.game.gameinfo.trumps)
w = max(rows*XS, (suits*decks+waste+1)*XS, (suits*decks+1)*XS+2*XM)
w = max(reserves*XS, rows*XS, (suits*decks+waste+1)*XS,
(suits*decks+1)*XS+2*XM)
w = XM + w
# set size so that at least 19 cards are fully playable
h = YS + (playcards-1)*self.YOFFSET
h = max(h, 3*YS)
if texts: h += self.TEXT_HEIGHT
if reserves:
h += YS
# top
x, y = (w - (rows*XS - XM))/2, YM
y = YM
if reserves:
x = (w - (reserves*XS - XM))/2
for i in range(reserves):
self.s.reserves.append(S(x, y))
x += XS
y += YS
x = (w - (rows*XS - XM))/2
for i in range(rows):
self.s.rows.append(S(x, y))
x = x + XS
x += XS
# bottom
x, y = XM, YM + h
@ -493,10 +515,12 @@ class Layout:
#
# Klondike layout
# - top: talon, waste, foundations
# - bottom: rows
# - below: rows
# - bottom: reserves
#
def klondikeLayout(self, rows, waste, texts=1, playcards=16, center=1, text_height=0):
def klondikeLayout(self, rows, waste, reserves=0,
texts=1, playcards=16, center=1, text_height=0):
S = self.__createStack
CW, CH = self.CW, self.CH
XM, YM = self.XM, self.YM
@ -507,7 +531,7 @@ class Layout:
foundrows = 1 + (suits > 5)
frows = decks * suits / foundrows
toprows = 1 + waste + frows
maxrows = max(rows, toprows)
maxrows = max(rows, toprows, reserves)
# set size so that at least 2/3 of a card is visible with 16 cards
h = CH * 2 / 3 + (playcards - 1) * self.YOFFSET
@ -544,7 +568,7 @@ class Layout:
x = x + XS
y = y + YS
# bottom
# below
x = XM
if rows < maxrows: x += (maxrows-rows) * XS/2
##y += YM * (3 - foundrows)
@ -554,6 +578,15 @@ class Layout:
self.s.rows.append(S(x, y))
x = x + XS
# bottom
if reserves:
x = (maxrows-reserves)*XS/2
y = h + YM + YS * foundrows
h += YS
for i in range(reserves):
self.s.reserves.append(S(x, y))
x += XS
# set window
self.size = (XM + maxrows * XS, h + YM + YS * foundrows)

View file

@ -57,6 +57,7 @@ __all__ = ['cardsFaceUp',
'SS_FoundationStack',
'RK_FoundationStack',
'AC_FoundationStack',
'SC_FoundationStack',
#'SequenceStack_StackMethods',
'BasicRowStack',
'SequenceRowStack',
@ -2123,6 +2124,27 @@ class AC_FoundationStack(SS_FoundationStack):
elif self.cap.dir < 0: return _('Foundation. Build down by alternate color.')
else: return _('Foundation. Build by same rank.')
# A SameColor_FoundationStack builds up in rank and alternate color.
# It is used in only a few games.
class SC_FoundationStack(SS_FoundationStack):
def __init__(self, x, y, game, suit, **cap):
kwdefault(cap, base_suit=suit)
SS_FoundationStack.__init__(self, x, y, game, ANY_SUIT, **cap)
def acceptsCards(self, from_stack, cards):
if not SS_FoundationStack.acceptsCards(self, from_stack, cards):
return 0
if self.cards:
# check the color
if cards[0].color != self.cards[-1].color:
return 0
return 1
def getHelp(self):
if self.cap.dir > 0: return _('Foundation. Build up by color.')
elif self.cap.dir < 0: return _('Foundation. Build down by color.')
else: return _('Foundation. Build by same rank.')
# /***********************************************************************
# // Abstract classes for row stacks.

View file

@ -37,7 +37,7 @@
__all__ = ['PysolMenubar']
# imports
import math, os, sys, re
import math, os, sys, re, traceback
import Tile as Tkinter
import tkFileDialog
@ -49,6 +49,7 @@ 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.settings import DEBUG
from pysollib.gamedb import GI
from pysollib.actions import PysolMenubarActions
@ -63,6 +64,7 @@ from findcarddialog import connect_game_find_card_dialog, destroy_find_card_dial
from solverdialog import connect_game_solver_dialog
from tkwrap import MfxRadioMenuItem, MfxCheckMenuItem, StringVar
from tkwidget import MfxMessageDialog
from wizarddialog import WizardDialog
#from toolbar import TOOLBAR_BUTTONS
from tkconst import TOOLBAR_BUTTONS
@ -351,7 +353,7 @@ class PysolMenubar(PysolMenubarActions):
if sys.platform == "darwin": m = "Cmd-"
if WIN_SYSTEM == "aqua":
applemenu=MfxMenu(self.__menubar, n_("apple"))
applemenu=MfxMenu(self.__menubar, "apple")
applemenu.add_command(label=_("&About ")+PACKAGE, command=self.mHelpAbout)
menu = MfxMenu(self.__menubar, n_("&File"))
@ -404,6 +406,10 @@ class PysolMenubar(PysolMenubarActions):
menu.add_command(label=n_("Restart"), command=self.mRestart, accelerator=m+"G")
menu.add_separator()
menu.add_command(label=n_("Solitaire &Wizard"), command=self.mWizard)
menu.add_command(label=n_("Edit current game"), command=self.mWizardEdit)
menu = MfxMenu(self.__menubar, label=n_("&Game"))
menu.add_command(label=n_("&Deal cards"), command=self.mDeal, accelerator="D")
menu.add_command(label=n_("&Auto drop"), command=self.mDrop, accelerator="A")
@ -1356,3 +1362,47 @@ the next time you restart """)+PACKAGE,
n = t.capitalize()
submenu.add_radiobutton(label=n, variable=self.tkopt.theme,
value=t, command=self.mOptTheme)
def wizardDialog(self, edit=False):
from pysollib.wizardutil import write_game, reset_wizard
if edit:
reset_wizard(self.game)
d = WizardDialog(self.top, _('Solitaire Wizard'), self.app)
if d.status == 0 and d.button == 0:
try:
if edit:
gameid = write_game(self.app, game=self.game)
else:
gameid = write_game(self.app)
except Exception, err:
if DEBUG:
traceback.print_exc()
d = MfxMessageDialog(self.top, title=_('Save game error'),
text=_('''
Error while saving game.
%s
''') % str(err),
bitmap='error')
return
if SELECT_GAME_MENU and not edit:
gi = self.app.getGameInfo(gameid)
label = gettext(gi.name)
menu = self.__menupath[".menubar.select.frenchgames.cusomgames"][2]
menu.add_radiobutton(command=self.mSelectGame,
variable=self.tkopt.gameid,
value=gameid, label=label, name=None)
self.tkopt.gameid.set(gameid)
self._mSelectGame(gameid, force=True)
def mWizard(self, *event):
if self._cancelDrag(break_pause=False): return
self.wizardDialog()
def mWizardEdit(self, *event):
if self._cancelDrag(break_pause=False): return
self.wizardDialog(edit=True)

View file

@ -63,9 +63,9 @@ class SolverDialog(MfxDialog):
self.solving_methods = {
'A*': 'a-star',
'Breadth-First Search': 'bfs',
'Depth-First Search': 'dfs', # default
'Depth-First Search': 'soft-dfs', # default
'A randomized DFS': 'random-dfs',
'"Soft" DFS': 'soft-dfs',
##'"Soft" DFS': 'soft-dfs',
}
self.games = {} # key: gamename; value: gameid
@ -99,8 +99,12 @@ class SolverDialog(MfxDialog):
).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']
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'))

View file

@ -247,27 +247,30 @@ class HTMLViewer:
##self.defcursor = 'xterm'
self.handcursor = "hand2"
frame = Tkinter.Frame(parent)
frame.pack(expand=True, fill='both')
# create buttons
button_width = 8
self.homeButton = Tkinter.Button(parent, text=_("Index"),
self.homeButton = Tkinter.Button(frame, text=_("Index"),
width=button_width,
command=self.goHome)
self.homeButton.grid(row=0, column=0, sticky='w')
self.backButton = Tkinter.Button(parent, text=_("Back"),
self.backButton = Tkinter.Button(frame, text=_("Back"),
width=button_width,
command=self.goBack)
self.backButton.grid(row=0, column=1, sticky='w')
self.forwardButton = Tkinter.Button(parent, text=_("Forward"),
self.forwardButton = Tkinter.Button(frame, text=_("Forward"),
width=button_width,
command=self.goForward)
self.forwardButton.grid(row=0, column=2, sticky='w')
self.closeButton = Tkinter.Button(parent, text=_("Close"),
self.closeButton = Tkinter.Button(frame, text=_("Close"),
width=button_width,
command=self.destroy)
self.closeButton.grid(row=0, column=3, sticky='e')
# create text widget
text_frame = Tkinter.Frame(parent)
text_frame = Tkinter.Frame(frame)
text_frame.grid(row=1, column=0, columnspan=4,
sticky='nsew', padx=1, pady=1)
vbar = Tkinter.Scrollbar(text_frame)
@ -282,10 +285,10 @@ class HTMLViewer:
vbar["command"] = self.text.yview
# statusbar
self.statusbar = HtmlStatusbar(parent, row=2, column=0, columnspan=4)
self.statusbar = HtmlStatusbar(frame, row=2, column=0, columnspan=4)
parent.columnconfigure(2, weight=1)
parent.rowconfigure(1, weight=1)
frame.columnconfigure(2, weight=1)
frame.rowconfigure(1, weight=1)
# load images
for name, fn in self.symbols_fn.items():

View file

@ -0,0 +1,103 @@
##---------------------------------------------------------------------------##
##
## 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__ = ['WizardDialog']
# imports
from Tile import *
# PySol imports
from pysollib.mfxutil import destruct, kwdefault, KwStruct, Struct
from pysollib.wizardutil import WizardWidgets
# Toolkit imports
from tkwidget import MfxDialog
from tkwidget import PysolScale
# /***********************************************************************
# //
# ************************************************************************/
class WizardDialog(MfxDialog):
def __init__(self, parent, title, app, **kw):
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 = Frame(top_frame)
frame.pack(expand=True, fill='both', padx=10, pady=10)
frame.columnconfigure(0, weight=1)
notebook = Notebook(frame)
notebook.pack(expand=True, fill='both')
for w in WizardWidgets:
if isinstance(w, basestring):
frame = Frame(notebook)
notebook.add(frame, text=w)
row = 0
continue
Label(frame, text=w.label).grid(row=row, column=0)
if w.widget == 'entry':
w.variable = var = StringVar()
en = Entry(frame, textvariable=var)
en.grid(row=row, column=1, sticky='ew')
elif w.widget == 'menu':
w.variable = var = StringVar()
##OptionMenu(frame, var, *w.values).grid(row=row, column=1)
cb = Combobox(frame, values=tuple(w.values), textvariable=var,
state='readonly', width=20)
cb.grid(row=row, column=1, sticky='ew', padx=2, pady=2)
elif w.widget == 'spin':
w.variable = var = IntVar()
from_, to = w.values
##s = Spinbox(frame, textvariable=var, from_=from_, to=to)
s = PysolScale(frame, from_=from_, to=to, resolution=1,
orient='horizontal',
variable=var)
s.grid(row=row, column=1, sticky='ew')
if w.current_value is None:
var.set(w.default)
else:
var.set(w.current_value)
row += 1
focus = self.createButtons(bottom_frame, kw)
self.mainloop(focus, kw.timeout)
def initKw(self, kw):
kw = KwStruct(kw,
strings=(_('&OK'), _('&Cancel')),
default=0,
)
return MfxDialog.initKw(self, kw)

View file

@ -348,7 +348,7 @@ class PysolMenubar(PysolMenubarActions):
if sys.platform == "darwin": m = "Cmd-"
if WIN_SYSTEM == "aqua":
applemenu=MfxMenu(self.__menubar, n_("apple"))
applemenu=MfxMenu(self.__menubar, "apple")
applemenu.add_command(label=_("&About ")+PACKAGE, command=self.mHelpAbout)
menu = MfxMenu(self.__menubar, n_("&File"))

View file

@ -62,9 +62,9 @@ class SolverDialog(MfxDialog):
self.solving_methods = {
'A*': 'a-star',
'Breadth-First Search': 'bfs',
'Depth-First Search': 'dfs', # default
'Depth-First Search': 'soft-dfs', # default
'A randomized DFS': 'random-dfs',
'"Soft" DFS': 'soft-dfs',
##'"Soft" DFS': 'soft-dfs',
}
self.games = {} # key: gamename; value: gameid
@ -102,8 +102,12 @@ class SolverDialog(MfxDialog):
).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']
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)

348
pysollib/wizardutil.py Normal file
View file

@ -0,0 +1,348 @@
##---------------------------------------------------------------------------##
##
## 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.
##
##---------------------------------------------------------------------------##
import sys, os
from gamedb import GI, loadGame
from util import *
from stack import *
#from game import Game
from layout import Layout
#from hint import AbstractHint, DefaultHint, CautiousDefaultHint
#from pysoltk import MfxCanvasText
gettext = _
n_ = lambda x: x
# /***********************************************************************
# //
# ************************************************************************/
class WizSetting:
def __init__(self, values_map, default, var_name,
label, widget='menu'):
self.values_map = values_map
self.default = gettext(default)
##self.values_dict = dict(self.values_map)
self.translate_map = {}
##self.values = [i[0] for i in self.values_map]
if widget == 'menu':
self.values = []
for k, v in self.values_map:
t = gettext(k)
self.values.append(t)
self.translate_map[t] = k
assert self.default in self.values
else:
self.values = self.values_map
self.var_name = var_name
self.label = label
self.widget = widget
self.variable = None # Tk variable
self.current_value = None
GameName = WizSetting(
values_map = (),
default = 'My Game',
widget = 'entry',
label = _('Name:'),
var_name = 'name',
)
SkillLevel = WizSetting(
values_map = ((n_('Luck only'), GI.SL_LUCK),
(n_('Mostly luck'), GI.SL_MOSTLY_LUCK),
(n_('Balanced'), GI.SL_BALANCED),
(n_('Mostly skill'), GI.SL_MOSTLY_SKILL),
(n_('Skill only'), GI.SL_SKILL),
),
default = n_('Balanced'),
label = _('Skill level:'),
var_name = 'skill_level',
)
NumDecks = WizSetting(
values_map = ((n_('One'), 1),
(n_('Two'), 2),
(n_('Three'), 3),
(n_('Four'), 4)),
default = n_('One'),
label = _('Number of decks:'),
var_name = 'decks',
)
LayoutType = WizSetting(
values_map = ((n_('FreeCell'), Layout.freeCellLayout),
(n_('Klondike'), Layout.klondikeLayout),
(n_('Gypsy'), Layout.gypsyLayout),
(n_('Harp'), Layout.harpLayout),
),
default = n_('FreeCell'),
label = _('Layout:'),
var_name = 'layout',
)
TalonType = WizSetting(
values_map = ((n_('Initial dealing'), InitialDealTalonStack),
(n_('Deal to waste'), WasteTalonStack),
(n_('Deal to rows'), DealRowTalonStack),
),
default = n_('Initial dealing'),
label = _('Type of talon:'),
var_name = 'talon',
)
Redeals = WizSetting(
values_map = ((n_('No redeals'), 0),
(n_('One redeal'), 1),
(n_('Two redeals'), 2),
(n_('Unlimited redeals'), -1),
),
default = n_('No redeals'),
label = _('Number of redeals:'),
var_name = 'redeals',
)
FoundType = WizSetting(
values_map = ((n_('Same suit'), SS_FoundationStack),
(n_('Alternate color'), AC_FoundationStack),
(n_('Same color'), SC_FoundationStack),
(n_('Rank'), RK_FoundationStack),
),
default = n_('Same suit'),
label = _('Type:'),
var_name = 'found_type',
)
FoundBaseCard = WizSetting(
values_map = ((n_('Ace'), ACE), (n_('King'), KING)),
default = n_('Ace'),
label = _('Base card:'),
var_name = 'found_base_card',
)
FoundDir = WizSetting(
values_map = ((n_('Up'), 1), (n_('Down'), -1)),
default = n_('Up'),
label = _('Direction:'),
var_name = 'found_dir',
)
FoundWrap = WizSetting(
values_map = ((n_('Yes'), True), (n_('No'), False)),
default = n_('No'),
label = _('Wrapping:'),
var_name = 'found_wrap',
)
FoundMaxMove = WizSetting(
values_map = ((n_('No move'), 0,), (n_('One card'), 1)),
default = n_('One card'),
label = _('Max move cards:'),
var_name = 'found_max_move',
)
RowsNum = WizSetting(
values_map = (1, 20),
default = 8,
widget = 'spin',
label = _('Number of rows:'),
var_name = 'rows_num',
)
RowsType = WizSetting(
values_map = ((n_('Same suit'), SS_RowStack),
(n_('Alternate color'), AC_RowStack),
(n_('Same color'), SC_RowStack),
(n_('Rank'), RK_RowStack),
(n_('Any suit but the same'), BO_RowStack),
),
default = n_('Alternate color'),
label = _('Type:'),
var_name = 'rows_type',
)
RowsBaseCard = WizSetting(
values_map = ((n_('Ace'), ACE),
(n_('King'), KING),
(n_('Any'), ANY_RANK),
(n_('No'), NO_RANK),
),
default = n_('Any'),
label = _('Base card:'),
var_name = 'rows_base_card',
)
RowsDir = WizSetting(
values_map = ((n_('Up'), 1), (n_('Down'), -1)),
default = n_('Down'),
label = _('Direction:'),
var_name = 'rows_dir',
)
RowsWrap = WizSetting(
values_map = ((n_('Yes'), True), (n_('No'), False)),
default = n_('No'),
label = _('Wrapping:'),
var_name = 'rows_wrap',
)
RowsMaxMove = WizSetting(
values_map = ((n_('One card'), 1), (n_('Unlimited'), UNLIMITED_MOVES)),
default = n_('Unlimited'),
label = _('Max move cards:'),
var_name = 'rows_max_move',
)
ReservesNum = WizSetting(
values_map = (0, 20),
default = 4,
widget = 'spin',
label = _('Number of reserves:'),
var_name = 'reserves_num',
)
ReservesType = WizSetting(
values_map = ((n_('FreeCell'), ReserveStack),
(n_('Reserve'), OpenStack),
),
default = n_('FreeCell'),
label = n_('Type of reserves:'),
var_name = 'reserves_type',
)
DealType = WizSetting(
values_map = ((n_('Triangle'), 'triangle'),
(n_('Rectangle'), 'rectangle'),
),
default = n_('Rectangle'),
label = _('Type:'),
var_name = 'deal_type',
)
DealFaceUp = WizSetting(
values_map = ((n_('Top cards'), 'top'), (n_('All cards'), 'all')),
default = n_('All cards'),
label = _('Face-up:'),
var_name = 'deal_faceup',
)
DealMaxRows = WizSetting(
values_map = (0, 20),
default = 7,
widget = 'spin',
label = _('Deal to rows:'),
var_name = 'deal_to_rows',
)
DealToReseves = WizSetting(
values_map = (0, 20),
default = 0,
widget = 'spin',
label = _('Deal ro reserves:'),
var_name = 'deal_to_reserves',
)
WizardWidgets = (
_('General'),
GameName,
SkillLevel,
NumDecks,
LayoutType,
_('Talon'),
TalonType,
Redeals,
_('Foundations'),
FoundType,
FoundBaseCard,
FoundDir,
FoundWrap,
FoundMaxMove,
_('Tableau'),
RowsNum,
RowsType,
RowsBaseCard,
RowsDir,
RowsWrap,
RowsMaxMove,
_('Reserves'),
ReservesNum,
ReservesType,
_('Initial dealing'),
DealType,
DealFaceUp,
DealMaxRows,
DealToReseves,
)
def write_game(app, game=None):
if game is None:
# new game
d = app.dn.plugins
ls = os.listdir(d)
n = 1
while True:
fn = os.path.join(d, 'customgame%d.py' % n) # file name
mn = 'customgame%d' % n # module name
gameid = 200000+n
if not os.path.exists(fn):
break
n += 1
check_game = True
else:
# edit current game
fn = game.SETTINGS['file']
fn = os.path.join(app.dn.plugins, fn)
mn = game.__module__
gameid = game.SETTINGS['gameid']
n = gameid-200000
check_game = False
##print '===>', fn
fd = open(fn, 'w')
fd.write('''\
## THIS FILE WAS GENERATED AUTOMATICALLY BY SOLITAIRE WIZARD
## DO NOT EDIT
from pysollib.customgame import CustomGame, registerCustomGame
class MyCustomGame(CustomGame):
WIZARD_VERSION = 1
SETTINGS = {
''')
for w in WizardWidgets:
if isinstance(w, basestring):
continue
v = w.variable.get()
if w.widget == 'menu':
v = w.translate_map[v]
if isinstance(v, int):
fd.write(" '%s': %i,\n" % (w.var_name, v))
else:
fd.write(" '%s': '%s',\n" % (w.var_name, v))
fd.write(" 'gameid': %i,\n" % gameid)
fd.write(" 'file': '%s',\n" % os.path.split(fn)[1])
fd.write('''\
}
registerCustomGame(MyCustomGame)
''')
fd.close()
loadGame(mn, fn, check_game=check_game)
return gameid
def reset_wizard(game):
s = game.SETTINGS
for w in WizardWidgets:
if isinstance(w, basestring):
continue
v = s[w.var_name]
if w.widget == 'menu':
v = gettext(v)
w.current_value = v