From 7ab84b4c328efb62f1e3f6eb9a42343a8ccbfa93 Mon Sep 17 00:00:00 2001 From: skomoroh Date: Sat, 29 Jul 2006 21:17:43 +0000 Subject: [PATCH] + 12 new games + new Layout method: createGame + new stack: BO_RowStack (ButOwn_RowStack) git-svn-id: https://pysolfc.svn.sourceforge.net/svnroot/pysolfc/PySolFC/trunk@31 39dd0a4e-7c14-0410-91b3-c4f2d318f732 --- pysollib/app.py | 1 - pysollib/game.py | 4 +- pysollib/games/acesup.py | 31 ++++++- pysollib/games/fortythieves.py | 38 ++++---- pysollib/games/glenwood.py | 159 +++++++++++++++++++++++++++++++- pysollib/games/gypsy.py | 104 +++++++++++++++++++++ pysollib/games/harp.py | 16 +++- pysollib/games/katzenschwanz.py | 22 +++-- pysollib/games/klondike.py | 56 ++++++----- pysollib/games/numerica.py | 51 ++++++++-- pysollib/games/pyramid.py | 72 ++++++++++++--- pysollib/games/spider.py | 3 +- pysollib/games/terrace.py | 22 ++++- pysollib/layout.py | 61 +++++++++++- pysollib/stack.py | 16 +++- pysollib/tk/menubar.py | 12 ++- 16 files changed, 574 insertions(+), 94 deletions(-) diff --git a/pysollib/app.py b/pysollib/app.py index 639dec14..94fd8d07 100644 --- a/pysollib/app.py +++ b/pysollib/app.py @@ -104,7 +104,6 @@ class Options: self.shade = 1 self.shade_filled_stacks = True self.demo_logo = 1 - self.demo_score = 0 self.toolbar = 1 ##self.toolbar_style = 'default' self.toolbar_style = 'crystal' diff --git a/pysollib/game.py b/pysollib/game.py index 677c1655..8b050dd5 100644 --- a/pysollib/game.py +++ b/pysollib/game.py @@ -198,7 +198,7 @@ class Game: # update display properties self.top.wm_geometry("") # cancel user-specified geometry self.canvas.setInitialSize(self.width, self.height) - if self.app.debug >= 2: + if self.app.debug >= 4: MfxCanvasRectangle(self.canvas, 0, 0, self.width, self.height, width=2, fill=None, outline='green') # restore game geometry @@ -1566,7 +1566,7 @@ for %d moves. assert to_stack.acceptsCards(from_stack, from_stack.cards[-ncards:]) if sleep <= 0.0: return h - info = (level == 1) or (level > 1 and self.app.opt.demo_score) + info = (level == 1) or (level > 1 and self.app.debug >= 3) if info and self.app.statusbar and self.app.opt.statusbar: self.app.statusbar.configLabel("info", text=_("Score %6d") % (score), fg=text_color) else: diff --git a/pysollib/games/acesup.py b/pysollib/games/acesup.py index 57435e2f..66b3380c 100644 --- a/pysollib/games/acesup.py +++ b/pysollib/games/acesup.py @@ -80,7 +80,7 @@ class AcesUp(Game): # game layout # - def createGame(self, rows=4, **layout): + def createGame(self, rows=4, reserve=False, **layout): # create layout l, s = Layout(self), self.s @@ -90,7 +90,10 @@ class AcesUp(Game): # create stacks x, y, = l.XM, l.YM s.talon = self.Talon_Class(x, y, self) - l.createText(s.talon, "ss") + if reserve: + l.createText(s.talon, "ne") + else: + l.createText(s.talon, "ss") x = x + 3*l.XS/2 for i in range(rows): s.rows.append(self.RowStack_Class(x, y, self)) @@ -101,6 +104,10 @@ class AcesUp(Game): l.createText(stack, "ss") s.foundations.append(stack) + if reserve: + x, y = l.XM, l.YM+l.YS + s.reserves.append(self.ReserveStack_Class(x, y, self)) + # define stack-groups l.defaultStackGroups() @@ -287,6 +294,24 @@ class Cover(AcesUp): return len(self.s.foundations[0].cards) == 48 +# /*********************************************************************** +# // Firing Squad +# ************************************************************************/ + +class FiringSquad_Foundation(AcesUp_Foundation): + def acceptsCards(self, from_stack, cards): + if not AcesUp_Foundation.acceptsCards(self, from_stack, cards): + return False + return from_stack in self.game.s.rows + +class FiringSquad(AcesUp): + Foundation_Class = FiringSquad_Foundation + ReserveStack_Class = ReserveStack + def createGame(self): + AcesUp.createGame(self, reserve=True) + + + # register the game registerGame(GameInfo(903, AcesUp, "Aces Up", # was: 52 GI.GT_1DECK_TYPE, 1, 0, GI.SL_LUCK, @@ -302,3 +327,5 @@ registerGame(GameInfo(353, AcesUp5, "Aces Up 5", GI.GT_1DECK_TYPE, 1, 0, GI.SL_LUCK)) registerGame(GameInfo(552, Cover, "Cover", GI.GT_1DECK_TYPE, 1, 0, GI.SL_LUCK)) +registerGame(GameInfo(583, FiringSquad, "Firing Squad", + GI.GT_1DECK_TYPE, 1, 0, GI.SL_BALANCED)) diff --git a/pysollib/games/fortythieves.py b/pysollib/games/fortythieves.py index 98990c50..bae783a2 100644 --- a/pysollib/games/fortythieves.py +++ b/pysollib/games/fortythieves.py @@ -94,19 +94,25 @@ class FortyThieves(Game): self.setSize(w1, l.YM + l.YS + h + l.YS + l.TEXT_HEIGHT) # create stacks + # foundations x = l.XM + (maxrows - 4*decks) * l.XS / 2 y = l.YM for i in range(4*decks): - s.foundations.append(self.Foundation_Class(x, y, self, suit=i/decks, max_move=self.FOUNDATION_MAX_MOVE)) + s.foundations.append(self.Foundation_Class(x, y, self, + suit=i/decks, max_move=self.FOUNDATION_MAX_MOVE)) x = x + l.XS + # rows x = l.XM + (maxrows - rows) * l.XS / 2 y = l.YM + l.YS for i in range(rows): - s.rows.append(self.RowStack_Class(x, y, self, max_move=self.ROW_MAX_MOVE)) + s.rows.append(self.RowStack_Class(x, y, self, + max_move=self.ROW_MAX_MOVE)) x = x + l.XS + # talon, waste x = self.width - l.XS y = self.height - l.YS - s.talon = WasteTalonStack(x, y, self, max_rounds=max_rounds, num_deal=num_deal) + s.talon = WasteTalonStack(x, y, self, + max_rounds=max_rounds, num_deal=num_deal) l.createText(s.talon, "n") if max_rounds > 1: tx, ty, ta, tf = l.getTextAttr(s.talon, "nn") @@ -345,6 +351,7 @@ class LittleForty(FortyThieves): # // Triple Line # // Big Streets # // Number Twelve +# // Roosevelt # // rows build down by alternate color # ************************************************************************/ @@ -352,9 +359,7 @@ class Streets(FortyThieves): RowStack_Class = AC_RowStack def shallHighlightMatch(self, stack1, card1, stack2, card2): - return (card1.color != card2.color and - (card1.rank + 1 == card2.rank or - card2.rank + 1 == card1.rank)) + return card1.color != card2.color and abs(card1.rank-card2.rank) == 1 class Maria(Streets): @@ -398,6 +403,12 @@ class NumberTwelve(NumberTen): FortyThieves.createGame(self, rows=12, XCARDS=96) +class Roosevelt(Streets): + DEAL = (0, 4) + def createGame(self): + Streets.createGame(self, rows=7) + + # /*********************************************************************** # // Red and Black # // Zebra @@ -439,15 +450,8 @@ class Zebra(RedAndBlack): # // rows build down by any suit but own # ************************************************************************/ -class Indian_RowStack(SequenceRowStack): - def _isSequence(self, cards): - return isAnySuitButOwnSequence(cards, self.cap.mod, self.cap.dir) - def getHelp(self): - return _('Tableau. Build down in any suit but the same.') - - class Indian(FortyThieves): - RowStack_Class = Indian_RowStack + RowStack_Class = BO_RowStack DEAL = (1, 2) def createGame(self): @@ -1015,9 +1019,9 @@ class FortyNine(Interchange): # // Indian Patience # ************************************************************************/ -class IndianPatience_RowStack(Indian_RowStack): +class IndianPatience_RowStack(BO_RowStack): def acceptsCards(self, from_stack, cards): - if not Indian_RowStack.acceptsCards(self, from_stack, cards): + if not BO_RowStack.acceptsCards(self, from_stack, cards): return False if not self.game.s.talon.cards: return True @@ -1150,4 +1154,6 @@ registerGame(GameInfo(577, FortyNine, "Forty Nine", GI.GT_FORTY_THIEVES, 2, 0, GI.SL_MOSTLY_SKILL)) registerGame(GameInfo(578, IndianPatience, "Indian Patience", GI.GT_FORTY_THIEVES, 2, 0, GI.SL_MOSTLY_SKILL)) +registerGame(GameInfo(588, Roosevelt, "Roosevelt", + GI.GT_FORTY_THIEVES, 2, 0, GI.SL_MOSTLY_SKILL)) diff --git a/pysollib/games/glenwood.py b/pysollib/games/glenwood.py index 9fad3717..2564fbeb 100644 --- a/pysollib/games/glenwood.py +++ b/pysollib/games/glenwood.py @@ -48,9 +48,9 @@ class Glenwood_Talon(WasteTalonStack): class Glenwood_Foundation(AbstractFoundationStack): def acceptsCards(self, from_stack, cards): if not AbstractFoundationStack.acceptsCards(self, from_stack, cards): - return 0 + return False if self.game.base_rank is None: - return 1 + return True if not self.cards: return cards[-1].rank == self.game.base_rank # check the rank @@ -68,7 +68,7 @@ class Glenwood_RowStack(AC_RowStack): def acceptsCards(self, from_stack, cards): if not AC_RowStack.acceptsCards(self, from_stack, cards): - return 0 + return False if not self.cards and from_stack is self.game.s.waste: for stack in self.game.s.reserves: if stack.cards: @@ -94,7 +94,7 @@ class Glenwood(Game): Foundation_Class = Glenwood_Foundation RowStack_Class = Glenwood_RowStack - ReserveStack_Class = Glenwood_ReserveStack #OpenStack + ReserveStack_Class = Glenwood_ReserveStack Hint_Class = Canfield_Hint base_rank = None @@ -181,8 +181,159 @@ class Glenwood(Game): return [self.base_rank] +# /*********************************************************************** +# // Double Fives +# ************************************************************************/ + +class DoubleFives_Talon(RedealTalonStack): + + def moveToStock(self): + stock = self.game.s.stock + for r in self.game.s.reserves[:5]: + if r.cards: + r.moveMove(1, stock) + stock.flipMove() + + def canDealCards(self): + if self.game.base_rank is None: + return False + if self.round == self.max_rounds: + return len(self.cards) != 0 + return not self.game.isGameWon() + + def dealCards(self, sound=0): + old_state = self.game.enterState(self.game.S_DEAL) + num_cards = 0 + if self.round == 1: + if sound: + self.game.startDealSample() + self.moveToStock() + if not self.cards: + num_cards += self.redealCards(rows=[self.game.s.stock], + frames=4, sound=0) + else: + num_cards += self.dealRowAvail(rows=self.game.s.reserves[:5], + sound=0) + if sound: + self.game.stopSamples() + else: + if sound and not self.game.demo: + self.game.playSample("dealwaste") + self.game.flipMove(self) + self.game.moveMove(1, self, self.game.s.reserves[0], + frames=4, shadow=0) + num_cards += 1 + self.game.leaveState(old_state) + return num_cards + + +class DoubleFives_RowStack(SS_RowStack): + def canMoveCards(self, cards): + if self.game.base_rank is None: + return False + if not SS_RowStack.canMoveCards(self, cards): + return False + return True + + +class DoubleFives_WasteStack(WasteStack): + def updateText(self): + if self.game.s.talon.round == 2: + WasteStack.updateText(self) + elif self.texts.ncards: + self.texts.ncards.config(text='') + + +class DoubleFives_Stock(WasteStack): + def canFlipCard(self): + return False + def updateText(self): + if self.cards: + WasteStack.updateText(self) + else: + self.texts.ncards.config(text='') + + +class DoubleFives(Glenwood): + Hint_Class = CautiousDefaultHint + + def createGame(self): + # create layout + l, s = Layout(self), self.s + + # set window + self.setSize(l.XM+11*l.XS, l.YM+3*l.YS+16*l.YOFFSET) + + # create stacks + # + x, y = l.XM, self.height-l.YS + s.talon = DoubleFives_Talon(x, y, self, max_rounds=2, num_deal=1) + l.createText(s.talon, "n") + x += l.XS + for i in range(5): + s.reserves.append(DoubleFives_WasteStack(x, y, self)) + x += l.XS + l.createText(s.reserves[0], 'n') + # + x = self.width-l.XS + s.addattr(stock=None) # register extra stack variable + s.stock = DoubleFives_Stock(x, y, self) + l.createText(s.stock, "n") + # + x, y = l.XM, l.YM + s.reserves.append(Glenwood_ReserveStack(x, y, self)) + x += l.XS + s.reserves.append(Glenwood_ReserveStack(x, y, self)) + # + x += 2*l.XS + for i in range(8): + s.foundations.append(Glenwood_Foundation(x, y, self, suit=i/2, + mod=13, base_rank=ANY_RANK, max_move=0)) + x += l.XS + tx, ty, ta, tf = l.getTextAttr(None, "ss") + tx, ty = x - l.XS + tx, y + ty + font = self.app.getFont("canvas_default") + self.texts.info = MfxCanvasText(self.canvas, tx, ty, + anchor=ta, font=font) + x, y = l.XM+l.XS/2, l.YM+l.YS+l.TEXT_HEIGHT + for i in range(10): + s.rows.append(DoubleFives_RowStack(x, y, self, mod=13, max_move=1)) + x += l.XS + + # define stack-groups + l.defaultStackGroups() + + def _shuffleHook(self, cards): + return self._shuffleHookMoveToTop(cards, + lambda c: (c.deck == 0, None)) + + def startGame(self): + self.base_rank = None + for i in range(4): + self.s.talon.dealRow(frames=0) + self.startDealSample() + self.s.talon.dealRow() + self.s.talon.dealRow(rows=self.s.reserves[-2:]) + + def _autoDeal(self, sound=1): + waste_cards = 0 + for r in self.s.reserves[:5]: + waste_cards += len(r.cards) + if waste_cards == 0 and self.canDealCards(): + return self.dealCards(sound=sound) + return 0 + + def shallHighlightMatch(self, stack1, card1, stack2, card2): + return (card1.suit == card2.suit + and ((card1.rank + 1) % 13 == card2.rank + or (card2.rank + 1) % 13 == card1.rank)) + + + # register the game registerGame(GameInfo(282, Glenwood, "Glenwood", GI.GT_CANFIELD, 1, 1, GI.SL_BALANCED, altnames=("Duchess",) )) +registerGame(GameInfo(587, DoubleFives, "Double Fives", + GI.GT_2DECK_TYPE, 2, 1, GI.SL_BALANCED)) diff --git a/pysollib/games/gypsy.py b/pysollib/games/gypsy.py index ffd27a02..5e0bf563 100644 --- a/pysollib/games/gypsy.py +++ b/pysollib/games/gypsy.py @@ -615,6 +615,104 @@ class RightTriangle(Hypotenuse): self.sg.reservestacks.append(self.s.talon) +# /*********************************************************************** +# // Trapdoor +# ************************************************************************/ + +class Trapdoor_Talon(DealRowTalonStack): + def dealCards(self, sound=0): + if not self.cards: + return 0 + if sound: + self.game.startDealSample() + n = 0 + rows = self.game.s.rows + reserves = self.game.s.reserves + for i in range(8): + r1 = reserves[i] + r2 = rows[i] + if r1.cards: + r1.moveMove(1, r2) + n += 1 + n += self.dealRowAvail(rows=self.game.s.reserves, sound=0) + if sound: + self.game.stopSamples() + return n + + +class Trapdoor(Gypsy): + + def createGame(self): + kw = {'rows' : 8, + 'waste' : 0, + 'texts' : 1, + 'reserves' : 8,} + Layout(self).createGame(layout_method = Layout.gypsyLayout, + talon_class = Trapdoor_Talon, + foundation_class = SS_FoundationStack, + row_class = AC_RowStack, + reserve_class = OpenStack, + **kw + ) + + def startGame(self): + Gypsy.startGame(self) + self.s.talon.dealCards() + +# /*********************************************************************** +# // Flamenco +# ************************************************************************/ + +class Flamenco(Gypsy): + + def createGame(self): + kw = {'rows' : 8, + 'waste' : 0, + 'texts' : 1,} + foundation_class = ( + SS_FoundationStack, + StackWrapper(SS_FoundationStack, base_rank=KING, dir=-1)) + Layout(self).createGame(layout_method = Layout.gypsyLayout, + talon_class = DealRowTalonStack, + foundation_class = foundation_class, + row_class = AC_RowStack, + **kw + ) + + def _shuffleHook(self, cards): + return self._shuffleHookMoveToTop(cards, + lambda c: (c.rank in (ACE, KING) and c.deck == 0, (c.suit,c.rank))) + + def startGame(self): + self.s.talon.dealRow(rows=self.s.foundations, frames=0) + for i in range(2): + self.s.talon.dealRow(frames=0) + self.startDealSample() + self.s.talon.dealRow() + + +# /*********************************************************************** +# // Eclipse +# ************************************************************************/ + +class Eclipse(Gypsy): + Layout_Method = Layout.klondikeLayout + RowStack_Class = SS_RowStack + + def createGame(self): + Gypsy.createGame(self, rows=13) + + def startGame(self): + self.s.talon.dealRow(frames=0) + self.s.talon.dealRow(frames=0) + self.s.talon.dealRow(frames=0) + self.startDealSample() + self.s.talon.dealRow() + + def shallHighlightMatch(self, stack1, card1, stack2, card2): + return (card1.suit == card2.suit and abs(card1.rank-card2.rank) == 1) + + # register the game registerGame(GameInfo(1, Gypsy, "Gypsy", GI.GT_GYPSY, 2, 0, GI.SL_MOSTLY_SKILL)) @@ -669,4 +767,10 @@ registerGame(GameInfo(567, EternalTriangle, "Eternal Triangle", altnames=('Lobachevsky',) )) registerGame(GameInfo(568, RightTriangle, "Right Triangle", GI.GT_GYPSY, 2, 0, GI.SL_MOSTLY_SKILL)) +registerGame(GameInfo(580, Trapdoor, "Trapdoor", + GI.GT_GYPSY | GI.GT_ORIGINAL, 2, 0, GI.SL_MOSTLY_SKILL)) +registerGame(GameInfo(581, Flamenco, "Flamenco", + GI.GT_GYPSY | GI.GT_ORIGINAL, 2, 0, GI.SL_MOSTLY_SKILL)) +registerGame(GameInfo(584, Eclipse, "Eclipse", + GI.GT_GYPSY, 2, 0, GI.SL_MOSTLY_SKILL)) diff --git a/pysollib/games/harp.py b/pysollib/games/harp.py index de6bf679..e0d244db 100644 --- a/pysollib/games/harp.py +++ b/pysollib/games/harp.py @@ -152,6 +152,8 @@ class Steps(DoubleKlondike): # /*********************************************************************** # // Triple Klondike +# // Triple Klondike by Threes +# // Chinese Klondike # ************************************************************************/ class TripleKlondike(DoubleKlondike): @@ -159,15 +161,17 @@ class TripleKlondike(DoubleKlondike): DoubleKlondike.createGame(self, rows=13) -# /*********************************************************************** -# // Triple Klondike by Threes -# ************************************************************************/ - class TripleKlondikeByThrees(DoubleKlondike): def createGame(self): DoubleKlondike.createGame(self, rows=13, num_deal=3) +class ChineseKlondike(DoubleKlondike): + RowStack_Class = StackWrapper(BO_RowStack, base_rank=KING) + def createGame(self): + DoubleKlondike.createGame(self, rows=12) + + # /*********************************************************************** # // Lady Jane # // Inquisitor @@ -304,4 +308,8 @@ 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)) +registerGame(GameInfo(590, ChineseKlondike, "Chinese Klondike", + GI.GT_KLONDIKE, 3, -1, GI.SL_BALANCED, + suits=(0, 1, 2) )) + diff --git a/pysollib/games/katzenschwanz.py b/pysollib/games/katzenschwanz.py index 603360ca..5e1227b8 100644 --- a/pysollib/games/katzenschwanz.py +++ b/pysollib/games/katzenschwanz.py @@ -42,13 +42,26 @@ from pysollib.game import Game from pysollib.layout import Layout from pysollib.hint import DefaultHint, FreeCellType_Hint, CautiousDefaultHint + +# /*********************************************************************** +# // +# ************************************************************************/ + +class DerKatzenschwanz_Hint(FreeCellType_Hint): + def _getMovePileScore(self, score, color, r, t, pile, rpile): + if len(rpile) == 0: + # don't create empty row + return -1, color + return FreeCellType_Hint._getMovePileScore(self, score, color, r, t, pile, rpile) + + # /*********************************************************************** # // # ************************************************************************/ class DerKatzenschwanz(Game): RowStack_Class = StackWrapper(AC_RowStack, base_rank=NO_RANK) - Hint_Class = FreeCellType_Hint + Hint_Class = DerKatzenschwanz_Hint # # game layout @@ -349,11 +362,6 @@ class LaggardLady_RowStack(OpenStack): return False return len(self.game.s.talon.cards) == 0 and len(self.cards) == 1 - def canMoveCards(self, cards): - if not OpenStack.canMoveCards(self, cards): - return False - return len(self.cards) > 1 - class LaggardLady(SalicLaw): @@ -361,7 +369,7 @@ class LaggardLady(SalicLaw): StackWrapper(RK_FoundationStack, base_rank=5, max_cards=6), StackWrapper(RK_FoundationStack, base_rank=4, max_cards=6, dir=-1, mod=13), ] - RowStack_Class = StackWrapper(LaggardLady_RowStack, max_accept=1) + RowStack_Class = StackWrapper(LaggardLady_RowStack, max_accept=1, min_cards=1) ROW_BASE_RANK = QUEEN diff --git a/pysollib/games/klondike.py b/pysollib/games/klondike.py index 80d30da6..875520bc 100644 --- a/pysollib/games/klondike.py +++ b/pysollib/games/klondike.py @@ -143,15 +143,8 @@ class KlondikeByThrees(Klondike): # // Thumb and Pouch # ************************************************************************/ -class ThumbAndPouch_RowStack(SequenceRowStack): - def _isSequence(self, cards): - return isAnySuitButOwnSequence(cards, self.cap.mod, self.cap.dir) - def getHelp(self): - return _('Tableau. Build down in any suit but the same.') - - class ThumbAndPouch(Klondike): - RowStack_Class = ThumbAndPouch_RowStack + RowStack_Class = BO_RowStack def createGame(self): Klondike.createGame(self, max_rounds=1) @@ -306,7 +299,7 @@ class Somerset(Klondike): class Morehead(Somerset): - RowStack_Class = StackWrapper(ThumbAndPouch_RowStack, max_move=1) + RowStack_Class = StackWrapper(BO_RowStack, max_move=1) class Canister(Klondike): @@ -514,19 +507,6 @@ class KingAlbert(Klondike): self.s.talon.dealRow(rows=self.s.reserves) -## class KingAlbertNew(KingAlbert): - -## def createGame(self): -## l = Klondike.createGame(self, max_rounds=1, rows=self.ROWS, waste=0, texts=0) -## self.setSize(self.width+l.XM+l.XS, self.height) -## self.s.reserves.append(ArbitraryStack(self.width-l.XS, l.YM, self)) -## l.defaultStackGroups() - -## def startGame(self): -## Klondike.startGame(self, flip=1, reverse=0) -## self.s.talon.dealRow(rows=self.s.reserves*7) - - class Raglan(KingAlbert): RESERVES = (2, 2, 2) @@ -1144,6 +1124,34 @@ class GoldMine(Klondike): self.s.talon.dealCards() +# /*********************************************************************** +# // Lucky Thirteen +# // Lucky Piles +# ************************************************************************/ + +class LuckyThirteen(Klondike): + Talon_Class = InitialDealTalonStack + RowStack_Class = StackWrapper(SS_RowStack, base_rank=NO_RANK, max_move=1) + + def createGame(self): + Klondike.createGame(self, waste=False, rows=13, max_rounds=1, texts=False) + + def startGame(self): + self.s.talon.dealRow(frames=0) + self.s.talon.dealRow(frames=0) + self.s.talon.dealRow(frames=0) + self.startDealSample() + self.s.talon.dealRow() + + def shallHighlightMatch(self, stack1, card1, stack2, card2): + return card1.suit == card2.suit and abs(card1.rank-card2.rank) == 1 + + +class LuckyPiles(LuckyThirteen): + RowStack_Class = StackWrapper(UD_SS_RowStack, base_rank=KING) + + + # register the game registerGame(GameInfo(2, Klondike, "Klondike", GI.GT_KLONDIKE, 1, -1, GI.SL_BALANCED)) @@ -1261,4 +1269,8 @@ registerGame(GameInfo(541, BatsfordAgain, "Batsford Again", GI.GT_KLONDIKE, 2, 1, GI.SL_BALANCED)) registerGame(GameInfo(572, GoldMine, "Gold Mine", GI.GT_NUMERICA, 1, 0, GI.SL_MOSTLY_SKILL)) +registerGame(GameInfo(585, LuckyThirteen, "Lucky Thirteen", + GI.GT_NUMERICA, 1, 0, GI.SL_MOSTLY_LUCK)) +registerGame(GameInfo(586, LuckyPiles, "Lucky Piles", + GI.GT_NUMERICA, 1, 0, GI.SL_MOSTLY_SKILL)) diff --git a/pysollib/games/numerica.py b/pysollib/games/numerica.py index e7ddfad6..393fa323 100644 --- a/pysollib/games/numerica.py +++ b/pysollib/games/numerica.py @@ -103,7 +103,7 @@ class Numerica(Game): # game layout # - def createGame(self, rows=4): + def createGame(self, rows=4, reserve=False): # create layout l, s = Layout(self), self.s decks = self.gameinfo.decks @@ -130,16 +130,20 @@ class Numerica(Game): s.rows.append(self.RowStack_Class(x, y, self)) x = x + l.XS self.setRegion(s.rows, (x0-l.XS/2, y-l.CH/2, 999999, 999999)) - x = l.XM + x, y = l.XM, l.YM+l.YS+l.YS/2*int(reserve) s.talon = WasteTalonStack(x, y, self, max_rounds=1) - l.createText(s.talon, 'n') + if reserve: + l.createText(s.talon, 'ne') + else: + l.createText(s.talon, 'n') y = y + l.YS s.waste = WasteStack(x, y, self, max_cards=1) + if reserve: + s.reserves.append(self.ReserveStack_Class(l.XM, l.YM, self)) # define stack-groups - self.sg.openstacks = s.foundations + s.rows - self.sg.talonstacks = [s.talon] + [s.waste] - self.sg.dropstacks = s.rows + [s.waste] + l.defaultStackGroups() + # # game overrides @@ -164,15 +168,45 @@ class Numerica2Decks(Numerica): # /*********************************************************************** # // Lady Betty +# // Last Chance # ************************************************************************/ class LadyBetty(Numerica): Foundation_Class = SS_FoundationStack - def createGame(self): Numerica.createGame(self, rows=6) +class LastChance_RowStack(Numerica_RowStack): + def acceptsCards(self, from_stack, cards): + if not BasicRowStack.acceptsCards(self, from_stack, cards): + return False + if not self.cards: + return True + return from_stack is self.game.s.waste and len(cards) == 1 + + +class LastChance_Reserve(OpenStack): + def canFlipCard(self): + return (len(self.game.s.talon.cards) == 0 and + len(self.game.s.waste.cards) == 0 and + self.cards and not self.cards[0].face_up) + + +class LastChance(LadyBetty): + RowStack_Class = StackWrapper(LastChance_RowStack, max_accept=1) + ReserveStack_Class = LastChance_Reserve + + def createGame(self): + Numerica.createGame(self, rows=7, reserve=True) + + def startGame(self): + self.startDealSample() + self.s.talon.dealRow() + self.s.talon.dealRow(rows=self.s.reserves, flip=False) + self.s.talon.dealCards() + + # /*********************************************************************** # // Puss in the Corner # ************************************************************************/ @@ -653,5 +687,6 @@ registerGame(GameInfo(472, Strategerie, "Strategerie", GI.GT_NUMERICA, 1, 0, GI.SL_MOSTLY_SKILL)) registerGame(GameInfo(558, Numerica2Decks, "Numerica (2 decks)", GI.GT_NUMERICA, 2, 0, GI.SL_BALANCED)) - +registerGame(GameInfo(589, LastChance, "Last Chance", + GI.GT_NUMERICA, 1, 0, GI.SL_BALANCED)) diff --git a/pysollib/games/pyramid.py b/pysollib/games/pyramid.py index ee0c4a39..aad9d9af 100644 --- a/pysollib/games/pyramid.py +++ b/pysollib/games/pyramid.py @@ -168,17 +168,23 @@ class Pyramid_RowStack(Pyramid_StackMethods, OpenStack): class Pyramid(Game): Hint_Class = Pyramid_Hint + Talon_Class = StackWrapper(Pyramid_Talon, max_rounds=3, max_accept=1) # # game layout # - def createGame(self, rows=4): + def createGame(self, rows=4, reserves=0, waste=True, texts=True): # create layout l, s = Layout(self), self.s # set window - self.setSize(l.XM + 9*l.XS, l.YM + 4*l.YS) + max_rows = max(9, reserves) + w = l.XM + max_rows*l.XS + h = l.YM + 4*l.YS + if reserves: + h += l.YS+4*l.YOFFSET + self.setSize(w, h) # create stacks for i in range(7): @@ -189,24 +195,33 @@ class Pyramid(Game): x = x + l.XS x, y = l.XM, l.YM - s.talon = Pyramid_Talon(x, y, self, max_rounds=3, max_accept=1) - 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")) - y = y + l.YS - s.waste = Pyramid_Waste(x, y, self, max_accept=1) - l.createText(s.waste, "se") + s.talon = self.Talon_Class(x, y, self) + if texts: + l.createText(s.talon, "se") + tx, ty, ta, tf = l.getTextAttr(s.talon, "ne") + font=self.app.getFont("canvas_default") + s.talon.texts.rounds = MfxCanvasText(self.canvas, tx, ty, + anchor=ta, font=font) + if waste: + y = y + l.YS + s.waste = Pyramid_Waste(x, y, self, max_accept=1) + l.createText(s.waste, "se") x, y = self.width - l.XS, l.YM s.foundations.append(Pyramid_Foundation(x, y, self, suit=ANY_SUIT, dir=0, base_rank=ANY_RANK, max_move=0, max_cards=52)) + if reserves: + x, y = l.XM+(max_rows-reserves)*l.XS/2, l.YM+4*l.YS + for i in range(reserves): + stack = self.Reserve_Class(x, y, self) + s.reserves.append(stack) + stack.CARD_YOFFSET = l.YOFFSET + x += l.XS # define stack-groups - self.sg.talonstacks = [s.talon] + [s.waste] - self.sg.openstacks = s.rows + self.sg.talonstacks - self.sg.dropstacks = s.rows + self.sg.talonstacks + l.defaultStackGroups() + self.sg.openstacks.append(s.talon) + self.sg.dropstacks.append(s.talon) # @@ -235,6 +250,31 @@ class RelaxedPyramid(Pyramid): return getNumberOfFreeStacks(self.s.rows) == len(self.s.rows) +# /*********************************************************************** +# // Giza +# ************************************************************************/ + +class Giza_Reserve(Pyramid_StackMethods, OpenStack): + def clickHandler(self, event): + if self._dropKingClickHandler(event): + return 1 + return OpenStack.clickHandler(self, event) + + +class Giza(Pyramid): + Talon_Class = InitialDealTalonStack + Reserve_Class = StackWrapper(Giza_Reserve, max_accept=1) + + def createGame(self): + Pyramid.createGame(self, reserves=8, waste=False, texts=False) + + def startGame(self): + for i in range(3): + self.s.talon.dealRow(rows=self.s.reserves, frames=0) + self.startDealSample() + self.s.talon.dealRow() + + # /*********************************************************************** # // Thirteen # // FIXME: UNFINISHED @@ -298,4 +338,8 @@ registerGame(GameInfo(193, RelaxedPyramid, "Relaxed Pyramid", GI.GT_PAIRING_TYPE | GI.GT_RELAXED, 1, 2, GI.SL_MOSTLY_LUCK)) ##registerGame(GameInfo(44, Thirteen, "Thirteen", ## GI.GT_PAIRING_TYPE, 1, 0)) +registerGame(GameInfo(591, Giza, "Giza", + GI.GT_PAIRING_TYPE | GI.GT_OPEN, 1, 0, GI.SL_BALANCED)) + + diff --git a/pysollib/games/spider.py b/pysollib/games/spider.py index 0ec48a5c..1afc39a6 100644 --- a/pysollib/games/spider.py +++ b/pysollib/games/spider.py @@ -1166,7 +1166,8 @@ registerGame(GameInfo(459, FredsSpider, "Fred's Spider", registerGame(GameInfo(460, FredsSpider3Decks, "Fred's Spider (3 decks)", GI.GT_SPIDER, 3, 0, GI.SL_MOSTLY_SKILL)) registerGame(GameInfo(461, OpenSpider, "Open Spider", - GI.GT_SPIDER, 2, 0, GI.SL_MOSTLY_SKILL)) + GI.GT_SPIDER, 2, 0, GI.SL_MOSTLY_SKILL, + altnames=('Beetle',) )) registerGame(GameInfo(501, WakeRobin, "Wake-Robin", GI.GT_SPIDER | GI.GT_ORIGINAL, 2, 0, GI.SL_MOSTLY_SKILL)) registerGame(GameInfo(502, TripleWakeRobin, "Wake-Robin (3 decks)", diff --git a/pysollib/games/terrace.py b/pysollib/games/terrace.py index 94fa8cc5..de2f579e 100644 --- a/pysollib/games/terrace.py +++ b/pysollib/games/terrace.py @@ -239,6 +239,8 @@ class GeneralsPatience(Terrace): # /*********************************************************************** # // Blondes and Brunettes +# // Falling Star +# // Wood # ************************************************************************/ class BlondesAndBrunettes(Terrace): @@ -260,14 +262,24 @@ class BlondesAndBrunettes(Terrace): return 1 -# /*********************************************************************** -# // Falling Star -# ************************************************************************/ - class FallingStar(BlondesAndBrunettes): INITIAL_RESERVE_CARDS = 11 +class Wood_RowStack(AC_RowStack): + def acceptsCards(self, from_stack, cards): + if not AC_RowStack.acceptsCards(self, from_stack, cards): + return False + if not self.cards: + return from_stack is self.game.s.waste + return from_stack not in self.game.s.reserves + +class Wood(BlondesAndBrunettes): + RowStack_Class = StackWrapper(Wood_RowStack, mod=13, max_move=1) + def fillStack(self, stack): + pass + + # /*********************************************************************** # // Signora # ************************************************************************/ @@ -347,4 +359,6 @@ registerGame(GameInfo(500, Madame, "Madame", GI.GT_TERRACE, 3, 0, GI.SL_MOSTLY_SKILL)) registerGame(GameInfo(533, MamySusan, "Mamy Susan", GI.GT_TERRACE, 2, 0, GI.SL_BALANCED)) +registerGame(GameInfo(582, Wood, "Wood", + GI.GT_TERRACE, 2, 0, GI.SL_BALANCED)) diff --git a/pysollib/layout.py b/pysollib/layout.py index 119989d4..6e3eb23c 100644 --- a/pysollib/layout.py +++ b/pysollib/layout.py @@ -144,6 +144,50 @@ class Layout: self.stackmap[mapkey] = stack return stack + # + # + # + + def createGame(self, layout_method, + talon_class=None, + waste_class=None, + foundation_class=None, + row_class=None, + reserve_class=None, + **kw + ): + # create layout + game = self.game + s = game.s + layout_method(self, **kw) + game.setSize(self.size[0], self.size[1]) + # create stacks + if talon_class: + s.talon = talon_class(self.s.talon.x, self.s.talon.y, game) + if waste_class: + s.waste = waste_class(self.s.waste.x, self.s.waste.y, game) + if foundation_class: + if type(foundation_class) in (list, tuple): + n = len(self.s.foundations)/len(foundation_class) + i = 0 + for j in range(n): + for cls in foundation_class: + r = self.s.foundations[i] + s.foundations.append(cls(r.x, r.y, game, suit=r.suit)) + i += 1 + + else: + for r in self.s.foundations: + s.foundations.append(foundation_class(r.x, r.y, game, + suit=r.suit)) + if row_class: + for r in self.s.rows: + s.rows.append(row_class(r.x, r.y, game)) + if reserve_class: + for r in self.s.reserves: + s.reserves.append(reserve_class(r.x, r.y, game)) + # default + self.defaultAll() # # public util for use by class Game @@ -342,9 +386,10 @@ class Layout: # Gypsy layout # - left: rows # - right: foundations, talon + # - bottom: reserves # - def gypsyLayout(self, rows, waste=0, texts=1, playcards=25): + def gypsyLayout(self, rows, waste=0, reserves=0, texts=1, playcards=25): S = self.__createStack CW, CH = self.CW, self.CH XM, YM = self.XM, self.YM @@ -353,8 +398,11 @@ class Layout: decks = self.game.gameinfo.decks suits = len(self.game.gameinfo.suits) + bool(self.game.gameinfo.trumps) - # set size so that at least 2/3 of a card is visible with 25 cards - h = CH*2/3 + (playcards-1)*self.YOFFSET + if reserves: + h = YS+(playcards-1)*self.YOFFSET+YS + else: + # set size so that at least 2/3 of a card is visible with 25 cards + h = CH*2/3 + (playcards-1)*self.YOFFSET h = YM + max(h, (suits+1)*YS) # create rows @@ -384,9 +432,14 @@ class Layout: if texts: # place text left of stack s.setText(x - self.TEXT_MARGIN, y + CH, anchor="se", format="%3d") + # create reserves + x, y = XM, h-YS + for i in range(reserves): + self.s.reserves.append(S(x, y)) + x += XS # set window - self.size = (XM + (rows+decks)*XS, h) + self.size = (XM + (max(rows, reserves)+decks)*XS, h) # diff --git a/pysollib/stack.py b/pysollib/stack.py index ebc599d9..5f6a223b 100644 --- a/pysollib/stack.py +++ b/pysollib/stack.py @@ -62,6 +62,7 @@ __all__ = ['cardsFaceUp', 'SC_RowStack', 'SS_RowStack', 'RK_RowStack', + 'BO_RowStack', 'UD_AC_RowStack', 'UD_SC_RowStack', 'UD_SS_RowStack', @@ -1267,11 +1268,11 @@ class Stack: elif br == 11: s = s % _('Queen') elif br == 12: s = s % _('King') elif br == 0 : s = s % _('Ace') - else : s = s % str(br) + else : s = s % str(br+1) return s def getNumCards(self): - if self.game.app.debug >= 3: + if self.game.app.debug >= 5: t = repr(self)+' ' else: t = '' @@ -1939,6 +1940,17 @@ class RK_RowStack(SequenceRowStack): elif self.cap.dir < 0: return _('Tableau. Build down regardless of suit.') else: return _('Tableau. Build by same rank.') + +# ButOwn_RowStack +class BO_RowStack(SequenceRowStack): + def _isSequence(self, cards): + return isAnySuitButOwnSequence(cards, self.cap.mod, self.cap.dir) + def getHelp(self): + if self.cap.dir > 0: return _('Tableau. Build up in any suit but the same.') + elif self.cap.dir < 0: return _('Tableau. Build down in any suit but the same.') + else: return _('Tableau. Build by same rank.') + + # A Freecell_AlternateColor_RowStack class FreeCell_AC_RowStack(AC_RowStack): def canMoveCards(self, cards): diff --git a/pysollib/tk/menubar.py b/pysollib/tk/menubar.py index 5e96ebca..34076b81 100644 --- a/pysollib/tk/menubar.py +++ b/pysollib/tk/menubar.py @@ -699,9 +699,15 @@ class PysolMenubar(PysolMenubarActions): submenu.delete(0, "last") # insert games g = [self.app.getGameInfo(id) for id in gameids] - self._addSelectGameSubSubMenu(submenu, g, - command=self.mSelectGame, - variable=self.tkopt.gameid) + if len(g) > self.__cb_max*4: + g.sort(lambda a, b: cmp(gettext(a.name), gettext(b.name))) + self._addSelectAllGameSubMenu(submenu, g, + command=self.mSelectGame, + variable=self.tkopt.gameid) + else: + self._addSelectGameSubSubMenu(submenu, g, + command=self.mSelectGame, + variable=self.tkopt.gameid) state = self._getEnabledState in_favor = self.app.game.id in gameids menu, index, submenu = self.__menupath[".menubar.file.addtofavorites"]