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