## vim:ts=4:et:nowrap
##
##---------------------------------------------------------------------------##
##
## PySol -- a Python Solitaire game
##
## Copyright (C) 2000 by T. Kirk
## Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer
## Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer
## Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; see the file COPYING.
## If not, write to the Free Software Foundation, Inc.,
## 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
##
## Markus F.X.J. Oberhumer
## <markus@oberhumer.com>
## http://www.oberhumer.com/pysol
##
## T. Kirk
## <grania@mailcity.com>
## http://www.inetarena.com/~grania
##
##---------------------------------------------------------------------------##


# Imports
import sys

# PySol imports
if sys.modules.has_key("pysoltk"):
    from gamedb import registerGame, GameInfo, GI
    from util import *
    from stack import *
    from game import Game
    from layout import Layout
    from hint import AbstractHint, DefaultHint, CautiousDefaultHint
    from pysoltk import MfxCanvasText, getFont


# /***********************************************************************
#  * Flower Foundation Stacks
#  ***********************************************************************/

class Flower_FoundationStack(AbstractFoundationStack):
    def __init__(self, x, y, game, suit, **cap):
        kwdefault(cap, max_cards=12, max_move=0, base_rank=ANY_RANK)
        apply(AbstractFoundationStack.__init__, (self, x, y, game, suit), cap)

    def isOonsooSequence(self, s):
        for i in range(len(s) - 1):
            if s[i].rank != s[i + 1].rank:
                return 0
            if s[i].rank == 10:
                a, b = s[i].suit, s[i + 1].suit
                if b == 0:
                    b = 4
            else:
                a, b = self.swapTrashCards(s[i], s[i + 1])
            if a + 1 != b:
                return 0
        return cardsFaceUp(s)

    def swapTrashCards(self, carda, cardb):
        a, b = carda.suit, cardb.suit
        if (a == 0 and b == 3) or (a == 3 and b == 0):
            a, b = 0, 1
        elif (a == 2 and b == 0) or (a == 1 and b == 0):
            b = 3
        return a, b


class FlowerClock_Foundation(Flower_FoundationStack):
    def acceptsCards(self, from_stack, cards):
        if not self.basicAcceptsCards(from_stack, cards):
            return 0
        stackcards = self.cards
        if not stackcards:
            return (cards[0].suit == 0)
        if not (cards[0].rank == stackcards[-1].rank):
            return 0
        i = cards[0].suit
        j = stackcards[-1].suit
        if j == 0:
            j = 4
        return i + 1 == j


class Gaji_Foundation(Flower_FoundationStack):
    def __init__(self, x, y, game, suit, **cap):
        apply(Flower_FoundationStack.__init__, (self, x, y, game, suit), cap)
        self.CARD_YOFFSET = self.game.app.images.CARD_YOFFSET

    def acceptsCards(self, from_stack, cards):
        if not self.basicAcceptsCards(from_stack, cards):
            return 0
        stackcards = self.cards
        return ((stackcards[-1].suit == cards[0].suit)
                and ((stackcards[-1].rank + 1) % 12) == cards[0].rank)


class Pagoda_Foundation(Flower_FoundationStack):
    def acceptsCards(self, from_stack, cards):
        if not self.basicAcceptsCards(from_stack, cards):
            return 0
        stackcards = self.cards
        if not stackcards:
            return ((cards[0].suit == 0) and (cards[0].rank == self.id))
        if not (cards[0].rank == stackcards[-1].rank):
            return 0
        i = cards[0].suit
        j = stackcards[-1].suit
        if j == 0:
            j = 4
        if len(stackcards) < 4:
            return i == j - 1
        elif len(stackcards) > 4:
            if i == 0:
                i = 4
            return i == j + 1
        else:
            return i == j


class Samuri_Foundation(Flower_FoundationStack):
    def __init__(self, x, y, game, suit, **cap):
        apply(Flower_FoundationStack.__init__, (self, x, y, game, suit), cap)
        self.CARD_YOFFSET = -self.game.app.images.CARD_YOFFSET

    def acceptsCards(self, from_stack, cards):
        if not self.basicAcceptsCards(from_stack, cards):
            return 0
        stackcards = self.cards
        if not stackcards:
            return ((cards[0].suit == 0) and (cards[0].rank == self.id))
        i = stackcards[-1].suit
        if i == 0:
            i = 4
        return ((cards[0].rank == stackcards[-1].rank) and (cards[0].suit == i - 1))

    def getBottomImage(self):
        return self.game.app.images.getTalonBottom()


class MatsuKiri_Foundation(Flower_FoundationStack):
    def __init__(self, x, y, game, suit, **cap):
        kwdefault(cap, max_cards=48, min_accept=4, max_accept=4)
        apply(AbstractFoundationStack.__init__, (self, x, y, game, suit), cap)
        self.CARD_YOFFSET = self.game.app.images.CARDH / 10

    def acceptsCards(self, from_stack, cards):
        if not self.basicAcceptsCards(from_stack, cards):
            return 0
        stackcards = self.cards
        if not self.isOonsooSequence(cards):
            return 0
        if not stackcards:
            return cards[0].suit == 1 and cards[0].rank == 0
        return stackcards[-1].rank + 1 == cards[0].rank

    def getBottomImage(self):
        return self.game.app.images.getLetter(0)


class GreatWall_Foundation(Flower_FoundationStack):
    def __init__(self, x, y, game, suit, **cap):
        kwdefault(cap, max_cards=36, min_accept=12, max_accept=12)
        apply(Flower_FoundationStack.__init__, (self, x, y, game, suit), cap)
        self.CARD_YOFFSET = 0

    def acceptsCards(self, from_stack, cards):
        if not self.basicAcceptsCards(from_stack, cards):
            return 0
        stackcards = self.cards
        if cards[0].rank != 0:
            return 0
        for i in range(12):
            if cards[i].suit != (self.id + 1) % 4:
                return 0
        return isRankSequence(cards, dir=1)


class GreatWall_BuildStack(Flower_FoundationStack):
    def __init__(self, x, y, game, suit, **cap):
        kwdefault(cap, max_cards=12, max_move=12)
        apply(Flower_FoundationStack.__init__, (self, x, y, game, suit), cap)
        self.CARD_YOFFSET = self.game.app.images.CARDH / 9

    def acceptsCards(self, from_stack, cards):
        if not self.basicAcceptsCards(from_stack, cards):
            return 0
        stackcards = self.cards
        if stackcards:
            if cards[0].rank != stackcards[-1].rank + 1:
                return 0
        elif cards[0].rank != 0:
            return 0
        for c in cards:
            if c.suit != (self.id + 1) % 4:
                return 0
        return isRankSequence(cards, dir=1)


class FourWinds_Foundation(Flower_FoundationStack):
    def acceptsCards(self, from_stack, cards):
        if not self.basicAcceptsCards(from_stack, cards):
            return 0
        stackcards = self.cards
        if cards[0].suit != self.id:
            return 0
        if not stackcards:
            return cards[0].rank == 0
        else:
            return stackcards[-1].rank + 1 == cards[0].rank


# /***********************************************************************
#  * Flower Row Stacks
#  ***********************************************************************/

class Flower_OpenStack(OpenStack):
    def __init__(self, x, y, game, yoffset, **cap):
        kwdefault(cap, max_move=999999, max_accept=999999)
        apply(OpenStack.__init__, (self, x, y, game), cap)
        self.CARD_YOFFSET = yoffset

    def isOonsooSequence(self, s):
        for i in range(len(s) - 1):
            if s[i].rank != s[i + 1].rank:
                return 0
            if s[i].rank == 10:
                a, b = s[i].suit, s[i + 1].suit
                if b == 0:
                    b = 4
            else:
                a, b = self.swapTrashCards(s[i], s[i + 1])
            if a + 1 != b:
                return 0
        return 1

    def swapTrashCards(self, carda, cardb):
        a, b = carda.suit, cardb.suit
        if (a == 0 and b == 3) or (a == 3 and b == 0):
            a, b = 3, 4
        elif (a == 2 and b == 0) or (a == 0 and b == 1):
            b = 3
        return a, b


class FlowerClock_RowStack(Flower_OpenStack):
    def acceptsCards(self, from_stack, cards):
        if not self.basicAcceptsCards(from_stack, cards):
            return 0
        stackcards = self.cards
        if not stackcards:
            return 1
        return (stackcards[-1].suit + 1) % 4 == cards[0].suit


class Gaji_RowStack(Flower_OpenStack):
    def acceptsCards(self, from_stack, cards):
        if not self.basicAcceptsCards(from_stack, cards):
            return 0
        stackcards = self.cards
        if not stackcards:
            return 1
        elif ((cards[0].suit == 0 and cards[0].rank == 10)
               or (stackcards[-1].suit == 0 and stackcards[-1].rank == 10)):
            return 1
        elif cards[0].rank != stackcards[-1].rank:
            return 0
        a, b = self.swapTrashCards(stackcards[-1], cards[0])
        return a + 1 == b


class Matsukiri_RowStack(Flower_OpenStack):
    def acceptsCards(self, from_stack, cards):
        if not self.basicAcceptsCards(from_stack, cards):
            return 0
        stackcards = self.cards
        if not stackcards:
            return cards[0].suit == 1
        return self.isOonsooSequence([stackcards[-1], cards[0]])


class Oonsoo_RowStack(Flower_OpenStack):
    def acceptsCards(self, from_stack, cards):
        if not self.basicAcceptsCards(from_stack, cards):
            return 0
        stackcards = self.cards
        if not self.isOonsooSequence(cards):
            return 0
        if not stackcards:
            return cards[0].suit == 1
        if stackcards[-1].rank != cards[0].rank:
            return 0
        return self.isOonsooSequence([stackcards[-1], cards[0]])


class Samuri_RowStack(Flower_OpenStack):
    def acceptsCards(self, from_stack, cards):
        if not self.basicAcceptsCards(from_stack, cards):
            return 0
        stackcards = self.cards
        if not stackcards:
            return cards[0].suit == 1
        i = cards[0].suit
        if i == 0:
            i = 4
        return stackcards[-1].rank == cards[0].rank and stackcards[-1].suit == i - 1


class GreatWall_RowStack(Flower_OpenStack):
    def acceptsCards(self, from_stack, cards):
        if not self.basicAcceptsCards(from_stack, cards):
            return 0
        stackcards = self.cards
        if not stackcards:
            return cards[0].suit == 1
        if cards[0].suit == stackcards[-1].suit:
            return (cards[0].rank + 1) % 12 == stackcards[-1].rank
        a, b = self.swapTrashCards(stackcards[-1], cards[0])
        return a + 1 == b


class FourWinds_RowStack(Flower_OpenStack):
    def acceptsCards(self, from_stack, cards):
        if not self.basicAcceptsCards(from_stack, cards):
            return 0
        stackcards = self.cards
        if not stackcards:
            return 1
        return cards[0].suit == stackcards[-1].suit and cards[0].rank + 1 == stackcards[-1].rank

    def getBottomImage(self):
        return self.game.app.images.getReserveBottom()


# /***********************************************************************
#  *
#  ***********************************************************************/

class AbstractFlowerGame(Game):
    # Suit names
    SUITS = ("Pine", "Plum", "Cherry", "Wisteria", "Iris", "Rose",
             "Clover", "Moon", "Mum", "Maple", "Rain", "Phoenix")

    def shallHighlightMatch(self, stack1, card1, stack2, card2):
        if card1.rank != card2.rank:
            return 0
        a, b = card1.suit, card2.suit
        if a == 0:
            a = 4
        elif b == 0:
            b = 4
        return a + 1 == b or a - 1 == b


# /***********************************************************************
#  * Flower Clock
#  ***********************************************************************/

class FlowerClock(AbstractFlowerGame):

    #
    # Game layout
    #

    def createGame(self):
        l, s = Layout(self), self.s
        font = getFont("canvas_card", cardw=l.CW)

        # Set window size
        self.setSize(l.XM + l.XS * 10.5, l.YM + l.YS * 5.5)

        # Create clock
        xoffset = ( 1, 2, 2.5, 2, 1, 0, -1, -2, -2.5, -2, -1, 0 )
        yoffset = ( 0.25, 0.75, 1.9, 3, 3.5, 3.75, 3.5, 3, 1.9, 0.75, 0.25, 0 )
        x = l.XM + l.XS * 7
        y = l.CH / 3
        for i in range(12):
            x0 = x + xoffset[i] * l.XS
            y0 = y + yoffset[i] * l.YS
            s.foundations.append(FlowerClock_Foundation(x0, y0, self, ANY_SUIT))
            t = MfxCanvasText(self.canvas, x0 + l.CW / 2, y0 + l.YS,
                              anchor="center", font=font,
                              text=self.SUITS[i])

        # Create row stacks
        for j in range(2):
            x, y = l.XM, l.YM + l.YS * j * 2.7
            for i in range(4):
                s.rows.append(FlowerClock_RowStack(x, y, self, yoffset=l.CH/4,
                                                   max_cards=8))
                x = x + l.XS
        self.setRegion(s.rows, (0, 0, l.XS * 4, 999999))

        # Create talon
        s.talon = InitialDealTalonStack(self.width - l.XS, self.height - l.YS, self)

        # Define stack groups
        l.defaultStackGroups()

    #
    # Game over rides
    #

    def startGame(self):
        assert len(self.s.talon.cards) == 48
        for i in range(5):
            self.s.talon.dealRow(frames=0)
        self.startDealSample()
        self.s.talon.dealRow()
        assert len(self.s.talon.cards) == 0

    def isGameWon(self):
        for i in self.s.foundations:
            if len(i.cards) != 4:
                return 0
            if i.cards[0].rank != i.id:
                return 0
        return 1

    # This is a "too easy" type game, so limit help/demo to ~zero.
    def shallHighlightMatch(self, stack1, card1, stack2, card2):
        return 0

    def getAutoStacks(self, event=None):
        if event is None:
            return (self.sg.dropstacks, (), self.sg.dropstacks)
        else:
            return (self.sg.dropstacks, self.sg.dropstacks, self.sg.dropstacks)


# /***********************************************************************
#  * Gaji
#  ***********************************************************************/

class Gaji(AbstractFlowerGame):

    #
    # Game layout
    #

    def createGame(self):
        l, s = Layout(self), self.s

        # Set window size
        self.setSize(l.XM * 7 + l.XS * 12, l.YM * 2 + l.YS * 6)

        # Create left foundations
        x = l.XM
        y = l.YM
        s.foundations.append(Gaji_Foundation(x, y, self, 1))
        x = x + l.XS + 10
        s.foundations.append(Gaji_Foundation(x, y, self, 2))

        # Create row stacks
        x = x + l.XS + 20
        for i in range(8):
            s.rows.append(Gaji_RowStack(x, y, self, yoffset=l.CH/2,
                                        max_cards=12))
            x = x + l.XS
        self.setRegion(s.rows, (l.XM + (l.XS * 2) + 11, -999, l.XM + (l.XS * 10) + 20, 999999))

        # Create right foundations
        x = x + 20
        s.foundations.append(Gaji_Foundation(x, y, self, 3))
        x = x + l.XS + 10
        s.foundations.append(Gaji_Foundation(x, y, self, 0))


        # Create talon
        s.talon = InitialDealTalonStack(self.width - l.XS, self.height - l.YS, self)

        # Define stack groups
        l.defaultStackGroups()

    #
    # Game over rides
    #

    def _shuffleHook(self, cards):
        # move one card of each rank excluding gaji to the
        # bottom of the Talon (i.e. last cards to be dealt)
        topcards = [None, None, None, None]
        for c in cards[:]:
            if not topcards[c.suit]:
                if not (c.rank == 10 and c.suit == 0):
                    topcards[c.suit] = c
                    cards.remove(c)
        return topcards + cards

    def startGame(self):
        assert len(self.s.talon.cards) == 48
        for i in range(4):
            self.s.talon.dealRow(frames=0)
        self.startDealSample()
        r = self.s.rows
        self.s.talon.dealRow(rows=(r[0], r[1], r[2], r[5], r[6], r[7]))
        self.s.talon.dealRow(rows=(r[0], r[1], r[6], r[7]))
        self.s.talon.dealRow(rows=(r[0], r[7]))
        r = self.s.foundations
        self.s.talon.dealRow(rows=(r[2], r[1], r[0], r[3]))
        assert len(self.s.talon.cards) == 0

    def fillStack(self, stack):
        if stack in self.s.rows:
            if stack.cards and not stack.cards[-1].face_up:
                self.flipMove(stack)


# /***********************************************************************
#  * Oonsoo
#  ***********************************************************************/

class Oonsoo(AbstractFlowerGame):

    #
    # Game layout
    #

    def createGame(self):
        l, s = Layout(self), self.s

        # Set window size
        self.setSize(l.XM * 2 + l.XS * 8, l.YM + l.YS * 6)

        # Create row stacks
        x = l.XM + l.XS * 1.5
        y = l.YM
        for i in range(6):
            s.rows.append(Oonsoo_RowStack(x, y, self, yoffset=l.YOFFSET))
            x = x + l.XS * 1.1
        y = y + l.YS * 2.5
        x = x - l.XS * 6.6
        for i in range(6):
            s.rows.append(Oonsoo_RowStack(x, y, self, yoffset=l.YOFFSET))
            x = x + l.XS * 1.1

        # Create talon
        x = l.XM
        y = l.YM
        s.talon = DealRowTalonStack(x, y, self)
        l.createText(s.talon, "ss")

        # Define stack groups
        l.defaultStackGroups()

    #
    # Game over rides
    #

    def startGame(self):
        assert len(self.s.talon.cards) == 48
        self.s.talon.dealRow(flip=0, frames=0)
        self.startDealSample()
        self.s.talon.dealCards()

    def isGameWon(self):
        if self.s.talon.cards:
            return 0
        for s in self.s.rows:
            if len(s.cards) != 4 or not cardsFaceUp(s.cards) or not s.isOonsooSequence(s.cards):
                return 0
        return 1

    def fillStack(self, stack):
        if stack in self.s.rows:
            if stack.cards and not stack.cards[-1].face_up:
                self.flipMove(stack)


# /***********************************************************************
#  * Pagoda
#  ***********************************************************************/

class Pagoda(AbstractFlowerGame):

    #
    # Game layout
    #

    def createGame(self):
        l, s = Layout(self), self.s
        font = getFont("canvas_card", cardw=l.CW)

        # Set window size
        self.setSize(l.XM + l.XS * 11, l.YS * 6)

        # Create foundations
        self.foundation_texts = []
        for j in range(4):
            x, y = l.XM + l.XS * 8, l.YM * 3 + l.YS * j * 1.5
            for i in range(3):
                s.foundations.append(Pagoda_Foundation(x, y, self, ANY_SUIT))
                t = MfxCanvasText(self.canvas, x + l.CW / 2, y - 12,
                                  anchor="center", font=font)
                self.foundation_texts.append(t)
                x = x + l.XS

        # Build pagoda
        x, y = l.XM + l.XS, l.YM
        d = ( 0.4, 0.25, 0, 0.25, 0.4 )
        for i in range(5):
            s.reserves.append(ReserveStack(x + l.XS * i, y + l.YS * d[i], self))

        x, y = l.XM + l.XS * 2, y + l.YS * 1.1
        d = ( 0.25, 0, 0.25 )
        for i in range(3):
            s.reserves.append(ReserveStack(x + l.XS * i, y + l.YS * d[i], self))

        x, y = l.XM, y + l.YS * 1.1
        d = ( 0.5, 0.4, 0.25, 0, 0.25, 0.4, 0.5 )
        for i in range(7):
            s.reserves.append(ReserveStack(x + l.XS * i, y + l.YS * d[i], self))

        x, y = l.XM + l.XS, y + l.YS * 1.5
        for i in range(5):
            s.reserves.append(ReserveStack(x + l.XS * i, y, self))

        # Create talon
        x = l.XM + l.XS * 2.5
        y = l.YM + l.YS * 4.9
        s.talon = WasteTalonStack(x, y, self, num_deal=4, max_rounds=1)
        l.createText(s.talon, "sw")
        x = x + l.XS
        s.waste = WasteStack(x, y, self)
        l.createText(s.waste, "se")

        # Define stack groups
        l.defaultStackGroups()

    #
    # Game extras
    #

    def updateText(self):
        if self.preview > 1:
            return
        for i in range(12):
            s = self.s.foundations[i]
            if not s.cards or len(s.cards) == 8:
                text = self.SUITS[i]
            elif len(s.cards) < 5:
                text = "Rising"
            else:
                text = "Setting"
            self.foundation_texts[i].config(text=text)

    #
    # Game over rides
    #

    def startGame(self):
        assert len(self.s.talon.cards) == 48 * 2
        self.updateText()
        self.startDealSample()
        self.s.talon.dealRow(rows=self.s.reserves)
        self.s.talon.dealCards()

    def fillStack(self, stack):
        if not stack.cards and stack is self.s.waste:
            if self.canDealCards():
                self.dealCards()


# /***********************************************************************
#  * Matsukiri
#  ***********************************************************************/

class MatsuKiri(AbstractFlowerGame):

    #
    # Game layout
    #

    def createGame(self):
        l, s = Layout(self), self.s

        # Set window size
        self.setSize(l.XM * 3 + l.XS * 9, l.YM + l.YS * 6)

        # Create row stacks
        x = l.XM
        y = l.YM
        for i in range(8):
            s.rows.append(Matsukiri_RowStack(x, y, self, yoffset=l.CH/2, max_cards=12))
            x = x + l.XS
        self.setRegion(s.rows, (-999, -999, l.XM + (l.XS * 8) + 10, 999999))

        # Create foundation
        x = x + l.XM * 2
        s.foundations.append(MatsuKiri_Foundation(x, y, self, ANY_SUIT))
        self.setRegion(s.foundations, (l.XM + (l.XS * 8) + 10, -999, 999999, 999999))

        # Create talon
        s.talon = InitialDealTalonStack(self.width - l.XS, self.height - l.YS, self)

        # Define stack groups
        l.defaultStackGroups()

    #
    # Game over rides
    #

    def startGame(self):
        assert len(self.s.talon.cards) == 48
        for i in range(5):
            self.s.talon.dealRow(frames=0)
        self.startDealSample()
        self.s.talon.dealRow()
        assert len(self.s.talon.cards) == 0

    def fillStack(self, stack):
        if stack in self.s.rows:
            if len(stack.cards) > 0 and not stack.cards[-1].face_up:
                self.flipMove(stack)


# /***********************************************************************
#  * Samuri
#  ***********************************************************************/

class Samuri(AbstractFlowerGame):

    #
    # Game layout
    #

    def createGame(self):
        l, s = Layout(self), self.s
        font = getFont("canvas_card", cardw=l.CW)

        # Set window size
        self.setSize(l.XM * 3 + l.XS * 11, l.YM + l.YS * 6)

        # Create left foundation group
        n = 0
        for i in range(3):
            x = l.XM
            y = l.YS - l.YM + (l.YS * i * 2)
            for j in range(2):
                s.foundations.append(Samuri_Foundation(x, y, self, ANY_SUIT))
                t = MfxCanvasText(self.canvas, x + l.CW / 2, y + l.YS + 5,
                                  anchor="center", font=font, text=self.SUITS[n])
                x = x + l.XS
                n = n + 1

        # Create right foundation group
        for i in range(3):
            x = l.XM * 3 + l.XS * 9
            y = l.YS - l.YM + (l.YS * i * 2)
            for j in range(2):
                s.foundations.append(Samuri_Foundation(x, y, self, ANY_SUIT))
                t = MfxCanvasText(self.canvas, x + l.CW / 2, y + l.YS + 5,
                                  anchor="center", font=font, text=self.SUITS[n])
                x = x + l.XS
                n = n + 1

        # Create row stacks
        x = l.XM * 2 + l.XS * 2
        y = l.YM
        for i in range(7):
            s.rows.append(Samuri_RowStack(x, y, self, yoffset=l.CH/4, max_cards=8))
            x = x + l.XS
        self.setRegion(s.rows, (l.XM + (l.XS * 2) , -999, (l.XM * 2) + (l.XS * 9), 999999))

        # Create talon
        x = l.XM * 2 + l.XS * 4.5
        y = self.height - l.YS * 2
        s.talon = WasteTalonStack(x, y, self, num_deal=1, max_rounds=1)
        l.createText(s.talon, "sw")
        x = x + l.XS
        s.waste = WasteStack(x, y, self)
        l.createText(s.waste, "se")

        # Define stack groups
        l.defaultStackGroups()

    #
    # Game over rides
    #

    def startGame(self):
        assert len(self.s.talon.cards) == 48
        self.s.talon.dealRow(flip=0, frames=0)
        for i in range(len(self.s.rows)):
            self.s.talon.dealRow(rows=self.s.rows[i:7-i], flip=0, frames=0)
        self.startDealSample()
        self.s.talon.dealRow()
        self.s.talon.dealCards()

    def fillStack(self, stack):
        if not stack.cards and stack is self.s.waste:
            if self.canDealCards():
                self.dealCards()
        if stack in self.s.rows:
            if len(stack.cards) and not stack.cards[-1].face_up:
                self.flipMove(stack)


# /***********************************************************************
#  * Great Wall
#  ***********************************************************************/

class GreatWall(AbstractFlowerGame):

    #
    # Game layout
    #

    def createGame(self):
        l, s = Layout(self), self.s
        font = getFont("canvas_card", cardw=l.CW)

        # Set window size
        self.setSize(l.XM + l.XS * 15, l.YM + l.YS * 6.2)

        # Create foundation stacks
        self.foundation_texts = []
        x = l.XM
        y = l.YM
        for i in range(2):
            s.foundations.append(GreatWall_Foundation(x, y, self, i + 1))
            self.foundation_texts.append(MfxCanvasText(self.canvas,
                                                       x + l.CW + 4,
                                                       y + l.CH / 2,
                                                       anchor="w", font=font))
            y = y + l.YS * 1
        x = self.width - l.XS
        y = l.YM
        for i in range(2):
            s.foundations.append(GreatWall_Foundation(x, y, self, (i + 3) % 4))
            self.foundation_texts.append(MfxCanvasText(self.canvas,
                                                       x - 4,
                                                       y + l.CH / 2,
                                                       anchor="e", font=font))
            y = y + l.YS * 1

        # Create build stacks
        x = l.XM
        y = l.YM + l.YS * 2.2
        for i in range(2):
            s.foundations.append(GreatWall_BuildStack(x, y, self, i + 1))
            self.foundation_texts.append(MfxCanvasText(self.canvas,
                                                       x + l.CW + 4,
                                                       y + l.CH / 2,
                                                       anchor="w", font=font))
            y = y + l.YS * 2
        x = self.width - l.XS
        y = l.YM + l.YS * 2.2
        for i in range(2):
            s.foundations.append(GreatWall_BuildStack(x, y, self, (i + 3) % 4))
            self.foundation_texts.append(MfxCanvasText(self.canvas,
                                                       x - 4,
                                                       y + l.CH / 2,
                                                       anchor="e", font=font))
            y = y + l.YS * 2

        # Create row stacks
        x = l.XM + l.XS * 1.5
        y = l.YM
        for i in range(12):
            s.rows.append(GreatWall_RowStack(x, y, self, yoffset=l.CH/4, max_cards=26))
            x = x + l.XS
        self.setRegion(s.rows, (l.XM + l.XS * 1.25, -999, self.width - l.XS * 1.25, 999999))

        # Create talon
        x = self.width / 2 - l.CW / 2
        y = self.height - l.YS * 1.2
        s.talon = InitialDealTalonStack(x, y, self)

        # Define stack groups
        l.defaultStackGroups()

    #
    # Game extras
    #

    def updateText(self):
        if self.preview > 1:
            return
        for i in range(8):
            if i < 4:
                t, f = len(self.s.foundations[i].cards) / 12, 3
            else:
                t, f = len(self.s.foundations[i].cards), 12
            if t == 0:
                t = ""
            elif t == f:
                t = "Full"
            self.foundation_texts[i].config(text=str(t))

    #
    # Game over rides
    #

    def startGame(self):
        assert len(self.s.talon.cards) == 48 * 4
        self.updateText()
        for i in range(15):
            self.s.talon.dealRow(flip=0, frames=0)
        self.startDealSample()
        self.s.talon.dealRow()
        assert len(self.s.talon.cards) == 0

    def fillStack(self, stack):
        if stack in self.s.rows:
            if stack.cards and not stack.cards[-1].face_up:
                self.flipMove(stack)

    def shallHighlightMatch(self, stack1, card1, stack2, card2):
        return (card1.suit == card2.suit and
                ((card1.rank + 1) % 12 == card2.rank or (card1.rank - 1) % 12 == card2.rank))


# /***********************************************************************
# * Four Winds
# ************************************************************************/

class FourWinds(AbstractFlowerGame):

    #
    # Game layout
    #

    def createGame(self):
        l, s = Layout(self), self.s
        font = getFont("canvas_card", cardw=l.CW)

        # Set window size
        self.setSize(7 * l.XS, 5 * l.YS + 3 * l.YM)

        # Four winds
        TEXTS = ("North", "East", "South", "West", "NW", "NE", "SE", "SW")

        # Create foundations
        x = l.XM * 3
        y = l.YM
        xoffset = (0, 2.5, 5, 2.5)
        yoffset = (2, 0, 2, 4)
        for i in range(4):
            x0 = x + (xoffset[i] * l.XS)
            y0 = y + (yoffset[i] * l.YS)
            s.foundations.append(FourWinds_Foundation(x0, y0, self, i))
            t = MfxCanvasText(self.canvas, x0 + l.CW / 2, y0 + l.YS + 5,
                              anchor="center", font=font, text=TEXTS[i])

        # Create rows
        xoffset = (1.25, 3.75, 3.75, 1.25)
        yoffset = (0.75, 0.75, 3, 3)
        for i in range(4):
            x0 = x + (xoffset[i] * l.XS)
            y0 = y + (yoffset[i] * l.YS)
            s.rows.append(FourWinds_RowStack(x0, y0, self, yoffset=10,
                                             max_cards=3, max_accept=1))
            t = MfxCanvasText(self.canvas, x0 + l.CW / 2, y0 + l.YS + 5,
                              anchor="center", font=font, text=TEXTS[i+4])
        self.setRegion(s.rows, (x + l.XS, y + l.YS * 0.65, x + l.XS * 4 + 5, y + l.YS * 3 + 5))

        # Create talon
        x = x + 2 * l.XS
        y = y + 2 * l.YS
        s.talon = WasteTalonStack(x, y, self, num_deal=1, max_rounds=2)
        l.createText(s.talon, "ss")
        x = x + l.XS
        s.waste = WasteStack(x, y, self)
        l.createText(s.waste, "ss")

        # Define stack-groups
        l.defaultStackGroups()

    #
    # Game over rides
    #

    def startGame(self):
        assert len(self.s.talon.cards) == 48
        self.startDealSample()
        self.s.talon.dealCards()

    def fillStack(self, stack):
        if not stack.cards and stack is self.s.waste:
            if self.canDealCards():
                self.dealCards()

    def shallHighlightMatch(self, stack1, card1, stack2, card2):
        return (card1.suit == card2.suit and
                ((card1.rank + 1) % 12 == card2.rank or (card1.rank - 1) % 12 == card2.rank))


# /***********************************************************************
# // register the games
# ************************************************************************/

def r(id, gameclass, name, game_type, decks, redeals):
    game_type = game_type | GI.GT_HANAFUDA | GI.GT_CONTRIB
    gi = GameInfo(id, gameclass, name, game_type, decks, redeals,
                  ranks=range(12))
    registerGame(gi)
    return gi

r(348, FlowerClock, "Flower Clock", GI.GT_HANAFUDA | GI.GT_OPEN, 1, 0)
r(347, Gaji, "Gaji", GI.GT_HANAFUDA | GI.GT_OPEN, 1, 0)
r(345, Oonsoo, "Oonsoo", GI.GT_HANAFUDA, 1, 0)
r(349, Pagoda, "Pagoda", GI.GT_HANAFUDA, 2, 0)
r(346, MatsuKiri, "MatsuKiri", GI.GT_HANAFUDA | GI.GT_OPEN, 1, 0)
r(350, Samuri, "Samuri", GI.GT_HANAFUDA, 1, 0)
r(351, GreatWall, "Great Wall", GI.GT_HANAFUDA, 4, 0)
r(352, FourWinds, "Four Winds", GI.GT_HANAFUDA, 1, 1)

del r

