diff --git a/pysollib/app.py b/pysollib/app.py index e1e49439..639dec14 100644 --- a/pysollib/app.py +++ b/pysollib/app.py @@ -501,7 +501,6 @@ class Application: self.toolbar = None self.canvas = None self.statusbar = None - self.cardsets_cache = {} # self.game = None self.dataloader = None @@ -521,6 +520,7 @@ class Application: self.progress_images = [] self.cardset_manager = CardsetManager() self.cardset = None # current cardset + self.cardsets_cache = {} self.tabletile_manager = TileManager() self.tabletile_index = 0 # current table tile self.sample_manager = SampleManager() diff --git a/pysollib/game.py b/pysollib/game.py index 8f8928b0..a36c093f 100644 --- a/pysollib/game.py +++ b/pysollib/game.py @@ -116,6 +116,7 @@ class Game: self.cards = [] self.stackmap = {} # dict with (x,y) tuples as key self.allstacks = [] + self.stackdesc_list = [] self.demo_logo = None self.pause_logo = None self.s = Struct( # stacks @@ -701,6 +702,7 @@ class Game: if break_pause and self.pause: self.doPause() self.interruptSleep() + self.deleteStackDesc() if self.busy: return 1 if self.drag.stack: self.drag.stack.cancelDrag() @@ -715,12 +717,14 @@ class Game: self.app.menubar.disableMenus() + # # UI & graphics support # def clickHandler(self, *args): self.interruptSleep() + self.deleteStackDesc() if self.demo: self.stopDemo() return EVENT_PROPAGATE @@ -793,7 +797,7 @@ class Game: def _unmapHandler(self, event): # pause game if root window has been iconified if event.widget is self.top and not self.pause: - self.doPause() + self.app.menubar.mPause() # @@ -2461,6 +2465,29 @@ in the current implementation.''' % version if kw.has_key('help') and self.app.opt.helpbar: self.app.helpbar.updateText(info=kw['help']) + # + # Piles descriptions + # + + def showStackDesc(self): + from pysoltk import StackDesc + from stack import InitialDealTalonStack + sd_list = [] + for s in self.allstacks: + sd = (s.__class__.__name__, s.cap.base_rank, s.cap.dir) + if sd in sd_list: + # one of each uniq pile + continue + if isinstance(s, InitialDealTalonStack): + continue + self.stackdesc_list.append(StackDesc(self, s)) + sd_list.append(sd) + + def deleteStackDesc(self): + if self.stackdesc_list: + for sd in self.stackdesc_list: + sd.delete() + self.stackdesc_list = [] # # subclass hooks diff --git a/pysollib/games/auldlangsyne.py b/pysollib/games/auldlangsyne.py index 06e905ca..6e9a7164 100644 --- a/pysollib/games/auldlangsyne.py +++ b/pysollib/games/auldlangsyne.py @@ -154,7 +154,7 @@ class Strategy_RowStack(BasicRowStack): return self.game.app.images.getReserveBottom() def getHelp(self): - return _('Row. Build regardless of rank and suit.') + return _('Tableau. Build regardless of rank and suit.') class Strategy(Game): diff --git a/pysollib/games/calculation.py b/pysollib/games/calculation.py index 0ad14be1..c1cbdaae 100644 --- a/pysollib/games/calculation.py +++ b/pysollib/games/calculation.py @@ -101,7 +101,7 @@ class Calculation_RowStack(BasicRowStack): return self.game.app.images.getReserveBottom() def getHelp(self): - return _('Row. Build regardless of rank and suit.') + return _('Tableau. Build regardless of rank and suit.') # /*********************************************************************** diff --git a/pysollib/games/curdsandwhey.py b/pysollib/games/curdsandwhey.py index dd26195b..6537ceb9 100644 --- a/pysollib/games/curdsandwhey.py +++ b/pysollib/games/curdsandwhey.py @@ -57,7 +57,7 @@ class CurdsAndWhey_RowStack(BasicRowStack): return isSameSuitSequence(cards) or isRankSequence(cards, dir=0) def getHelp(self): - return _('Row. Build down by suit or of the same rank.') + return _('Tableau. Build down by suit or of the same rank.') class CurdsAndWhey(Game): diff --git a/pysollib/games/diplomat.py b/pysollib/games/diplomat.py index a5606425..99469452 100644 --- a/pysollib/games/diplomat.py +++ b/pysollib/games/diplomat.py @@ -41,6 +41,7 @@ from pysollib.stack import * from pysollib.game import Game from pysollib.layout import Layout from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint +from pysollib.pysoltk import MfxCanvasText from fortythieves import FortyThieves_Hint from spider import Spider_Hint @@ -138,7 +139,7 @@ class Congress(Diplomat): # game layout (just rearrange the stacks a little bit) # - def createGame(self): + def createGame(self, max_rounds=1): # create layout l, s = Layout(self), self.s @@ -160,11 +161,18 @@ class Congress(Diplomat): stack.CARD_YOFFSET = 0 s.rows.append(stack) x, y, = l.XM, l.YM - s.talon = WasteTalonStack(x, y, self, max_rounds=1) + s.talon = WasteTalonStack(x, y, self, max_rounds=max_rounds) l.createText(s.talon, "ss") x = x + l.XS s.waste = WasteStack(x, y, self) l.createText(s.waste, "ss") + if max_rounds > 1: + tx, ty, ta, tf = l.getTextAttr(s.waste, "ne") + font = self.app.getFont("canvas_default") + s.talon.texts.rounds = MfxCanvasText(self.canvas, + tx, ty, + anchor=ta, + font=font) # define stack-groups l.defaultStackGroups() @@ -251,6 +259,22 @@ class LittleNapoleon(Diplomat): return 0 +# /*********************************************************************** +# // Twin Queens +# ************************************************************************/ + +class TwinQueens(Congress): + Foundation_Classes = [ + StackWrapper(SS_FoundationStack, base_rank=KING, mod=13), + StackWrapper(SS_FoundationStack, base_rank=KING, mod=13), + ] + RowStack_Class = StackWrapper(SS_RowStack, max_move=1) + + def createGame(self): + Congress.createGame(self, max_rounds=2) + + + # register the game registerGame(GameInfo(149, Diplomat, "Diplomat", GI.GT_FORTY_THIEVES, 2, 0, GI.SL_BALANCED)) @@ -268,4 +292,6 @@ registerGame(GameInfo(548, Parliament, "Parliament", GI.GT_FORTY_THIEVES, 2, 0, GI.SL_BALANCED)) registerGame(GameInfo(549, Wheatsheaf, "Wheatsheaf", GI.GT_FORTY_THIEVES, 2, 0, GI.SL_BALANCED)) +registerGame(GameInfo(563, TwinQueens, "Twin Queens", + GI.GT_FORTY_THIEVES, 2, 1, GI.SL_MOSTLY_SKILL)) diff --git a/pysollib/games/fortythieves.py b/pysollib/games/fortythieves.py index e764b6fc..44e5ba4c 100644 --- a/pysollib/games/fortythieves.py +++ b/pysollib/games/fortythieves.py @@ -32,7 +32,6 @@ __all__ = [] # imports -import sys # PySol imports from pysollib.gamedb import registerGame, GameInfo, GI @@ -43,6 +42,9 @@ from pysollib.layout import Layout from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint from pysollib.pysoltk import MfxCanvasText +from gypsy import DieRussische_Foundation + + # /*********************************************************************** # // # ************************************************************************/ @@ -440,7 +442,7 @@ class Indian_RowStack(SequenceRowStack): def _isSequence(self, cards): return isAnySuitButOwnSequence(cards, self.cap.mod, self.cap.dir) def getHelp(self): - return _('Row. Build down in any suit but the same.') + return _('Tableau. Build down in any suit but the same.') class Indian(FortyThieves): @@ -805,8 +807,6 @@ class Waterloo(FortyThieves): # // Junction # ************************************************************************/ -from gypsy import DieRussische_Foundation - class Junction(Game): def createGame(self, rows=7): @@ -849,6 +849,89 @@ class Junction(Game): return card1.color != card2.color and abs(card1.rank-card2.rank) == 1 +# /*********************************************************************** +# // The Spark +# ************************************************************************/ + +class TheSpark_Talon(TalonStack): + + def canDealCards(self): + return len(self.cards) > 0 + + def dealCards(self, sound=0): + old_state = self.game.enterState(self.game.S_DEAL) + num_cards = 0 + if self.cards: + if sound and not self.game.demo: + self.game.playSample("dealwaste") + for i in range(self.num_deal): + for r in self.game.s.reserves: + if not self.cards: + break + self.game.flipMove(self) + self.game.moveMove(1, self, r, frames=4, shadow=0) + num_cards += 1 + self.game.leaveState(old_state) + return num_cards + + +class TheSpark(Game): + Hint_Class = CautiousDefaultHint + + def createGame(self): + + l, s = Layout(self), self.s + + w, h = l.XM+8*l.XS, l.YM+4*l.YS + self.setSize(w, h) + + x, y = l.XM, l.YM + for i in range(8): + s.foundations.append(SS_FoundationStack(x, y, self, + suit=i/2, base_rank=KING, mod=13)) + x += l.XS + x, y = l.XM, l.YM+l.YS + s.talon = TheSpark_Talon(x, y, self, max_rounds=1, num_deal=3) + l.createText(s.talon, 'se') + y += l.YS + for i in (0,1): + stack = WasteStack(x, y, self) + s.reserves.append(stack) + l.createText(stack, 'se') + y += l.YS + y = l.YM+l.YS*3/2 + for i in range(2): + x = l.XM+2*l.XS + for j in range(6): + stack = SS_RowStack(x, y, self, max_move=1) + stack.CARD_XOFFSET, stack.CARD_YOFFSET = 0, 0 + s.rows.append(stack) + x += l.XS + y += l.YS + + l.defaultStackGroups() + + + def _shuffleHook(self, cards): + # move Aces to top of the Talon (i.e. first cards to be dealt) + return self._shuffleHookMoveToTop(cards, + lambda c: (c.rank == KING, c.suit)) + + + def startGame(self): + self.s.talon.dealRow(rows=self.s.foundations, frames=0) + self.startDealSample() + self.s.talon.dealRow() + self.s.talon.dealCards() # deal first card to WasteStack + + + def shallHighlightMatch(self, stack1, card1, stack2, card2): + return card1.suit == card2.suit and abs(card1.rank-card2.rank) == 1 + + + + + # register the game registerGame(GameInfo(13, FortyThieves, "Forty Thieves", @@ -941,5 +1024,7 @@ registerGame(GameInfo(540, Waterloo, "Waterloo", registerGame(GameInfo(556, Junction, "Junction", GI.GT_FORTY_THIEVES, 4, 0, GI.SL_MOSTLY_SKILL, ranks=(0, 6, 7, 8, 9, 10, 11, 12) )) +registerGame(GameInfo(564, TheSpark, "The Spark", + GI.GT_FORTY_THIEVES, 2, 0, GI.SL_MOSTLY_LUCK)) diff --git a/pysollib/games/golf.py b/pysollib/games/golf.py index b946c836..fecb49fc 100644 --- a/pysollib/games/golf.py +++ b/pysollib/games/golf.py @@ -111,7 +111,7 @@ class Golf_RowStack(BasicRowStack): def clickHandler(self, event): return self.doubleclickHandler(event) def getHelp(self): - return _('Row. No building.') + return _('Tableau. No building.') # /*********************************************************************** @@ -411,7 +411,7 @@ class BlackHole_RowStack(ReserveStack): def clickHandler(self, event): return self.doubleclickHandler(event) def getHelp(self): - return _('Row. No building.') + return _('Tableau. No building.') class BlackHole(Game): diff --git a/pysollib/games/gypsy.py b/pysollib/games/gypsy.py index f27f5485..58cc2ccc 100644 --- a/pysollib/games/gypsy.py +++ b/pysollib/games/gypsy.py @@ -539,13 +539,68 @@ class Elba(Gypsy): class Millie(Gypsy): Layout_Method = Layout.klondikeLayout - RowStack_Class = AC_RowStack def startGame(self): self.startDealSample() self.s.talon.dealRow() +# /*********************************************************************** +# // Hypotenuse +# // Eternal Triangle +# // Right Triangle +# ************************************************************************/ + +class Hypotenuse(Gypsy): + Layout_Method = Layout.klondikeLayout + RowStack_Class = KingAC_RowStack + + def createGame(self): + Gypsy.createGame(self, rows=10, playcards=24) + + def startGame(self, flip=0, reverse=1): + for i in range(1, 10): + self.s.talon.dealRow(rows=self.s.rows[:i], flip=0, frames=0) + self.startDealSample() + self.s.talon.dealRow() + + +class EternalTriangle(Hypotenuse): + + def startGame(self, flip=0, reverse=1): + for i in range(1, 10): + self.s.talon.dealRow(rows=self.s.rows[i:], frames=0) + self.startDealSample() + self.s.talon.dealRow() + + +class RightTriangle_Talon(OpenStack, DealRowTalonStack): + def __init__(self, x, y, game, max_rounds=1, num_deal=1, **cap): + Stack.__init__(self, x, y, game, cap=cap) + self.max_rounds = max_rounds + self.num_deal = num_deal + self.round = 1 + self.base_cards = [] # for DealBaseCard_StackMethods + + def canFlipCard(self): + return False + + def getBottomImage(self): + return self.game.app.images.getReserveBottom() + + def getHelp(self): + return '' + +class RightTriangle(Hypotenuse): + Talon_Class = StackWrapper(RightTriangle_Talon, max_accept=1, max_move=1) + + def createGame(self): + Gypsy.createGame(self, rows=10, playcards=24) + self.sg.dropstacks.append(self.s.talon) + self.sg.openstacks.append(self.s.talon) + self.sg.reservestacks.append(self.s.talon) + + # register the game registerGame(GameInfo(1, Gypsy, "Gypsy", GI.GT_GYPSY, 2, 0, GI.SL_MOSTLY_SKILL)) @@ -593,4 +648,11 @@ registerGame(GameInfo(487, Millie, "Millie", GI.GT_GYPSY, 2, 0, GI.SL_BALANCED)) registerGame(GameInfo(498, Steve, "Steve", GI.GT_GYPSY, 2, 0, GI.SL_BALANCED)) +registerGame(GameInfo(566, Hypotenuse, "Hypotenuse", + GI.GT_GYPSY, 2, 0, GI.SL_MOSTLY_SKILL)) +registerGame(GameInfo(567, EternalTriangle, "Eternal Triangle", + GI.GT_GYPSY, 2, 0, GI.SL_MOSTLY_SKILL, + altnames=('Lobachevsky',) )) +registerGame(GameInfo(568, RightTriangle, "Right Triangle", + GI.GT_GYPSY, 2, 0, GI.SL_MOSTLY_SKILL)) diff --git a/pysollib/games/harp.py b/pysollib/games/harp.py index 7562ba56..de6bf679 100644 --- a/pysollib/games/harp.py +++ b/pysollib/games/harp.py @@ -224,12 +224,14 @@ class Arabella(DoubleKlondike): # ************************************************************************/ class BigDeal(DoubleKlondike): - def createGame(self, rows=12, max_rounds=2): + RowStack_Class = KingAC_RowStack + + def createGame(self, rows=12, max_rounds=2, XOFFSET=0): l, s = Layout(self), self.s self.setSize(l.XM+(rows+2)*l.XS, l.YM+8*l.YS) x, y = l.XM, l.YM for i in range(rows): - s.rows.append(AC_RowStack(x, y, self, base_rank=KING)) + s.rows.append(self.RowStack_Class(x, y, self)) x += l.XS for i in range(2): y = l.YM @@ -242,16 +244,39 @@ class BigDeal(DoubleKlondike): l.createText(s.talon, 'n') x += l.XS s.waste = WasteStack(x, y, self) + s.waste.CARD_XOFFSET = XOFFSET l.createText(s.waste, 'n') if max_rounds > 1: tx, ty, ta, tf = l.getTextAttr(s.talon, 'nn') - ty -= 2*l.TEXT_MARGIN + ty -= l.TEXT_MARGIN font = self.app.getFont('canvas_default') - s.talon.texts.rounds = MfxCanvasText(self.canvas, tx, ty, anchor=ta, font=font) + s.talon.texts.rounds = MfxCanvasText(self.canvas, tx, ty, + anchor=ta, font=font) self.setRegion(s.rows, (-999, -999, l.XM+rows*l.XS-l.CW/2, 999999), priority=1) l.defaultStackGroups() +# /*********************************************************************** +# // Delivery +# ************************************************************************/ + +class Delivery(BigDeal): + RowStack_Class = StackWrapper(SS_RowStack, max_move=1) + + def createGame(self): + dx = self.app.images.CARDW/10 + BigDeal.createGame(self, rows=12, max_rounds=1, XOFFSET=dx) + + def shallHighlightMatch(self, stack1, card1, stack2, card2): + return card1.suit == card2.suit and abs(card1.rank-card2.rank) == 1 + + def startGame(self): + for i in range(2): + self.s.talon.dealRow(frames=0) + self.startDealSample() + self.s.talon.dealRow() + self.s.talon.dealCards() # deal first card to WasteStack + # register the game registerGame(GameInfo(21, DoubleKlondike, "Double Klondike", @@ -277,4 +302,6 @@ registerGame(GameInfo(497, Arabella, "Arabella", GI.GT_KLONDIKE, 3, 0, GI.SL_BALANCED)) registerGame(GameInfo(545, BigDeal, "Big Deal", GI.GT_KLONDIKE | GI.GT_ORIGINAL, 4, 1, GI.SL_BALANCED)) +registerGame(GameInfo(562, Delivery, "Delivery", + GI.GT_FORTY_THIEVES | GI.GT_ORIGINAL, 4, 0, GI.SL_BALANCED)) diff --git a/pysollib/games/klondike.py b/pysollib/games/klondike.py index 22985c82..518a9e8e 100644 --- a/pysollib/games/klondike.py +++ b/pysollib/games/klondike.py @@ -145,7 +145,7 @@ class ThumbAndPouch_RowStack(SequenceRowStack): def _isSequence(self, cards): return isAnySuitButOwnSequence(cards, self.cap.mod, self.cap.dir) def getHelp(self): - return _('Row. Build down in any suit but the same.') + return _('Tableau. Build down in any suit but the same.') class ThumbAndPouch(Klondike): diff --git a/pysollib/games/numerica.py b/pysollib/games/numerica.py index bc2f0072..e7ddfad6 100644 --- a/pysollib/games/numerica.py +++ b/pysollib/games/numerica.py @@ -86,8 +86,8 @@ class Numerica_RowStack(BasicRowStack): return self.game.app.images.getReserveBottom() def getHelp(self): - ##return _('Row. Accepts any one card from the Waste.') - return _('Row. Build regardless of rank and suit.') + ##return _('Tableau. Accepts any one card from the Waste.') + return _('Tableau. Build regardless of rank and suit.') # /*********************************************************************** @@ -233,8 +233,8 @@ class PussInTheCorner_RowStack(BasicRowStack): def getBottomImage(self): return self.game.app.images.getReserveBottom() def getHelp(self): - ##return _('Row. Accepts any one card from the Waste.') - return _('Row. Build regardless of rank and suit.') + ##return _('Tableau. Accepts any one card from the Waste.') + return _('Tableau. Build regardless of rank and suit.') class PussInTheCorner(Numerica): @@ -587,7 +587,7 @@ class Strategerie_RowStack(BasicRowStack): return self.game.app.images.getReserveBottom() def getHelp(self): - return _('Row. Build regardless of rank and suit.') + return _('Tableau. Build regardless of rank and suit.') class Strategerie_ReserveStack(ReserveStack): diff --git a/pysollib/games/pileon.py b/pysollib/games/pileon.py index 875019eb..4a079ead 100644 --- a/pysollib/games/pileon.py +++ b/pysollib/games/pileon.py @@ -146,22 +146,22 @@ class Foursome(Game): def createGame(self, rows=6, texts=True): l, s = Layout(self), self.s - max_rows = max(6, rows) - self.setSize(l.XM+max_rows*l.XS, l.YM+3*l.YS+13*l.YOFFSET) - x, y = l.XM+(max_rows-6)*l.XS/2, l.YM + max_rows = max(8, rows) + self.setSize(l.XM+max_rows*l.XS, l.YM+2*l.YS+13*l.YOFFSET) + x, y = l.XM+l.XS*(max_rows-4)/2, l.YM for i in range(4): s.reserves.append(ReserveStack(x, y, self)) x += l.XS x = l.XM+(max_rows-1)*l.XS s.foundations.append(AbstractFoundationStack(x, y, self, suit=ANY_SUIT, max_cards=52, max_accept=0)) - x, y = l.XM, l.YM+l.YS + x, y = l.XM+l.XS*(max_rows-rows)/2, l.YM+l.YS for i in range(rows): s.rows.append(UD_AC_RowStack(x, y, self, mod=13)) x += l.XS - s.talon = self.Talon_Class(self.width-l.XS, self.height-l.YS, self) + s.talon = self.Talon_Class(l.XM, l.YM, self) if texts: - l.createText(s.talon, 'n') + l.createText(s.talon, 'ne') l.defaultStackGroups() def startGame(self): diff --git a/pysollib/games/royalcotillion.py b/pysollib/games/royalcotillion.py index b68a7c4d..8261a448 100644 --- a/pysollib/games/royalcotillion.py +++ b/pysollib/games/royalcotillion.py @@ -233,21 +233,6 @@ class Alhambra_RowStack(UD_SS_RowStack): return self.game.app.images.getReserveBottom() -class Alhambra_Talon__(RedealTalonStack): - - def canDealCards(self): - if self.round == self.max_rounds: - return len(self.cards) != 0 - return not self.game.isGameWon() - - def dealCards(self, sound=0): - if self.cards: - return self.dealRowAvail(sound=sound) - RedealTalonStack.redealCards(self, frames=0, - shuffle=False, sound=sound) - return self.dealRowAvail(sound=sound) - - class Alhambra_Talon(DealRowTalonStack): def canDealCards(self): r_cards = sum([len(r.cards) for r in self.game.s.rows]) diff --git a/pysollib/games/sultan.py b/pysollib/games/sultan.py index 0b38af90..d3027ed6 100644 --- a/pysollib/games/sultan.py +++ b/pysollib/games/sultan.py @@ -746,22 +746,23 @@ class Marshal(Game): def createGame(self): l, s = Layout(self), self.s - self.setSize(l.XM+8*l.XS, l.YM+5*l.YS) + self.setSize(l.XM+9*l.XS, l.YM+5*l.YS) x, y = l.XM, l.YM for i in range(4): s.foundations.append(SS_FoundationStack(x, y, self, suit=i)) - x += l.XS + y += l.YS + x, y = self.width-l.XS, l.YM for i in range(4): s.foundations.append(SS_FoundationStack(x, y, self, suit=i, base_rank=KING, dir=-1)) - x += l.XS - x, y = l.XM, l.YM+l.YS - s.talon = TalonStack(x, y, self) + y += l.YS + x, y = (self.width-l.XS)/2, self.height-l.YS + s.talon = DealRowTalonStack(x, y, self) l.createText(s.talon, 'se') - y = l.YM+l.YS + y = l.YM for i in range(4): - x = l.XM+2*l.XS + x = l.XM+l.XS*3/2 for j in range(6): stack = UD_SS_RowStack(x, y, self, base_rank=NO_RANK) s.rows.append(stack) @@ -788,6 +789,75 @@ class Marshal(Game): (abs(card1.rank-card2.rank) == 1)) +# /*********************************************************************** +# // Royal Aids +# ************************************************************************/ + +class RoyalAids_RowStack(KingAC_RowStack): + def getBottomImage(self): + return self.game.app.images.getReserveBottom() + + +class RoyalAids(Game): + + Hint_Class = CautiousDefaultHint + + def createGame(self): + + l, s = Layout(self), self.s + self.setSize(l.XM+8*l.XS, l.YM+4*l.YS) + + x0 = l.XM+1.5*l.XS + for k in (0,1): + suit = 0 + for i, j in ((1,0), (0,0.5), (2,0.5), (1,1)): + x, y = x0+i*l.XS, l.YM+j*l.YS + s.foundations.append(AC_FoundationStack(x, y, self, suit=suit)) + suit += 1 + x0 += 3.5*l.XS + + x, y = l.XM, l.YM+l.YS + s.talon = WasteTalonStack(x, y, self, max_rounds=UNLIMITED_REDEALS) + l.createText(s.talon, 'se') + y += l.YS + s.waste = WasteStack(x, y, self) + l.createText(s.waste, 'se') + + x, y = l.XM+4*l.XS, l.YM+2*l.YS + for i in (0,1): + stack = RoyalAids_RowStack(x, y, self, max_move=1) + s.rows.append(stack) + stack.CARD_XOFFSET, stack.CARD_YOFFSET = 0, 0 + x += l.XS + x, y = l.XM+3*l.XS, l.YM+3*l.YS + for i in range(4): + stack = BasicRowStack(x, y, self) + s.reserves.append(stack) + stack.CARD_XOFFSET, stack.CARD_YOFFSET = 0, 0 + x += l.XS + + l.defaultStackGroups() + + + def _shuffleHook(self, cards): + return self._shuffleHookMoveToTop(cards, + lambda c: (c.rank == ACE, (c.deck, c.suit))) + + + def startGame(self): + self.s.talon.dealRow(rows=self.s.foundations, frames=0) + for i in range(6): + self.s.talon.dealRow(rows=self.s.reserves, frames=0) + self.startDealSample() + for i in range(4): + self.s.talon.dealRow(rows=self.s.reserves) + self.s.talon.dealCards() + + + def shallHighlightMatch(self, stack1, card1, stack2, card2): + return (card1.color != card2.color and + (abs(card1.rank-card2.rank) == 1)) + # register the game registerGame(GameInfo(330, Sultan, "Sultan", @@ -819,3 +889,5 @@ registerGame(GameInfo(477, CornerSuite, "Corner Suite", GI.GT_2DECK_TYPE, 1, 0, GI.SL_MOSTLY_LUCK)) registerGame(GameInfo(559, Marshal, "Marshal", GI.GT_2DECK_TYPE, 2, 0, GI.SL_BALANCED)) +registerGame(GameInfo(565, RoyalAids, "Royal Aids", + GI.GT_2DECK_TYPE, 2, UNLIMITED_REDEALS, GI.SL_BALANCED)) diff --git a/pysollib/games/windmill.py b/pysollib/games/windmill.py index bc267c86..1a49963a 100644 --- a/pysollib/games/windmill.py +++ b/pysollib/games/windmill.py @@ -77,6 +77,7 @@ class Windmill(Game): ] RowStack_Class = Windmill_RowStack + FOUNDATIONS_LAYOUT = ((1,0.6), (3,0.6), (1,3.4), (3,3.4)) ROWS_LAYOUT = ((2,0), (2,1), (0,2), (1,2), (3,2), (4,2), (2,3), (2,4)) FILL_STACK = True @@ -84,12 +85,14 @@ class Windmill(Game): # game layout # - def createGame(self): + def createGame(self, card_x_space=20): # create layout - l, s = Layout(self, card_x_space=20), self.s + l, s = Layout(self, card_x_space=card_x_space), self.s # set window - self.setSize(7*l.XS+l.XM, 5*l.YS+l.YM+l.YM) + max_x = max([i[0] for i in self.FOUNDATIONS_LAYOUT+self.ROWS_LAYOUT]) + max_y = max([i[1] for i in self.FOUNDATIONS_LAYOUT+self.ROWS_LAYOUT]) + self.setSize((3+max_x)*l.XS+l.XM, (1+max_y)*l.YS+l.YM+l.YM) # create stacks x = l.XM @@ -109,7 +112,7 @@ class Windmill(Game): fnd_cls = self.Foundation_Classes[0] s.foundations.append(fnd_cls(x, y, self)) fnd_cls = self.Foundation_Classes[1] - for d in ((1,0.6), (3,0.6), (1,3.4), (3,3.4)): + for d in self.FOUNDATIONS_LAYOUT: x, y = x0 + d[0] * l.XS, y0 + d[1] * l.YS s.foundations.append(fnd_cls(x, y, self)) @@ -158,22 +161,38 @@ class DutchSolitaire_RowStack(UD_RK_RowStack): class DutchSolitaire(Windmill): Foundation_Classes = [ - StackWrapper(BlackHole_Foundation, suit=ANY_SUIT, mod=13, max_cards=UNLIMITED_CARDS), - StackWrapper(BlackHole_Foundation, suit=ANY_SUIT, mod=13, max_cards=UNLIMITED_CARDS), + StackWrapper(BlackHole_Foundation, suit=ANY_SUIT, mod=13, + max_cards=UNLIMITED_CARDS, min_cards=1), + StackWrapper(BlackHole_Foundation, suit=ANY_SUIT, mod=13, + max_cards=UNLIMITED_CARDS, min_cards=1), ] RowStack_Class = DutchSolitaire_RowStack - ##ROWS_LAYOUT = ((2,0), (2,1), (0,2), (1,2), (3,2), (4,2), (2,3), (2,4)) - ROWS_LAYOUT = ((2,0), (2,1), (1,2), (3,2), (2,3), (2,4)) + FOUNDATIONS_LAYOUT = ((1,1), (3,1), (1,3), (3,3)) + ROWS_LAYOUT = ((2,0.5), (-0.5,2), (0.5,2), (3.5,2), (4.5,2), (2,3.5)) FILL_STACK = False + def createGame(self): + Windmill.createGame(self, card_x_space=10) + def _shuffleHook(self, cards): - return cards + # move 5 Aces to top of the Talon (i.e. first cards to be dealt) + def select_cards(c): + if c.rank == ACE: + if c.suit in (0, 1): + return True, c.suit + if c.suit == 3 and c.deck == 0: + return True, c.suit + return False, None + return self._shuffleHookMoveToTop(cards, select_cards) def startGame(self): + self.s.talon.dealRow(rows=self.s.foundations, frames=0) + for i in range(8): + self.s.talon.dealRow(frames=0) self.startDealSample() - #self.s.talon.dealRow(rows=(self.s.foundations[0],)) - #self.s.talon.dealRow() + self.s.talon.dealRow() + self.s.talon.dealRow() self.s.talon.dealCards() # deal first card to WasteStack def getAutoStacks(self, event=None): diff --git a/pysollib/games/yukon.py b/pysollib/games/yukon.py index 6ccb67fe..a5ed6f67 100644 --- a/pysollib/games/yukon.py +++ b/pysollib/games/yukon.py @@ -140,7 +140,7 @@ class Moosehide_RowStack(Yukon_AC_RowStack): def _isSequence(self, c1, c2): return (c1.suit != c2.suit and c1.rank == c2.rank+1) def getHelp(self): - return _('Row. Build down in any suit but the same, can move any face-up cards regardless of sequence.') + return _('Tableau. Build down in any suit but the same, can move any face-up cards regardless of sequence.') class Moosehide(Yukon): RowStack_Class = StackWrapper(Moosehide_RowStack, base_rank=KING) @@ -199,7 +199,7 @@ class Alaska_RowStack(Yukon_SS_RowStack): ((c1.rank + self.cap.dir) % self.cap.mod == c2.rank or (c2.rank + self.cap.dir) % self.cap.mod == c1.rank)) def getHelp(self): - return _('Row. Build up or down by suit, can move any face-up cards regardless of sequence.') + return _('Tableau. Build up or down by suit, can move any face-up cards regardless of sequence.') class Alaska(RussianSolitaire): @@ -216,7 +216,7 @@ class Roslin_RowStack(Yukon_AC_RowStack): ((c1.rank + self.cap.dir) % self.cap.mod == c2.rank or (c2.rank + self.cap.dir) % self.cap.mod == c1.rank)) def getHelp(self): - return _('Row. Build up or down by alternate color, can move any face-up cards regardless of sequence.') + return _('Tableau. Build up or down by alternate color, can move any face-up cards regardless of sequence.') class Roslin(Yukon): diff --git a/pysollib/layout.py b/pysollib/layout.py index aa3e02ce..119989d4 100644 --- a/pysollib/layout.py +++ b/pysollib/layout.py @@ -500,7 +500,7 @@ class Layout: if rows < maxrows: x += (maxrows-rows) * XS/2 ##y += YM * (3 - foundrows) y += text_height - self.setRegion(self.s.rows, (-999, y - YM / 2, 999999, 999999)) + self.setRegion(self.s.rows, (-999, y-CH/2, 999999, 999999)) for i in range(rows): self.s.rows.append(S(x, y)) x = x + XS diff --git a/pysollib/stack.py b/pysollib/stack.py index 3da4d72a..6ac0ae51 100644 --- a/pysollib/stack.py +++ b/pysollib/stack.py @@ -1556,8 +1556,8 @@ class TalonStack(Stack, ##round = _('Round #%d.') % self.round return _('Talon.')+' '+nredeals ##+' '+round - def getBaseCard(self): - return self._getBaseCard() + #def getBaseCard(self): + # return self._getBaseCard() # A single click deals one card to each of the RowStacks. @@ -1777,6 +1777,9 @@ class AbstractFoundationStack(OpenStack): if len(self.cards) == self.cap.max_cards: self.game.closeStackMove(self) + def getHelp(self): + return _('Foundation.') + # A SameSuit_FoundationStack is the typical Foundation stack. # It builds up in rank and suit. @@ -1874,7 +1877,7 @@ class BasicRowStack(OpenStack): def getHelp(self): if self.cap.max_accept == 0: - return _('Row. No building.') + return _('Tableau. No building.') return '' #def getBaseCard(self): @@ -1904,9 +1907,9 @@ class AC_RowStack(SequenceRowStack): def _isSequence(self, cards): return isAlternateColorSequence(cards, self.cap.mod, self.cap.dir) def getHelp(self): - if self.cap.dir > 0: return _('Row. Build up by alternate color.') - elif self.cap.dir < 0: return _('Row. Build down by alternate color.') - else: return _('Row. Build by same rank.') + if self.cap.dir > 0: return _('Tableau. Build up by alternate color.') + elif self.cap.dir < 0: return _('Tableau. Build down by alternate color.') + else: return _('Tableau. Build by same rank.') # A SameColor_RowStack builds down by rank and same color. # e.g. Klondike @@ -1914,27 +1917,27 @@ class SC_RowStack(SequenceRowStack): def _isSequence(self, cards): return isSameColorSequence(cards, self.cap.mod, self.cap.dir) def getHelp(self): - if self.cap.dir > 0: return _('Row. Build up by color.') - elif self.cap.dir < 0: return _('Row. Build down by color.') - else: return _('Row. Build by same rank.') + if self.cap.dir > 0: return _('Tableau. Build up by color.') + elif self.cap.dir < 0: return _('Tableau. Build down by color.') + else: return _('Tableau. Build by same rank.') # A SameSuit_RowStack builds down by rank and suit. class SS_RowStack(SequenceRowStack): def _isSequence(self, cards): return isSameSuitSequence(cards, self.cap.mod, self.cap.dir) def getHelp(self): - if self.cap.dir > 0: return _('Row. Build up by suit.') - elif self.cap.dir < 0: return _('Row. Build down by suit.') - else: return _('Row. Build by same rank.') + if self.cap.dir > 0: return _('Tableau. Build up by suit.') + elif self.cap.dir < 0: return _('Tableau. Build down by suit.') + else: return _('Tableau. Build by same rank.') # A Rank_RowStack builds down by rank ignoring suit. class RK_RowStack(SequenceRowStack): def _isSequence(self, cards): return isRankSequence(cards, self.cap.mod, self.cap.dir) def getHelp(self): - if self.cap.dir > 0: return _('Row. Build up regardless of suit.') - elif self.cap.dir < 0: return _('Row. Build down regardless of suit.') - else: return _('Row. Build by same rank.') + if self.cap.dir > 0: return _('Tableau. Build up regardless of suit.') + elif self.cap.dir < 0: return _('Tableau. Build down regardless of suit.') + else: return _('Tableau. Build by same rank.') # A Freecell_AlternateColor_RowStack class FreeCell_AC_RowStack(AC_RowStack): @@ -1960,9 +1963,9 @@ class Spider_SS_RowStack(SS_RowStack): def _isAcceptableSequence(self, cards): return isRankSequence(cards, self.cap.mod, self.cap.dir) def getHelp(self): - if self.cap.dir > 0: return _('Row. Build up regardless of suit.') - elif self.cap.dir < 0: return _('Row. Build down regardless of suit.') - else: return _('Row. Build by same rank.') + if self.cap.dir > 0: return _('Tableau. Build up regardless of suit.') + elif self.cap.dir < 0: return _('Tableau. Build down regardless of suit.') + else: return _('Tableau. Build by same rank.') # A Yukon_AlternateColor_RowStack builds down by rank and alternate color, # but can move any face-up cards regardless of sequence. @@ -1983,10 +1986,12 @@ class Yukon_AC_RowStack(BasicRowStack): return 1 def getHelp(self): - if self.cap.dir > 0: return _('Row. Build up by alternate color, can move any face-up cards regardless of sequence.') - elif self.cap.dir < 0: return _('Row. Build down by alternate color, can move any face-up cards regardless of sequence.') - else: return _('Row. Build by same rank, can move any face-up cards regardless of sequence.') + if self.cap.dir > 0: return _('Tableau. Build up by alternate color, can move any face-up cards regardless of sequence.') + elif self.cap.dir < 0: return _('Tableau. Build down by alternate color, can move any face-up cards regardless of sequence.') + else: return _('Tableau. Build by same rank, can move any face-up cards regardless of sequence.') + def getBaseCard(self): + return self._getBaseCard() # A Yukon_SameSuit_RowStack builds down by rank and suit, # but can move any face-up cards regardless of sequence. @@ -1994,9 +1999,9 @@ class Yukon_SS_RowStack(Yukon_AC_RowStack): def _isSequence(self, c1, c2): return (c1.rank + self.cap.dir) % self.cap.mod == c2.rank and c1.suit == c2.suit def getHelp(self): - if self.cap.dir > 0: return _('Row. Build up by suit, can move any face-up cards regardless of sequence.') - elif self.cap.dir < 0: return _('Row. Build down by suit, can move any face-up cards regardless of sequence.') - else: return _('Row. Build by same rank, can move any face-up cards regardless of sequence.') + if self.cap.dir > 0: return _('Tableau. Build up by suit, can move any face-up cards regardless of sequence.') + elif self.cap.dir < 0: return _('Tableau. Build down by suit, can move any face-up cards regardless of sequence.') + else: return _('Tableau. Build by same rank, can move any face-up cards regardless of sequence.') # # King-versions of some of the above stacks: they accepts only Kings or @@ -2028,7 +2033,7 @@ class UD_SC_RowStack(SequenceRowStack): return (isSameColorSequence(cards, self.cap.mod, 1) or isSameColorSequence(cards, self.cap.mod, -1)) def getHelp(self): - return _('Row. Build up or down by color.') + return _('Tableau. Build up or down by color.') # up or down by alternate color class UD_AC_RowStack(SequenceRowStack): @@ -2039,7 +2044,7 @@ class UD_AC_RowStack(SequenceRowStack): return (isAlternateColorSequence(cards, self.cap.mod, 1) or isAlternateColorSequence(cards, self.cap.mod, -1)) def getHelp(self): - return _('Row. Build up or down by alternate color.') + return _('Tableau. Build up or down by alternate color.') # up or down by suit class UD_SS_RowStack(SequenceRowStack): @@ -2050,7 +2055,7 @@ class UD_SS_RowStack(SequenceRowStack): return (isSameSuitSequence(cards, self.cap.mod, 1) or isSameSuitSequence(cards, self.cap.mod, -1)) def getHelp(self): - return _('Row. Build up or down by suit.') + return _('Tableau. Build up or down by suit.') # up or down by rank ignoring suit class UD_RK_RowStack(SequenceRowStack): @@ -2061,7 +2066,7 @@ class UD_RK_RowStack(SequenceRowStack): return (isRankSequence(cards, self.cap.mod, 1) or isRankSequence(cards, self.cap.mod, -1)) def getHelp(self): - return _('Row. Build up or down regardless of suit.') + return _('Tableau. Build up or down regardless of suit.') diff --git a/pysollib/tk/menubar.py b/pysollib/tk/menubar.py index 74c1a2b5..5e96ebca 100644 --- a/pysollib/tk/menubar.py +++ b/pysollib/tk/menubar.py @@ -37,7 +37,7 @@ __all__ = ['PysolMenubar'] # imports -import math, os, re, sys, types +import math, os, re, types import Tkinter, tkColorChooser, tkFileDialog # PySol imports @@ -318,6 +318,8 @@ class PysolMenubar(PysolMenubarActions): menu.add_separator() menu.add_command(label=n_("&Demo"), command=self.mDemo, accelerator=m+"D") menu.add_command(label=n_("Demo (&all games)"), command=self.mMixedDemo) + menu.add_separator() + menu.add_command(label=n_("Show descriptions od piles"), command=self.mStackDesk, accelerator="F2") menu = MfxMenu(self.__menubar, label=n_("&Options")) menu.add_command(label=n_("&Player options..."), command=self.mOptPlayerOptions) submenu = MfxMenu(menu, label=n_("&Automatic play")) @@ -425,8 +427,8 @@ class PysolMenubar(PysolMenubarActions): self._bindKey("", "Print", self.mScreenshot) self._bindKey(ctrl, "u", self.mPlayNextMusic) # undocumented self._bindKey("", "p", self.mPause) - self._bindKey("", "Pause", self.mPause) - self._bindKey("", "Escape", self.mIconify) + self._bindKey("", "Pause", self.mPause) # undocumented + self._bindKey("", "Escape", self.mIconify) # undocumented # ASD and LKJ self._bindKey("", "a", self.mDrop) self._bindKey(ctrl, "a", self.mDrop1) @@ -436,6 +438,8 @@ class PysolMenubar(PysolMenubarActions): self._bindKey(ctrl, "l", self.mDrop1) self._bindKey("", "k", self.mUndo) self._bindKey("", "j", self.mDeal) + + self._bindKey("", "F2", self.mStackDesk) # self._bindKey("", "slash", self.mGameInfo) # undocumented, devel @@ -1053,5 +1057,14 @@ class PysolMenubar(PysolMenubarActions): self.app.toolbar.config(w, v) self.top.update_idletasks() + # + # stacks descriptions + # + def mStackDesk(self, *event): + if self.game.stackdesc_list: + self.game.deleteStackDesc() + else: + if self._cancelDrag(break_pause=True): return + self.game.showStackDesc() diff --git a/pysollib/tk/tkwidget.py b/pysollib/tk/tkwidget.py index b6fd0086..5c8503e3 100644 --- a/pysollib/tk/tkwidget.py +++ b/pysollib/tk/tkwidget.py @@ -38,6 +38,7 @@ __all__ = ['MfxMessageDialog', 'MfxSimpleEntry', 'MfxTooltip', 'MfxScrolledCanvas', + 'StackDesc', ] # imports @@ -669,3 +670,43 @@ class MfxScrolledCanvas: return self._yview('moveto', 1) +# /*********************************************************************** +# // +# ************************************************************************/ + +class StackDesc: + + def __init__(self, game, stack): + self.game = game + self.stack = stack + self.canvas = game.canvas + + font = game.app.getFont('canvas_small') + ##print self.app.cardset.CARDW, self.app.images.CARDW + cardw = game.app.images.CARDW + x, y = stack.x+cardw/2, stack.y + text = stack.getHelp()+'\n'+stack.getBaseCard() + text = text.strip() + if text: + frame = Tkinter.Frame(self.canvas, highlightthickness=1, + highlightbackground='black') + label = Tkinter.Message(frame, font=font, text=text, width=cardw-8, + fg='#000000', bg='#ffffe0') + label.pack() + self.label = label + self.id = self.canvas.create_window(x, y, window=frame, anchor='n') + self.binding = label.bind('', self.buttonPressEvent) + else: + self.id = None + + def buttonPressEvent(self, *event): + self.game.deleteStackDesc() + + def delete(self): + if self.id: + self.canvas.delete(self.id) + self.label.unbind('', self.binding) + + + +