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 24import time 25 26from pysollib.game import Game 27from pysollib.gamedb import GI, GameInfo, registerGame 28from pysollib.hint import CautiousDefaultHint, DefaultHint 29from pysollib.layout import Layout 30from pysollib.mfxutil import kwdefault 31from pysollib.mygettext import _ 32from pysollib.stack import \ 33 AC_RowStack, \ 34 BasicRowStack, \ 35 DealRowTalonStack, \ 36 InitialDealTalonStack, \ 37 OpenStack, \ 38 OpenTalonStack, \ 39 RK_FoundationStack, \ 40 RK_RowStack, \ 41 ReserveStack, \ 42 SS_FoundationStack, \ 43 Stack, \ 44 StackWrapper, \ 45 TalonStack, \ 46 WasteStack, \ 47 WasteTalonStack 48from pysollib.util import ACE, ANY_RANK, ANY_SUIT, JACK, KING, NO_RANK, \ 49 UNLIMITED_ACCEPTS, \ 50 UNLIMITED_CARDS 51 52 53class Numerica_Hint(DefaultHint): 54 # FIXME: demo is clueless 55 56 # def _getDropCardScore(self, score, color, r, t, ncards): 57 # FIXME: implement this method 58 59 def _getMoveWasteScore(self, score, color, r, t, pile, rpile): 60 assert r in (self.game.s.waste, self.game.s.talon) and len(pile) == 1 61 score = self._computeScore(r, t) 62 return score, color 63 64 def _computeScore(self, r, t): 65 score = 30000 66 if len(t.cards) == 0: 67 score = score - (KING - r.cards[0].rank) * 1000 68 elif t.cards[-1].rank < r.cards[0].rank: 69 # FIXME: add intelligence here 70 score = 10000 + t.cards[-1].rank - len(t.cards) 71 elif t.cards[-1].rank == r.cards[0].rank: 72 score = 20000 73 else: 74 score = score - (t.cards[-1].rank - r.cards[0].rank) * 1000 75 return score 76 77 78# ************************************************************************ 79# * 80# ************************************************************************ 81 82class Numerica_RowStack(BasicRowStack): 83 def acceptsCards(self, from_stack, cards): 84 if not BasicRowStack.acceptsCards(self, from_stack, cards): 85 return False 86 # this stack accepts any one card from the Waste pile 87 return from_stack is self.game.s.waste and len(cards) == 1 88 89 getBottomImage = Stack._getReserveBottomImage 90 91 def getHelp(self): 92 # return _('Tableau. Accepts any one card from the Waste.') 93 return _('Tableau. Build regardless of rank and suit.') 94 95 96# ************************************************************************ 97# * Numerica 98# ************************************************************************ 99 100class Numerica(Game): 101 Hint_Class = Numerica_Hint 102 Foundation_Class = StackWrapper(RK_FoundationStack, suit=ANY_SUIT) 103 RowStack_Class = StackWrapper(Numerica_RowStack, max_accept=1) 104 105 # 106 # game layout 107 # 108 109 def createGame(self, rows=4, reserve=False, max_rounds=1, 110 waste_max_cards=1): 111 # create layout 112 l, s = Layout(self), self.s 113 decks = self.gameinfo.decks 114 foundations = 4*decks 115 116 # set window 117 # (piles up to 20 cards are playable in default window size) 118 h = max(2.5 * l.YS, 20 * l.YOFFSET) 119 max_rows = max(rows, foundations) 120 self.setSize(l.XM + (1.5 + max_rows) * l.XS + l.XM, l.YM + l.YS + h) 121 122 # create stacks 123 x0 = l.XM + l.XS * 3 // 2 124 if decks == 1: 125 x = x0 + (rows-4)*l.XS//2 126 else: 127 x = x0 128 y = l.YM 129 for i in range(foundations): 130 s.foundations.append(self.Foundation_Class(x, y, self, suit=i)) 131 x = x + l.XS 132 x, y = x0, l.YM + l.YS 133 for i in range(rows): 134 s.rows.append(self.RowStack_Class(x, y, self)) 135 x = x + l.XS 136 self.setRegion(s.rows, (x0-l.XS//2, y-l.CH//2, 999999, 999999)) 137 x, y = l.XM, l.YM+l.YS+l.YS//2*int(reserve) 138 s.talon = WasteTalonStack(x, y, self, max_rounds=max_rounds) 139 if reserve or waste_max_cards > 1: 140 l.createText(s.talon, 'ne') 141 else: 142 l.createText(s.talon, 'n') 143 y = y + l.YS 144 s.waste = WasteStack(x, y, self, max_cards=waste_max_cards) 145 if waste_max_cards > 1: 146 l.createText(s.waste, 'ne') 147 if reserve: 148 s.reserves.append(self.ReserveStack_Class(l.XM, l.YM, self)) 149 150 # define stack-groups 151 l.defaultStackGroups() 152 153 return l 154 155 # 156 # game overrides 157 # 158 159 def startGame(self): 160 self.startDealSample() 161 self.s.talon.dealCards() # deal first card to WasteStack 162 163 shallHighlightMatch = Game._shallHighlightMatch_SS 164 165 def getHighlightPilesStacks(self): 166 return () 167 168 169class Numerica2Decks(Numerica): 170 def createGame(self): 171 Numerica.createGame(self, rows=6) 172 173 174# ************************************************************************ 175# * Lady Betty 176# * Last Chance 177# ************************************************************************ 178 179class LadyBetty(Numerica): 180 Foundation_Class = SS_FoundationStack 181 182 def createGame(self): 183 Numerica.createGame(self, rows=6) 184 185 186class LastChance_RowStack(Numerica_RowStack): 187 def acceptsCards(self, from_stack, cards): 188 if not BasicRowStack.acceptsCards(self, from_stack, cards): 189 return False 190 if not self.cards: 191 return True 192 return from_stack is self.game.s.waste and len(cards) == 1 193 194 195class LastChance_Reserve(OpenStack): 196 def canFlipCard(self): 197 return (len(self.game.s.talon.cards) == 0 and 198 len(self.game.s.waste.cards) == 0 and 199 self.cards and not self.cards[0].face_up) 200 201 202class LastChance(LadyBetty): 203 RowStack_Class = StackWrapper(LastChance_RowStack, max_accept=1) 204 ReserveStack_Class = LastChance_Reserve 205 206 def createGame(self): 207 Numerica.createGame(self, rows=7, reserve=True) 208 209 def startGame(self): 210 self.startDealSample() 211 self.s.talon.dealRow() 212 self.s.talon.dealRow(rows=self.s.reserves, flip=False) 213 self.s.talon.dealCards() 214 215 216# ************************************************************************ 217# * Puss in the Corner 218# ************************************************************************ 219 220class PussInTheCorner_Talon(OpenTalonStack): 221 rightclickHandler = OpenStack.rightclickHandler 222 doubleclickHandler = OpenStack.doubleclickHandler 223 224 def canDealCards(self): 225 if self.round != self.max_rounds: 226 return True 227 return False 228 229 def clickHandler(self, event): 230 if self.cards: 231 return OpenStack.clickHandler(self, event) 232 else: 233 return TalonStack.clickHandler(self, event) 234 235 def dealCards(self, sound=False): 236 ncards = 0 237 old_state = self.game.enterState(self.game.S_DEAL) 238 if not self.cards and self.round != self.max_rounds: 239 self.game.nextRoundMove(self) 240 self.game.startDealSample() 241 for r in self.game.s.rows: 242 while r.cards: 243 self.game.moveMove(1, r, self, frames=4) 244 self.game.flipMove(self) 245 ncards += 1 246 self.fillStack() 247 self.game.stopSamples() 248 self.game.leaveState(old_state) 249 return ncards 250 251 252class PussInTheCorner_Foundation(SS_FoundationStack): 253 def __init__(self, x, y, game, **cap): 254 kwdefault(cap, base_suit=ANY_SUIT) 255 SS_FoundationStack.__init__(self, x, y, game, ANY_SUIT, **cap) 256 257 def acceptsCards(self, from_stack, cards): 258 if not SS_FoundationStack.acceptsCards(self, from_stack, cards): 259 return False 260 if self.cards: 261 # check the color 262 if cards[0].color != self.cards[-1].color: 263 return False 264 return True 265 266 def getHelp(self): 267 return _('Foundation. Build up by color.') 268 269 270class PussInTheCorner_RowStack(BasicRowStack): 271 272 def acceptsCards(self, from_stack, cards): 273 if not BasicRowStack.acceptsCards(self, from_stack, cards): 274 return False 275 # this stack accepts any one card from the Talon 276 return from_stack is self.game.s.talon and len(cards) == 1 277 278 getBottomImage = Stack._getReserveBottomImage 279 280 def getHelp(self): 281 # return _('Tableau. Accepts any one card from the Waste.') 282 return _('Tableau. Build regardless of rank and suit.') 283 284 285class PussInTheCorner(Numerica): 286 287 def createGame(self, rows=4): 288 l, s = Layout(self), self.s 289 self.setSize(l.XM+5*l.XS, l.YM+4*l.YS) 290 for x, y in ((l.XM, l.YM), 291 (l.XM+4*l.XS, l.YM), 292 (l.XM, l.YM+3*l.YS), 293 (l.XM+4*l.XS, l.YM+3*l.YS), 294 ): 295 stack = PussInTheCorner_RowStack(x, y, self, 296 max_accept=1, max_move=1) 297 stack.CARD_XOFFSET, stack.CARD_YOFFSET = 0, 0 298 s.rows.append(stack) 299 for x, y in ((l.XM+1.5*l.XS, l.YM + l.YS), 300 (l.XM+1.5*l.XS, l.YM + 2*l.YS), 301 (l.XM+2.5*l.XS, l.YM + l.YS), 302 (l.XM+2.5*l.XS, l.YM + 2*l.YS), 303 ): 304 s.foundations.append(PussInTheCorner_Foundation(x, y, self, 305 max_move=0)) 306 x, y = l.XM + 2*l.XS, l.YM 307 s.waste = s.talon = PussInTheCorner_Talon(x, y, self, max_rounds=2) 308 l.createText(s.talon, 'se') 309 l.createRoundText(self.s.talon, 'ne') 310 311 # define stack-groups 312 l.defaultStackGroups() 313 314 def _shuffleHook(self, cards): 315 return self._shuffleHookMoveToTop( 316 cards, lambda c: (c.rank == ACE, c.suit)) 317 318 def startGame(self): 319 self.startDealSample() 320 self.s.talon.dealRow(rows=self.s.foundations) 321 self.s.talon.fillStack() 322 323 def _autoDeal(self, sound=True): 324 return 0 325 326 327# ************************************************************************ 328# * Frog 329# * Fly 330# * Fanny 331# ************************************************************************ 332 333class Frog(Game): 334 335 Hint_Class = Numerica_Hint 336 # Foundation_Class = SS_FoundationStack 337 Foundation_Class = RK_FoundationStack 338 339 def createGame(self): 340 # create layout 341 l, s = Layout(self), self.s 342 343 # set window 344 self.setSize(l.XM + 8*l.XS, l.YM + 2*l.YS+16*l.YOFFSET) 345 346 # create stacks 347 x, y, = l.XM, l.YM 348 for i in range(8): 349 if self.Foundation_Class is RK_FoundationStack: 350 suit = ANY_SUIT 351 else: 352 suit = int(i//2) 353 s.foundations.append(self.Foundation_Class(x, y, self, 354 suit=suit, max_move=0)) 355 x += l.XS 356 x, y = l.XM, l.YM+l.YS 357 stack = OpenStack(x, y, self, max_accept=0) 358 stack.CARD_XOFFSET, stack.CARD_YOFFSET = 0, l.YOFFSET 359 s.reserves.append(stack) 360 x += l.XS 361 s.talon = WasteTalonStack(x, y, self, max_rounds=1) 362 l.createText(s.talon, "s") 363 x += l.XS 364 s.waste = WasteStack(x, y, self, max_cards=1) 365 x += l.XS 366 for i in range(5): 367 stack = Numerica_RowStack(x, y, self, max_accept=UNLIMITED_ACCEPTS) 368 # stack.CARD_XOFFSET, stack.CARD_YOFFSET = 0, l.YOFFSET 369 s.rows.append(stack) 370 x = x + l.XS 371 372 # define stack-groups 373 l.defaultStackGroups() 374 375 def startGame(self): 376 self.startDealSample() 377 n = 0 378 f = 0 379 while True: 380 c = self.s.talon.cards[-1] 381 if c.rank == ACE: 382 r = self.s.foundations[f] 383 f += 1 384 # r = self.s.foundations[c.suit*2] 385 else: 386 r = self.s.reserves[0] 387 n += 1 388 self.s.talon.dealRow(rows=[r]) 389 if n == 13: 390 break 391 self.s.talon.dealCards() 392 393 394class Fly(Frog): 395 396 Foundation_Class = RK_FoundationStack 397 398 def _shuffleHook(self, cards): 399 return self._shuffleHookMoveToTop( 400 cards, lambda c: (c.rank == ACE, c.suit)) 401 402 def startGame(self): 403 self.startDealSample() 404 self.s.talon.dealRow(rows=self.s.foundations) 405 for i in range(13): 406 self.s.talon.dealRow(self.s.reserves) 407 self.s.talon.dealCards() 408 409 410class Fanny(Frog): 411 412 Foundation_Class = RK_FoundationStack 413 414 def startGame(self): 415 self.startDealSample() 416 for i in range(11): 417 self.s.talon.dealRow(self.s.reserves, flip=0) 418 self.s.talon.dealRow(self.s.reserves) 419 self.s.talon.dealCards() 420 421 422# ************************************************************************ 423# * Gnat 424# ************************************************************************ 425 426class Gnat(Game): 427 428 Hint_Class = Numerica_Hint 429 430 def createGame(self): 431 # create layout 432 l, s = Layout(self), self.s 433 434 # set window 435 self.setSize(l.XM + 8*l.XS, l.YM + 2*l.YS+16*l.YOFFSET) 436 437 # create stacks 438 x, y = l.XM, l.YM 439 s.talon = WasteTalonStack(x, y, self, max_rounds=1) 440 l.createText(s.talon, "s") 441 x += l.XS 442 s.waste = WasteStack(x, y, self, max_cards=1) 443 x += l.XS 444 for i in range(4): 445 s.foundations.append(SS_FoundationStack(x, y, self, suit=i)) 446 x += l.XS 447 448 x, y = l.XM+2*l.XS, l.YM+l.YS 449 for i in range(4): 450 s.rows.append( 451 Numerica_RowStack(x, y, self, max_accept=UNLIMITED_ACCEPTS)) 452 x += l.XS 453 x = l.XM+6*l.XS 454 for i in range(2): 455 y = l.YM + l.YS//2 456 for j in range(3): 457 s.reserves.append(OpenStack(x, y, self, max_accept=0)) 458 y += l.YS 459 x += l.XS 460 461 # define stack-groups 462 l.defaultStackGroups() 463 464 def _shuffleHook(self, cards): 465 return self._shuffleHookMoveToTop( 466 cards, lambda c: (c.rank == ACE, c.suit)) 467 468 def startGame(self): 469 self.startDealSample() 470 self.s.talon.dealRow(rows=self.s.foundations) 471 self.s.talon.dealRow(rows=self.s.reserves) 472 self.s.talon.dealCards() 473 474 475# ************************************************************************ 476# * Gloaming 477# * Chamberlain 478# ************************************************************************ 479 480class Gloaming_Hint(Numerica_Hint): 481 def computeHints(self): 482 self.step010(self.game.s.rows, self.game.s.rows) 483 self.step060(self.game.sg.reservestacks, self.game.s.rows) 484 485 # try if we should move a card from a ReserveStack to a RowStack 486 def step060(self, reservestacks, rows): 487 for r in reservestacks: 488 if not r.cards: 489 continue 490 for t in rows: 491 if t.cards: 492 score = self._computeScore(r, t) 493 self.addHint(score, 1, r, t) 494 else: 495 self.addHint(90000+r.cards[-1].rank, 1, r, t) 496 497 498class Gloaming_RowStack(Numerica_RowStack): 499 def acceptsCards(self, from_stack, cards): 500 if not BasicRowStack.acceptsCards(self, from_stack, cards): 501 return False 502 # this stack accepts any one card from reserves 503 return from_stack in self.game.s.reserves 504 505 506class Gloaming(Game): 507 508 Hint_Class = Gloaming_Hint 509 Foundation_Class = SS_FoundationStack 510 511 def createGame(self, reserves=3, rows=5): 512 # create layout 513 l, s = Layout(self), self.s 514 515 # set window 516 n = 52//reserves+1 517 w, h = l.XM + (reserves+rows+1)*l.XS, l.YM + 2*l.YS+n*l.YOFFSET 518 self.setSize(w, h) 519 520 # create stacks 521 x, y = l.XM+(reserves+rows+1-4)*l.XS//2, l.YM 522 for i in range(4): 523 if self.Foundation_Class is RK_FoundationStack: 524 suit = ANY_SUIT 525 else: 526 suit = i 527 s.foundations.append(self.Foundation_Class(x, y, self, 528 suit=suit, max_move=0)) 529 x += l.XS 530 531 x, y = l.XM, l.YM+l.YS 532 for i in range(reserves): 533 stack = OpenStack(x, y, self, max_accept=0) 534 stack.CARD_XOFFSET, stack.CARD_YOFFSET = 0, l.YOFFSET 535 s.reserves.append(stack) 536 x += l.XS 537 538 x += l.XS 539 for i in range(rows): 540 s.rows.append( 541 Gloaming_RowStack(x, y, self, max_accept=UNLIMITED_ACCEPTS)) 542 x += l.XS 543 544 s.talon = InitialDealTalonStack(w-l.XS, h-l.YS, self) 545 546 # default 547 l.defaultAll() 548 549 def startGame(self): 550 n = 52//len(self.s.reserves)+1 551 for i in range(n-3): 552 self.s.talon.dealRow(rows=self.s.reserves, frames=0) 553 self.startDealSample() 554 self.s.talon.dealRow(rows=self.s.reserves) 555 self.s.talon.dealRow(rows=self.s.reserves) 556 self.s.talon.dealRowAvail(rows=self.s.reserves) 557 558 559class Chamberlain(Gloaming): 560 Foundation_Class = RK_FoundationStack 561 562 def createGame(self, reserves=3, rows=5): 563 Gloaming.createGame(self, reserves=4, rows=3) 564 565 566# ************************************************************************ 567# * Toad 568# ************************************************************************ 569 570 571class Toad_TalonStack(DealRowTalonStack): 572 def canDealCards(self): 573 if not DealRowTalonStack.canDealCards(self): 574 return False 575 for r in self.game.s.reserves: 576 if r.cards: 577 return False 578 return True 579 580 def dealCards(self, sound=False): 581 self.dealRow(rows=self.game.s.reserves, sound=sound) 582 583 584class Toad(Game): 585 Hint_Class = Gloaming_Hint 586 587 def createGame(self, reserves=3, rows=5): 588 # create layout 589 l, s = Layout(self), self.s 590 591 # set window 592 w, h = l.XM+11*l.XS, l.YM+6*l.YS 593 self.setSize(w, h) 594 595 # create stacks 596 x, y = w-l.XS, h-l.YS 597 s.talon = Toad_TalonStack(x, y, self) 598 l.createText(s.talon, "n") 599 x, y = l.XM, l.YM 600 for i in range(8): 601 s.foundations.append(SS_FoundationStack(x, y, self, suit=i//2)) 602 x += l.XS 603 x, y = l.XM+3*l.XS//2, l.YM+l.YS 604 for i in range(5): 605 s.rows.append( 606 Gloaming_RowStack(x, y, self, max_accept=UNLIMITED_ACCEPTS)) 607 x += l.XS 608 y = l.YM+l.YS//2 609 for i in (3, 3, 3, 3, 1): 610 x = l.XM+8*l.XS 611 for j in range(i): 612 s.reserves.append(OpenStack(x, y, self, max_accept=0)) 613 x += l.XS 614 y += l.YS 615 616 # define stack-groups 617 l.defaultStackGroups() 618 619 def startGame(self): 620 self.startDealSample() 621 self.s.talon.dealRow(rows=self.s.reserves) 622 623 624# ************************************************************************ 625# * Shifting 626# ************************************************************************ 627 628class Shifting_Hint(Numerica_Hint): 629 shallMovePile = DefaultHint._cautiousShallMovePile 630 631 632class Shifting_RowStack(Numerica_RowStack): 633 def acceptsCards(self, from_stack, cards): 634 if not BasicRowStack.acceptsCards(self, from_stack, cards): 635 return False 636 if from_stack is self.game.s.waste: 637 return True 638 if not self.cards: 639 return cards[0].rank == KING 640 if (from_stack in self.game.s.rows and 641 self.cards[-1].rank-cards[0].rank == 1): 642 return True 643 return False 644 645 646class Shifting(Numerica): 647 Hint_Class = Shifting_Hint 648 RowStack_Class = StackWrapper(Shifting_RowStack, max_accept=1) 649 650 651# ************************************************************************ 652# * Strategerie 653# ************************************************************************ 654 655class Strategerie_Talon(OpenTalonStack): 656 rightclickHandler = OpenStack.rightclickHandler 657 doubleclickHandler = OpenStack.doubleclickHandler 658 659 660class Strategerie_RowStack(BasicRowStack): 661 662 def acceptsCards(self, from_stack, cards): 663 if not BasicRowStack.acceptsCards(self, from_stack, cards): 664 return False 665 if from_stack is self.game.s.talon or \ 666 from_stack in self.game.s.reserves: 667 return True 668 return False 669 670 getBottomImage = Stack._getReserveBottomImage 671 672 def getHelp(self): 673 return _('Tableau. Build regardless of rank and suit.') 674 675 676class Strategerie_ReserveStack(ReserveStack): 677 def acceptsCards(self, from_stack, cards): 678 if not ReserveStack.acceptsCards(self, from_stack, cards): 679 return False 680 if from_stack is self.game.s.talon: 681 return True 682 return False 683 684 685class Strategerie(Game): 686 Hint_Class = Numerica_Hint 687 688 def createGame(self, **layout): 689 # create layout 690 l, s = Layout(self), self.s 691 l.freeCellLayout(rows=4, reserves=4, texts=1) 692 self.setSize(l.size[0], l.size[1]) 693 # create stacks 694 s.talon = Strategerie_Talon(l.s.talon.x, l.s.talon.y, self) 695 for r in l.s.foundations: 696 s.foundations.append(RK_FoundationStack(r.x, r.y, self)) 697 for r in l.s.rows: 698 s.rows.append(Strategerie_RowStack(r.x, r.y, self, 699 max_accept=UNLIMITED_ACCEPTS)) 700 for r in l.s.reserves: 701 s.reserves.append(Strategerie_ReserveStack(r.x, r.y, self)) 702 # default 703 l.defaultAll() 704 self.sg.dropstacks.append(s.talon) 705 706 def startGame(self): 707 self.startDealSample() 708 self.s.talon.fillStack() 709 710 711# ************************************************************************ 712# * Assembly 713# * Anno Domini 714# ************************************************************************ 715 716class Assembly_RowStack(RK_RowStack): 717 def acceptsCards(self, from_stack, cards): 718 if not RK_RowStack.acceptsCards(self, from_stack, cards): 719 return False 720 if not self.cards: 721 return from_stack is self.game.s.waste 722 return True 723 724 725class Assembly(Numerica): 726 Hint_Class = CautiousDefaultHint 727 728 Foundation_Class = StackWrapper(RK_FoundationStack, suit=ANY_SUIT) 729 RowStack_Class = StackWrapper(Assembly_RowStack, max_move=1) 730 731 def createGame(self): 732 Numerica.createGame(self, waste_max_cards=UNLIMITED_CARDS) 733 734 def startGame(self): 735 self.startDealSample() 736 self.s.talon.dealRow() 737 self.s.talon.dealCards() 738 739 shallHighlightMatch = Game._shallHighlightMatch_RK 740 741 742class AnnoDomini_Hint(DefaultHint): 743 def step030(self, foundations, rows, dropstacks): 744 pass 745 746 747class AnnoDomini(Numerica): 748 Hint_Class = AnnoDomini_Hint 749 750 Foundation_Class = StackWrapper(SS_FoundationStack, suit=ANY_SUIT, mod=13) 751 RowStack_Class = StackWrapper(AC_RowStack, mod=13) 752 753 def createGame(self): 754 lay = Numerica.createGame( 755 self, max_rounds=3, waste_max_cards=UNLIMITED_CARDS) 756 year = str(time.localtime()[0]) 757 i = 0 758 for s in self.s.foundations: 759 # setup base_rank & base_suit 760 s.cap.suit = i 761 s.cap.base_suit = i 762 d = int(year[i]) 763 if d == 0: 764 d = JACK 765 s.cap.base_rank = d 766 i += 1 767 lay.createRoundText(self.s.talon, 'nn') 768 769 def startGame(self): 770 self.startDealSample() 771 self.s.talon.dealRow() 772 self.s.talon.dealCards() 773 774 shallHighlightMatch = Game._shallHighlightMatch_ACW 775 776 777# ************************************************************************ 778# * Circle Nine 779# * Measure 780# * Double Measure 781# ************************************************************************ 782 783class CircleNine_RowStack(BasicRowStack): 784 def acceptsCards(self, from_stack, cards): 785 if not BasicRowStack.acceptsCards(self, from_stack, cards): 786 return False 787 return from_stack is self.game.s.talon 788 789 def getHelp(self): 790 return _('Tableau. Build regardless of rank and suit.') 791 792 793class CircleNine(Game): 794 Hint_Class = Numerica_Hint 795 796 def createGame(self): 797 l, s = Layout(self), self.s 798 self.setSize(l.XM+7*l.XS, l.YM+3*l.YS) 799 800 for i, j in ((1, 0), 801 (2, 0), 802 (3, 0), 803 (4, 0), 804 (5, 1), 805 (3.5, 2), 806 (2.5, 2), 807 (1.5, 2), 808 (0, 1), 809 ): 810 x, y = l.XM+(1+i)*l.XS, l.YM+j*l.YS 811 stack = CircleNine_RowStack(x, y, self, max_accept=1, 812 max_move=1, base_rank=NO_RANK) 813 s.rows.append(stack) 814 stack.CARD_YOFFSET = 0 815 816 x, y = l.XM+3.5*l.XS, l.YM+l.YS 817 stack = RK_FoundationStack(x, y, self, suit=ANY_SUIT, max_cards=52, 818 max_move=0, mod=13, base_rank=ANY_RANK) 819 s.foundations.append(stack) 820 l.createText(stack, 'ne') 821 x, y = l.XM, l.YM 822 s.talon = Strategerie_Talon(x, y, self) 823 l.createText(s.talon, 'ne') 824 825 l.defaultStackGroups() 826 self.sg.dropstacks.append(s.talon) 827 828 def startGame(self): 829 self.startDealSample() 830 self.s.talon.dealRow(rows=self.s.foundations) 831 self.s.talon.dealRow() 832 self.s.talon.fillStack() 833 834 def fillStack(self, stack): 835 if stack in self.s.rows and not stack.cards: 836 if self.s.talon.cards: 837 old_state = self.enterState(self.S_FILL) 838 self.s.talon.moveMove(1, stack) 839 self.leaveState(old_state) 840 841 842class Measure(CircleNine): 843 844 Foundation_Class = StackWrapper(RK_FoundationStack, max_cards=52) 845 846 def createGame(self, rows=8): 847 l, s = Layout(self), self.s 848 self.setSize(l.XM+rows*l.XS, l.YM+2*l.YS+10*l.YOFFSET) 849 850 x, y = l.XM, l.YM 851 s.talon = Strategerie_Talon(x, y, self) 852 l.createText(s.talon, 'ne') 853 x = self.width-l.XS 854 stack = self.Foundation_Class(x, y, self, suit=ANY_SUIT, max_cards=52, 855 max_move=0, mod=13, base_rank=ANY_RANK) 856 s.foundations.append(stack) 857 l.createText(stack, 'nw') 858 859 x, y = l.XM, l.YM+l.YS 860 for i in range(rows): 861 s.rows.append(CircleNine_RowStack(x, y, self, max_accept=1, 862 max_move=1, base_rank=NO_RANK)) 863 x += l.XS 864 865 l.defaultStackGroups() 866 self.sg.dropstacks.append(s.talon) 867 868 def startGame(self): 869 self.startDealSample() 870 self.s.talon.dealRow() 871 self.s.talon.fillStack() 872 873 874class DoubleMeasure(Measure): 875 Foundation_Class = StackWrapper(RK_FoundationStack, max_cards=104) 876 877 def createGame(self, rows=8): 878 Measure.createGame(self, rows=10) 879 880 881# ************************************************************************ 882# * Amphibian 883# ************************************************************************ 884 885class Amphibian(Game): 886 Hint_Class = Gloaming_Hint 887 888 def createGame(self, rows=5, reserves=4, playcards=15): 889 # create layout 890 l, s = Layout(self), self.s 891 892 # set window 893 self.setSize(l.XM + 8 * l.XS, l.YM + 3*l.YS + playcards*l.YOFFSET) 894 895 # create stacks 896 x, y = l.XM, l.YM 897 for i in range(4): 898 for j in range(2): 899 s.foundations.append(RK_FoundationStack(x, y, self, 900 suit=ANY_SUIT)) 901 x += l.XS 902 x, y = l.XM+(8-rows)*l.XS//2, l.YM + l.YS 903 for i in range(rows): 904 s.rows.append(Gloaming_RowStack(x, y, self, max_accept=1)) 905 x += l.XS 906 907 x, y = l.XM+(8-reserves-1)*l.XS//2, self.height-l.YS 908 for i in range(reserves): 909 s.reserves.append(OpenStack(x, y, self, max_accept=0)) 910 x += l.XS 911 912 s.talon = TalonStack(x, y, self) 913 l.createText(s.talon, 'n') 914 915 # define stack-groups 916 l.defaultStackGroups() 917 918 def startGame(self): 919 self.startDealSample() 920 self.s.talon.dealRow(rows=self.s.reserves) 921 922 def fillStack(self, stack): 923 if stack in self.s.reserves: 924 for stack in self.s.reserves: 925 if stack.cards: 926 return 927 old_state = self.enterState(self.S_FILL) 928 self.s.talon.dealRow(rows=self.s.reserves, sound=1) 929 self.leaveState(old_state) 930 931 932# ************************************************************************ 933# * Aglet 934# ************************************************************************ 935 936class Aglet(Game): 937 938 def createGame(self, playcards=20, rows=8, reserves=1): 939 940 decks = self.gameinfo.decks 941 l, s = Layout(self), self.s 942 self.setSize(l.XM+(reserves+0.5+rows)*l.XS, 943 l.YM+max(2*l.YS+7*l.YOFFSET, l.YS+playcards*l.YOFFSET)) 944 945 x, y = self.width-l.XS, self.height-l.YS 946 s.talon = InitialDealTalonStack(x, y, self) 947 948 x, y = l.XM, l.YM 949 for i in range(reserves): 950 stack = ReserveStack(x, y, self, max_cards=UNLIMITED_CARDS) 951 stack.CARD_YOFFSET = l.YOFFSET 952 s.reserves.append(stack) 953 x += l.XS 954 955 x, y = l.XM + (reserves+0.5+(rows-decks*4)/2.0)*l.XS, l.YM 956 for i in range(4): 957 s.foundations.append(RK_FoundationStack(x, y, self, suit=ANY_SUIT)) 958 x += l.XS 959 960 x, y = l.XM+(reserves+0.5)*l.XS, l.YM+l.YS 961 for i in range(rows): 962 s.rows.append(BasicRowStack(x, y, self, base_rank=NO_RANK)) 963 x += l.XS 964 965 l.defaultStackGroups() 966 967 def _shuffleHook(self, cards): 968 # move Aces to top of the Talon (i.e. first cards to be dealt) 969 return self._shuffleHookMoveToTop( 970 cards, lambda c: (c.rank == ACE, c.suit)) 971 972 def startGame(self): 973 self.s.talon.dealRow(rows=self.s.foundations, frames=0) 974 self._startDealNumRows(4) 975 self.s.talon.dealRowAvail() 976 self.s.talon.dealRowAvail() 977 978 979# register the game 980registerGame(GameInfo(257, Numerica, "Numerica", 981 GI.GT_NUMERICA | GI.GT_CONTRIB, 1, 0, GI.SL_BALANCED, 982 altnames=("Sir Tommy",))) 983registerGame(GameInfo(171, LadyBetty, "Lady Betty", 984 GI.GT_NUMERICA, 1, 0, GI.SL_BALANCED)) 985registerGame(GameInfo(355, Frog, "Frog", 986 GI.GT_NUMERICA, 2, 0, GI.SL_BALANCED)) 987registerGame(GameInfo(356, Fly, "Fly", 988 GI.GT_NUMERICA, 2, 0, GI.SL_BALANCED, 989 rules_filename='frog.html')) 990registerGame(GameInfo(357, Gnat, "Gnat", 991 GI.GT_NUMERICA, 1, 0, GI.SL_BALANCED)) 992registerGame(GameInfo(378, Gloaming, "Gloaming", 993 GI.GT_NUMERICA | GI.GT_OPEN | GI.GT_ORIGINAL, 1, 0, 994 GI.SL_MOSTLY_SKILL)) 995registerGame(GameInfo(379, Chamberlain, "Chamberlain", 996 GI.GT_NUMERICA | GI.GT_OPEN | GI.GT_ORIGINAL, 1, 0, 997 GI.SL_MOSTLY_SKILL)) 998registerGame(GameInfo(402, Toad, "Toad", 999 GI.GT_NUMERICA, 2, 0, GI.SL_BALANCED)) 1000registerGame(GameInfo(430, PussInTheCorner, "Puss in the Corner", 1001 GI.GT_NUMERICA, 1, 1, GI.SL_BALANCED)) 1002registerGame(GameInfo(435, Shifting, "Shifting", 1003 GI.GT_NUMERICA, 1, 0, GI.SL_BALANCED)) 1004registerGame(GameInfo(472, Strategerie, "Strategerie", 1005 GI.GT_NUMERICA, 1, 0, GI.SL_MOSTLY_SKILL)) 1006registerGame(GameInfo(558, Numerica2Decks, "Numerica (2 decks)", 1007 GI.GT_NUMERICA, 2, 0, GI.SL_BALANCED)) 1008registerGame(GameInfo(589, LastChance, "Last Chance", 1009 GI.GT_NUMERICA, 1, 0, GI.SL_BALANCED)) 1010registerGame(GameInfo(599, Assembly, "Assembly", 1011 GI.GT_NUMERICA, 1, 0, GI.SL_BALANCED)) 1012registerGame(GameInfo(600, AnnoDomini, "Anno Domini", 1013 GI.GT_NUMERICA, 1, 2, GI.SL_BALANCED)) 1014registerGame(GameInfo(613, Fanny, "Fanny", 1015 GI.GT_NUMERICA, 2, 0, GI.SL_BALANCED)) 1016registerGame(GameInfo(641, CircleNine, "Circle Nine", 1017 GI.GT_NUMERICA, 1, 0, GI.SL_BALANCED)) 1018registerGame(GameInfo(643, Measure, "Measure", 1019 GI.GT_NUMERICA | GI.GT_ORIGINAL, 1, 0, 1020 GI.SL_MOSTLY_SKILL)) 1021registerGame(GameInfo(644, DoubleMeasure, "Double Measure", 1022 GI.GT_NUMERICA | GI.GT_ORIGINAL, 2, 0, 1023 GI.SL_MOSTLY_SKILL)) 1024registerGame(GameInfo(754, Amphibian, "Amphibian", 1025 GI.GT_NUMERICA | GI.GT_ORIGINAL, 2, 0, 1026 GI.SL_MOSTLY_SKILL)) 1027registerGame(GameInfo(760, Aglet, "Aglet", 1028 GI.GT_1DECK_TYPE | GI.GT_OPEN | GI.GT_ORIGINAL, 1, 0, 1029 GI.SL_MOSTLY_SKILL)) 1030