From 7b85049f7ca77c385178ec79cd1d629c3b39a5c2 Mon Sep 17 00:00:00 2001 From: Joe R Date: Tue, 25 Mar 2025 17:42:31 -0400 Subject: [PATCH] Add Flamboyant game --- html-src/rules/flamboyant.html | 22 ++++++++ pysollib/gamedb.py | 2 +- pysollib/games/interlock.py | 94 +++++++++++++++++++++++++++++++++- 3 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 html-src/rules/flamboyant.html diff --git a/html-src/rules/flamboyant.html b/html-src/rules/flamboyant.html new file mode 100644 index 00000000..59b9c586 --- /dev/null +++ b/html-src/rules/flamboyant.html @@ -0,0 +1,22 @@ +

Flamboyant

+

+Fan game type. 1 deck. No redeal. + +

Object

+

+Move all cards to the foundations. + +

Rules

+

+The tableau piles are built in seventeen sets of three cards, with +two in the back row face-down, and a third face-up card overlapping +them. The final card is dealt separately. +

+The tableau piles are built down by alternate color, with any face-up card +or valid sequence of cards being movable. Once the top card is moved, +the two below it can be flipped face-up and played. Empty piles cannot +be filled, except for the final single card, where a king or sequence of +cards starting with a king can be played. +

+Foundations are built up in suit from Ace to King. The game is won +when all cards are moved to the foundations. diff --git a/pysollib/gamedb.py b/pysollib/gamedb.py index c68c89c0..4bfee226 100644 --- a/pysollib/gamedb.py +++ b/pysollib/gamedb.py @@ -603,7 +603,7 @@ class GI: tuple(range(19000, 19012)) + tuple(range(22303, 22311)) + tuple(range(22353, 22361))), ('fc-3.1', tuple(range(961, 971))), - ('dev', tuple(range(971, 979)) + tuple(range(5419, 5421)) + + ('dev', tuple(range(971, 980)) + tuple(range(5419, 5421)) + tuple(range(16683, 16686)) + tuple(range(18005, 18007)) + (44, 526, 22399,)), ) diff --git a/pysollib/games/interlock.py b/pysollib/games/interlock.py index 812e5745..f483357a 100644 --- a/pysollib/games/interlock.py +++ b/pysollib/games/interlock.py @@ -36,7 +36,7 @@ from pysollib.stack import \ WasteTalonStack, \ Yukon_AC_RowStack, \ getNumberOfFreeStacks -from pysollib.util import ANY_RANK, KING +from pysollib.util import ANY_RANK, KING, NO_RANK # ************************************************************************ @@ -301,6 +301,96 @@ class Sarlacc(Interlock): self.s.talon.dealRow(rows=self.s.rows[42:]) +# ************************************************************************ +# * Flamboyant +# ************************************************************************ + +class Flamboyant_RowStack(Interlock_RowStack): + STEP = ((2,), (1,), (), (2,), (1,), (), (2,), (1,), (), + (2,), (1,), (), (2,), (1,), (), (2,), (1,), (), + (2,), (1,), (), (2,), (1,), (), (2,), (1,), (), + (2,), (1,), (), (2,), (1,), (), (2,), (1,), (), + (2,), (1,), (), (2,), (1,), (), (2,), (1,), (), + (2,), (1,), (), (2,), (1,), ()) + + +class Flamboyant(Interlock): + RowStack_Class = Flamboyant_RowStack + Talon_Class = InitialDealTalonStack + + PLAYCARDS = 8 + + def _createTableauPiece(self, layout, x0, y0): + for i in range(2): + x = x0 + i * layout.XS // 2 + y = y0 + i * layout.YS // 4 + for j in range(2 - i): + stack = self.RowStack_Class(x, y, self, base_rank=NO_RANK) + self.s.rows.append(stack) + x = x + layout.XS + + def createGame(self): + lay, s = Layout(self), self.s + w = (14.5 * lay.XS) + lay.XM + h = (4.2 * lay.YS) + (self.PLAYCARDS * lay.YOFFSET * 3) + lay.YM + self.setSize(w, h) + + x, y = lay.XM, lay.YM + # create stacks + for i in range(2): + for i in range(6): + self._createTableauPiece(lay, x, y) + x += (2.25 * lay.XS) + y += (1.4 * lay.YS) + (self.PLAYCARDS * lay.YOFFSET) + x = lay.XM + for i in range(5): + self._createTableauPiece(lay, x, y) + x += (2.25 * lay.XS) + x += (.5 * lay.XS) + self.s.rows.append(AC_RowStack(x, y, self, base_rank=KING)) + + x, y = lay.XM + (13.5 * lay.XS), lay.YM + for i in range(4): + s.foundations.append(SS_FoundationStack(x, y, self, i)) + y += lay.YS + s.talon = InitialDealTalonStack(x, y, self) + + lay.defaultStackGroups() + + def startGame(self): + self.startDealSample() + backrows = [] + frontrows = [] + for i, item in enumerate(self.s.rows): + if (i + 1) % 3 == 0 or i >= 51: + frontrows.append(item) + else: + backrows.append(item) + self.s.talon.dealRow(rows=backrows, flip=0, frames=0) + self.s.talon.dealRow(rows=frontrows) + + def _getClosestStack(self, cx, cy, stacks, dragstack): + closest, cdist = None, 999999999 + # Since we only compare distances, + # we don't bother to take the square root. + for stack in stacks: + # Flamboyant uses different logic to determine back row + # stacks. + backrows = [] + frontrows = [] + for i, item in enumerate(self.s.rows): + if (i + 1) % 3 == 0: + frontrows.append(item) + else: + backrows.append(item) + if len(stack.cards) == 0 and stack in frontrows: + continue + dist = (stack.x - cx)**2 + (stack.y - cy)**2 + if dist < cdist: + closest, cdist = stack, dist + return closest + + # register the game registerGame(GameInfo(852, Guardian, "Guardian", GI.GT_KLONDIKE, 1, -1, GI.SL_BALANCED)) @@ -310,3 +400,5 @@ registerGame(GameInfo(939, LoveADuck, "Love a Duck", GI.GT_YUKON | GI.GT_OPEN, 1, 0, GI.SL_MOSTLY_SKILL)) registerGame(GameInfo(946, Sarlacc, "Sarlacc", GI.GT_FREECELL | GI.GT_OPEN, 1, 0, GI.SL_MOSTLY_SKILL)) +registerGame(GameInfo(979, Flamboyant, "Flamboyant", + GI.GT_FAN_TYPE, 1, 0, GI.SL_BALANCED))