GIF89a;
EcchiShell v1.0
/
/
usr/
lib64/
lib64/
lib64/
lib64/
python2.7/
', self.clickhandler)
self.group.bind('', self.doubleclickhandler)
self.group.bind('', self.motionhandler)
self.group.bind('', 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()