1
0
Fork 0
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:
Joe R 2023-01-31 19:48:09 -05:00
parent b40ffe8981
commit fdddc014e0
4 changed files with 219 additions and 9 deletions

View 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.

View file

@ -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

View file

@ -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

View 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))