mirror of
https://github.com/shlomif/PySolFC.git
synced 2025-04-05 00:02:29 -04:00
* improved findcarddialog + new command-line options: `--game' & `--gameid' git-svn-id: file:///home/shlomif/Backup/svn-dumps/PySolFC/svnsync-repos/pysolfc/PySolFC/trunk@37 efabe8c0-fbe8-4139-b769-b5e6d273206e
701 lines
23 KiB
Python
701 lines
23 KiB
Python
## vim:ts=4:et:nowrap
|
|
##
|
|
##---------------------------------------------------------------------------##
|
|
##
|
|
## PySol -- a Python Solitaire game
|
|
##
|
|
## Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer
|
|
## Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer
|
|
## Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer
|
|
##
|
|
## This program is free software; you can redistribute it and/or modify
|
|
## it under the terms of the GNU General Public License as published by
|
|
## the Free Software Foundation; either version 2 of the License, or
|
|
## (at your option) any later version.
|
|
##
|
|
## This program is distributed in the hope that it will be useful,
|
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
## GNU General Public License for more details.
|
|
##
|
|
## You should have received a copy of the GNU General Public License
|
|
## along with this program; see the file COPYING.
|
|
## If not, write to the Free Software Foundation, Inc.,
|
|
## 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
##
|
|
## Markus F.X.J. Oberhumer
|
|
## <markus@oberhumer.com>
|
|
## http://www.oberhumer.com/pysol
|
|
##
|
|
##---------------------------------------------------------------------------##
|
|
|
|
__all__ = []
|
|
|
|
# imports
|
|
import sys
|
|
|
|
# PySol imports
|
|
from pysollib.gamedb import registerGame, GameInfo, GI
|
|
from pysollib.util import *
|
|
from pysollib.stack import *
|
|
from pysollib.game import Game
|
|
from pysollib.layout import Layout
|
|
from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint
|
|
from pysollib.pysoltk import MfxCanvasText
|
|
|
|
|
|
# /***********************************************************************
|
|
# //
|
|
# ************************************************************************/
|
|
|
|
class Pyramid_Hint(DefaultHint):
|
|
# consider moving card to the Talon as well
|
|
def step010(self, dropstacks, rows):
|
|
rows = rows + (self.game.s.talon,)
|
|
return DefaultHint.step010(self, dropstacks, rows)
|
|
|
|
|
|
# /***********************************************************************
|
|
# // basic logic for Talon, Waste and Rows
|
|
# ************************************************************************/
|
|
|
|
class Pyramid_StackMethods:
|
|
def acceptsCards(self, from_stack, cards):
|
|
if self.basicIsBlocked():
|
|
return 0
|
|
if from_stack is self or not self.cards or len(cards) != 1:
|
|
return 0
|
|
c = self.cards[-1]
|
|
return c.face_up and cards[0].face_up and cards[0].rank + c.rank == 11
|
|
|
|
def _dropKingClickHandler(self, event):
|
|
if not self.cards:
|
|
return 0
|
|
c = self.cards[-1]
|
|
if c.face_up and c.rank == KING and not self.basicIsBlocked():
|
|
self.game.playSample("autodrop", priority=20)
|
|
self.playMoveMove(1, self.game.s.foundations[0], sound=0)
|
|
return 1
|
|
return 0
|
|
|
|
def _dropPairMove(self, n, other_stack, frames=-1, shadow=-1):
|
|
if not self.game.demo:
|
|
self.game.playSample("droppair", priority=200)
|
|
assert n == 1 and self.acceptsCards(other_stack, [other_stack.cards[-1]])
|
|
old_state = self.game.enterState(self.game.S_FILL)
|
|
f = self.game.s.foundations[0]
|
|
self.game.moveMove(n, self, f, frames=frames, shadow=shadow)
|
|
self.game.moveMove(n, other_stack, f, frames=frames, shadow=shadow)
|
|
self.game.leaveState(old_state)
|
|
self.fillStack()
|
|
other_stack.fillStack()
|
|
|
|
def moveMove(self, ncards, to_stack, frames=-1, shadow=-1):
|
|
if to_stack in self.game.s.foundations:
|
|
self.game.moveMove(ncards, self, to_stack, frames=frames, shadow=shadow)
|
|
self.fillStack()
|
|
else:
|
|
self._dropPairMove(ncards, to_stack, frames=-1, shadow=shadow)
|
|
|
|
|
|
# /***********************************************************************
|
|
# //
|
|
# ************************************************************************/
|
|
|
|
class Pyramid_Foundation(AbstractFoundationStack):
|
|
def acceptsCards(self, from_stack, cards):
|
|
if not AbstractFoundationStack.acceptsCards(self, from_stack, cards):
|
|
return 0
|
|
# We accept any King. Pairs will get delivered by _dropPairMove.
|
|
return cards[0].rank == KING
|
|
|
|
|
|
# note that this Talon can accept and drop cards
|
|
class Pyramid_Talon(Pyramid_StackMethods, FaceUpWasteTalonStack):
|
|
def clickHandler(self, event):
|
|
if self._dropKingClickHandler(event):
|
|
return 1
|
|
return FaceUpWasteTalonStack.clickHandler(self, event)
|
|
|
|
def canDealCards(self):
|
|
if not FaceUpWasteTalonStack.canDealCards(self):
|
|
return 0
|
|
return not self.game.isGameWon()
|
|
|
|
def canDropCards(self, stacks):
|
|
if self.cards:
|
|
cards = self.cards[-1:]
|
|
for s in stacks:
|
|
if s is not self and s.acceptsCards(self, cards):
|
|
return (s, 1)
|
|
return (None, 0)
|
|
|
|
|
|
class Pyramid_Waste(Pyramid_StackMethods, WasteStack):
|
|
def clickHandler(self, event):
|
|
if self._dropKingClickHandler(event):
|
|
return 1
|
|
return WasteStack.clickHandler(self, event)
|
|
|
|
|
|
class Pyramid_RowStack(Pyramid_StackMethods, OpenStack):
|
|
def __init__(self, x, y, game):
|
|
OpenStack.__init__(self, x, y, game, max_accept=1, max_cards=2)
|
|
self.CARD_YOFFSET = 1
|
|
|
|
STEP = (1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6)
|
|
|
|
def basicIsBlocked(self):
|
|
r, step = self.game.s.rows, self.STEP
|
|
i, n = self.id, 1
|
|
while i < 21:
|
|
i = i + step[i]
|
|
n = n + 1
|
|
for j in range(i, i+n):
|
|
if r[j].cards:
|
|
return 1
|
|
return 0
|
|
|
|
def clickHandler(self, event):
|
|
if self._dropKingClickHandler(event):
|
|
return 1
|
|
return OpenStack.clickHandler(self, event)
|
|
|
|
|
|
# /***********************************************************************
|
|
# // Pyramid
|
|
# ************************************************************************/
|
|
|
|
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, reserves=0, waste=True, texts=True):
|
|
# create layout
|
|
l, s = Layout(self), self.s
|
|
|
|
# set window
|
|
max_rows = max(9, reserves)
|
|
w = l.XM + max_rows*l.XS
|
|
h = l.YM + 4*l.YS
|
|
if reserves:
|
|
h += l.YS+2*l.YOFFSET
|
|
self.setSize(w, h)
|
|
|
|
# create stacks
|
|
for i in range(7):
|
|
x = l.XM + (8-i) * l.XS / 2
|
|
y = l.YM + i * l.YS / 2
|
|
for j in range(i+1):
|
|
s.rows.append(Pyramid_RowStack(x, y, self))
|
|
x = x + l.XS
|
|
|
|
x, y = l.XM, l.YM
|
|
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
|
|
l.defaultStackGroups()
|
|
self.sg.openstacks.append(s.talon)
|
|
self.sg.dropstacks.append(s.talon)
|
|
|
|
|
|
#
|
|
# game overrides
|
|
#
|
|
|
|
def startGame(self):
|
|
self.startDealSample()
|
|
self.s.talon.dealRow()
|
|
self.s.talon.dealCards() # deal first card to WasteStack
|
|
|
|
def getAutoStacks(self, event=None):
|
|
return (self.sg.dropstacks, self.sg.dropstacks, ())
|
|
|
|
def shallHighlightMatch(self, stack1, card1, stack2, card2):
|
|
return card1.rank + card2.rank == 11
|
|
|
|
|
|
# /***********************************************************************
|
|
# // Relaxed Pyramid
|
|
# ************************************************************************/
|
|
|
|
class RelaxedPyramid(Pyramid):
|
|
# the pyramid must be empty
|
|
def isGameWon(self):
|
|
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
|
|
# // (this doesn't work yet as 2 cards of the Waste should be playable)
|
|
# ************************************************************************/
|
|
# Thirteen #89404422185320919548
|
|
|
|
class Thirteen(Pyramid):
|
|
|
|
#
|
|
# game layout
|
|
#
|
|
|
|
def createGame(self):
|
|
# create layout
|
|
l, s = Layout(self), self.s
|
|
|
|
# set window
|
|
self.setSize(7*l.XS+l.XM, 5*l.YS+l.YM)
|
|
|
|
# create stacks
|
|
for i in range(7):
|
|
x = l.XM + (6-i) * l.XS / 2
|
|
y = l.YM + l.YS + i * l.YS / 2
|
|
for j in range(i+1):
|
|
s.rows.append(Pyramid_RowStack(x, y, self))
|
|
x = x + l.XS
|
|
x, y = l.XM, l.YM
|
|
s.talon = WasteTalonStack(x, y, self, max_rounds=1)
|
|
l.createText(s.talon, "s")
|
|
x = x + l.XS
|
|
s.waste = Pyramid_Waste(x, y, self)
|
|
l.createText(s.waste, "s")
|
|
s.waste.CARD_XOFFSET = 14
|
|
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=UNLIMITED_CARDS))
|
|
|
|
# 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
|
|
|
|
|
|
#
|
|
# game overrides
|
|
#
|
|
|
|
def startGame(self):
|
|
self.startDealSample()
|
|
self.s.talon.dealRow(rows=self.s.rows[:21], flip=0)
|
|
self.s.talon.dealRow(rows=self.s.rows[21:])
|
|
self.s.talon.dealCards() # deal first card to WasteStack
|
|
|
|
|
|
# /***********************************************************************
|
|
# // Thirteens
|
|
# ************************************************************************/
|
|
|
|
class Thirteens(Pyramid):
|
|
|
|
def createGame(self):
|
|
# create layout
|
|
l, s = Layout(self), self.s
|
|
|
|
# set window
|
|
self.setSize(l.XM+5*l.XS, l.YM+4*l.YS)
|
|
|
|
# create stacks
|
|
x, y = l.XM, l.YM
|
|
for i in range(2):
|
|
x = l.XM
|
|
for j in range(5):
|
|
s.rows.append(Giza_Reserve(x, y, self, max_accept=1))
|
|
x += l.XS
|
|
y += l.YS
|
|
x, y = l.XM, self.height-l.YS
|
|
s.talon = TalonStack(x, y, self)
|
|
l.createText(s.talon, 'n')
|
|
x, y = self.width-l.XS, self.height-l.YS
|
|
s.foundations.append(Pyramid_Foundation(x, y, self,
|
|
suit=ANY_SUIT, dir=0, base_rank=ANY_RANK,
|
|
max_move=0, max_cards=52))
|
|
|
|
# define stack-groups
|
|
l.defaultStackGroups()
|
|
|
|
def startGame(self):
|
|
self.startDealSample()
|
|
self.s.talon.dealRow()
|
|
|
|
def fillStack(self, stack):
|
|
if stack in self.s.rows:
|
|
if not stack.cards and self.s.talon.cards:
|
|
old_state = self.enterState(self.S_FILL)
|
|
self.s.talon.flipMove()
|
|
self.s.talon.moveMove(1, stack)
|
|
self.leaveState(old_state)
|
|
|
|
# /***********************************************************************
|
|
# // Elevens
|
|
# // Suit Elevens
|
|
# ************************************************************************/
|
|
|
|
class Elevens_RowStack(Giza_Reserve):
|
|
ACCEPTED_SUM = 9
|
|
|
|
def acceptsCards(self, from_stack, cards):
|
|
#if self.basicIsBlocked():
|
|
# return 0
|
|
if from_stack is self or not self.cards or len(cards) != 1:
|
|
return False
|
|
c = self.cards[-1]
|
|
return (c.face_up and cards[0].face_up
|
|
and cards[0].rank + c.rank == self.ACCEPTED_SUM)
|
|
|
|
def clickHandler(self, event):
|
|
return OpenStack.clickHandler(self, event)
|
|
|
|
def moveMove(self, ncards, to_stack, frames=-1, shadow=-1):
|
|
if to_stack in self.game.s.rows:
|
|
self._dropPairMove(ncards, to_stack, frames=-1, shadow=shadow)
|
|
else:
|
|
self.game.moveMove(ncards, self, to_stack,
|
|
frames=frames, shadow=shadow)
|
|
self.fillStack()
|
|
|
|
|
|
class Elevens_Reserve(ReserveStack):
|
|
ACCEPTED_CARDS = (JACK, QUEEN, KING)
|
|
|
|
def acceptsCards(self, from_stack, cards):
|
|
if not ReserveStack.acceptsCards(self, from_stack, cards):
|
|
return False
|
|
c = cards[0]
|
|
if not c.rank in self.ACCEPTED_CARDS:
|
|
return False
|
|
for s in self.game.s.reserves:
|
|
if s.cards and s.cards[0].rank == c.rank:
|
|
return False
|
|
return True
|
|
|
|
|
|
class Elevens(Pyramid):
|
|
|
|
RowStack_Class = Elevens_RowStack
|
|
Reserve_Class = Elevens_Reserve
|
|
|
|
def createGame(self, rows=3, cols=3, reserves=3, texts=False):
|
|
|
|
l, s = Layout(self), self.s
|
|
|
|
self.setSize(l.XM+(cols+2)*l.XS, l.YM+(rows+1.5)*l.YS)
|
|
|
|
x, y = self.width-l.XS, l.YM
|
|
s.talon = TalonStack(x, y, self)
|
|
l.createText(s.talon, 's')
|
|
x, y = self.width-l.XS, self.height-l.YS
|
|
s.foundations.append(AbstractFoundationStack(x, y, self,
|
|
suit=ANY_SUIT, max_accept=0,
|
|
max_move=0, max_cards=52))
|
|
y = l.YM
|
|
for i in range(rows):
|
|
x = l.XM
|
|
for j in range(cols):
|
|
s.rows.append(self.RowStack_Class(x, y, self, max_accept=1))
|
|
x += l.XS
|
|
y += l.YS
|
|
x, y = l.XM, self.height-l.YS
|
|
for i in range(reserves):
|
|
stack = self.Reserve_Class(x, y, self)
|
|
s.reserves.append(stack)
|
|
stack.CARD_XOFFSET = l.XOFFSET # for fifteens
|
|
x += l.XS
|
|
|
|
if texts:
|
|
stack = s.reserves[0]
|
|
tx, ty, ta, tf = l.getTextAttr(stack, "n")
|
|
font = self.app.getFont("canvas_default")
|
|
stack.texts.misc = MfxCanvasText(self.canvas, tx, ty,
|
|
anchor=ta, font=font)
|
|
|
|
l.defaultStackGroups()
|
|
|
|
|
|
def startGame(self):
|
|
self.startDealSample()
|
|
self.s.talon.dealRow()
|
|
|
|
|
|
def fillStack(self, stack):
|
|
old_state = self.enterState(self.S_FILL)
|
|
if stack in self.s.rows:
|
|
if not stack.cards and self.s.talon.cards:
|
|
self.s.talon.flipMove()
|
|
self.s.talon.moveMove(1, stack)
|
|
reserves_ncards = 0
|
|
for s in self.s.reserves:
|
|
if s.cards:
|
|
reserves_ncards += 1
|
|
if reserves_ncards == len(self.s.reserves):
|
|
if not self.demo:
|
|
self.playSample("droppair", priority=200)
|
|
for s in self.s.reserves:
|
|
s.moveMove(1, self.s.foundations[0], frames=4)
|
|
self.leaveState(old_state)
|
|
|
|
|
|
class ElevensToo(Elevens):
|
|
|
|
def fillStack(self, stack):
|
|
old_state = self.enterState(self.S_FILL)
|
|
reserves_ncards = 0
|
|
for s in self.s.reserves:
|
|
if s.cards:
|
|
reserves_ncards += 1
|
|
if reserves_ncards == 0:
|
|
for r in self.s.rows:
|
|
if not r.cards and self.s.talon.cards:
|
|
self.s.talon.flipMove()
|
|
self.s.talon.moveMove(1, r)
|
|
elif reserves_ncards == len(self.s.reserves):
|
|
if not self.demo:
|
|
self.playSample("droppair", priority=200)
|
|
for s in self.s.reserves:
|
|
s.moveMove(1, self.s.foundations[0], frames=4)
|
|
self.fillStack(stack)
|
|
self.leaveState(old_state)
|
|
|
|
|
|
class SuitElevens_RowStack(Elevens_RowStack):
|
|
def acceptsCards(self, from_stack, cards):
|
|
if not Elevens_RowStack.acceptsCards(self, from_stack, cards):
|
|
return False
|
|
return cards[0].suit == self.cards[0].suit
|
|
|
|
class SuitElevens_Reserve(Elevens_Reserve):
|
|
def acceptsCards(self, from_stack, cards):
|
|
if not Elevens_Reserve.acceptsCards(self, from_stack, cards):
|
|
return False
|
|
for r in self.game.s.reserves:
|
|
if r.cards and r.cards[0].suit != cards[0].suit:
|
|
return False
|
|
return True
|
|
|
|
class SuitElevens(Elevens):
|
|
RowStack_Class = SuitElevens_RowStack
|
|
Reserve_Class = SuitElevens_Reserve
|
|
def createGame(self):
|
|
Elevens.createGame(self, rows=3, cols=5)
|
|
|
|
|
|
# /***********************************************************************
|
|
# // Fifteens
|
|
# ************************************************************************/
|
|
|
|
class Fifteens_RowStack(Elevens_RowStack):
|
|
ACCEPTED_SUM = 13
|
|
def acceptsCards(self, from_stack, cards):
|
|
if not Elevens_RowStack.acceptsCards(self, from_stack, cards):
|
|
return False
|
|
return cards[0].rank < 9 and self.cards[0] < 9
|
|
|
|
|
|
class Fifteens_Reserve(ReserveStack):
|
|
def updateText(self):
|
|
if self.game.preview > 1 or self.texts.misc is None:
|
|
return
|
|
t = ''
|
|
if self.cards:
|
|
ranks = [c.rank for c in self.cards]
|
|
for r in (9, JACK, QUEEN, KING):
|
|
if r in ranks:
|
|
break
|
|
else:
|
|
n = sum([i+1 for i in ranks])
|
|
t = str(n)
|
|
self.texts.misc.config(text=t)
|
|
|
|
|
|
class Fifteens(Elevens):
|
|
Hint_Class = None
|
|
|
|
RowStack_Class = Fifteens_RowStack
|
|
Reserve_Class = StackWrapper(Fifteens_Reserve, max_cards=UNLIMITED_CARDS)
|
|
|
|
def createGame(self):
|
|
Elevens.createGame(self, rows=4, cols=4, reserves=1, texts=True)
|
|
|
|
def _dropReserve(self):
|
|
reserve = self.s.reserves[0]
|
|
if not self.demo:
|
|
self.playSample("droppair", priority=200)
|
|
while reserve.cards:
|
|
reserve.moveMove(1, self.s.foundations[0], frames=4)
|
|
self.fillStack()
|
|
|
|
def fillStack(self, stack=None):
|
|
old_state = self.enterState(self.S_FILL)
|
|
reserve = self.s.reserves[0]
|
|
if len(reserve.cards) == 0:
|
|
for r in self.s.rows:
|
|
if not r.cards and self.s.talon.cards:
|
|
self.s.talon.flipMove()
|
|
self.s.talon.moveMove(1, r)
|
|
else:
|
|
reserve_ranks = [c.rank for c in reserve.cards]
|
|
reserve_ranks.sort()
|
|
if (9 in reserve_ranks or JACK in reserve_ranks
|
|
or QUEEN in reserve_ranks or KING in reserve_ranks):
|
|
if reserve_ranks == [9, JACK, QUEEN, KING]:
|
|
self._dropReserve()
|
|
else:
|
|
reserve_sum = sum([c.rank+1 for c in reserve.cards])
|
|
if reserve_sum == 15:
|
|
self._dropReserve()
|
|
self.leaveState(old_state)
|
|
|
|
|
|
# /***********************************************************************
|
|
# // Triple Alliance
|
|
# ************************************************************************/
|
|
|
|
class TripleAlliance_Reserve(ReserveStack):
|
|
def acceptsCards(self, from_stack, cards):
|
|
if not ReserveStack.acceptsCards(self, from_stack, cards):
|
|
return False
|
|
r_ranks = []
|
|
for r in self.game.s.reserves:
|
|
if r.cards:
|
|
r_ranks.append(r.cards[0].rank)
|
|
if not r_ranks:
|
|
return True
|
|
r_ranks.append(cards[0].rank)
|
|
r_ranks.sort()
|
|
if len(r_ranks) == 2:
|
|
return r_ranks[1]-r_ranks[0] in (1, 12)
|
|
for i in range(3):
|
|
j, k = (i+1)%3, (i+2)%3
|
|
if ((r_ranks[i]+1) % 13 == r_ranks[j] and
|
|
(r_ranks[j]+1) % 13 == r_ranks[k]):
|
|
return True
|
|
return False
|
|
|
|
|
|
class TripleAlliance(Game):
|
|
|
|
def createGame(self):
|
|
|
|
l, s = Layout(self), self.s
|
|
w0 = l.XS+5*l.XOFFSET
|
|
self.setSize(l.XM+5*w0, l.YM+5*l.YS)
|
|
|
|
x, y = l.XM, l.YM
|
|
for i in range(3):
|
|
s.reserves.append(TripleAlliance_Reserve(x, y, self))
|
|
x += l.XS
|
|
x, y = self.width-l.XS, l.YM
|
|
s.foundations.append(AbstractFoundationStack(x, y, self, suit=ANY_SUIT,
|
|
max_move=0, max_accept=0, max_cards=52))
|
|
y = l.YM+l.YS
|
|
nstacks = 0
|
|
for i in range(4):
|
|
x = l.XM
|
|
for j in range(5):
|
|
stack = BasicRowStack(x, y, self, max_accept=0)
|
|
s.rows.append(stack)
|
|
stack.CARD_XOFFSET, stack.CARD_YOFFSET = l.XOFFSET, 0
|
|
x += w0
|
|
nstacks += 1
|
|
if nstacks >= 18:
|
|
break
|
|
y += l.YS
|
|
|
|
x, y = self.width-l.XS, self.height-l.YS
|
|
s.talon = InitialDealTalonStack(x, y, self)
|
|
|
|
l.defaultStackGroups()
|
|
|
|
def startGame(self):
|
|
self.s.talon.dealRow(frames=0)
|
|
self.s.talon.dealRow(frames=0)
|
|
self.startDealSample()
|
|
self.s.talon.dealRowAvail()
|
|
|
|
def fillStack(self, stack):
|
|
for r in self.s.reserves:
|
|
if not r.cards:
|
|
return
|
|
if not self.demo:
|
|
self.playSample("droppair", priority=200)
|
|
old_state = self.enterState(self.S_FILL)
|
|
for r in self.s.reserves:
|
|
r.moveMove(1, self.s.foundations[0])
|
|
self.leaveState(old_state)
|
|
|
|
def isGameWon(self):
|
|
return len(self.s.foundations[0].cards) == 51
|
|
|
|
|
|
# register the game
|
|
registerGame(GameInfo(38, Pyramid, "Pyramid",
|
|
GI.GT_PAIRING_TYPE, 1, 2, GI.SL_MOSTLY_LUCK))
|
|
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(592, Giza, "Giza",
|
|
GI.GT_PAIRING_TYPE | GI.GT_OPEN, 1, 0, GI.SL_BALANCED))
|
|
registerGame(GameInfo(593, Thirteens, "Thirteens",
|
|
GI.GT_PAIRING_TYPE, 1, 0, GI.SL_LUCK))
|
|
registerGame(GameInfo(594, Elevens, "Elevens",
|
|
GI.GT_PAIRING_TYPE, 1, 0, GI.SL_LUCK))
|
|
registerGame(GameInfo(595, ElevensToo, "Elevens Too",
|
|
GI.GT_PAIRING_TYPE, 1, 0, GI.SL_LUCK))
|
|
registerGame(GameInfo(596, SuitElevens, "Suit Elevens",
|
|
GI.GT_PAIRING_TYPE, 1, 0, GI.SL_LUCK))
|
|
registerGame(GameInfo(597, Fifteens, "Fifteens",
|
|
GI.GT_PAIRING_TYPE, 1, 0, GI.SL_MOSTLY_LUCK))
|
|
registerGame(GameInfo(619, TripleAlliance, "Triple Alliance",
|
|
GI.GT_PAIRING_TYPE, 1, 0, GI.SL_MOSTLY_SKILL))
|
|
|
|
|