1#!/usr/bin/env python 2# -*- mode: python; coding: utf-8; -*- 3# ---------------------------------------------------------------------------## 4# 5# Copyright (C) 1998-2003 Markus Franz Xaver Johannes Oberhumer 6# Copyright (C) 2003 Mt. Hood Playing Card Co. 7# Copyright (C) 2005-2009 Skomoroh 8# 9# This program is free software: you can redistribute it and/or modify 10# it under the terms of the GNU General Public License as published by 11# the Free Software Foundation, either version 3 of the License, or 12# (at your option) any later version. 13# 14# This program is distributed in the hope that it will be useful, 15# but WITHOUT ANY WARRANTY; without even the implied warranty of 16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17# GNU General Public License for more details. 18# 19# You should have received a copy of the GNU General Public License 20# along with this program. If not, see <http://www.gnu.org/licenses/>. 21# 22# ---------------------------------------------------------------------------## 23 24from pysollib.game import Game 25from pysollib.gamedb import GI, GameInfo, registerGame 26from pysollib.games.spider import Spider_Hint, Spider_RowStack, \ 27 Spider_SS_Foundation 28from pysollib.hint import KlondikeType_Hint, YukonType_Hint 29from pysollib.layout import Layout 30from pysollib.mfxutil import kwdefault 31from pysollib.stack import \ 32 AC_RowStack, \ 33 AbstractFoundationStack, \ 34 BasicRowStack, \ 35 DealRowTalonStack, \ 36 InitialDealTalonStack, \ 37 KingAC_RowStack, \ 38 OpenStack, \ 39 ReserveStack, \ 40 SS_FoundationStack, \ 41 SS_RowStack, \ 42 Spider_SS_RowStack, \ 43 Stack, \ 44 StackWrapper, \ 45 TalonStack, \ 46 WasteStack, \ 47 WasteTalonStack, \ 48 Yukon_AC_RowStack 49from pysollib.util import ACE, ANY_SUIT, KING, UNLIMITED_ACCEPTS, \ 50 UNLIMITED_MOVES 51 52# ************************************************************************ 53# * Gypsy 54# ************************************************************************ 55 56 57class Gypsy(Game): 58 Layout_Method = staticmethod(Layout.gypsyLayout) 59 Talon_Class = DealRowTalonStack 60 Foundation_Class = SS_FoundationStack 61 RowStack_Class = AC_RowStack 62 Hint_Class = KlondikeType_Hint 63 64 def createGame(self, **layout): 65 # create layout 66 l, s = Layout(self), self.s 67 kwdefault(layout, rows=8, waste=0, texts=1) 68 self.Layout_Method(l, **layout) 69 self.setSize(l.size[0], l.size[1]) 70 # create stacks 71 s.talon = self.Talon_Class(l.s.talon.x, l.s.talon.y, self) 72 if l.s.waste: 73 s.waste = WasteStack(l.s.waste.x, l.s.waste.y, self) 74 for r in l.s.foundations: 75 s.foundations.append( 76 self.Foundation_Class(r.x, r.y, self, suit=r.suit)) 77 for r in l.s.rows: 78 s.rows.append(self.RowStack_Class(r.x, r.y, self)) 79 # default 80 l.defaultAll() 81 82 def startGame(self): 83 for i in range(2): 84 self.s.talon.dealRow(flip=0, frames=0) 85 self._startAndDealRow() 86 87 shallHighlightMatch = Game._shallHighlightMatch_AC 88 89 90# ************************************************************************ 91# * Giant 92# ************************************************************************ 93 94class Giant_Foundation(SS_FoundationStack): 95 def canMoveCards(self, cards): 96 if not SS_FoundationStack.canMoveCards(self, cards): 97 return False 98 # can only move cards if the Talon is empty 99 return len(self.game.s.talon.cards) == 0 100 101 102class Giant(Gypsy): 103 Foundation_Class = Giant_Foundation 104 105 def startGame(self): 106 self._startAndDealRow() 107 108 109# ************************************************************************ 110# * Irmgard 111# ************************************************************************ 112 113class Irmgard_Talon(TalonStack): 114 # A single click deals 9 (or 7) new cards to the RowStacks. 115 def dealCards(self, sound=False): 116 if self.cards: 117 if len(self.cards) > 7: 118 c = self.dealRow(sound=sound) 119 else: 120 c = self.dealRow(self.game.s.rows[1:8], sound=sound) 121 return c 122 return 0 123 124 125class Irmgard(Gypsy): 126 GAME_VERSION = 2 127 128 Layout_Method = staticmethod(Layout.harpLayout) 129 Talon_Class = Irmgard_Talon 130 RowStack_Class = KingAC_RowStack 131 132 def createGame(self): 133 Gypsy.createGame(self, rows=9, playcards=19) 134 135 def startGame(self): 136 r = self.s.rows 137 for i in range(1, 5): 138 self.s.talon.dealRow(rows=r[i:len(r)-i], flip=0, frames=0) 139 self._startAndDealRow() 140 141 142# ************************************************************************ 143# * Die Königsbergerin 144# ************************************************************************ 145 146class DieKoenigsbergerin_Talon(DealRowTalonStack): 147 # all Aces go to Foundations 148 dealToStacks = DealRowTalonStack.dealToStacksOrFoundations 149 150 151class DieKoenigsbergerin(Gypsy): 152 Talon_Class = DieKoenigsbergerin_Talon 153 Foundation_Class = StackWrapper(SS_FoundationStack, max_move=0) 154 155 def startGame(self): 156 self.startDealSample() 157 for i in range(3): 158 self.s.talon.dealRow() 159 160 161# ************************************************************************ 162# * Die Russische 163# ************************************************************************ 164 165class DieRussische_Foundation(AbstractFoundationStack): 166 def acceptsCards(self, from_stack, cards): 167 if not AbstractFoundationStack.acceptsCards(self, from_stack, cards): 168 return False 169 if self.cards: 170 # check the rank - an ACE equals a Six 171 rank = self.cards[-1].rank 172 if rank == ACE: 173 rank = 5 174 if (rank + self.cap.dir) % self.cap.mod != cards[0].rank: 175 return False 176 return True 177 178 179class DieRussische_RowStack(AC_RowStack): 180 def acceptsCards(self, from_stack, cards): 181 if not AC_RowStack.acceptsCards(self, from_stack, cards): 182 return False 183 # when empty, only accept a single card 184 return self.cards or len(cards) == 1 185 186 187class DieRussische(Gypsy): 188 Talon_Class = InitialDealTalonStack 189 Foundation_Class = StackWrapper( 190 DieRussische_Foundation, min_cards=1, max_cards=8) 191 RowStack_Class = DieRussische_RowStack 192 193 def createGame(self): 194 Gypsy.createGame(self, rows=7, texts=0) 195 196 def _shuffleHook(self, cards): 197 # move one Ace to bottom of the Talon (i.e. last card to be dealt) 198 return self._shuffleHookMoveToBottom( 199 cards, lambda c: (c.rank == 0, c.suit), 1) 200 201 def startGame(self): 202 self._startDealNumRows(6) 203 for i in range(3): 204 self.s.talon.dealRow() 205 c = self.s.talon.cards[-1] 206 self.s.talon.dealRow(rows=(self.s.foundations[c.suit*2],)) 207 208 209# ************************************************************************ 210# * Miss Milligan 211# * Imperial Guards 212# ************************************************************************ 213 214class MissMilligan_ReserveStack(AC_RowStack): 215 def acceptsCards(self, from_stack, cards): 216 if not AC_RowStack.acceptsCards(self, from_stack, cards): 217 return False 218 # Note that this reserve stack accepts sequences if both 219 # the reserve stack and the Talon are empty. 220 return len(self.cards) == 0 and len(self.game.s.talon.cards) == 0 221 222 getBottomImage = Stack._getReserveBottomImage 223 224 225class MissMilligan(Gypsy): 226 Foundation_Class = StackWrapper(SS_FoundationStack, max_move=0) 227 RowStack_Class = KingAC_RowStack 228 ReserveStack_Class = MissMilligan_ReserveStack 229 230 def createGame(self, rows=8, reserves=1): 231 # create layout 232 l, s = Layout(self), self.s 233 234 # set window 235 self.setSize( 236 l.XM + (1+max(8, rows))*l.XS, 237 l.YM + (1+max(4, reserves))*l.YS+l.TEXT_HEIGHT) 238 239 # create stacks 240 x, y = l.XM, l.YM 241 s.talon = self.Talon_Class(x, y, self) 242 for i in range(8): 243 x = x + l.XS 244 s.foundations.append(self.Foundation_Class(x, y, self, suit=i//2)) 245 x, y = l.XM, y + l.YS 246 rx, ry = x + l.XS - l.CW//2, y - l.CH//2 247 for i in range(reserves): 248 s.reserves.append( 249 self.ReserveStack_Class(x, y+l.TEXT_HEIGHT, self)) 250 y = y + l.YS 251 l.createText(s.talon, "s") 252 if s.reserves: 253 self.setRegion(s.reserves, (-999, ry+l.TEXT_HEIGHT, rx-1, 999999)) 254 else: 255 rx = -999 256 x, y = l.XM + (8-rows)*l.XS//2, l.YM + l.YS 257 for i in range(rows): 258 x = x + l.XS 259 s.rows.append(self.RowStack_Class(x, y, self)) 260 self.setRegion(s.rows, (rx, ry, 999999, 999999)) 261 262 # define stack-groups 263 l.defaultStackGroups() 264 265 def startGame(self): 266 self._startAndDealRow() 267 268 269class ImperialGuards(MissMilligan): 270 RowStack_Class = AC_RowStack 271 272 273# ************************************************************************ 274# * Nomad 275# ************************************************************************ 276 277class Nomad(MissMilligan): 278 Foundation_Class = SS_FoundationStack 279 RowStack_Class = AC_RowStack 280 ReserveStack_Class = ReserveStack 281 282 def startGame(self): 283 self._startDealNumRowsAndDealSingleRow(3) 284 285 286# ************************************************************************ 287# * Milligan Cell 288# ************************************************************************ 289 290class MilliganCell(MissMilligan): 291 ReserveStack_Class = ReserveStack 292 293 def createGame(self): 294 MissMilligan.createGame(self, reserves=4) 295 296 def startGame(self): 297 self._startAndDealRow() 298 299 300# ************************************************************************ 301# * Milligan Harp 302# * Carlton 303# * Steve 304# ************************************************************************ 305 306class MilliganHarp(Gypsy): 307 Foundation_Class = StackWrapper(SS_FoundationStack, max_move=0) 308 309 def startGame(self, flip=0): 310 for i in range(len(self.s.rows)): 311 self.s.talon.dealRow(rows=self.s.rows[i+1:], flip=flip, frames=0) 312 self._startAndDealRow() 313 314 315class Carlton(MilliganHarp): 316 def startGame(self): 317 MilliganHarp.startGame(self, flip=1) 318 319 320class Steve(Carlton): 321 Hint_Class = Spider_Hint 322 RowStack_Class = Spider_SS_RowStack 323 324 shallHighlightMatch = Game._shallHighlightMatch_RK 325 getQuickPlayScore = Game._getSpiderQuickPlayScore 326 327 328# ************************************************************************ 329# * Lexington Harp 330# * Brunswick 331# * Mississippi 332# * Griffon 333# ************************************************************************ 334 335class LexingtonHarp(MilliganHarp): 336 GAME_VERSION = 2 337 RowStack_Class = Yukon_AC_RowStack 338 Hint_Class = YukonType_Hint 339 340 def getHighlightPilesStacks(self): 341 return () 342 343 344class Brunswick(LexingtonHarp): 345 def startGame(self): 346 LexingtonHarp.startGame(self, flip=1) 347 348 349class Mississippi(LexingtonHarp): 350 def createGame(self): 351 LexingtonHarp.createGame(self, rows=7) 352 353 354class Griffon(Mississippi): 355 def startGame(self): 356 Mississippi.startGame(self, flip=1) 357 358 359# ************************************************************************ 360# * Blockade 361# * Phantom Blockade 362# ************************************************************************ 363 364class Blockade(Gypsy): 365 Layout_Method = staticmethod(Layout.klondikeLayout) 366 RowStack_Class = SS_RowStack 367 368 def createGame(self): 369 Gypsy.createGame(self, rows=12) 370 371 def startGame(self): 372 self._startAndDealRow() 373 374 def fillStack(self, stack): 375 if stack in self.s.rows and not stack.cards and self.s.talon.cards: 376 old_state = self.enterState(self.S_FILL) 377 self.s.talon.flipMove() 378 self.s.talon.moveMove(1, stack) 379 self.leaveState(old_state) 380 381 shallHighlightMatch = Game._shallHighlightMatch_SS 382 383 384class PhantomBlockade(Gypsy): 385 Layout_Method = staticmethod(Layout.klondikeLayout) 386 RowStack_Class = KingAC_RowStack 387 388 def createGame(self): 389 Gypsy.createGame(self, rows=13, playcards=24) 390 391 def startGame(self): 392 self._startDealNumRowsAndDealSingleRow(2) 393 394 395# ************************************************************************ 396# * Cone 397# ************************************************************************ 398 399class Cone_Talon(DealRowTalonStack): 400 def canDealCards(self): 401 if not DealRowTalonStack.canDealCards(self): 402 return False 403 if len(self.cards) == 4: 404 return True 405 for r in self.game.s.rows: 406 if not r.cards: 407 return False 408 return True 409 410 def dealCards(self, sound=False): 411 rows = self.game.s.rows 412 if len(self.cards) == 4: 413 rows = self.game.s.reserves 414 return self.dealRowAvail(rows=rows, sound=sound) 415 416 417class Cone(Gypsy): 418 419 def createGame(self): 420 # create layout 421 l, s = Layout(self), self.s 422 423 # set window 424 self.setSize(l.XM+9*l.XS, 3*l.YM+5*l.YS) 425 426 # create stacks 427 x, y = l.XM, l.YM 428 s.talon = Cone_Talon(x, y, self) 429 l.createText(s.talon, 's') 430 y += l.YS+2*l.YM 431 for i in range(4): 432 s.reserves.append(OpenStack(x, y, self, max_accept=0)) 433 y += l.YS 434 x, y = l.XM+l.XS, l.YM 435 for i in range(7): 436 s.rows.append(AC_RowStack(x, y, self, mod=13)) 437 x += l.XS 438 for i in range(4): 439 s.foundations.append(SS_FoundationStack(x, y, self, suit=i, 440 mod=13, max_cards=26)) 441 y += l.YS 442 443 # define stack-groups 444 l.defaultStackGroups() 445 446 def startGame(self): 447 self.startDealSample() 448 self.s.talon.dealRow() 449 for i in (1, 2, 3): 450 self.s.talon.dealRow(rows=self.s.rows[i:-i]) 451 452 shallHighlightMatch = Game._shallHighlightMatch_ACW 453 454 455# ************************************************************************ 456# * Surprise 457# ************************************************************************ 458 459class Surprise_ReserveStack(ReserveStack): 460 def acceptsCards(self, from_stack, cards): 461 if not ReserveStack.acceptsCards(self, from_stack, cards): 462 return False 463 return len(self.game.s.talon.cards) == 0 464 465 466class Surprise(Gypsy): 467 468 def createGame(self, rows=8, reserves=1): 469 # create layout 470 l, s = Layout(self), self.s 471 472 # set window 473 self.setSize(l.XM+11*l.XS, l.YM+2*l.YS+12*l.YOFFSET+20) 474 475 # create stacks 476 x, y = l.XM, l.YM 477 s.talon = self.Talon_Class(x, y, self) 478 l.createText(s.talon, 's') 479 x += l.XS 480 stack = Surprise_ReserveStack(x, y, self, max_cards=3) 481 xoffset = min(l.XOFFSET, l.XS//3) 482 stack.CARD_XOFFSET = xoffset 483 s.reserves.append(stack) 484 x += 2*l.XS 485 for i in range(8): 486 s.foundations.append(SS_FoundationStack(x, y, self, suit=i//2)) 487 x += l.XS 488 x, y = l.XM, l.YM+l.YS+l.TEXT_HEIGHT 489 for i in range(11): 490 s.rows.append(KingAC_RowStack(x, y, self)) 491 x += l.XS 492 493 # define stack-groups 494 l.defaultStackGroups() 495 496 def startGame(self): 497 for i in range(1, 6): 498 self.s.talon.dealRow(rows=self.s.rows[i:-i], flip=0, frames=0) 499 self._startAndDealRow() 500 501 502# ************************************************************************ 503# * Elba 504# ************************************************************************ 505 506class Elba(Gypsy): 507 Layout_Method = staticmethod(Layout.klondikeLayout) 508 RowStack_Class = KingAC_RowStack 509 510 def createGame(self): 511 Gypsy.createGame(self, rows=10) 512 513 def startGame(self): 514 for i in range(4): 515 self.s.talon.dealRow(flip=0, frames=0) 516 self._startAndDealRow() 517 518 519# ************************************************************************ 520# * Millie 521# ************************************************************************ 522 523class Millie(Gypsy): 524 Layout_Method = staticmethod(Layout.klondikeLayout) 525 526 def createGame(self): 527 Gypsy.createGame(self, playcards=24) 528 529 def startGame(self): 530 self._startAndDealRow() 531 532 533# ************************************************************************ 534# * Hypotenuse 535# * Eternal Triangle 536# * Right Triangle 537# ************************************************************************ 538 539class Hypotenuse(Gypsy): 540 Layout_Method = staticmethod(Layout.klondikeLayout) 541 RowStack_Class = KingAC_RowStack 542 543 def createGame(self): 544 Gypsy.createGame(self, rows=10, playcards=24) 545 546 def startGame(self, flip=0, reverse=1): 547 for i in range(1, 10): 548 self.s.talon.dealRow(rows=self.s.rows[:i], flip=0, frames=0) 549 self._startAndDealRow() 550 551 552class EternalTriangle(Hypotenuse): 553 554 def startGame(self, flip=0, reverse=1): 555 for i in range(1, 10): 556 self.s.talon.dealRow(rows=self.s.rows[i:], frames=0) 557 self._startAndDealRow() 558 559 560class RightTriangle_Talon(OpenStack, DealRowTalonStack): 561 def __init__(self, x, y, game, max_rounds=1, num_deal=1, **cap): 562 kwdefault(cap, max_move=1, max_accept=1, max_cards=999999) 563 Stack.__init__(self, x, y, game, cap=cap) 564 self.max_rounds = max_rounds 565 self.num_deal = num_deal 566 self.round = 1 567 self.base_cards = [] # for DealBaseCard_StackMethods 568 569 def clickHandler(self, event): 570 if self.cards and not self.cards[-1].face_up: 571 return self.game.dealCards(sound=True) 572 return OpenStack.clickHandler(self, event) 573 574 def canDealCards(self): 575 if not DealRowTalonStack.canDealCards(self): 576 return False 577 if self.cards and self.cards[-1].face_up: 578 return False 579 return True 580 581 def canFlipCard(self): 582 return False 583 584 getBottomImage = Stack._getReserveBottomImage 585 586 prepareView = Stack.prepareView 587 588 resize = Stack.resize 589 590 def getHelp(self): 591 return DealRowTalonStack.getHelp(self) 592 593 594class RightTriangle(Hypotenuse): 595 Talon_Class = RightTriangle_Talon 596 597 def createGame(self): 598 Gypsy.createGame(self, rows=10, playcards=24) 599 self.sg.dropstacks.append(self.s.talon) 600 self.sg.openstacks.append(self.s.talon) 601 self.sg.reservestacks.append(self.s.talon) 602 603 604# ************************************************************************ 605# * Trapdoor 606# ************************************************************************ 607 608class Trapdoor_Talon(DealRowTalonStack): 609 def dealCards(self, sound=False): 610 if not self.cards: 611 return 0 612 if sound: 613 self.game.startDealSample() 614 n = 0 615 rows = self.game.s.rows 616 reserves = self.game.s.reserves 617 for i in range(len(rows)): 618 r1 = reserves[i] 619 r2 = rows[i] 620 if r1.cards: 621 r1.moveMove(1, r2) 622 n += 1 623 n += self.dealRowAvail(rows=self.game.s.reserves, sound=False) 624 if sound: 625 self.game.stopSamples() 626 return n 627 628 629class Trapdoor(Gypsy): 630 Foundation_Class = SS_FoundationStack 631 RowStack_Class = AC_RowStack 632 633 def createGame(self, rows=8): 634 kw = {'rows': rows, 635 'waste': 0, 636 'texts': 1, 637 'reserves': rows} 638 Layout(self).createGame(layout_method=Layout.gypsyLayout, 639 talon_class=Trapdoor_Talon, 640 foundation_class=self.Foundation_Class, 641 row_class=self.RowStack_Class, 642 reserve_class=OpenStack, 643 **kw 644 ) 645 646 def startGame(self): 647 Gypsy.startGame(self) 648 self.s.talon.dealCards() 649 650 651class TrapdoorSpider(Trapdoor): 652 Foundation_Class = Spider_SS_Foundation 653 RowStack_Class = Spider_RowStack 654 Hint_Class = Spider_Hint 655 656 def createGame(self): 657 Trapdoor.createGame(self, rows=10) 658 659 def startGame(self, flip=0): 660 for i in range(3): 661 self.s.talon.dealRow(flip=flip, frames=0) 662 r = self.s.rows 663 rows = (r[0], r[3], r[6], r[9]) 664 self.s.talon.dealRow(rows=rows, flip=flip, frames=0) 665 self.startDealSample() 666 self.s.talon.dealRow() 667 self.s.talon.dealCards() 668 669 shallHighlightMatch = Game._shallHighlightMatch_RK 670 getQuickPlayScore = Game._getSpiderQuickPlayScore 671 672 673# ************************************************************************ 674# * Flamenco 675# ************************************************************************ 676 677class Flamenco(Gypsy): 678 679 def createGame(self): 680 kw = {'rows': 8, 681 'waste': 0, 682 'texts': 1, } 683 foundation_class = ( 684 SS_FoundationStack, 685 StackWrapper(SS_FoundationStack, base_rank=KING, dir=-1)) 686 Layout(self).createGame(layout_method=Layout.gypsyLayout, 687 talon_class=DealRowTalonStack, 688 foundation_class=foundation_class, 689 row_class=AC_RowStack, 690 **kw 691 ) 692 693 def _shuffleHook(self, cards): 694 return self._shuffleHookMoveToTop( 695 cards, 696 lambda c: (c.rank in (ACE, KING) and c.deck == 0, 697 (c.suit, c.rank))) 698 699 def startGame(self): 700 self.s.talon.dealRow(rows=self.s.foundations, frames=0) 701 self._startDealNumRowsAndDealSingleRow(2) 702 703 704# ************************************************************************ 705# * Eclipse 706# ************************************************************************ 707 708class Eclipse(Gypsy): 709 Layout_Method = staticmethod(Layout.klondikeLayout) 710 RowStack_Class = SS_RowStack 711 712 def createGame(self): 713 Gypsy.createGame(self, rows=13) 714 715 def startGame(self): 716 self._startDealNumRowsAndDealSingleRow(3) 717 718 shallHighlightMatch = Game._shallHighlightMatch_SS 719 720 721# ************************************************************************ 722# * Brazilian Patience 723# ************************************************************************ 724 725class BrazilianPatience(Gypsy): 726 Layout_Method = staticmethod(Layout.klondikeLayout) 727 RowStack_Class = KingAC_RowStack 728 729 def createGame(self): 730 Gypsy.createGame(self, rows=10, playcards=22) 731 732 def startGame(self, flip=0, reverse=1): 733 for i in range(1, 10): 734 self.s.talon.dealRow(rows=self.s.rows[i:], flip=0, frames=0) 735 self._startAndDealRow() 736 737 738# ************************************************************************ 739# * Leprechaun 740# ************************************************************************ 741 742class Leprechaun_Reserve(OpenStack): 743 def canFlipCard(self): 744 if not OpenStack.canFlipCard(self): 745 return False 746 i = list(self.game.s.reserves).index(self) 747 return len(self.game.s.foundations[i].cards) != 0 748 749 750class Leprechaun(Game): 751 752 def createGame(self): 753 754 # create layout 755 l, s = Layout(self), self.s 756 757 # set window 758 self.setSize(l.XM+9.5*l.XS, l.YM+3*l.YS+l.TEXT_HEIGHT+12*l.YOFFSET) 759 760 # create stacks 761 x, y = l.XM+1.5*l.XS, l.TEXT_HEIGHT 762 for i in range(8): 763 stack = Leprechaun_Reserve(x, y, self) 764 s.reserves.append(stack) 765 l.createText(stack, 'n') 766 x += l.XS 767 768 x, y = l.XM+1.5*l.XS, l.YS+l.TEXT_HEIGHT 769 for i in range(8): 770 s.foundations.append(SS_FoundationStack(x, y, self, suit=i//2)) 771 x += l.XS 772 773 x, y = l.XM+1.5*l.XS, 2*l.YS+l.TEXT_HEIGHT 774 for i in range(8): 775 s.rows.append(AC_RowStack(x, y, self)) 776 x += l.XS 777 778 s.talon = DealRowTalonStack(l.XM, l.YM, self) 779 l.createText(s.talon, 's') 780 781 # define stack-groups 782 l.defaultStackGroups() 783 784 def startGame(self): 785 for i in range(4): 786 self.s.talon.dealRow(rows=self.s.reserves, flip=0, frames=0) 787 self.s.talon.dealRow(flip=0, frames=0) 788 self.s.talon.dealRow(flip=0, frames=0) 789 self._startAndDealRow() 790 791 shallHighlightMatch = Game._shallHighlightMatch_AC 792 793 794# ************************************************************************ 795# * Locked Cards 796# * Topsy-Turvy Queens 797# ************************************************************************ 798 799class LockedCards_Reserve(OpenStack): 800 def canFlipCard(self): 801 if not OpenStack.canFlipCard(self): 802 return False 803 i = list(self.game.s.reserves).index(self) 804 return len(self.game.s.foundations[i].cards) == 13 805 806 807class LockedCards_Foundation(SS_FoundationStack): 808 def acceptsCards(self, from_stack, cards): 809 if not SS_FoundationStack.acceptsCards(self, from_stack, cards): 810 return False 811 if self.cards: 812 # check suit 813 return self.cards[-1].suit == cards[0].suit 814 return True 815 816 817class LockedCards(Game): 818 Foundation_Class = LockedCards_Foundation 819 RowStack_Class = AC_RowStack 820 821 def createGame(self): 822 823 # create layout 824 l, s = Layout(self), self.s 825 826 # set window 827 self.setSize(l.XM+10*l.XS, l.YM+3*l.YS+14*l.YOFFSET) 828 829 # create stacks 830 x, y = l.XM, l.YM 831 for i in range(7): 832 s.reserves.append(LockedCards_Reserve(x, y, self)) 833 x += l.XS 834 835 x, y = l.XM, l.YM+l.YS 836 for i in range(8): 837 s.foundations.append(self.Foundation_Class(x, y, self, 838 suit=ANY_SUIT, max_move=0)) 839 x += l.XS 840 841 x, y = l.XM, l.YM+2*l.YS 842 for i in range(8): 843 s.rows.append(self.RowStack_Class(x, y, self)) 844 x += l.XS 845 846 x, y = self.width-l.XS, self.height-l.YS 847 s.talon = WasteTalonStack(x, y, self, max_rounds=3) 848 l.createText(s.talon, 'n') 849 l.createRoundText(s.talon, 'nnn') 850 851 x -= l.XS 852 s.waste = WasteStack(x, y, self) 853 l.createText(s.waste, 'n') 854 855 # define stack-groups 856 l.defaultStackGroups() 857 858 def startGame(self, rows=5): 859 self.s.talon.dealRow(rows=self.s.reserves, flip=0, frames=0) 860 self._startDealNumRowsAndDealRowAndCards(rows-1) 861 862 shallHighlightMatch = Game._shallHighlightMatch_AC 863 864 865class TopsyTurvyQueens(LockedCards): 866 Foundation_Class = StackWrapper(LockedCards_Foundation, 867 base_rank=KING, mod=13) 868 RowStack_Class = StackWrapper(SS_RowStack, mod=13) 869 870 def startGame(self): 871 LockedCards.startGame(self, rows=4) 872 873 shallHighlightMatch = Game._shallHighlightMatch_SSW 874 875 876class KingsSecrets(LockedCards): 877 Foundation_Class = StackWrapper(LockedCards_Foundation, 878 base_rank=KING, mod=13) 879 RowStack_Class = StackWrapper(SS_RowStack, mod=13, base_rank=11) 880 881 def startGame(self): 882 LockedCards.startGame(self, rows=5) 883 884 shallHighlightMatch = Game._shallHighlightMatch_SSW 885 886 887# ************************************************************************ 888# * Thirty 889# ************************************************************************ 890 891class Thirty_RowStack(BasicRowStack): 892 def acceptsCards(self, from_stack, cards): 893 if not BasicRowStack.acceptsCards(self, from_stack, cards): 894 return False 895 if self.cards: 896 # check the rank - an ACE equals a Six 897 rank = self.cards[-1].rank 898 if rank == ACE: 899 rank = 5 900 if (rank + self.cap.dir) % self.cap.mod != cards[0].rank: 901 return False 902 return True 903 904 def canMoveCards(self, cards): 905 if not BasicRowStack.canMoveCards(self, cards): 906 return False 907 c1 = cards[0] 908 for c2 in cards[1:]: 909 if c1.suit != c2.suit: 910 return False 911 # check the rank - an ACE equals a Six 912 rank = c1.rank 913 if rank == ACE: 914 rank = 5 915 if (rank + self.cap.dir) % self.cap.mod != c2.rank: 916 return False 917 c1 = c2 918 return True 919 920 921class Thirty(Game): 922 Hint_Class = Spider_Hint 923 924 def createGame(self): 925 926 l, s = Layout(self), self.s 927 self.setSize(l.XM+7*l.XS, l.YM+2*l.YS+12*l.YOFFSET) 928 929 x, y = l.XM, l.YM 930 for i in range(2): 931 s.reserves.append(OpenStack(x, y, self)) 932 x += l.XS 933 934 x, y = l.XM+3*l.XS, l.YM 935 for i in range(4): 936 s.foundations.append(DieRussische_Foundation(x, y, self, 937 suit=i, max_cards=8)) 938 x += l.XS 939 940 x, y = l.XM+l.XS//2, l.YM+l.YS 941 for i in range(6): 942 s.rows.append(Thirty_RowStack(x, y, self, 943 max_move=UNLIMITED_MOVES, 944 max_accept=UNLIMITED_ACCEPTS)) 945 x += l.XS 946 947 x, y = self.width-l.XS, self.height-l.YS 948 s.talon = InitialDealTalonStack(x, y, self) 949 950 l.defaultAll() 951 952 def startGame(self): 953 self._startDealNumRows(4) 954 self.s.talon.dealRow() 955 self.s.talon.dealRow(rows=self.s.reserves) 956 957 shallHighlightMatch = Game._shallHighlightMatch_RK 958 getQuickPlayScore = Game._getSpiderQuickPlayScore 959 960 961# register the game 962registerGame(GameInfo(1, Gypsy, "Gypsy", 963 GI.GT_GYPSY, 2, 0, GI.SL_MOSTLY_SKILL)) 964registerGame(GameInfo(65, Giant, "Giant", 965 GI.GT_GYPSY, 2, 0, GI.SL_MOSTLY_SKILL)) 966registerGame(GameInfo(3, Irmgard, "Irmgard", 967 GI.GT_GYPSY, 2, 0, GI.SL_MOSTLY_SKILL)) 968registerGame(GameInfo(119, DieKoenigsbergerin, "Die Koenigsbergerin", 969 GI.GT_GYPSY, 2, 0, GI.SL_MOSTLY_SKILL)) 970registerGame(GameInfo(174, DieRussische, "Russian Patience", 971 GI.GT_2DECK_TYPE | GI.GT_OPEN, 2, 0, GI.SL_MOSTLY_SKILL, 972 ranks=(0, 6, 7, 8, 9, 10, 11, 12), 973 altnames=("Die Russische",))) 974registerGame(GameInfo(62, MissMilligan, "Miss Milligan", 975 GI.GT_GYPSY, 2, 0, GI.SL_MOSTLY_SKILL)) 976registerGame(GameInfo(200, Nomad, "Nomad", 977 GI.GT_GYPSY | GI.GT_CONTRIB | GI.GT_ORIGINAL, 2, 0, 978 GI.SL_MOSTLY_SKILL)) 979registerGame(GameInfo(78, MilliganCell, "Milligan Cell", 980 GI.GT_GYPSY, 2, 0, GI.SL_MOSTLY_SKILL)) 981registerGame(GameInfo(217, MilliganHarp, "Milligan Harp", 982 GI.GT_GYPSY, 2, 0, GI.SL_BALANCED)) 983registerGame(GameInfo(218, Carlton, "Carlton", 984 GI.GT_GYPSY, 2, 0, GI.SL_MOSTLY_SKILL)) 985registerGame(GameInfo(68, LexingtonHarp, "Lexington Harp", 986 GI.GT_YUKON, 2, 0, GI.SL_BALANCED)) 987registerGame(GameInfo(154, Brunswick, "Brunswick", 988 GI.GT_YUKON, 2, 0, GI.SL_BALANCED)) 989registerGame(GameInfo(121, Mississippi, "Mississippi", 990 GI.GT_YUKON | GI.GT_XORIGINAL, 2, 0, GI.SL_MOSTLY_SKILL)) 991registerGame(GameInfo(122, Griffon, "Griffon", 992 GI.GT_YUKON | GI.GT_XORIGINAL, 2, 0, GI.SL_MOSTLY_SKILL)) 993registerGame(GameInfo(226, Blockade, "Blockade", 994 GI.GT_GYPSY, 2, 0, GI.SL_BALANCED)) 995registerGame(GameInfo(412, Cone, "Cone", 996 GI.GT_GYPSY, 2, 0, GI.SL_BALANCED)) 997registerGame(GameInfo(463, Surprise, "Surprise", 998 GI.GT_GYPSY, 2, 0, GI.SL_BALANCED)) 999registerGame(GameInfo(469, PhantomBlockade, "Phantom Blockade", 1000 GI.GT_GYPSY, 2, 0, GI.SL_MOSTLY_SKILL)) 1001registerGame(GameInfo(478, Elba, "Elba", 1002 GI.GT_GYPSY, 2, 0, GI.SL_MOSTLY_SKILL)) 1003registerGame(GameInfo(486, ImperialGuards, "Imperial Guards", 1004 GI.GT_GYPSY, 2, 0, GI.SL_MOSTLY_SKILL)) 1005registerGame(GameInfo(487, Millie, "Millie", 1006 GI.GT_GYPSY, 2, 0, GI.SL_BALANCED)) 1007registerGame(GameInfo(498, Steve, "Steve", 1008 GI.GT_GYPSY, 2, 0, GI.SL_BALANCED)) 1009registerGame(GameInfo(566, Hypotenuse, "Hypotenuse", 1010 GI.GT_GYPSY, 2, 0, GI.SL_MOSTLY_SKILL)) 1011registerGame(GameInfo(567, EternalTriangle, "Eternal Triangle", 1012 GI.GT_GYPSY, 2, 0, GI.SL_MOSTLY_SKILL, 1013 altnames=('Lobachevsky',))) 1014registerGame(GameInfo(568, RightTriangle, "Right Triangle", 1015 GI.GT_GYPSY, 2, 0, GI.SL_MOSTLY_SKILL)) 1016registerGame(GameInfo(580, Trapdoor, "Trapdoor", 1017 GI.GT_GYPSY | GI.GT_ORIGINAL, 2, 0, GI.SL_MOSTLY_SKILL)) 1018registerGame(GameInfo(581, Flamenco, "Flamenco", 1019 GI.GT_GYPSY | GI.GT_ORIGINAL, 2, 0, GI.SL_MOSTLY_SKILL)) 1020registerGame(GameInfo(584, Eclipse, "Eclipse", 1021 GI.GT_GYPSY, 2, 0, GI.SL_MOSTLY_SKILL)) 1022registerGame(GameInfo(640, BrazilianPatience, "Brazilian Patience", 1023 GI.GT_GYPSY, 2, 0, GI.SL_MOSTLY_SKILL)) 1024registerGame(GameInfo(666, TrapdoorSpider, "Trapdoor Spider", 1025 GI.GT_SPIDER | GI.GT_ORIGINAL, 2, 0, GI.SL_MOSTLY_SKILL)) 1026registerGame(GameInfo(712, Leprechaun, "Leprechaun", 1027 GI.GT_GYPSY | GI.GT_ORIGINAL, 2, 0, GI.SL_MOSTLY_SKILL)) 1028registerGame(GameInfo(718, LockedCards, "Locked Cards", 1029 GI.GT_2DECK_TYPE, 2, 2, GI.SL_BALANCED)) 1030registerGame(GameInfo(721, Thirty, "Thirty", 1031 GI.GT_1DECK_TYPE | GI.GT_OPEN, 1, 0, GI.SL_MOSTLY_SKILL, 1032 ranks=(0, 6, 7, 8, 9, 10, 11, 12))) 1033registerGame(GameInfo(725, TopsyTurvyQueens, "Topsy-Turvy Queens", 1034 GI.GT_2DECK_TYPE, 2, 2, GI.SL_BALANCED)) 1035registerGame(GameInfo(792, KingsSecrets, "King's Secrets", 1036 GI.GT_2DECK_TYPE, 2, 2, GI.SL_BALANCED)) 1037