#!/usr/bin/env python # -*- mode: python; coding: utf-8; -*- # ---------------------------------------------------------------------------## # # Copyright (C) 1998-2003 Markus Franz Xaver Johannes Oberhumer # Copyright (C) 2003 Mt. Hood Playing Card Co. # Copyright (C) 2005-2009 Skomoroh # # 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 3 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. If not, see . # # ---------------------------------------------------------------------------## # Imports import math # PySol imports from pysollib.mygettext import _ from pysollib.gamedb import registerGame, GameInfo, GI from pysollib.mfxutil import kwdefault from pysollib.game import Game from pysollib.layout import Layout from pysollib.hint import DefaultHint, CautiousDefaultHint from pysollib.pysoltk import MfxCanvasText from pysollib.util import ANY_RANK, ANY_SUIT, NO_RANK, UNLIMITED_ACCEPTS, \ UNLIMITED_MOVES from pysollib.stack import \ AC_RowStack, \ AbstractFoundationStack, \ InitialDealTalonStack, \ ReserveStack, \ SS_FoundationStack, \ StackWrapper, \ WasteStack, \ WasteTalonStack, \ OpenStack # ************************************************************************ # * Hex A Deck Foundation Stacks # ************************************************************************ class HexADeck_FoundationStack(SS_FoundationStack): def __init__(self, x, y, game, suit, **cap): kwdefault(cap, max_move=0, max_cards=12) SS_FoundationStack.__init__(self, x, y, game, suit, **cap) class HexATrump_Foundation(HexADeck_FoundationStack): def acceptsCards(self, from_stack, cards): if not self.basicAcceptsCards(from_stack, cards): return 0 for s in self.game.s.foundations[:3]: if len(s.cards) != 16: return 0 return 1 class Merlins_Foundation(AbstractFoundationStack): def __init__(self, x, y, game, suit, **cap): kwdefault(cap, mod=16, dir=0, base_rank=NO_RANK, max_move=0) AbstractFoundationStack.__init__(self, x, y, game, suit, **cap) def acceptsCards(self, from_stack, cards): if not AbstractFoundationStack.acceptsCards(self, from_stack, cards): return 0 if not self.cards: return 1 stack_dir = self.game.getFoundationDir() if stack_dir == 0: card_dir = (cards[0].rank - self.cards[-1].rank) % self.cap.mod return card_dir in (1, 15) else: return (self.cards[-1].rank + stack_dir) % self.cap.mod \ == cards[0].rank # ************************************************************************ # * Hex A Deck Row Stacks # ************************************************************************ class HexADeck_OpenStack(OpenStack): def __init__(self, x, y, game, yoffset, **cap): kwdefault(cap, max_move=UNLIMITED_MOVES, max_accept=UNLIMITED_ACCEPTS, dir=-1) OpenStack.__init__(self, x, y, game, **cap) self.CARD_YOFFSET = yoffset def isRankSequence(self, cards, dir=None): if not dir: dir = self.cap.dir c1 = cards[0] for c2 in cards[1:]: if not c1.rank + dir == c2.rank: return 0 c1 = c2 return 1 def isAlternateColorSequence(self, cards, dir=None): if not dir: dir = self.cap.dir c1 = cards[0] for c2 in cards[1:]: if (c1.color < 2 and c1.color == c2.color or not c1.rank + dir == c2.rank): return 0 c1 = c2 return 1 def isSuitSequence(self, cards, dir=None): if not dir: dir = self.cap.dir c1 = cards[0] for c2 in cards[1:]: if not ((c1.color == 2 or c2.color == 2 or c1.suit == c2.suit) and c1.rank + dir == c2.rank): return 0 c1 = c2 return 1 class HexADeck_RK_RowStack(HexADeck_OpenStack): def acceptsCards(self, from_stack, cards): if (not self.basicAcceptsCards(from_stack, cards) or not self.isRankSequence(cards)): return 0 if not self.cards: return cards[0].rank == 15 or self.cap.base_rank == ANY_RANK return self.isRankSequence([self.cards[-1], cards[0]]) def canMoveCards(self, cards): return (self.basicCanMoveCards(cards) and self.isRankSequence(cards)) class HexADeck_AC_RowStack(HexADeck_OpenStack): def acceptsCards(self, from_stack, cards): if (not self.basicAcceptsCards(from_stack, cards) or not self.isAlternateColorSequence(cards)): return 0 if not self.cards: return cards[0].rank == 15 or self.cap.base_rank == ANY_RANK return self.isAlternateColorSequence([self.cards[-1], cards[0]]) def canMoveCards(self, cards): return (self.basicCanMoveCards(cards) and self.isAlternateColorSequence(cards)) class HexADeck_SS_RowStack(HexADeck_OpenStack): def acceptsCards(self, from_stack, cards): if (not self.basicAcceptsCards(from_stack, cards) or not self.isSuitSequence(cards)): return 0 if not self.cards: return cards[0].rank == 15 or self.cap.base_rank == ANY_RANK return self.isSuitSequence([self.cards[-1], cards[0]]) def canMoveCards(self, cards): return (self.basicCanMoveCards(cards) and self.isSuitSequence(cards)) class Bits_RowStack(ReserveStack): def acceptsCards(self, from_stack, cards): if not self.basicAcceptsCards(from_stack, cards): return 0 stackcards = self.cards if stackcards or cards[0].suit == 4: return 0 i = int(self.id // 4) for r in self.game.s.rows[i * 4:self.id]: if not r.cards: return 0 return ((self.game.s.foundations[i].cards[-1].rank + 1 >> (self.id % 4)) % 2 == (cards[0].rank + 1) % 2) class Bytes_RowStack(ReserveStack): def acceptsCards(self, from_stack, cards): if not self.basicAcceptsCards(from_stack, cards): return 0 stackcards = self.cards if stackcards or cards[0].suit == 4: return 0 id = self.id - 16 i = int(id // 2) for r in self.game.s.rows[16 + i * 2:self.id]: if not r.cards: return 0 return self.game.s.foundations[i].cards[-1].rank == cards[0].rank class HexAKlon_RowStack(AC_RowStack): def acceptsCards(self, from_stack, cards): if not self.basicAcceptsCards(from_stack, cards): return 0 stackcards = self.cards if stackcards: if (stackcards[-1].suit == 4 or cards[0].suit == 4): return 1 return AC_RowStack.acceptsCards(self, from_stack, cards) class HexADeck_ACRowStack(AC_RowStack): def acceptsCards(self, from_stack, cards): if not self.basicAcceptsCards(from_stack, cards): return 0 stackcards = self.cards if stackcards: if (stackcards[-1].suit == 4 or cards[0].suit == 4): return stackcards[-1].rank == cards[0].rank + 1 return AC_RowStack.acceptsCards(self, from_stack, cards) class Familiar_ReserveStack(ReserveStack): def acceptsCards(self, from_stack, cards): if not ReserveStack.acceptsCards(self, from_stack, cards): return 0 # Only take Wizards return cards[0].suit == 4 def getBottomImage(self): return self.game.app.images.getSuitBottom(4) class Merlins_BraidStack(OpenStack): def __init__(self, x, y, game): OpenStack.__init__(self, x, y, game) self.CARD_YOFFSET = self.game.app.images.CARD_YOFFSET # use a sine wave for the x offsets self.CARD_XOFFSET = [] j = 1 for i in range(20): self.CARD_XOFFSET.append(int(math.sin(j) * 20)) j = j + .9 class Merlins_RowStack(ReserveStack): def fillStack(self): if not self.cards and self.game.s.braid.cards: self.game.moveMove(1, self.game.s.braid, self) def getBottomImage(self): return self.game.app.images.getBraidBottom() class Merlins_ReserveStack(ReserveStack): def acceptsCards(self, from_stack, cards): if from_stack is self.game.s.braid or from_stack in self.game.s.rows: return 0 return ReserveStack.acceptsCards(self, from_stack, cards) def getBottomImage(self): return self.game.app.images.getTalonBottom() # ************************************************************************ # * # ************************************************************************ class AbstractHexADeckGame(Game): RANKS = (_("Ace"), "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "10") class Merlins_Hint(DefaultHint): pass # ************************************************************************ # * Bits n Bytes # ************************************************************************ class BitsNBytes(Game): # # Game layout # def createGame(self): l, s = Layout(self), self.s font = self.app.getFont("canvas_default") # Set window size self.setSize(l.XM * 4 + l.XS * 8, l.YM + l.YS * 4) # Create bit stacks self.bit_texts = [] y = l.YM for j in range(4): x = l.XM * 4 + l.XS * 7 for i in range(4): stack = Bits_RowStack(x, y, self, max_cards=1, max_accept=1, base_suit=j, max_move=0) s.rows.append(stack) stack.texts.misc = MfxCanvasText(self.canvas, x + l.CW // 2, y + l.CH // 2, anchor="center", font=font) x = x - l.XS y = y + l.YS # Create byte stacks y = l.YM for j in range(4): x = l.XM * 3 + l.XS * 3 for i in range(2): s.rows.append( Bytes_RowStack( x, y, self, max_cards=1, max_accept=1, base_suit=ANY_SUIT, max_move=0)) x = x - l.XS y = y + l.YS # Create foundations x = l.XM * 2 + l.XS y = l.YM for i in range(4): s.foundations.append( SS_FoundationStack( x, y, self, i, mod=1, max_move=0, max_cards=1)) y = y + l.YS self.setRegion(s.rows, (0, 0, 999999, 999999)) # Create talon x = l.XM y = l.YM s.talon = WasteTalonStack(x, y, self, num_deal=2, max_rounds=2) l.createText(s.talon, "s") y += l.YS + l.TEXT_HEIGHT s.waste = WasteStack(x, y, self) l.createText(s.waste, "s") # Define stack groups l.defaultStackGroups() # # Game over rides # def updateText(self): if self.preview > 1: return for j in range(4): if not len(self.s.foundations[j].cards): break s = self.s.foundations[j].cards[-1].rank + 1 for i in range(4): stack = self.s.rows[i + j * 4] stack.texts.misc.config(text=str(s % 2)) s = int(s // 2) def _shuffleHook(self, cards): topcards, ranks = [None] * 4, [None] * 4 for c in cards[:]: if not c.suit == 4: if not topcards[c.suit]: haverank = 0 for i in range(4): if c.rank == ranks[i]: haverank = 1 if not haverank: topcards[c.suit] = c ranks[c.suit] = c.rank cards.remove(c) cards = topcards + cards cards.reverse() return cards def startGame(self): assert len(self.s.talon.cards) == 68 self.startDealSample() self.s.talon.dealRow(rows=self.s.foundations) self.s.talon.dealCards() def isGameWon(self): for s in self.s.rows: if not s.cards: return 0 return 1 def shallHighlightMatch(self, stack1, card1, stack2, card2): return 0 # ************************************************************************ # * Hex A Klon # ************************************************************************ class HexAKlon(Game): Hint_Class = CautiousDefaultHint Layout_Method = staticmethod(Layout.klondikeLayout) Talon_Class = WasteTalonStack Foundation_Class = SS_FoundationStack RowStack_Class = HexAKlon_RowStack # # Game layout # def createGame(self, max_rounds=-1, num_deal=1, **layout): l, s = Layout(self), self.s kwdefault(layout, rows=8, waste=1, playcards=20) self.Layout_Method(l, **layout) self.setSize(l.size[0], l.size[1]) # Create talon s.talon = self.Talon_Class( l.s.talon.x, l.s.talon.y, self, max_rounds=max_rounds, num_deal=num_deal) s.waste = WasteStack(l.s.waste.x, l.s.waste.y, self) # Create foundations for r in l.s.foundations[:4]: s.foundations.append( self.Foundation_Class( r.x, r.y, self, r.suit, mod=16, max_cards=16, max_move=1)) r = l.s.foundations[4] s.foundations.append( HexATrump_Foundation( r.x, r.y, self, 4, mod=4, max_move=0, max_cards=4, base_rank=ANY_RANK)) # Create rows for r in l.s.rows: s.rows.append( self.RowStack_Class( r.x, r.y, self, suit=ANY_SUIT, base_rank=ANY_RANK)) # Define stack groups l.defaultAll() # # Game over rides # def startGame(self): assert len(self.s.talon.cards) == 68 for i in range(len(self.s.rows)): self.s.talon.dealRow(rows=self.s.rows[i+1:], flip=0, frames=0) self.startDealSample() self.s.talon.dealRow() self.s.talon.dealCards() def shallHighlightMatch(self, stack1, card1, stack2, card2): return (card1.color != card2.color and (card1.rank + 1 == card2.rank or card2.rank + 1 == card1.rank)) # ************************************************************************ # * Hex A Klon by Threes # ************************************************************************ class HexAKlonByThrees(Game): Hint_Class = CautiousDefaultHint Layout_Method = staticmethod(Layout.klondikeLayout) Talon_Class = WasteTalonStack Foundation_Class = SS_FoundationStack RowStack_Class = HexAKlon_RowStack # # Game layout # def createGame(self, max_rounds=-1, num_deal=3, **layout): l, s = Layout(self), self.s kwdefault(layout, rows=8, waste=1, playcards=20) self.Layout_Method(l, **layout) self.setSize(l.size[0], l.size[1]) # Create talon s.talon = self.Talon_Class( l.s.talon.x, l.s.talon.y, self, max_rounds=max_rounds, num_deal=num_deal) s.waste = WasteStack(l.s.waste.x, l.s.waste.y, self) # Create foundations for r in l.s.foundations[:4]: s.foundations.append( self.Foundation_Class( r.x, r.y, self, r.suit, mod=16, max_cards=16, max_move=1)) r = l.s.foundations[4] s.foundations.append( HexATrump_Foundation( r.x, r.y, self, 4, mod=4, max_move=0, max_cards=4, base_rank=ANY_RANK)) # Create rows for r in l.s.rows: s.rows.append( self.RowStack_Class( r.x, r.y, self, suit=ANY_SUIT, base_rank=ANY_RANK)) # Define stack groups l.defaultAll() # # Game over rides # def startGame(self): assert len(self.s.talon.cards) == 68 for i in range(len(self.s.rows)): self.s.talon.dealRow(rows=self.s.rows[i+1:], flip=0, frames=0) self.startDealSample() self.s.talon.dealRow() self.s.talon.dealCards() def shallHighlightMatch(self, stack1, card1, stack2, card2): return (card1.color != card2.color and (card1.rank + 1 == card2.rank or card2.rank + 1 == card1.rank)) # ************************************************************************ # * King Only Hex A Klon # ************************************************************************ class KingOnlyHexAKlon(Game): Hint_Class = CautiousDefaultHint Layout_Method = staticmethod(Layout.klondikeLayout) Talon_Class = WasteTalonStack Foundation_Class = SS_FoundationStack RowStack_Class = HexAKlon_RowStack # # Game layout # def createGame(self, max_rounds=-1, num_deal=1, **layout): l, s = Layout(self), self.s kwdefault(layout, rows=8, waste=1, playcards=20) self.Layout_Method(l, **layout) self.setSize(l.size[0], l.size[1]) # Create talon s.talon = self.Talon_Class(l.s.talon.x, l.s.talon.y, self, max_rounds=max_rounds, num_deal=num_deal) s.waste = WasteStack(l.s.waste.x, l.s.waste.y, self) # Create foundations for r in l.s.foundations[:4]: s.foundations.append( self.Foundation_Class( r.x, r.y, self, r.suit, mod=16, max_cards=16, max_move=1)) r = l.s.foundations[4] s.foundations.append( HexATrump_Foundation( r.x, r.y, self, 4, mod=4, max_move=0, max_cards=4, base_rank=ANY_RANK)) # Create rows for r in l.s.rows: s.rows.append(self.RowStack_Class(r.x, r.y, self, suit=ANY_SUIT, base_rank=15)) # Define stack groups l.defaultAll() # # Game over rides # def startGame(self): assert len(self.s.talon.cards) == 68 for i in range(len(self.s.rows)): self.s.talon.dealRow(rows=self.s.rows[i+1:], flip=0, frames=0) self.startDealSample() self.s.talon.dealRow() self.s.talon.dealCards() def _shuffleHook(self, cards): basecard = [None] for c in cards[:]: if c.suit == 4: if basecard[0] is None: basecard[0] = c cards.remove(c) cards = basecard + cards cards.reverse() return cards def shallHighlightMatch(self, stack1, card1, stack2, card2): return (card1.color != card2.color and (card1.rank + 1 == card2.rank or card2.rank + 1 == card1.rank)) # ************************************************************************ # * Klondike Plus 16 # ************************************************************************ class KlondikePlus16(Game): Hint_Class = CautiousDefaultHint Layout_Method = staticmethod(Layout.klondikeLayout) Talon_Class = WasteTalonStack Foundation_Class = SS_FoundationStack RowStack_Class = HexAKlon_RowStack # # Game layout # def createGame(self, max_rounds=2, num_deal=1, **layout): l, s = Layout(self), self.s kwdefault(layout, rows=8, waste=1, playcards=20) self.Layout_Method(l, **layout) self.setSize(l.size[0], l.size[1]) # Create talon s.talon = self.Talon_Class(l.s.talon.x, l.s.talon.y, self, max_rounds=max_rounds, num_deal=num_deal) s.waste = WasteStack(l.s.waste.x, l.s.waste.y, self) # Create foundations for r in l.s.foundations: s.foundations.append( self.Foundation_Class( r.x, r.y, self, r.suit, mod=16, max_cards=16, max_move=1)) # Create rows for r in l.s.rows: s.rows.append(self.RowStack_Class(r.x, r.y, self, suit=ANY_SUIT, base_rank=15)) # Define stack groups l.defaultAll() # # Game over rides # def startGame(self): assert len(self.s.talon.cards) == 68 for i in range(len(self.s.rows)): self.s.talon.dealRow(rows=self.s.rows[i+1:], flip=0, frames=0) self.startDealSample() self.s.talon.dealRow() self.s.talon.dealCards() def shallHighlightMatch(self, stack1, card1, stack2, card2): return (card1.color != card2.color and (card1.rank + 1 == card2.rank or card2.rank + 1 == card1.rank)) # ************************************************************************ # * The Familiar # ************************************************************************ class TheFamiliar(Game): Hint_Class = CautiousDefaultHint Layout_Method = staticmethod(Layout.klondikeLayout) Talon_Class = WasteTalonStack Foundation_Class = SS_FoundationStack RowStack_Class = AC_RowStack # # Game layout # def createGame(self, max_rounds=2, num_deal=1, **layout): l, s = Layout(self), self.s kwdefault(layout, rows=8, waste=1, playcards=20) self.Layout_Method(l, **layout) self.setSize(l.size[0], l.size[1]) # Create talon s.talon = self.Talon_Class(l.s.talon.x, l.s.talon.y, self, max_rounds=max_rounds, num_deal=num_deal) s.waste = WasteStack(l.s.waste.x, l.s.waste.y, self) # Create foundations for r in l.s.foundations: s.foundations.append( self.Foundation_Class( r.x, r.y, self, r.suit, mod=16, max_cards=16, max_move=1)) # Create rows for r in l.s.rows: s.rows.append(self.RowStack_Class(r.x, r.y, self, suit=ANY_SUIT, base_rank=15)) # Create reserve x, y = l.XM, self.height - l.YS s.reserves.append(Familiar_ReserveStack(x, y, self, max_cards=3)) self.setRegion( s.reserves, (-999, y - l.YM, x + l.XS, 999999), priority=1) l.createText(s.reserves[0], "se") # Define stack groups l.defaultAll() # # Game over rides # def startGame(self): assert len(self.s.talon.cards) == 68 for i in range(len(self.s.rows)): self.s.talon.dealRow(rows=self.s.rows[i+1:], flip=0, frames=0) self.startDealSample() self.s.talon.dealRow() self.s.talon.dealCards() def shallHighlightMatch(self, stack1, card1, stack2, card2): return (card1.color != card2.color and (card1.rank + 1 == card2.rank or card2.rank + 1 == card1.rank)) # ************************************************************************ # * Two Familiars # ************************************************************************ class TwoFamiliars(Game): Hint_Class = CautiousDefaultHint Layout_Method = staticmethod(Layout.klondikeLayout) Talon_Class = WasteTalonStack Foundation_Class = SS_FoundationStack RowStack_Class = AC_RowStack # # Game layout # def createGame(self, max_rounds=2, num_deal=1, **layout): l, s = Layout(self), self.s kwdefault(layout, rows=12, waste=1, playcards=20) self.Layout_Method(l, **layout) self.setSize(l.size[0], l.size[1]) # Create talon s.talon = self.Talon_Class(l.s.talon.x, l.s.talon.y, self, max_rounds=max_rounds, num_deal=num_deal) s.waste = WasteStack(l.s.waste.x, l.s.waste.y, self) # Create foundations for r in l.s.foundations: s.foundations.append( self.Foundation_Class( r.x, r.y, self, r.suit, mod=16, max_cards=16, max_move=1)) # Create rows for r in l.s.rows: s.rows.append(self.RowStack_Class(r.x, r.y, self, suit=ANY_SUIT, base_rank=15)) # Create reserve x, y = l.XM, self.height - l.YS s.reserves.append(Familiar_ReserveStack(x, y, self, max_cards=3)) self.setRegion( s.reserves, (-999, y - l.YM, x + l.XS, 999999), priority=1) l.createText(s.reserves[0], "se") # Define stack groups l.defaultAll() # # Game over rides # def startGame(self): assert len(self.s.talon.cards) == 68 * 2 for i in range(len(self.s.rows)): self.s.talon.dealRow(rows=self.s.rows[i+1:], flip=0, frames=0) self.startDealSample() self.s.talon.dealRow() self.s.talon.dealCards() def shallHighlightMatch(self, stack1, card1, stack2, card2): return (card1.color != card2.color and (card1.rank + 1 == card2.rank or card2.rank + 1 == card1.rank)) # ************************************************************************ # * Ten by Eight # ************************************************************************ class TenByEight(Game): Hint_Class = CautiousDefaultHint Layout_Method = staticmethod(Layout.gypsyLayout) Talon_Class = WasteTalonStack Foundation_Class = SS_FoundationStack RowStack_Class = AC_RowStack # # Game layout # def createGame(self, max_rounds=-1, num_deal=1, **layout): l, s = Layout(self), self.s kwdefault(layout, rows=10, waste=1, playcards=30) self.Layout_Method(l, **layout) self.setSize(l.size[0], l.size[1]) # Create talon s.talon = self.Talon_Class(l.s.talon.x, l.s.talon.y, self, max_rounds=max_rounds, num_deal=num_deal) s.waste = WasteStack(l.s.waste.x, l.s.waste.y, self) # Create foundations for r in l.s.foundations: s.foundations.append( self.Foundation_Class( r.x, r.y, self, r.suit, mod=16, max_cards=16, max_move=1)) # Create rows for r in l.s.rows: s.rows.append(self.RowStack_Class(r.x, r.y, self, suit=ANY_SUIT, base_rank=ANY_RANK)) # Define stack groups l.defaultAll() # # Game over rides # def startGame(self): assert len(self.s.talon.cards) == 68 * 2 frames = 0 for i in range(8): if i == 5: frames = -1 self.startDealSample() self.s.talon.dealRow(frames=frames) self.s.talon.dealCards() def shallHighlightMatch(self, stack1, card1, stack2, card2): return (card1.color != card2.color and (card1.rank + 1 == card2.rank or card2.rank + 1 == card1.rank)) # ************************************************************************ # * Drawbridge # ************************************************************************ class Drawbridge(Game): Hint_Class = CautiousDefaultHint Layout_Method = staticmethod(Layout.harpLayout) Talon_Class = WasteTalonStack Foundation_Class = SS_FoundationStack RowStack_Class = AC_RowStack # # Game layout # def createGame(self, max_rounds=2, num_deal=1, **layout): l, s = Layout(self), self.s kwdefault(layout, rows=7, waste=1, playcards=20) self.Layout_Method(l, **layout) self.setSize(l.size[0], l.size[1]) # Create talon s.talon = self.Talon_Class(l.s.talon.x, l.s.talon.y, self, max_rounds=max_rounds, num_deal=num_deal) s.waste = WasteStack(l.s.waste.x, l.s.waste.y, self) # Create foundations for r in l.s.foundations: s.foundations.append( self.Foundation_Class( r.x, r.y, self, r.suit, mod=16, max_cards=16, max_move=1)) # Create rows for r in l.s.rows: s.rows.append(self.RowStack_Class(r.x, r.y, self, suit=ANY_SUIT, base_rank=ANY_RANK)) # Define stack groups l.defaultAll() # # Game over rides # def startGame(self): assert len(self.s.talon.cards) == 68 for i in range(len(self.s.rows) - 1): self.s.talon.dealRow(rows=self.s.rows[i+1:], flip=0, frames=0) self.startDealSample() self.s.talon.dealRow() self.s.talon.dealCards() def shallHighlightMatch(self, stack1, card1, stack2, card2): return (card1.color != card2.color and (card1.rank + 1 == card2.rank or card2.rank + 1 == card1.rank)) # ************************************************************************ # * Double Drawbridge # ************************************************************************ class DoubleDrawbridge(Game): Hint_Class = CautiousDefaultHint Layout_Method = staticmethod(Layout.harpLayout) Talon_Class = WasteTalonStack Foundation_Class = SS_FoundationStack RowStack_Class = AC_RowStack # # Game layout # def createGame(self, max_rounds=2, num_deal=1, **layout): l, s = Layout(self), self.s kwdefault(layout, rows=10, waste=1, playcards=20) self.Layout_Method(l, **layout) self.setSize(l.size[0], l.size[1]) # Create talon s.talon = self.Talon_Class(l.s.talon.x, l.s.talon.y, self, max_rounds=max_rounds, num_deal=num_deal) s.waste = WasteStack(l.s.waste.x, l.s.waste.y, self) # Create foundations for r in l.s.foundations: s.foundations.append( self.Foundation_Class( r.x, r.y, self, r.suit, mod=16, max_cards=16, max_move=1)) # Create rows for r in l.s.rows: s.rows.append( self.RowStack_Class( r.x, r.y, self, suit=ANY_SUIT, base_rank=ANY_RANK)) # Define stack groups l.defaultAll() # # Game over rides # def startGame(self): assert len(self.s.talon.cards) == 68 * 2 for i in range(len(self.s.rows) - 1): self.s.talon.dealRow(rows=self.s.rows[i+1:], flip=0, frames=0) self.startDealSample() self.s.talon.dealRow() self.s.talon.dealCards() def shallHighlightMatch(self, stack1, card1, stack2, card2): return (card1.color != card2.color and (card1.rank + 1 == card2.rank or card2.rank + 1 == card1.rank)) # ************************************************************************ # * Hidden Passages # ************************************************************************ class HiddenPassages(Game): Hint_Class = CautiousDefaultHint Layout_Method = staticmethod(Layout.klondikeLayout) Talon_Class = WasteTalonStack Foundation_Class = SS_FoundationStack RowStack_Class = AC_RowStack # # Game layout # def createGame(self, max_rounds=2, num_deal=1, **layout): l, s = Layout(self), self.s kwdefault(layout, rows=7, waste=1, playcards=20) self.Layout_Method(l, **layout) self.setSize(l.size[0], l.size[1]) # Create talon s.talon = self.Talon_Class(l.s.talon.x, l.s.talon.y, self, max_rounds=max_rounds, num_deal=num_deal) s.waste = WasteStack(l.s.waste.x, l.s.waste.y, self) # Create foundations for r in l.s.foundations[:4]: s.foundations.append( self.Foundation_Class( r.x, r.y, self, r.suit, mod=16, max_cards=16, max_move=1)) r = l.s.foundations[4] s.foundations.append( HexATrump_Foundation( r.x, r.y, self, 4, mod=4, max_move=0, max_cards=4, base_rank=ANY_RANK)) # Create rows for r in l.s.rows: s.rows.append(self.RowStack_Class(r.x, r.y, self, suit=ANY_SUIT, base_rank=ANY_RANK)) # Define stack groups l.defaultAll() # # Game over rides # def _shuffleHook(self, cards): topcards = [None] * 4 for c in cards[:]: if c.rank == 0 and not c.suit == 4: topcards[c.suit] = c cards.remove(c) cards = topcards + cards cards.reverse() return cards def startGame(self): assert len(self.s.talon.cards) == 68 self.s.talon.dealRow(rows=self.s.foundations[:4], frames=0) for i in range(2): self.s.talon.dealRow(flip=0, frames=0) self.startDealSample() self.s.talon.dealRow() self.s.talon.dealCards() def shallHighlightMatch(self, stack1, card1, stack2, card2): return (card1.color != card2.color and (card1.rank + 1 == card2.rank or card2.rank + 1 == card1.rank)) # ************************************************************************ # * Cluitjar's Lair # ************************************************************************ class CluitjarsLair(Game): Hint_Class = CautiousDefaultHint Layout_Method = staticmethod(Layout.klondikeLayout) Talon_Class = WasteTalonStack Foundation_Class = SS_FoundationStack RowStack_Class = HexADeck_ACRowStack # # Game layout # def createGame(self, max_rounds=1, num_deal=1, **layout): l, s = Layout(self), self.s kwdefault(layout, rows=7, waste=1, playcards=20) self.Layout_Method(l, **layout) self.setSize(l.size[0], l.size[1]) # Create talon s.talon = self.Talon_Class(l.s.talon.x, l.s.talon.y, self, max_rounds=max_rounds, num_deal=num_deal) s.waste = WasteStack(l.s.waste.x, l.s.waste.y, self) # Create foundations for r in l.s.foundations[:4]: s.foundations.append( self.Foundation_Class( r.x, r.y, self, r.suit, mod=16, max_cards=16, max_move=1)) r = l.s.foundations[4] s.foundations.append( HexATrump_Foundation( r.x, r.y, self, 4, mod=4, max_move=0, max_cards=4, base_rank=ANY_RANK)) # Create rows for r in l.s.rows: s.rows.append(self.RowStack_Class(r.x, r.y, self, suit=ANY_SUIT, base_rank=ANY_RANK)) # Define stack groups l.defaultAll() # # Game over rides # def startGame(self): assert len(self.s.talon.cards) == 68 for i in range(2): self.s.talon.dealRow(flip=0, frames=0) self.startDealSample() self.s.talon.dealRow() self.s.talon.dealCards() def shallHighlightMatch(self, stack1, card1, stack2, card2): return (card1.color != card2.color and (card1.rank + 1 == card2.rank or card2.rank + 1 == card1.rank)) # ************************************************************************ # * Merlin's Meander # ************************************************************************ class MerlinsMeander(AbstractHexADeckGame): Hint_Class = Merlins_Hint MERLINS_CARDS = 20 # # game layout # def createGame(self): # create layout l, s = Layout(self), self.s # set window # (piles up to 20 cards are playable - needed for Braid_BraidStack) h = max(4*l.YS + 30, l.YS+(self.MERLINS_CARDS-1)*l.YOFFSET) self.setSize(10*l.XS+l.XM, l.YM + h) # extra settings self.base_card = None # Create rows, reserves s.addattr(braid=None) # register extra stack variable x, y = l.XM, l.YM for i in range(2): s.rows.append(Merlins_RowStack(x + l.XS * 0.5, y, self)) s.rows.append(Merlins_RowStack(x + l.XS * 4.5, y, self)) s.reserves.append( Familiar_ReserveStack(x + l.XS * 6.5, y, self, max_cards=3)) y = y + l.YS * 3 y = l.YM + l.YS for i in range(2): s.rows.append(Merlins_ReserveStack(x, y, self)) s.rows.append(Merlins_ReserveStack(x + l.XS, y, self)) s.rows.append(Merlins_ReserveStack(x, y + l.YS, self)) s.rows.append(Merlins_ReserveStack(x + l.XS, y + l.YS, self)) x = x + l.XS * 4 # Create braid x, y = l.XM + l.XS * 2.2, l.YM s.braid = Merlins_BraidStack(x, y, self) # Create talon, waste x, y = l.XM + l.XS * 7, l.YM + l.YS * 1.5 s.talon = WasteTalonStack(x, y, self, max_rounds=3) l.createText(s.talon, "s") s.talon.texts.rounds = MfxCanvasText( self.canvas, x + l.CW // 2, y - l.YM, anchor="s", font=self.app.getFont("canvas_default")) x -= l.XS s.waste = WasteStack(x, y, self) l.createText(s.waste, "s") # Create foundations x, y = l.XM + l.XS * 8, l.YM for i in range(4): s.foundations.append( Merlins_Foundation( x, y, self, i, mod=16, max_cards=16, base_rank=ANY_RANK)) s.foundations.append( Merlins_Foundation( x + l.XS, y, self, i, mod=16, max_cards=16, base_rank=ANY_RANK)) y = y + l.YS self.texts.info = MfxCanvasText( self.canvas, x + l.CW + l.XM // 2, y, anchor="n", font=self.app.getFont("canvas_default")) # define stack-groups self.sg.talonstacks = [s.talon] + [s.waste] self.sg.openstacks = s.foundations + s.rows + s.reserves self.sg.dropstacks = [s.braid] + s.rows + [s.waste] + s.reserves # # game overrides # def startGame(self): assert len(self.s.talon.cards) == 68 * 2 self.startDealSample() self.base_card = None self.updateText() for i in range(self.MERLINS_CARDS): self.s.talon.dealRow(rows=[self.s.braid]) self.s.talon.dealRow() # deal base_card to foundations, update cap.base_rank self.base_card = self.s.talon.getCard() while self.base_card.suit == 4: self.s.talon.cards.remove(self.base_card) self.s.talon.cards.insert(0, self.base_card) self.base_card = self.s.talon.getCard() to_stack = self.s.foundations[2 * self.base_card.suit] self.flipMove(self.s.talon) self.moveMove(1, self.s.talon, to_stack) self.updateText() for s in self.s.foundations: s.cap.base_rank = self.base_card.rank # deal first card to WasteStack self.s.talon.dealCards() def shallHighlightMatch(self, stack1, card1, stack2, card2): return (card1.suit == card2.suit and ((card1.rank + 1) % 16 == card2.rank or (card2.rank + 1) % 16 == card1.rank)) def getHighlightPilesStacks(self): return () 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) # # game extras # def updateText(self): if self.preview > 1 or not self.texts.info: return if not self.base_card: t = "" else: t = self.RANKS[self.base_card.rank] dir = self.getFoundationDir() % 16 if dir == 1: t = t + _(" Ascending") elif dir == 15: t = t + _(" Descending") self.texts.info.config(text=t) def isGameWon(self): for s in self.s.rows: if s.cards and s.cards[0].suit != 4: return 0 if not len(self.s.talon.cards) and len(self.s.waste.cards) == 1: return self.s.waste.cards[0].suit == 4 return len(self.s.talon.cards) + len(self.s.waste.cards) == 0 # ************************************************************************ # * Mage's Game # ************************************************************************ class MagesGame(Game): Hint_Class = CautiousDefaultHint Layout_Method = staticmethod(Layout.gypsyLayout) Talon_Class = InitialDealTalonStack Foundation_Class = SS_FoundationStack RowStack_Class = AC_RowStack # # Game layout # def createGame(self, max_rounds=1, num_deal=1, **layout): l, s = Layout(self), self.s kwdefault(layout, rows=12, texts=0, playcards=20) self.Layout_Method(l, **layout) self.setSize(l.size[0], l.size[1]) # Create talon s.talon = self.Talon_Class(l.s.talon.x, l.s.talon.y, self, max_rounds=max_rounds, num_deal=num_deal) # Create foundations for r in l.s.foundations: s.foundations.append( self.Foundation_Class( r.x, r.y, self, r.suit, mod=16, max_cards=16, max_move=1)) # Create rows for r in l.s.rows: s.rows.append(self.RowStack_Class(r.x, r.y, self, suit=ANY_SUIT, base_rank=ANY_RANK)) # Define stack groups l.defaultAll() # # Game over rides # def startGame(self): assert len(self.s.talon.cards) == 68 for i in range(4): self.s.talon.dealRow(frames=0) self.startDealSample() self.s.talon.dealRow() self.s.talon.dealRow(rows=self.s.rows[2:10]) self.s.talon.dealCards() def shallHighlightMatch(self, stack1, card1, stack2, card2): return (card1.color != card2.color and (card1.rank + 1 == card2.rank or card2.rank + 1 == card1.rank)) # ************************************************************************ # * # ************************************************************************ class Convolution(AbstractHexADeckGame): RowStack_Class = StackWrapper(HexADeck_RK_RowStack, base_rank=NO_RANK) # # game layout # def createGame(self, rows=9, reserves=8): # create layout l, s = Layout(self), self.s # set size maxrows = max(rows, reserves) self.setSize(l.XM + (maxrows + 2) * l.XS, l.YM + 6 * l.YS) # playcards = 4 * l.YS // l.YOFFSET xoffset, yoffset = [], [] for i in range(playcards): xoffset.append(0) yoffset.append(l.YOFFSET) for i in range(68 * self.gameinfo.decks - playcards): xoffset.append(l.XOFFSET) yoffset.append(0) # create stacks x, y = l.XM + (maxrows - reserves) * l.XS // 2, l.YM for i in range(reserves): s.reserves.append(ReserveStack(x, y, self)) x = x + l.XS x, y = l.XM + (maxrows - rows) * l.XS // 2, l.YM + l.YS self.setRegion(s.reserves, (-999, -999, 999999, y - l.YM // 2)) for i in range(rows): stack = self.RowStack_Class(x, y, self, yoffset=yoffset) stack.CARD_XOFFSET = xoffset stack.CARD_YOFFSET = yoffset s.rows.append(stack) x = x + l.XS x, y = l.XM + maxrows * l.XS, l.YM for i in range(2): for suit in range(5): s.foundations.append( SS_FoundationStack(x, y, self, suit=suit, max_cards=16)) y = y + l.YS x, y = x + l.XS, l.YM self.setRegion(self.s.foundations, (x - l.XS * 2, -999, 999999, self.height - (l.YS + l.YM)), priority=1) s.talon = InitialDealTalonStack( self.width - 3 * l.XS // 2, self.height - l.YS, self) # define stack-groups l.defaultStackGroups() # # game overrides # def startGame(self): self.startDealSample() i = 0 while self.s.talon.cards: if self.s.talon.cards[-1].rank == 15: if self.s.rows[i].cards: i = i + 1 self.s.talon.dealRow(rows=[self.s.rows[i]], frames=4) # must look at cards def _getClosestStack(self, cx, cy, stacks, dragstack): closest, cdist = None, 999999999 for stack in stacks: if stack.cards and stack is not dragstack: dist = (stack.cards[-1].x - cx)**2 + \ (stack.cards[-1].y - cy)**2 else: dist = (stack.x - cx)**2 + (stack.y - cy)**2 if dist < cdist: closest, cdist = stack, dist return closest def shallHighlightMatch(self, stack1, card1, stack2, card2): row = self.s.rows[0] sequence = row.isRankSequence return (sequence([card1, card2]) or sequence([card2, card1])) # ************************************************************************ # * # ************************************************************************ class Labyrinth(Convolution): RowStack_Class = StackWrapper(HexADeck_AC_RowStack, base_rank=NO_RANK) def shallHighlightMatch(self, stack1, card1, stack2, card2): row = self.s.rows[0] sequence = row.isAlternateColorSequence return (sequence([card1, card2]) or sequence([card2, card1])) # ************************************************************************ # * # ************************************************************************ class Snakestone(Convolution): RowStack_Class = StackWrapper(HexADeck_SS_RowStack, base_rank=NO_RANK) def shallHighlightMatch(self, stack1, card1, stack2, card2): row = self.s.rows[0] sequence = row.isSuitSequence return (sequence([card1, card2]) or sequence([card2, card1])) # ************************************************************************ # * # ************************************************************************ def r(id, gameclass, name, game_type, decks, redeals, skill_level): game_type = game_type | GI.GT_HEXADECK gi = GameInfo(id, gameclass, name, game_type, decks, redeals, skill_level, suits=list(range(4)), ranks=list(range(16)), trumps=list(range(4))) registerGame(gi) return gi r(165, BitsNBytes, 'Bits n Bytes', GI.GT_HEXADECK, 1, 1, GI.SL_BALANCED) r(166, HexAKlon, 'Hex A Klon', GI.GT_HEXADECK, 1, -1, GI.SL_BALANCED) r(16666, KlondikePlus16, 'Klondike Plus 16', GI.GT_HEXADECK, 1, 1, GI.SL_BALANCED) r(16667, HexAKlonByThrees, 'Hex A Klon by Threes', GI.GT_HEXADECK, 1, -1, GI.SL_BALANCED) r(16668, KingOnlyHexAKlon, 'King Only Hex A Klon', GI.GT_HEXADECK, 1, -1, GI.SL_BALANCED) r(16669, TheFamiliar, 'The Familiar', GI.GT_HEXADECK, 1, 1, GI.SL_BALANCED) r(16670, TwoFamiliars, 'Two Familiars', GI.GT_HEXADECK, 2, 1, GI.SL_BALANCED) r(16671, TenByEight, '10 x 8', GI.GT_HEXADECK, 2, -1, GI.SL_BALANCED) r(16672, Drawbridge, 'Drawbridge', GI.GT_HEXADECK, 1, 1, GI.SL_BALANCED) r(16673, DoubleDrawbridge, 'Double Drawbridge', GI.GT_HEXADECK, 2, 1, GI.SL_BALANCED) r(16674, HiddenPassages, 'Hidden Passages', GI.GT_HEXADECK, 1, 1, GI.SL_MOSTLY_LUCK) r(16675, CluitjarsLair, 'Cluitjar\'s Lair', GI.GT_HEXADECK, 1, 0, GI.SL_BALANCED) r(16676, MerlinsMeander, 'Merlin\'s Meander', GI.GT_HEXADECK, 2, 2, GI.SL_BALANCED) r(16677, MagesGame, 'Mage\'s Game', GI.GT_HEXADECK, 1, 0, GI.SL_BALANCED) r(16678, Convolution, 'Convolution', GI.GT_HEXADECK, 2, 0, GI.SL_MOSTLY_SKILL) r(16679, Labyrinth, 'Hex Labyrinth', GI.GT_HEXADECK, 2, 0, GI.SL_MOSTLY_SKILL) r(16680, Snakestone, 'Snakestone', GI.GT_HEXADECK, 2, 0, GI.SL_MOSTLY_SKILL) del r