#!/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 from pysollib.game import Game from pysollib.gamedb import GI, GameInfo, registerGame from pysollib.games.montana import Montana, Montana_RowStack from pysollib.hint import CautiousDefaultHint, DefaultHint from pysollib.layout import Layout from pysollib.mfxutil import kwdefault from pysollib.mygettext import _ from pysollib.pysoltk import MfxCanvasText from pysollib.stack import \ AC_RowStack, \ AbstractFoundationStack, \ BasicRowStack, \ InitialDealTalonStack, \ OpenStack, \ ReserveStack, \ SS_FoundationStack, \ StackWrapper, \ WasteStack, \ WasteTalonStack, \ Yukon_AC_RowStack from pysollib.util import ANY_RANK, ANY_SUIT, NO_RANK, UNLIMITED_ACCEPTS, \ UNLIMITED_MOVES # ************************************************************************ # * 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 self._startDealNumRows(4) 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])) # ************************************************************************ # * # ************************************************************************ class HexYukon_RowStack(Yukon_AC_RowStack): def canMoveCards(self, cards): if len(cards) > 1: for card in cards: if card.suit == 4: return False return Yukon_AC_RowStack.canMoveCards(self, cards) 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 Yukon_AC_RowStack.acceptsCards(self, from_stack, cards) class HexYukon(Game): Layout_Method = staticmethod(Layout.yukonLayout) Talon_Class = InitialDealTalonStack Foundation_Class = HexADeck_FoundationStack RowStack_Class = StackWrapper(HexYukon_RowStack, base_rank=15) def createGame(self, **layout): # create layout l, s = Layout(self), self.s kwdefault(layout, rows=8, texts=0, playcards=25) self.Layout_Method(l, **layout) self.setSize(l.size[0], l.size[1]) # create stacks s.talon = self.Talon_Class(l.s.talon.x, l.s.talon.y, self) 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)) for r in l.s.rows: s.rows.append(self.RowStack_Class(r.x, r.y, self)) # default l.defaultAll() return l def startGame(self): for i in range(1, len(self.s.rows)): self.s.talon.dealRow(rows=self.s.rows[i:], flip=0, frames=0) for i in range(4): self.s.talon.dealRow(rows=self.s.rows[0:], flip=1, frames=0) self._startAndDealRow() def getHighlightPilesStacks(self): return () shallHighlightMatch = Game._shallHighlightMatch_AC # ************************************************************************ # * # ************************************************************************ class MagicMontana_RowStack(Montana_RowStack): def acceptsCards(self, from_stack, cards): if not BasicRowStack.acceptsCards(self, from_stack, cards): return False if self.id % self.game.RSTEP == 0: return cards[0].rank == self.game.RBASE left = self.game.s.rows[self.id - 1] if left.cards: if left.cards[-1].suit == 4: return False if cards[0].suit == 4: return True return left.cards and left.cards[-1].suit == cards[0].suit \ and left.cards[-1].rank + 1 == cards[0].rank class MagicMontana_Hint(DefaultHint): def computeHints(self): game = self.game RSTEP, RBASE = game.RSTEP, game.RBASE freerows = [s for s in game.s.rows if not s.cards] # for each stack for r in game.s.rows: if not r.cards: continue assert len(r.cards) == 1 and r.cards[-1].face_up c, pile, rpile = r.cards[0], r.cards, [] if r.id % RSTEP > 0: left = game.s.rows[r.id - 1] else: left = None if c.rank == RBASE: # do not move the leftmost card of a row if the # rank is correct continue for t in freerows: if self.shallMovePile(r, t, pile, rpile): # FIXME: this scoring is completely simple if left and left.cards: # prefer low-rank left neighbours score = 40000 + (self.K - left.cards[-1].rank) else: score = 50000 # Deprioritize moving wizards, unless the next card # is a 10. This will prevent an endless loop in the # demo, but more advanced logic can probably use the # wizards more strategically. if r.cards[-1].suit == 4: if t.id % RSTEP > 0: leftt = game.s.rows[t.id - 1] if leftt.cards[-1].rank < 15: score -= 30000 else: continue self.addHint(score, 1, r, t) class MagicMontana(Montana): RowStack_Class = MagicMontana_RowStack Hint_Class = MagicMontana_Hint RLEN, RSTEP, RBASE = 72, 18, 0 def startGame(self): frames = 0 for i in range(self.RLEN): if i == self.RLEN-self.RSTEP: # last row self.startDealSample() frames = -1 if i % self.RSTEP == 0: # left column continue self.s.talon.dealRow(rows=(self.s.rows[i],), frames=frames) def isGameWon(self): rows = self.s.rows for i in range(0, self.RLEN, self.RSTEP): if not rows[i].cards: return False suit = rows[i].cards[-1].suit for j in range(self.RSTEP - 2): r = rows[i + j] if not r.cards or r.cards[-1].rank != self.RBASE + j \ or r.cards[-1].suit != suit: return False w = rows[i + self.RSTEP - 2] if not w.cards or w.cards[-1].suit != 4: return False return True # ************************************************************************ # * # ************************************************************************ def r(id, gameclass, name, game_type, decks, redeals, skill_level, altnames=()): 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)), altnames=altnames) 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 (Draw 3)', 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, altnames=('Merlin\'s Coil')) r(16677, MagesGame, 'Mage\'s Game', GI.GT_HEXADECK | GI.GT_OPEN, 1, 0, GI.SL_BALANCED) r(16678, Convolution, 'Convolution', GI.GT_HEXADECK | GI.GT_OPEN, 2, 0, GI.SL_MOSTLY_SKILL) r(16679, Labyrinth, 'Hex Labyrinth', GI.GT_HEXADECK | GI.GT_OPEN, 2, 0, GI.SL_MOSTLY_SKILL) r(16680, Snakestone, 'Snakestone', GI.GT_HEXADECK | GI.GT_OPEN, 2, 0, GI.SL_MOSTLY_SKILL) r(16681, HexYukon, 'Hex Yukon', GI.GT_HEXADECK, 1, 0, GI.SL_BALANCED) r(16682, MagicMontana, 'Magic Montana', GI.GT_HEXADECK | GI.GT_OPEN, 1, 2, GI.SL_MOSTLY_SKILL) del r