mirror of
https://github.com/shlomif/PySolFC.git
synced 2025-04-05 00:02:29 -04:00
Added Demons and Thieves game.
This commit is contained in:
parent
b40ffe8981
commit
fdddc014e0
4 changed files with 219 additions and 9 deletions
34
html-src/rules/demonsandthieves.html
Normal file
34
html-src/rules/demonsandthieves.html
Normal file
|
@ -0,0 +1,34 @@
|
|||
<h1>Demons and Thieves</h1>
|
||||
<p>
|
||||
Forty Thieves type. 2 decks. 2 redeals.
|
||||
|
||||
<h3>Object</h3>
|
||||
<p>
|
||||
Move all cards to the foundations.
|
||||
|
||||
<h3>Rules</h3>
|
||||
<p>
|
||||
There are nine tableau piles, divided into two sets. The first set of tableau
|
||||
piles contains four piles, each with a single card dealt to it, and are built
|
||||
down by alternate color. The second set consists of five piles, with eight
|
||||
cards dealt to each, and are built down by same suit. An additional thirteen
|
||||
cards are dealt to a reserve pile, with only the top card face-up, and a single
|
||||
card is dealt to a foundation, determining the base rank of the foundations.
|
||||
<p>
|
||||
Any card or valid sequence can be moved between tableau piles. A sequence can
|
||||
be moved from one set to the other, regardless of whether it matches the rules
|
||||
of the piles it moved to, but in this case, the sequence cannot be moved further.
|
||||
Cards from the reserve can be moved to an appropriate foundation or tableau pile
|
||||
at any time.
|
||||
<p>
|
||||
Cards are dealt from the talon one at a time, and can be moved to the tableau or
|
||||
foundations. You have two redeals, so you can go through the deck three times.
|
||||
<p>
|
||||
The foundations are built up by same suit, starting from the rank dealt to the
|
||||
first foundation at the start of the game, wrapping from king to ace as necessary.
|
||||
The game is won if all cards are moved to the foundations.
|
||||
|
||||
<h3>History</h3>
|
||||
<p>
|
||||
Demons and Thieves is a combination of Canfield-type and Forty Thieves-type
|
||||
games. It was invented by Thomas Warfield.
|
|
@ -400,12 +400,12 @@ class GI:
|
|||
# Ace of Hearts, Agnes Three, Antares, Avenue, Baker's Fan,
|
||||
# Baker's Spider, Bedeviled, Binding, Black Holes,
|
||||
# Black Spider, California, Cascade, Club, Color Cell,
|
||||
# Cornelius, Demons and Thieves, Desert Fox, Deuces and Queens,
|
||||
# Double Antares, Double Antarctica, Double Arctica,
|
||||
# Double Baker's Spider, Double Cascade, Double Line 8,
|
||||
# Double Majesty, Double Spidercells, Doublet Cell 5, Doubt,
|
||||
# Dream Fan, Dumfries Cell, Falcon Wing, Fan Nine, Fanny 6,
|
||||
# Four By Ten, FreeCell AK, Gaps Alter, Gaps Diff, George V,
|
||||
# Cornelius, Desert Fox, Deuces and Queens, Double Antares,
|
||||
# Double Antarctica, Double Arctica, Double Baker's Spider,
|
||||
# Double Cascade, Double Line 8, Double Majesty,
|
||||
# Double Spidercells, Doublet Cell 5, Doubt, Dream Fan,
|
||||
# Dumfries Cell, Falcon Wing, Fan Nine, Fanny 6, Four By Ten,
|
||||
# FreeCell AK, Gaps Alter, Gaps Diff, George V,
|
||||
# Grandmother's Clock, In a Frame, Inverted FreeCell, Kings,
|
||||
# Klondike FreeCell, La Cabane, La Double Entente,
|
||||
# Little Gazette, Magic FreeCell, Mini Gaps, Montreal,
|
||||
|
@ -427,7 +427,7 @@ class GI:
|
|||
415, 416, 425, 451, 453, 461, 464, 466, 467, 476, 480, 484,
|
||||
511, 512, 513, 516, 561, 610, 625, 629, 631, 638, 641, 647,
|
||||
650, 655, 678, 684, 734, 751, 784, 825, 829, 834, 837, 844,
|
||||
862, 867, 880, 901,
|
||||
862, 867, 880, 889, 901,
|
||||
)),
|
||||
|
||||
# xpat2 1.06 (we have 14 out of 16 games)
|
||||
|
@ -470,7 +470,7 @@ class GI:
|
|||
("Peter Voke", (876,)),
|
||||
("Thomas Warfield", (189, 264, 300, 320, 336, 337, 359,
|
||||
415, 427, 458, 495, 496, 497, 508,
|
||||
800, 814, 820, 825,)),
|
||||
800, 814, 820, 825, 889,)),
|
||||
("Mary Whitmore Jones", (421, 624,)),
|
||||
)
|
||||
|
||||
|
@ -551,7 +551,7 @@ class GI:
|
|||
tuple(range(22217, 22219))),
|
||||
('fc-2.14', tuple(range(811, 827))),
|
||||
('fc-2.15', tuple(range(827, 855)) + tuple(range(22400, 22407))),
|
||||
('dev', tuple(range(855, 889)))
|
||||
('dev', tuple(range(855, 890)))
|
||||
)
|
||||
|
||||
# deprecated - the correct way is to or a GI.GT_XXX flag
|
||||
|
|
|
@ -39,6 +39,7 @@ from . import capricieuse # noqa: F401
|
|||
from . import crossword # noqa: F401
|
||||
from . import curdsandwhey # noqa: F401
|
||||
from . import daddylonglegs # noqa: F401
|
||||
from . import demonsandthieves # noqa: F401
|
||||
from . import dieboesesieben # noqa: F401
|
||||
from . import diplomat # noqa: F401
|
||||
from . import doublets # noqa: F401
|
||||
|
|
175
pysollib/games/demonsandthieves.py
Normal file
175
pysollib/games/demonsandthieves.py
Normal file
|
@ -0,0 +1,175 @@
|
|||
from pysollib.game import Game
|
||||
from pysollib.gamedb import GI, GameInfo, registerGame
|
||||
from pysollib.layout import Layout
|
||||
from pysollib.pysoltk import MfxCanvasText
|
||||
from pysollib.stack import \
|
||||
AC_RowStack, \
|
||||
OpenStack, \
|
||||
SS_FoundationStack, \
|
||||
SS_RowStack, \
|
||||
WasteStack, \
|
||||
WasteTalonStack
|
||||
from pysollib.util import ANY_RANK, RANKS
|
||||
|
||||
|
||||
# ************************************************************************
|
||||
# * Demons and Thieves
|
||||
# ************************************************************************
|
||||
|
||||
class DemonsAndThieves_StackMethods:
|
||||
def acceptsCards(self, from_stack, cards):
|
||||
if not self.basicAcceptsCards(from_stack, cards):
|
||||
return False
|
||||
# [topcard + bottomcard] must be an acceptable sequence
|
||||
if (self.cards and not
|
||||
self._isAcceptableSequence([self.cards[-1]] + [cards[0]])):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class DemonsAndThieves_AC_RowStack(DemonsAndThieves_StackMethods, AC_RowStack):
|
||||
pass
|
||||
|
||||
|
||||
class DemonsAndThieves_SS_RowStack(DemonsAndThieves_StackMethods, SS_RowStack):
|
||||
pass
|
||||
|
||||
|
||||
class DemonsAndThieves(Game):
|
||||
def createGame(self, max_rounds=3, num_deal=1,
|
||||
text=True, round_text=True, dir=-1):
|
||||
# create layout
|
||||
lay, s = Layout(self), self.s
|
||||
decks = self.gameinfo.decks
|
||||
|
||||
# (piles up to 20 cards are playable in default window size)
|
||||
h = max(3 * lay.YS, lay.YS + 13 * 10)
|
||||
if round_text:
|
||||
h += lay.TEXT_HEIGHT
|
||||
self.setSize(
|
||||
lay.XM + (2 + max(9.5, 4 * decks)) * lay.XS + lay.XM,
|
||||
lay.YM + lay.YS + lay.TEXT_HEIGHT + h)
|
||||
|
||||
# extra settings
|
||||
self.base_card = None
|
||||
|
||||
# create stacks
|
||||
x, y = lay.XM, lay.YM
|
||||
if round_text:
|
||||
y += lay.TEXT_HEIGHT
|
||||
s.talon = WasteTalonStack(x, y, self,
|
||||
max_rounds=max_rounds, num_deal=num_deal)
|
||||
lay.createText(s.talon, "s")
|
||||
if round_text:
|
||||
lay.createRoundText(s.talon, 'n')
|
||||
x += lay.XS
|
||||
s.waste = WasteStack(x, y, self)
|
||||
lay.createText(s.waste, "s")
|
||||
x += lay.XM
|
||||
y = lay.YM
|
||||
if round_text:
|
||||
y += lay.TEXT_HEIGHT
|
||||
for i in range(4):
|
||||
for j in range(decks):
|
||||
x += lay.XS
|
||||
s.foundations.append(SS_FoundationStack(x, y, self, i,
|
||||
mod=13, max_move=0))
|
||||
if text:
|
||||
if 10 > 4 * decks:
|
||||
tx, ty, ta, tf = lay.getTextAttr(None, "se")
|
||||
tx, ty = x + tx + lay.XM, y + ty
|
||||
else:
|
||||
tx, ty, ta, tf = lay.getTextAttr(None, "s")
|
||||
tx, ty = x + tx, y + ty
|
||||
font = self.app.getFont("canvas_default")
|
||||
self.texts.info = MfxCanvasText(self.canvas, tx, ty,
|
||||
anchor=ta, font=font)
|
||||
x, y = lay.XM, lay.YM + lay.YS + lay.TEXT_HEIGHT
|
||||
if round_text:
|
||||
y += lay.TEXT_HEIGHT
|
||||
s.reserves.append(OpenStack(x, y, self))
|
||||
s.reserves[0].CARD_YOFFSET = 10
|
||||
x, y = lay.XM + 2 * lay.XS + lay.XM, lay.YM + lay.YS
|
||||
if round_text:
|
||||
y += lay.TEXT_HEIGHT
|
||||
if text:
|
||||
y += lay.TEXT_HEIGHT
|
||||
for i in range(4):
|
||||
s.rows.append(DemonsAndThieves_AC_RowStack(x, y, self,
|
||||
base_rank=ANY_RANK,
|
||||
dir=dir))
|
||||
x += lay.XS
|
||||
x += (lay.XS * .5)
|
||||
for i in range(5):
|
||||
s.rows.append(DemonsAndThieves_SS_RowStack(x, y, self,
|
||||
base_rank=ANY_RANK,
|
||||
dir=dir))
|
||||
x += lay.XS
|
||||
|
||||
# define stack-groups
|
||||
lay.defaultStackGroups()
|
||||
|
||||
#
|
||||
# game extras
|
||||
#
|
||||
|
||||
def updateText(self):
|
||||
if self.preview > 1:
|
||||
return
|
||||
if not self.texts.info:
|
||||
return
|
||||
if not self.base_card:
|
||||
t = ""
|
||||
else:
|
||||
t = RANKS[self.base_card.rank]
|
||||
self.texts.info.config(text=t)
|
||||
|
||||
#
|
||||
# game overrides
|
||||
#
|
||||
|
||||
def startGame(self):
|
||||
self.startDealSample()
|
||||
self.base_card = None
|
||||
self.updateText()
|
||||
for i in range(7):
|
||||
self.s.talon.dealRow(rows=self.s.rows[4:9], flip=1, frames=0)
|
||||
self.s.talon.dealRow()
|
||||
# deal base_card to Foundations, update foundations cap.base_rank
|
||||
self.base_card = self.s.talon.getCard()
|
||||
for s in self.s.foundations:
|
||||
s.cap.base_rank = self.base_card.rank
|
||||
n = self.base_card.suit * self.gameinfo.decks
|
||||
if self.s.foundations[n].cards:
|
||||
assert self.gameinfo.decks > 1
|
||||
n = n + 1
|
||||
self.flipMove(self.s.talon)
|
||||
self.moveMove(1, self.s.talon, self.s.foundations[n])
|
||||
self.updateText()
|
||||
# fill the Reserve
|
||||
for i in range(13):
|
||||
self.moveMove(
|
||||
1, self.s.talon, self.s.reserves[0], frames=4, shadow=0)
|
||||
if self.s.reserves[0].canFlipCard():
|
||||
self.flipMove(self.s.reserves[0])
|
||||
|
||||
self.s.talon.dealCards()
|
||||
|
||||
shallHighlightMatch = Game._shallHighlightMatch_ACW
|
||||
|
||||
def _restoreGameHook(self, game):
|
||||
self.base_card = self.cards[game.loadinfo.base_card_id]
|
||||
for s in self.s.foundations:
|
||||
s.cap.base_rank = self.base_card.rank
|
||||
|
||||
def _loadGameHook(self, p):
|
||||
self.loadinfo.addattr(base_card_id=None) # register extra load var.
|
||||
self.loadinfo.base_card_id = p.load()
|
||||
|
||||
def _saveGameHook(self, p):
|
||||
p.dump(self.base_card.id)
|
||||
|
||||
|
||||
# register the game
|
||||
registerGame(GameInfo(889, DemonsAndThieves, "Demons and Thieves",
|
||||
GI.GT_FORTY_THIEVES, 2, 2, GI.SL_MOSTLY_SKILL))
|
Loading…
Add table
Reference in a new issue