dabodemo Commit Revision 278 Date: 2005-08-31 14:18:03 -0700 (Wed, 31 Aug 2005) Author: ed
Changed: D trunk/montana/ A trunk/montana.py
Log: Moved the code out of its own directory, and into a simple script. Added a help dialog to display the rules.
Diff: Copied: trunk/montana.py (from rev 275, trunk/montana/main.py) =================================================================== --- trunk/montana/main.py 2005-08-31 20:02:54 UTC (rev 275) +++ trunk/montana.py 2005-08-31 21:18:03 UTC (rev 278) @@ -0,0 +1,628 @@ +#!/usr/bin/env python +"""This is a favorite card game of mine called 'Montana'. """ + +helpText = """Object: +To arrange all of the cards into 4 rows in increasing order from +2 to King, with one suit per row. + +Starting a game +The cards are dealt out into 4 rows of 13 cards each. The aces +are then removed, leaving 4 gaps. + +Playing the game: +Move cards into the gaps, which will create new gaps in their old +location. The only card that can be moved into any gap is determined +by the card to the immediate left of the gap. The moved card must be +the same suit, and one rank higher. Example: if there is a gap, and the +card to left of it is 4C, only 5C can be moved to the gap. +If the gap is located in the leftmost column, any 2 card can be moved +there. If the card to the left of the gap is a King, no card can be moved +there. +When all 4 gaps are located to the right of Kings, no further moves are +possible, and the hand ends. If there are any re-deals remaining, the +cards are re-dealt, and another hand is played. When all re-deals are +used, the game ends. The number of re-deals can be set in the game +preferences (default=2 re-deals). + +Scoring: +When a card is placed "in order", it scores a point. "In order" is defined +as any cards arranged with a 2 of that suit in the leftmost column of a +row, followed by other cards of that suit in sequence. Since Aces are not +played, you can score a maximum of 48 points per level. Completing a +level starts you over again, adding an additional re-deal to your remaining +re-deal status. + +Re-deals: +All ordered cards (i.e., those that in sequence and have scored a point) +remain where they are. All unordered cards (i.e., those not in sequence) +are picked up, shuffled with the Aces, and dealt into the open spaces. The +Aces are then removed, and play resumes. +""" + +import random +import dabo +from dabo.dLocalize import _ +dabo.ui.loadUI("wx") + + +class Card(dabo.ui.dBitmapButton): + def afterInit(self): + self._suit = None + self._rank = None + self.scored = False + self.bindEvent(dabo.dEvents.Hit, self.onClick) + self.bindEvent(dabo.dEvents.MouseLeftDown, self.onMDown) + self.bindEvent(dabo.dEvents.MouseLeftUp, self.onMUp) + # Turn auto-resize on + self.AutoSize = True + # These help when re-scaling + baseBmp = dabo.ui.dBitmap(self.Parent, Picture="cards/blank") + self._baseWd = baseBmp.Width + self._baseHt = baseBmp.Height + baseBmp.release() + + # Base the size on the ImageScale + self.ImageScale = 1.0 + + + def onClick(self, evt): + self.Parent.cardClick(self) + + + def onMDown(self, evt): + self.Parent.cardMDown(self) + + + def onMUp(self, evt): + self.Parent.cardMUp(self) + + + def updPic(self): + """Sets the Picture property for this card to match + its Suit and Rank. + """ + if not self.Rank or not self.Suit: + # Card isn't set yet + pic = "cards/blank" + else: + rank = self.Rank + suit = self.Suit.lower() + if rank == 1: + # Ace, hide it + pic = "cards/blank" + else: + pic = "cards/%s%s" % (suit, str(rank)) + self.Picture = pic + + + def setDeadPic(self): + """Mark the card as dead""" + self.Picture = "cards/x" + + + def setLivePic(self): + """Mark the card as active""" + self.Picture = "cards/blank" + + + def _getBaseHt(self): + return self._baseHt + + def _getBaseWd(self): + return self._baseWd + + def _getDesc(self): + rank = self._rank + suit = self._suit + if rank == 1: + ret = "Empty Space" + else: + if rank == 11: + ret = "Jack" + elif rank == 12: + ret = "Queen" + elif rank == 13: + ret = "King" + else: + ret = str(rank) + suitNames = {"S" : "Spades", "D" : "Diamonds", "H" : "Hearts", "C" : "Clubs"} + ret += " of %s" % suitNames[suit] + return ret + + + def _getRank(self): + return self._rank + def _setRank(self, val): + if self._rank != val: + self._rank = val + self.updPic() + + def _getSuit(self): + return self._suit + def _setSuit(self, val): + suit = val[0].upper() + if self._suit != suit: + if suit in ("H", "D", "S", "C"): + self._suit = suit + self.updPic() + + BaseHeight = property(_getBaseHt, None, None, + _("Normal (100%) height of the card (int)") ) + + BaseWidth = property(_getBaseWd, None, None, + _("Normal (100%) width of the card (int)") ) + + Description = property(_getDesc, None, None, + _("Descriptive name for this card, such as 'King of Clubs' (str)") ) + + Rank = property(_getRank, _setRank, None, + _("Rank for this card (int)") ) + + Suit = property(_getSuit, _setSuit, None, + _("Suit for this card (Spades, Hearts, Diamonds, Clubs)") ) + + +class Board(dabo.ui.dPanel): + def afterInit(self): + self.Sizer = dabo.ui.dSizer("v") + self.gridSizer = None + self._redeals = 2 + self._score = 0 + self.isStuck = True + # Controls card flashing + self.cardTimer = dabo.ui.dTimer(self, Interval=100) + self.cardTimer.bindEvent(dabo.dEvents.Hit, self.onCardTimer) + self.flashCard = None + # Holds a reference to all the aces in the deck. + self.aces = [] + # Flag that indicates we need to resize the cards + self.needResize = False + # Base (100%) size of the board + self._baseWd = self._baseHt = None + # Create the deck + self.deck = self.createDeck() + + + def initEvents(self): + self.bindEvent(dabo.dEvents.Resize, self.onResize) + self.bindEvent(dabo.dEvents.Idle, self.onIdle) + + + def onResize(self, evt): + """Resize the cards to fit the board.""" + self.needResize = True + + + def onIdle(self, evt): + if not self.needResize: + return + if self._baseWd is None: + # No deck info yet + return + self.needResize = False + # Determine the ratios needed to fill this panel + wdRatio = self.Width / float(self._baseWd) + htRatio = self.Height / float(self._baseHt) + # Use the smaller of the two, in order to guarantee that + # the full layout will fit. + newRatio = min(wdRatio, htRatio) + self.setAll("ImageScale", newRatio) + self.layout() + + + def sizeToNormal(self): + """Reset the cards to 100% size""" + self.setAll("ImageScale", 1.0) + self.layout() + + + def createSizer(self): + if self.gridSizer: + self.Sizer.remove(self.gridSizer) + for card in self.deck: + self.gridSizer.remove(card) + self.gridSizer.release() + self.gridSizer = dabo.ui.dGridSizer(maxCols=13, hgap=2, vgap=2) + self.Sizer.append1x(self.gridSizer) + + + def newGame(self): + # Change this to use preference setting! + self._redeals = 2 + self._score = 0 + + # Contains the current layout of the cards. + self.cardLayout = () + # This holds the history, enabling undo. Most recent positions + # are at the end. + self.historyStack = [] + # This holds the redo stack + self.redoStack = [] + random.shuffle(self.deck) + self.createSizer() + self.gridSizer.appendItems(self.deck) + self.updateCardLayout() + self.updateStatus() + + if self._baseWd is None: + # First time through. Set the base measurments + cd = self.deck[0] + cdW, cdH = cd.BaseWidth, cd.BaseHeight + hgap = self.gridSizer.hgap + vgap = self.gridSizer.vgap + # Total width is the 13 card widths + 12 hgaps + self._baseWd = (13 * cdW) + (12 * hgap) + # Same goes for heights + self._baseHt = (4 * cdH) + (3 * vgap) + self.needResize = True + + + def createDeck(self): + """Creates a dict representing a 52-card deck.""" + ret = [] + for suit in "SHDC": + for rank in range(1, 14): + card = Card(self, Suit=suit, Rank=rank) + ret.append(card) + if rank == 1: + self.aces.append(card) + return ret + + + def redeal(self): + """ Gather all the non-scored cards, shuff [excessive length snipped] ©2005 Ed Leafe |
|