#!/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 . # # ---------------------------------------------------------------------------## from pysollib.game import Game from pysollib.gamedb import GI, GameInfo, registerGame from pysollib.hint import CautiousDefaultHint from pysollib.hint import FreeCellSolverWrapper from pysollib.layout import Layout from pysollib.mfxutil import kwdefault from pysollib.stack import \ AC_FoundationStack, \ AC_RowStack, \ InitialDealTalonStack, \ InvisibleStack, \ RK_RowStack, \ SS_FoundationStack, \ SS_RowStack, \ StackWrapper, \ SuperMoveAC_RowStack, \ TalonStack, \ UD_AC_RowStack, \ UD_SS_RowStack from pysollib.util import ACE, ANY_RANK, KING, NO_RANK, \ UNLIMITED_ACCEPTS, \ UNLIMITED_MOVES # ************************************************************************ # * Castles in Spain # ************************************************************************ class CastlesInSpain(Game): Layout_Method = staticmethod(Layout.bakersDozenLayout) Talon_Class = InitialDealTalonStack Foundation_Class = SS_FoundationStack RowStack_Class = SuperMoveAC_RowStack Hint_Class = CautiousDefaultHint Solver_Class = FreeCellSolverWrapper() GAME_VERSION = 2 # # game layout # def createGame(self, **layout): # create layout l, s = Layout(self), self.s kwdefault(layout, rows=13, playcards=9) 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, suit=r.suit)) for r in l.s.rows: s.rows.append(self.RowStack_Class(r.x, r.y, self)) # default self.s.internals.append(InvisibleStack(self)) l.defaultAll() return l def startGame(self, flip=(0, 0, 0)): for f in flip: self.s.talon.dealRow(flip=f, frames=0) self._startAndDealRow() shallHighlightMatch = Game._shallHighlightMatch_AC # ************************************************************************ # * Martha # * Stewart # ************************************************************************ class Martha_RowStack(AC_RowStack): def acceptsCards(self, from_stack, cards): if not AC_RowStack.acceptsCards(self, from_stack, cards): return False # when empty, only accept a single card return self.cards or len(cards) == 1 class Martha(CastlesInSpain): Solver_Class = None RowStack_Class = Martha_RowStack def createGame(self): CastlesInSpain.createGame(self, rows=12, playcards=13) def _shuffleHook(self, cards): # move Aces to bottom of the Talon (i.e. last cards to be dealt) return self._shuffleHookMoveToBottom( cards, lambda c: (c.rank == 0, c.suit)) def startGame(self): CastlesInSpain.startGame(self, flip=(0, 1, 0)) self.s.talon.dealRow(rows=self.s.foundations) class Stewart(Martha): RowStack_Class = StackWrapper(AC_RowStack, max_move=1, max_accept=1) # ************************************************************************ # * Baker's Dozen # ************************************************************************ class BakersDozen(CastlesInSpain): RowStack_Class = StackWrapper(RK_RowStack, max_move=1, max_accept=1, base_rank=NO_RANK) Solver_Class = FreeCellSolverWrapper(preset='bakers_dozen') def _shuffleHook(self, cards): # move Kings to bottom of each stack i, n = 0, len(self.s.rows) kings = [] for c in cards: if c.rank == KING: kings.append(i) i = i + 1 for i in kings: j = i % n while j < i: if cards[j].rank != KING: cards[i], cards[j] = cards[j], cards[i] break j = j + n cards.reverse() return cards def startGame(self): CastlesInSpain.startGame(self, flip=(1, 1, 1)) shallHighlightMatch = Game._shallHighlightMatch_RK class BakersDozen2Decks(BakersDozen): def createGame(self): CastlesInSpain.createGame(self, rows=26, playcards=13) # ************************************************************************ # * Spanish Patience # * Portuguese Solitaire # ************************************************************************ class SpanishPatience(BakersDozen): Foundation_Class = AC_FoundationStack Solver_Class = None class PortugueseSolitaire(BakersDozen): RowStack_Class = StackWrapper(RK_RowStack, base_rank=KING, max_move=1) Solver_Class = FreeCellSolverWrapper(sbb='rank', esf='kings') def _shuffleHook(self, cards): return cards class SpanishPatienceII(PortugueseSolitaire): RowStack_Class = StackWrapper(RK_RowStack, max_move=1) Solver_Class = FreeCellSolverWrapper(sbb='rank') # ************************************************************************ # * Good Measure # * Vineyard # ************************************************************************ class GoodMeasure(BakersDozen): Solver_Class = FreeCellSolverWrapper(preset='good_measure') def createGame(self): CastlesInSpain.createGame(self, rows=10) def _shuffleHook(self, cards): cards = BakersDozen._shuffleHook(self, cards) # move 2 Aces to bottom of the Talon (i.e. last cards to be dealt) return self._shuffleHookMoveToBottom( cards, lambda c: (c.rank == 0, c.suit), 2) def startGame(self): CastlesInSpain.startGame(self, flip=(1, 1, 1, 1)) for i in range(2): c = self.s.talon.cards[-1] assert c.rank == ACE self.flipMove(self.s.talon) self.moveMove(1, self.s.talon, self.s.foundations[c.suit]) class Vineyard(CastlesInSpain): RowStack_Class = StackWrapper(AC_RowStack, max_move=1, max_accept=1, base_rank=ANY_RANK) def createGame(self): CastlesInSpain.createGame(self, rows=10) def startGame(self): CastlesInSpain.startGame(self, flip=(1, 1, 1, 1)) self.s.talon.dealRow(self.s.rows[0:2]) # ************************************************************************ # * Cruel # * Relaxed Cruel # * Unusual # ************************************************************************ class Cruel_Talon(TalonStack): def canDealCards(self): # FIXME: this is to avoid loops in the demo # if self.game.demo and self.game.moves.index >= 100: # return False if self.round == self.max_rounds: return False return not self.game.isGameWon() def dealCards(self, sound=False): lr = len(self.game.s.rows) # move all cards to the Talon and redeal (no shuffling) num_cards = 0 assert len(self.cards) == 0 rows = list(self.game.s.rows)[:] # rows.reverse() for r in rows: for i in range(len(r.cards)): num_cards = num_cards + 1 self.game.moveMove(1, r, self.game.s.internals[0], frames=0) if len(self.game.s.internals[0].cards) % 4 != 0: self.game.moveMove(len(self.game.s.internals[0].cards) % 4, self.game.s.internals[0], self, frames=0) while len(self.game.s.internals[0].cards) > 0: self.game.moveMove(4, self.game.s.internals[0], self, frames=0) assert len(self.cards) == num_cards if num_cards == 0: # game already finished return 0 # redeal in packs of 4 cards self.game.nextRoundMove(self) n, i = num_cards, 0 deal = [4] * lr extra_cards = n - 4 * lr while extra_cards > 0: # note: this can only happen in Tarock games like Nasty deal[i] = deal[i] + 1 i = (i + 1) % lr extra_cards = extra_cards - 1 # print n, deal self.game.startDealSample() for i in range(lr): k = min(deal[i], n) frames = (0, 4)[n <= 3*4] for j in range(k): self.game.moveMove(1, self, self.game.s.rows[i], frames=frames) n = n - k if n == 0: break # done self.game.stopSamples() assert n == len(self.cards) == 0 return num_cards class Cruel(CastlesInSpain): Talon_Class = StackWrapper(Cruel_Talon, max_rounds=-1) RowStack_Class = StackWrapper(SS_RowStack, max_move=1, max_accept=1, base_rank=NO_RANK) # Solver_Class = FreeCellSolverWrapper(preset='cruel') Solver_Class = None def createGame(self): return CastlesInSpain.createGame(self, rows=12) def _shuffleHook(self, cards): # move Aces to bottom of the Talon (i.e. last cards to be dealt) return self._shuffleHookMoveToBottom( cards, lambda c: (c.rank == 0, c.suit)) def startGame(self): CastlesInSpain.startGame(self, flip=(1, 1, 1)) self.s.talon.dealRow(rows=self.s.foundations) shallHighlightMatch = Game._shallHighlightMatch_SS class RelaxedCruel(Cruel): RowStack_Class = StackWrapper(SS_RowStack, base_rank=NO_RANK) class Unusual(Cruel): def createGame(self): return CastlesInSpain.createGame(self, rows=24) # ************************************************************************ # * Royal Family # * Indefatigable # ************************************************************************ class RoyalFamily(Cruel): Foundation_Class = StackWrapper(SS_FoundationStack, base_rank=KING, dir=-1) Talon_Class = StackWrapper(Cruel_Talon, max_rounds=2) RowStack_Class = UD_AC_RowStack def createGame(self): lay = Cruel.createGame(self) lay.createRoundText(self.s.talon, 'sw') def _shuffleHook(self, cards): # move Kings to bottom of the Talon (i.e. last cards to be dealt) return self._shuffleHookMoveToBottom( cards, lambda c: (c.rank == KING, c.suit)) shallHighlightMatch = Game._shallHighlightMatch_AC class Indefatigable(Cruel): Foundation_Class = StackWrapper(SS_FoundationStack, max_move=0) Talon_Class = StackWrapper(Cruel_Talon, max_rounds=3) RowStack_Class = UD_SS_RowStack def createGame(self): lay = Cruel.createGame(self) lay.createRoundText(self.s.talon, 'sw') def _shuffleHook(self, cards): # move Aces to bottom of the Talon (i.e. last cards to be dealt) return self._shuffleHookMoveToBottom( cards, lambda c: (c.rank == ACE, c.suit)) shallHighlightMatch = Game._shallHighlightMatch_SS # ************************************************************************ # * Perseverance # ************************************************************************ class Perseverance(Cruel, BakersDozen): Talon_Class = StackWrapper(Cruel_Talon, max_rounds=3) RowStack_Class = StackWrapper(SS_RowStack, base_rank=NO_RANK, dir=-1, max_move=UNLIMITED_MOVES, max_accept=UNLIMITED_ACCEPTS) Solver_Class = None def createGame(self): lay = Cruel.createGame(self) lay.createRoundText(self.s.talon, 'sw') def _shuffleHook(self, cards): # move Kings to bottom of each stack (???) # cards = BakersDozen._shuffleHook(self, cards) # move Aces to bottom of the Talon (i.e. last cards to be dealt) cards = Cruel._shuffleHook(self, cards) return cards # def dealCards(self, sound=True): # Cruel.dealCards(self, sound) # ************************************************************************ # * Ripple Fan # ************************************************************************ class RippleFan(CastlesInSpain): Solver_Class = None def createGame(self): # create layout l, s = Layout(self), self.s Layout.bakersDozenLayout(l, rows=13) self.setSize(l.size[0], l.size[1]) # create stacks s.talon = Cruel_Talon(l.s.talon.x, l.s.talon.y, self, max_rounds=-1) for r in l.s.foundations: s.foundations.append( SS_FoundationStack(r.x, r.y, self, suit=r.suit)) for r in l.s.rows: s.rows.append(SS_RowStack(r.x, r.y, self, base_rank=NO_RANK)) # default self.s.internals.append(InvisibleStack(self)) l.defaultAll() def startGame(self): CastlesInSpain.startGame(self, flip=(1, 1, 1)) shallHighlightMatch = Game._shallHighlightMatch_SS # register the game registerGame(GameInfo(83, CastlesInSpain, "Castles in Spain", GI.GT_BAKERS_DOZEN, 1, 0, GI.SL_BALANCED)) registerGame(GameInfo(84, Martha, "Martha", GI.GT_BAKERS_DOZEN, 1, 0, GI.SL_BALANCED)) registerGame(GameInfo(31, BakersDozen, "Baker's Dozen", GI.GT_BAKERS_DOZEN | GI.GT_OPEN, 1, 0, GI.SL_MOSTLY_SKILL)) registerGame(GameInfo(85, SpanishPatience, "Spanish Patience", GI.GT_BAKERS_DOZEN | GI.GT_OPEN, 1, 0, GI.SL_MOSTLY_SKILL)) registerGame(GameInfo(86, GoodMeasure, "Good Measure", GI.GT_BAKERS_DOZEN | GI.GT_OPEN, 1, 0, GI.SL_MOSTLY_SKILL)) registerGame(GameInfo(104, Cruel, "Cruel", GI.GT_BAKERS_DOZEN | GI.GT_OPEN, 1, -1, GI.SL_BALANCED)) registerGame(GameInfo(291, RoyalFamily, "Royal Family", GI.GT_BAKERS_DOZEN | GI.GT_OPEN, 1, 1, GI.SL_MOSTLY_SKILL)) registerGame(GameInfo(308, PortugueseSolitaire, "Portuguese Solitaire", GI.GT_BAKERS_DOZEN | GI.GT_OPEN, 1, 0, GI.SL_MOSTLY_SKILL)) registerGame(GameInfo(404, Perseverance, "Perseverance", GI.GT_BAKERS_DOZEN | GI.GT_OPEN, 1, 2, GI.SL_BALANCED)) registerGame(GameInfo(369, RippleFan, "Ripple Fan", GI.GT_BAKERS_DOZEN | GI.GT_OPEN, 1, -1, GI.SL_MOSTLY_SKILL)) registerGame(GameInfo(515, Indefatigable, "Indefatigable", GI.GT_BAKERS_DOZEN | GI.GT_OPEN, 1, 2, GI.SL_MOSTLY_SKILL)) registerGame(GameInfo(664, SpanishPatienceII, "Spanish Patience II", GI.GT_BAKERS_DOZEN | GI.GT_OPEN, 1, 0, GI.SL_MOSTLY_SKILL)) registerGame(GameInfo(823, Unusual, "Unusual", GI.GT_BAKERS_DOZEN | GI.GT_OPEN, 2, -1, GI.SL_BALANCED)) registerGame(GameInfo(860, BakersDozen2Decks, "Baker's Dozen (2 Decks)", GI.GT_BAKERS_DOZEN | GI.GT_OPEN, 2, 0, GI.SL_MOSTLY_SKILL)) registerGame(GameInfo(876, Vineyard, "Vineyard", GI.GT_BAKERS_DOZEN | GI.GT_OPEN, 1, 0, GI.SL_MOSTLY_SKILL)) registerGame(GameInfo(907, Martha, "Stewart", GI.GT_BAKERS_DOZEN, 1, 0, GI.SL_BALANCED)) registerGame(GameInfo(977, RelaxedCruel, "Relaxed Cruel", GI.GT_BAKERS_DOZEN | GI.GT_OPEN | GI.GT_RELAXED, 1, -1, GI.SL_BALANCED))