diff --git a/pysollib/actions.py b/pysollib/actions.py index 83e20763..25561a40 100644 --- a/pysollib/actions.py +++ b/pysollib/actions.py @@ -195,7 +195,7 @@ class PysolMenubarActions: tkopt.negative_bottom.set(opt.negative_bottom) for w in TOOLBAR_BUTTONS: tkopt.toolbar_vars[w].set(opt.toolbar_vars[w]) - if game.gameinfo.category == GI.GC_FRENCH: + if game.canFindCard(): connect_game_find_card_dialog(game) else: destroy_find_card_dialog() @@ -282,7 +282,7 @@ class PysolMenubarActions: ms.quickplay = 1 if opt.highlight_piles and game.getHighlightPilesStacks(): ms.highlight_piles = 1 - if game.gameinfo.category == GI.GC_FRENCH: + if game.canFindCard(): ms.find_card = 1 if game.app.getGameRulesFilename(game.id): # note: this may return "" ms.rules = 1 diff --git a/pysollib/app.py b/pysollib/app.py index 32faa22f..fe0c8615 100644 --- a/pysollib/app.py +++ b/pysollib/app.py @@ -1380,7 +1380,7 @@ Please select a %s type %s. return None config = CardsetConfig() if not self._parseCardsetConfig(config, lines): - ##print filename, 'invalide config' + ##print filename, 'invalid config' return None if config.CARDD > self.top.winfo_screendepth(): return None @@ -1444,7 +1444,7 @@ Please select a %s type %s. return 0 cs.year = int(m.group(1)) if len(cs.ext) < 2 or cs.ext[0] != ".": - if _debug: print_err(1, msg='invalide extention') + if _debug: print_err(1, msg='invalid extention') return 0 # line[1]: identifier/name if not line[1]: @@ -1453,19 +1453,19 @@ Please select a %s type %s. cs.ident = line[1] m = re.search(r"^(.*;)?([^;]+)$", cs.ident) if not m: - if _debug: print_err(2, msg='invalide format') + if _debug: print_err(2, msg='invalid format') return 0 cs.name = m.group(2).strip() # line[2]: CARDW, CARDH, CARDD m = re.search(r"^(\d+)\s+(\d+)\s+(\d+)", line[2]) if not m: - if _debug: print_err(3, msg='invalide format') + if _debug: print_err(3, msg='invalid format') return 0 cs.CARDW, cs.CARDH, cs.CARDD = int(m.group(1)), int(m.group(2)), int(m.group(3)) # line[3]: CARD_UP_YOFFSET, CARD_DOWN_YOFFSET, SHADOW_XOFFSET, SHADOW_YOFFSET m = re.search(r"^(\d+)\s+(\d+)\s+(\d+)\s+(\d+)", line[3]) if not m: - if _debug: print_err(4, msg='invalide format') + if _debug: print_err(4, msg='invalid format') return 0 cs.CARD_XOFFSET = int(m.group(1)) cs.CARD_YOFFSET = int(m.group(2)) diff --git a/pysollib/game.py b/pysollib/game.py index 77fb3c5e..e084847e 100644 --- a/pysollib/game.py +++ b/pysollib/game.py @@ -1590,7 +1590,7 @@ for %d moves. return card1.color != card2.color and abs(card1.rank-card2.rank) == 1 def _shallHighlightMatch_ACW(self, stack1, card1, stack2, card2): - # by alternate color with wrapping (only for france games) + # by alternate color with wrapping (only for french games) return (card1.color != card2.color and ((card1.rank + 1) % 13 == card2.rank or (card2.rank + 1) % 13 == card1.rank)) @@ -1600,7 +1600,7 @@ for %d moves. return card1.suit == card2.suit and abs(card1.rank-card2.rank) == 1 def _shallHighlightMatch_SSW(self, stack1, card1, stack2, card2): - # by same suit with wrapping (only for france games) + # by same suit with wrapping (only for french games) return (card1.suit == card2.suit and ((card1.rank + 1) % 13 == card2.rank or (card2.rank + 1) % 13 == card1.rank)) @@ -1610,7 +1610,7 @@ for %d moves. return abs(card1.rank-card2.rank) == 1 def _shallHighlightMatch_RKW(self, stack1, card1, stack2, card2): - # by rank with wrapping (only for france games) + # by rank with wrapping (only for french games) return ((card1.rank + 1) % 13 == card2.rank or (card2.rank + 1) % 13 == card1.rank) @@ -2640,6 +2640,10 @@ in the current implementation.''' % version sd.delete() self.stackdesc_list = [] + ## for find_card_dialog + def canFindCard(self): + return self.gameinfo.category == GI.GC_FRENCH + # # subclass hooks # diff --git a/pysollib/gamedb.py b/pysollib/gamedb.py index e1e124f6..bbbb2138 100644 --- a/pysollib/gamedb.py +++ b/pysollib/gamedb.py @@ -490,16 +490,22 @@ class GameManager: raise GameInfoException, "wrong GameInfo class" gi.plugin = self.loading_plugin if self.__all_games.has_key(gi.id): - raise GameInfoException, "duplicate game ID " + str(gi.id) + raise GameInfoException, "duplicate game ID %s: %s and %s" % \ + (gi.id, str(gi.gameclass), + str(self.__all_games[gi.id].gameclass)) if self.__all_gamenames.has_key(gi.name): - raise GameInfoException, "duplicate game name " + str(gi.id) + ": " + gi.name + raise GameInfoException, "duplicate game name %s: %s and %s" % \ + (gi.id, gi.name, str(self.__all_games[gi.id].gameclass)) if 1: for id, game in self.__all_games.items(): if gi.gameclass is game.gameclass: - raise GameInfoException, "duplicate game class " + str(gi.id) + raise GameInfoException, \ + "duplicate game class %s: %s and %s" % \ + (gi.id, str(gi.gameclass), str(game.gameclass)) for n in gi.altnames: if self.__all_gamenames.has_key(n): - raise GameInfoException, "duplicate altgame name " + str(gi.id) + ": " + n + raise GameInfoException, "duplicate game altname %s: %s" % \ + (gi.id, n) ##if 0 and gi.si.game_flags & GI.GT_XORIGINAL: ## return ##print gi.id, gi.name diff --git a/pysollib/games/__init__.py b/pysollib/games/__init__.py index 064b3243..8c6dee31 100644 --- a/pysollib/games/__init__.py +++ b/pysollib/games/__init__.py @@ -30,6 +30,7 @@ import headsandtails import katzenschwanz import klondike import labyrinth +import larasgame import matriarchy import montana import montecarlo @@ -46,6 +47,7 @@ import pushpin import pyramid import royalcotillion import royaleast +import sanibel import siebenbisas import simplex import spider @@ -53,6 +55,7 @@ import sthelena import sultan import takeaway import terrace +import threepeaks import tournament import unionsquare import wavemotion diff --git a/pysollib/games/capricieuse.py b/pysollib/games/capricieuse.py index 62d6c96c..d6ffc19c 100644 --- a/pysollib/games/capricieuse.py +++ b/pysollib/games/capricieuse.py @@ -128,7 +128,7 @@ class Strata(Game): s.foundations.append(DieRussische_Foundation(x, y, self, suit=i%4, max_cards=8)) x = x + l.XS - x, y, = l.XM+l.XS, l.YM+l.YS + x, y, = l.XM+l.XS/2, l.YM+l.YS for i in range(8): s.rows.append(AC_RowStack(x, y, self, max_move=1, max_accept=1)) x = x + l.XS diff --git a/pysollib/games/contrib/__init__.py b/pysollib/games/contrib/__init__.py deleted file mode 100644 index bec3b914..00000000 --- a/pysollib/games/contrib/__init__.py +++ /dev/null @@ -1 +0,0 @@ -import sanibel diff --git a/pysollib/games/golf.py b/pysollib/games/golf.py index da1690f6..13a916f3 100644 --- a/pysollib/games/golf.py +++ b/pysollib/games/golf.py @@ -277,118 +277,6 @@ class Escalator(Elevator): self.s.talon.dealRow() self.s.talon.dealCards() # deal first card to WasteStack -# /*********************************************************************** -# // Tri Peaks - Relaxed Golf in a Pyramid layout -# ************************************************************************/ - -class TriPeaks_RowStack(Elevator_RowStack): - STEP = (1, 2, 2, 3+12, 3+12, 3+12, - 1, 2, 2, 3+ 9, 3+ 9, 3+ 9, - 1, 2, 2, 3+ 6, 3+ 6, 3+ 6) - - -class TriPeaks(RelaxedGolf): - - # - # game layout - # - - def createGame(self): - # create layout - l, s = Layout(self), self.s - - # set window - l.XOFFSET = int(l.XS * 9 / self.gameinfo.ncards) - self.setSize(10*l.XS+l.XM, 4*l.YS+l.YM) - - # extra settings - self.talon_card_ids = {} - - # create stacks - for i in range(3): - for d in ((2, 0), (1, 1), (3, 1), (0, 2), (2, 2), (4, 2)): - x = l.XM + (6*i+1+d[0]) * l.XS / 2 - y = l.YM + d[1] * l.YS / 2 - s.rows.append(TriPeaks_RowStack(x, y, self)) - x, y = l.XM, 3*l.YS/2 - for i in range(10): - s.rows.append(Golf_RowStack(x, y, self)) - x = x + l.XS - x, y = l.XM, self.height - l.YS - s.talon = Golf_Talon(x, y, self, max_rounds=1) - l.createText(s.talon, "nn") - x = x + l.XS - s.waste = self.Waste_Class(x, y, self) - s.waste.CARD_XOFFSET = l.XOFFSET - l.createText(s.waste, "nn") - # the Waste is also our only Foundation in this game - s.foundations.append(s.waste) - - self.texts.score = MfxCanvasText(self.canvas, - self.width - 8, self.height - 8, - anchor="se", - font=self.app.getFont("canvas_large")) - - # define stack-groups (non default) - self.sg.openstacks = [s.waste] - self.sg.talonstacks = [s.talon] - self.sg.dropstacks = s.rows - - # - # game overrides - # - - def startGame(self): - self.startDealSample() - self.s.talon.dealRow(rows=self.s.rows[:18], flip=0) - self.s.talon.dealRow(rows=self.s.rows[18:]) - # extra settings: remember cards from the talon - self.talon_card_ids = {} - for c in self.s.talon.cards: - self.talon_card_ids[c.id] = 1 - self.s.talon.dealCards() # deal first card to WasteStack - - def getGameScore(self): - v = -24 * 5 - if not self.s.waste.cards: - return v - # compute streaks for cards on the waste - streak = 0 - for c in self.s.waste.cards: - if self.talon_card_ids.get(c.id): - streak = 0 - else: - streak = streak + 1 - v = v + streak - # each cleared peak gains $15 bonus, and all 3 gain extra $30 - extra = 30 - for i in (0, 6, 12): - if not self.s.rows[i].cards: - v = v + 15 - else: - extra = 0 - return v + extra - - getGameBalance = getGameScore - - def updateText(self): - if self.preview > 1: - return - b1, b2 = self.app.stats.gameid_balance, 0 - if self.shallUpdateBalance(): - b2 = self.getGameBalance() - t = _("Balance $%4d") % (b1 + b2) - self.texts.score.config(text=t) - - def _restoreGameHook(self, game): - self.talon_card_ids = game.loadinfo.talon_card_ids - - def _loadGameHook(self, p): - self.loadinfo.addattr(talon_card_ids=p.load(types.DictType)) - - def _saveGameHook(self, p): - p.dump(self.talon_card_ids) - # /*********************************************************************** # // Black Hole @@ -679,8 +567,6 @@ registerGame(GameInfo(260, RelaxedGolf, "Relaxed Golf", registerGame(GameInfo(40, Elevator, "Elevator", GI.GT_GOLF, 1, 0, GI.SL_BALANCED, altnames=("Egyptian Solitaire", "Pyramid Golf") )) -registerGame(GameInfo(237, TriPeaks, "Tri Peaks", - GI.GT_GOLF | GI.GT_SCORE, 1, 0, GI.SL_MOSTLY_SKILL)) registerGame(GameInfo(98, BlackHole, "Black Hole", GI.GT_GOLF | GI.GT_OPEN, 1, 0, GI.SL_MOSTLY_SKILL)) registerGame(GameInfo(267, FourLeafClovers, "Four Leaf Clovers", diff --git a/pysollib/games/larasgame.py b/pysollib/games/larasgame.py new file mode 100644 index 00000000..be32a224 --- /dev/null +++ b/pysollib/games/larasgame.py @@ -0,0 +1,449 @@ +##---------------------------------------------------------------------------## +## +## Ultrasol -- a Python Solitaire game +## +## Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer +## Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer +## Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer +## Copyright (C) 1999 Matthew Hohlfeld +## +## 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__ = [] + +# imports + +# PySol imports +from pysollib.gamedb import registerGame, GameInfo, GI +from pysollib.util import * +from pysollib.stack import * +from pysollib.game import Game +from pysollib.layout import Layout +from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint +from pysollib.pysoltk import MfxCanvasText + + +# /*********************************************************************** +# // +# ************************************************************************/ + +class LarasGame_Hint(CautiousDefaultHint): + pass + + +# /*********************************************************************** +# // +# ************************************************************************/ + +class LarasGame_Talon(WasteTalonStack): + # Deal a card to each of the RowStacks. Then deal + # cards to the talon. Return number of cards dealt. + def dealRow(self, rows=None, flip=1, reverse=0, frames=-1, sound=0): + game = self.game + if rows is None: + rows = game.s.rows + old_state = game.enterState(game.S_DEAL) + cards = self.dealToStacks(rows[:game.MAX_ROW], flip, reverse, frames) + if sound and frames and self.game.app.opt.animations: + self.game.startDealSample() + for i in range(game.DEAL_TO_TALON): + if self.cards: + game.moveMove(1, self, game.s.rows[-1], frames=frames) + cards = cards + 1 + game.leaveState(old_state) + if sound: + self.game.stopSamples() + return cards + + def dealToStacks(self, stacks, flip=1, reverse=0, frames=-1): + game = self.game + i, move = 0, game.moveMove + for r in stacks: + if not self.cards: + return 0 + assert not self.getCard().face_up + assert r is not self + if flip: + game.flipMove(self) + move(1, self, r, frames=frames) + # Dealing has extra rules in this game type: + # If card rank == card location then add one card to talon + # If card rank == ACE then add two cards to talon + # If card rank == JACK, or higher then add one card to talon + # After all the rows have been dealt, deal cards to talon in self.dealRow + rank = r.getCard().rank + if rank == i: # Is the rank == position? + if not self.cards: + return 0 + move(1, self, game.s.rows[-1], frames=frames) + i = i + 1 + if rank == 0: # Is this an Ace? + for j in range(2): + if not self.cards: + return 0 + move(1, self, game.s.rows[-1], frames=frames) + if rank >= 10: # Is it a Jack or better? + if not self.cards: + return 0 + move(1, self, game.s.rows[-1], frames=frames) + return len(stacks) + + def dealCards(self, sound=0): + game = self.game + if sound and self.game.app.opt.animations: + self.game.startDealSample() + for r in game.s.reserves[:20]: + while r.cards: + game.moveMove(1, r, game.s.rows[game.active_row], frames=3, shadow=0) + if self.cards: + game.active_row = self.getActiveRow() + game.flipMove(self) + game.moveMove(1, self, game.s.reserves[0], frames=4, shadow=0) + ncards = len(game.s.rows[game.active_row].cards) + if ncards >= 20: + # We have encountered an extreme situation. + # In some game type variations it's possible + # to have up to 28 cards on a row stack. + # We'll have to double up on some of the reserves. + for i in range(ncards - 19): + game.moveMove(1, game.s.rows[game.active_row], + game.s.reserves[19 - i], frames=4, shadow=0) + ncards = len(game.s.rows[game.active_row].cards) + assert ncards <= 19 + for i in range(ncards): + game.moveMove(1, game.s.rows[game.active_row], + game.s.reserves[ncards - i], frames=4, shadow=0) + num_cards = len(self.cards) or self.canDealCards() + else: # not self.cards + if self.round < self.max_rounds: + ncards = 0 + rows = list(game.s.rows)[:game.MAX_ROW] + rows.reverse() + for r in rows: + while r.cards: + ncards += 1 + if r.cards[-1].face_up: + game.flipMove(r) + game.moveMove(1, r, self, frames=0) + assert len(self.cards) == ncards + if ncards != 0: + game.nextRoundMove(self) + game.dealToRows() + num_cards = len(self.cards) + if sound: + game.stopSamples() + return num_cards + + def canDealCards(self): + if self.game.demo and self.game.moves.index >= 400: + return 0 + return (self.cards or (self.round < self.max_rounds and not self.game.isGameWon())) + + def updateText(self): + if self.game.preview > 1: + return + WasteTalonStack.updateText(self, update_rounds=0) + if not self.max_rounds - 1: + return + t = _("Round %d") % self.round + self.texts.rounds.config(text=t) + + def getActiveRow(self): + return self.getCard().rank + + +class LarasGame_RowStack(OpenStack): + def __init__(self, x, y, game, yoffset = 1, **cap): + apply(OpenStack.__init__, (self, x, y, game), cap) + self.CARD_YOFFSET = yoffset + + +class LarasGame_ReserveStack(OpenStack): + pass + + +class LarasGame_Reserve(OpenStack): + + def acceptsCards(self, from_stack, cards): + if not OpenStack.acceptsCards(self, from_stack, cards): + return 0 + return from_stack in self.game.s.rows + + def getBottomImage(self): + return self.game.app.images.getReserveBottom() + + +# /*********************************************************************** +# // Lara's Game +# ************************************************************************/ + +class LarasGame(Game): + Hint_Class = LarasGame_Hint + Talon_Class = LarasGame_Talon + Reserve_Class = None + DEAL_TO_TALON = 2 + MAX_ROUNDS = 1 + ROW_LENGTH = 4 + MAX_ROW = 13 + DIR = (-1, 1) + + # + # game layout + # + + def createGame(self): + # create layout + l, s = Layout(self), self.s + ROW_LENGTH = self.ROW_LENGTH + + # set window + w, h = l.XM + l.XS * (ROW_LENGTH + 5), l.YM + l.YS * (ROW_LENGTH + (ROW_LENGTH != 6)) + self.setSize(w, h) + + # extra settings + self.active_row = None + + # Create foundations + x, y = l.XM, l.YM + for j in range(2): + for i in range(ROW_LENGTH): + s.foundations.append(SS_FoundationStack(x, y, self, self.Base_Suit(i, j), + max_cards = self.Max_Cards(i), mod = self.Mod(i), + dir = self.DIR[j], base_rank = self.Base_Rank(i, j))) + y = y + l.YS * (not j) + x = x + l.XS * j + x, y = x + l.XS * 2, l.YM + + # Create rows + x, y = l.XM + l.XS, y + l.YS + for i in range(self.MAX_ROW): + s.rows.append(LarasGame_RowStack(x, y, self)) + x = x + l.XS + if i == ROW_LENGTH or i == ROW_LENGTH * 2 + 1 or i == ROW_LENGTH * 3 + 2: + x, y = l.XM + l.XS, y + l.YS + + # Create reserves + x, y = l.XM + l.XS * (ROW_LENGTH == 6), l.YM + l.YS * (ROW_LENGTH - (ROW_LENGTH == 6)) + for i in range(20): + s.reserves.append(LarasGame_ReserveStack(x, y, self, max_cards=2)) + x = x + l.XS * (i < (ROW_LENGTH + 4)) - l.XS * (i == (ROW_LENGTH + 9)) + y = y - l.YS * (i > (ROW_LENGTH + 3) and i < (ROW_LENGTH + 9)) + l.YS * (i > (ROW_LENGTH + 9)) + + # Create talon + x, y = l.XM + l.XS * (ROW_LENGTH + 2), h - l.YM - l.YS * 3 + s.talon = self.Talon_Class(x, y, self, max_rounds=self.MAX_ROUNDS) + l.createText(s.talon, "s") + if self.MAX_ROUNDS - 1: + tx, ty, ta, tf = l.getTextAttr(s.talon, "nn") + s.talon.texts.rounds = MfxCanvasText(self.canvas, + tx, ty, anchor=ta, + font=self.app.getFont("canvas_default")) + y = h - l.YS * 2 + s.rows.append(LarasGame_RowStack(x, y, self, yoffset=0)) + + # Define stack-groups (not default) + self.sg.openstacks = s.foundations + s.rows[:self.MAX_ROW] + self.sg.talonstacks = [s.talon] + s.rows[-1:] + self.sg.dropstacks = s.rows[:self.MAX_ROW] + s.reserves + + # Create relaxed reserve + if self.Reserve_Class != None: + x, y = l.XM + l.XS * (ROW_LENGTH + 2), l.YM + l.YS * .5 + s.reserves.append(self.Reserve_Class(x, y, self, + max_accept=1, max_cards=self.Reserve_Cards)) + self.sg.openstacks = self.sg.openstacks + s.reserves[19:] + self.sg.dropstacks = self.sg.dropstacks + s.reserves[19:] + self.setRegion(s.reserves[19:], (x - l.XM / 2, 0, 99999, 99999)) + + # + # Game extras + # + + def Max_Cards(self, i): + return 13 + + def Mod(self, i): + return 13 + + def Base_Rank(self, i, j): + return 12 * (not j) + + def Deal_Rows(self, i): + return 13 + + def Base_Suit(self, i, j): + return i + + # + # game overrides + # + + def startGame(self): + assert len(self.s.talon.cards) == self.gameinfo.ncards + self.dealToRows() + + def dealToRows(self): + frames, ncards = 0, len(self.s.talon.cards) + for i in range(8): + if not self.s.talon.cards: + break + if i == 4 or len(self.s.talon.cards) <= ncards / 2: + self.startDealSample() + frames = 4 + self.s.talon.dealRow(rows=self.s.rows[:self.Deal_Rows(i)], frames=frames) + self.moveMove(len(self.s.rows[-1].cards), self.s.rows[-1], self.s.talon, frames=0) + self.active_row = None + + def shallHighlightMatch(self, stack1, card1, stack2, card2): + i, j = (stack1 in self.s.foundations), (stack2 in self.s.foundations) + if not (i or j): return 0 + if i: stack = stack1 + else: stack = stack2 + i = 0 + for f in self.s.foundations: + if f == stack: break + i = i + 1 % self.ROW_LENGTH + return (card1.suit == card2.suit and + ((card1.rank + 1) % self.Mod(i) == card2.rank + or (card1.rank - 1) % self.Mod(i) == card2.rank)) + + def getHighlightPilesStacks(self): + return () + + # Finish the current move. + # Append current active_row to moves.current. + # Append moves.current to moves.history. + def finishMove(self): + moves, stats = self.moves, self.stats + if not moves.current: + return 0 + # invalidate hints + self.hints.list = None + # resize (i.e. possibly shorten list from previous undos) + if not moves.index == 0: + m = moves.history[len(moves.history) - 1] + del moves.history[moves.index : ] + # update stats + if self.demo: + stats.demo_moves = stats.demo_moves + 1 + if moves.index == 0: + stats.player_moves = 0 # clear all player moves + else: + stats.player_moves = stats.player_moves + 1 + if moves.index == 0: + stats.demo_moves = 0 # clear all demo moves + stats.total_moves = stats.total_moves + 1 + # add current move to history (which is a list of lists) + moves.current.append(self.active_row) + moves.history.append(moves.current) + moves.index = moves.index + 1 + assert moves.index == len(moves.history) + moves.current = [] + self.updateText() + self.updateStatus(moves=(moves.index, self.stats.total_moves)) + self.updateMenus() + return 1 + + def undo(self): + assert self.canUndo() + assert self.moves.state == self.S_PLAY and self.moves.current == [] + assert 0 <= self.moves.index <= len(self.moves.history) + if self.moves.index == 0: + return + self.moves.index = self.moves.index - 1 + m = self.moves.history[self.moves.index] + m = m[:len(m) - 1] + m.reverse() + self.moves.state = self.S_UNDO + for atomic_move in m: + atomic_move.undo(self) + self.moves.state = self.S_PLAY + m = self.moves.history[max(0, self.moves.index - 1)] + self.active_row = m[len(m) - 1] + self.stats.undo_moves = self.stats.undo_moves + 1 + self.stats.total_moves = self.stats.total_moves + 1 + self.hints.list = None + self.updateText() + self.updateStatus(moves=(self.moves.index, self.stats.total_moves)) + self.updateMenus() + + def redo(self): + assert self.canRedo() + assert self.moves.state == self.S_PLAY and self.moves.current == [] + assert 0 <= self.moves.index <= len(self.moves.history) + if self.moves.index == len(self.moves.history): + return + m = self.moves.history[self.moves.index] + self.moves.index = self.moves.index + 1 + self.active_row = m[len(m) - 1] + m = m[:len(m) - 1] + self.moves.state = self.S_REDO + for atomic_move in m: + atomic_move.redo(self) + self.moves.state = self.S_PLAY + self.stats.redo_moves = self.stats.redo_moves + 1 + self.stats.total_moves = self.stats.total_moves + 1 + self.hints.list = None + self.updateText() + self.updateStatus(moves=(self.moves.index, self.stats.total_moves)) + self.updateMenus() + + def _restoreGameHook(self, game): + self.active_row = game.loadinfo.active_row + + def _loadGameHook(self, p): + self.loadinfo.addattr(active_row=0) # register extra load var. + self.loadinfo.active_row = p.load() + + def _saveGameHook(self, p): + p.dump(self.active_row) + + + +# /*********************************************************************** +# // Relaxed Lara's Game +# ************************************************************************/ + +class RelaxedLarasGame(LarasGame): + Reserve_Class = LarasGame_Reserve + Reserve_Cards = 1 + DEAL_TO_TALON = 3 + MAX_ROUNDS = 2 + + +# /*********************************************************************** +# // Double Lara's Game +# ************************************************************************/ + +class DoubleLarasGame(RelaxedLarasGame): + Reserve_Cards = 2 + MAX_ROUNDS = 3 + def Max_Cards(self, i): + return 26 + + +# register the game + +registerGame(GameInfo(37, LarasGame, "Lara's Game", + GI.GT_2DECK_TYPE, 2, 0, GI.SL_BALANCED)) +registerGame(GameInfo(13006, RelaxedLarasGame, "Lara's Game Relaxed", + GI.GT_2DECK_TYPE, 2, 1, GI.SL_BALANCED)) +registerGame(GameInfo(13007, DoubleLarasGame, "Lara's Game Doubled", + GI.GT_2DECK_TYPE, 4, 2, GI.SL_BALANCED)) + + diff --git a/pysollib/games/numerica.py b/pysollib/games/numerica.py index ea0d6df0..bd36c947 100644 --- a/pysollib/games/numerica.py +++ b/pysollib/games/numerica.py @@ -767,8 +767,8 @@ class CircleNine(Game): stack.CARD_YOFFSET = 0 x, y = l.XM+3.5*l.XS, l.YM+l.YS - stack = RK_FoundationStack(x, y, self, suit=ANY_SUIT, - max_cards=52, max_move=0, mod=13) + stack = RK_FoundationStack(x, y, self, suit=ANY_SUIT, max_cards=52, + max_move=0, mod=13, base_rank=ANY_RANK) s.foundations.append(stack) l.createText(stack, 'ne') x, y = l.XM, l.YM @@ -794,6 +794,44 @@ class CircleNine(Game): self.leaveState(old_state) +class Measure(CircleNine): + + Foundation_Class = StackWrapper(RK_FoundationStack, max_cards=52) + + def createGame(self, rows=8): + l, s = Layout(self), self.s + self.setSize(l.XM+rows*l.XS, l.YM+2*l.YS+10*l.YOFFSET) + + x, y = l.XM, l.YM + s.talon = Strategerie_Talon(x, y, self) + l.createText(s.talon, 'ne') + x = self.width-l.XS + stack = self.Foundation_Class(x, y, self, suit=ANY_SUIT, max_cards=52, + max_move=0, mod=13, base_rank=ANY_RANK) + s.foundations.append(stack) + l.createText(stack, 'nw') + + x, y = l.XM, l.YM+l.YS + for i in range(rows): + s.rows.append(CircleNine_RowStack(x, y, self, max_accept=1, + max_move=1, base_rank=NO_RANK)) + x += l.XS + + l.defaultStackGroups() + self.sg.dropstacks.append(s.talon) + + def startGame(self): + self.startDealSample() + self.s.talon.dealRow() + self.s.talon.fillStack() + + +class DoubleMeasure(Measure): + Foundation_Class = StackWrapper(RK_FoundationStack, max_cards=104) + + def createGame(self, rows=8): + Measure.createGame(self, rows=10) + # register the game @@ -833,4 +871,8 @@ registerGame(GameInfo(613, Fanny, "Fanny", GI.GT_NUMERICA, 2, 0, GI.SL_BALANCED)) registerGame(GameInfo(641, CircleNine, "Circle Nine", GI.GT_NUMERICA, 1, 0, GI.SL_BALANCED)) +registerGame(GameInfo(643, Measure, "Measure", + GI.GT_NUMERICA | GI.GT_ORIGINAL, 1, 0, GI.SL_MOSTLY_SKILL)) +registerGame(GameInfo(644, DoubleMeasure, "Double Measure", + GI.GT_NUMERICA | GI.GT_ORIGINAL, 2, 0, GI.SL_MOSTLY_SKILL)) diff --git a/pysollib/games/contrib/sanibel.py b/pysollib/games/sanibel.py similarity index 100% rename from pysollib/games/contrib/sanibel.py rename to pysollib/games/sanibel.py diff --git a/pysollib/games/ultra/threepeaks.py b/pysollib/games/threepeaks.py similarity index 92% rename from pysollib/games/ultra/threepeaks.py rename to pysollib/games/threepeaks.py index 1de25809..5e562a93 100644 --- a/pysollib/games/ultra/threepeaks.py +++ b/pysollib/games/threepeaks.py @@ -28,7 +28,6 @@ __all__ = [] # Imports -import sys, math # PySol imports from pysollib.gamedb import registerGame, GameInfo, GI @@ -40,7 +39,7 @@ from pysollib.layout import Layout from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint from pysollib.pysoltk import MfxCanvasText, MfxCanvasImage, bind, ANCHOR_NW -from pysollib.games.golf import Golf_Waste, Golf_Hint +from golf import Golf_Waste, Golf_Hint # /*********************************************************************** @@ -77,7 +76,7 @@ class ThreePeaks_TalonStack(WasteTalonStack): class ThreePeaks_RowStack(OpenStack): def __init__(self, x, y, game, **cap): - kwdefault(cap, max_move=0, max_accept=0, max_cards=1, + kwdefault(cap, max_move=1, max_accept=0, max_cards=1, base_rank=ANY_RANK) apply(OpenStack.__init__, (self, x, y, game), cap) @@ -134,9 +133,11 @@ class ThreePeaks(Game): l, s = Layout(self), self.s # set window - # (compute best XOFFSET - up to 64/72 cards can be in the Waste) decks = self.gameinfo.decks - l.XOFFSET = int(l.XS * 9 / self.gameinfo.ncards) + # compute best XOFFSET + xoffset = int(l.XS * 8 / self.gameinfo.ncards) + if xoffset < l.XOFFSET: + l.XOFFSET = xoffset # Set window size w, h = l.XM + l.XS * 10, l.YM + l.YS * 4 @@ -170,12 +171,12 @@ class ThreePeaks(Game): # Create talon x, y = l.XM, y + l.YM + l.YS s.talon = ThreePeaks_TalonStack(x, y, self, num_deal=1, max_rounds=1) - l.createText(s.talon, "ss") + l.createText(s.talon, "s") x = x + l.XS s.waste = self.Waste_Class(x, y, self) s.waste.CARD_XOFFSET = l.XOFFSET s.foundations.append(s.waste) - l.createText(s.waste, "ss") + l.createText(s.waste, "s") # Create text for scores if self.preview <= 1: @@ -279,21 +280,10 @@ class ThreePeaksNoScore(ThreePeaks): return 1 -# /*********************************************************************** -# // Le Grande Teton -# ************************************************************************/ - -class LeGrandeTeton(ThreePeaksNoScore): - pass - - registerGame(GameInfo(22216, ThreePeaks, "Three Peaks", GI.GT_PAIRING_TYPE, 1, 0, GI.SL_BALANCED)) registerGame(GameInfo(22231, ThreePeaksNoScore, "Three Peaks Non-scoring", GI.GT_PAIRING_TYPE, 1, 0, GI.SL_BALANCED)) -registerGame(GameInfo(22232, LeGrandeTeton, "Le Grande Teton", - GI.GT_TAROCK, 1, 0, GI.SL_BALANCED, - ranks=range(14), trumps=range(22))) diff --git a/pysollib/games/tournament.py b/pysollib/games/tournament.py index dd22babd..f3b187fe 100644 --- a/pysollib/games/tournament.py +++ b/pysollib/games/tournament.py @@ -84,7 +84,7 @@ class Tournament(Game): # game layout # - def createGame(self, **layout): + def createGame(self): # create layout l, s = Layout(self), self.s @@ -121,8 +121,9 @@ class Tournament(Game): s.talon = Tournament_Talon(l.XM, l.YM, self, max_rounds=3) l.createText(s.talon, "se") tx, ty, ta, tf = l.getTextAttr(s.talon, "ne") - s.talon.texts.rounds = MfxCanvasText(self.canvas, tx, ty, anchor=ta, - font=self.app.getFont("canvas_default")) + font = self.app.getFont("canvas_default") + s.talon.texts.rounds = MfxCanvasText(self.canvas, tx, ty, + anchor=ta, font=font) # define stack-groups l.defaultStackGroups() @@ -130,6 +131,7 @@ class Tournament(Game): # # game overrides # + def _shuffleHook(self, cards): for c in cards[-8:]: if c.rank in (ACE, KING): @@ -189,7 +191,7 @@ class KingsdownEights(Game): Hint_Class = CautiousDefaultHint - def createGame(self, **layout): + def createGame(self): # create layout l, s = Layout(self), self.s @@ -232,6 +234,54 @@ class KingsdownEights(Game): shallHighlightMatch = Game._shallHighlightMatch_AC +# /*********************************************************************** +# // Saxony +# ************************************************************************/ + +class Saxony_Reserve(SS_RowStack): + def getBottomImage(self): + return self.game.app.images.getReserveBottom() + def getHelp(self): + return _('Reserve. Build down by suit.') + + +class Saxony(Game): + + def createGame(self): + l, s = Layout(self), self.s + self.setSize(l.XM+11*l.XS, 2*l.YM+max(2*l.YS+12*l.YOFFSET, 5*l.YS)) + + x, y, = l.XM+1.5*l.XS, l.YM + for i in range(8): + s.foundations.append(SS_FoundationStack(x, y, self, suit=i%4)) + x = x + l.XS + x, y = l.XM+1.5*l.XS, 2*l.YM+l.YS + for i in range(8): + s.rows.append(BasicRowStack(x, y, self, max_move=1, max_accept=0)) + x = x + l.XS + x, y = l.XM, 2*l.YM+l.YS + for i in range(4): + stack = Saxony_Reserve(x, y, self, max_move=1) + self.s.reserves.append(stack) + stack.CARD_YOFFSET = 0 + y += l.YS + x, y = self.width-l.XS, 2*l.YM+l.YS + for i in range(4): + self.s.reserves.append(ReserveStack(x, y, self)) + y += l.YS + s.talon = DealRowTalonStack(l.XM, l.YM, self) + l.createText(s.talon, "ne") + + l.defaultStackGroups() + + + def startGame(self): + self.s.talon.dealRow(rows=self.s.reserves, frames=0) + self.startDealSample() + self.s.talon.dealRow() + + + # register the game registerGame(GameInfo(303, Tournament, "Tournament", GI.GT_2DECK_TYPE, 2, 2, GI.SL_MOSTLY_LUCK)) @@ -240,6 +290,8 @@ registerGame(GameInfo(304, LaNivernaise, "La Nivernaise", altnames = ("Napoleon's Flank", ),)) registerGame(GameInfo(386, KingsdownEights, "Kingsdown Eights", GI.GT_2DECK_TYPE, 2, 0, GI.SL_BALANCED)) +registerGame(GameInfo(645, Saxony, "Saxony", + GI.GT_2DECK_TYPE, 2, 0, GI.SL_MOSTLY_SKILL)) diff --git a/pysollib/games/ultra/__init__.py b/pysollib/games/ultra/__init__.py index a1e0b46b..ee1f0273 100644 --- a/pysollib/games/ultra/__init__.py +++ b/pysollib/games/ultra/__init__.py @@ -6,4 +6,3 @@ import larasgame import matrix import mughal import tarock -import threepeaks diff --git a/pysollib/games/ultra/larasgame.py b/pysollib/games/ultra/larasgame.py index a421be49..7d058f7a 100644 --- a/pysollib/games/ultra/larasgame.py +++ b/pysollib/games/ultra/larasgame.py @@ -27,7 +27,6 @@ __all__ = [] # imports -import sys, types # PySol imports from pysollib.gamedb import registerGame, GameInfo, GI @@ -38,165 +37,25 @@ from pysollib.layout import Layout from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint from pysollib.pysoltk import MfxCanvasText - -# /*********************************************************************** -# // -# ************************************************************************/ - -class LarasGame_Hint(CautiousDefaultHint): - pass +from pysollib.games.larasgame import LarasGame_Talon, LarasGame, LarasGame_Reserve # /*********************************************************************** # // # ************************************************************************/ -class LarasGame_Talon(WasteTalonStack): - # Deal a card to each of the RowStacks. Then deal - # cards to the talon. Return number of cards dealt. - def dealRow(self, rows=None, flip=1, reverse=0, frames=-1, sound=0): - game = self.game - if rows is None: - rows = game.s.rows - old_state = game.enterState(game.S_DEAL) - cards = self.dealToStacks(rows[:game.MAX_ROW], flip, reverse, frames) - if sound and frames and self.game.app.opt.animations: - self.game.startDealSample() - for i in range(game.DEAL_TO_TALON): - if self.cards: - game.moveMove(1, self, game.s.rows[-1], frames=frames) - cards = cards + 1 - game.leaveState(old_state) - if sound: - self.game.stopSamples() - return cards - - def dealToStacks(self, stacks, flip=1, reverse=0, frames=-1): - game = self.game - i, move = 0, game.moveMove - for r in stacks: - if not self.cards: - return 0 - assert not self.getCard().face_up - assert r is not self - if flip: - game.flipMove(self) - move(1, self, r, frames=frames) - # Dealing has extra rules in this game type: - # If card rank == card location then add one card to talon - # If card rank == ACE then add two cards to talon - # If card rank == JACK, or higher then add one card to talon - # After all the rows have been dealt, deal cards to talon in self.dealRow - rank = r.getCard().rank - if rank == i: # Is the rank == position? - if not self.cards: - return 0 - move(1, self, game.s.rows[-1], frames=frames) - i = i + 1 - if rank == 0: # Is this an Ace? - for j in range(2): - if not self.cards: - return 0 - move(1, self, game.s.rows[-1], frames=frames) - if rank >= 10: # Is it a Jack or better? - if not self.cards: - return 0 - move(1, self, game.s.rows[-1], frames=frames) - return len(stacks) - - def dealCards(self, sound=0): - game = self.game - if sound and self.game.app.opt.animations: - self.game.startDealSample() - for r in game.s.reserves[:20]: - while r.cards: - game.moveMove(1, r, game.s.rows[game.active_row], frames=3, shadow=0) - if self.cards: - game.active_row = self.getActiveRow() - game.flipMove(self) - game.moveMove(1, self, game.s.reserves[0], frames=4, shadow=0) - ncards = len(game.s.rows[game.active_row].cards) - if ncards >= 20: - # We have encountered an extreme situation. - # In some game type variations it's possible - # to have up to 28 cards on a row stack. - # We'll have to double up on some of the reserves. - for i in range(ncards - 19): - game.moveMove(1, game.s.rows[game.active_row], - game.s.reserves[19 - i], frames=4, shadow=0) - ncards = len(game.s.rows[game.active_row].cards) - assert ncards <= 19 - for i in range(ncards): - game.moveMove(1, game.s.rows[game.active_row], - game.s.reserves[ncards - i], frames=4, shadow=0) - num_cards = len(self.cards) or self.canDealCards() - else: # not self.cards - if self.round < self.max_rounds: - ncards = 0 - rows = list(game.s.rows)[:game.MAX_ROW] - rows.reverse() - for r in rows: - while r.cards: - ncards += 1 - if r.cards[-1].face_up: - game.flipMove(r) - game.moveMove(1, r, self, frames=0) - assert len(self.cards) == ncards - if ncards != 0: - game.nextRoundMove(self) - game.dealToRows() - num_cards = len(self.cards) - if sound: - game.stopSamples() - return num_cards - - def canDealCards(self): - if self.game.demo and self.game.moves.index >= 400: - return 0 - return (self.cards or (self.round < self.max_rounds and not self.game.isGameWon())) - - def updateText(self): - if self.game.preview > 1: - return - WasteTalonStack.updateText(self, update_rounds=0) - if not self.max_rounds - 1: - return - t = _("Round %d") % self.round - self.texts.rounds.config(text=t) - - def getActiveRow(self): - return self.getCard().rank - - - class DojoujisGame_Talon(LarasGame_Talon): - def getActiveRow(self): card = self.getCard() return card.rank + card.deck * 4 - class DoubleKalisGame_Talon(LarasGame_Talon): - def getActiveRow(self): card = self.getCard() return card.rank + card.deck * 12 - -class LarasGame_RowStack(OpenStack): - def __init__(self, x, y, game, yoffset = 1, **cap): - apply(OpenStack.__init__, (self, x, y, game), cap) - self.CARD_YOFFSET = yoffset - - - -class LarasGame_ReserveStack(OpenStack): - pass - - - class BridgetsGame_Reserve(OpenStack): def acceptsCards(self, from_stack, cards): @@ -210,270 +69,6 @@ class BridgetsGame_Reserve(OpenStack): return self.game.app.images.getReserveBottom() - -class LarasGame_Reserve(BridgetsGame_Reserve): - - def acceptsCards(self, from_stack, cards): - if not OpenStack.acceptsCards(self, from_stack, cards): - return 0 - return from_stack in self.game.s.rows - - - -# /*********************************************************************** -# // Lara's Game -# ************************************************************************/ - -class LarasGame(Game): - Hint_Class = LarasGame_Hint - Talon_Class = LarasGame_Talon - Reserve_Class = None - DEAL_TO_TALON = 2 - MAX_ROUNDS = 1 - ROW_LENGTH = 4 - MAX_ROW = 13 - DIR = (-1, 1) - - # - # game layout - # - - def createGame(self): - # create layout - l, s = Layout(self), self.s - ROW_LENGTH = self.ROW_LENGTH - - # set window - w, h = l.XM + l.XS * (ROW_LENGTH + 5), l.YM + l.YS * (ROW_LENGTH + (ROW_LENGTH != 6)) - self.setSize(w, h) - - # extra settings - self.active_row = None - - # Create foundations - x, y = l.XM, l.YM - for j in range(2): - for i in range(ROW_LENGTH): - s.foundations.append(SS_FoundationStack(x, y, self, self.Base_Suit(i, j), - max_cards = self.Max_Cards(i), mod = self.Mod(i), - dir = self.DIR[j], base_rank = self.Base_Rank(i, j))) - y = y + l.YS * (not j) - x = x + l.XS * j - x, y = x + l.XS * 2, l.YM - - # Create rows - x, y = l.XM + l.XS, y + l.YS - for i in range(self.MAX_ROW): - s.rows.append(LarasGame_RowStack(x, y, self)) - x = x + l.XS - if i == ROW_LENGTH or i == ROW_LENGTH * 2 + 1 or i == ROW_LENGTH * 3 + 2: - x, y = l.XM + l.XS, y + l.YS - - # Create reserves - x, y = l.XM + l.XS * (ROW_LENGTH == 6), l.YM + l.YS * (ROW_LENGTH - (ROW_LENGTH == 6)) - for i in range(20): - s.reserves.append(LarasGame_ReserveStack(x, y, self, max_cards=2)) - x = x + l.XS * (i < (ROW_LENGTH + 4)) - l.XS * (i == (ROW_LENGTH + 9)) - y = y - l.YS * (i > (ROW_LENGTH + 3) and i < (ROW_LENGTH + 9)) + l.YS * (i > (ROW_LENGTH + 9)) - - # Create talon - x, y = l.XM + l.XS * (ROW_LENGTH + 2), h - l.YM - l.YS * 3 - s.talon = self.Talon_Class(x, y, self, max_rounds=self.MAX_ROUNDS) - l.createText(s.talon, "s") - if self.MAX_ROUNDS - 1: - tx, ty, ta, tf = l.getTextAttr(s.talon, "nn") - s.talon.texts.rounds = MfxCanvasText(self.canvas, - tx, ty, anchor=ta, - font=self.app.getFont("canvas_default")) - y = h - l.YS * 2 - s.rows.append(LarasGame_RowStack(x, y, self, yoffset=0)) - - # Define stack-groups (not default) - self.sg.openstacks = s.foundations + s.rows[:self.MAX_ROW] - self.sg.talonstacks = [s.talon] + s.rows[-1:] - self.sg.dropstacks = s.rows[:self.MAX_ROW] + s.reserves - - # Create relaxed reserve - if self.Reserve_Class != None: - x, y = l.XM + l.XS * (ROW_LENGTH + 2), l.YM + l.YS * .5 - s.reserves.append(self.Reserve_Class(x, y, self, - max_accept=1, max_cards=self.Reserve_Cards)) - self.sg.openstacks = self.sg.openstacks + s.reserves[19:] - self.sg.dropstacks = self.sg.dropstacks + s.reserves[19:] - self.setRegion(s.reserves[19:], (x - l.XM / 2, 0, 99999, 99999)) - - # - # Game extras - # - - def Max_Cards(self, i): - return 13 - - def Mod(self, i): - return 13 - - def Base_Rank(self, i, j): - return 12 * (not j) - - def Deal_Rows(self, i): - return 13 - - def Base_Suit(self, i, j): - return i - - # - # game overrides - # - - def startGame(self): - assert len(self.s.talon.cards) == self.gameinfo.ncards - self.dealToRows() - - def dealToRows(self): - frames, ncards = 0, len(self.s.talon.cards) - for i in range(8): - if not self.s.talon.cards: - break - if i == 4 or len(self.s.talon.cards) <= ncards / 2: - self.startDealSample() - frames = 4 - self.s.talon.dealRow(rows=self.s.rows[:self.Deal_Rows(i)], frames=frames) - self.moveMove(len(self.s.rows[-1].cards), self.s.rows[-1], self.s.talon, frames=0) - self.active_row = None - - def shallHighlightMatch(self, stack1, card1, stack2, card2): - i, j = (stack1 in self.s.foundations), (stack2 in self.s.foundations) - if not (i or j): return 0 - if i: stack = stack1 - else: stack = stack2 - i = 0 - for f in self.s.foundations: - if f == stack: break - i = i + 1 % self.ROW_LENGTH - return (card1.suit == card2.suit and - ((card1.rank + 1) % self.Mod(i) == card2.rank - or (card1.rank - 1) % self.Mod(i) == card2.rank)) - - def getHighlightPilesStacks(self): - return () - - # Finish the current move. - # Append current active_row to moves.current. - # Append moves.current to moves.history. - def finishMove(self): - moves, stats = self.moves, self.stats - if not moves.current: - return 0 - # invalidate hints - self.hints.list = None - # resize (i.e. possibly shorten list from previous undos) - if not moves.index == 0: - m = moves.history[len(moves.history) - 1] - del moves.history[moves.index : ] - # update stats - if self.demo: - stats.demo_moves = stats.demo_moves + 1 - if moves.index == 0: - stats.player_moves = 0 # clear all player moves - else: - stats.player_moves = stats.player_moves + 1 - if moves.index == 0: - stats.demo_moves = 0 # clear all demo moves - stats.total_moves = stats.total_moves + 1 - # add current move to history (which is a list of lists) - moves.current.append(self.active_row) - moves.history.append(moves.current) - moves.index = moves.index + 1 - assert moves.index == len(moves.history) - moves.current = [] - self.updateText() - self.updateStatus(moves=(moves.index, self.stats.total_moves)) - self.updateMenus() - return 1 - - def undo(self): - assert self.canUndo() - assert self.moves.state == self.S_PLAY and self.moves.current == [] - assert 0 <= self.moves.index <= len(self.moves.history) - if self.moves.index == 0: - return - self.moves.index = self.moves.index - 1 - m = self.moves.history[self.moves.index] - m = m[:len(m) - 1] - m.reverse() - self.moves.state = self.S_UNDO - for atomic_move in m: - atomic_move.undo(self) - self.moves.state = self.S_PLAY - m = self.moves.history[max(0, self.moves.index - 1)] - self.active_row = m[len(m) - 1] - self.stats.undo_moves = self.stats.undo_moves + 1 - self.stats.total_moves = self.stats.total_moves + 1 - self.hints.list = None - self.updateText() - self.updateStatus(moves=(self.moves.index, self.stats.total_moves)) - self.updateMenus() - - def redo(self): - assert self.canRedo() - assert self.moves.state == self.S_PLAY and self.moves.current == [] - assert 0 <= self.moves.index <= len(self.moves.history) - if self.moves.index == len(self.moves.history): - return - m = self.moves.history[self.moves.index] - self.moves.index = self.moves.index + 1 - self.active_row = m[len(m) - 1] - m = m[:len(m) - 1] - self.moves.state = self.S_REDO - for atomic_move in m: - atomic_move.redo(self) - self.moves.state = self.S_PLAY - self.stats.redo_moves = self.stats.redo_moves + 1 - self.stats.total_moves = self.stats.total_moves + 1 - self.hints.list = None - self.updateText() - self.updateStatus(moves=(self.moves.index, self.stats.total_moves)) - self.updateMenus() - - def _restoreGameHook(self, game): - self.active_row = game.loadinfo.active_row - - def _loadGameHook(self, p): - self.loadinfo.addattr(active_row=0) # register extra load var. - self.loadinfo.active_row = p.load() - - def _saveGameHook(self, p): - p.dump(self.active_row) - - - -# /*********************************************************************** -# // Relaxed Lara's Game -# ************************************************************************/ - -class RelaxedLarasGame(LarasGame): - Reserve_Class = LarasGame_Reserve - Reserve_Cards = 1 - DEAL_TO_TALON = 3 - MAX_ROUNDS = 2 - - -# /*********************************************************************** -# // Double Lara's Game -# ************************************************************************/ - -class DoubleLarasGame(RelaxedLarasGame): - Reserve_Cards = 2 - MAX_ROUNDS = 3 - - # - # Game extras - # - - def Max_Cards(self, i): - return 26 - - # /*********************************************************************** # // Katrina's Game # ************************************************************************/ @@ -528,10 +123,6 @@ class DoubleKatrinasGame(RelaxedKatrinasGame): Reserve_Cards = 3 MAX_ROUNDS = 3 - # - # Game extras - # - def Max_Cards(self, i): return 28 + 16 * (i == 4) @@ -552,10 +143,6 @@ class BridgetsGame(LarasGame): ROW_LENGTH = 5 MAX_ROW = 16 - # - # Game extras - # - def Max_Cards(self, i): return 16 - 12 * (i == 4) @@ -580,10 +167,6 @@ class DoubleBridgetsGame(BridgetsGame): Reserve_Cards = 3 MAX_ROUNDS = 3 - # - # Game extras - # - def Max_Cards(self, i): return 32 - 24 * (i == 4) @@ -598,10 +181,6 @@ class FatimehsGame(LarasGame): MAX_ROW = 12 DIR = (1, 1) - # - # Game extras - # - def Max_Cards(self, i): return 12 @@ -635,10 +214,6 @@ class KalisGame(FatimehsGame): DEAL_TO_TALON = 6 ROW_LENGTH = 5 - # - # Game extras - # - def Base_Suit(self, i, j): return i + j * 5 @@ -662,10 +237,6 @@ class DoubleKalisGame(RelaxedKalisGame): MAX_ROUNDS = 4 MAX_ROW = 24 - # - # Game extras - # - def Max_Cards(self, i): return 24 @@ -683,10 +254,6 @@ class DojoujisGame(LarasGame): MAX_ROW = 8 DIR = (-1, -1) - # - # Game extras - # - def Max_Cards(self, i): return 8 @@ -703,7 +270,6 @@ class DojoujisGame(LarasGame): return i + j * 6 - # /*********************************************************************** # // Double Dojouji's Game # ************************************************************************/ @@ -711,10 +277,6 @@ class DojoujisGame(LarasGame): class DoubleDojoujisGame(DojoujisGame): MAX_ROW = 16 - # - # Game extras - # - def Max_Cards(self, i): return 16 @@ -724,59 +286,39 @@ class DoubleDojoujisGame(DojoujisGame): # register the game -registerGame(GameInfo(37, LarasGame, "Lara's Game", - GI.GT_2DECK_TYPE, 2, 0, GI.SL_BALANCED)) - registerGame(GameInfo(13001, KatrinasGame, "Katrina's Game", GI.GT_TAROCK, 2, 1, GI.SL_BALANCED, ranks = range(14), trumps = range(22))) - registerGame(GameInfo(13002, BridgetsGame, "Bridget's Game", GI.GT_HEXADECK, 2, 1, GI.SL_BALANCED, ranks = range(16), trumps = range(4))) - registerGame(GameInfo(13003, FatimehsGame, "Fatimeh's Game", GI.GT_MUGHAL_GANJIFA, 1, 2, GI.SL_BALANCED, suits = range(8), ranks = range(12))) - registerGame(GameInfo(13004, KalisGame, "Kali's Game", GI.GT_DASHAVATARA_GANJIFA, 1, 2, GI.SL_BALANCED, suits = range(10), ranks = range(12))) - registerGame(GameInfo(13005, DojoujisGame, "Dojouji's Game", GI.GT_HANAFUDA, 2, 0, GI.SL_BALANCED, suits = range(12), ranks = range(4))) - -registerGame(GameInfo(13006, RelaxedLarasGame, "Lara's Game Relaxed", - GI.GT_2DECK_TYPE, 2, 1, GI.SL_BALANCED)) - -registerGame(GameInfo(13007, DoubleLarasGame, "Lara's Game Doubled", - GI.GT_2DECK_TYPE, 4, 2, GI.SL_BALANCED)) - registerGame(GameInfo(13008, RelaxedKatrinasGame, "Katrina's Game Relaxed", GI.GT_TAROCK, 2, 1, GI.SL_BALANCED, ranks = range(14), trumps = range(22))) - registerGame(GameInfo(13009, DoubleKatrinasGame, "Katrina's Game Doubled", GI.GT_TAROCK, 4, 2, GI.SL_BALANCED, ranks = range(14), trumps = range(22))) - registerGame(GameInfo(13010, DoubleBridgetsGame, "Bridget's Game Doubled", GI.GT_HEXADECK, 4, 2, GI.SL_BALANCED, ranks = range(16), trumps = range(4))) - registerGame(GameInfo(13011, RelaxedKalisGame, "Kali's Game Relaxed", GI.GT_DASHAVATARA_GANJIFA, 1, 2, GI.SL_BALANCED, suits = range(10), ranks = range(12))) - registerGame(GameInfo(13012, DoubleKalisGame, "Kali's Game Doubled", GI.GT_DASHAVATARA_GANJIFA, 2, 3, GI.SL_BALANCED, suits = range(10), ranks = range(12))) - registerGame(GameInfo(13013, RelaxedFatimehsGame, "Fatimeh's Game Relaxed", GI.GT_MUGHAL_GANJIFA, 1, 2, GI.SL_BALANCED, suits = range(8), ranks = range(12))) - registerGame(GameInfo(13014, DoubleDojoujisGame, "Dojouji's Game Doubled", GI.GT_HANAFUDA, 4, 0, GI.SL_BALANCED, suits = range(12), ranks = range(4))) diff --git a/pysollib/games/ultra/tarock.py b/pysollib/games/ultra/tarock.py index 9e49ce1a..17d65c12 100644 --- a/pysollib/games/ultra/tarock.py +++ b/pysollib/games/ultra/tarock.py @@ -39,6 +39,7 @@ from pysollib.layout import Layout #from pysollib.pysoltk import MfxCanvasText from pysollib.games.special.tarock import AbstractTarockGame, Grasshopper +from pysollib.games.threepeaks import ThreePeaksNoScore # /*********************************************************************** @@ -254,6 +255,15 @@ class Rambling(Corkscrew): sequence = row.isSuitSequence return (sequence([card1, card2]) or sequence([card2, card1])) +# /*********************************************************************** +# // Le Grande Teton +# ************************************************************************/ + +class LeGrandeTeton(ThreePeaksNoScore): + pass + + + # /*********************************************************************** # // register the games # ************************************************************************/ @@ -270,5 +280,6 @@ r(13164, DoubleCockroach, 'Double Cockroach', GI.GT_TAROCK, 2, 0, GI.SL_MOSTLY_S r(13165, Corkscrew, 'Corkscrew', GI.GT_TAROCK, 2, 0, GI.SL_MOSTLY_SKILL) r(13166, Serpent, 'Serpent', GI.GT_TAROCK, 2, 0, GI.SL_MOSTLY_SKILL) r(13167, Rambling, 'Rambling', GI.GT_TAROCK, 2, 0, GI.SL_MOSTLY_SKILL) +r(22232, LeGrandeTeton, 'Le Grande Teton', GI.GT_TAROCK, 1, 0, GI.SL_BALANCED) + -del r diff --git a/pysollib/main.py b/pysollib/main.py index e9f14010..f223e9aa 100644 --- a/pysollib/main.py +++ b/pysollib/main.py @@ -89,6 +89,7 @@ def parse_option(argv): "fg=", "foreground=", "bg=", "background=", "fn=", "font=", + "french-only", "noplugins", "nosound", "debug=", @@ -103,6 +104,7 @@ def parse_option(argv): "fg": None, "bg": None, "fn": None, + "french-only": False, "noplugins": False, "nosound": False, "debug": 0, @@ -120,6 +122,8 @@ def parse_option(argv): opts["bg"] = i[1] elif i[0] in ("--fn", "--font"): opts["fn"] = i[1] + elif i[0] == "--french-only": + opts["french-only"] = True elif i[0] == "--noplugins": opts["noplugins"] = True elif i[0] == "--nosound": @@ -146,7 +150,7 @@ def parse_option(argv): return None filename = args and args[0] or None if filename and not os.path.isfile(filename): - print _("%s: invalide file name\ntry %s --help for more information") % (prog_name, prog_name) + print _("%s: invalid file name\ntry %s --help for more information") % (prog_name, prog_name) return None return opts, filename @@ -190,15 +194,19 @@ def pysol_init(app, args): try: app.commandline.gameid = int(opts['gameid']) except: - print >> sys.stderr, 'WARNING: invalide game id:', opts['gameid'] - app.debug = int(opts['debug']) + print >> sys.stderr, 'WARNING: invalid game id:', opts['gameid'] + try: + app.debug = int(opts['debug']) + except: + print >> sys.stderr, 'invalid argument for debug' # init games database import games - import games.contrib - import games.special - import games.ultra - import games.mahjongg + if not opts['french-only']: + #import games.contrib + import games.ultra + import games.mahjongg + import games.special # init DataLoader f = os.path.join("html", "license.html") @@ -322,7 +330,7 @@ def pysol_init(app, args): try: f = Font(top, font) except: - print >> sys.stderr, "invalide font name:", font + print >> sys.stderr, "invalid font name:", font pass else: if font: @@ -345,8 +353,7 @@ Main data directory is: %s Please check your %s installation. -''') % (app.dataloader.dir, PACKAGE), - bitmap="error", strings=(_("&Quit"),)) +''') % (app.dataloader.dir, PACKAGE), bitmap="error", strings=(_("&Quit"),)) return 1 # init cardsets diff --git a/pysollib/tk/colorsdialog.py b/pysollib/tk/colorsdialog.py index a9fe39a3..9810c02c 100644 --- a/pysollib/tk/colorsdialog.py +++ b/pysollib/tk/colorsdialog.py @@ -23,7 +23,7 @@ __all__ = ['ColorsDialog'] # imports import os, sys -from Tkinter import * +import Tkinter from tkColorChooser import askcolor # PySol imports @@ -44,40 +44,40 @@ class ColorsDialog(MfxDialog): top_frame, bottom_frame = self.createFrames(kw) self.createBitmaps(top_frame, kw) - frame = Frame(top_frame) - frame.pack(expand=YES, fill=BOTH, padx=5, pady=10) + frame = Tkinter.Frame(top_frame) + frame.pack(expand=True, fill='both', padx=5, pady=10) frame.columnconfigure(0, weight=1) - self.table_text_color_var = BooleanVar() + self.table_text_color_var = Tkinter.BooleanVar() self.table_text_color_var.set(app.opt.table_text_color) - self.table_text_color_value_var = StringVar() + self.table_text_color_value_var = Tkinter.StringVar() self.table_text_color_value_var.set(app.opt.table_text_color_value) ##self.table_color_var = StringVar() ##self.table_color_var.set(app.opt.table_color) - self.highlight_piles_colors_var = StringVar() + self.highlight_piles_colors_var = Tkinter.StringVar() self.highlight_piles_colors_var.set(app.opt.highlight_piles_colors[1]) - self.highlight_cards_colors_1_var = StringVar() + self.highlight_cards_colors_1_var = Tkinter.StringVar() self.highlight_cards_colors_1_var.set(app.opt.highlight_cards_colors[1]) - self.highlight_cards_colors_2_var = StringVar() + self.highlight_cards_colors_2_var = Tkinter.StringVar() self.highlight_cards_colors_2_var.set(app.opt.highlight_cards_colors[3]) - self.highlight_samerank_colors_1_var = StringVar() + self.highlight_samerank_colors_1_var = Tkinter.StringVar() self.highlight_samerank_colors_1_var.set(app.opt.highlight_samerank_colors[1]) - self.highlight_samerank_colors_2_var = StringVar() + self.highlight_samerank_colors_2_var = Tkinter.StringVar() self.highlight_samerank_colors_2_var.set(app.opt.highlight_samerank_colors[3]) - self.hintarrow_color_var = StringVar() + self.hintarrow_color_var = Tkinter.StringVar() self.hintarrow_color_var.set(app.opt.hintarrow_color) - self.highlight_not_matching_color_var = StringVar() + self.highlight_not_matching_color_var = Tkinter.StringVar() self.highlight_not_matching_color_var.set(app.opt.highlight_not_matching_color) # - c = Checkbutton(frame, variable=self.table_text_color_var, - text=_("Text foreground:"), anchor=W) - c.grid(row=0, column=0, sticky=W+E) - l = Label(frame, width=10, height=2, - bg=self.table_text_color_value_var.get(), - textvariable=self.table_text_color_value_var) + c = Tkinter.Checkbutton(frame, variable=self.table_text_color_var, + text=_("Text foreground:"), anchor='w') + c.grid(row=0, column=0, sticky='we') + l = Tkinter.Label(frame, width=10, height=2, + bg=self.table_text_color_value_var.get(), + textvariable=self.table_text_color_value_var) l.grid(row=0, column=1, padx=5) - b = Button(frame, text=_('Change...'), width=10, - command=lambda l=l: self.selectColor(l)) + b = Tkinter.Button(frame, text=_('Change...'), width=10, + command=lambda l=l: self.selectColor(l)) b.grid(row=0, column=2) row = 1 for title, var in ( @@ -90,12 +90,13 @@ class ColorsDialog(MfxDialog): (_('Hint arrow:'), self.hintarrow_color_var), (_('Highlight not matching:'), self.highlight_not_matching_color_var), ): - Label(frame, text=title, anchor=W).grid(row=row, column=0, sticky=W+E) - l = Label(frame, width=10, height=2, - bg=var.get(), textvariable=var) + 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) l.grid(row=row, column=1, padx=5) - b = Button(frame, text=_('Change...'), width=10, - command=lambda l=l: self.selectColor(l)) + b = Tkinter.Button(frame, text=_('Change...'), width=10, + command=lambda l=l: self.selectColor(l)) b.grid(row=row, column=2) row += 1 # diff --git a/pysollib/tk/fontsdialog.py b/pysollib/tk/fontsdialog.py index 6ea9da64..3c5d3940 100644 --- a/pysollib/tk/fontsdialog.py +++ b/pysollib/tk/fontsdialog.py @@ -24,7 +24,7 @@ __all__ = ['FontsDialog'] # imports import os, sys import types -from Tkinter import * +import Tkinter import tkFont # PySol imports @@ -71,38 +71,40 @@ class FontChooserDialog(MfxDialog): else: raise TypeError, 'invalid font style: '+ init_font[3] - #self.family_var = StringVar() - self.weight_var = BooleanVar() - self.slant_var = BooleanVar() - self.size_var = IntVar() + #self.family_var = Tkinter.StringVar() + self.weight_var = Tkinter.BooleanVar() + self.slant_var = Tkinter.BooleanVar() + self.size_var = Tkinter.IntVar() - frame = Frame(top_frame) - frame.pack(expand=YES, fill=BOTH, padx=5, pady=10) + frame = Tkinter.Frame(top_frame) + frame.pack(expand=True, fill='both', padx=5, pady=10) frame.columnconfigure(0, weight=1) #frame.rowconfigure(1, weight=1) - self.entry = Entry(frame, bg='white') - self.entry.grid(row=0, column=0, columnspan=2, sticky=W+E+N+S) - self.entry.insert(END, _('abcdefghABCDEFGH')) - self.list_box = Listbox(frame, width=36, exportselection=False) - sb = Scrollbar(frame) + self.entry = Tkinter.Entry(frame, bg='white') + self.entry.grid(row=0, column=0, columnspan=2, sticky='news') + self.entry.insert('end', _('abcdefghABCDEFGH')) + self.list_box = Tkinter.Listbox(frame, width=36, exportselection=False) + sb = Tkinter.Scrollbar(frame) self.list_box.configure(yscrollcommand=sb.set) sb.configure(command=self.list_box.yview) - self.list_box.grid(row=1, column=0, sticky=W+E+N+S) # rowspan=4 - sb.grid(row=1, column=1, sticky=N+S) + self.list_box.grid(row=1, column=0, sticky='news') # rowspan=4 + sb.grid(row=1, column=1, sticky='ns') bind(self.list_box, '<>', self.fontupdate) ##self.list_box.focus() - cb1 = Checkbutton(frame, anchor=W, text=_('Bold'), - command=self.fontupdate, variable=self.weight_var) - cb1.grid(row=2, column=0, columnspan=2, sticky=W+E) - cb2 = Checkbutton(frame, anchor=W, text=_('Italic'), - command=self.fontupdate, variable=self.slant_var) - cb2.grid(row=3, column=0, columnspan=2, sticky=W+E) + cb1 = Tkinter.Checkbutton(frame, anchor='w', text=_('Bold'), + command=self.fontupdate, + variable=self.weight_var) + cb1.grid(row=2, column=0, columnspan=2, sticky='we') + cb2 = Tkinter.Checkbutton(frame, anchor='w', text=_('Italic'), + command=self.fontupdate, + variable=self.slant_var) + cb2.grid(row=3, column=0, columnspan=2, sticky='we') - sc = Scale(frame, from_=6, to=40, resolution=1, - #label='Size', - orient=HORIZONTAL, - command=self.fontupdate, variable=self.size_var) - sc.grid(row=4, column=0, columnspan=2, sticky=W+E+N+S) + sc = Tkinter.Scale(frame, from_=6, to=40, resolution=1, + #label='Size', + orient='horizontal', + command=self.fontupdate, variable=self.size_var) + sc.grid(row=4, column=0, columnspan=2, sticky='news') # self.size_var.set(self.font_size) self.weight_var.set(self.font_weight == 'bold') @@ -111,10 +113,11 @@ class FontChooserDialog(MfxDialog): font_families.sort() selected = -1 n = 0 + self.list_box.insert('end', *font_families) for font in font_families: - self.list_box.insert(END, font) if font.lower() == self.font_family.lower(): selected = n + break n += 1 if selected >= 0: self.list_box.select_set(selected) @@ -155,35 +158,33 @@ class FontsDialog(MfxDialog): top_frame, bottom_frame = self.createFrames(kw) self.createBitmaps(top_frame, kw) - frame = Frame(top_frame) - frame.pack(expand=YES, fill=BOTH, padx=5, pady=10) + frame = Tkinter.Frame(top_frame) + frame.pack(expand=True, fill='both', padx=5, pady=10) frame.columnconfigure(0, weight=1) self.fonts = {} row = 0 - for fn in (#"default", - "sans", - "small", - "fixed", - "canvas_default", - #"canvas_card", - "canvas_fixed", - "canvas_large", - "canvas_small", - #"tree_small", - ): + for fn, title in (##('default', _('Default')), + ('sans', _('HTML: ')), + ('small', _('Small: ')), + ('fixed', _('Fixed: ')), + ('canvas_default', _('Tableau default: ')), + ('canvas_fixed', _('Tableau fixed: ')), + ('canvas_large', _('Tableau large: ')), + ('canvas_small', _('Tableau small: ')), + ): font = app.opt.fonts[fn] self.fonts[fn] = font - title = fn.replace("_", " ").capitalize()+": " - Label(frame, text=title, anchor=W).grid(row=row, column=0, sticky=W+E) + Tkinter.Label(frame, text=title, anchor='w' + ).grid(row=row, column=0, sticky='we') if font: - title = " ".join([str(i) for i in font if not i in ('roman', 'normal')]) + title = ' '.join([str(i) for i in font if not i in ('roman', 'normal')]) elif font is None: title = 'Default' - l = Label(frame, font=font, text=title) + l = Tkinter.Label(frame, font=font, text=title) l.grid(row=row, column=1) - b = Button(frame, text=_('Change...'), width=10, - command=lambda l=l, fn=fn: self.selectFont(l, fn)) + b = Tkinter.Button(frame, text=_('Change...'), width=10, + command=lambda l=l, fn=fn: self.selectFont(l, fn)) b.grid(row=row, column=2) row += 1 # @@ -192,16 +193,16 @@ class FontsDialog(MfxDialog): def selectFont(self, label, fn): - d = FontChooserDialog(self.top, _("Select font"), self.fonts[fn]) + d = FontChooserDialog(self.top, _('Select font'), self.fonts[fn]) if d.status == 0 and d.button == 0: self.fonts[fn] = d.font - title = " ".join([str(i) for i in d.font if not i in ('roman', 'normal')]) + title = ' '.join([str(i) for i in d.font if not i in ('roman', 'normal')]) label.configure(font=d.font, text=title) def initKw(self, kw): kw = KwStruct(kw, - strings=(_("&OK"), _("&Cancel")), + strings=(_('&OK'), _('&Cancel')), default=0, ) return MfxDialog.initKw(self, kw) diff --git a/pysollib/tk/gameinfodialog.py b/pysollib/tk/gameinfodialog.py index 3e34891b..6718b0ea 100644 --- a/pysollib/tk/gameinfodialog.py +++ b/pysollib/tk/gameinfodialog.py @@ -24,7 +24,7 @@ __all__ = ['GameInfoDialog'] # imports import os, sys -from Tkinter import * +import Tkinter # PySol imports from pysollib.mfxutil import KwStruct @@ -44,8 +44,8 @@ class GameInfoDialog(MfxDialog): top_frame, bottom_frame = self.createFrames(kw) self.createBitmaps(top_frame, kw) - frame = Frame(top_frame) - frame.pack(expand=YES, fill=BOTH, padx=5, pady=10) + frame = Tkinter.Frame(top_frame) + frame.pack(expand=True, fill='both', padx=5, pady=10) frame.columnconfigure(0, weight=1) game = app.game @@ -108,8 +108,10 @@ class GameInfoDialog(MfxDialog): ('Hint:', hint), ): if t: - Label(frame, text=n, anchor=W).grid(row=row, column=0, sticky=N+W) - Label(frame, text=t, anchor=W, justify=LEFT).grid(row=row, column=1, sticky=N+W) + Tkinter.Label(frame, text=n, anchor='w' + ).grid(row=row, column=0, sticky='nw') + Tkinter.Label(frame, text=t, anchor='w', justify='left' + ).grid(row=row, column=1, sticky='nw') row += 1 if game.s.talon: @@ -131,9 +133,9 @@ class GameInfoDialog(MfxDialog): focus = self.createButtons(bottom_frame, kw) self.mainloop(focus, kw.timeout) - def showStacks(self, frame, row, title, stacks): - Label(frame, text=title, anchor=W).grid(row=row, column=0, sticky=N+W) + Tkinter.Label(frame, text=title, anchor='w' + ).grid(row=row, column=0, sticky='nw') if isinstance(stacks, (list, tuple)): fs = {} for f in stacks: @@ -145,8 +147,8 @@ class GameInfoDialog(MfxDialog): t = '\n'.join(['%s (%d)' % (i[0], i[1]) for i in fs.items()]) else: t = stacks.__class__.__name__ - Label(frame, text=t, anchor=W, justify=LEFT).grid(row=row, column=1, sticky=N+W) - + Tkinter.Label(frame, text=t, anchor='w', justify='left' + ).grid(row=row, column=1, sticky='nw') def initKw(self, kw): kw = KwStruct(kw, diff --git a/pysollib/tk/menubar.py b/pysollib/tk/menubar.py index 7364dfec..a84fc103 100644 --- a/pysollib/tk/menubar.py +++ b/pysollib/tk/menubar.py @@ -721,14 +721,18 @@ class PysolMenubar(PysolMenubarActions): submenu = self.__menupath[".menubar.file.favoritegames"][2] submenu.delete(0, "last") # insert games - g = [self.app.getGameInfo(id) for id in gameids] - if len(g) > self.__cb_max*4: - g.sort(lambda a, b: cmp(gettext(a.name), gettext(b.name))) - self._addSelectAllGameSubMenu(submenu, g, + games = [] + for id in gameids: + gi = self.app.getGameInfo(id) + if gi: + games.append(gi) + if len(games) > self.__cb_max*4: + games.sort(lambda a, b: cmp(gettext(a.name), gettext(b.name))) + self._addSelectAllGameSubMenu(submenu, games, command=self.mSelectGame, variable=self.tkopt.gameid) else: - self._addSelectGameSubSubMenu(submenu, g, + self._addSelectGameSubSubMenu(submenu, games, command=self.mSelectGame, variable=self.tkopt.gameid) state = self._getEnabledState diff --git a/pysollib/tk/selectgame.py b/pysollib/tk/selectgame.py index e8cec38e..6baa6853 100644 --- a/pysollib/tk/selectgame.py +++ b/pysollib/tk/selectgame.py @@ -99,13 +99,14 @@ class SelectGameData(SelectDialogTreeData): self.all_games_gi = map(app.gdb.get, app.gdb.getGamesIdSortedByName()) self.no_games = [ SelectGameLeaf(None, None, _("(no games)"), None), ] # - s_by_type = s_oriental = s_special = s_original = s_contrib = None + s_by_type = s_oriental = s_special = s_original = s_contrib = s_mahjongg = None g = [] for data in (GI.SELECT_GAME_BY_TYPE, GI.SELECT_ORIENTAL_GAME_BY_TYPE, GI.SELECT_SPECIAL_GAME_BY_TYPE, GI.SELECT_ORIGINAL_GAME_BY_TYPE, - GI.SELECT_CONTRIB_GAME_BY_TYPE): + GI.SELECT_CONTRIB_GAME_BY_TYPE, + ): gg = [] for name, select_func in data: if name is None or not filter(select_func, self.all_games_gi): @@ -114,21 +115,23 @@ class SelectGameData(SelectDialogTreeData): name = name.replace("&", "") gg.append(SelectGameNode(None, name, select_func)) g.append(gg) - if 1 and g[0]: + select_mahjongg_game = lambda gi: gi.si.game_type == GI.GT_MAHJONGG + gg = None + if filter(select_mahjongg_game, self.all_games_gi): + gg = SelectGameNode(None, _("Mahjongg Games"), select_mahjongg_game) + g.append(gg) + if g[0]: s_by_type = SelectGameNode(None, _("French games"), tuple(g[0]), expanded=1) - pass - if 1 and g[1]: + if g[1]: s_oriental = SelectGameNode(None, _("Oriental Games"), tuple(g[1])) - pass - if 1 and g[2]: + if g[2]: s_special = SelectGameNode(None, _("Special Games"), tuple(g[2])) - pass - if 1 and g[3]: + if g[3]: s_original = SelectGameNode(None, _("Original Games"), tuple(g[3])) - pass - if 1 and g[4]: - ##s_contrib = SelectGameNode(None, "Contributed Games", tuple(g[4])) - pass +## if g[4]: +## s_contrib = SelectGameNode(None, "Contributed Games", tuple(g[4])) + if g[5]: + s_mahjongg = g[5] # s_by_compatibility, gg = None, [] for name, games in GI.GAMES_BY_COMPATIBILITY: @@ -140,16 +143,6 @@ class SelectGameData(SelectDialogTreeData): if 1 and gg: s_by_compatibility = SelectGameNode(None, _("by Compatibility"), tuple(gg)) pass -## # -## s_mahjongg, gg = None, [] -## for name, games in GI.GAMES_BY_COMPATIBILITY: -## select_func = lambda gi, games=games: gi.id in games -## if name is None or not filter(select_func, self.all_games_gi): -## continue -## gg.append(SelectGameNode(None, name, select_func)) -## if 1 and gg: -## s_by_compatibility = SelectGameNode(None, "by Compatibility", tuple(gg)) -## pass # s_by_pysol_version, gg = None, [] for name, games in GI.GAMES_BY_PYSOL_VERSION: @@ -169,9 +162,7 @@ class SelectGameData(SelectDialogTreeData): SelectGameNode(None, _("All Games"), None, expanded=0), SelectGameNode(None, _("Alternate Names"), ul_alternate_names), SelectGameNode(None, _("Popular Games"), lambda gi: gi.si.game_flags & GI.GT_POPULAR, expanded=0), - SelectGameNode(None, _("Mahjongg Games"), - lambda gi: gi.si.game_type == GI.GT_MAHJONGG, - expanded=0), + s_mahjongg, s_oriental, s_special, s_by_type, @@ -398,7 +389,8 @@ class SelectGameDialogWithPreview(SelectGameDialog): padx=padx, pady=pady, sticky='nsew') right_frame.columnconfigure(1, weight=1) right_frame.rowconfigure(1, weight=1) - + # + focus = self.createButtons(bottom_frame, kw) # set the scale factor self.preview.canvas.preview = 2 # create a preview of the current game @@ -406,8 +398,6 @@ class SelectGameDialogWithPreview(SelectGameDialog): self.preview_game = None self.preview_app = None self.updatePreview(gameid, animations=0) - # - focus = self.createButtons(bottom_frame, kw) ##focus = self.tree.frame SelectGameTreeWithPreview.html_viewer = None self.mainloop(focus, kw.timeout) @@ -532,6 +522,12 @@ class SelectGameDialogWithPreview(SelectGameDialog): self.preview_key = gameid # self.updateInfo(gameid) + # + rules_button = self.buttons[1] + if self.app.getGameRulesFilename(gameid): + rules_button.config(state="normal") + else: + rules_button.config(state="disabled") def updateInfo(self, gameid): gi = self.app.gdb.get(gameid) diff --git a/pysollib/tk/soundoptionsdialog.py b/pysollib/tk/soundoptionsdialog.py index ebec5c7e..1b577346 100644 --- a/pysollib/tk/soundoptionsdialog.py +++ b/pysollib/tk/soundoptionsdialog.py @@ -37,7 +37,7 @@ __all__ = ['SoundOptionsDialog'] # imports import os, sys, string -from Tkinter import * +import Tkinter import traceback # PySol imports @@ -63,87 +63,88 @@ class SoundOptionsDialog(MfxDialog): self.createBitmaps(top_frame, kw) # self.saved_opt = app.opt.copy() - self.sound = BooleanVar() + self.sound = Tkinter.BooleanVar() self.sound.set(app.opt.sound != 0) - self.sound_mode = BooleanVar() + self.sound_mode = Tkinter.BooleanVar() self.sound_mode.set(app.opt.sound_mode != 0) - self.sample_volume = IntVar() + self.sample_volume = Tkinter.IntVar() self.sample_volume.set(app.opt.sound_sample_volume) - self.music_volume = IntVar() + self.music_volume = Tkinter.IntVar() self.music_volume.set(app.opt.sound_music_volume) self.samples = [ - ('areyousure', _('Are You Sure'), BooleanVar()), + ('areyousure', _('Are You Sure'), Tkinter.BooleanVar()), - ('deal', _('Deal'), BooleanVar()), - ('dealwaste', _('Deal waste'), BooleanVar()), + ('deal', _('Deal'), Tkinter.BooleanVar()), + ('dealwaste', _('Deal waste'), Tkinter.BooleanVar()), - ('turnwaste', _('Turn waste'), BooleanVar()), - ('startdrag', _('Start drag'), BooleanVar()), + ('turnwaste', _('Turn waste'), Tkinter.BooleanVar()), + ('startdrag', _('Start drag'), Tkinter.BooleanVar()), - ('drop', _('Drop'), BooleanVar()), - ('droppair', _('Drop pair'), BooleanVar()), - ('autodrop', _('Auto drop'), BooleanVar()), + ('drop', _('Drop'), Tkinter.BooleanVar()), + ('droppair', _('Drop pair'), Tkinter.BooleanVar()), + ('autodrop', _('Auto drop'), Tkinter.BooleanVar()), - ('flip', _('Flip'), BooleanVar()), - ('autoflip', _('Auto flip'), BooleanVar()), - ('move', _('Move'), BooleanVar()), - ('nomove', _('No move'), BooleanVar()), + ('flip', _('Flip'), Tkinter.BooleanVar()), + ('autoflip', _('Auto flip'), Tkinter.BooleanVar()), + ('move', _('Move'), Tkinter.BooleanVar()), + ('nomove', _('No move'), Tkinter.BooleanVar()), - ('undo', _('Undo'), BooleanVar()), - ('redo', _('Redo'), BooleanVar()), + ('undo', _('Undo'), Tkinter.BooleanVar()), + ('redo', _('Redo'), Tkinter.BooleanVar()), - ('autopilotlost', _('Autopilot lost'), BooleanVar()), - ('autopilotwon', _('Autopilot won'), BooleanVar()), + ('autopilotlost', _('Autopilot lost'), Tkinter.BooleanVar()), + ('autopilotwon', _('Autopilot won'), Tkinter.BooleanVar()), - ('gamefinished', _('Game finished'), BooleanVar()), - ('gamelost', _('Game lost'), BooleanVar()), - ('gamewon', _('Game won'), BooleanVar()), - ('gameperfect', _('Perfect game'), BooleanVar()), + ('gamefinished', _('Game finished'), Tkinter.BooleanVar()), + ('gamelost', _('Game lost'), Tkinter.BooleanVar()), + ('gamewon', _('Game won'), Tkinter.BooleanVar()), + ('gameperfect', _('Perfect game'), Tkinter.BooleanVar()), ] # - frame = Frame(top_frame) - frame.pack(expand=1, fill='both', padx=5, pady=5) + frame = Tkinter.Frame(top_frame) + frame.pack(expand=True, fill='both', padx=5, pady=5) frame.columnconfigure(1, weight=1) # row = 0 - w = Checkbutton(frame, variable=self.sound, + w = Tkinter.Checkbutton(frame, variable=self.sound, text=_("Sound enabled"), anchor='w') w.grid(row=row, column=0, columnspan=2, sticky='ew') # if os.name == "nt" and pysolsoundserver: row += 1 - w = Checkbutton(frame, variable=self.sound_mode, + w = Tkinter.Checkbutton(frame, variable=self.sound_mode, text=_("Use DirectX for sound playing"), command=self.mOptSoundDirectX, anchor='w') w.grid(row=row, column=0, columnspan=2, sticky='ew') # if pysolsoundserver and app.startup_opt.sound_mode > 0: row += 1 - w = Label(frame, text=_('Sample volume:')) + w = Tkinter.Label(frame, text=_('Sample volume:')) w.grid(row=row, column=0, sticky='w') - w = Scale(frame, from_=0, to=128, resolution=1, - orient='horizontal', takefocus=0, - length="3i", #label=_('Sample volume'), - variable=self.sample_volume) + w = Tkinter.Scale(frame, from_=0, to=128, resolution=1, + orient='horizontal', takefocus=0, + length="3i", #label=_('Sample volume'), + variable=self.sample_volume) w.grid(row=row, column=1, sticky='w', padx=5) row += 1 - w = Label(frame, text=_('Music volume:')) + w = Tkinter.Label(frame, text=_('Music volume:')) w.grid(row=row, column=0, sticky='w', padx=5) - w = Scale(frame, from_=0, to=128, resolution=1, - orient='horizontal', takefocus=0, - length="3i", #label=_('Music volume'), - variable=self.music_volume) + w = Tkinter.Scale(frame, from_=0, to=128, resolution=1, + orient='horizontal', takefocus=0, + length="3i", #label=_('Music volume'), + variable=self.music_volume) w.grid(row=row, column=1, sticky='w', padx=5) else: # remove "Apply" button kw.strings[1] = None # - if TkVersion >= 8.4: - frame = LabelFrame(top_frame, text=_('Enable samles'), padx=5, pady=5) + if Tkinter.TkVersion >= 8.4: + frame = Tkinter.LabelFrame(top_frame, text=_('Enable samles'), + padx=5, pady=5) else: - frame = Frame(top_frame) + frame = Tkinter.Frame(top_frame) frame.pack(expand=1, fill='both', padx=5, pady=5) frame.columnconfigure(0, weight=1) frame.columnconfigure(1, weight=1) @@ -152,7 +153,7 @@ class SoundOptionsDialog(MfxDialog): col = 0 for n, t, v in self.samples: v.set(app.opt.sound_samples[n]) - w = Checkbutton(frame, text=t, anchor='w', variable=v) + w = Tkinter.Checkbutton(frame, text=t, anchor='w', variable=v) w.grid(row=row, column=col, sticky='ew') if col == 1: col = 0 @@ -203,7 +204,9 @@ class SoundOptionsDialog(MfxDialog): def mOptSoundDirectX(self, *event): ##print self.sound_mode.get() d = MfxMessageDialog(self.top, title=_("Sound preferences info"), - text=_("Changing DirectX settings will take effect\nthe next time you restart ")+PACKAGE, + text=_("""\ +Changing DirectX settings will take effect +the next time you restart """)+PACKAGE, bitmap="warning", default=0, strings=(_("&OK"),)) diff --git a/pysollib/tk/statusbar.py b/pysollib/tk/statusbar.py index 7f0664cf..e69f3e3e 100644 --- a/pysollib/tk/statusbar.py +++ b/pysollib/tk/statusbar.py @@ -39,6 +39,12 @@ __all__ = ['PysolStatusbar', # imports import os, sys, Tkinter +if __name__ == '__main__': + d = os.path.abspath(os.path.join(sys.path[0], '..', '..')) + sys.path.append(d) + import gettext + gettext.install('pysol', d, unicode=True) + # PySol imports from pysollib.mfxutil import destruct @@ -61,6 +67,7 @@ class MfxStatusbar: self._columnspan = columnspan # self.padx = 1 + self.label_relief = 'sunken' self.frame = Tkinter.Frame(self.top, bd=1) self.frame.grid(row=self._row, column=self._column, columnspan=self._columnspan, sticky='ew', @@ -70,13 +77,28 @@ class MfxStatusbar: if os.name == 'nt': self.frame.config(relief='raised') self.padx = 0 + if 0: + self.frame.config(bd=0) + self.label_relief = 'flat' + self.padx = 0 # util def _createLabel(self, name, side='left', fill='none', expand=0, width=0, tooltip=None): - label = Tkinter.Label(self.frame, width=width, relief='sunken', bd=1) - label.pack(side=side, fill=fill, padx=self.padx, expand=expand) + if 0: + frame = Tkinter.Frame(self.frame, bd=1, relief=self.label_relief, + highlightbackground='#9e9a9e', + highlightthickness=1) + frame.pack(side=side, fill=fill, padx=self.padx, expand=expand) + label = Tkinter.Label(frame, width=width, bd=0) + label.pack(expand=True, fill='both') + else: + label = Tkinter.Label(self.frame, width=width, + relief=self.label_relief, bd=1, + highlightbackground='black' + ) + label.pack(side=side, fill=fill, padx=self.padx, expand=expand) setattr(self, name + "_label", label) self._widgets.append(label) if tooltip: diff --git a/pysollib/tk/timeoutsdialog.py b/pysollib/tk/timeoutsdialog.py index 1ec23785..a2294147 100644 --- a/pysollib/tk/timeoutsdialog.py +++ b/pysollib/tk/timeoutsdialog.py @@ -23,7 +23,7 @@ __all__ = ['TimeoutsDialog'] # imports import os, sys -from Tkinter import * +import Tkinter # PySol imports from pysollib.mfxutil import destruct, kwdefault, KwStruct, Struct @@ -43,24 +43,24 @@ class TimeoutsDialog(MfxDialog): top_frame, bottom_frame = self.createFrames(kw) #self.createBitmaps(top_frame, kw) - frame = Frame(top_frame) - frame.pack(expand=YES, fill=BOTH, padx=5, pady=10) + frame = Tkinter.Frame(top_frame) + frame.pack(expand=True, fill='both', padx=5, pady=10) frame.columnconfigure(0, weight=1) - self.demo_sleep_var = DoubleVar() + self.demo_sleep_var = Tkinter.DoubleVar() self.demo_sleep_var.set(app.opt.demo_sleep) - self.hint_sleep_var = DoubleVar() + self.hint_sleep_var = Tkinter.DoubleVar() self.hint_sleep_var.set(app.opt.hint_sleep) - self.raise_card_sleep_var = DoubleVar() + self.raise_card_sleep_var = Tkinter.DoubleVar() self.raise_card_sleep_var.set(app.opt.raise_card_sleep) - self.highlight_piles_sleep_var = DoubleVar() + self.highlight_piles_sleep_var = Tkinter.DoubleVar() self.highlight_piles_sleep_var.set(app.opt.highlight_piles_sleep) - self.highlight_cards_sleep_var = DoubleVar() + self.highlight_cards_sleep_var = Tkinter.DoubleVar() self.highlight_cards_sleep_var.set(app.opt.highlight_cards_sleep) - self.highlight_samerank_sleep_var = DoubleVar() + self.highlight_samerank_sleep_var = Tkinter.DoubleVar() self.highlight_samerank_sleep_var.set(app.opt.highlight_samerank_sleep) # - #Label(frame, text='Set delays in seconds').grid(row=0, column=0, columnspan=2) + #Tkinter.Label(frame, text='Set delays in seconds').grid(row=0, column=0, columnspan=2) row = 0 for title, var in ((_('Demo:'), self.demo_sleep_var), (_('Hint:'), self.hint_sleep_var), @@ -69,11 +69,11 @@ class TimeoutsDialog(MfxDialog): (_('Highlight cards:'), self.highlight_cards_sleep_var), (_('Highlight same rank:'), self.highlight_samerank_sleep_var), ): - Label(frame, text=title, anchor=W).grid(row=row, column=0, sticky=W+E) - widget = Scale(frame, from_=0.2, to=9.9, - resolution=0.1, orient=HORIZONTAL, - length="3i", - variable=var, takefocus=0) + Tkinter.Label(frame, text=title, anchor='w' + ).grid(row=row, column=0, sticky='we') + widget = Tkinter.Scale(frame, from_=0.2, to=9.9, + resolution=0.1, orient='horizontal', + length="3i", variable=var, takefocus=0) widget.grid(row=row, column=1) row += 1 # diff --git a/pysollib/tk/tkhtml.py b/pysollib/tk/tkhtml.py index 8c735d90..099ab5f2 100644 --- a/pysollib/tk/tkhtml.py +++ b/pysollib/tk/tkhtml.py @@ -40,13 +40,19 @@ import os, sys, re, types import htmllib, formatter import Tkinter +if __name__ == '__main__': + d = os.path.abspath(os.path.join(sys.path[0], '..', '..')) + sys.path.append(d) + import gettext + gettext.install('pysol', d, unicode=True) + # PySol imports from pysollib.mfxutil import Struct, openURL from pysollib.settings import PACKAGE # Toolkit imports from tkutil import bind, unbind_destroy, loadImage -from tkwidget import MfxDialog +from tkwidget import MfxMessageDialog from statusbar import HtmlStatusbar @@ -65,9 +71,13 @@ class tkHTMLWriter(formatter.DumbWriter): self.viewer = viewer ## - font = app.getFont("sans") + if app: + font = app.getFont("sans") + fixed = app.getFont("fixed") + else: + font = ('helvetica', 12) + fixed = ('courier', 12) size = font[1] - fixed = app.getFont("fixed") sign = 1 if size < 0: sign = -1 self.fontmap = { @@ -121,14 +131,21 @@ class tkHTMLWriter(formatter.DumbWriter): if self.anchor: url = self.anchor[0] tag = "href_" + url - self.text.tag_add(tag, self.anchor_mark, "insert") + anchor_mark = self.anchor_mark + if self.text.get(anchor_mark) == ' ': # FIXME + try: + y, x = anchor_mark.split('.') + anchor_mark = y+'.'+str(int(x)+1) + except: + pass + self.text.tag_add(tag, anchor_mark, "insert") self.text.tag_bind(tag, "<1>", self.createCallback(url)) self.text.tag_bind(tag, "", lambda e: self.anchor_enter(url)) self.text.tag_bind(tag, "", self.anchor_leave) fg = 'blue' u = self.viewer.normurl(url, with_protocol=False) if u in self.viewer.visited_urls: - fg = '#303080' + fg = '#660099' self.text.tag_config(tag, foreground=fg, underline=1) self.anchor = None @@ -379,7 +396,7 @@ class tkHTMLViewer: for p in REMOTE_PROTOCOLS: if url.startswith(p): if not openURL(url): - self.errorDialog(PACKAGE + _(''' HTML limitation: + self.errorDialog(PACKAGE + _('''HTML limitation: The %s protocol is not supported yet. Please use your standard web browser @@ -491,9 +508,9 @@ to open the following URL: self.display(self.home, relpath=0) def errorDialog(self, msg): - d = MfxDialog(self.parent, title=PACKAGE+" HTML Problem", - text=msg, bitmap="warning", - strings=(_("&OK"),), default=0) + d = MfxMessageDialog(self.parent, title=PACKAGE+" HTML Problem", + text=msg, bitmap="warning", + strings=(_("&OK"),), default=0) def getImage(self, fn): if self.images.has_key(fn): @@ -526,6 +543,7 @@ def tkhtml_main(args): top = Tkinter.Tk() top.wm_minsize(400, 200) viewer = tkHTMLViewer(top) + viewer.app = None viewer.display(url) top.mainloop() return 0 diff --git a/pysollib/tk/tkwidget.py b/pysollib/tk/tkwidget.py index 9fec16f4..9ac6b9e4 100644 --- a/pysollib/tk/tkwidget.py +++ b/pysollib/tk/tkwidget.py @@ -70,6 +70,7 @@ class MfxDialog: # ex. _ToplevelDialog self.status = 0 self.button = default self.timer = None + self.buttons = [] self.accel_keys = {} self.top = makeToplevel(parent, title=title) self.top.wm_resizable(resizable, resizable) @@ -231,6 +232,7 @@ class MfxDialog: # ex. _ToplevelDialog if button == kw.default: focus = b focus.config(default="active") + self.buttons.append(b) # b.config(width=button_width) if accel_indx >= 0: diff --git a/scripts/all_games.py b/scripts/all_games.py index 7112f18e..07464c34 100755 --- a/scripts/all_games.py +++ b/scripts/all_games.py @@ -5,6 +5,7 @@ import sys, os, re, time from pprint import pprint +os.environ['LANG'] = 'C' import gettext gettext.install('pysol', 'locale', unicode=True) @@ -15,7 +16,6 @@ rules_dir = os.path.normpath(os.path.join(pysollib_path, 'data/html/rules')) #print rules_dir import pysollib.games -import pysollib.games.contrib import pysollib.games.special import pysollib.games.ultra import pysollib.games.mahjongg @@ -138,17 +138,17 @@ def all_games(sort_by='id'): gt = CSI.TYPE_NAME[gi.category] if gt == 'French': gt = 'French (%s)' % GAME_BY_TYPE[gi.si.game_type] + name = gi.name.encode('utf-8') + altnames = '
'.join(gi.altnames).encode('utf-8') if 1 and os.path.exists(os.path.join(rules_dir, rules_fn)): fn = '../data/html/rules/'+rules_fn print '''%s %s %s%s -''' % (id, fn, - gi.name.encode('utf-8'), '
'.join(gi.altnames).encode('utf-8'), gt) +''' % (id, fn, name, altnames, gt) else: print '''%s%s%s%s -''' % (id, gi.name.encode('utf-8'), - '
'.join(gi.altnames).encode('utf-8'), gt) +''' % (id, name, altnames, gt) print '' def create_html(sort_by): @@ -219,6 +219,7 @@ def plain_text(): for id in get_games_func(): gi = GAME_DB.get(id) if gi.category == GI.GC_FRENCH: + ##print str(gi.gameclass) print gi.name.encode('utf-8') ##name = gi.name.lower() ##name = re.sub('\W', '', name) @@ -235,6 +236,8 @@ elif sys.argv[1] == 'gettext': get_text() elif sys.argv[1] == 'text': plain_text() +else: + sys.exit('invalid argument')