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.unionsquare import UnionSquare_Foundation 27from pysollib.hint import CautiousDefaultHint, DefaultHint 28from pysollib.layout import Layout 29from pysollib.mygettext import _ 30from pysollib.stack import \ 31 AC_FoundationStack, \ 32 AC_RowStack, \ 33 AbstractFoundationStack, \ 34 BasicRowStack, \ 35 DealRowTalonStack, \ 36 OpenStack, \ 37 OpenTalonStack, \ 38 RK_FoundationStack, \ 39 RK_RowStack, \ 40 ReserveStack, \ 41 SS_FoundationStack, \ 42 SS_RowStack, \ 43 Stack, \ 44 StackWrapper, \ 45 UD_RK_RowStack, \ 46 UD_SS_RowStack, \ 47 WasteStack, \ 48 WasteTalonStack, \ 49 isSameSuitSequence 50from pysollib.util import ACE, ANY_RANK, ANY_SUIT, JACK, KING, NO_RANK 51 52# ************************************************************************ 53# * Royal Cotillion 54# ************************************************************************ 55 56 57class RoyalCotillion_Foundation(SS_FoundationStack): 58 def getBottomImage(self): 59 if self.cap.base_rank == 1: 60 return self.game.app.images.getLetter(1) 61 return self.game.app.images.getSuitBottom(self.cap.base_suit) 62 63 64class RoyalCotillion(Game): 65 Foundation_Class = RoyalCotillion_Foundation 66 67 # 68 # game layout 69 # 70 71 def createGame(self): 72 # create layout 73 l, s = Layout(self), self.s 74 75 # set window 76 self.setSize(l.XM + 10*l.XS, l.YM + 4*l.YS) 77 78 # create stacks 79 for i in range(4): 80 x, y, = l.XM + i*l.XS, l.YM 81 s.rows.append(BasicRowStack(x, y, self, max_accept=0)) 82 for i in range(4): 83 x, y, = l.XM + 4*l.XS, l.YM + i*l.YS 84 s.foundations.append( 85 self.Foundation_Class(x, y, self, i, dir=2, mod=13)) 86 x += l.XS 87 s.foundations.append( 88 self.Foundation_Class( 89 x, y, self, i, dir=2, mod=13, base_rank=1)) 90 for i in range(4): 91 for j in range(4): 92 x, y, = l.XM + (j+6)*l.XS, l.YM + i*l.YS 93 s.reserves.append(ReserveStack(x, y, self, max_accept=0)) 94 x, y = l.XM + l.XS, self.height - l.YS 95 s.talon = WasteTalonStack(x, y, self, max_rounds=1) 96 l.createText(s.talon, "sw") 97 x += l.XS 98 s.waste = WasteStack(x, y, self) 99 l.createText(s.waste, "se") 100 101 # define stack-groups 102 l.defaultStackGroups() 103 104 # 105 # game overrides 106 # 107 108 def startGame(self): 109 self.s.talon.dealRow(rows=self.s.reserves, frames=0) 110 self.startDealSample() 111 for i in range(3): 112 self.s.talon.dealRow() 113 self.s.talon.dealCards() # deal first card to WasteStack 114 115 def fillStack(self, stack): 116 if not stack.cards: 117 old_state = self.enterState(self.S_FILL) 118 if stack is self.s.waste and self.s.talon.cards: 119 self.s.talon.dealCards() 120 elif stack in self.s.reserves and self.s.waste.cards: 121 self.s.waste.moveMove(1, stack) 122 self.leaveState(old_state) 123 124 def getHighlightPilesStacks(self): 125 return () 126 127 def getAutoStacks(self, event=None): 128 if event is None: 129 # disable auto drop - this would ruin the whole gameplay 130 return (self.sg.dropstacks, (), self.sg.dropstacks) 131 else: 132 # rightclickHandler 133 return (self.sg.dropstacks, self.sg.dropstacks, self.sg.dropstacks) 134 135 136# ************************************************************************ 137# * Odd and Even 138# ************************************************************************ 139 140class OddAndEven(RoyalCotillion): 141 def createGame(self): 142 # create layout 143 l, s = Layout(self), self.s 144 145 # set window 146 self.setSize(l.XM + 8*l.XS, l.YM + 4*l.YS) 147 148 # create stacks 149 x, y, = l.XM, l.YM 150 for i in range(4): 151 s.foundations.append( 152 self.Foundation_Class(x, y, self, i, dir=2, mod=13)) 153 x += l.XS 154 for i in range(4): 155 s.foundations.append( 156 self.Foundation_Class( 157 x, y, self, i, dir=2, mod=13, base_rank=1)) 158 x += l.XS 159 for i in range(2): 160 x, y, = l.XM + ((4, 3)[i])*l.XS, l.YM + (i+1)*l.YS 161 for j in range((4, 5)[i]): 162 s.reserves.append(ReserveStack(x, y, self, max_accept=0)) 163 x += l.XS 164 x, y = l.XM, self.height - l.YS 165 s.talon = WasteTalonStack(x, y, self, max_rounds=2) 166 l.createText(s.talon, "n") 167 l.createRoundText(s.talon, 'nnn') 168 x += l.XS 169 s.waste = WasteStack(x, y, self) 170 l.createText(s.waste, "n") 171 172 # define stack-groups 173 l.defaultStackGroups() 174 175 # 176 # game overrides 177 # 178 179 def startGame(self): 180 self.startDealSample() 181 self.s.talon.dealRow(rows=self.s.reserves) 182 self.s.talon.dealCards() # deal first card to WasteStack 183 184 185# ************************************************************************ 186# * Kingdom 187# ************************************************************************ 188 189class Kingdom(RoyalCotillion): 190 Foundation_Class = RK_FoundationStack 191 192 def createGame(self): 193 # create layout 194 l, s = Layout(self), self.s 195 196 # set window 197 self.setSize(l.XM + 8*l.XS, l.YM + 4*l.YS) 198 199 # create stacks 200 x, y, = l.XM, l.YM 201 for i in range(8): 202 s.foundations.append(self.Foundation_Class(x, y, self, ANY_SUIT)) 203 x += l.XS 204 x, y, = l.XM, y + l.YS 205 for i in range(8): 206 s.reserves.append(ReserveStack(x, y, self, max_accept=0)) 207 x += l.XS 208 x, y = l.XM + 3*l.XS, l.YM + 3*l.YS 209 s.talon = WasteTalonStack(x, y, self, max_rounds=1) 210 l.createText(s.talon, "sw") 211 x += l.XS 212 s.waste = WasteStack(x, y, self) 213 l.createText(s.waste, "se") 214 215 # define stack-groups 216 l.defaultStackGroups() 217 218 # 219 # game overrides 220 # 221 222 def _shuffleHook(self, cards): 223 # move one Ace to top of the Talon (i.e. first card to be dealt) 224 return self._shuffleHookMoveToTop( 225 cards, lambda c: (c.rank == 0, c.suit), 1) 226 227 def startGame(self): 228 self.startDealSample() 229 self.s.talon.dealRow(rows=(self.s.foundations[0],)) 230 self.s.talon.dealRow(rows=self.s.reserves) 231 self.s.talon.dealCards() # deal first card to WasteStack 232 233 234# ************************************************************************ 235# * Alhambra 236# * Granada 237# * Reserves 238# * Grant's Reinforcement 239# ************************************************************************ 240 241class Alhambra_Hint(CautiousDefaultHint): 242 def _getDropCardScore(self, score, color, r, t, ncards): 243 return 93000, color 244 245 246class Alhambra_RowStack(UD_SS_RowStack): 247 getBottomImage = Stack._getReserveBottomImage 248 249 def getHelp(self): 250 return _('Waste. Build up or down by suit.') 251 252 253class Alhambra_Talon(DealRowTalonStack): 254 def canDealCards(self): 255 r_cards = sum([len(r.cards) for r in self.game.s.rows]) 256 if self.cards: 257 return True 258 elif r_cards and self.round != self.max_rounds: 259 return True 260 return False 261 262 def _deal(self): 263 num_cards = 0 264 for r in self.game.s.rows: 265 if self.cards: 266 self.game.flipAndMoveMove(self, r) 267 num_cards += 1 268 269 def dealCards(self, sound=False): 270 old_state = self.game.enterState(self.game.S_DEAL) 271 num_cards = 0 272 rows = self.game.s.rows 273 r_cards = sum([len(r.cards) for r in self.game.s.rows]) 274 if self.cards: 275 if sound and not self.game.demo: 276 self.game.playSample("dealwaste") 277 if len(self.game.s.rows) > 1: 278 num_cards = self.dealRowAvail(sound=False, frames=4) 279 else: 280 num_cards = self._deal() 281 elif r_cards and self.round != self.max_rounds: 282 if sound: 283 self.game.playSample("turnwaste", priority=20) 284 for r in rows: 285 for i in range(len(r.cards)): 286 self.game.moveMove(1, r, self, frames=0) 287 self.game.flipMove(self) 288 if len(self.game.s.rows) > 1: 289 num_cards = self.dealRowAvail(sound=False, frames=4) 290 else: 291 num_cards = self._deal() 292 self.game.nextRoundMove(self) 293 self.game.leaveState(old_state) 294 return num_cards 295 296 297class Alhambra(Game): 298 Hint_Class = Alhambra_Hint 299 300 RowStack_Class = StackWrapper(Alhambra_RowStack, base_rank=ANY_RANK) 301 302 def createGame(self, rows=1, reserves=8, playcards=3): 303 # create layout 304 l, s = Layout(self), self.s 305 306 # set window 307 w, h = l.XM+8*l.XS, l.YM+3.5*l.YS+playcards*l.YOFFSET 308 h += l.TEXT_HEIGHT 309 self.setSize(w, h) 310 311 # create stacks 312 x, y, = l.XM, l.YM 313 for i in range(4): 314 s.foundations.append(SS_FoundationStack(x, y, self, suit=i, 315 max_move=0)) 316 x += l.XS 317 for i in range(4): 318 s.foundations.append(SS_FoundationStack(x, y, self, suit=i, 319 max_move=0, base_rank=KING, dir=-1)) 320 x += l.XS 321 x, y, = l.XM+(8-reserves)*l.XS//2, y+l.YS 322 for i in range(reserves): 323 stack = OpenStack(x, y, self, max_accept=0) 324 stack.CARD_XOFFSET, stack.CARD_YOFFSET = 0, l.YOFFSET 325 s.reserves.append(stack) 326 x += l.XS 327 x, y = l.XM+(8-1-rows)*l.XS//2, self.height-l.YS 328 s.talon = Alhambra_Talon(x, y, self, max_rounds=3) 329 if rows == 1: 330 l.createText(s.talon, 'sw') 331 else: 332 l.createText(s.talon, 'n') 333 anchor = 'nn' 334 if rows > 1: 335 anchor = 'nnn' 336 l.createRoundText(s.talon, anchor) 337 338 x += l.XS 339 for i in range(rows): 340 stack = self.RowStack_Class(x, y, self, mod=13, max_accept=1) 341 stack.CARD_XOFFSET, stack.CARD_YOFFSET = 0, 0 342 s.rows.append(stack) 343 x += l.XS 344 if rows == 1: 345 l.createText(stack, 'se') 346 else: 347 l.createText(stack, 'n') 348 349 # define stack-groups (non default) 350 l.defaultStackGroups() 351 352 # 353 # game overrides 354 # 355 356 def _shuffleHook(self, cards): 357 # move one Aces and Kings of first deck to top of the Talon (i.e. first 358 # card to be dealt) 359 return self._shuffleHookMoveToTop( 360 cards, lambda c: (c.deck == 0 and 361 c.rank in (ACE, KING), (c.rank, c.suit)), 8) 362 363 def startGame(self): 364 self.s.talon.dealRow(rows=self.s.foundations, frames=0) 365 for i in range(3): 366 self.s.talon.dealRow(rows=self.s.reserves, frames=0) 367 self.startDealSample() 368 self.s.talon.dealRow(rows=self.s.reserves) 369 self.s.talon.dealCards() 370 371 shallHighlightMatch = Game._shallHighlightMatch_SSW 372 373 374class Granada(Alhambra): 375 def createGame(self): 376 Alhambra.createGame(self, rows=4) 377 378 379class Reserves_RowStack(UD_RK_RowStack): 380 getBottomImage = Stack._getReserveBottomImage 381 382 def getHelp(self): 383 return _('Waste. Build up or down regardless of suit.') 384 385 386class Reserves(Alhambra): 387 RowStack_Class = StackWrapper(Reserves_RowStack, base_rank=NO_RANK) 388 389 def createGame(self): 390 Alhambra.createGame(self, reserves=4, playcards=11) 391 392 def startGame(self): 393 self.s.talon.dealRow(rows=self.s.foundations, frames=0) 394 for i in range(11): 395 self.s.talon.dealRow(rows=self.s.reserves, frames=0) 396 self.startDealSample() 397 self.s.talon.dealRow(rows=self.s.reserves) 398 self.s.talon.dealCards() 399 400 shallHighlightMatch = Game._shallHighlightMatch_RKW 401 402 403class GrantsReinforcement(Reserves): 404 RowStack_Class = StackWrapper(Alhambra_RowStack, base_rank=NO_RANK) 405 406 def fillStack(self, stack): 407 for r in self.s.reserves: 408 if r.cards: 409 continue 410 if self.s.talon.cards: 411 old_state = self.enterState(self.S_FILL) 412 self.s.talon.flipMove() 413 self.s.talon.moveMove(1, r) 414 self.leaveState(old_state) 415 416 shallHighlightMatch = Game._shallHighlightMatch_SSW 417 418 419# ************************************************************************ 420# * Carpet 421# ************************************************************************ 422 423class Carpet(Game): 424 Foundation_Class = SS_FoundationStack 425 426 # 427 # game layout 428 # 429 430 def createGame(self): 431 # create layout 432 l, s = Layout(self), self.s 433 434 # set window 435 self.setSize(l.XM + 9*l.XS, l.YM + 4*l.YS) 436 437 # create stacks 438 for i in range(4): 439 for j in range(5): 440 x, y = l.XM + (j+3)*l.XS, l.YM + i*l.YS 441 s.rows.append(ReserveStack(x, y, self)) 442 for i in range(4): 443 dx, dy = ((2, 1), (8, 1), (2, 2), (8, 2))[i] 444 x, y = l.XM + dx*l.XS, l.YM + dy*l.YS 445 s.foundations.append(self.Foundation_Class(x, y, self, i)) 446 x, y = l.XM, l.YM 447 s.talon = WasteTalonStack(x, y, self, max_rounds=1) 448 l.createText(s.talon, "se") 449 y = y + l.YS 450 s.waste = WasteStack(x, y, self) 451 l.createText(s.waste, "se") 452 453 # define stack-groups 454 l.defaultStackGroups() 455 456 # 457 # game overrides 458 # 459 460 def _shuffleHook(self, cards): 461 # move Aces to top of the Talon (i.e. first cards to be dealt) 462 return self._shuffleHookMoveToTop( 463 cards, lambda c: (c.rank == 0, c.suit)) 464 465 def startGame(self): 466 self.startDealSample() 467 self.s.talon.dealRow(rows=self.s.foundations) 468 self.s.talon.dealRow() 469 self.s.talon.dealCards() # deal first card to WasteStack 470 471 472# ************************************************************************ 473# * British Constitution 474# ************************************************************************ 475 476class BritishConstitution_RowStackMethods: 477 def acceptsCards(self, from_stack, cards): 478 if self in self.game.s.rows[:8] and \ 479 from_stack in self.game.s.rows[8:16]: 480 return True 481 if self in self.game.s.rows[8:16] and \ 482 from_stack in self.game.s.rows[16:24]: 483 return True 484 if self in self.game.s.rows[16:24] and \ 485 from_stack in self.game.s.rows[24:]: 486 return True 487 if self in self.game.s.rows[24:] and from_stack is self.game.s.waste: 488 return True 489 return False 490 491 492class BritishConstitution_RowStack(BritishConstitution_RowStackMethods, 493 AC_RowStack): 494 def acceptsCards(self, from_stack, cards): 495 if not AC_RowStack.acceptsCards(self, from_stack, cards): 496 return False 497 return BritishConstitution_RowStackMethods.acceptsCards( 498 self, from_stack, cards) 499 500 501class NewBritishConstitution_RowStack(BritishConstitution_RowStackMethods, 502 RK_RowStack): 503 def acceptsCards(self, from_stack, cards): 504 if not RK_RowStack.acceptsCards(self, from_stack, cards): 505 return False 506 return BritishConstitution_RowStackMethods.acceptsCards( 507 self, from_stack, cards) 508 509 510class BritishConstitution_Foundation(SS_FoundationStack): 511 def acceptsCards(self, from_stack, cards): 512 if not SS_FoundationStack.acceptsCards(self, from_stack, cards): 513 return False 514 if from_stack in self.game.s.rows[:8]: 515 return True 516 return False 517 518 519class BritishConstitution(Game): 520 RowStack_Class = BritishConstitution_RowStack 521 522 def createGame(self): 523 # create layout 524 l, s = Layout(self), self.s 525 526 # set window 527 self.setSize(l.XM + 9*l.XS, l.YM + 5*l.YS) 528 529 # create stacks 530 x, y = l.XM+l.XS, l.YM 531 for i in range(8): 532 s.foundations.append(BritishConstitution_Foundation(x, y, self, 533 suit=int(i//2), max_cards=11)) 534 x += l.XS 535 536 y = l.YM+l.YS 537 for i in range(4): 538 x = l.XM+l.XS 539 for j in range(8): 540 stack = self.RowStack_Class(x, y, self, max_move=1) 541 stack.CARD_XOFFSET, stack.CARD_YOFFSET = 0, 0 542 s.rows.append(stack) 543 x += l.XS 544 y += l.YS 545 546 x, y = l.XM, l.YM 547 s.talon = WasteTalonStack(x, y, self, max_rounds=1) 548 l.createText(s.talon, "s") 549 y += l.YS+l.TEXT_HEIGHT 550 s.waste = WasteStack(x, y, self) 551 l.createText(s.waste, "s") 552 553 # define stack-groups 554 l.defaultStackGroups() 555 556 def startGame(self): 557 self.s.talon.dealRow(rows=self.s.foundations, frames=0) 558 self._startAndDealRowAndCards() 559 560 def _shuffleHook(self, cards): 561 # move Aces to top of the Talon (i.e. first cards to be dealt) 562 return self._shuffleHookMoveToTop( 563 cards, lambda c: (c.rank == ACE, c.suit)) 564 565 def fillStack(self, stack): 566 if not stack.cards: 567 if stack in self.s.rows[:24]: 568 return 569 old_state = self.enterState(self.S_FILL) 570 if stack is self.s.waste and self.s.talon.cards: 571 self.s.talon.dealCards() 572 elif stack in self.s.rows[24:] and self.s.waste.cards: 573 self.s.waste.moveMove(1, stack) 574 self.leaveState(old_state) 575 576 shallHighlightMatch = Game._shallHighlightMatch_AC 577 578 579class NewBritishConstitution(BritishConstitution): 580 RowStack_Class = StackWrapper( 581 NewBritishConstitution_RowStack, base_rank=JACK) 582 583 shallHighlightMatch = Game._shallHighlightMatch_RK 584 585 586# ************************************************************************ 587# * Twenty 588# ************************************************************************ 589 590class Twenty_RowStack(BasicRowStack): 591 def acceptsCards(self, from_stack, cards): 592 if not BasicRowStack.acceptsCards(self, from_stack, cards): 593 return False 594 return len(self.cards) == 0 595 596 def getHelp(self): 597 return _('Tableau. Empty piles can be filled with any card.') 598 599 600class Twenty(Game): 601 def createGame(self): 602 # create layout 603 l, s = Layout(self), self.s 604 605 # set window 606 self.setSize(l.XM+10*l.XS, l.YM+3*l.YS+10*l.YOFFSET) 607 608 # create stacks 609 x, y = l.XM, l.YM 610 s.talon = DealRowTalonStack(x, y, self) 611 l.createText(s.talon, 'se') 612 x += 2*l.XS 613 for i in range(4): 614 s.foundations.append(SS_FoundationStack(x, y, self, suit=i)) 615 x += l.XS 616 for i in range(4): 617 s.foundations.append(SS_FoundationStack(x, y, self, suit=i, 618 base_rank=KING, dir=-1)) 619 x += l.XS 620 621 for y in (l.YM+l.YS, l.YM+2*l.YS+5*l.YOFFSET): 622 x = l.XM 623 for i in range(10): 624 s.rows.append(Twenty_RowStack(x, y, self, 625 base_rank=ANY_RANK, max_accept=1)) 626 x += l.XS 627 628 # define stack-groups 629 l.defaultStackGroups() 630 631 def _shuffleHook(self, cards): 632 return self._shuffleHookMoveToTop( 633 cards, 634 lambda c: (c.rank in (ACE, KING) and c.deck == 1, 635 (c.rank, c.suit))) 636 637 def startGame(self): 638 self.s.talon.dealRow(rows=self.s.foundations, frames=0) 639 self._startAndDealRow() 640 641 def fillStack(self, stack): 642 if not stack.cards and stack in self.s.rows and self.s.talon.cards: 643 old_state = self.enterState(self.S_FILL) 644 self.flipMove(self.s.talon) 645 self.s.talon.moveMove(1, stack) 646 self.leaveState(old_state) 647 648 649# ************************************************************************ 650# * Three Pirates 651# ************************************************************************ 652 653class ThreePirates_Talon(DealRowTalonStack): 654 def dealCards(self, sound=False): 655 num_cards = 0 656 old_state = self.game.enterState(self.game.S_DEAL) 657 if self.cards: 658 if sound and not self.game.demo: 659 self.game.playSample("dealwaste") 660 num_cards = self.dealRowAvail(rows=self.game.s.reserves, 661 sound=False, frames=4) 662 self.game.leaveState(old_state) 663 return num_cards 664 665 666class ThreePirates(Game): 667 Hint_Class = CautiousDefaultHint 668 669 def createGame(self): 670 l, s = Layout(self), self.s 671 672 self.setSize(l.XM+10*l.XS, l.YM+3*l.YS+16*l.YOFFSET) 673 674 x, y, = l.XM+l.XS, l.YM 675 for i in range(8): 676 s.foundations.append(SS_FoundationStack(x, y, self, suit=i//2)) 677 x += l.XS 678 679 x, y, = l.XM, l.YM+l.YS 680 for i in range(10): 681 s.rows.append(SS_RowStack(x, y, self, max_move=1)) 682 x += l.XS 683 684 x, y = l.XM, self.height-l.YS 685 s.talon = ThreePirates_Talon(x, y, self) 686 l.createText(s.talon, 'n') 687 x += l.XS 688 for i in (0, 1, 2): 689 stack = WasteStack(x, y, self) 690 s.reserves.append(stack) 691 l.createText(stack, 'n') 692 x += l.XS 693 694 l.defaultStackGroups() 695 696 def startGame(self): 697 self._startDealNumRowsAndDealRowAndCards(3) 698 699 shallHighlightMatch = Game._shallHighlightMatch_SS 700 701 702# ************************************************************************ 703# * Frames 704# ************************************************************************ 705 706class Frames_Hint(CautiousDefaultHint): 707 def computeHints(self): 708 CautiousDefaultHint.computeHints(self) 709 if self.hints: 710 return 711 if not self.game.s.talon.cards: 712 return 713 for s in self.game.s.reserves: 714 if s.cards: 715 for r in self.game.s.rows: 716 if r.acceptsCards(s, s.cards): 717 self.addHint(5000, 1, s, r) 718 719 720class Frames_Foundation(UnionSquare_Foundation): 721 def acceptsCards(self, from_stack, cards): 722 if not UnionSquare_Foundation.acceptsCards(self, from_stack, cards): 723 return False 724 return from_stack in self.game.s.rows 725 726 727class Frames_RowStack(UD_SS_RowStack): 728 def acceptsCards(self, from_stack, cards): 729 if not UD_SS_RowStack.acceptsCards(self, from_stack, cards): 730 return False 731 if not (from_stack in self.game.s.reserves or 732 from_stack in self.game.s.rows): 733 return False 734 if len(self.cards) > 1: 735 cs = self.cards+cards 736 if not (isSameSuitSequence(cs, dir=1) or 737 isSameSuitSequence(cs, dir=-1)): 738 return False 739 if from_stack in self.game.s.reserves: 740 if hasattr(self.cap, 'column') and \ 741 self.cap.column != from_stack.cap.column: 742 return False 743 if hasattr(self.cap, 'row') and \ 744 self.cap.row != from_stack.cap.row: 745 return False 746 return True 747 748 749class Frames(Game): 750 Hint_Class = Frames_Hint # CautiousDefaultHint 751 752 def createGame(self): 753 l, s = Layout(self), self.s 754 755 self.setSize(l.XM+8*l.XS, l.YM+5*l.YS) 756 757 x0, y0 = l.XM+2*l.XS, l.YM 758 # foundations (corners) 759 suit = 0 760 for i, j in ((0, 0), (5, 0), (0, 4), (5, 4)): 761 x, y = x0+i*l.XS, y0+j*l.YS 762 s.foundations.append(Frames_Foundation(x, y, self, 763 suit=suit, dir=0, max_cards=26)) 764 suit += 1 765 # rows (frame) 766 for i in (1, 2, 3, 4): 767 for j in (0, 4): 768 x, y = x0+i*l.XS, y0+j*l.YS 769 stack = Frames_RowStack(x, y, self) 770 s.rows.append(stack) 771 stack.cap.addattr(column=i) 772 stack.CARD_YOFFSET = 0 773 for i in (0, 5): 774 for j in (1, 2, 3): 775 x, y = x0+i*l.XS, y0+j*l.YS 776 stack = Frames_RowStack(x, y, self) 777 s.rows.append(stack) 778 stack.cap.addattr(row=j) 779 stack.CARD_YOFFSET = 0 780 # reserves (picture) 781 for j in (1, 2, 3): 782 for i in (1, 2, 3, 4): 783 x, y = x0+i*l.XS, y0+j*l.YS 784 stack = OpenStack(x, y, self) 785 s.reserves.append(stack) 786 stack.cap.addattr(column=i) 787 stack.cap.addattr(row=j) 788 # talon & waste 789 x, y, = l.XM, l.YM 790 s.talon = WasteTalonStack(x, y, self, max_rounds=1) 791 l.createText(s.talon, 'ne') 792 y += l.YS 793 s.waste = WasteStack(x, y, self) 794 l.createText(s.waste, 'ne') 795 796 l.defaultStackGroups() 797 798 def startGame(self): 799 self.s.talon.dealRow(frames=0) 800 self.startDealSample() 801 self.s.talon.dealRow(rows=self.s.reserves) 802 self.s.talon.dealCards() 803 804 def fillStack(self, stack): 805 if not stack.cards and stack in self.s.reserves: 806 if not self.s.waste.cards: 807 self.s.talon.dealCards() 808 if self.s.waste.cards: 809 old_state = self.enterState(self.S_FILL) 810 self.s.waste.moveMove(1, stack) 811 self.leaveState(old_state) 812 813 shallHighlightMatch = Game._shallHighlightMatch_SS 814 815 816# ************************************************************************ 817# * Royal Rendezvous 818# ************************************************************************ 819 820class RoyalRendezvous(Game): 821 822 def createGame(self): 823 l, s = Layout(self), self.s 824 self.setSize(l.XM+9.5*l.XS, l.YM+4.5*l.YS) 825 826 y = l.YM 827 # kings 828 suit = 0 829 for i in (0, 1, 6, 7): 830 x = l.XM+(1.5+i)*l.XS 831 s.foundations.append(SS_FoundationStack(x, y, self, suit=suit, 832 base_rank=KING, max_cards=1)) 833 suit += 1 834 # aces 835 suit = 0 836 for i in (2, 3, 4, 5): 837 x = l.XM+(1.5+i)*l.XS 838 s.foundations.append(SS_FoundationStack(x, y, self, suit=suit)) 839 suit += 1 840 y += l.YS 841 # twos 842 suit = 0 843 for i in (0, 1, 6, 7): 844 x = l.XM+(1.5+i)*l.XS 845 s.foundations.append(SS_FoundationStack(x, y, self, suit=suit, 846 base_rank=1, dir=2, max_cards=6)) 847 suit += 1 848 # aces 849 suit = 0 850 for i in (2, 3, 4, 5): 851 x = l.XM+(1.5+i)*l.XS 852 s.foundations.append(SS_FoundationStack(x, y, self, suit=suit, 853 dir=2, max_cards=6)) 854 suit += 1 855 856 y += 1.5*l.YS 857 for i in (0, 1): 858 x = l.XM+1.5*l.XS 859 for j in range(8): 860 s.rows.append(OpenStack(x, y, self, max_accept=0)) 861 x += l.XS 862 y += l.YS 863 864 x, y = l.XM, l.YM 865 s.talon = WasteTalonStack(x, y, self, max_rounds=1) 866 l.createText(s.talon, 'ne') 867 y += l.YS 868 s.waste = WasteStack(x, y, self) 869 l.createText(s.waste, 'ne') 870 871 l.defaultStackGroups() 872 873 def _shuffleHook(self, cards): 874 # move twos to top 875 cards = self._shuffleHookMoveToTop( 876 cards, 877 lambda c: (c.rank == 1 and c.deck == 0, c.suit)) 878 # move aces to top 879 cards = self._shuffleHookMoveToTop( 880 cards, 881 lambda c: (c.rank == ACE, (c.deck, c.suit))) 882 return cards 883 884 def startGame(self): 885 # deal aces 886 self.s.talon.dealRow(rows=self.s.foundations[4:8], frames=0) 887 self.s.talon.dealRow(rows=self.s.foundations[12:16], frames=0) 888 # deal twos 889 self.s.talon.dealRow(rows=self.s.foundations[8:12], frames=0) 890 # 891 self.startDealSample() 892 self.s.talon.dealRow() 893 self.s.talon.dealCards() 894 895 def fillStack(self, stack): 896 if not stack.cards and stack in self.s.rows: 897 if not self.s.waste.cards: 898 self.s.talon.dealCards() 899 if self.s.waste.cards: 900 old_state = self.enterState(self.S_FILL) 901 self.s.waste.moveMove(1, stack) 902 self.leaveState(old_state) 903 904 905# ************************************************************************ 906# * Shady Lanes 907# ************************************************************************ 908 909class ShadyLanes_Hint(CautiousDefaultHint): 910 def computeHints(self): 911 CautiousDefaultHint.computeHints(self) 912 if self.hints: 913 return 914 for r in self.game.s.rows: 915 if not r.cards: 916 for s in self.game.s.reserves: 917 if s.cards: 918 self.addHint(5000-s.cards[0].rank, 1, s, r) 919 920 921class ShadyLanes_Foundation(AbstractFoundationStack): 922 def acceptsCards(self, from_stack, cards): 923 if not AbstractFoundationStack.acceptsCards(self, from_stack, cards): 924 return False 925 if self.cards: 926 # check the rank 927 if self.cards[-1].rank+1 != cards[0].rank: 928 return False 929 return True 930 931 def getHelp(self): 932 return _('Foundation. Build up by color.') 933 934 935class ShadyLanes_RowStack(AC_RowStack): 936 def acceptsCards(self, from_stack, cards): 937 if not AC_RowStack.acceptsCards(self, from_stack, cards): 938 return False 939 if not self.cards: 940 return from_stack in self.game.s.reserves 941 return True 942 943 944class ShadyLanes(Game): 945 Hint_Class = ShadyLanes_Hint 946 947 def createGame(self): 948 l, s = Layout(self), self.s 949 self.setSize(l.XM+8*l.XS, l.YM+5*l.YS) 950 951 x, y = l.XM, l.YM 952 for i in range(8): 953 suit = i//2 954 color = suit//2 955 s.foundations.append( 956 ShadyLanes_Foundation( 957 x, y, self, 958 base_suit=suit, suit=ANY_SUIT, color=color)) 959 x += l.XS 960 x, y = l.XM, l.YM+l.YS 961 s.talon = WasteTalonStack(x, y, self, max_rounds=1) 962 l.createText(s.talon, 'ne') 963 y += l.YS 964 s.waste = WasteStack(x, y, self) 965 l.createText(s.waste, 'ne') 966 x, y = l.XM+2*l.XS, l.YM+l.YS 967 for i in range(4): 968 s.rows.append(ShadyLanes_RowStack(x, y, self, max_move=1)) 969 x += l.XS 970 971 x, y = self.width-l.XS, l.YM+l.YS 972 for i in range(4): 973 s.reserves.append(OpenStack(x, y, self, max_accept=0)) 974 y += l.YS 975 976 l.defaultStackGroups() 977 978 def fillStack(self, stack): 979 if not stack.cards and stack in self.s.reserves: 980 if not self.s.waste.cards: 981 self.s.talon.dealCards() 982 if self.s.waste.cards: 983 old_state = self.enterState(self.S_FILL) 984 self.s.waste.moveMove(1, stack) 985 self.leaveState(old_state) 986 987 def startGame(self): 988 self.startDealSample() 989 self.s.talon.dealRow(rows=self.s.reserves) 990 self.s.talon.dealRow() 991 self.s.talon.dealCards() 992 993 shallHighlightMatch = Game._shallHighlightMatch_AC 994 995 996# ************************************************************************ 997# * Four Winds 998# * Boxing the Compass 999# ************************************************************************ 1000 1001class FourWinds(Game): 1002 1003 def createGame(self): 1004 l, s = Layout(self), self.s 1005 self.setSize(l.XM+9*l.XS, l.YM+6*l.YS) 1006 1007 # vertical rows 1008 x = l.XM+l.XS 1009 for i in (0, 1): 1010 y = l.YM+l.YS 1011 for j in range(4): 1012 stack = ReserveStack(x, y, self, base_suit=i) 1013 stack.getBottomImage = stack._getSuitBottomImage 1014 s.rows.append(stack) 1015 y += l.YS 1016 x += 6*l.XS 1017 # horizontal rows 1018 y = l.YM+l.YS 1019 for i in (2, 3): 1020 x = l.XM+2.5*l.XS 1021 for j in range(4): 1022 stack = ReserveStack(x, y, self, base_suit=i) 1023 stack.getBottomImage = stack._getSuitBottomImage 1024 s.rows.append(stack) 1025 x += l.XS 1026 y += 3*l.YS 1027 # foundations 1028 decks = self.gameinfo.decks 1029 for k in range(decks): 1030 suit = 0 1031 for i, j in ((0, 3-decks*0.5+k), 1032 (8, 3-decks*0.5+k), 1033 (4.5-decks*0.5+k, 0), 1034 (4.5-decks*0.5+k, 5)): 1035 x, y = l.XM+i*l.XS, l.YM+j*l.YS 1036 s.foundations.append(SS_FoundationStack(x, y, self, 1037 suit=suit, max_move=0)) 1038 suit += 1 1039 # talon & waste 1040 x, y = l.XM+3.5*l.XS, l.YM+2.5*l.YS 1041 s.talon = WasteTalonStack(x, y, self, max_rounds=2) 1042 l.createText(s.talon, 's') 1043 l.createRoundText(self.s.talon, 'nn') 1044 x += l.XS 1045 s.waste = WasteStack(x, y, self) 1046 l.createText(s.waste, 's') 1047 1048 l.defaultStackGroups() 1049 1050 def startGame(self): 1051 self.s.talon.dealRow(rows=self.s.foundations, frames=0) 1052 self.startDealSample() 1053 self.s.talon.dealRow() 1054 self.s.talon.dealCards() 1055 1056 def _shuffleHook(self, cards): 1057 # move Aces to top of the Talon (i.e. first cards to be dealt) 1058 return self._shuffleHookMoveToTop( 1059 cards, 1060 lambda c: (c.rank == ACE, (c.deck, c.suit))) 1061 1062 1063class BoxingTheCompass(FourWinds): 1064 pass 1065 1066 1067# ************************************************************************ 1068# * Colonel 1069# ************************************************************************ 1070 1071class Colonel_Hint(DefaultHint): 1072 def _getMoveCardBonus(self, r, t, pile, rpile): 1073 if r in self.game.s.rows and t in self.game.s.rows: 1074 if rpile: 1075 return 0 1076 return DefaultHint._getMoveCardBonus(self, r, t, pile, rpile) 1077 1078 1079class Colonel_RowStack(SS_RowStack): 1080 1081 def _getStackIndex(self, stack): 1082 index = list(self.game.s.rows).index(stack) 1083 if index < 12: 1084 row = 0 1085 elif 12 <= index < 24: 1086 row = 1 1087 else: 1088 row = 2 1089 return index, row 1090 1091 def acceptsCards(self, from_stack, cards): 1092 if not SS_RowStack.acceptsCards(self, from_stack, cards): 1093 return False 1094 1095 self_index, self_row = self._getStackIndex(self) 1096 1097 if self_row in (1, 2): 1098 above_stack = self.game.s.rows[self_index-12] 1099 if not above_stack.cards: 1100 return False 1101 1102 below_stack = None 1103 if self_row in (0, 1): 1104 below_stack = self.game.s.rows[self_index+12] 1105 1106 # from_stack is waste 1107 if from_stack is self.game.s.waste: 1108 if below_stack is None or not below_stack.cards: 1109 return True 1110 else: 1111 return False 1112 1113 # from_stack in rows 1114 from_index, from_row = self._getStackIndex(from_stack) 1115 if below_stack and below_stack.cards: 1116 return from_stack is below_stack 1117 return from_row > self_row 1118 1119 def canMoveCards(self, cards): 1120 self_index, self_row = self._getStackIndex(self) 1121 if self_row in (0, 1): 1122 below_stack = self.game.s.rows[self_index+12] 1123 if below_stack.cards: 1124 return False 1125 return SS_RowStack.canMoveCards(self, cards) 1126 1127 getBottomImage = Stack._getReserveBottomImage 1128 1129 1130class Colonel(Game): 1131 Hint_Class = Colonel_Hint 1132 1133 def createGame(self): 1134 l, s = Layout(self), self.s 1135 self.setSize(l.XM+12*l.XS, l.YM+5*l.YS) 1136 1137 x, y = l.XM+2*l.XS, l.YM 1138 for i in range(8): 1139 s.foundations.append(SS_FoundationStack(x, y, self, suit=i//2, 1140 max_move=0)) 1141 x += l.XS 1142 1143 y = l.YM+l.YS 1144 for i in range(3): 1145 x = l.XM 1146 for j in range(12): 1147 stack = Colonel_RowStack(x, y, self, max_move=1) 1148 stack.CARD_YOFFSET = 0 1149 s.rows.append(stack) 1150 x += l.XS 1151 y += l.YS 1152 1153 x, y = l.XM+5*l.XS, l.YM+4*l.YS 1154 s.talon = WasteTalonStack(x, y, self, max_rounds=1) 1155 l.createText(s.talon, 'sw') 1156 x += l.XS 1157 s.waste = WasteStack(x, y, self) 1158 l.createText(s.waste, 'se') 1159 1160 l.defaultStackGroups() 1161 1162 def startGame(self): 1163 self.startDealSample() 1164 self.s.talon.dealRow(frames=4) 1165 self.s.talon.dealCards() 1166 1167 shallHighlightMatch = Game._shallHighlightMatch_SS 1168 1169 1170# ************************************************************************ 1171# * The Red and the Black 1172# ************************************************************************ 1173 1174 1175class TheRedAndTheBlack_Foundation(AC_FoundationStack): 1176 def acceptsCards(self, from_stack, cards): 1177 if not AC_FoundationStack.acceptsCards(self, from_stack, cards): 1178 return False 1179 if from_stack is self.game.s.waste or from_stack in self.game.s.rows: 1180 return True 1181 return False 1182 1183 1184class TheRedAndTheBlack_Reserve(ReserveStack): 1185 def acceptsCards(self, from_stack, cards): 1186 if not ReserveStack.acceptsCards(self, from_stack, cards): 1187 return False 1188 if from_stack is self.game.s.waste: 1189 return True 1190 return False 1191 1192 1193class TheRedAndTheBlack(Game): 1194 Hint_Class = CautiousDefaultHint 1195 1196 def createGame(self): 1197 1198 l, s = Layout(self), self.s 1199 self.setSize(l.XM + 8*l.XS, l.YM + 4.5*l.YS) 1200 1201 x, y = l.XM, l.YM 1202 for i in range(8): 1203 s.foundations.append(TheRedAndTheBlack_Foundation(x, y, self, 1204 suit=i//2)) 1205 x += l.XS 1206 x, y = l.XM+2*l.XS, l.YM+l.YS 1207 for i in range(4): 1208 stack = AC_RowStack(x, y, self, max_move=1) 1209 stack.getBottomImage = stack._getReserveBottomImage 1210 stack.CARD_YOFFSET = 0 1211 s.rows.append(stack) 1212 x += l.XS 1213 x, y = l.XM+2*l.XS, l.YM+2*l.YS 1214 for i in range(4): 1215 s.reserves.append(TheRedAndTheBlack_Reserve(x, y, self)) 1216 x += l.XS 1217 x, y = l.XM+3*l.XS, l.YM+3.5*l.YS 1218 s.talon = WasteTalonStack(x, y, self, max_rounds=1) 1219 l.createText(s.talon, "sw") 1220 x += l.XS 1221 s.waste = WasteStack(x, y, self) 1222 l.createText(s.waste, "se") 1223 1224 # define stack-groups 1225 l.defaultStackGroups() 1226 1227 def startGame(self): 1228 self.s.talon.dealRow(rows=self.s.foundations, frames=0) 1229 self.startDealSample() 1230 self.s.talon.dealRow() 1231 self.s.talon.dealRow(rows=self.s.reserves) 1232 self.s.talon.dealCards() # deal first card to WasteStack 1233 1234 def _shuffleHook(self, cards): 1235 # move Aces to top of the Talon (i.e. first cards to be dealt) 1236 return self._shuffleHookMoveToTop( 1237 cards, lambda c: (c.rank == ACE, c.suit)) 1238 1239 shallHighlightMatch = Game._shallHighlightMatch_AC 1240 1241 1242# ************************************************************************ 1243# * Twilight Zone 1244# ************************************************************************ 1245 1246class TwilightZone_Foundation(AC_FoundationStack): 1247 def acceptsCards(self, from_stack, cards): 1248 if not AC_FoundationStack.acceptsCards(self, from_stack, cards): 1249 return False 1250 if from_stack is self.game.s.waste or \ 1251 from_stack in self.game.s.reserves: 1252 return False 1253 return True 1254 1255 1256class TwilightZone_Talon(OpenTalonStack, WasteTalonStack): 1257 rightclickHandler = OpenStack.rightclickHandler 1258 doubleclickHandler = OpenStack.doubleclickHandler 1259 1260 def prepareStack(self): 1261 OpenTalonStack.prepareStack(self) 1262 self.waste = self.game.s.waste 1263 1264 canDealCards = WasteTalonStack.canDealCards 1265 dealCards = WasteTalonStack.dealCards 1266 1267 1268class TwilightZone_RowStack(AC_RowStack): 1269 def acceptsCards(self, from_stack, cards): 1270 if not AC_RowStack.acceptsCards(self, from_stack, cards): 1271 return False 1272 if from_stack is self.game.s.waste: 1273 return False 1274 return True 1275 1276 1277class TwilightZone_Waste(WasteStack): 1278 def acceptsCards(self, from_stack, cards): 1279 if not WasteStack.acceptsCards(self, from_stack, cards): 1280 return False 1281 return from_stack is self.game.s.talon 1282 1283 1284class TwilightZone(Game): 1285 Hint_Class = CautiousDefaultHint 1286 1287 def createGame(self): 1288 1289 # create layout 1290 l, s = Layout(self), self.s 1291 1292 # set window 1293 self.setSize(l.XM+7*l.XS, l.YM+5*l.YS) 1294 1295 # create stacks 1296 y = l.YM 1297 for i in range(2): 1298 x = l.XM+3*l.XS 1299 for j in range(4): 1300 s.foundations.append(TwilightZone_Foundation(x, y, self, 1301 suit=j)) 1302 x += l.XS 1303 y += l.YS 1304 1305 x, y = l.XM+3*l.XS, l.YM+2*l.YS 1306 for i in range(4): 1307 stack = TwilightZone_RowStack(x, y, self, max_move=1) 1308 stack.CARD_YOFFSET = 0 1309 s.rows.append(stack) 1310 x += l.XS 1311 1312 x, y = l.XM+3*l.XS, l.YM+4*l.YS 1313 for i in range(4): 1314 s.reserves.append(OpenStack(x, y, self)) 1315 x += l.XS 1316 1317 x, y = l.XM, l.YM+l.YS//2 1318 s.talon = TwilightZone_Talon(x, y, self, max_move=1, max_rounds=2) 1319 l.createText(s.talon, 's') 1320 l.createRoundText(s.talon, 'nn') 1321 x += l.XS 1322 s.waste = TwilightZone_Waste(x, y, self, max_accept=1) 1323 l.createText(s.waste, 's') 1324 1325 # define stack-groups 1326 l.defaultStackGroups() 1327 self.sg.dropstacks.append(s.talon) 1328 self.sg.openstacks.append(s.waste) 1329 1330 def startGame(self): 1331 self.startDealSample() 1332 self.s.talon.dealRow() 1333 self.s.talon.dealRow(rows=self.s.reserves) 1334 self.s.talon.fillStack() 1335 1336 def fillStack(self, stack): 1337 if not stack.cards: 1338 old_state = self.enterState(self.S_FILL) 1339 if stack in self.s.rows: 1340 i = list(self.s.rows).index(stack) 1341 from_stack = self.s.reserves[i] 1342 if from_stack.cards: 1343 from_stack.moveMove(1, stack) 1344 elif stack in self.s.reserves: 1345 from_stack = self.s.waste 1346 if not from_stack.cards: 1347 from_stack = self.s.talon 1348 if from_stack.cards: 1349 from_stack.moveMove(1, stack) 1350 self.leaveState(old_state) 1351 1352 def _autoDeal(self, sound=True): 1353 return 0 1354 1355 shallHighlightMatch = Game._shallHighlightMatch_AC 1356 1357 1358# register the game 1359registerGame(GameInfo(54, RoyalCotillion, "Royal Cotillion", 1360 GI.GT_2DECK_TYPE, 2, 0, GI.SL_LUCK)) 1361registerGame(GameInfo(55, OddAndEven, "Odd and Even", 1362 GI.GT_2DECK_TYPE, 2, 1, GI.SL_LUCK)) 1363registerGame(GameInfo(143, Kingdom, "Kingdom", 1364 GI.GT_2DECK_TYPE, 2, 0, GI.SL_MOSTLY_LUCK)) 1365registerGame(GameInfo(234, Alhambra, "Alhambra", 1366 GI.GT_2DECK_TYPE, 2, 2, GI.SL_BALANCED)) 1367registerGame(GameInfo(97, Carpet, "Carpet", 1368 GI.GT_1DECK_TYPE, 1, 0, GI.SL_MOSTLY_LUCK)) 1369registerGame(GameInfo(391, BritishConstitution, "British Constitution", 1370 GI.GT_2DECK_TYPE, 2, 0, GI.SL_BALANCED, 1371 ranks=list(range(11)), # without Queens and Kings 1372 altnames=("Constitution",))) 1373registerGame(GameInfo(392, NewBritishConstitution, "New British Constitution", 1374 GI.GT_2DECK_TYPE | GI.GT_ORIGINAL, 2, 0, GI.SL_BALANCED, 1375 ranks=list(range(11)) # without Queens and Kings 1376 )) 1377registerGame(GameInfo(443, Twenty, "Twenty", 1378 GI.GT_2DECK_TYPE, 2, 0, GI.SL_BALANCED)) 1379registerGame(GameInfo(465, Granada, "Granada", 1380 GI.GT_2DECK_TYPE, 2, 2, GI.SL_BALANCED)) 1381registerGame(GameInfo(579, ThreePirates, "Three Pirates", 1382 GI.GT_2DECK_TYPE, 2, 0, GI.SL_MOSTLY_SKILL)) 1383registerGame(GameInfo(608, Frames, "Frames", 1384 GI.GT_2DECK_TYPE, 2, 0, GI.SL_MOSTLY_SKILL)) 1385registerGame(GameInfo(609, GrantsReinforcement, "Grant's Reinforcement", 1386 GI.GT_2DECK_TYPE, 2, 2, GI.SL_BALANCED)) 1387registerGame(GameInfo(638, RoyalRendezvous, "Royal Rendezvous", 1388 GI.GT_2DECK_TYPE, 2, 0, GI.SL_BALANCED)) 1389registerGame(GameInfo(639, ShadyLanes, "Shady Lanes", 1390 GI.GT_2DECK_TYPE, 2, 0, GI.SL_BALANCED)) 1391registerGame(GameInfo(675, FourWinds, "Four Winds", 1392 GI.GT_1DECK_TYPE, 1, 1, GI.SL_BALANCED)) 1393registerGame(GameInfo(676, BoxingTheCompass, "Boxing the Compass", 1394 GI.GT_2DECK_TYPE, 2, 1, GI.SL_BALANCED)) 1395registerGame(GameInfo(693, Colonel, "Colonel", 1396 GI.GT_2DECK_TYPE, 2, 0, GI.SL_MOSTLY_SKILL)) 1397registerGame(GameInfo(695, TheRedAndTheBlack, "The Red and the Black", 1398 GI.GT_2DECK_TYPE, 2, 0, GI.SL_BALANCED)) 1399registerGame(GameInfo(748, TwilightZone, "Twilight Zone", 1400 GI.GT_2DECK_TYPE, 2, 1, GI.SL_BALANCED)) 1401registerGame(GameInfo(752, Reserves, "Reserves", 1402 GI.GT_2DECK_TYPE, 2, 2, GI.SL_BALANCED)) 1403