symbian-qemu-0.9.1-12/python-2.6.1/Demo/tkinter/guido/solitaire.py
changeset 1 2fb8b9db1c86
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/symbian-qemu-0.9.1-12/python-2.6.1/Demo/tkinter/guido/solitaire.py	Fri Jul 31 15:01:17 2009 +0100
@@ -0,0 +1,637 @@
+#! /usr/bin/env python
+
+"""Solitaire game, much like the one that comes with MS Windows.
+
+Limitations:
+
+- No cute graphical images for the playing cards faces or backs.
+- No scoring or timer.
+- No undo.
+- No option to turn 3 cards at a time.
+- No keyboard shortcuts.
+- Less fancy animation when you win.
+- The determination of which stack you drag to is more relaxed.
+
+Apology:
+
+I'm not much of a card player, so my terminology in these comments may
+at times be a little unusual.  If you have suggestions, please let me
+know!
+
+"""
+
+# Imports
+
+import math
+import random
+
+from Tkinter import *
+from Canvas import Rectangle, CanvasText, Group, Window
+
+
+# Fix a bug in Canvas.Group as distributed in Python 1.4.  The
+# distributed bind() method is broken.  Rather than asking you to fix
+# the source, we fix it here by deriving a subclass:
+
+class Group(Group):
+    def bind(self, sequence=None, command=None):
+        return self.canvas.tag_bind(self.id, sequence, command)
+
+
+# Constants determining the size and lay-out of cards and stacks.  We
+# work in a "grid" where each card/stack is surrounded by MARGIN
+# pixels of space on each side, so adjacent stacks are separated by
+# 2*MARGIN pixels.  OFFSET is the offset used for displaying the
+# face down cards in the row stacks.
+
+CARDWIDTH = 100
+CARDHEIGHT = 150
+MARGIN = 10
+XSPACING = CARDWIDTH + 2*MARGIN
+YSPACING = CARDHEIGHT + 4*MARGIN
+OFFSET = 5
+
+# The background color, green to look like a playing table.  The
+# standard green is way too bright, and dark green is way to dark, so
+# we use something in between.  (There are a few more colors that
+# could be customized, but they are less controversial.)
+
+BACKGROUND = '#070'
+
+
+# Suits and colors.  The values of the symbolic suit names are the
+# strings used to display them (you change these and VALNAMES to
+# internationalize the game).  The COLOR dictionary maps suit names to
+# colors (red and black) which must be Tk color names.  The keys() of
+# the COLOR dictionary conveniently provides us with a list of all
+# suits (in arbitrary order).
+
+HEARTS = 'Heart'
+DIAMONDS = 'Diamond'
+CLUBS = 'Club'
+SPADES = 'Spade'
+
+RED = 'red'
+BLACK = 'black'
+
+COLOR = {}
+for s in (HEARTS, DIAMONDS):
+    COLOR[s] = RED
+for s in (CLUBS, SPADES):
+    COLOR[s] = BLACK
+
+ALLSUITS = COLOR.keys()
+NSUITS = len(ALLSUITS)
+
+
+# Card values are 1-13.  We also define symbolic names for the picture
+# cards.  ALLVALUES is a list of all card values.
+
+ACE = 1
+JACK = 11
+QUEEN = 12
+KING = 13
+ALLVALUES = range(1, 14) # (one more than the highest value)
+NVALUES = len(ALLVALUES)
+
+
+# VALNAMES is a list that maps a card value to string.  It contains a
+# dummy element at index 0 so it can be indexed directly with the card
+# value.
+
+VALNAMES = ["", "A"] + map(str, range(2, 11)) + ["J", "Q", "K"]
+
+
+# Solitaire constants.  The only one I can think of is the number of
+# row stacks.
+
+NROWS = 7
+
+
+# The rest of the program consists of class definitions.  These are
+# further described in their documentation strings.
+
+
+class Card:
+
+    """A playing card.
+
+    A card doesn't record to which stack it belongs; only the stack
+    records this (it turns out that we always know this from the
+    context, and this saves a ``double update'' with potential for
+    inconsistencies).
+
+    Public methods:
+
+    moveto(x, y) -- move the card to an absolute position
+    moveby(dx, dy) -- move the card by a relative offset
+    tkraise() -- raise the card to the top of its stack
+    showface(), showback() -- turn the card face up or down & raise it
+
+    Public read-only instance variables:
+
+    suit, value, color -- the card's suit, value and color
+    face_shown -- true when the card is shown face up, else false
+
+    Semi-public read-only instance variables (XXX should be made
+    private):
+
+    group -- the Canvas.Group representing the card
+    x, y -- the position of the card's top left corner
+
+    Private instance variables:
+
+    __back, __rect, __text -- the canvas items making up the card
+
+    (To show the card face up, the text item is placed in front of
+    rect and the back is placed behind it.  To show it face down, this
+    is reversed.  The card is created face down.)
+
+    """
+
+    def __init__(self, suit, value, canvas):
+        """Card constructor.
+
+        Arguments are the card's suit and value, and the canvas widget.
+
+        The card is created at position (0, 0), with its face down
+        (adding it to a stack will position it according to that
+        stack's rules).
+
+        """
+        self.suit = suit
+        self.value = value
+        self.color = COLOR[suit]
+        self.face_shown = 0
+
+        self.x = self.y = 0
+        self.group = Group(canvas)
+
+        text = "%s  %s" % (VALNAMES[value], suit)
+        self.__text = CanvasText(canvas, CARDWIDTH//2, 0,
+                               anchor=N, fill=self.color, text=text)
+        self.group.addtag_withtag(self.__text)
+
+        self.__rect = Rectangle(canvas, 0, 0, CARDWIDTH, CARDHEIGHT,
+                              outline='black', fill='white')
+        self.group.addtag_withtag(self.__rect)
+
+        self.__back = Rectangle(canvas, MARGIN, MARGIN,
+                              CARDWIDTH-MARGIN, CARDHEIGHT-MARGIN,
+                              outline='black', fill='blue')
+        self.group.addtag_withtag(self.__back)
+
+    def __repr__(self):
+        """Return a string for debug print statements."""
+        return "Card(%r, %r)" % (self.suit, self.value)
+
+    def moveto(self, x, y):
+        """Move the card to absolute position (x, y)."""
+        self.moveby(x - self.x, y - self.y)
+
+    def moveby(self, dx, dy):
+        """Move the card by (dx, dy)."""
+        self.x = self.x + dx
+        self.y = self.y + dy
+        self.group.move(dx, dy)
+
+    def tkraise(self):
+        """Raise the card above all other objects in its canvas."""
+        self.group.tkraise()
+
+    def showface(self):
+        """Turn the card's face up."""
+        self.tkraise()
+        self.__rect.tkraise()
+        self.__text.tkraise()
+        self.face_shown = 1
+
+    def showback(self):
+        """Turn the card's face down."""
+        self.tkraise()
+        self.__rect.tkraise()
+        self.__back.tkraise()
+        self.face_shown = 0
+
+
+class Stack:
+
+    """A generic stack of cards.
+
+    This is used as a base class for all other stacks (e.g. the deck,
+    the suit stacks, and the row stacks).
+
+    Public methods:
+
+    add(card) -- add a card to the stack
+    delete(card) -- delete a card from the stack
+    showtop() -- show the top card (if any) face up
+    deal() -- delete and return the top card, or None if empty
+
+    Method that subclasses may override:
+
+    position(card) -- move the card to its proper (x, y) position
+
+        The default position() method places all cards at the stack's
+        own (x, y) position.
+
+    userclickhandler(), userdoubleclickhandler() -- called to do
+    subclass specific things on single and double clicks
+
+        The default user (single) click handler shows the top card
+        face up.  The default user double click handler calls the user
+        single click handler.
+
+    usermovehandler(cards) -- called to complete a subpile move
+
+        The default user move handler moves all moved cards back to
+        their original position (by calling the position() method).
+
+    Private methods:
+
+    clickhandler(event), doubleclickhandler(event),
+    motionhandler(event), releasehandler(event) -- event handlers
+
+        The default event handlers turn the top card of the stack with
+        its face up on a (single or double) click, and also support
+        moving a subpile around.
+
+    startmoving(event) -- begin a move operation
+    finishmoving() -- finish a move operation
+
+    """
+
+    def __init__(self, x, y, game=None):
+        """Stack constructor.
+
+        Arguments are the stack's nominal x and y position (the top
+        left corner of the first card placed in the stack), and the
+        game object (which is used to get the canvas; subclasses use
+        the game object to find other stacks).
+
+        """
+        self.x = x
+        self.y = y
+        self.game = game
+        self.cards = []
+        self.group = Group(self.game.canvas)
+        self.group.bind('<1>', self.clickhandler)
+        self.group.bind('<Double-1>', self.doubleclickhandler)
+        self.group.bind('<B1-Motion>', self.motionhandler)
+        self.group.bind('<ButtonRelease-1>', self.releasehandler)
+        self.makebottom()
+
+    def makebottom(self):
+        pass
+
+    def __repr__(self):
+        """Return a string for debug print statements."""
+        return "%s(%d, %d)" % (self.__class__.__name__, self.x, self.y)
+
+    # Public methods
+
+    def add(self, card):
+        self.cards.append(card)
+        card.tkraise()
+        self.position(card)
+        self.group.addtag_withtag(card.group)
+
+    def delete(self, card):
+        self.cards.remove(card)
+        card.group.dtag(self.group)
+
+    def showtop(self):
+        if self.cards:
+            self.cards[-1].showface()
+
+    def deal(self):
+        if not self.cards:
+            return None
+        card = self.cards[-1]
+        self.delete(card)
+        return card
+
+    # Subclass overridable methods
+
+    def position(self, card):
+        card.moveto(self.x, self.y)
+
+    def userclickhandler(self):
+        self.showtop()
+
+    def userdoubleclickhandler(self):
+        self.userclickhandler()
+
+    def usermovehandler(self, cards):
+        for card in cards:
+            self.position(card)
+
+    # Event handlers
+
+    def clickhandler(self, event):
+        self.finishmoving()             # In case we lost an event
+        self.userclickhandler()
+        self.startmoving(event)
+
+    def motionhandler(self, event):
+        self.keepmoving(event)
+
+    def releasehandler(self, event):
+        self.keepmoving(event)
+        self.finishmoving()
+
+    def doubleclickhandler(self, event):
+        self.finishmoving()             # In case we lost an event
+        self.userdoubleclickhandler()
+        self.startmoving(event)
+
+    # Move internals
+
+    moving = None
+
+    def startmoving(self, event):
+        self.moving = None
+        tags = self.game.canvas.gettags('current')
+        for i in range(len(self.cards)):
+            card = self.cards[i]
+            if card.group.tag in tags:
+                break
+        else:
+            return
+        if not card.face_shown:
+            return
+        self.moving = self.cards[i:]
+        self.lastx = event.x
+        self.lasty = event.y
+        for card in self.moving:
+            card.tkraise()
+
+    def keepmoving(self, event):
+        if not self.moving:
+            return
+        dx = event.x - self.lastx
+        dy = event.y - self.lasty
+        self.lastx = event.x
+        self.lasty = event.y
+        if dx or dy:
+            for card in self.moving:
+                card.moveby(dx, dy)
+
+    def finishmoving(self):
+        cards = self.moving
+        self.moving = None
+        if cards:
+            self.usermovehandler(cards)
+
+
+class Deck(Stack):
+
+    """The deck is a stack with support for shuffling.
+
+    New methods:
+
+    fill() -- create the playing cards
+    shuffle() -- shuffle the playing cards
+
+    A single click moves the top card to the game's open deck and
+    moves it face up; if we're out of cards, it moves the open deck
+    back to the deck.
+
+    """
+
+    def makebottom(self):
+        bottom = Rectangle(self.game.canvas,
+                           self.x, self.y,
+                           self.x+CARDWIDTH, self.y+CARDHEIGHT,
+                           outline='black', fill=BACKGROUND)
+        self.group.addtag_withtag(bottom)
+
+    def fill(self):
+        for suit in ALLSUITS:
+            for value in ALLVALUES:
+                self.add(Card(suit, value, self.game.canvas))
+
+    def shuffle(self):
+        n = len(self.cards)
+        newcards = []
+        for i in randperm(n):
+            newcards.append(self.cards[i])
+        self.cards = newcards
+
+    def userclickhandler(self):
+        opendeck = self.game.opendeck
+        card = self.deal()
+        if not card:
+            while 1:
+                card = opendeck.deal()
+                if not card:
+                    break
+                self.add(card)
+                card.showback()
+        else:
+            self.game.opendeck.add(card)
+            card.showface()
+
+
+def randperm(n):
+    """Function returning a random permutation of range(n)."""
+    r = range(n)
+    x = []
+    while r:
+        i = random.choice(r)
+        x.append(i)
+        r.remove(i)
+    return x
+
+
+class OpenStack(Stack):
+
+    def acceptable(self, cards):
+        return 0
+
+    def usermovehandler(self, cards):
+        card = cards[0]
+        stack = self.game.closeststack(card)
+        if not stack or stack is self or not stack.acceptable(cards):
+            Stack.usermovehandler(self, cards)
+        else:
+            for card in cards:
+                self.delete(card)
+                stack.add(card)
+            self.game.wincheck()
+
+    def userdoubleclickhandler(self):
+        if not self.cards:
+            return
+        card = self.cards[-1]
+        if not card.face_shown:
+            self.userclickhandler()
+            return
+        for s in self.game.suits:
+            if s.acceptable([card]):
+                self.delete(card)
+                s.add(card)
+                self.game.wincheck()
+                break
+
+
+class SuitStack(OpenStack):
+
+    def makebottom(self):
+        bottom = Rectangle(self.game.canvas,
+                           self.x, self.y,
+                           self.x+CARDWIDTH, self.y+CARDHEIGHT,
+                           outline='black', fill='')
+
+    def userclickhandler(self):
+        pass
+
+    def userdoubleclickhandler(self):
+        pass
+
+    def acceptable(self, cards):
+        if len(cards) != 1:
+            return 0
+        card = cards[0]
+        if not self.cards:
+            return card.value == ACE
+        topcard = self.cards[-1]
+        return card.suit == topcard.suit and card.value == topcard.value + 1
+
+
+class RowStack(OpenStack):
+
+    def acceptable(self, cards):
+        card = cards[0]
+        if not self.cards:
+            return card.value == KING
+        topcard = self.cards[-1]
+        if not topcard.face_shown:
+            return 0
+        return card.color != topcard.color and card.value == topcard.value - 1
+
+    def position(self, card):
+        y = self.y
+        for c in self.cards:
+            if c == card:
+                break
+            if c.face_shown:
+                y = y + 2*MARGIN
+            else:
+                y = y + OFFSET
+        card.moveto(self.x, y)
+
+
+class Solitaire:
+
+    def __init__(self, master):
+        self.master = master
+
+        self.canvas = Canvas(self.master,
+                             background=BACKGROUND,
+                             highlightthickness=0,
+                             width=NROWS*XSPACING,
+                             height=3*YSPACING + 20 + MARGIN)
+        self.canvas.pack(fill=BOTH, expand=TRUE)
+
+        self.dealbutton = Button(self.canvas,
+                                 text="Deal",
+                                 highlightthickness=0,
+                                 background=BACKGROUND,
+                                 activebackground="green",
+                                 command=self.deal)
+        Window(self.canvas, MARGIN, 3*YSPACING + 20,
+               window=self.dealbutton, anchor=SW)
+
+        x = MARGIN
+        y = MARGIN
+
+        self.deck = Deck(x, y, self)
+
+        x = x + XSPACING
+        self.opendeck = OpenStack(x, y, self)
+
+        x = x + XSPACING
+        self.suits = []
+        for i in range(NSUITS):
+            x = x + XSPACING
+            self.suits.append(SuitStack(x, y, self))
+
+        x = MARGIN
+        y = y + YSPACING
+
+        self.rows = []
+        for i in range(NROWS):
+            self.rows.append(RowStack(x, y, self))
+            x = x + XSPACING
+
+        self.openstacks = [self.opendeck] + self.suits + self.rows
+
+        self.deck.fill()
+        self.deal()
+
+    def wincheck(self):
+        for s in self.suits:
+            if len(s.cards) != NVALUES:
+                return
+        self.win()
+        self.deal()
+
+    def win(self):
+        """Stupid animation when you win."""
+        cards = []
+        for s in self.openstacks:
+            cards = cards + s.cards
+        while cards:
+            card = random.choice(cards)
+            cards.remove(card)
+            self.animatedmoveto(card, self.deck)
+
+    def animatedmoveto(self, card, dest):
+        for i in range(10, 0, -1):
+            dx, dy = (dest.x-card.x)//i, (dest.y-card.y)//i
+            card.moveby(dx, dy)
+            self.master.update_idletasks()
+
+    def closeststack(self, card):
+        closest = None
+        cdist = 999999999
+        # Since we only compare distances,
+        # we don't bother to take the square root.
+        for stack in self.openstacks:
+            dist = (stack.x - card.x)**2 + (stack.y - card.y)**2
+            if dist < cdist:
+                closest = stack
+                cdist = dist
+        return closest
+
+    def deal(self):
+        self.reset()
+        self.deck.shuffle()
+        for i in range(NROWS):
+            for r in self.rows[i:]:
+                card = self.deck.deal()
+                r.add(card)
+        for r in self.rows:
+            r.showtop()
+
+    def reset(self):
+        for stack in self.openstacks:
+            while 1:
+                card = stack.deal()
+                if not card:
+                    break
+                self.deck.add(card)
+                card.showback()
+
+
+# Main function, run when invoked as a stand-alone Python program.
+
+def main():
+    root = Tk()
+    game = Solitaire(root)
+    root.protocol('WM_DELETE_WINDOW', root.quit)
+    root.mainloop()
+
+if __name__ == '__main__':
+    main()