mirror of
https://github.com/shlomif/PySolFC.git
synced 2025-04-05 00:02:29 -04:00
+ 4 new games
+ added `close stack' (Game.closeStackMove, Stack.closeStackMove, ACloseStackMove) + option `shade_filled_stacks' + new stack RedealTalonStack and new Game method redealCards * added closeStackMove to PileOn and PictureGallery (used flipAllMove) git-svn-id: https://pysolfc.svn.sourceforge.net/svnroot/pysolfc/PySolFC/trunk@21 39dd0a4e-7c14-0410-91b3-c4f2d318f732
This commit is contained in:
parent
e97a944692
commit
d71672694a
26 changed files with 366 additions and 80 deletions
|
@ -146,3 +146,10 @@ class AbstractCard:
|
|||
def updateCardBackground(self, image):
|
||||
raise SubclassResponsibility
|
||||
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
def unclose(self):
|
||||
pass
|
||||
|
||||
|
|
|
@ -122,6 +122,7 @@ class PysolMenubarActions:
|
|||
animations = IntVar(),
|
||||
shadow = BooleanVar(),
|
||||
shade = BooleanVar(),
|
||||
shade_filled_stacks = BooleanVar(),
|
||||
toolbar = IntVar(),
|
||||
toolbar_style = StringVar(),
|
||||
toolbar_relief = StringVar(),
|
||||
|
@ -164,6 +165,7 @@ class PysolMenubarActions:
|
|||
tkopt.highlight_cards.set(opt.highlight_cards)
|
||||
tkopt.highlight_samerank.set(opt.highlight_samerank)
|
||||
tkopt.highlight_not_matching.set(opt.highlight_not_matching)
|
||||
tkopt.shade_filled_stacks.set(opt.shade_filled_stacks)
|
||||
tkopt.mahjongg_show_removed.set(opt.mahjongg_show_removed)
|
||||
tkopt.shisen_show_hint.set(opt.shisen_show_hint)
|
||||
tkopt.sound.set(opt.sound)
|
||||
|
@ -853,6 +855,12 @@ class PysolMenubarActions:
|
|||
self.app.opt.highlight_not_matching = self.tkopt.highlight_not_matching.get()
|
||||
##self.game.updateMenus()
|
||||
|
||||
def mOptShadeFilledStacks(self, *args):
|
||||
if self._cancelDrag(break_pause=False): return
|
||||
self.app.opt.shade_filled_stacks = self.tkopt.shade_filled_stacks.get()
|
||||
self.game.endGame(bookmark=1)
|
||||
self.game.quitGame(bookmark=1)
|
||||
|
||||
def mOptMahjonggShowRemoved(self, *args):
|
||||
if self._cancelDrag(): return
|
||||
self.app.opt.mahjongg_show_removed = self.tkopt.mahjongg_show_removed.get()
|
||||
|
|
|
@ -102,6 +102,7 @@ class Options:
|
|||
self.animations = 2 # default to Timer based
|
||||
self.shadow = 1
|
||||
self.shade = 1
|
||||
self.shade_filled_stacks = True
|
||||
self.demo_logo = 1
|
||||
self.demo_score = 0
|
||||
self.toolbar = 1
|
||||
|
|
|
@ -62,6 +62,7 @@ from pysoltk import Card
|
|||
from move import AMoveMove, AFlipMove, ATurnStackMove
|
||||
from move import ANextRoundMove, ASaveSeedMove, AShuffleStackMove
|
||||
from move import AUpdateStackMove, AFlipAllMove, ASaveStateMove
|
||||
from move import ACloseStackMove
|
||||
from hint import DefaultHint
|
||||
from help import helpAbout
|
||||
|
||||
|
@ -177,6 +178,12 @@ class Game:
|
|||
if self.s.talon:
|
||||
assert hasattr(self.s.talon, "round")
|
||||
assert hasattr(self.s.talon, "max_rounds")
|
||||
if self.app.debug and self.s.foundations:
|
||||
ncards = 0
|
||||
for stack in self.s.foundations:
|
||||
ncards += stack.cap.max_cards
|
||||
if ncards != self.gameinfo.ncards:
|
||||
print 'WARNING: invalid sum of foundations.max_cards:', self.__class__.__name__, ncards, self.gameinfo.ncards
|
||||
# optimize regions
|
||||
self.optimizeRegions()
|
||||
# create cards
|
||||
|
@ -987,6 +994,8 @@ class Game:
|
|||
def getCardBackImage(self, deck, suit, rank):
|
||||
return self.app.images.getBack(deck, suit, rank)
|
||||
|
||||
def getCardShadeImage(self):
|
||||
return self.app.images.getShade()
|
||||
|
||||
#
|
||||
# layout support
|
||||
|
@ -1085,6 +1094,10 @@ class Game:
|
|||
def fillStack(self, stack):
|
||||
pass
|
||||
|
||||
# redeal cards (used in RedealTalonStack; all cards already in talon)
|
||||
def redealCards(self):
|
||||
pass
|
||||
|
||||
# the actual hint class (or None)
|
||||
Hint_Class = DefaultHint
|
||||
|
||||
|
@ -1931,7 +1944,7 @@ for %d moves.
|
|||
# move type 8
|
||||
def flipAllMove(self, stack):
|
||||
assert stack
|
||||
am = AFlipAllMove(self, stack)
|
||||
am = AFlipAllMove(stack)
|
||||
self.__storeMove(am)
|
||||
am.do(self)
|
||||
self.hints.list = None
|
||||
|
@ -1943,6 +1956,13 @@ for %d moves.
|
|||
am.do(self)
|
||||
##self.hints.list = None
|
||||
|
||||
# move type 10
|
||||
def closeStackMove(self, stack):
|
||||
assert stack
|
||||
am = ACloseStackMove(stack)
|
||||
self.__storeMove(am)
|
||||
am.do(self)
|
||||
|
||||
|
||||
# Finish the current move.
|
||||
def finishMove(self):
|
||||
|
@ -2244,7 +2264,10 @@ Please report this bug."""))
|
|||
if not game.canLoadGame(version_tuple, game_version):
|
||||
destruct(game)
|
||||
game = None
|
||||
assert game is not None, "Cannot load this game from version " + version + "\nas the game rules have changed\nin the current implementation."
|
||||
assert game is not None, '''\
|
||||
Cannot load this game from version %s
|
||||
as the game rules have changed
|
||||
in the current implementation.''' % version
|
||||
game.version = version
|
||||
game.version_tuple = version_tuple
|
||||
#
|
||||
|
|
|
@ -422,7 +422,7 @@ class Amazons(Game):
|
|||
l.createText(s.talon, "ss")
|
||||
x, y = l.XM+2*l.XS, l.YM
|
||||
for i in range(4):
|
||||
s.foundations.append(Amazons_Foundation(x, y, self, suit=i))
|
||||
s.foundations.append(Amazons_Foundation(x, y, self, suit=i, max_cards=7))
|
||||
x += l.XS
|
||||
x, y = l.XM+2*l.XS, l.YM+l.YS
|
||||
for i in range(4):
|
||||
|
|
|
@ -174,7 +174,7 @@ class Braid(Game):
|
|||
s.foundations.append(cl(x, y, self, suit=i))
|
||||
x += l.XS
|
||||
y = y + l.YS
|
||||
x = 8*l.XS+decks*l.XS/2
|
||||
x = 8*l.XS+decks*l.XS/2+l.XM/2
|
||||
self.texts.info = MfxCanvasText(self.canvas,
|
||||
x, y, anchor="n",
|
||||
font=self.app.getFont("canvas_default"))
|
||||
|
|
|
@ -241,7 +241,7 @@ class Arachnida(CurdsAndWhey):
|
|||
s.rows.append(stack)
|
||||
x += l.XS
|
||||
s.foundations.append(AbstractFoundationStack(x, y, self, suit=ANY_SUIT,
|
||||
max_accept=0))
|
||||
max_accept=0, max_cards=104))
|
||||
l.createText(s.foundations[0], "ss")
|
||||
|
||||
# define stack-groups
|
||||
|
|
|
@ -98,7 +98,7 @@ class DieBoeseSieben(Game):
|
|||
# create stacks
|
||||
for i in range(8):
|
||||
x, y, = l.XM + i*l.XS, l.YM
|
||||
s.foundations.append(DieRussische_Foundation(x, y, self, i/2, max_move=0))
|
||||
s.foundations.append(DieRussische_Foundation(x, y, self, i/2, max_move=0, max_cards=8))
|
||||
for i in range(rows):
|
||||
x, y, = l.XM + (2*i+8-rows)*l.XS/2, l.YM + l.YS
|
||||
s.rows.append(AC_RowStack(x, y, self))
|
||||
|
|
|
@ -768,6 +768,36 @@ class Squadron(FortyThieves):
|
|||
self.s.talon.dealCards() # deal first card to WasteStack
|
||||
|
||||
|
||||
# /***********************************************************************
|
||||
# // Waterloo
|
||||
# ************************************************************************/
|
||||
|
||||
class Waterloo(FortyThieves):
|
||||
|
||||
RowStack_Class = Spider_SS_RowStack
|
||||
|
||||
ROW_MAX_MOVE = UNLIMITED_MOVES
|
||||
DEAL = (0, 1)
|
||||
|
||||
def createGame(self):
|
||||
FortyThieves.createGame(self, rows=6)
|
||||
|
||||
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 == ACE, (c.deck, c.suit)))
|
||||
|
||||
def startGame(self):
|
||||
self.startDealSample()
|
||||
self.s.talon.dealRow(rows=self.s.foundations)
|
||||
self.s.talon.dealRow()
|
||||
self.s.talon.dealCards() # deal first card to WasteStack
|
||||
|
||||
def getQuickPlayScore(self, ncards, from_stack, to_stack):
|
||||
if to_stack.cards:
|
||||
return int(from_stack.cards[-1].suit == to_stack.cards[-1].suit)+1
|
||||
return 0
|
||||
|
||||
|
||||
# register the game
|
||||
registerGame(GameInfo(13, FortyThieves, "Forty Thieves",
|
||||
|
@ -855,5 +885,7 @@ registerGame(GameInfo(528, FinalBattle, "Final Battle",
|
|||
GI.GT_FORTY_THIEVES, 2, 0, GI.SL_BALANCED))
|
||||
registerGame(GameInfo(529, SanJuanHill, "San Juan Hill",
|
||||
GI.GT_FORTY_THIEVES, 2, 0, GI.SL_BALANCED))
|
||||
registerGame(GameInfo(540, Waterloo, "Waterloo",
|
||||
GI.GT_FORTY_THIEVES, 2, 0, GI.SL_BALANCED))
|
||||
|
||||
|
||||
|
|
|
@ -543,6 +543,19 @@ class OceanTowers(TripleFreecell):
|
|||
return card1.suit == card2.suit and abs(card1.rank-card2.rank) == 1
|
||||
|
||||
|
||||
# /***********************************************************************
|
||||
# // KingCell
|
||||
# ************************************************************************/
|
||||
|
||||
class KingCell_RowStack(RK_RowStack):
|
||||
def canMoveCards(self, cards):
|
||||
max_move = getNumberOfFreeStacks(self.game.s.reserves) + 1
|
||||
return len(cards) <= max_move and RK_RowStack.canMoveCards(self, cards)
|
||||
|
||||
class KingCell(FreeCell):
|
||||
Hint_Class = FreeCellType_Hint
|
||||
RowStack_Class = StackWrapper(KingCell_RowStack, base_rank=KING)
|
||||
|
||||
|
||||
# register the game
|
||||
registerGame(GameInfo(5, RelaxedFreeCell, "Relaxed FreeCell",
|
||||
|
@ -585,4 +598,6 @@ registerGame(GameInfo(513, OceanTowers, "Ocean Towers",
|
|||
GI.GT_FREECELL | GI.GT_OPEN | GI.GT_ORIGINAL, 2, 0, GI.SL_MOSTLY_SKILL))
|
||||
registerGame(GameInfo(520, GermanFreeCell, "German FreeCell",
|
||||
GI.GT_FREECELL | GI.GT_OPEN, 1, 0, GI.SL_SKILL))
|
||||
registerGame(GameInfo(542, KingCell, "KingCell",
|
||||
GI.GT_FREECELL | GI.GT_OPEN, 1, 0, GI.SL_MOSTLY_SKILL))
|
||||
|
||||
|
|
|
@ -188,7 +188,7 @@ class DieRussische_RowStack(AC_RowStack):
|
|||
|
||||
class DieRussische(Gypsy):
|
||||
Talon_Class = InitialDealTalonStack
|
||||
Foundation_Class = StackWrapper(DieRussische_Foundation, min_cards=1)
|
||||
Foundation_Class = StackWrapper(DieRussische_Foundation, min_cards=1, max_cards=8)
|
||||
RowStack_Class = DieRussische_RowStack
|
||||
|
||||
def createGame(self):
|
||||
|
|
|
@ -323,6 +323,21 @@ class Canister(Klondike):
|
|||
self.s.talon.dealRow(rows=self.s.rows[2:6])
|
||||
|
||||
|
||||
class Usk(Somerset):
|
||||
|
||||
Talon_Class = RedealTalonStack
|
||||
RowStack_Class = StackWrapper(AC_RowStack, base_rank=KING)
|
||||
|
||||
def createGame(self):
|
||||
Klondike.createGame(self, max_rounds=2, rows=10, waste=0, texts=0)
|
||||
|
||||
def redealCards(self):
|
||||
n = 0
|
||||
while self.s.talon.cards:
|
||||
self.s.talon.dealRowAvail(rows=self.s.rows[n:], frames=4)
|
||||
n += 1
|
||||
|
||||
|
||||
# /***********************************************************************
|
||||
# // Agnes Sorel
|
||||
# ************************************************************************/
|
||||
|
@ -376,6 +391,7 @@ class AchtmalAcht(EightTimesEight):
|
|||
|
||||
# /***********************************************************************
|
||||
# // Batsford
|
||||
# // Batsford Again
|
||||
# ************************************************************************/
|
||||
|
||||
class Batsford_ReserveStack(ReserveStack):
|
||||
|
@ -389,7 +405,8 @@ class Batsford_ReserveStack(ReserveStack):
|
|||
|
||||
class Batsford(Klondike):
|
||||
def createGame(self, **layout):
|
||||
l = Klondike.createGame(self, rows=10, max_rounds=1, playcards=22)
|
||||
kwdefault(layout, rows=10, max_rounds=1, playcards=22)
|
||||
l = apply(Klondike.createGame, (self,), layout)
|
||||
s = self.s
|
||||
x, y = l.XM, self.height - l.YS
|
||||
s.reserves.append(Batsford_ReserveStack(x, y, self, max_cards=3))
|
||||
|
@ -398,6 +415,11 @@ class Batsford(Klondike):
|
|||
l.defaultStackGroups()
|
||||
|
||||
|
||||
class BatsfordAgain(Batsford):
|
||||
def createGame(self):
|
||||
Batsford.createGame(self, max_rounds=2)
|
||||
|
||||
|
||||
# /***********************************************************************
|
||||
# // Jumbo
|
||||
# ************************************************************************/
|
||||
|
@ -1199,4 +1221,8 @@ registerGame(GameInfo(522, ArticGarden, "Artic Garden",
|
|||
GI.GT_RAGLAN, 1, 0, GI.SL_MOSTLY_SKILL))
|
||||
registerGame(GameInfo(532, GoldRush, "Gold Rush",
|
||||
GI.GT_KLONDIKE, 1, 2, GI.SL_BALANCED))
|
||||
registerGame(GameInfo(539, Usk, "Usk",
|
||||
GI.GT_KLONDIKE, 1, 1, GI.SL_BALANCED))
|
||||
registerGame(GameInfo(541, BatsfordAgain, "Batsford Again",
|
||||
GI.GT_KLONDIKE, 2, 1, GI.SL_BALANCED))
|
||||
|
||||
|
|
|
@ -135,7 +135,7 @@ class MonteCarlo(Game):
|
|||
dir=0, base_rank=NO_RANK))
|
||||
x, y = l.XM + 11*l.XS/2, l.YM
|
||||
s.foundations.append(self.Foundation_Class(x, y, self, suit=ANY_SUIT,
|
||||
max_move=0, max_cards=52, base_rank=ANY_RANK))
|
||||
max_move=0, max_cards=self.gameinfo.ncards, base_rank=ANY_RANK))
|
||||
l.createText(s.foundations[0], "ss")
|
||||
y = y + 2*l.YS
|
||||
s.talon = self.Talon_Class(x, y, self, max_rounds=1)
|
||||
|
@ -598,7 +598,7 @@ class TheWish(Game):
|
|||
|
||||
x, y = self.width - l.XS, self.height - l.YS
|
||||
s.foundations.append(AbstractFoundationStack(x, y, self, suit=ANY_SUIT,
|
||||
max_move=0, max_cards=52, max_accept=0, base_rank=ANY_RANK))
|
||||
max_move=0, max_cards=32, max_accept=0, base_rank=ANY_RANK))
|
||||
l.createText(s.foundations[0], "nn")
|
||||
|
||||
# define stack-groups
|
||||
|
|
|
@ -137,10 +137,18 @@ class PictureGallery_Foundation(RK_FoundationStack):
|
|||
def getBottomImage(self):
|
||||
return self.game.app.images.getLetter(ACE)
|
||||
|
||||
def closeStackMove(self):
|
||||
if len(self.cards) == 8:
|
||||
self.game.flipAllMove(self)
|
||||
|
||||
def canFlipCard(self):
|
||||
return False
|
||||
|
||||
|
||||
class PictureGallery_TableauStack(SS_RowStack):
|
||||
def __init__(self, x, y, game, base_rank, yoffset, dir=3):
|
||||
SS_RowStack.__init__(self, x, y, game, base_rank=base_rank, dir=dir, max_accept=1)
|
||||
def __init__(self, x, y, game, base_rank, yoffset, dir=3, max_cards=4):
|
||||
SS_RowStack.__init__(self, x, y, game,
|
||||
base_rank=base_rank, dir=dir, max_cards=max_cards, max_accept=1)
|
||||
self.CARD_YOFFSET = yoffset
|
||||
|
||||
def acceptsCards(self, from_stack, cards):
|
||||
|
@ -154,6 +162,13 @@ class PictureGallery_TableauStack(SS_RowStack):
|
|||
def getBottomImage(self):
|
||||
return self.game.app.images.getLetter(self.cap.base_rank)
|
||||
|
||||
def closeStackMove(self):
|
||||
if len(self.cards) == self.cap.max_cards:
|
||||
self.game.flipAllMove(self)
|
||||
|
||||
def canFlipCard(self):
|
||||
return False
|
||||
|
||||
|
||||
class PictureGallery_RowStack(BasicRowStack):
|
||||
def acceptsCards(self, from_stack, cards):
|
||||
|
@ -176,7 +191,11 @@ class PictureGallery(Game):
|
|||
Hint_Class = PictureGallery_Hint
|
||||
|
||||
Foundation_Class = PictureGallery_Foundation
|
||||
TableauStack_Class = PictureGallery_TableauStack
|
||||
TableauStack_Classes = [
|
||||
StackWrapper(PictureGallery_TableauStack, base_rank=3, max_cards=4, dir=3),
|
||||
StackWrapper(PictureGallery_TableauStack, base_rank=2, max_cards=4, dir=3),
|
||||
StackWrapper(PictureGallery_TableauStack, base_rank=1, max_cards=4, dir=3),
|
||||
]
|
||||
RowStack_Class = StackWrapper(PictureGallery_RowStack, max_accept=1)
|
||||
Talon_Class = DealRowTalonStack
|
||||
|
||||
|
@ -184,7 +203,8 @@ class PictureGallery(Game):
|
|||
# game layout
|
||||
#
|
||||
|
||||
def createGame(self, rows=3, waste=False, dir=3):
|
||||
def createGame(self, waste=False):
|
||||
rows = len(self.TableauStack_Classes)
|
||||
# create layout
|
||||
l, s = Layout(self), self.s
|
||||
TABLEAU_YOFFSET = min(9, max(3, l.YOFFSET / 3))
|
||||
|
@ -201,10 +221,10 @@ class PictureGallery(Game):
|
|||
y = l.YM + l.CH / 2
|
||||
s.foundations.append(self.Foundation_Class(x, y, self))
|
||||
y = l.YM
|
||||
for i in range(rows,0,-1): #(3, 2, 1):
|
||||
for cl in self.TableauStack_Classes:
|
||||
x = l.XM
|
||||
for j in range(8):
|
||||
s.tableaux.append(self.TableauStack_Class(x, y, self, i, yoffset=TABLEAU_YOFFSET, dir=dir))
|
||||
s.tableaux.append(cl(x, y, self, yoffset=TABLEAU_YOFFSET))
|
||||
x = x + l.XS
|
||||
y = y + th
|
||||
x, y = l.XM, y + l.YM
|
||||
|
@ -297,7 +317,10 @@ class GreatWheel_RowStack(BasicRowStack):
|
|||
class GreatWheel(PictureGallery):
|
||||
|
||||
Foundation_Class = GreatWheel_Foundation
|
||||
TableauStack_Class = PictureGallery_TableauStack
|
||||
TableauStack_Classes = [
|
||||
StackWrapper(PictureGallery_TableauStack, base_rank=2, max_cards=5, dir=2),
|
||||
StackWrapper(PictureGallery_TableauStack, base_rank=1, max_cards=6, dir=2),
|
||||
]
|
||||
RowStack_Class = StackWrapper(GreatWheel_RowStack, max_accept=1)
|
||||
Talon_Class = StackWrapper(WasteTalonStack, max_rounds=1)
|
||||
|
||||
|
@ -373,12 +396,12 @@ class MountOlympus(Game):
|
|||
x, y = l.XM+l.XS, l.YM
|
||||
for i in range(8):
|
||||
s.foundations.append(MountOlympus_Foundation(x, y, self,
|
||||
suit=i/2, base_rank=ACE, dir=2, max_move=0))
|
||||
suit=i/2, base_rank=ACE, dir=2, max_move=0, max_cards=7))
|
||||
x += l.XS
|
||||
x, y = l.XM+l.XS, l.YM+l.YS
|
||||
for i in range(8):
|
||||
s.foundations.append(MountOlympus_Foundation(x, y, self,
|
||||
suit=i/2, base_rank=1, dir=2, max_move=0))
|
||||
suit=i/2, base_rank=1, dir=2, max_move=0, max_cards=6))
|
||||
x += l.XS
|
||||
x, y = l.XM, l.YM+2*l.YS
|
||||
for i in range(9):
|
||||
|
|
|
@ -50,6 +50,13 @@ class PileOn_RowStack(RK_RowStack):
|
|||
def getBottomImage(self):
|
||||
return self.game.app.images.getReserveBottom()
|
||||
|
||||
def closeStackMove(self):
|
||||
if len(self.cards) == 4 and isRankSequence(self.cards, dir=0):
|
||||
self.game.flipAllMove(self)
|
||||
|
||||
def canFlipCard(self):
|
||||
return False
|
||||
|
||||
|
||||
class PileOn(Game):
|
||||
Hint_Class = DefaultHint
|
||||
|
@ -106,19 +113,20 @@ class PileOn(Game):
|
|||
|
||||
def isGameWon(self):
|
||||
for r in self.s.rows:
|
||||
if r.cards:
|
||||
if len(r.cards) != 4 or not r._isSequence(r.cards):
|
||||
return 0
|
||||
return 1
|
||||
if r.cards and not cardsFaceDown(r.cards):
|
||||
return False
|
||||
return True
|
||||
|
||||
def shallHighlightMatch(self, stack1, card1, stack2, card2):
|
||||
return card1.rank == card2.rank
|
||||
|
||||
|
||||
class SmallPileOn(PileOn):
|
||||
TWIDTH = 3
|
||||
NSTACKS = 11
|
||||
PLAYCARDS = 4
|
||||
|
||||
|
||||
class PileOn2Decks(PileOn):
|
||||
TWIDTH = 4
|
||||
NSTACKS = 15
|
||||
|
|
|
@ -413,7 +413,7 @@ class BritishConstitution(Game):
|
|||
# create stacks
|
||||
x, y = l.XM+l.XS, l.YM
|
||||
for i in range(8):
|
||||
s.foundations.append(BritishConstitution_Foundation(x, y, self, suit=int(i/2)))
|
||||
s.foundations.append(BritishConstitution_Foundation(x, y, self, suit=int(i/2), max_cards=11))
|
||||
x += l.XS
|
||||
|
||||
y = l.YM+l.YS
|
||||
|
|
|
@ -136,7 +136,7 @@ class SiebenBisAs(Game):
|
|||
s.reserves.append(ReserveStack(x, y, self, max_accept=0))
|
||||
for i in range(4):
|
||||
x, y, = l.XM + (i+3)*l.XS, l.YM + 4*l.YS
|
||||
s.foundations.append(SiebenBisAs_Foundation(x, y, self, i, base_rank=6, mod=13, max_move=0))
|
||||
s.foundations.append(SiebenBisAs_Foundation(x, y, self, i, base_rank=6, mod=13, max_move=0, max_cards=8))
|
||||
s.talon = InitialDealTalonStack(l.XM, self.height-l.YS, self)
|
||||
|
||||
# define stack-groups
|
||||
|
|
|
@ -141,7 +141,7 @@ class Pegged(Game):
|
|||
s.rows.append(stack)
|
||||
self.map[stack.pos] = stack
|
||||
x, y = self.width - l.XS, l.YM
|
||||
s.foundations.append(AbstractFoundationStack(x, y, self, ANY_SUIT, max_move=0, max_accept=0))
|
||||
s.foundations.append(AbstractFoundationStack(x, y, self, ANY_SUIT, max_move=0, max_accept=0, max_cards=self.gameinfo.ncards))
|
||||
l.createText(s.foundations[0], "ss")
|
||||
y = self.height - l.YS
|
||||
s.talon = InitialDealTalonStack(x, y, self)
|
||||
|
|
|
@ -53,20 +53,20 @@ class Sultan(Game):
|
|||
self.setSize(w, h)
|
||||
|
||||
# create stacks
|
||||
lay = ((0,0,0,1),
|
||||
(2,0,0,1),
|
||||
(0,1,1,1),
|
||||
(2,1,1,1),
|
||||
(1,1,2,0),
|
||||
(1,2,2,1),
|
||||
(0,2,3,1),
|
||||
(2,2,3,1),
|
||||
(1,0,2,1),
|
||||
lay = ((0,0,0,1,13),
|
||||
(2,0,0,1,13),
|
||||
(0,1,1,1,13),
|
||||
(2,1,1,1,13),
|
||||
(1,1,2,0,1),
|
||||
(1,2,2,1,13),
|
||||
(0,2,3,1,13),
|
||||
(2,2,3,1,13),
|
||||
(1,0,2,1,12),
|
||||
)
|
||||
for i, j, suit, max_accept in lay:
|
||||
for i, j, suit, max_accept, max_cards in lay:
|
||||
x, y = 2*l.XM+l.XS+i*l.XS, l.YM+j*l.YS
|
||||
stack = SS_FoundationStack(x, y, self, suit=suit,
|
||||
max_move=0, max_accept=max_accept, mod=13)
|
||||
max_move=0, max_accept=max_accept, max_cards=max_cards, mod=13)
|
||||
s.foundations.append(stack)
|
||||
|
||||
x, y = l.XM, l.YM
|
||||
|
@ -309,7 +309,7 @@ class IdleAces(Game):
|
|||
x, y = x0+i*l.XS, y0+j*l.YS
|
||||
s.foundations.append(RK_FoundationStack(x, y, self,
|
||||
##suit=ANY_SUIT,
|
||||
base_rank=1, max_move=0))
|
||||
base_rank=1, max_move=0, max_cards=12))
|
||||
k += 1
|
||||
k = 0
|
||||
for i, j in((1, 0.2), (3, 0.2), (1, 2.8), (3, 2.8)):
|
||||
|
@ -635,14 +635,15 @@ class SixesAndSevens(Game):
|
|||
for i in range(2):
|
||||
x = l.XM
|
||||
for j in range(4):
|
||||
s.foundations.append(SS_FoundationStack(x, y, self, suit=j, base_rank=6))
|
||||
s.foundations.append(SS_FoundationStack(x, y, self,
|
||||
suit=j, base_rank=6, max_cards=7))
|
||||
x += l.XS
|
||||
y += l.YS
|
||||
for i in range(2):
|
||||
x = l.XM
|
||||
for j in range(4):
|
||||
s.foundations.append(SS_FoundationStack(x, y, self, suit=j,
|
||||
base_rank=5, dir=-1))
|
||||
base_rank=5, dir=-1, max_cards=6))
|
||||
x += l.XS
|
||||
y += l.YS
|
||||
y = l.YM
|
||||
|
|
|
@ -46,6 +46,10 @@ class TakeAway_Foundation(AbstractFoundationStack):
|
|||
return (c1.rank == (c2.rank + 1) % mod or
|
||||
c2.rank == (c1.rank + 1) % mod)
|
||||
|
||||
def closeStackMove(self):
|
||||
pass
|
||||
|
||||
|
||||
class TakeAway(Game):
|
||||
|
||||
RowStack_Class = BasicRowStack
|
||||
|
|
|
@ -171,17 +171,22 @@ class Odessa(RussianSolitaire):
|
|||
# // Grandfather
|
||||
# ************************************************************************/
|
||||
|
||||
class Grandfather_Talon(RedealTalonStack):
|
||||
def redealCards(self, sound=0):
|
||||
RedealTalonStack.redealCards(self, sound=sound, shuffle=True)
|
||||
|
||||
class Grandfather(RussianSolitaire):
|
||||
Talon_Class = StackWrapper(Grandfather_Talon, max_rounds=3)
|
||||
|
||||
def startGame(self):
|
||||
n = 1
|
||||
for i in (2,4,6,5,3,1):
|
||||
self.s.talon.dealRow(rows=[self.s.rows[n]]*i, flip=0, frames=0)
|
||||
n += 1
|
||||
n = 0
|
||||
for i, j in ((1,7),(1,6),(2,6),(2,5),(3,5),(3,4)):
|
||||
self.s.talon.dealRowAvail(rows=self.s.rows[i:j], flip=0, frames=0)
|
||||
self.startDealSample()
|
||||
for i in (1,5,5,5,5,5,5):
|
||||
self.s.talon.dealRow(rows=[self.s.rows[n]]*i)
|
||||
n += 1
|
||||
self.s.talon.dealRowAvail()
|
||||
for i in range(4):
|
||||
self.s.talon.dealRowAvail(rows=self.s.rows[1:])
|
||||
|
||||
redealCards = startGame
|
||||
|
||||
|
||||
# /***********************************************************************
|
||||
|
@ -644,7 +649,7 @@ registerGame(GameInfo(20, RussianSolitaire, "Russian Solitaire",
|
|||
registerGame(GameInfo(27, Odessa, "Odessa",
|
||||
GI.GT_YUKON, 1, 0, GI.SL_BALANCED))
|
||||
registerGame(GameInfo(278, Grandfather, "Grandfather",
|
||||
GI.GT_YUKON, 1, 0, GI.SL_MOSTLY_LUCK))
|
||||
GI.GT_YUKON, 1, 2, GI.SL_BALANCED))
|
||||
registerGame(GameInfo(186, Alaska, "Alaska",
|
||||
GI.GT_YUKON, 1, 0, GI.SL_BALANCED))
|
||||
registerGame(GameInfo(187, ChineseDiscipline, "Chinese Discipline",
|
||||
|
|
|
@ -144,7 +144,6 @@ class AFlipAllMove(AtomicMove):
|
|||
|
||||
# do the actual move
|
||||
def __doMove(self, game, stack):
|
||||
#card = stack.cards[-1]
|
||||
for card in stack.cards:
|
||||
if card.face_up:
|
||||
card.showBack()
|
||||
|
@ -434,3 +433,28 @@ class AShuffleStackMove(AtomicMove):
|
|||
cmp(self.card_ids, other.card_ids) or
|
||||
cmp(self.state, other.state))
|
||||
|
||||
|
||||
# /***********************************************************************
|
||||
# // ACloseStackMove
|
||||
# ************************************************************************/
|
||||
|
||||
class ACloseStackMove(AtomicMove):
|
||||
|
||||
def __init__(self, stack):
|
||||
self.stack_id = stack.id
|
||||
|
||||
def redo(self, game):
|
||||
stack = game.allstacks[self.stack_id]
|
||||
assert stack.cards
|
||||
stack.is_closed = True
|
||||
stack._shadeStack()
|
||||
|
||||
def undo(self, game):
|
||||
stack = game.allstacks[self.stack_id]
|
||||
assert stack.cards
|
||||
stack.is_closed = False
|
||||
stack._unshadeStack()
|
||||
|
||||
def cmpForRedo(self, other):
|
||||
return cmp(self.stack_id, other.stack_id)
|
||||
|
||||
|
|
|
@ -45,9 +45,11 @@ __all__ = ['cardsFaceUp',
|
|||
'Stack',
|
||||
'DealRow_StackMethods',
|
||||
'DealBaseCard_StackMethods',
|
||||
'RedealCards_StackMethods',
|
||||
'TalonStack',
|
||||
'DealRowTalonStack',
|
||||
'InitialDealTalonStack',
|
||||
'RedealTalonStack',
|
||||
'OpenStack',
|
||||
'AbstractFoundationStack',
|
||||
'SS_FoundationStack',
|
||||
|
@ -276,10 +278,12 @@ class Stack:
|
|||
bottom = None, # canvas item
|
||||
redeal = None, # canvas item
|
||||
redeal_img = None, # the corresponding PhotoImage
|
||||
shade_img = None,
|
||||
)
|
||||
# other canvas items
|
||||
view.items = Struct(
|
||||
bottom = None, # dummy canvas item
|
||||
shade_item = None,
|
||||
)
|
||||
# text items
|
||||
view.texts = Struct(
|
||||
|
@ -296,6 +300,8 @@ class Stack:
|
|||
view.is_open = -1
|
||||
view.can_hide_cards = -1
|
||||
view.max_shadow_cards = -1
|
||||
#
|
||||
view.is_closed = False
|
||||
|
||||
def destruct(self):
|
||||
# help breaking circular references
|
||||
|
@ -422,6 +428,7 @@ class Stack:
|
|||
view._position(card)
|
||||
if update:
|
||||
view.updateText()
|
||||
self.closeStackMove()
|
||||
return card
|
||||
|
||||
# Remove a card from the stack. Also update display. {model -> view}
|
||||
|
@ -449,6 +456,9 @@ class Stack:
|
|||
model.cards.remove(card)
|
||||
if update:
|
||||
view.updateText()
|
||||
if self.is_closed:
|
||||
self._unshadeStack()
|
||||
self.is_closed = False
|
||||
return card
|
||||
|
||||
# Get the top card {model}
|
||||
|
@ -621,6 +631,8 @@ class Stack:
|
|||
def fillStack(self):
|
||||
self.game.fillStack(self)
|
||||
|
||||
def closeStackMove(self):
|
||||
pass
|
||||
|
||||
#
|
||||
# Playing move actions. Better not override.
|
||||
|
@ -974,6 +986,8 @@ class Stack:
|
|||
i = self._findCard(event)
|
||||
if i < 0 or not self.canMoveCards(self.cards[i:]):
|
||||
return
|
||||
if self.is_closed:
|
||||
self.items.shade_item.config(state='hidden')
|
||||
x_offset, y_offset = self.cards[i].x, self.cards[i].y
|
||||
if sound:
|
||||
self.game.playSample("startdrag")
|
||||
|
@ -1048,24 +1062,29 @@ class Stack:
|
|||
return ()
|
||||
cy = c.y
|
||||
img0, img1 = images.getShadow(0), images.getShadow(l)
|
||||
if 0:
|
||||
# Dynamically compute the shadow. Doesn't work because
|
||||
# PhotoImage.copy() doesn't preserve transparency.
|
||||
img1 = images.getShadow(13)
|
||||
if img1:
|
||||
h = images.CARDH - img0.height()
|
||||
h = h + (l - 1) * self.CARD_YOFFSET[0]
|
||||
if h < img1.height():
|
||||
import Tkinter
|
||||
dest = Tkinter.PhotoImage(width=img1.width(), height=h)
|
||||
dest.blank()
|
||||
img1.tk.call(dest, "copy", img1.name, "-from", 0, 0, img1.width(), h)
|
||||
assert dest.height() == h and dest.width() == img1.width()
|
||||
#print h, img1.height(), dest.height()
|
||||
img1 = dest
|
||||
self._foo = img1 # keep a reference
|
||||
elif h > img1.height():
|
||||
img1 = None
|
||||
## if 0:
|
||||
## # Dynamically compute the shadow. Doesn't work because
|
||||
## # PhotoImage.copy() doesn't preserve transparency.
|
||||
## img1 = images.getShadow(13)
|
||||
## if img1:
|
||||
## h = images.CARDH - img0.height()
|
||||
## h = h + (l - 1) * self.CARD_YOFFSET[0]
|
||||
## if h < img1.height():
|
||||
## if hasattr(img1, '_pil_image'): # use PIL
|
||||
## import ImageTk
|
||||
## im = img1._pil_image.crop((0,0,img1.width(),h))
|
||||
## img1 = ImageTk.PhotoImage(im)
|
||||
## else:
|
||||
## import Tkinter
|
||||
## dest = Tkinter.PhotoImage(width=img1.width(), height=h)
|
||||
## dest.blank()
|
||||
## img1.tk.call(dest, "copy", img1.name, "-from", 0, 0, img1.width(), h)
|
||||
## assert dest.height() == h and dest.width() == img1.width()
|
||||
## #print h, img1.height(), dest.height()
|
||||
## img1 = dest
|
||||
## self._foo = img1 # keep a reference
|
||||
## elif h > img1.height():
|
||||
## img1 = None
|
||||
if img0 and img1:
|
||||
c = cards[-1]
|
||||
if self.CARD_YOFFSET[0] < 0: c = cards[0]
|
||||
|
@ -1090,7 +1109,11 @@ class Stack:
|
|||
# optimized for speed - we use lots of local variables
|
||||
game = self.game
|
||||
images = game.app.images
|
||||
img = images.getShade()
|
||||
if not self.images.shade_img:
|
||||
img = images.getShade()
|
||||
self.images.shade_img = img
|
||||
else:
|
||||
img = self.images.shade_img
|
||||
if img is None:
|
||||
return
|
||||
CW, CH = images.CARDW, images.CARDH
|
||||
|
@ -1139,6 +1162,31 @@ class Stack:
|
|||
else:
|
||||
img.lower(drag.cards[0].item)
|
||||
|
||||
# for closeStackMove
|
||||
def _shadeStack(self):
|
||||
if not self.game.app.opt.shade_filled_stacks:
|
||||
return
|
||||
if not self.images.shade_img:
|
||||
img = self.game.app.images.getShade()
|
||||
self.images.shade_img = img
|
||||
else:
|
||||
img = self.images.shade_img
|
||||
if img is None:
|
||||
return
|
||||
if not self.items.shade_item:
|
||||
self.game.canvas.update_idletasks()
|
||||
card = self.cards[-1]
|
||||
item = MfxCanvasImage(self.game.canvas, card.x, card.y,
|
||||
image=img, anchor=ANCHOR_NW)
|
||||
##item.tkraise()
|
||||
item.addtag(self.group)
|
||||
self.items.shade_item = item
|
||||
|
||||
def _unshadeStack(self):
|
||||
if self.items.shade_item:
|
||||
self.items.shade_item.delete()
|
||||
self.items.shade_item = None
|
||||
|
||||
def _stopDrag(self):
|
||||
drag = self.game.drag
|
||||
after_cancel(drag.timer)
|
||||
|
@ -1151,6 +1199,9 @@ class Stack:
|
|||
drag.shadows = []
|
||||
drag.stack = None
|
||||
drag.cards = []
|
||||
if self.is_closed:
|
||||
self.items.shade_item.config(state='normal')
|
||||
self.items.shade_item.tkraise()
|
||||
|
||||
# finish a drag operation
|
||||
def finishDrag(self, event=None):
|
||||
|
@ -1173,8 +1224,7 @@ class Stack:
|
|||
self.moveCardsBackHandler(event, drag)
|
||||
|
||||
def getHelp(self):
|
||||
# devel
|
||||
return str(self)
|
||||
return str(self) # debug
|
||||
|
||||
def getBaseCard(self):
|
||||
return ''
|
||||
|
@ -1319,11 +1369,46 @@ class DealBaseCard_StackMethods:
|
|||
ncards = ncards - 1
|
||||
|
||||
|
||||
class RedealCards_StackMethods:
|
||||
|
||||
def redealCards(self, sound=0, shuffle=False, reverse=False, frames=4):
|
||||
if sound and self.game.app.opt.animations:
|
||||
self.game.startDealSample()
|
||||
lr = len(self.game.s.rows)
|
||||
# move all cards to the Talon
|
||||
num_cards = 0
|
||||
assert len(self.cards) == 0
|
||||
rows = list(self.game.s.rows)[:]
|
||||
if reverse:
|
||||
rows.reverse()
|
||||
for r in rows:
|
||||
for i in range(len(r.cards)):
|
||||
num_cards += 1
|
||||
self.game.moveMove(1, r, self, frames=0)
|
||||
if self.cards[-1].face_up:
|
||||
self.game.flipMove(self)
|
||||
assert len(self.cards) == num_cards
|
||||
if num_cards == 0: # game already finished
|
||||
return 0
|
||||
if shuffle:
|
||||
# shuffle
|
||||
self.game.shuffleStackMove(self)
|
||||
# redeal
|
||||
self.game.nextRoundMove(self)
|
||||
self.game.redealCards()
|
||||
if sound:
|
||||
self.game.stopSamples()
|
||||
return num_cards
|
||||
|
||||
|
||||
# /***********************************************************************
|
||||
# // The Talon is a stack with support for dealing.
|
||||
# ************************************************************************/
|
||||
|
||||
class TalonStack(Stack, DealRow_StackMethods, DealBaseCard_StackMethods):
|
||||
class TalonStack(Stack,
|
||||
DealRow_StackMethods,
|
||||
DealBaseCard_StackMethods,
|
||||
):
|
||||
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
|
||||
|
@ -1361,8 +1446,8 @@ class TalonStack(Stack, DealRow_StackMethods, DealBaseCard_StackMethods):
|
|||
def removeAllCards(self):
|
||||
for stack in self.game.allstacks:
|
||||
while stack.cards:
|
||||
##stack.removeCard(update=0)
|
||||
stack.removeCard(unhide=0, update=0)
|
||||
stack.removeCard(update=0)
|
||||
##stack.removeCard(unhide=0, update=0)
|
||||
for stack in self.game.allstacks:
|
||||
stack.updateText()
|
||||
|
||||
|
@ -1461,6 +1546,15 @@ class InitialDealTalonStack(TalonStack):
|
|||
return None
|
||||
|
||||
|
||||
class RedealTalonStack(TalonStack, RedealCards_StackMethods):
|
||||
def canDealCards(self):
|
||||
if self.round == self.max_rounds:
|
||||
return False
|
||||
return not self.game.isGameWon()
|
||||
def dealCards(self, sound=0):
|
||||
RedealCards_StackMethods.redealCards(self, sound=sound)
|
||||
|
||||
|
||||
# /***********************************************************************
|
||||
# // An OpenStack is a stack where cards can be placed and dragged
|
||||
# // (i.e. FoundationStack, RowStack, ReserveStack, ...)
|
||||
|
@ -1646,6 +1740,10 @@ class AbstractFoundationStack(OpenStack):
|
|||
def getBaseCard(self):
|
||||
return self._getBaseCard()
|
||||
|
||||
def closeStackMove(self):
|
||||
if len(self.cards) == self.cap.max_cards:
|
||||
self.game.closeStackMove(self)
|
||||
|
||||
|
||||
# A SameSuit_FoundationStack is the typical Foundation stack.
|
||||
# It builds up in rank and suit.
|
||||
|
|
|
@ -115,8 +115,10 @@ class _OneImageCard(_HideableCard):
|
|||
_HideableCard.__init__(self, id, deck, suit, rank, game, x=x, y=y)
|
||||
self._face_image = game.getCardFaceImage(deck, suit, rank)
|
||||
self._back_image = game.getCardBackImage(deck, suit, rank)
|
||||
self._shade_image = game.getCardShadeImage()
|
||||
self._active_image = self._back_image
|
||||
self.item = MfxCanvasImage(game.canvas, self.x, self.y, image=self._active_image, anchor="nw")
|
||||
self.shade_item = None
|
||||
##self._setImage = self.item.config
|
||||
|
||||
def _setImage(self, image):
|
||||
|
@ -153,6 +155,7 @@ class _OneImageCard(_HideableCard):
|
|||
item.canvas.tk.call(item.canvas._w, "move", item.id, dx, dy)
|
||||
|
||||
|
||||
|
||||
# /***********************************************************************
|
||||
# // New idea since 3.00
|
||||
# //
|
||||
|
|
|
@ -335,8 +335,8 @@ class PysolMenubar(PysolMenubarActions):
|
|||
submenu.add_checkbutton(label=n_("Enable highlight same &rank"), variable=self.tkopt.highlight_samerank, command=self.mOptEnableHighlightSameRank)
|
||||
submenu.add_checkbutton(label=n_("Highlight &no matching"), variable=self.tkopt.highlight_not_matching, command=self.mOptEnableHighlightNotMatching)
|
||||
submenu.add_separator()
|
||||
submenu.add_checkbutton(label=n_("Show removed tiles (in Mahjongg games)"), variable=self.tkopt.mahjongg_show_removed, command=self.mOptMahjonggShowRemoved)
|
||||
submenu.add_checkbutton(label=n_("Show hint arrow (in Shisen-Sho games)"), variable=self.tkopt.shisen_show_hint, command=self.mOptShisenShowHint)
|
||||
submenu.add_checkbutton(label=n_("&Show removed tiles (in Mahjongg games)"), variable=self.tkopt.mahjongg_show_removed, command=self.mOptMahjonggShowRemoved)
|
||||
submenu.add_checkbutton(label=n_("Show hint &arrow (in Shisen-Sho games)"), variable=self.tkopt.shisen_show_hint, command=self.mOptShisenShowHint)
|
||||
menu.add_separator()
|
||||
label = n_("&Sound...")
|
||||
if self.app.audio.audiodev is None:
|
||||
|
@ -354,6 +354,7 @@ class PysolMenubar(PysolMenubarActions):
|
|||
submenu.add_checkbutton(label=n_("Card shado&w"), variable=self.tkopt.shadow, command=self.mOptShadow)
|
||||
submenu.add_checkbutton(label=n_("Shade &legal moves"), variable=self.tkopt.shade, command=self.mOptShade)
|
||||
submenu.add_checkbutton(label=n_("&Negative cards bottom"), variable=self.tkopt.negative_bottom, command=self.mOptNegativeBottom)
|
||||
submenu.add_checkbutton(label=n_("Shade &filled stacks"), variable=self.tkopt.shade_filled_stacks, command=self.mOptShadeFilledStacks)
|
||||
submenu = MfxMenu(menu, label=n_("A&nimations"))
|
||||
submenu.add_radiobutton(label=n_("&None"), variable=self.tkopt.animations, value=0, command=self.mOptAnimations)
|
||||
submenu.add_radiobutton(label=n_("&Timer based"), variable=self.tkopt.animations, value=2, command=self.mOptAnimations)
|
||||
|
|
|
@ -222,10 +222,17 @@ class MfxCanvas(Tkinter.Canvas):
|
|||
if stack.cards[i].item.tag in current:
|
||||
return i
|
||||
else:
|
||||
current = self.find("withtag", "current") # get item ids
|
||||
for i in range(len(stack.cards)):
|
||||
if stack.cards[i].item.id in current:
|
||||
return i
|
||||
## current = self.find("withtag", "current") # get item ids
|
||||
## for i in range(len(stack.cards)):
|
||||
## if stack.cards[i].item.id in current:
|
||||
## return i
|
||||
x, y = event.x, event.y
|
||||
items = list(self.find_overlapping(x,y,x,y))
|
||||
items.reverse()
|
||||
for item in items:
|
||||
for i in range(len(stack.cards)):
|
||||
if stack.cards[i].item.id == item:
|
||||
return i
|
||||
return -1
|
||||
|
||||
def setTextColor(self, color):
|
||||
|
|
Loading…
Add table
Reference in a new issue