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.bakersdozen import Cruel_Talon 27from pysollib.games.braid import Braid, Braid_ReserveStack, Braid_RowStack 28from pysollib.games.braid import Braid_BraidStack, Braid_Foundation 29from pysollib.hint import CautiousDefaultHint 30from pysollib.layout import Layout 31from pysollib.mfxutil import kwdefault 32from pysollib.mygettext import _ 33from pysollib.pysoltk import MfxCanvasText 34from pysollib.stack import \ 35 InitialDealTalonStack, \ 36 OpenStack, \ 37 RK_RowStack, \ 38 ReserveStack, \ 39 SS_FoundationStack, \ 40 SS_RowStack, \ 41 StackWrapper, \ 42 WasteStack, \ 43 WasteTalonStack 44from pysollib.util import ANY_RANK, NO_RANK, UNLIMITED_ACCEPTS, UNLIMITED_MOVES 45 46# ************************************************************************ 47# * Tarock Talon Stacks 48# ************************************************************************ 49 50 51class Wicked_Talon(Cruel_Talon): 52 pass 53 54 55# ************************************************************************ 56# * Tarock Foundation Stacks 57# ************************************************************************ 58 59class ImperialTrump_Foundation(SS_FoundationStack): 60 def acceptsCards(self, from_stack, cards): 61 if not SS_FoundationStack.acceptsCards(self, from_stack, cards): 62 return 0 63 return cards[-1].rank < len(self.game.s.foundations[4].cards) 64 65 66class Ponytail_Foundation(Braid_Foundation): 67 pass 68 69 70# ************************************************************************ 71# * Tarock Row Stacks 72# ************************************************************************ 73 74class Tarock_OpenStack(OpenStack): 75 def __init__(self, x, y, game, yoffset=-1, **cap): 76 kwdefault(cap, max_move=UNLIMITED_MOVES, max_accept=UNLIMITED_ACCEPTS) 77 OpenStack.__init__(self, x, y, game, **cap) 78 if yoffset < 0: 79 yoffset = game.app.images.CARD_YOFFSET 80 self.CARD_YOFFSET = yoffset 81 82 83class Tarock_AC_RowStack(Tarock_OpenStack): 84 def acceptsCards(self, from_stack, cards): 85 if not self.basicAcceptsCards(from_stack, cards): 86 return 0 87 if not self.cards: 88 return 1 89 if cards[0].rank != self.cards[-1].rank - 1: 90 return 0 91 elif cards[0].color == 2 or self.cards[-1].color == 2: 92 return 1 93 else: 94 return cards[0].color != self.cards[-1].color 95 96 97class Skiz_RowStack(RK_RowStack): 98 def acceptsCards(self, from_stack, cards): 99 if not self.basicAcceptsCards(from_stack, cards): 100 return 0 101 if not self.cards: 102 if cards[0].suit == len(self.game.gameinfo.suits): 103 return cards[0].rank == len(self.game.gameinfo.trumps) - 1 104 else: 105 return cards[0].rank == len(self.game.gameinfo.ranks) - 1 106 return self.cards[-1].suit == cards[0].suit and \ 107 self.cards[-1].rank - 1 == cards[0].rank 108 109 110class Pagat_RowStack(RK_RowStack): 111 def acceptsCards(self, from_stack, cards): 112 if not self.basicAcceptsCards(from_stack, cards): 113 return 0 114 if not self.cards: 115 return 1 116 return self.cards[-1].suit == cards[0].suit and \ 117 self.cards[-1].rank - 1 == cards[0].rank 118 119 120class TrumpWild_RowStack(Tarock_OpenStack): 121 def acceptsCards(self, from_stack, cards): 122 if not self.basicAcceptsCards(from_stack, cards): 123 return 0 124 if not self.cards: 125 if cards[0].suit == len(self.game.gameinfo.suits): 126 return cards[0].rank == len(self.game.gameinfo.trumps) - 1 127 else: 128 return cards[0].rank == len(self.game.gameinfo.ranks) - 1 129 if cards[0].rank != self.cards[-1].rank - 1: 130 return 0 131 elif cards[0].color == 2 or self.cards[-1].color == 2: 132 return 1 133 else: 134 return cards[0].color != self.cards[-1].color 135 136 137class TrumpOnly_RowStack(Tarock_OpenStack): 138 def acceptsCards(self, from_stack, cards): 139 if not self.basicAcceptsCards(from_stack, cards): 140 return 0 141 if not self.cards: 142 return cards[0].suit == len(self.game.gameinfo.suits) 143 return cards[0].color == 2 and cards[0].rank == self.cards[-1].rank - 1 144 145 def getBottomImage(self): 146 return self.game.app.images.getReserveBottom() 147 148 149class Excuse_RowStack(Tarock_OpenStack): 150 def acceptsCards(self, from_stack, cards): 151 if not self.basicAcceptsCards(from_stack, cards): 152 return 0 153 if not self.cards: 154 return 0 155 return cards[0].rank == self.cards[-1].rank - 1 156 157 158class WheelOfFortune_RowStack(Tarock_OpenStack): 159 def acceptsCards(self, from_stack, cards): 160 if not self.basicAcceptsCards(from_stack, cards): 161 return 0 162 if not self.cards: 163 return 1 164 return ((cards[0].suit == self.cards[-1].suit) and 165 (cards[0].rank == self.cards[-1].rank - 1)) 166 167 def getBottomImage(self): 168 return self.game.app.images.getReserveBottom() 169 170 171class Ponytail_PonytailStack(Braid_BraidStack): 172 pass 173 174 175class Ponytail_RowStack(Braid_RowStack): 176 pass 177 178 179class Ponytail_ReserveStack(Braid_ReserveStack): 180 pass 181 182 183class Cavalier_RowStack(Tarock_AC_RowStack): 184 def acceptsCards(self, from_stack, cards): 185 if not Tarock_AC_RowStack.acceptsCards(self, from_stack, cards): 186 return 0 187 return self.cards or len(cards) == 1 188 189 def canMoveCards(self, cards): 190 for i in range(len(cards) - 1): 191 if not cards[i].suit == 4: 192 if cards[i].color == cards[i + 1].color: 193 return 0 194 if cards[i].rank - 1 != cards[i + 1].rank: 195 return 0 196 return 1 197 198 199class Nasty_RowStack(SS_RowStack): 200 def acceptsCards(self, from_stack, cards): 201 if not self.basicAcceptsCards(from_stack, cards): 202 return 0 203 if self.cards: 204 return (cards[0].rank == self.cards[-1].rank - 1 and 205 cards[0].suit == self.cards[-1].suit) 206 return cards[0].rank == 13 + 8 * (cards[0].suit == 4) 207 208 209# ************************************************************************ 210# * 211# ************************************************************************ 212 213class Tarock_GameMethods: 214 SUITS = (_("Wand"), _("Sword"), _("Cup"), _("Coin"), _("Trump")) 215 RANKS = (_("Ace"), "2", "3", "4", "5", "6", "7", "8", "9", "10", 216 _("Page"), _("Valet"), _("Queen"), _("King")) 217 218 def getCardFaceImage(self, deck, suit, rank): 219 return self.app.images.getFace(deck, suit, rank) 220 221 222class AbstractTarockGame(Tarock_GameMethods, Game): 223 pass 224 225 226# ************************************************************************ 227# * Wheel of Fortune 228# ************************************************************************ 229 230class WheelOfFortune(AbstractTarockGame): 231 Hint_Class = CautiousDefaultHint 232 233 # 234 # Game layout 235 # 236 237 def createGame(self): 238 l, s = Layout(self), self.s 239 240 # Set window size 241 self.setSize(l.XM + l.XS * 11.5, l.YM + l.YS * 5.5) 242 243 # Create wheel 244 xoffset = (1, 2, 3, 3.9, 3, 2, 1, 0, -1, -2, -3, 245 -3.9, -3, -2, -1, 0, -2, -1, 0, 1, 2) 246 yoffset = (0.2, 0.5, 1.1, 2.2, 3.3, 3.9, 4.2, 4.4, 247 4.2, 3.9, 3.3, 2.2, 1.1, 0.5, 0.2, 0, 248 1.8, 2.1, 2.2, 2.4, 2.6) 249 x = l.XM + l.XS * 4 250 y = l.YM 251 for i in range(21): 252 x0 = x + xoffset[i] * l.XS 253 y0 = y + yoffset[i] * l.YS 254 s.rows.append(WheelOfFortune_RowStack(x0, y0, self, 255 yoffset=l.CH//4, 256 max_cards=2, max_move=1, max_accept=1)) 257 self.setRegion(s.rows, (-999, -999, l.XS * 9, 999999)) 258 259 # Create foundations 260 x = self.width - l.XS * 2 261 y = l.YM 262 s.foundations.append(SS_FoundationStack(x, y, self, 0, max_cards=14)) 263 x = x + l.XS 264 s.foundations.append(SS_FoundationStack(x, y, self, 1, max_cards=14)) 265 y = y + l.YS 266 s.foundations.append(SS_FoundationStack(x, y, self, 3, max_cards=14)) 267 x = x - l.XS 268 s.foundations.append(SS_FoundationStack(x, y, self, 2, max_cards=14)) 269 x = x + l.XS * 0.5 270 y = y + l.YS 271 s.foundations.append(SS_FoundationStack(x, y, self, 4, max_cards=22)) 272 273 # Create talon 274 x = self.width - l.XS 275 y = self.height - l.YS * 1.5 276 s.talon = WasteTalonStack(x, y, self, num_deal=2, max_rounds=1) 277 l.createText(s.talon, "n") 278 x = x - l.XS 279 s.waste = WasteStack(x, y, self) 280 l.createText(s.waste, "n") 281 282 # Define stack groups 283 l.defaultStackGroups() 284 285 # 286 # Game over rides 287 # 288 289 def startGame(self): 290 assert len(self.s.talon.cards) == 78 291 self.startDealSample() 292 self.s.talon.dealRow(rows=self.s.rows[-5:]) 293 self.s.talon.dealRow(rows=self.s.rows[4:-5]) 294 self.s.talon.dealRow(rows=self.s.rows[:4]) 295 self.s.talon.dealCards() 296 297 def shallHighlightMatch(self, stack1, card1, stack2, card2): 298 return 0 299 300 301# ************************************************************************ 302# * Imperial Trumps 303# ************************************************************************ 304 305class ImperialTrumps(AbstractTarockGame): 306 307 # 308 # Game layout 309 # 310 311 def createGame(self): 312 l, s = Layout(self), self.s 313 314 # Set window size 315 self.setSize(l.XM + l.XS * 8, l.YM + l.YS * 5) 316 317 # Create foundations 318 x = l.XM + l.XS * 3 319 y = l.YM 320 for i in range(4): 321 s.foundations.append( 322 ImperialTrump_Foundation(x, y, self, i, max_cards=14)) 323 x = x + l.XS 324 s.foundations.append(SS_FoundationStack(x, y, self, 4, max_cards=22)) 325 326 # Create talon 327 x = l.XM 328 s.talon = WasteTalonStack(x, y, self, num_deal=1, max_rounds=-1) 329 l.createText(s.talon, "s") 330 x = x + l.XS 331 s.waste = WasteStack(x, y, self) 332 l.createText(s.waste, "s") 333 334 # Create rows 335 x = l.XM 336 y = l.YM + l.YS + l.TEXT_HEIGHT 337 for i in range(8): 338 s.rows.append(TrumpWild_RowStack(x, y, self)) 339 x = x + l.XS 340 self.setRegion(s.rows, (-999, y, 999999, 999999)) 341 342 # Define stack groups 343 l.defaultStackGroups() 344 345 # 346 # Game over rides 347 # 348 349 def startGame(self, reverse=1): 350 assert len(self.s.talon.cards) == 78 351 for i in range(1, len(self.s.rows)): 352 self.s.talon.dealRow(rows=self.s.rows[i:], flip=0, frames=0) 353 self.startDealSample() 354 self.s.talon.dealRow(reverse=reverse) 355 self.s.talon.dealCards() 356 357 def shallHighlightMatch(self, stack1, card1, stack2, card2): 358 return 0 359 360 361# ************************************************************************ 362# * Pagat 363# ************************************************************************ 364 365class Pagat(AbstractTarockGame): 366 367 # 368 # Game layout 369 # 370 371 def createGame(self): 372 l, s = Layout(self), self.s 373 374 # Set window size 375 h = max(3 * l.YS, 20 * l.YOFFSET) 376 self.setSize(l.XM + 12 * l.XS, l.YM + l.YS + h) 377 378 # Create foundations 379 x = l.XM + l.XS * 3.5 380 y = l.YM 381 s.foundations.append(SS_FoundationStack(x, y, self, 0, max_cards=14)) 382 x = x + l.XS 383 s.foundations.append(SS_FoundationStack(x, y, self, 1, max_cards=14)) 384 x = x + l.XS 385 s.foundations.append(SS_FoundationStack(x, y, self, 4, max_cards=22)) 386 x = x + l.XS 387 s.foundations.append(SS_FoundationStack(x, y, self, 2, max_cards=14)) 388 x = x + l.XS 389 s.foundations.append(SS_FoundationStack(x, y, self, 3, max_cards=14)) 390 391 # Create reserves 392 x = l.XM 393 for i in range(3): 394 s.reserves.append(ReserveStack(x, y, self)) 395 x = x + l.XS 396 x = x + l.XS * 6 397 for i in range(3): 398 s.reserves.append(ReserveStack(x, y, self)) 399 x = x + l.XS 400 401 # Create rows 402 x = l.XM 403 y = l.YM + l.YS * 1.1 404 for i in range(12): 405 s.rows.append(Pagat_RowStack(x, y, self)) 406 x = x + l.XS 407 self.setRegion(s.rows, (-999, int(y), 999999, 999999)) 408 409 # Create talon 410 s.talon = InitialDealTalonStack(l.XM, self.height-l.YS, self) 411 412 # Define stack groups 413 l.defaultStackGroups() 414 415 # 416 # Game over rides 417 # 418 419 def startGame(self): 420 assert len(self.s.talon.cards) == 78 421 self._startDealNumRows(6) 422 self.s.talon.dealRow(rows=self.s.rows[3:9]) 423 424 def shallHighlightMatch(self, stack1, card1, stack2, card2): 425 return (card1.suit == card2.suit and 426 (card1.rank + 1 == card2.rank or card2.rank + 1 == card1.rank)) 427 428 429# ************************************************************************ 430# * Skiz 431# ************************************************************************ 432 433class Skiz(AbstractTarockGame): 434 435 # 436 # Game layout 437 # 438 439 def createGame(self): 440 l, s = Layout(self), self.s 441 442 # Set window size 443 h = max(3 * l.YS, 20 * l.YOFFSET) 444 self.setSize(l.XM + 12 * l.XS, l.YM + l.YS + h) 445 446 # Create foundations 447 x = l.XM + l.XS * 3.5 448 y = l.YM 449 s.foundations.append(SS_FoundationStack(x, y, self, 0, max_cards=14)) 450 x = x + l.XS 451 s.foundations.append(SS_FoundationStack(x, y, self, 1, max_cards=14)) 452 x = x + l.XS 453 s.foundations.append(SS_FoundationStack(x, y, self, 4, max_cards=22)) 454 x = x + l.XS 455 s.foundations.append(SS_FoundationStack(x, y, self, 2, max_cards=14)) 456 x = x + l.XS 457 s.foundations.append(SS_FoundationStack(x, y, self, 3, max_cards=14)) 458 459 # Create reserves 460 x = l.XM 461 for i in range(3): 462 s.reserves.append(ReserveStack(x, y, self)) 463 x = x + l.XS 464 x = x + l.XS * 6 465 for i in range(3): 466 s.reserves.append(ReserveStack(x, y, self)) 467 x = x + l.XS 468 469 # Create rows 470 x = l.XM 471 y = l.YM + l.YS * 1.1 472 for i in range(12): 473 s.rows.append(Skiz_RowStack(x, y, self)) 474 x = x + l.XS 475 self.setRegion(s.rows, (-999, int(y), 999999, 999999)) 476 477 # Create talon 478 s.talon = InitialDealTalonStack(l.XM, self.height-l.YS, self) 479 480 # Define stack groups 481 l.defaultStackGroups() 482 483 # 484 # Game over rides 485 # 486 487 def startGame(self): 488 assert len(self.s.talon.cards) == 78 489 self._startDealNumRows(6) 490 self.s.talon.dealRow(rows=self.s.rows[3:9]) 491 492 def shallHighlightMatch(self, stack1, card1, stack2, card2): 493 return (card1.suit == card2.suit and 494 (card1.rank + 1 == card2.rank or card2.rank + 1 == card1.rank)) 495 496 497# ************************************************************************ 498# * Fifteen Plus 499# ************************************************************************ 500 501class FifteenPlus(AbstractTarockGame): 502 Hint_Class = CautiousDefaultHint 503 504 # 505 # Game layout 506 # 507 508 def createGame(self): 509 l, s = Layout(self), self.s 510 511 # Set window size 512 h = max(5 * l.YS, 20 * l.YOFFSET) 513 self.setSize(l.XM + 9 * l.XS, l.YM + l.YS + h) 514 515 # Create foundations 516 x = self.width - l.XS 517 y = l.YM 518 s.foundations.append(SS_FoundationStack(x, y, self, 4, max_cards=22)) 519 y = y + l.YS 520 for i in range(4): 521 s.foundations.append( 522 SS_FoundationStack(x, y, self, i, max_cards=14)) 523 y = y + l.YS 524 525 # Create rows 526 x = l.XM 527 y = l.YM 528 for j in range(2): 529 for i in range(8): 530 s.rows.append( 531 Tarock_AC_RowStack(x, y, self, max_move=1, max_accept=1)) 532 x = x + l.XS 533 x = l.XM 534 y = y + l.YS * 3 535 self.setRegion(s.rows, (-999, -999, l.XM + l.XS * 8, 999999)) 536 537 # Create talon 538 s.talon = InitialDealTalonStack(l.XM, self.height-l.YS, self) 539 540 # Define stack groups 541 l.defaultStackGroups() 542 543 # 544 # Game over rides 545 # 546 547 def startGame(self): 548 assert len(self.s.talon.cards) == 78 549 for i in range(2): 550 self.s.talon.dealRow(flip=0, frames=0) 551 for i in range(2): 552 self.s.talon.dealRow(rows=self.s.rows[:15], flip=0, frames=0) 553 self._startAndDealRow() 554 555 def shallHighlightMatch(self, stack1, card1, stack2, card2): 556 return (card1.suit == card2.suit and 557 (card1.rank + 1 == card2.rank or card2.rank + 1 == card1.rank)) 558 559 560# ************************************************************************ 561# * Excuse 562# ************************************************************************ 563 564class Excuse(AbstractTarockGame): 565 Hint_Class = CautiousDefaultHint 566 GAME_VERSION = 2 567 568 # 569 # Game layout 570 # 571 572 def createGame(self): 573 l, s = Layout(self), self.s 574 575 # Set window size 576 h = max(5 * l.YS, 20 * l.YOFFSET) 577 self.setSize(l.XM + 9 * l.XS, l.YM + l.YS + h) 578 579 # Create foundations 580 x = self.width - l.XS 581 y = l.YM 582 s.foundations.append(SS_FoundationStack(x, y, self, 4, max_cards=22)) 583 y = y + l.YS 584 for i in range(4): 585 s.foundations.append( 586 SS_FoundationStack(x, y, self, i, max_cards=14)) 587 y = y + l.YS 588 589 # Create rows 590 x = l.XM 591 y = l.YM 592 for j in range(2): 593 for i in range(8): 594 s.rows.append(Excuse_RowStack(x, y, self, 595 max_move=1, max_accept=1, base_rank=NO_RANK)) 596 x = x + l.XS 597 x = l.XM 598 y = y + l.YS * 3 599 self.setRegion(s.rows, (-999, -999, l.XM + l.XS * 8, 999999)) 600 601 # Create talon 602 s.talon = InitialDealTalonStack(l.XM, self.height-l.YS, self) 603 604 # Define stack groups 605 l.defaultStackGroups() 606 607 # 608 # Game over rides 609 # 610 611 def _shuffleHook(self, cards): 612 # move Kings to bottom of each stack (see Baker's Dozen) 613 def isKing(c): 614 return ((c.suit < 4 and c.rank == 13) or 615 (c.suit == 4 and c.rank == 21)) 616 i, n = 0, len(self.s.rows) 617 kings = [] 618 for c in cards: 619 if isKing(c): 620 kings.append(i) 621 i = i + 1 622 for i in kings: 623 j = i % n 624 while j < i: 625 if not isKing(cards[j]): 626 cards[i], cards[j] = cards[j], cards[i] 627 break 628 j = j + n 629 cards.reverse() 630 return cards 631 632 def startGame(self): 633 assert len(self.s.talon.cards) == 78 634 self._dealNumRows(3) 635 self.s.talon.dealRow(rows=self.s.rows[:15], frames=0) 636 self.startDealSample() 637 self.s.talon.dealRow(rows=self.s.rows[:15]) 638 639 def shallHighlightMatch(self, stack1, card1, stack2, card2): 640 return (card1.rank + 1 == card2.rank or card1.rank - 1 == card2.rank) 641 642 643# ************************************************************************ 644# * Grasshopper 645# * Double Grasshopper 646# ************************************************************************ 647 648class Grasshopper(AbstractTarockGame): 649 GAME_VERSION = 2 650 MAX_ROUNDS = 2 651 652 # 653 # Game layout 654 # 655 656 def createGame(self): 657 l, s = Layout(self), self.s 658 659 # Set window size 660 decks = self.gameinfo.decks 661 self.setSize(2*l.XM + (2 + 5*decks)*l.XS, 3*l.YM + 5*l.YS) 662 yoffset = min(l.YOFFSET, max(10, l.YOFFSET // 2)) 663 664 # Create talon 665 x = l.XM 666 y = l.YM 667 s.talon = WasteTalonStack( 668 x, y, self, num_deal=1, max_rounds=self.MAX_ROUNDS) 669 l.createText(s.talon, "s") 670 x = x + l.XS 671 s.waste = WasteStack(x, y, self) 672 l.createText(s.waste, "s") 673 674 # Create foundations 675 x = x + l.XM + l.XS 676 for j in range(4): 677 for i in range(decks): 678 s.foundations.append( 679 SS_FoundationStack(x, y, self, j, max_cards=14)) 680 x = x + l.XS 681 for i in range(decks): 682 s.foundations.append( 683 SS_FoundationStack(x, y, self, 4, max_cards=22)) 684 x = x + l.XS 685 686 # Create reserve 687 x = l.XM 688 y = l.YM + l.YS + l.TEXT_HEIGHT 689 s.reserves.append(OpenStack(x, y, self)) 690 s.reserves[0].CARD_YOFFSET = (l.YOFFSET, yoffset)[decks == 2] 691 692 # Create rows 693 x = x + l.XM + l.XS 694 for i in range(decks): 695 s.rows.append(TrumpOnly_RowStack(x, y, self, yoffset=yoffset)) 696 x = x + l.XS 697 for i in range(4*decks+1): 698 s.rows.append(Tarock_AC_RowStack(x, y, self)) 699 x = x + l.XS 700 self.setRegion(s.rows, (-999, y - l.YS, 999999, 999999)) 701 702 # Define stack groups 703 l.defaultStackGroups() 704 705 # 706 # Game over rides 707 # 708 709 def startGame(self): 710 decks = self.gameinfo.decks 711 assert len(self.s.talon.cards) == 78 * decks 712 self.startDealSample() 713 for i in range(14 * decks): 714 self.s.talon.dealRow(rows=self.s.reserves, flip=0, frames=4) 715 self.s.reserves[0].flipMove() 716 self.s.talon.dealRow(rows=self.s.rows[decks:]) 717 self.s.talon.dealCards() # deal first card to WasteStack 718 719 def fillStack(self, stack): 720 r = self.s.reserves[0] 721 if not stack.cards and stack in self.s.rows: 722 if r.cards and stack.acceptsCards(r, r.cards[-1:]): 723 r.moveMove(1, stack) 724 if r.canFlipCard(): 725 r.flipMove() 726 727 def shallHighlightMatch(self, stack1, card1, stack2, card2): 728 return ((card1.rank + 1 == card2.rank or 729 card1.rank - 1 == card2.rank) and 730 card1.color != card2.color) 731 732 733class DoubleGrasshopper(Grasshopper): 734 pass 735 736 737# ************************************************************************ 738# * Ponytail 739# ************************************************************************ 740 741class Ponytail(Tarock_GameMethods, Braid): 742 743 # 744 # game layout 745 # 746 747 def createGame(self): 748 # create layout 749 l, s = Layout(self), self.s 750 751 # set window 752 # (piles up to 20 cards are playable - 753 # needed for Ponytail_PonytailStack) 754 h = max(5*l.YS + l.TEXT_HEIGHT, l.YS+(self.BRAID_CARDS-1)*l.YOFFSET) 755 self.setSize(10*l.XS+l.XM, l.YM + h) 756 757 # extra settings 758 self.base_card = None 759 760 # create stacks 761 s.addattr(braid=None) # register extra stack variable 762 x, y = l.XM, l.YM 763 for i in range(2): 764 s.rows.append(Ponytail_RowStack(x + 0.5 * l.XS, y, self)) 765 s.rows.append(Ponytail_RowStack(x + 4.5 * l.XS, y, self)) 766 s.rows.append(Ponytail_RowStack(x + 5.5 * l.XS, y, self)) 767 s.rows.append(Ponytail_RowStack(x + 6.5 * l.XS, y, self)) 768 y = y + 4 * l.YS 769 y = l.YM + l.YS 770 for i in range(2): 771 s.rows.append(Ponytail_ReserveStack(x, y, self)) 772 s.rows.append(Ponytail_ReserveStack(x + l.XS, y, self)) 773 s.rows.append(Ponytail_ReserveStack(x, y + l.YS, self)) 774 s.rows.append(Ponytail_ReserveStack(x + l.XS, y + l.YS, self)) 775 s.rows.append(Ponytail_ReserveStack(x, y + 2 * l.YS, self)) 776 s.rows.append(Ponytail_ReserveStack(x + l.XS, y + 2 * l.YS, self)) 777 x = x + 4 * l.XS 778 x = l.XM + 5*l.XS//2 779 y = l.YM 780 s.braid = Ponytail_PonytailStack(x, y, self, sine=1) 781 x = l.XM + 7 * l.XS 782 y = l.YM + 2*l.YS 783 s.talon = WasteTalonStack(x, y, self, max_rounds=3) 784 l.createText(s.talon, "s") 785 s.talon.texts.rounds = MfxCanvasText( 786 self.canvas, 787 x + l.CW // 2, y - l.YM, 788 anchor="s", 789 font=self.app.getFont("canvas_default")) 790 x = x - l.XS 791 s.waste = WasteStack(x, y, self) 792 l.createText(s.waste, "s") 793 x = l.XM + 8 * l.XS 794 y = l.YM 795 for i in range(4): 796 s.foundations.append( 797 Ponytail_Foundation(x, y, self, i, mod=14, max_cards=14)) 798 s.foundations.append( 799 Ponytail_Foundation( 800 x + l.XS, y, self, i, mod=14, max_cards=14)) 801 y = y + l.YS 802 s.foundations.append( 803 Ponytail_Foundation(x, y, self, 4, mod=22, max_cards=22)) 804 s.foundations.append( 805 Ponytail_Foundation(x + l.XS, y, self, 4, mod=22, max_cards=22)) 806 # ??? 807 self.texts.info = MfxCanvasText( 808 self.canvas, 809 x + l.CW + l.XM // 2, y + l.YS, 810 anchor="n", 811 font=self.app.getFont("canvas_default")) 812 813 # define stack-groups 814 self.sg.openstacks = s.foundations + s.rows 815 self.sg.talonstacks = [s.talon] + [s.waste] 816 self.sg.dropstacks = [s.braid] + s.rows + [s.waste] 817 818 819# ************************************************************************ 820# * Cavalier 821# * Five Aces 822# * Wicked 823# * Nasty 824# ************************************************************************ 825 826class Cavalier(AbstractTarockGame): 827 Layout_Method = staticmethod(Layout.bakersDozenLayout) 828 Talon_Class = InitialDealTalonStack 829 Foundation_Class = SS_FoundationStack 830 RowStack_Class = Cavalier_RowStack 831 832 # 833 # Game layout 834 # 835 836 def createGame(self, **layout): 837 # Create layout 838 l, s = Layout(self), self.s 839 kwdefault(layout, rows=18, playcards=19) 840 self.Layout_Method(l, **layout) 841 self.setSize(l.size[0], l.size[1]) 842 843 # Create foundations 844 for r in l.s.foundations: 845 n = 14 + 8 * (r.suit == 4) 846 s.foundations.append(self.Foundation_Class(r.x, r.y, self, r.suit, 847 mod=n, max_cards=n)) 848 849 # Create rows 850 for r in l.s.rows: 851 s.rows.append(self.RowStack_Class(r.x, r.y, self)) 852 853 # Create talon 854 s.talon = self.Talon_Class(l.s.talon.x, l.s.talon.y, self) 855 856 # Define stack groups 857 l.defaultAll() 858 859 # 860 # Game over rides 861 # 862 863 def startGame(self, flip=(0, 1, 0), foundations=0): 864 assert len(self.s.talon.cards) == 78 865 for f in flip: 866 self.s.talon.dealRow(flip=f, frames=0) 867 self.startDealSample() 868 self.s.talon.dealRow() 869 if foundations: 870 self.s.talon.dealRow(rows=self.s.rows[0:1]) 871 self.s.talon.dealRow(rows=self.s.foundations) 872 else: 873 self.s.talon.dealRow(rows=self.s.rows[:6]) 874 875 def shallHighlightMatch(self, stack1, card1, stack2, card2): 876 return ((card1.rank + 1 == card2.rank or 877 card1.rank - 1 == card2.rank) and 878 ((card1.suit == 4 or card2.suit == 4) or 879 card1.color != card2.color)) 880 881 882class FiveAces(Cavalier): 883 def _shuffleHook(self, cards): 884 return self._shuffleHookMoveToBottom( 885 cards, lambda c: (c.rank == 0, c.suit)) 886 887 def startGame(self): 888 Cavalier.startGame(self, foundations=1) 889 890 891class Wicked(FiveAces): 892 Talon_Class = StackWrapper(Wicked_Talon, max_rounds=-1) 893 RowStack_Class = StackWrapper( 894 SS_RowStack, max_move=1, max_accept=1, base_rank=NO_RANK) 895 Hint_Class = CautiousDefaultHint 896 897 def startGame(self): 898 Cavalier.startGame(self, flip=(1, 1, 1), foundations=1) 899 900 def shallHighlightMatch(self, stack1, card1, stack2, card2): 901 return ((card1.rank + 1 == card2.rank or 902 card1.rank - 1 == card2.rank) and 903 card1.suit == card2.suit) 904 905 906class Nasty(Wicked): 907 RowStack_Class = StackWrapper( 908 Nasty_RowStack, max_move=1, max_accept=1, base_rank=ANY_RANK) 909 910 911# ************************************************************************ 912# * register the games 913# ************************************************************************ 914 915def r(id, gameclass, name, game_type, decks, redeals, skill_level): 916 game_type = game_type | GI.GT_TAROCK | GI.GT_CONTRIB | GI.GT_ORIGINAL 917 gi = GameInfo(id, gameclass, name, game_type, decks, redeals, skill_level, 918 ranks=list(range(14)), trumps=list(range(22))) 919 registerGame(gi) 920 return gi 921 922 923r(157, WheelOfFortune, "Wheel of Fortune", GI.GT_TAROCK, 1, 0, GI.SL_BALANCED) 924r(158, ImperialTrumps, "Imperial Trumps", GI.GT_TAROCK, 1, -1, GI.SL_BALANCED) 925r(159, Pagat, "Pagat", GI.GT_TAROCK | GI.GT_OPEN, 1, 0, GI.SL_MOSTLY_SKILL) 926r(160, Skiz, "Skiz", GI.GT_TAROCK | GI.GT_OPEN, 1, 0, GI.SL_MOSTLY_SKILL) 927r(161, FifteenPlus, "Fifteen plus", GI.GT_TAROCK, 1, 0, GI.SL_BALANCED) 928r(162, Excuse, "Excuse", GI.GT_TAROCK | GI.GT_OPEN, 1, 0, GI.SL_BALANCED) 929r(163, Grasshopper, "Grasshopper", GI.GT_TAROCK, 1, 1, GI.SL_MOSTLY_SKILL) 930r(164, DoubleGrasshopper, "Double Grasshopper", GI.GT_TAROCK, 2, 1, 931 GI.SL_MOSTLY_SKILL) 932r(179, Ponytail, "Ponytail", GI.GT_TAROCK, 2, 2, GI.SL_MOSTLY_SKILL) 933r(202, Cavalier, "Cavalier", GI.GT_TAROCK, 1, 0, GI.SL_MOSTLY_SKILL) 934r(203, FiveAces, "Five Aces", GI.GT_TAROCK, 1, 0, GI.SL_MOSTLY_SKILL) 935r(204, Wicked, "Wicked", GI.GT_TAROCK | GI.GT_OPEN, 1, -1, GI.SL_BALANCED) 936r(205, Nasty, "Nasty", GI.GT_TAROCK | GI.GT_OPEN, 1, -1, GI.SL_BALANCED) 937 938del r 939