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

+ 2 new games

* moved `Options' from app.py to options.py
* added validation of config file
* improved data/tcl/fsdialog*.tcl


git-svn-id: file:///home/shlomif/Backup/svn-dumps/PySolFC/svnsync-repos/pysolfc/PySolFC/trunk@182 efabe8c0-fbe8-4139-b769-b5e6d273206e
This commit is contained in:
skomoroh 2007-07-03 21:57:17 +00:00
parent fb1141d96c
commit 0f198dec99
9 changed files with 885 additions and 487 deletions

View file

@ -895,48 +895,12 @@ proc ::ttk::dialog::file::scrollhdr {w first last} {
proc ::ttk::dialog::file::configure {w} { proc ::ttk::dialog::file::configure {w} {
UpdateWhenIdle $w UpdateWhenIdle $w
return
set dataName [winfo name $w]
upvar ::ttk::dialog::file::$dataName data
if {$data(columns) == 0} return
set dir ::ttk::dialog::image::folder
set file ::ttk::dialog::image::file
set h [winfo height $data(fileArea)]
set rows [expr {$h / 18}]
if {$rows == $data(rows)} return
set t $data(fileArea)
set lines $rows
set row 1
set col 0
$t configure -state normal
$t delete 1.0 end
foreach {name type} $data(list) {
set idx $row.end
set image [expr {$type eq "directory" ? $dir : $file}]
$t tag add file [$t image create $idx -image $image]
$t insert $idx " $name" file "\t"
if {[incr row] > $lines} {
incr col
set row 1
} elseif {$col == 0} {
$t insert $idx "\n"
}
}
$t insert 1.end "\t"
$t configure -state disabled
set data(columns) [expr {$row > 1 ? $col + 1 : $col}]
set data(rows) $lines
} }
proc ::ttk::dialog::file::setopt {w option var} { proc ::ttk::dialog::file::setopt {w option var} {
set dataName [winfo name $w] set dataName [winfo name $w]
upvar ::ttk::dialog::file::$dataName data upvar ::ttk::dialog::file::$dataName data
upvar #0 $var value upvar #0 $var value
set data($option) $value set data($option) $value
UpdateWhenIdle $w UpdateWhenIdle $w
@ -1039,7 +1003,7 @@ proc ::ttk::dialog::file::NewDirExit {w {save 0}} {
set newdir [file join $dir [$w.new.f.box get]] set newdir [file join $dir [$w.new.f.box get]]
if {[catch {file mkdir $newdir} err]} { if {[catch {file mkdir $newdir} err]} {
ttk::messageBox -type ok -parent $w.new -icon error \ ttk::messageBox -type ok -parent $w.new -icon error \
-message "$err" -message "$err" -title Error
return return
} else { } else {
ChangeDir $w $newdir ChangeDir $w $newdir
@ -1079,7 +1043,7 @@ proc ::ttk::dialog::file::Done {w} {
if {[file exists $path]} { if {[file exists $path]} {
if {[string equal $data(type) save]} { if {[string equal $data(type) save]} {
set reply [ttk::messageBox -icon warning -type yesno \ set reply [ttk::messageBox -icon warning -type yesno \
-parent $w -message "File\ -parent $w -title Warning -message "File\
\"$path\" already exists.\nDo\ \"$path\" already exists.\nDo\
you want to overwrite it?"] you want to overwrite it?"]
if {[string equal $reply "no"]} {return} if {[string equal $reply "no"]} {return}
@ -1087,7 +1051,8 @@ proc ::ttk::dialog::file::Done {w} {
} else { } else {
if {[string equal $data(type) open]} { if {[string equal $data(type) open]} {
ttk::messageBox -icon warning -type ok -parent $w \ ttk::messageBox -icon warning -type ok -parent $w \
-message "File \"$path\" does not exist." -title Error \
-message "File \"$path\" does not exist."
return return
} }
} }
@ -1109,7 +1074,7 @@ proc ::ttk::dialog::file::chdir {w} {
ttk::messageBox -type ok -parent $w \ ttk::messageBox -type ok -parent $w \
-message "Cannot change to the directory\ -message "Cannot change to the directory\
\"$data(selectPath)\".\nPermission denied." \ \"$data(selectPath)\".\nPermission denied." \
-icon warning -icon warning -title Error
} }
return -code break return -code break
} }

View file

@ -1003,7 +1003,7 @@ proc ::ttk::dialog::file::NewDirExit {w {save 0}} {
set newdir [file join $dir [$w.new.f.box get]] set newdir [file join $dir [$w.new.f.box get]]
if {[catch {file mkdir $newdir} err]} { if {[catch {file mkdir $newdir} err]} {
ttk::messageBox -type ok -parent $w.new -icon error \ ttk::messageBox -type ok -parent $w.new -icon error \
-message "$err" -message "$err" -title Error
return return
} else { } else {
ChangeDir $w $newdir ChangeDir $w $newdir
@ -1043,7 +1043,7 @@ proc ::ttk::dialog::file::Done {w} {
if {[file exists $path]} { if {[file exists $path]} {
if {[string equal $data(type) save]} { if {[string equal $data(type) save]} {
set reply [ttk::messageBox -icon warning -type yesno \ set reply [ttk::messageBox -icon warning -type yesno \
-parent $w -message "File\ -parent $w -title Warning -message "File\
\"$path\" already exists.\nDo\ \"$path\" already exists.\nDo\
you want to overwrite it?"] you want to overwrite it?"]
if {[string equal $reply "no"]} {return} if {[string equal $reply "no"]} {return}
@ -1051,7 +1051,8 @@ proc ::ttk::dialog::file::Done {w} {
} else { } else {
if {[string equal $data(type) open]} { if {[string equal $data(type) open]} {
ttk::messageBox -icon warning -type ok -parent $w \ ttk::messageBox -icon warning -type ok -parent $w \
-message "File \"$path\" does not exist." -title Error \
-message "File \"$path\" does not exist."
return return
} }
} }
@ -1073,7 +1074,7 @@ proc ::ttk::dialog::file::chdir {w} {
ttk::messageBox -type ok -parent $w \ ttk::messageBox -type ok -parent $w \
-message "Cannot change to the directory\ -message "Cannot change to the directory\
\"$data(selectPath)\".\nPermission denied." \ \"$data(selectPath)\".\nPermission denied." \
-icon warning -icon warning -title Error
} }
return -code break return -code break
} }

View file

@ -35,9 +35,8 @@
# imports # imports
import sys, os, re import os, re
import traceback import traceback
import ConfigParser
# PySol imports # PySol imports
from mfxutil import destruct, Struct from mfxutil import destruct, Struct
@ -53,6 +52,7 @@ from resource import Music, MusicManager
from images import Images, SubsampledImages from images import Images, SubsampledImages
from pysolrandom import PysolRandom from pysolrandom import PysolRandom
from gamedb import GI, GAME_DB, loadGame from gamedb import GI, GAME_DB, loadGame
from options import Options
from settings import TOP_SIZE, TOOLKIT from settings import TOP_SIZE, TOOLKIT
from settings import DEBUG from settings import DEBUG
from winsystems import TkSettings from winsystems import TkSettings
@ -68,445 +68,11 @@ from pysoltk import PysolStatusbar, HelpStatusbar
from pysoltk import SelectCardsetDialogWithPreview from pysoltk import SelectCardsetDialogWithPreview
from pysoltk import SelectDialogTreeData from pysoltk import SelectDialogTreeData
from pysoltk import HTMLViewer from pysoltk import HTMLViewer
from pysoltk import TOOLBAR_BUTTONS
from pysoltk import destroy_find_card_dialog from pysoltk import destroy_find_card_dialog
from pysoltk import destroy_solver_dialog from pysoltk import destroy_solver_dialog
from help import help_about, destroy_help_html from help import help_about, destroy_help_html
# /***********************************************************************
# // Options
# ************************************************************************/
from configobj import configobj
class Options:
GENERAL_OPTIONS = [
('player', 'str'),
('confirm', 'bool'),
('update_player_stats', 'bool'),
('autofaceup', 'bool'),
('autodrop', 'bool'),
('autodeal', 'bool'),
('quickplay', 'bool'),
('undo', 'bool'),
('bookmarks', 'bool'),
('hint', 'bool'),
('highlight_piles', 'bool'),
('highlight_cards', 'bool'),
('highlight_samerank', 'bool'),
('highlight_not_matching', 'bool'),
('mahjongg_show_removed', 'bool'),
('mahjongg_create_solvable', 'int'),
('shisen_show_hint', 'bool'),
('animations', 'int'),
('redeal_animation', 'bool'),
('win_animation', 'bool'),
('flip_animation', 'bool'),
('shadow', 'bool'),
('shade', 'bool'),
('shrink_face_down', 'bool'),
('shade_filled_stacks', 'bool'),
('demo_logo', 'bool'),
('tile_theme', 'str'),
('default_tile_theme', 'str'),
('toolbar', 'int'),
('toolbar_style', 'str'),
('toolbar_relief', 'str'),
('toolbar_compound', 'str'),
('toolbar_size', 'int'),
('statusbar', 'bool'),
('statusbar_game_number', 'bool'),
('num_cards', 'bool'),
('helpbar', 'bool'),
('num_recent_games', 'int'),
('last_gameid', 'int'),
('game_holded', 'int'),
('wm_maximized', 'bool'),
('splashscreen', 'bool'),
('mouse_type', 'str'),
('mouse_undo', 'bool'),
('negative_bottom', 'bool'),
('randomize_place', 'bool'),
('save_cardsets', 'bool'),
('dragcursor', 'bool'),
('save_games_geometry', 'bool'),
('sound', 'bool'),
('sound_mode', 'int'),
('sound_sample_volume', 'int'),
('sound_music_volume', 'int'),
('tabletile_name', 'str'),
#('toolbar_vars', 'list'),
#('recent_gameid', 'list'),
#('favorite_gameid', 'list'),
]
def __init__(self):
self._config = None # configobj.ConfigObj instance
self._config_encoding = 'utf-8'
self.version_tuple = VERSION_TUPLE # XXX
self.saved = 0 # XXX
# options menu:
self.player = _("Unknown")
self.confirm = True
self.update_player_stats = True
self.autofaceup = True
self.autodrop = False
self.autodeal = True
self.quickplay = True
self.undo = True
self.bookmarks = True
self.hint = True
self.highlight_piles = True
self.highlight_cards = True
self.highlight_samerank = True
self.highlight_not_matching = True
self.mahjongg_show_removed = False
self.mahjongg_create_solvable = 2 # 0 - none, 1 - easy, 2 - hard
self.shisen_show_hint = True
self.animations = 2 # default to Fast
self.redeal_animation = True
self.win_animation = True
self.flip_animation = True
self.shadow = True
self.shade = True
self.shrink_face_down = True
self.shade_filled_stacks = True
self.demo_logo = True
self.tile_theme = 'default'
self.default_tile_theme = 'default'
self.toolbar = 1 # 0 == hide, 1,2,3,4 == top, bottom, lef, right
##self.toolbar_style = 'default'
self.toolbar_style = 'bluecurve'
self.toolbar_relief = 'flat'
self.toolbar_compound = 'none' # icons only
self.toolbar_size = 0
self.toolbar_vars = {}
for w in TOOLBAR_BUTTONS:
self.toolbar_vars[w] = True # show all buttons
self.statusbar = True
self.statusbar_game_number = False # show game number in statusbar
self.num_cards = False
self.helpbar = False
self.splashscreen = True
self.mouse_type = 'drag-n-drop' # or 'sticky-mouse' or 'point-n-click'
self.mouse_undo = False # use mouse for undo/redo
self.negative_bottom = True
# sound
self.sound = True
self.sound_mode = 1
self.sound_sample_volume = 80
self.sound_music_volume = 100
self.sound_samples = {
'areyousure' : True,
'autodrop' : True,
'autoflip' : True,
'autopilotlost' : True,
'autopilotwon' : True,
'deal' : True,
'dealwaste' : True,
'droppair' : True,
'drop' : True,
#'extra' : True,
'flip' : True,
'move' : True,
'nomove' : True,
'redo' : True,
'startdrag' : True,
'turnwaste' : True,
'undo' : True,
'gamefinished' : False,
'gamelost' : False,
'gameperfect' : False,
'gamewon' : False,
}
# fonts
self.fonts = {
"default" : None,
#"default" : ("helvetica", 12),
"sans" : ("times", 12), # for html
"fixed" : ("courier", 12), # for html & log
"small" : ("helvetica", 12),
"canvas_default" : ("helvetica", 12),
#"canvas_card" : ("helvetica", 12),
"canvas_fixed" : ("courier", 12),
"canvas_large" : ("helvetica", 16),
"canvas_small" : ("helvetica", 10),
}
# colors
self.colors = {
'table': '#008200',
'text': '#ffffff',
'piles': '#ffc000',
'cards_1': '#ffc000',
'cards_2': '#0000ff',
'samerank_1': '#ffc000',
'samerank_2': '#0000ff',
'hintarrow': '#303030',
'not_matching': '#ff0000',
}
# delays
self.timeouts = {
'hint': 1.0,
'demo': 1.0,
'raise_card': 1.0,
'highlight_piles': 1.0,
'highlight_cards': 1.0,
'highlight_samerank': 1.0,
}
# additional startup information
self.num_recent_games = 15
self.recent_gameid = []
self.favorite_gameid = []
self.last_gameid = 0 # last game played
self.game_holded = 0 # gameid or 0
self.wm_maximized = 0
self.save_games_geometry = False
self.games_geometry = {} # saved games geometry (gameid: (width, height))
#
self.randomize_place = False
self.save_cardsets = True
self.dragcursor = True
# defaults & constants
self.setDefaults()
self.setConstants()
def setDefaults(self, top=None):
# toolbar
#if WIN_SYSTEM == 'win32':
# self.toolbar_style = 'crystal'
# fonts
if WIN_SYSTEM == 'win32':
self.fonts["sans"] = ("times new roman", 12)
self.fonts["fixed"] = ("courier new", 10)
elif WIN_SYSTEM == 'x11':
self.fonts["sans"] = ("helvetica", 12)
# tile theme
if WIN_SYSTEM == 'win32':
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 = 'clam'
self.default_tile_theme = 'default'
elif WIN_SYSTEM == 'aqua':
self.tile_theme = self.default_tile_theme = 'aqua'
#
sw, sh, sd = 0, 0, 8
if top:
sw, sh, sd = (top.winfo_screenwidth(),
top.winfo_screenheight(),
top.winfo_screendepth())
# bg
if sd > 8:
self.tabletile_name = "Nostalgy.gif" # basename
else:
self.tabletile_name = None
# cardsets
c = "Standard"
if sw < 800 or sh < 600:
c = "2000"
#if sw > 1024 and sh > 768:
# c = 'Dondorf'
self.cardset = {
# game_type: (cardset_name, back_file)
0: (c, ""),
CSI.TYPE_FRENCH: (c, ""),
CSI.TYPE_HANAFUDA: ("Kintengu", ""),
CSI.TYPE_MAHJONGG: ("Crystal Mahjongg", ""),
CSI.TYPE_TAROCK: ("Vienna 2K", ""),
CSI.TYPE_HEXADECK: ("Hex A Deck", ""),
CSI.TYPE_MUGHAL_GANJIFA: ("Mughal Ganjifa", ""),
##CSI.TYPE_NAVAGRAHA_GANJIFA: ("Navagraha Ganjifa", ""),
CSI.TYPE_NAVAGRAHA_GANJIFA: ("Dashavatara Ganjifa", ""),
CSI.TYPE_DASHAVATARA_GANJIFA: ("Dashavatara Ganjifa", ""),
CSI.TYPE_TRUMP_ONLY: ("Matrix", ""),
}
# not changeable options
def setConstants(self):
if 'shuffle' not in self.toolbar_vars:
# new in v.1.1
self.toolbar_vars['shuffle'] = True
if isinstance(self.mahjongg_create_solvable, bool):
# changed in v.1.1
self.mahjongg_create_solvable = 2
pass
def copy(self):
opt = Options()
opt.__dict__.update(self.__dict__)
opt.setConstants()
return opt
def save(self, filename):
config = self._config
# general
for key, t in self.GENERAL_OPTIONS:
val = getattr(self, key)
config['general'][key] = val
config['general']['recent_gameid'] = self.recent_gameid
config['general']['favorite_gameid'] = self.favorite_gameid
visible_buttons = [b for b in self.toolbar_vars
if self.toolbar_vars[b]]
config['general']['visible_buttons'] = visible_buttons
# sound_samples
config['sound_samples'] = self.sound_samples
# fonts
for key, val in self.fonts.items():
if val is None:
continue
config['fonts'][key] = val
# colors
config['colors'] = self.colors
# timeouts
config['timeouts'] = self.timeouts
# cardsets
for key, val in self.cardset.items():
config['cardsets'][str(key)] = val
# games_geometry
config['games_geometry'].clear()
for key, val in self.games_geometry.items():
config['games_geometry'][str(key)] = val
config.write()
##config.write(sys.stdout); print
def _getOption(self, section, key, t):
config = self._config
try:
if t == 'bool':
val = config[section].as_bool(key)
elif t == 'int':
val = config[section].as_int(key)
elif t == 'float':
val = config[section].as_float(key)
elif t == 'list':
val = config[section][key]
assert isinstance(val, (list, tuple))
else: # str
val = config[section][key]
except KeyError:
val = None
except:
print_err('load option error: %s: %s' % (section, key))
traceback.print_exc()
val = None
return val
def load(self, filename):
try:
config = configobj.ConfigObj(filename,
encoding=self._config_encoding)
except configobj.ParseError:
traceback.print_exc()
config = configobj.ConfigObj(encoding=self._config_encoding)
self._config = config
for section in (
'general',
'sound_samples',
'fonts',
'colors',
'timeouts',
'cardsets',
'games_geometry',
):
if section not in config:
config[section] = {}
if not os.path.exists(filename):
config.initial_comment = ['-*- coding: %s -*-' %
self._config_encoding]
return
# general
for key, t in self.GENERAL_OPTIONS:
val = self._getOption('general', key, t)
if val is not None:
setattr(self, key, val)
recent_gameid = self._getOption('general', 'recent_gameid', 'list')
if recent_gameid is not None:
try:
self.recent_gameid = [int(i) for i in recent_gameid]
except:
traceback.print_exc()
favorite_gameid = self._getOption('general', 'favorite_gameid', 'list')
if favorite_gameid is not None:
try:
self.favorite_gameid = [int(i) for i in favorite_gameid]
except:
traceback.print_exc()
visible_buttons = self._getOption('general', 'visible_buttons', 'list')
if visible_buttons is not None:
for key in TOOLBAR_BUTTONS:
self.toolbar_vars[key] = (key in visible_buttons)
# sound_samples
for key in self.sound_samples:
val = self._getOption('sound_samples', key, 'bool')
if val is not None:
self.sound_samples[key] = val
# fonts
for key in self.fonts:
val = self._getOption('fonts', key, 'str')
if val is not None:
try:
val[1] = int(val[1])
except:
traceback.print_exc()
else:
val = tuple(val)
self.fonts[key] = val
# colors
for key in self.colors:
val = self._getOption('colors', key, 'str')
if val is not None:
self.colors[key] = val
# timeouts
for key in self.timeouts:
val = self._getOption('timeouts', key, 'float')
if val is not None:
self.timeouts[key] = val
# cardsets
for key in self.cardset:
val = self._getOption('cardsets', str(key), 'list')
if val is not None:
try:
self.cardset[int(key)] = val
except:
traceback.print_exc()
# games_geometry
for key, val in config['games_geometry'].items():
try:
val = [int(i) for i in val]
assert len(val) == 2
self.games_geometry[int(key)] = val
except:
traceback.print_exc()
# /*********************************************************************** # /***********************************************************************
# // Statistics # // Statistics
# ************************************************************************/ # ************************************************************************/

View file

@ -1970,8 +1970,12 @@ for %d moves.
# #
color = self.app.opt.colors['not_matching'] color = self.app.opt.colors['not_matching']
width = 6 width = 6
x0, y0 = x+width/2-self.canvas.xmargin, y+width/2-self.canvas.ymargin xmargin, ymargin = self.canvas.xmargin, self.canvas.ymargin
x1, y1 = x+w-width-self.canvas.xmargin, y+h-width-self.canvas.ymargin if self.preview:
width = 4
xmargin, ymargin = 0, 0
x0, y0 = x+width/2-xmargin, y+width/2-ymargin
x1, y1 = x+w-width-xmargin, y+h-width-ymargin
r = MfxCanvasRectangle(self.canvas, x0, y0, x1, y1, r = MfxCanvasRectangle(self.canvas, x0, y0, x1, y1,
width=width, fill=None, outline=color) width=width, fill=None, outline=color)
self.canvas.update_idletasks() self.canvas.update_idletasks()

View file

@ -244,9 +244,9 @@ class GI:
## 41, 42, 43, 58, 59, 92, 93, 94, 95, 96, ## 41, 42, 43, 58, 59, 92, 93, 94, 95, 96,
## 100, 105, 111, 112, 113, 130, 200, 201, ## 100, 105, 111, 112, 113, 130, 200, 201,
##)), ##)),
# Gnome AisleRiot 2.2.0 (we have 60 out of 70 games) # Gnome AisleRiot 2.2.0 (we have 61 out of 70 games)
# still missing: # still missing:
# Clock, Gay gordons, Helsinki, # Gay gordons, Helsinki,
# Isabel, Labyrinth, Quatorze, Thieves, # Isabel, Labyrinth, Quatorze, Thieves,
# Treize, Valentine, Yeld. # Treize, Valentine, Yeld.
("Gnome AisleRiot", ( ("Gnome AisleRiot", (
@ -254,7 +254,7 @@ class GI:
41, 42, 43, 45, 48, 58, 59, 67, 89, 91, 92, 93, 94, 95, 96, 41, 42, 43, 45, 48, 58, 59, 67, 89, 91, 92, 93, 94, 95, 96,
100, 105, 111, 112, 113, 130, 139, 144, 146, 147, 148, 200, 100, 105, 111, 112, 113, 130, 139, 144, 146, 147, 148, 200,
201, 206, 224, 225, 229, 230, 233, 257, 258, 280, 281, 282, 201, 206, 224, 225, 229, 230, 233, 257, 258, 280, 281, 282,
283, 284, 551, 552, 553, 283, 284, 551, 552, 553, 737,
)), )),
## KDE Patience 0.7.3 from KDE 1.1.2 (we have 6 out of 9 games) ## KDE Patience 0.7.3 from KDE 1.1.2 (we have 6 out of 9 games)

View file

@ -473,6 +473,139 @@ class BigBen(Game):
shallHighlightMatch = Game._shallHighlightMatch_SSW shallHighlightMatch = Game._shallHighlightMatch_SSW
# /***********************************************************************
# // Clock
# ************************************************************************/
class Clock_RowStack(RK_RowStack):
def _numFaceDown(self):
ncards = 0
for c in self.cards:
if not c.face_up:
ncards += 1
return ncards
def acceptsCards(self, from_stack, cards):
return cards[0].rank == self.id
def moveMove(self, ncards, to_stack, frames=-1, shadow=-1):
self._swapPairMove(ncards, to_stack, frames=-1, shadow=0)
def _swapPairMove(self, n, other_stack, frames=-1, shadow=-1):
is_king = other_stack.cards[-1].rank == KING
game = self.game
old_state = game.enterState(game.S_FILL)
swap = game.s.internals[0]
ncards = other_stack._numFaceDown()
for i in range(ncards):
game.moveMove(n, other_stack, swap, frames=0)
game.moveMove(n, self, other_stack, frames=0)
for i in range(ncards):
game.moveMove(n, swap, other_stack, frames=0)
game.flipMove(other_stack)
game.moveMove(n, other_stack, self)
if is_king:
self._moveKingToBottom()
game.leaveState(old_state)
def _moveKingToBottom(self):
# move king to bottom of stack
game = self.game
swap, swap2 = game.s.internals
game.moveMove(1, self, swap2, frames=0)
ncards = self._numFaceDown()
for i in range(ncards):
game.moveMove(1, self, swap, frames=0)
game.moveMove(1, swap2, self, frames=0)
for i in range(ncards):
game.moveMove(1, swap, self, frames=0)
if not self.cards[-1].face_up:
game.flipMove(self)
self._fillStack()
def _fillStack(self):
c = self.cards[-1]
n = self._numFaceDown()
if n == 0:
return
if c.face_up and c.rank == KING:
self._moveKingToBottom()
def canFlipCard(self):
return False
class Clock(Game):
def createGame(self):
# create layout
l, s = Layout(self), self.s
# set window
dx = l.XS + 3*l.XOFFSET
w = max(5.25*dx + l.XS, 5.5*dx)
self.setSize(l.XM + w, l.YM + 4*l.YS)
# create stacks
for xx, yy in (
(3.25, 0.15),
(4.25, 0.5),
(4.5, 1.5),
(4.25, 2.5),
(3.25, 2.85),
(2.25, 3),
(1.25, 2.85),
(0.25, 2.5),
(0, 1.5),
(0.25, 0.5),
(1.25, 0.15),
(2.25, 0),
):
x = l.XM + xx*dx
y = l.YM + yy*l.YS
stack = Clock_RowStack(x, y, self, max_move=0)
stack.CARD_XOFFSET, stack.CARD_YOFFSET = l.XOFFSET, 0
stack.SHRINK_FACTOR = 1
s.rows.append(stack)
x, y = l.XM + 2.25*dx, l.YM + 1.5*l.YS
stack = Clock_RowStack(x, y, self, max_move=1)
stack.CARD_XOFFSET, stack.CARD_YOFFSET = l.XOFFSET, 0
stack.SHRINK_FACTOR = 1
s.rows.append(stack)
x, y = self.width - l.XS, self.height - l.YS
s.talon = InitialDealTalonStack(x, y, self)
# create an invisible stacks
s.internals.append(InvisibleStack(self))
s.internals.append(InvisibleStack(self))
# default
l.defaultAll()
def startGame(self):
for i in range(3):
self.s.talon.dealRow(frames=0, flip=False)
self.startDealSample()
self.s.talon.dealRow(flip=False)
self.flipMove(self.s.rows[-1])
self.s.rows[-1]._fillStack()
def isGameWon(self):
for r in self.s.rows:
if not r.cards[-1].face_up:
return False
return True
def getHighlightPilesStacks(self):
return ()
def getAutoStacks(self, event=None):
return (), (), ()
# register the game # register the game
registerGame(GameInfo(261, GrandfathersClock, "Grandfather's Clock", registerGame(GameInfo(261, GrandfathersClock, "Grandfather's Clock",
@ -483,6 +616,8 @@ registerGame(GameInfo(690, Hemispheres, "Hemispheres",
GI.GT_2DECK_TYPE, 2, 0, GI.SL_BALANCED, GI.GT_2DECK_TYPE, 2, 0, GI.SL_BALANCED,
altnames=("The Four Continents",) )) altnames=("The Four Continents",) ))
registerGame(GameInfo(697, BigBen, "Big Ben", registerGame(GameInfo(697, BigBen, "Big Ben",
GI.GT_2DECK_TYPE, 2, 0, GI.SL_BALANCED, GI.GT_2DECK_TYPE, 2, 0, GI.SL_BALANCED))
altnames=("Clock",) )) registerGame(GameInfo(737, Clock, "Clock",
GI.GT_1DECK_TYPE, 1, 0, GI.SL_LUCK,
altnames=("Travellers",) ))

View file

@ -440,6 +440,116 @@ class Paganini(BlueMoon):
return True return True
# /***********************************************************************
# // Spoilt
# ************************************************************************/
class Spoilt_RowStack(BasicRowStack):
def acceptsCards(self, from_stack, cards):
#if not BasicRowStack.acceptsCards(self, from_stack, cards):
# return False
card = cards[0]
RSTEP = self.game.RSTEP
RBASE = self.game.RBASE
row, col = divmod(self.id, RSTEP)
# check rank
if card.rank == ACE:
if col != RSTEP-1:
return False
else:
if card.rank - RBASE != col:
return False
# check suit
suit = None
for i in range(row*RSTEP, (row+1)*RSTEP):
r = self.game.s.rows[i]
if r.cards and r.cards[0].face_up:
suit = r.cards[0].suit
break
if suit is not None:
return card.suit == suit
for r in self.game.s.rows: # check other rows
if r.cards and r.cards[0].face_up and r.cards[0].suit == card.suit:
return False
return True
def canFlipCard(self):
return False
class Spoilt_Waste(WasteStack):
def moveMove(self, ncards, to_stack, frames=-1, shadow=-1):
assert ncards == 1 and to_stack in self.game.s.rows
if to_stack.cards:
self._swapPairMove(ncards, to_stack, frames=-1, shadow=0)
else:
WasteStack.moveMove(self, ncards, to_stack, frames, shadow)
def _swapPairMove(self, n, other_stack, frames=-1, shadow=-1):
game = self.game
old_state = game.enterState(game.S_FILL)
swap = game.s.internals[0]
game.flipMove(other_stack)
game.moveMove(n, self, swap, frames=0)
game.moveMove(n, other_stack, self, frames=frames, shadow=shadow)
game.moveMove(n, swap, other_stack, frames=0)
game.leaveState(old_state)
class Spoilt(Game):
RSTEP, RBASE = 8, 6
def createGame(self):
# create layout
l, s = Layout(self), self.s
# set window
self.setSize(l.XM + self.RSTEP*l.XS, l.YM + 5.5*l.YS)
# create stacks
for i in range(4):
x, y, = l.XM, l.YM + i*l.YS
for j in range(self.RSTEP):
s.rows.append(Spoilt_RowStack(x, y, self,
max_accept=1, max_cards=2, min_cards=1))
x += l.XS
x, y = self.width/2 - l.XS, self.height-l.YS
s.talon = WasteTalonStack(x, y, self, max_rounds=1)
l.createText(s.talon, 'n')
x += l.XS
s.waste = Spoilt_Waste(x, y, self, max_cards=1)
# create an invisible stack
s.internals.append(InvisibleStack(self))
# define stack-groups
l.defaultStackGroups()
def startGame(self):
self.startDealSample()
for i in range(4):
rows = self.s.rows[self.RSTEP*i+1:self.RSTEP*(i+1)]
self.s.talon.dealRow(rows=rows, frames=4, flip=False)
self.s.talon.dealCards()
def isGameWon(self):
for r in self.s.rows:
if not r.cards:
return False
if not r.cards[0].face_up:
return False
return True
def getHighlightPilesStacks(self):
return ()
def getAutoStacks(self, event=None):
return (), (), ()
# register the game # register the game
registerGame(GameInfo(53, Montana, "Montana", registerGame(GameInfo(53, Montana, "Montana",
GI.GT_MONTANA | GI.GT_OPEN, 1, 2, GI.SL_MOSTLY_SKILL, GI.GT_MONTANA | GI.GT_OPEN, 1, 2, GI.SL_MOSTLY_SKILL,
@ -465,4 +575,8 @@ registerGame(GameInfo(706, Paganini, "Paganini",
GI.GT_MONTANA | GI.GT_OPEN, 1, 1, GI.SL_MOSTLY_SKILL, GI.GT_MONTANA | GI.GT_OPEN, 1, 1, GI.SL_MOSTLY_SKILL,
ranks=(0, 5, 6, 7, 8, 9, 10, 11, 12), ranks=(0, 5, 6, 7, 8, 9, 10, 11, 12),
altnames=('Long Trip',) )) altnames=('Long Trip',) ))
registerGame(GameInfo(736, Spoilt, "Spoilt",
GI.GT_MONTANA, 1, 0, GI.SL_MOSTLY_LUCK,
ranks=(0, 6, 7, 8, 9, 10, 11, 12),
))

612
pysollib/options.py Normal file
View file

@ -0,0 +1,612 @@
##---------------------------------------------------------------------------##
##
## 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.
##
##---------------------------------------------------------------------------##
# imports
import sys, os
import traceback
# PySol imports
from mfxutil import print_err
from settings import VERSION_TUPLE, WIN_SYSTEM
from resource import CSI
from configobj import configobj, validate
# Toolkit imports
from pysoltk import TOOLBAR_BUTTONS
# /***********************************************************************
# // Options
# ************************************************************************/
configspec = '''
[general]
player = string
confirm = boolean
update_player_stats = boolean
autofaceup = boolean
autodrop = boolean
autodeal = boolean
quickplay = boolean
undo = boolean
bookmarks = boolean
hint = boolean
highlight_piles = boolean
highlight_cards = boolean
highlight_samerank = boolean
highlight_not_matching = boolean
mahjongg_show_removed = boolean
mahjongg_create_solvable = integer(0, 2)
shisen_show_hint = boolean
animations = integer(0, 5)
redeal_animation = boolean
win_animation = boolean
flip_animation = boolean
shadow = boolean
shade = boolean
shrink_face_down = boolean
shade_filled_stacks = boolean
demo_logo = boolean
tile_theme = string
default_tile_theme = string
toolbar = integer(0, 4)
toolbar_style = string
toolbar_relief = string
toolbar_compound = string
toolbar_size = integer(0, 1)
statusbar = boolean
statusbar_game_number = boolean
num_cards = boolean
helpbar = boolean
num_recent_games = integer
last_gameid = integer
game_holded = integer
wm_maximized = boolean
splashscreen = boolean
mouse_type = string
mouse_undo = boolean
negative_bottom = boolean
randomize_place = boolean
save_cardsets = boolean
dragcursor = boolean
save_games_geometry = boolean
sound = boolean
sound_mode = integer
sound_sample_volume = integer(0, 128)
sound_music_volume = integer(0, 128)
tabletile_name = string
recent_gameid = int_list
favorite_gameid = int_list
visible_buttons = string_list
[sound_samples]
move = boolean
autodrop = boolean
drop = boolean
nomove = boolean
gameperfect = boolean
deal = boolean
gamelost = boolean
autopilotwon = boolean
flip = boolean
undo = boolean
gamefinished = boolean
areyousure = boolean
startdrag = boolean
autoflip = boolean
autopilotlost = boolean
turnwaste = boolean
gamewon = boolean
droppair = boolean
redo = boolean
dealwaste = boolean
[fonts]
default = list
sans = list
small = list
fixed = list
canvas_default = list
canvas_small = list
canvas_fixed = list
canvas_large = list
[colors]
piles = string
text = string
table = string
hintarrow = string
cards_1 = string
cards_2 = string
samerank_1 = string
samerank_2 = string
not_matching = string
[timeouts]
highlight_samerank = float(0.2, 9.9)
raise_card = float(0.2, 9.9)
demo = float(0.2, 9.9)
highlight_cards = float(0.2, 9.9)
hint = float(0.2, 9.9)
highlight_piles = float(0.2, 9.9)
[cardsets]
0 = string_list(min=2, max=2)
1 = string_list(min=2, max=2)
2 = string_list(min=2, max=2)
3 = string_list(min=2, max=2)
4 = string_list(min=2, max=2)
5 = string_list(min=2, max=2)
6 = string_list(min=2, max=2)
7 = string_list(min=2, max=2)
8 = string_list(min=2, max=2)
9 = string_list(min=2, max=2)
'''.splitlines()
class Options:
GENERAL_OPTIONS = [
('player', 'str'),
('confirm', 'bool'),
('update_player_stats', 'bool'),
('autofaceup', 'bool'),
('autodrop', 'bool'),
('autodeal', 'bool'),
('quickplay', 'bool'),
('undo', 'bool'),
('bookmarks', 'bool'),
('hint', 'bool'),
('highlight_piles', 'bool'),
('highlight_cards', 'bool'),
('highlight_samerank', 'bool'),
('highlight_not_matching', 'bool'),
('mahjongg_show_removed', 'bool'),
('mahjongg_create_solvable', 'int'),
('shisen_show_hint', 'bool'),
('animations', 'int'),
('redeal_animation', 'bool'),
('win_animation', 'bool'),
('flip_animation', 'bool'),
('shadow', 'bool'),
('shade', 'bool'),
('shrink_face_down', 'bool'),
('shade_filled_stacks', 'bool'),
('demo_logo', 'bool'),
('tile_theme', 'str'),
('default_tile_theme', 'str'),
('toolbar', 'int'),
('toolbar_style', 'str'),
('toolbar_relief', 'str'),
('toolbar_compound', 'str'),
('toolbar_size', 'int'),
('statusbar', 'bool'),
('statusbar_game_number', 'bool'),
('num_cards', 'bool'),
('helpbar', 'bool'),
('num_recent_games', 'int'),
('last_gameid', 'int'),
('game_holded', 'int'),
('wm_maximized', 'bool'),
('splashscreen', 'bool'),
('mouse_type', 'str'),
('mouse_undo', 'bool'),
('negative_bottom', 'bool'),
('randomize_place', 'bool'),
('save_cardsets', 'bool'),
('dragcursor', 'bool'),
('save_games_geometry', 'bool'),
('sound', 'bool'),
('sound_mode', 'int'),
('sound_sample_volume', 'int'),
('sound_music_volume', 'int'),
('tabletile_name', 'str'),
#('toolbar_vars', 'list'),
#('recent_gameid', 'list'),
#('favorite_gameid', 'list'),
]
def __init__(self):
self._config = None # configobj.ConfigObj instance
self._config_encoding = 'utf-8'
self.version_tuple = VERSION_TUPLE # XXX
self.saved = 0 # XXX
# options menu:
self.player = _("Unknown")
self.confirm = True
self.update_player_stats = True
self.autofaceup = True
self.autodrop = False
self.autodeal = True
self.quickplay = True
self.undo = True
self.bookmarks = True
self.hint = True
self.highlight_piles = True
self.highlight_cards = True
self.highlight_samerank = True
self.highlight_not_matching = True
self.mahjongg_show_removed = False
self.mahjongg_create_solvable = 2 # 0 - none, 1 - easy, 2 - hard
self.shisen_show_hint = True
self.animations = 2 # default to Fast
self.redeal_animation = True
self.win_animation = True
self.flip_animation = True
self.shadow = True
self.shade = True
self.shrink_face_down = True
self.shade_filled_stacks = True
self.demo_logo = True
self.tile_theme = 'default'
self.default_tile_theme = 'default'
self.toolbar = 1 # 0 == hide, 1,2,3,4 == top, bottom, lef, right
##self.toolbar_style = 'default'
self.toolbar_style = 'bluecurve'
self.toolbar_relief = 'flat'
self.toolbar_compound = 'none' # icons only
self.toolbar_size = 0
self.toolbar_vars = {}
for w in TOOLBAR_BUTTONS:
self.toolbar_vars[w] = True # show all buttons
self.statusbar = True
self.statusbar_game_number = False # show game number in statusbar
self.num_cards = False
self.helpbar = False
self.splashscreen = True
self.mouse_type = 'drag-n-drop' # or 'sticky-mouse' or 'point-n-click'
self.mouse_undo = False # use mouse for undo/redo
self.negative_bottom = True
# sound
self.sound = True
self.sound_mode = 1
self.sound_sample_volume = 80
self.sound_music_volume = 100
self.sound_samples = {
'areyousure' : True,
'autodrop' : True,
'autoflip' : True,
'autopilotlost' : True,
'autopilotwon' : True,
'deal' : True,
'dealwaste' : True,
'droppair' : True,
'drop' : True,
#'extra' : True,
'flip' : True,
'move' : True,
'nomove' : True,
'redo' : True,
'startdrag' : True,
'turnwaste' : True,
'undo' : True,
'gamefinished' : False,
'gamelost' : False,
'gameperfect' : False,
'gamewon' : False,
}
# fonts
self.fonts = {
"default" : None,
#"default" : ("helvetica", 12),
"sans" : ("times", 12), # for html
"fixed" : ("courier", 12), # for html & log
"small" : ("helvetica", 12),
"canvas_default" : ("helvetica", 12),
#"canvas_card" : ("helvetica", 12),
"canvas_fixed" : ("courier", 12),
"canvas_large" : ("helvetica", 16),
"canvas_small" : ("helvetica", 10),
}
# colors
self.colors = {
'table': '#008200',
'text': '#ffffff',
'piles': '#ffc000',
'cards_1': '#ffc000',
'cards_2': '#0000ff',
'samerank_1': '#ffc000',
'samerank_2': '#0000ff',
'hintarrow': '#303030',
'not_matching': '#ff0000',
}
# delays
self.timeouts = {
'hint': 1.0,
'demo': 1.0,
'raise_card': 1.0,
'highlight_piles': 1.0,
'highlight_cards': 1.0,
'highlight_samerank': 1.0,
}
# additional startup information
self.num_recent_games = 15
self.recent_gameid = []
self.favorite_gameid = []
self.last_gameid = 0 # last game played
self.game_holded = 0 # gameid or 0
self.wm_maximized = 0
self.save_games_geometry = False
self.games_geometry = {} # saved games geometry (gameid: (width, height))
#
self.randomize_place = False
self.save_cardsets = True
self.dragcursor = True
# defaults & constants
self.setDefaults()
self.setConstants()
def setDefaults(self, top=None):
# toolbar
#if WIN_SYSTEM == 'win32':
# self.toolbar_style = 'crystal'
# fonts
if WIN_SYSTEM == 'win32':
self.fonts["sans"] = ("times new roman", 12)
self.fonts["fixed"] = ("courier new", 10)
elif WIN_SYSTEM == 'x11':
self.fonts["sans"] = ("helvetica", 12)
# tile theme
if WIN_SYSTEM == 'win32':
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 = 'clam'
self.default_tile_theme = 'default'
elif WIN_SYSTEM == 'aqua':
self.tile_theme = self.default_tile_theme = 'aqua'
#
sw, sh, sd = 0, 0, 8
if top:
sw, sh, sd = (top.winfo_screenwidth(),
top.winfo_screenheight(),
top.winfo_screendepth())
# bg
if sd > 8:
self.tabletile_name = "Nostalgy.gif" # basename
else:
self.tabletile_name = None
# cardsets
c = "Standard"
if sw < 800 or sh < 600:
c = "2000"
#if sw > 1024 and sh > 768:
# c = 'Dondorf'
self.cardset = {
# game_type: (cardset_name, back_file)
0: (c, ""),
CSI.TYPE_FRENCH: (c, ""),
CSI.TYPE_HANAFUDA: ("Kintengu", ""),
CSI.TYPE_MAHJONGG: ("Crystal Mahjongg", ""),
CSI.TYPE_TAROCK: ("Vienna 2K", ""),
CSI.TYPE_HEXADECK: ("Hex A Deck", ""),
CSI.TYPE_MUGHAL_GANJIFA: ("Mughal Ganjifa", ""),
##CSI.TYPE_NAVAGRAHA_GANJIFA: ("Navagraha Ganjifa", ""),
CSI.TYPE_NAVAGRAHA_GANJIFA: ("Dashavatara Ganjifa", ""),
CSI.TYPE_DASHAVATARA_GANJIFA: ("Dashavatara Ganjifa", ""),
CSI.TYPE_TRUMP_ONLY: ("Matrix", ""),
}
# not changeable options
def setConstants(self):
if 'shuffle' not in self.toolbar_vars:
# new in v.1.1
self.toolbar_vars['shuffle'] = True
if isinstance(self.mahjongg_create_solvable, bool):
# changed in v.1.1
self.mahjongg_create_solvable = 2
pass
def copy(self):
opt = Options()
opt.__dict__.update(self.__dict__)
opt.setConstants()
return opt
def save(self, filename):
config = self._config
# general
for key, t in self.GENERAL_OPTIONS:
val = getattr(self, key)
config['general'][key] = val
config['general']['recent_gameid'] = self.recent_gameid
config['general']['favorite_gameid'] = self.favorite_gameid
visible_buttons = [b for b in self.toolbar_vars
if self.toolbar_vars[b]]
config['general']['visible_buttons'] = visible_buttons
# sound_samples
config['sound_samples'] = self.sound_samples
# fonts
for key, val in self.fonts.items():
if val is None:
continue
config['fonts'][key] = val
# colors
config['colors'] = self.colors
# timeouts
config['timeouts'] = self.timeouts
# cardsets
for key, val in self.cardset.items():
config['cardsets'][str(key)] = val
# games_geometry
config['games_geometry'].clear()
for key, val in self.games_geometry.items():
config['games_geometry'][str(key)] = val
config.write()
##config.write(sys.stdout); print
def _getOption(self, section, key, t):
config = self._config
try:
if config[section][key] is None:
# invalid value
return None
if t == 'bool':
val = config[section].as_bool(key)
elif t == 'int':
val = config[section].as_int(key)
elif t == 'float':
val = config[section].as_float(key)
elif t == 'list':
val = config[section][key]
assert isinstance(val, (list, tuple))
else: # str
val = config[section][key]
except KeyError:
val = None
except:
print_err('load option error: %s: %s' % (section, key))
traceback.print_exc()
val = None
return val
def load(self, filename):
try:
config = configobj.ConfigObj(filename,
configspec=configspec,
encoding=self._config_encoding)
except configobj.ParseError:
traceback.print_exc()
config = configobj.ConfigObj(configspec=configspec,
encoding=self._config_encoding)
self._config = config
for section in (
'general',
'sound_samples',
'fonts',
'colors',
'timeouts',
'cardsets',
'games_geometry',
):
if section not in config:
config[section] = {}
if not os.path.exists(filename):
config.initial_comment = ['-*- coding: %s -*-' %
self._config_encoding]
return
# validation
vdt = validate.Validator()
res = config.validate(vdt)
##from pprint import pprint; pprint(res)
if res is not True:
for section, data in res.items():
if data is True:
continue
for key, value in data.items():
if value is False:
print_err('config file: validation error: '
'section: "%s", key: "%s"' % (section, key))
config[section][key] = None
# general
for key, t in self.GENERAL_OPTIONS:
val = self._getOption('general', key, t)
if val is not None:
setattr(self, key, val)
recent_gameid = self._getOption('general', 'recent_gameid', 'list')
if recent_gameid is not None:
try:
self.recent_gameid = [int(i) for i in recent_gameid]
except:
traceback.print_exc()
favorite_gameid = self._getOption('general', 'favorite_gameid', 'list')
if favorite_gameid is not None:
try:
self.favorite_gameid = [int(i) for i in favorite_gameid]
except:
traceback.print_exc()
visible_buttons = self._getOption('general', 'visible_buttons', 'list')
if visible_buttons is not None:
for key in TOOLBAR_BUTTONS:
self.toolbar_vars[key] = (key in visible_buttons)
# sound_samples
for key in self.sound_samples:
val = self._getOption('sound_samples', key, 'bool')
if val is not None:
self.sound_samples[key] = val
# fonts
for key in self.fonts:
val = self._getOption('fonts', key, 'str')
if val is not None:
try:
val[1] = int(val[1])
except:
traceback.print_exc()
else:
val = tuple(val)
self.fonts[key] = val
# colors
for key in self.colors:
val = self._getOption('colors', key, 'str')
if val is not None:
self.colors[key] = val
# timeouts
for key in self.timeouts:
val = self._getOption('timeouts', key, 'float')
if val is not None:
self.timeouts[key] = val
# cardsets
for key in self.cardset:
val = self._getOption('cardsets', str(key), 'list')
if val is not None:
try:
self.cardset[int(key)] = val
except:
traceback.print_exc()
# games_geometry
for key, val in config['games_geometry'].items():
try:
val = [int(i) for i in val]
assert len(val) == 2
self.games_geometry[int(key)] = val
except:
traceback.print_exc()

View file

@ -68,6 +68,7 @@ kw = {
'license' : 'GPL', 'license' : 'GPL',
'scripts' : ['pysol.py'], 'scripts' : ['pysol.py'],
'packages' : ['pysollib', 'packages' : ['pysollib',
'pysollib.configobj',
'pysollib.macosx', 'pysollib.macosx',
'pysollib.winsystems', 'pysollib.winsystems',
'pysollib.tk', 'pysollib.tk',