#! /usr/bin/python
##
## vim:ts=4:et:nowrap
##
##---------------------------------------------------------------------------##
##
## PySol -- a Python Solitaire game
##
## 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@jk.uni-linz.ac.at>
## http://wildsau.idv.uni-linz.ac.at/mfx/pysol.html
##
##---------------------------------------------------------------------------##

### DO NOT EDIT, THIS FILE WAS GENERATED AUTOMATICALLY BY BUNDLE.PY !

import sys,os,re,string,time,types,whrandom
import formatter,htmllib
import Tkinter,Canvas,tkColorChooser,tkFileDialog
from Canvas import Bitmap,CanvasText,ImageItem,Rectangle,Window
try:
 from cPickle import Pickler,Unpickler,UnpicklingError
except ImportError:
 from pickle import Pickler,Unpickler,UnpicklingError
PACKAGE = "PySol"
VERSION = "1.00"
SUITS = [ 'Club', 'Spade', 'Heart', 'Diamond' ]
COLORS = [ 'black', 'red' ]
ACE=0
JACK=10
QUEEN=11
KING=12
class Struct:
 def __init__(self,**kw):
  self.__dict__.update(kw)
 def __setattr__(self,name,value):
  if not self.__dict__.has_key(name):
   raise AttributeError,name
  self.__dict__[name]=value
def static(f,*args,**kw):
 if args:
  a=tuple([f.im_class()]+list(args))
 else:
  a=(f.im_class(),)
 return apply(f,a,kw)
def destruct(obj):
 if obj:
  for k in obj.__dict__.keys():
   obj.__dict__[k]=None
def pickle(obj,filename,binmode=0):
 f=None
 try:
  f = open(filename, 'wb')
  p=Pickler(f,binmode)
  p.dump(obj)
  f.close(); f=None
 finally:
  if f:f.close()
def unpickle(filename):
 f=obj=None
 try:
  f = open(filename, 'rb')
  p=Unpickler(f)
  x=p.load()
  f.close(); f=None
  obj=x
 finally:
  if f:f.close()
 return obj
class DataLoader:
 def __init__(self,argv0,filename):
  self.dir=None
  path=[]
  head,tail=os.path.split(argv0)
  if head:
   path.append(os.path.join(head, 'data'))
  path.append(os.path.join(os.curdir, 'data'))
  if os.name == 'posix':
    path.append('/usr/lib/games/pysol')
# Debian doesn't need any of these
#   path.append('/usr/local/share/pysol')
#   path.append('/usr/local/pysol')
#   path.append('/usr/share/pysol')
#   path.append('/usr/games/share/pysol')
#   path.append('/usr/games/lib/pysol')
#   path.append('/usr/games/pysol')
  self.path=[]
  for p in path:
   try:
    np=os.path.normpath(p)
    if os.path.isdir(np):
     self.path.append(np)
   except:
    pass
  for p in self.path:
   try:
    f=os.path.join(p,filename)
    if os.path.isfile(f):
     self.dir=p
     break
   except:
    pass
  else:
   raise os.error, "DataLoader could not find " + filename
 def findFile(self,filename):
  f=os.path.join(self.dir,filename)
  if os.path.isfile(f):
   return f
  raise os.error, "DataLoader could not find " + filename
 def findCardImage(self,dir,filename):
  return self.findFile(os.path.join(dir,filename))
class Images:
 def __init__(self,dataloader,dir):
  self.d=dataloader
  self.dir=dir
  self.CARDW=-1
  self.CARDH=-1
  self.__cards=[]
 def __loadCard(self,filename):
  f=self.d.findCardImage(self.dir,filename)
  img=Tkinter.PhotoImage(file=f)
  w,h=img.width(),img.height()
  if self.CARDW<0:
   self.CARDW,self.CARDH=w,h
  elif w!=self.CARDW or h!=self.CARDH:
   raise Exception, "Invalid size %dx%d of image %s" % (w, h, f)
  self.__cards.append(img)
 def load(self):
  self.__cards=[]
  for value in range(13):
   for suit in 'cshd':
    self.__loadCard("%02d%s.gif" % (value + 1, suit))
  self.__loadCard('back1.gif')
  self.__loadCard('back2.gif')
  self.__loadCard('bottom1.gif')
  self.__loadCard('bottom2.gif')
 def getFace(self,suit,value):
  assert 0<=suit<=3
  assert ACE<=value<=KING
  index=value*4+suit
  assert 0<=index<=51
  return self.__cards[index]
 def getBack(self,set):
  return self.__cards[52]
 def getSuitBottom(self):
  return self.__cards[54]
 def getTalonBottom(self):
  return self.__cards[55]
class Random(whrandom.whrandom):
 def getSeed(self):
  return self._seed
 def setSeed(self,seed):
  self._seed=seed
class Timer:
 def __init__(self, msg = ""):
  self.msg=msg
  self.t=time.clock()
 def reset(self):
  self.t=time.clock()
 def __repr__(self):
  return "%s%6.3f seconds" % (self.msg, time.clock() - self.t)
total=Struct(
 cards=0,maxcards=0,
 stacks=0,maxstacks=0,
 games=0,maxgames=0
)
class Group(Canvas.Group):
 def bind(self,sequence=None,command=None):
  return self.canvas.tag_bind(self.id,sequence,command)
def makeToplevel(master,title=None,class_=None):
 if class_:
  widget=Tkinter.Toplevel(master,class_=class_)
 else:
  widget=Tkinter.Toplevel(master)
 if title:
  widget.title(title)
  widget.iconname(title)
 return widget
def setTransient(widget,master,relx=0.5,rely=0.3,expose=1):
 widget.withdraw()
 widget.transient(master)
 if os.name != 'nt':
  widget.update_idletasks()
 if master.winfo_ismapped():
  m_width=master.winfo_width()
  m_height=master.winfo_height()
  m_x=master.winfo_rootx()
  m_y=master.winfo_rooty()
 else:
  m_width=master.winfo_screenwidth()
  m_height=master.winfo_screenheight()
  m_x=m_y=0
 w_width=widget.winfo_reqwidth()
 w_height=widget.winfo_reqheight()
 x=m_x+(m_width-w_width)*relx
 y=m_y+(m_height-w_height)*rely
 widget.geometry("+%d+%d" % (x, abs(y)))
 if expose:
  widget.deiconify()
 return widget
class ToplevelDialog:
 def __init__(self, master, title=''):
  self.master=master
  self.root=makeToplevel(master,title=title)
  self.status=0
  self.timer_id=None
  try:
   self.old_focus=self.root.focus_get()
  except:
   self.old_focus=None
 def prepare(self):
  self.root.bind('<Destroy>', self.mDestroy)
  setTransient(self.root,self.master)
  try:
   self.root.grab_set()
  except Tkinter.TclError:
   print "*** Grab failed ***"
   pass
 def mainloop(self):
  try:self.root.mainloop()
  except SystemExit:pass
  try:
   if self.timer_id:
    self.root.after_cancel(self.timer_id)
  except:pass
  try: self.root.bind('<Destroy>', '')
  except:pass
  try:self.root.withdraw()
  except:pass
  try:self.root.destroy()
  except:pass
  try:
   if self.old_focus:
    self.old_focus.focus_set()
  except:pass
  try:
   if self.master:
    self.master.update_idletasks()
  except:pass
 def mDefault(self,*event):
  pass
 def mDestroy(self,*event):
  self.status=1
  try: self.root.bind('<Destroy>', '')
  except:pass
  raise SystemExit
 def mCancel(self,*event):
  self.status=1
  raise SystemExit
 def mTimeout(self,*event):
  self.status=2
  raise SystemExit
class Dialog(ToplevelDialog):
 def __init__(self, master, title='', text='', bitmap='',
     default=-1,strings=[],
     justify='center', width=0, timeout=0,
     separatorwidth=0,
     font=None,buttonfont=None,
     padx='20', pady='20'):
  ToplevelDialog.__init__(self,master,title)
  self.num=default
  top=Tkinter.Frame(self.root)
  top.pack(side=Tkinter.TOP,fill=Tkinter.BOTH,expand=1)
  if separatorwidth>0:
   separator = Tkinter.Frame(self.root, relief='sunken',
     height=separatorwidth,width=separatorwidth,
     borderwidth=separatorwidth/2)
   separator.pack(side=Tkinter.TOP,fill=Tkinter.X)
  bot=Tkinter.Frame(self.root)
  bot.pack(side=Tkinter.BOTTOM,fill=Tkinter.BOTH)
  if font is None:
   font = ('Helvetica','14')
   if os.name == 'nt':
    font = ('Times','14')
  if buttonfont is None:
   buttonfont=font
  msg=Tkinter.Label(top,text=text,justify=justify,width=width,font=font)
  msg.pack(side=Tkinter.RIGHT,fill=Tkinter.BOTH,expand=1,
     padx=padx,pady=pady)
  if bitmap:
   b=Tkinter.Label(top,bitmap=bitmap)
   b.pack(side=Tkinter.LEFT,padx=padx,pady=pady)
  num=0
  focus=None
  for s in strings:
   b=Tkinter.Button(bot,text=s,font=buttonfont,
          command=(lambda self=self,num=num:self.mDone(num)))
   if num==default:
    b.config(default='active')
    focus=b
   else:
    b.config(default='normal')
   b.grid_configure(column=num, row = 0, sticky='ew', padx=10, pady=10)
   b.grid_columnconfigure(num)
   num=num+1
  if 0<=default<len(strings):
   self.root.bind('<Return>', self.mDefault)
   self.root.bind('<KP_Enter>', self.mDefault)
  self.root.bind('<Escape>', self.mCancel)
  self.prepare()
  if focus:
   focus.focus()
  if timeout>0:
   self.timer_id=self.root.after(timeout,self.mTimeout)
  self.mainloop()
 def mDefault(self,*event):
  raise SystemExit
 def mDone(self,num):
  self.num=num
  raise SystemExit
class Slider(ToplevelDialog):
 def __init__(self,master,title,label,value,from_,to,resolution,**kw):
  ToplevelDialog.__init__(self,master,title)
  frame=Tkinter.Frame(self.root)
  frame.pack(side=Tkinter.TOP,fill=Tkinter.BOTH,expand=1)
  self.var=Tkinter.DoubleVar()
  self.var.set(value)
  self.value=value
  slider=Tkinter.Scale(frame,from_=from_,to=to,
          resolution=resolution,
          orient=Tkinter.HORIZONTAL,
          length="3i", label=label,
          variable=self.var)
  ok = Tkinter.Button(frame, text='Ok', foreground='black',
       command=self.mDone)
  ok.config(default='active')
  cancel = Tkinter.Button(frame, text='Cancel', foreground='black',
       command=self.mCancel)
  slider.pack(side=Tkinter.TOP,padx=20,pady=15)
  ok.pack(side=Tkinter.LEFT,padx=20,pady=10)
  cancel.pack(side=Tkinter.RIGHT,padx=20,pady=10)
  self.root.bind('<Return>', self.mDone)
  self.root.bind('<KP_Enter>', self.mDone)
  self.root.bind('<Escape>', self.mCancel)
  self.prepare()
  ok.focus()
  self.mainloop()
 def mDone(self,*event):
  self.value=self.var.get()
  raise SystemExit
class tkScrolledText(Tkinter.Text):
 def __init__(self,master=None,**cnf):
  fcnf={}
  for k in cnf.keys():
   if type(k) == types.ClassType or k == 'name':
    fcnf[k]=cnf[k]
    del cnf[k]
  self.frame=apply(Tkinter.Frame,(master,),fcnf)
  self.vbar = Tkinter.Scrollbar(self.frame, name='vbar')
  self.vbar.pack(side=Tkinter.RIGHT,fill=Tkinter.Y)
  cnf['name'] = 'text'
  apply(Tkinter.Text.__init__,(self,self.frame),cnf)
  self.pack(side=Tkinter.LEFT,fill=Tkinter.BOTH,expand=1)
  self['yscrollcommand'] = self.vbar.set
  self.vbar['command'] = self.yview
  for m in Tkinter.Pack.__dict__.keys():
   if m[0] != '_' and m != 'config' and m != 'configure':
    setattr(self,m,getattr(self.frame,m))
  self.vbar.focus()
class tkHTMLWriter(formatter.DumbWriter):
 fonts  = { 'h1'   : 'Times 24 bold',
      'h2'   : 'Times 20 bold',
      'h3'   : 'Times 18 bold',
      'h4'   : 'Times 14 bold',
      'h5'   : 'Times 12 bold',
      'h6'   : 'Times 10 bold',
      'pre'  : 'Courier 12',
      'ul'   : 'Helvetica 16',         ## doesn't work yet...
     }
 def __init__(self,text,viewer):
  formatter.DumbWriter.__init__(self,self,maxcol=9999)
  self.text=text
  self.viewer=viewer
  self.text.config(cursor=self.viewer.defcursor)
  for f in self.fonts.keys():
   self.text.tag_config(f,font=self.fonts[f])
  self.anchor=None
  self.anchor_mark=None
  self.font=None
  self.font_mark=None
  self.indent = ""
 def createCB(self,href):
  class Functor:
   def __init__(self,func,arg):
    self.func=func
    self.arg=arg
   def __call__(self,*args):
    return self.func(self.arg)
  return Functor(self.viewer.display,href)
 def write(self,data):
  self.text.insert("insert", data)
 def anchor_bgn(self,href,name,type):
  if href:
   self.text.update_idletasks()
   self.anchor=(href,name,type)
   self.anchor_mark = self.text.index("insert")
 def anchor_end(self):
  if self.anchor:
   url=self.anchor[0]
   tag = "href_" + url
   self.text.tag_add(tag, self.anchor_mark, "insert")
   self.text.tag_bind(tag, "<ButtonPress>", self.createCB(url))
   self.text.tag_bind(tag, "<Enter>", self.anchor_enter)
   self.text.tag_bind(tag, "<Leave>", self.anchor_leave)
   self.text.tag_config(tag, foreground="blue", underline=1)
   self.anchor=None
 def anchor_enter(self,*args):
  self.text.config(cursor=self.viewer.handcursor)
 def anchor_leave(self,*args):
  self.text.config(cursor=self.viewer.defcursor)
 def new_font(self,font):
  if self.font:
   self.text.tag_add(self.font, self.font_mark, "insert")
   self.font=None
  if font:
   self.font_mark = self.text.index("insert")
   if self.fonts.has_key(font[0]):
    self.font=font[0]
   elif font[3]:
    self.font = 'pre'
   else:
    self.font=None
 def new_margin(self,margin,level):
  self.indent = '    ' * level
 def send_label_data(self,data):
  self.write(self.indent + data + ' ')
 def send_paragraph(self,blankline):
  if blankline>0:
   self.write('\n'*blankline)
  else:
   self.write('\n' + '\n'*blankline)
  self.col=0
  self.atbreak=0
 def send_hor_rule(self,*args,**kw):
  self.write('-'*50)
  self.write('\n')
  self.col=0
  self.atbreak=0
class tkHTMLParser(htmllib.HTMLParser):
 def anchor_bgn(self,href,name,type):
  htmllib.HTMLParser.anchor_bgn(self,href,name,type)
  self.formatter.writer.anchor_bgn(href,name,type)
 def anchor_end(self):
  if self.anchor:
   self.anchor=None
  self.formatter.writer.anchor_end()
class tkHTMLViewer:
 def __init__(self,master):
  self.master=master
  self.home=None
  self.url=None
  self.history=Struct(
   list=[],
   index=0,
  )
  self.defcursor = master['cursor']
  self.handcursor = 'hand2'
  frame=Tkinter.Frame(master)
  frame.pack(side='bottom', fill='x')
  self.homeButton = Tkinter.Button(frame, text='Index',
           command=self.goHome)
  self.homeButton.pack(side='left')
  self.backButton = Tkinter.Button(frame, text='Back',
           command=self.goBack)
  self.backButton.pack(side='left')
  self.forwardButton = Tkinter.Button(frame, text='Forward',
           command=self.goForward)
  self.forwardButton.pack(side='left')
  self.closeButton = Tkinter.Button(frame, text="Close",
            command=self.master.destroy)
  self.closeButton.pack(side='right')
  self.text = tkScrolledText(master, background='#f0f0ff',
           cursor=self.defcursor,
           font='Times 14', wrap='word',
           padx='20', pady='20')
  self.text.pack(side='top', fill='both', expand=1)
  self.text.config(state="disabled")
 def display(self,url,add=1):
  if self.__dict__.get('main'):
   if self.main and self.main.game:
    self.main.game._cancelDrag()
  for p in ['ftp:', 'http:', 'mailto:', 'news:']:
   if string.find(url,p)!=-1:
    self.errorDialog(p + " is not supported yet.\n\n" + url)
    return
  filename=url
  if not os.path.isabs(filename) and self.url:
   h1,t1=os.path.split(filename)
   h2,t2=os.path.split(self.url)
   if cmp(h1,h2)!=0:
    filename=os.path.join(h2,h1,t1)
   filename=os.path.normpath(filename)
  try:
   if os.path.isdir(filename):
    filename = os.path.join(filename, "index.html")
   filename=os.path.normpath(filename)
   file=open(filename)
   data=file.read()
   file.close()
  except IOError:
   self.errorDialog("Unable to service request:\n" + filename)
   return
  self.url=filename
  if not self.home:
   self.home=self.url
  if add:
   self.addHistory(self.url)
  if self.history.index>1:
   self.backButton.config(state="normal")
  else:
   self.backButton.config(state="disabled")
  if self.history.index<len(self.history.list):
   self.forwardButton.config(state="normal")
  else:
   self.forwardButton.config(state="disabled")
  old_c1,old_c2=self.defcursor,self.handcursor
  self.defcursor = self.handcursor = 'watch'
  self.text.config(cursor=self.defcursor)
  self.text.config(state="normal")
  self.text.update_idletasks()
  self.text.delete('1.0', 'end')
  writer=tkHTMLWriter(self.text,self)
  fmt=formatter.AbstractFormatter(writer)
  parser=tkHTMLParser(fmt)
  parser.feed(data)
  parser.close()
  self.text.config(state="disabled")
  self.master.title(parser.title)
  self.master.iconname(parser.title)
  self.defcursor,self.handcursor=old_c1,old_c2
  self.text.config(cursor=self.defcursor)
 def addHistory(self,url):
  if self.history.index>0:
   u=self.history.list[self.history.index-1]
   if cmp(u,url)==0:
    return
  del self.history.list[self.history.index:]
  self.history.list.append(url)
  self.history.index=self.history.index+1
 def goBack(self):
  if self.history.index>1:
   self.history.index=self.history.index-1
   url=self.history.list[self.history.index-1]
   self.display(url,add=0)
 def goForward(self):
  if self.history.index<len(self.history.list):
   url=self.history.list[self.history.index]
   self.history.index=self.history.index+1
   self.display(url,add=0)
 def goHome(self):
  self.display(self.home)
 def errorDialog(self,msg):
  d = Dialog(self.master, title = "tkHTML Problem",
       text = msg, bitmap = 'warning',
       default = 0, strings = ("Ok",))
def tkhtml_main(args):
 try:
  url=args[1]
 except:
  url = '../data/html/index.html'
 top=Tkinter.Tk()
 top.minsize(400,200)
 viewer=tkHTMLViewer(top)
 viewer.display(url)
 top.mainloop()
 return 0
def helpAbout(top,timeout=0):
 d = Dialog(top, title="About PySol", timeout=timeout,
      text="PySol\nA Python Solitaire Game\nVersion " + VERSION +"\n\n" +
     "Copyright (C) 1998 Markus F.X.J. Oberhumer\n" +
     "PySol is distributed under the terms of the\n" +
     "GNU General Public License\n\n" +
     "For more information about this application visit:\n" +
     "http://wildsau.idv.uni-linz.ac.at/mfx/pysol.html",
      bitmap="info", default=0, strings=("Nice","Credits..."),
      separatorwidth=2)
 if d.status==0 and d.num==1:
  helpCredits(top)
 return d.status
def helpCredits(top):
 d = Dialog(top, title="PySol Credits",
      text="PySol credits go to:\n\n" +
     "Volker Weidner for getting me into Solitaire\n" +
     "Guido van Rossum for the initial example program\n" +
     "Zachary Roadhouse for the basis of the HTML widget\n" +
     "John Fitzgibbon for providing the card images\n" +
     "Charles B. Dorsett for help with the documentation\n" +
     "\n" +
     "The Linux, KDE, Python & Tcl/Tk crews\nfor making this program possible",
      bitmap="info", default=0, strings=("Ok",),
      separatorwidth=2)
 return d.status
html_index=None
def helpHTML(main,document):
 try:
  document = os.path.join('html', document)
  doc=main.dataloader.findFile(document)
  global html_index
  if html_index is None:
   document = os.path.join('html', 'index.html')
   html_index=main.dataloader.findFile(document)
 except:
  d = Dialog(main.top,title="HTML Error",
       text="Cannot find help document " + document,
       bitmap="error",default=0,strings=("Ok",))
  return 0
 top=makeToplevel(main.top)
 top.title("PySol Help")
 top.iconname("PySol Help")
 top.minsize(400,200)
 viewer=tkHTMLViewer(top)
 viewer.main=main
 viewer.home=html_index
 viewer.display(doc)
 return 1
class AtomicMove:
 pass
class AMoveMove(AtomicMove):
 def __init__(self,ncards,from_stack,to_stack,frames):
  self.ncards=ncards
  self.from_stack_id=from_stack.id
  self.to_stack_id=to_stack.id
  self.frames=frames
 def redo(self,game):
  game.doMoveMove(self.ncards,game.allstacks[self.from_stack_id],
      game.allstacks[self.to_stack_id],self.frames)
 def undo(self,game):
  game.doMoveMove(self.ncards,game.allstacks[self.to_stack_id],
      game.allstacks[self.from_stack_id],self.frames)
class AFlipMove(AtomicMove):
 def __init__(self,stack):
  self.stack_id=stack.id
 def redo(self,game):
  game.doFlipMove(game.allstacks[self.stack_id])
 def undo(self,game):
  game.doFlipMove(game.allstacks[self.stack_id])
class Card:
 def __init__(self,id,set,suit,value,game):
  total.cards=total.cards+1
  total.maxcards=max(total.cards,total.maxcards)
  self.id=id
  self.set=set
  self.suit=suit
  self.color=suit/2
  self.value=value
  self.x=self.y=0
  self.group=Group(game.canvas)
  images=game.main.images
  self.face = ImageItem(game.canvas, 0, 0, image=images.getFace(suit,value), anchor='nw')
  self.back = ImageItem(game.canvas, 0, 0, image=images.getBack(set), anchor='nw')
  self.group.addtag_withtag(self.face)
  self.group.addtag_withtag(self.back)
  self.face_up=0
 def __del__(self):
  total.cards=total.cards-1
 def __repr__(self):
  return "Card(%s, %s)" % (`self.suit`, `self.value`)
 def moveTo(self,x,y):
  self.moveBy(x-self.x,y-self.y)
 def moveBy(self,dx,dy):
  self.x=self.x+dx
  self.y=self.y+dy
  self.group.move(dx,dy)
 def tkraise(self):
  self.group.tkraise()
 def showFace(self):
  if not self.face_up:
   self.face.tkraise()
   self.group.tkraise()
   self.face_up=1
 def showBack(self):
  if self.face_up:
   self.back.tkraise()
   self.group.tkraise()
   self.face_up=0
def SlowStack_addTag(self,card):
 self.group.addtag_withtag(card.group)
def SlowStack_removeTag(self,card):
 card.group.dtag(self.group)
def FastStack_addTag(self,card):
 self.group.addtag_withtag(card.group)
def FastStack_removeTag(self,card):
 card.group.dtag(self.group)
class Stack:
 def __init__(self,x,y,game):
  total.stacks=total.stacks+1
  total.maxstacks=max(total.stacks,total.maxstacks)
  self.addTag=FastStack_addTag
  self.removeTag=FastStack_removeTag
  self.game=game
  self.id=len(self.game.allstacks)
  self.game.allstacks.append(self)
  self.FACE_UP_OFFSET=0
  self.FACE_DOWN_OFFSET=0
  self.x=x
  self.y=y
  self.cards=[]
  self.dragcards=None
  self.group=Group(self.game.canvas)
  self.group.bind('<1>', self.__clickEventHandler)
  self.group.bind('<2>', self.__middleclickEventHandler)
  self.group.bind('<3>', self.__rightclickEventHandler)
  self.group.bind('<Double-1>', self.__doubleclickEventHandler)
  self.group.bind('<B1-Motion>', self.__motionEventHandler)
  self.group.bind('<ButtonRelease-1>', self.__releaseEventHandler)
  self.bottom=self.getBottomFace()
  if self.bottom:
   self.group.addtag_withtag(self.bottom)
  self.text=None
  self.text_format=None
 def __del__(self):
  total.stacks=total.stacks-1
 def __repr__(self):
  return "%s(%d)" % (self.__class__.__name__, self.id)
 def getPositionFor(self,card):
  return (self.x,self.y)
 def getBottomFace(self):
  return None
 def _getBottomRectangle(self):
  images=self.game.main.images
  return Rectangle(self.game.canvas,self.x,self.y,
       self.x+images.CARDW-1,self.y+images.CARDH-1,
       outline='black', fill=self.game.main.opt.tablecolor)
 def _getBottomImage(self,img):
  if not img:return None
  return ImageItem(self.game.canvas,self.x,self.y,
       image=img, anchor='nw')
 def add(self,card):
  self.cards.append(card)
  card.tkraise()
  self.position(card)
  self.addTag(self,card)
  if self.text:
   self.text['text'] = self.text_format % len(self.cards)
 def delete(self,card):
  self.cards.remove(card)
  self.removeTag(self,card)
  if self.text:
   self.text['text'] = self.text_format % len(self.cards)
 def getCard(self):
  if not self.cards:
   return None
  return self.cards[-1]
 def position(self,card):
  x,y=self.getPositionFor(card)
  card.moveTo(x,y)
 def acceptsCards(self,cards,stackcards=None):
  return 0
 def prefersCards(self,cards):
  return 0
 def canFlipCard(self):
  return 0
 def canDropCard(self,stacks,card=None):
  return None
 def clickHandler(self):
  pass
 def middleclickHandler(self):
  pass
 def rightclickHandler(self):
  pass
 def doubleclickHandler(self):
  self.clickHandler()
 def releaseHandler(self,cards):
  for card in cards:
   self.position(card)
 def __clickEventHandler(self,event):
  self.game.demo=None
  if self.game.dragstack:
   self.game.dragstack.cancelDrag()
  self.clickHandler()
  self.startDrag(event)
 def __middleclickEventHandler(self,event):
  self.game.demo=None
  if self.game.dragstack:
   self.game.dragstack.cancelDrag()
  self.middleclickHandler()
 def __rightclickEventHandler(self,event):
  self.game.demo=None
  if self.game.dragstack:
   self.game.dragstack.cancelDrag()
  self.rightclickHandler()
 def __doubleclickEventHandler(self,event):
  self.game.demo=None
  if self.game.dragstack:
   self.game.dragstack.cancelDrag()
  self.doubleclickHandler()
  self.startDrag(event)
 def __motionEventHandler(self,event):
  self.game.demo=None
  self.keepDrag(event)
 def __releaseEventHandler(self,event):
  self.game.demo=None
  self.keepDrag(event)
  self.finishDrag()
 def canDrag(self,i,l=-1):
  if (l<0):l=len(self.cards)
  if (i>=l):return 0
  return self.cardsAlternateRow(self.cards[i:l])
 def startDrag(self,event):
  self.dragcards=None
  self.game.dragstack=None
  tags = self.game.canvas.gettags('current')
  for i in range(len(self.cards)):
   if self.cards[i].group.tag in tags:
    break
  else:
   return
  if not self.canDrag(i):
   return
  self.dragcards=self.cards[i:]
  self.game.dragstack=self
  self.lastx=event.x
  self.lasty=event.y
  for card in self.dragcards:
   card.tkraise()
 def keepDrag(self,event):
  if not self.dragcards:
   return
  dx=event.x-self.lastx
  dy=event.y-self.lasty
  if dx or dy:
   self.lastx=event.x
   self.lasty=event.y
   for card in self.dragcards:
    card.moveBy(dx,dy)
 def finishDrag(self):
  cards=self.dragcards
  self.dragcards=None
  self.game.dragstack=None
  if cards:
   self.releaseHandler(cards)
 def cancelDrag(self):
  cards=self.dragcards
  self.dragcards=None
  self.game.dragstack=None
  for card in cards:
   self.position(card)
 def cardsFaceUp(self,cards):
  if not cards:return 0
  for c in cards:
   if not c.face_up:
    return 0
  return 1
 def cardsFaceDown(self,cards):
  if not cards:return 0
  for c in cards:
   if c.face_up:
    return 0
  return 1
 def cardsAlternateRow(self,cards):
  if not self.cardsFaceUp(cards):
   return 0
  c1=cards[0]
  for i in range(1,len(cards)):
   c2=cards[i]
   if c1.value!=c2.value+1 or c1.color==c2.color:
    return 0
   c1=c2
  return 1
 def getAlternateRow(self):
  cards=self.cards[:]
  while cards:
   if self.cardsAlternateRow(cards):
    return cards
   del cards[0]
  return None
class TalonStack(Stack):
 def clickHandler(self):
  self.game.dealCards()
 def createCards(self,sets):
  timer = Timer("Talon.createCards ")
  cards=[]
  id=0
  for set in range(sets):
   for suit in range(len(SUITS)):
    for value in range(13):
     card=Card(id,set,suit,value,self.game)
     cards.append(card)
     id=id+1
  return cards
 def removeCards(self):
  for stack in self.game.allstacks:
   while 1:
    card=stack.getCard()
    if not card:
     break
    stack.delete(card)
 def shuffle(self,seed=None):
  cards=self.game.cards[:]
  for c in cards:
   assert cards.index(c)==c.id
  random=self.game.main.gamerandom
  if seed:
   random.setSeed(seed)
  else:
   seed=random.getSeed()
  n=len(cards)-1
  while n>0:
   j=random.randint(0,n)
   swap=cards[n]
   cards[n]=cards[j]
   cards[j]=swap
   n=n-1
  for c in cards:
   self.add(c)
   c.showBack()
  return seed
 def dealRow(self,rows=None,flip=1,reverse=0,frames=-1):
  if len(self.cards)==0:
   return 0
  if not rows:
     rows=self.game.rows
  assert len(self.cards)>=len(rows)
  old_state=self.game.moves.state
  self.game.moves.state=self.game.S_DEAL
  if reverse:
   rows=rows[:]
   rows.reverse()
  for r in rows:
   assert not self.getCard().face_up
   if flip:
    self.game.flipMove(self)
   self.game.moveMove(1,self,r,frames=frames)
  self.game.moves.state=old_state
  return len(rows)
 def getBottomFace(self):
  images=self.game.main.images
  img=self._getBottomImage(images.getTalonBottom())
  if img:return img
  return self._getBottomRectangle()
class OpenStack(Stack):
 def __init__(self,x,y,game):
  Stack.__init__(self,x,y,game)
  self.addTag=SlowStack_addTag
  self.removeTag=SlowStack_removeTag
 def canFlipCard(self):
  card=self.getCard()
  if card and not card.face_up:
   return 1
  return 0
 def canDropCard(self,stacks,card=None):
  if not card:
   card=self.getCard()
  if not card:
   return None
  for s in stacks:
   if s.prefersCards([card]):
    return s
  for s in stacks:
   if s.acceptsCards([card]):
    return s
  return None
 def dropCard(self,stacks):
  stack=self.canDropCard(stacks)
  if stack:
   assert stack.acceptsCards([self.getCard()])
   self.game.moveMove(1,self,stack,frames=-1)
   self.game.checkForWin()
  return stack
 def clickHandler(self):
  if self.canFlipCard():
   self.game.flipMove(self)
   self.game.finishMove()
 def rightclickHandler(self):
  if self.dropCard(self.game.suits):
   self.game.finishMove()
  elif self.canFlipCard():
   self.game.flipMove(self)
   self.game.finishMove()
 def doubleclickHandler(self):
  self.rightclickHandler()
 def releaseHandler(self,cards):
  card=cards[0]
  stack=self.game.getClosestStack(card)
  if not stack or stack is self or not stack.acceptsCards(cards):
   Stack.releaseHandler(self,cards)
  else:
   self.game.moveMove(len(cards),self,stack,frames=0)
   self.game.finishMove()
class SuitStack(OpenStack):
 def __init__(self,x,y,game,preferred_suit=None,allowed_suit=None):
  OpenStack.__init__(self,x,y,game)
  self.preferred_suit=preferred_suit
  self.allowed_suit=allowed_suit
 def canFlipCard(self):
  return 0
 def canDropCard(self,stacks,card=None):
  return None
 def acceptsCards(self,cards,stackcards=None):
  if stackcards is None:stackcards=self.cards
  if len(cards)!=1:
   return 0
  card=cards[0]
  if not card or not card.face_up:
   return 0
  if self.allowed_suit and self.allowed_suit!=card.suit:
   return 0
  if not stackcards:
   return card.value==ACE
  topcard=stackcards[-1]
  if not topcard.face_up:
   return 0
  return card.suit==topcard.suit and card.value==topcard.value+1
 def prefersCards(self,cards):
  if not self.preferred_suit:
   return 0
  if not self.acceptsCards(cards):
   return 0
  return cards[0].suit==self.preferred_suit
 def clickHandler(self):
  pass
 def rightclickHandler(self):
  pass
 def getBottomFace(self):
  images=self.game.main.images
  img=self._getBottomImage(images.getSuitBottom())
  if img:return img
  return self._getBottomRectangle()
class RowStack(OpenStack):
 def __init__(self,x,y,game):
  OpenStack.__init__(self,x,y,game)
  self.FACE_UP_OFFSET=self.game.FACE_UP_OFFSET
  self.FACE_DOWN_OFFSET=self.game.FACE_DOWN_OFFSET
 def acceptsCards(self,cards,stackcards=None):
  if stackcards is None:stackcards=self.cards
  if not self.cardsAlternateRow(cards):
   return 0
  card=cards[0]
  if not card or not card.face_up:
   return 0
  if not stackcards:
   return 1
  topcard=stackcards[-1]
  if not topcard.face_up:
   return 0
  return card.color!=topcard.color and card.value==topcard.value-1
 def getPositionFor(self,card):
  y=self.y
  for c in self.cards:
   if c==card:
    break
   if c.face_up:
    y=y+self.FACE_UP_OFFSET
   else:
    y=y+self.FACE_DOWN_OFFSET
  return (self.x,y)
class StrictRowStack(RowStack):
 def acceptsCards(self,cards,stackcards=None):
  if stackcards is None:stackcards=self.cards
  if not RowStack.acceptsCards(self,cards,stackcards):
   return 0
  if not stackcards:
   return cards[0].value==KING
  return 1
class WasteStack(OpenStack):
 def getAlternateRow(self):
  c=self.getCard()
  if c:return[c]
  return None
class Hint:
 BLACK = 'black'
 RED = 'red'
 BLUE = 'blue'
 def __init__(self,game):
  self.game=game
 def getHints(self,level,taken_hint=None):
  return[]
 def getDropStack(self,from_stacks,to_stacks):
  stacks=[]
  for r in from_stacks:
   if r.canDropCard(to_stacks):
    stacks.append((r,r.getCard().value))
  if not stacks:
   return None
  stacks.sort(lambda a,b:cmp(a[1],b[1]))
  return stacks[0][0]
 def _canDropPile(self,stacks,pile):
  cards=pile[:]
  while cards:
   card=cards[-1]
   for s in stacks:
    if s.acceptsCards([card]):
     break
   else:
    return 0
   del cards[-1]
  return 1
 def _sortHints(self,hints,level):
  i=1000
  for h in hints:
   if level==0:
    h[3]=(h[3]/10000)*10000
    h[4]=self.BLACK
   i=i-1
   h[3]=h[3]*1000+i
  hints.sort(lambda a,b:cmp(b[3],a[3]))
  for h in hints:
   h[3]=h[3]/1000
class Gypsy_Hint(Hint):
 def getHints(self,level,taken_hint=None):
  game=self.game
  K=KING+1
  hints=[]
  if taken_hint and taken_hint[5]:
   hints.append(taken_hint[5])
   self._sortHints(hints,level)
   return hints
  if level>=2:
   for r in game.allstacks:
    if r.canFlipCard():
     hints.append([1,r,r,100000,self.BLACK,None])
  for r in game.dropstacks:
   pile=r.getAlternateRow()
   if not pile:
    continue
   lp=len(pile)
   lr=len(r.cards)
   assert 1<=lp<=lr
   t=r.canDropCard(game.suits)
   if t:
    color=self.BLACK
    c=r.getCard()
    assert t!=r and c
    done=0
    if c.value<=1:
     score=92000
     done=1
    elif r in game.talonstacks:
     score=20000
    elif lp==1:
     score=91000
     done=1
    elif self._canDropPile(game.suits,r.cards):
     score=90000
     done=1
     color=self.RED
    else:
     score=50000
    score=score+(K-c.value)
    hints.append([1,r,t,score,color,None])
    if done:
     continue
   rpile=r.cards[:(lr-lp)]
   cb=None
   if lp<lr:
      cb=rpile[-1]
   empty_row_seen=0
   for t in game.rows:
    if t==r or not t.acceptsCards(pile):
     continue
    score=0
    color=self.BLACK
    if r in game.talonstacks:
     score=30000
     hints.append([lp,r,t,score,color,None])
     continue
    lt=len(t.cards)
    if lt==0:
     if lp==lr:
      continue
     if empty_row_seen:
      continue
     score=60000
     empty_row_seen=1
    else:
     score=80000
    if not cb:
     score=score+9000
    elif not cb.face_up:
     score=score+3000+(500-(lr-lp))
    else:
     if r.cardsAlternateRow(rpile):
      for x in game.rows:
       if len(x.cards)==0:
        continue
       if x.acceptsCards(rpile):
        score=score+5000
        color=self.BLUE
        break
       elif self._canDropPile(game.suits,rpile):
        score=score+4000
        color=self.RED
        break
     if r.canDropCard(game.suits,card=cb):
      score=score+200+(K-cb.value)
     else:
      score=score+100+(K-cb.value)
    hints.append([lp,r,t,score,color,None])
  if len(hints)==0 and level>=1:
   for r in game.rows:
    pile=r.getAlternateRow()
    if not pile:
     continue
    drop_cards=[]
    for c in pile:
     stack=r.canDropCard(game.suits,card=c)
     if stack and stack!=r:
      drop_cards.append((c,stack))
    for cc in drop_cards:
     c=cc[0]
     sub_pile=pile[pile.index(c)+1:]
     assert r.cardsAlternateRow(sub_pile)
     for t in game.rows:
      if t==r or not t.acceptsCards(sub_pile):
       continue
      score=40000
      score=score+1000+(K-r.getCard().value)
      force=[1,r,cc[1],999999,self.BLUE,None]
      hints.append([len(sub_pile),r,t,score,self.RED,force])
  if len(hints)==0 and level>=1:
   for s in game.suits:
    card=s.getCard()
    if not card or not card.face_up:
     continue
    for t in game.rows:
     if t==s or not t.acceptsCards([card]):
      continue
     for r in game.dropstacks:
      if r==t:
       continue
      pile=r.getAlternateRow()
      if not pile:
       continue
      if not t.acceptsCards(pile,stackcards=t.cards+[card]):
       continue
      rpile=r.cards[:(len(r.cards)-len(pile))]
      if r.acceptsCards(pile,stackcards=rpile):
       continue
      score=15000+card.value
      force=[len(pile),r,t,999999,self.BLUE,None]
      hints.append([1,s,t,score,self.BLUE,force])
  if level>=2:
   for r in game.talonstacks:
    if r.cards>0:
     hints.append([0,r,None,10000,self.BLACK,None])
     break
  self._sortHints(hints,level)
  return hints
class Klondike_Hint(Gypsy_Hint):
 pass
class Game:
 def __init__(self,id):
  total.games=total.games+1
  total.maxgames=max(total.games,total.maxgames)
  assert id>0
  self.id=id
  self.version=VERSION
  self.allstacks=[]
  self.talon=self.waste=None
  self.loadstacks=None
  self.seed=None
  self.reset()
 def reset(self):
  self.filename = ''
  self.keypress=None
  self.demo=None
  self.stats=Struct(
   hints=0,
   undo=0,
   redo=0,
   player_moves=0,
   demo_moves=0,
   total_moves=0,
   loaded=0,
   saved=0,
   updated=0,
  )
  self.startMoves()
  self.hints=Struct(
   list=None,
   index=-1,
   level=-1,
  )
  self.changed_index=0
 def __del__(self):
  total.games=total.games-1
 def getName(self):
  return str(self.__class__.__name__)
 def create(self,main):
  self.main=main
  self.top=main.top
  self.canvas=main.canvas
  self.dragstack=None
  self.top_cursor = self.top.cget('cursor')
  self.canvas_cursor = self.canvas.cget('cursor')
  self.filename = ''
  timer1 = Timer("Game.create      ")
  self.top.configure(cursor='watch')
  self.canvas.configure(cursor='watch')
  self.top.title("PySol - " + self.getName());
  self.top.iconname("PySol - " + self.getName());
  self.tkopt=Struct(
   gameid=Tkinter.IntVar(),
   confirm=Tkinter.BooleanVar(),
   animations=Tkinter.BooleanVar(),
  )
  self.tkopt.gameid.set(self.id)
  self.tkopt.confirm.set(self.main.opt.confirm)
  self.tkopt.animations.set(self.main.opt.animations)
  self.FACE_UP_OFFSET=self.FACE_DOWN_OFFSET=18
  timer2 = Timer("Game.createGame  ")
  self.createGame()
  menubar,menus=self.createMenus()
  self.updateMenus()
  self.top.configure(menu=menubar)
  self.top.extra.menubar=menubar
  self.top.extra.menus=menus
  self.top.geometry('')
  self.top.configure(background=self.main.opt.tablecolor)
  self.canvas.configure(width=self.width,height=self.height,background=self.main.opt.tablecolor)
  self.canvas.pack(fill=Tkinter.BOTH,expand=Tkinter.TRUE)
  self.canvas.configure(cursor=self.canvas_cursor)
  self.top.configure(cursor=self.top_cursor)
 def createMenus(self):
  m = 'Ctrl-'
  menubar=Tkinter.Menu(self.top,tearoff=0)
  menus=[]
  menu = self.createMenu(menubar, "File", underline=0)
  menus.append(menu)
  menu.add_command(label="New game", command=self.mNewGame, underline=0, accelerator=m+'N')
  submenu=Tkinter.Menu(menu,tearoff=0)
  self._addSelectGameMenu(submenu,1,"Gypsy",0)
  self._addSelectGameMenu(submenu,3,"Irmgard",0)
  self._addSelectGameMenu(submenu,4,"8x8",0)
  self._addSelectGameMenu(submenu,2,"Klondike",0)
  menu.add_cascade(label="Select game", menu=submenu, underline=7)
  menu.add_separator()
  menu.add_command(label="Open...", command=self.mOpen, underline=0, accelerator=m+'O')
  menu.add_command(label="Save", underline=0, command=self.mSave, accelerator=m+'S')
  menu.add_command(label="Save as...", command=self.mSaveAs, underline=5)
  menu.add_separator()
  menu.add_command(label="Quit", command=self.mQuit, underline=0, accelerator=m+'Q')
  menu = self.createMenu(menubar, "Edit", underline=0)
  menus.append(menu)
  menu.add_command(label="Undo", command=self.mUndo, underline=0, accelerator='S')
  menu.add_command(label="Redo", command=self.mRedo, underline=0, accelerator='R')
  menu.add_command(label="Redo all", command=self.mRedoAll, underline=5)
  menu.add_separator()
  menu.add_command(label="Restart game", command=self.mRestart, underline=8, accelerator=m+'G')
  menu = self.createMenu(menubar, "Game", underline=0)
  menus.append(menu)
  menu.add_command(label="Deal cards", command=self.mDeal, underline=0, accelerator='D')
  menu.add_command(label="Auto drop", command=self.mDrop, underline=0, accelerator='A')
  menu.add_separator()
  menu.add_command(label="Status...", command=self.mStatus, underline=1, accelerator='T')
  menu.add_separator()
  menu.add_command(label="Hint", command=self.mHint, underline=0, accelerator='H')
  menu.add_command(label="Demo", command=self.mDemo, underline=0)
  self.initGamemenu(menu)
  menu = self.createMenu(menubar, "Options", underline=0)
  menus.append(menu)
  menu.add_checkbutton(label="Confirm", variable=self.tkopt.confirm, command=self.mOptConfirm, underline=0)
  menu.add_checkbutton(label="Animations", variable=self.tkopt.animations, command=self.mOptAnimations, underline=0)
  menu.add_command(label="Table color...", command=self.mOptTableColor, underline=0)
  menu.add_command(label="Demo speed...", command=self.mOptDemoSpeed, underline=0)
  menu.add_separator()
  menu.add_command(label="Save options", command=self.mOptSave, underline=0)
  menu = self.createMenu(menubar, "Help", underline=0)
  menus.append(menu)
  menu.add_command(label="Contents", command=self.mHelp,underline=0, accelerator='F1')
  menu.add_command(label="Rules", command=self.mHelpRules,underline=0)
  menu.add_separator()
  menu.add_command(label="About PySol...", command=self.mHelpAbout, underline=0)
  self.top.bind('<KeyPress>',self.keyPressHandler)
  ctrl = 'Control-'
  self.bindKey(ctrl,'n',self.mNewGame)
  self.bindKey(ctrl,'o',self.mOpen)
  self.bindKey(ctrl,'s',self.mSave)
  self.bindKey(ctrl,'q',self.mQuit)
  self.bindKey(None,'s',self.mUndo)
  self.bindKey(ctrl,'z',self.mUndo)
  self.bindKey(None,'r',self.mRedo)
  self.bindKey(ctrl,'g',self.mRestart)
  self.bindKey(None,'d',self.mDeal);
  self.bindKey(None,'a',self.mDrop);
  self.bindKey(None,'t',self.mStatus)
  self.bindKey(None,'h',self.mHint)
  self.bindKey(ctrl,'h',self.mHint1)
  self.top.bind('<F1>',self.mHelp)
  self.top.bind('<1>',self.clickHandler)
  self.top.bind('<2>',self.clickHandler)
  self.top.bind('<3>',self.clickHandler)
  self.initBindings()
  return (menubar,menus)
 def bindKey(self,modifier,key,func):
  if modifier: sequence = '<' + modifier + key + '>'
  else:sequence=key
  self.top.bind(sequence,func)
  key=string.upper(key)
  if modifier: sequence = '<' + modifier + key + '>'
  else:sequence=key
  self.top.bind(sequence,func)
 def createMenu(self, menubar, name, underline='', tearoff=0):
  if name == 'Help' and os.name == 'mac':
   name = 'Apple'
  w_name = str(menubar)[1:] + '.' + string.lower(name)
  menu=Tkinter.Menu(name=w_name,tearoff=tearoff)
  menubar.add_cascade(label=name,menu=menu,underline=underline)
  return menu
 def _addSelectGameMenu(self, menu, id, label, underline=''):
  menu.add_radiobutton(variable=self.tkopt.gameid,value=id,
        command=self.mSelectGame,
        label=label,underline=underline)
 def setSize(self,w,h):
  self.width,self.height=w,h
 def initGamemenu(self,menu):
  return None
 def initBindings(self):
  pass
 def updateMenus(self):
  if not self.top.extra.menus:
   return
  menu=self.top.extra.menus[1]
  if menu:
   if self.moves.index==0:
    menu.entryconfig(0,state=Tkinter.DISABLED)
    menu.entryconfig(4,state=Tkinter.DISABLED)
   else:
    menu.entryconfig(0,state=Tkinter.NORMAL)
    menu.entryconfig(4,state=Tkinter.NORMAL)
   if self.moves.index==len(self.moves.history):
    menu.entryconfig(1,state=Tkinter.DISABLED)
    menu.entryconfig(2,state=Tkinter.DISABLED)
   else:
    menu.entryconfig(1,state=Tkinter.NORMAL)
    menu.entryconfig(2,state=Tkinter.NORMAL)
  menu=self.top.extra.menus[2]
  if menu:
   state=Tkinter.DISABLED
   if (self.talon and self.talon.cards or
    self.waste and self.waste.cards):
    state=Tkinter.NORMAL
   menu.entryconfig(0,state=state)
   state=Tkinter.DISABLED
   if self.getHintClass():
    state=Tkinter.NORMAL
   menu.entryconfig(1,state=state)
   menu.entryconfig(5,state=state)
   if self.demo:
    pass
   menu.entryconfig(6,state=state)
 filetypes = (("PySol files", "*.pso"), ("All files", "*"))
 def _finishDrag(self):
  self.demo=None
  if self.dragstack:
   self.dragstack.finishDrag()
 def _cancelDrag(self):
  self.demo=None
  if self.dragstack:
   self.dragstack.cancelDrag()
 def mNewGame(self,*args):
  self._cancelDrag()
  if self.changed():
   if not self.areYouSure("New game"): return
  self.newGame()
 def mSelectGame(self,*args):
  self._cancelDrag()
  id=self.tkopt.gameid.get()
  if self.id==id:
   return
  if self.changed():
   if not self.areYouSure("Select game"):
    self.tkopt.gameid.set(self.id)
    return
  self._quitGame(id)
 def mOpen(self,*args):
  self._cancelDrag()
  filename=self.filename
  idir,ifile=os.path.split(os.path.normpath(filename))
  filename=tkFileDialog.askopenfilename(
    filetypes=self.filetypes,
    initialdir=idir,initialfile=ifile)
  if filename:
   filename=os.path.normpath(filename)
   self.loadGame(filename)
 def mSave(self,*args):
  self._cancelDrag()
  if self.filename:
   self.saveGame(self.filename)
  else:
   self.mSaveAs()
 def mSaveAs(self,*args):
  self._cancelDrag()
  filename=self.filename
  if not filename:
   filename = string.lower(self.getName() + "01")
   filename = re.sub('\W', '', filename)
   if not filename:
    filename=self.main.initialfilename
   filename = filename + ".pso"
  idir,ifile=os.path.split(os.path.normpath(filename))
  filename=tkFileDialog.asksaveasfilename(
    filetypes=self.filetypes,
    initialdir=idir,initialfile=ifile)
  if filename:
   filename=os.path.normpath(filename)
   self.saveGame(filename)
 def mQuit(self,*args):
  self._cancelDrag()
  if self.changed():
   if not self.areYouSure("Quit PySol"): return
  self._quitGame()
 def mUndo(self,*args):
  self._cancelDrag()
  self.undo()
 def mRedo(self,*args):
  self._cancelDrag()
  self.redo()
 def mRedoAll(self,*args):
  self._cancelDrag()
  while self.moves.index<len(self.moves.history):
   self.redo()
 def mRestart(self,*args):
  self._cancelDrag()
  if self.moves.index==0:
   return
  if self.changed():
   if not self.areYouSure("Restart game","Restart this game ?"): return
  self.newGame(self.seed,update_stats=0)
 def mDeal(self,*args):
  self._cancelDrag()
  self.dealCards()
 def mDrop(self,*args):
  self._cancelDrag()
  self.autoDropCards()
 def mStatus(self,*args):
  self._cancelDrag()
  w = ''
  if self.talon:
   w = w + "\nCards in Talon: " +str(len(self.talon.cards))
  if self.waste:
   w = w + "\nCards in Waste: " +str(len(self.waste.cards))
  d = Dialog(self.top,title="Game status",
       text="Moves: " + str(self.moves.index) + "\n" +
      "Hints: " + str(self.stats.hints) + "\n\n" +
      "Total player moves: " + str(self.stats.player_moves) + "\n" +
      "Total demo moves: " + str(self.stats.demo_moves) + "\n" +
      "Total moves in this game: " + str(self.stats.total_moves) + "\n" +
      w,
       bitmap='', default=0, strings=("Ok","Player...","Demo..."),
       width='30', separatorwidth=2)
  if d.status==0:
   if d.num==1:
    self.mPlayerStatus()
   elif d.num==2:
    self.mDemoStatus()
 def mPlayerStatus(self,*args):
  self._cancelDrag()
  u=self.main.user
  n=self.getName()
  won1,lost1=self.main.stats.getStats(u,0)
  won2,lost2=self.main.stats.getStats(u,self.id)
  d = Dialog(self.top,title="Player statistics",
       text="Statistics for " + u + "\n\n" +
      "Total games won: " + str(won1) + "\n" +
      "Total games lost: " + str(lost1) + "\n\n" +
      n + " games won: " + str(won2) + "\n" +
      n + " games lost: " + str(lost2),
       bitmap='', default=0, strings=("Ok","Reset"),
       width='30', separatorwidth=2)
  if d.status==0:
   if d.num==1:
    if self.areYouSure("Reset player statistics",
           "Reset statistics for " + u + " ?",
           confirm=1,default=1):
     self.main.stats.resetStats(u)
   elif d.num==2:
    if self.areYouSure("Reset player statistics",
           "Reset statistics all players ?",
           confirm=1,default=1):
     self.main.stats.resetStats()
 def mDemoStatus(self,*args):
  self._cancelDrag()
  n=self.getName()
  won1,lost1=self.main.stats.getDemoStats(0)
  won2,lost2=self.main.stats.getDemoStats(self.id)
  d = Dialog(self.top,title="PySol demo statistics",
       text="Total demos won: " + str(won1) + "\n" +
      "Total demos lost: " + str(lost1) + "\n\n" +
      n + " demos won: " + str(won2) + "\n" +
      n + " demos lost: " + str(lost2),
       bitmap='', default=0, strings=("Ok","Reset"),
       width='30', separatorwidth=2)
  if d.status==0 and d.num==1:
   if self.areYouSure("Reset demo statistics",
          "Reset demo statistics ?",
          confirm=1,default=1):
    self.main.stats.resetDemoStats()
 def mHint(self,*args):
  self._cancelDrag()
  if self.showHint(0,self.main.opt.hint_sleep):
   self.stats.hints=self.stats.hints+1
 def mHint1(self,*args):
  self._cancelDrag()
  if self.showHint(1,self.main.opt.hint_sleep):
   self.stats.hints=self.stats.hints+1
 def mDemo(self,*args):
  self._cancelDrag()
  if self.changed() and self.stats.demo_moves==0:
   if not self.areYouSure("Play demo"): return
  self.playDemo()
 def mOptConfirm(self,*args):
  self._cancelDrag()
  self.main.opt.confirm=self.tkopt.confirm.get()
 def mOptAnimations(self,*args):
  self._cancelDrag()
  self.main.opt.animations=self.tkopt.animations.get()
 def mOptDemoSpeed(self,*args):
  self._cancelDrag()
  d = Slider(self.top, "Set demo speed", "Set delay in seconds",
       self.main.opt.demo_sleep,0.2,10.0,0.1)
  if d.status==0:
   self.main.opt.demo_sleep=d.value
 def mOptTableColor(self,*args):
  self._cancelDrag()
  c = tkColorChooser.askcolor(initialcolor=self.main.opt.tablecolor,title="Select table color")
  if c and c[1]:
   self.main.opt.tablecolor=c[1]
   self.top.configure(background=self.main.opt.tablecolor)
   self.canvas.configure(background=self.main.opt.tablecolor)
   for stack in self.allstacks:
    if stack.bottom:
     try:
      stack.bottom.configure(fill=self.main.opt.tablecolor)
     except:
      pass
 def mOptSave(self,*args):
  self._cancelDrag()
  try:
   self.main.saveOptions()
  except Exception,ex:
   d = Dialog(self.top,title="Error",text="Error while saving options\n"+str(ex),
        bitmap="error", default=0, strings=("Ok",))
 def mHelp(self,*args):
  self._cancelDrag()
  helpHTML(self.main, "index.html")
 def mHelpRules(self,*args):
  self._cancelDrag()
  n=string.lower(self.getName())
  n = re.sub('\s', '', n)
  helpHTML(self.main, "rules_" + n + ".html")
 def mHelpAbout(self,*args):
  self._cancelDrag()
  helpAbout(self.top)
 def keyPressHandler(self,*args):
  try:
   if args and args[0]:
    self.keypress=args[0]
    if self.demo and self.keypress.char:
     self.demo.keypress=args
  except:
   pass
 def clickHandler(self,*args):
  if self.demo:
   self.demo=None
   self.updateMenus()
 def _quitGame(self,restartid=0,startdemo=0,loadedgame=None):
  self.updateStats()
  self.main.nextgame.id=restartid
  self.main.nextgame.startdemo=startdemo
  self.main.nextgame.loadedgame=loadedgame
  self.top.after_idle(self.top.quit)
 def areYouSure(self,title=None,text=None,confirm=-1,default=0):
  if confirm<0:
   confirm=self.main.opt.confirm
  if confirm:
   if not title: title = "PySol"
   if not text: text = "Discard current game ?"
   d=Dialog(self.top,title=title,text=text,
        bitmap='questhead',
        default=default,strings=("Ok","Cancel"))
   if d.status!=0 or d.num!=0:
    return 0
  return 1
 def notYetImplemented(self):
  d = Dialog(self.top,title="Not yet implemented",
       text="This function is\nnot yet implemented.",
       bitmap='error',
       default=0,strings=("Ok",))
 def animatedMoveTo(self,card,x,y,tkraise=1,frames=-1):
  if not self.main.opt.animations:return
  if tkraise:card.tkraise()
  if frames<0:frames=8
  if frames<2:frames=2
  for i in range(frames,0,-1):
   dx,dy=(x-card.x)/i,(y-card.y)/i
   card.moveBy(dx,dy)
   self.top.update_idletasks()
 def _winAnimation(self):
  cards=[]
  for s in self.openstacks:
   cards=cards+s.cards
  while cards:
   card=self.main.miscrandom.choice(cards)
   cards.remove(card)
   self.animatedMoveTo(card,self.talon.x,self.talon.y,tkraise=0)
 def changed(self):
  if self.stats.updated==2:
   return 1
  if self.changed_index!=self.moves.index:
   return 1
  return 0
 def newGame(self,seed=None,update_stats=1):
  if not update_stats and self.stats.updated!=1:
   self.reset()
   self.stats.updated=2
  else:
   self.updateStats()
   self.reset()
  self.talon.removeCards()
  self.seed=self.talon.shuffle(seed)
  if 1:
   self.top.update()
  self.startGame()
  self.startMoves()
  self.updateMenus()
 def restoreGame(self,game):
  self.reset()
  self.talon.removeCards()
  self.filename=game.filename
  assert len(self.allstacks)==len(game.loadstacks)
  for i in range(len(self.allstacks)):
   for t in game.loadstacks[i]:
    card_id,face_up=t
    card=self.cards[card_id]
    self.allstacks[i].add(card)
    if face_up:
     card.showFace()
    else:
     card.showBack()
  self.version=game.version
  self.seed=game.seed
  self.moves=game.moves
  self.stats=game.stats
  self.updateMenus()
 def updateStats(self,was_demo=0):
  if self.stats.loaded>0:
   return
  if self.stats.updated==1:
   return
  if self.stats.updated==0:
   if self.moves.index==0:
    return
  won=self.isGameWon()
  if was_demo and self.stats.player_moves==0:
   if won:
    self.main.stats.updateDemoStats(self.id,1,0)
   else:
    self.main.stats.updateDemoStats(self.id,0,1)
   self.stats.updated=1
   self.changed_index=self.moves.index
  elif self.stats.updated==2 or self.stats.player_moves>0:
   if self.stats.hints>0 or self.stats.demo_moves>0:
    won=0
   if won:
    self.main.stats.updateStats(self.main.user,self.id,1,0)
   else:
    self.main.stats.updateStats(self.main.user,self.id,0,1)
   self.stats.updated=1
   self.changed_index=self.moves.index
  else:
   pass
 def startGame(self):
  pass
 def dealCards(self):
  return 0
 def getHintClass(self):
  return None
 def showHint(self,level=0,sleep=1.5,taken_hint=None):
  if not self.getHintClass():
   return None
  if level!=self.hints.level:
   self.hints.level=level
   self.hints.list=None
  if self.hints.list is None:
   h=(self.getHintClass())(self)
   self.hints.list=h.getHints(level,taken_hint)
   self.hints.index=0
  if len(self.hints.list)==0:
   return None
  h=self.hints.list[self.hints.index]
  self.hints.index=self.hints.index+1
  if self.hints.index>=len(self.hints.list):
   self.hints.index=0
  ncards,from_stack,to_stack,score,fill,th=h
  assert len(from_stack.cards)>=ncards
  if ncards==0:
   assert level>=2
   assert from_stack==self.talon
   return h
  elif from_stack==to_stack:
   assert level>=2
   assert ncards==1 and from_stack
   return h
  else:
   assert ncards>0 and from_stack and to_stack
   assert to_stack.acceptsCards(from_stack.cards[-ncards:])
  x1,y1=from_stack.getPositionFor(from_stack.cards[-ncards])
  x2,y2=to_stack.getPositionFor(to_stack.getCard())
  x1=x1+self.main.images.CARDW/2
  x2=x2+self.main.images.CARDW/2
  if ncards==1:
   y1=y1+self.main.images.CARDH/2
  else:
   y1=y1+from_stack.FACE_UP_OFFSET/2
  y2=y2+self.main.images.CARDH/2
  arrow=None
  arrow=self.canvas.create_line(
     x1, y1, x2, y2, width=7, fill='#303030',
     arrow='last', arrowshape=(30,30,10))
  text=None
  if level==1 or (level>1 and self.main.maint):
   text=self.canvas.create_text(
      10, self.height - 10, anchor='sw', fill=fill,
      text="Score %6d" % (score))
  self.top.update_idletasks()
  time.sleep(sleep)
  if text:
   self.canvas.delete(text)
  self.canvas.delete(arrow)
  self.top.update_idletasks()
  return h
 def playDemo(self,level=9):
  assert level>=2
  self.demo=None
  if not self.getHintClass():
   return None
  self.demo=Struct(
   level=level,
   sleep=self.main.opt.demo_sleep,
   last_deal=None,
   hint=None,
   keypress=None,
  )
  self.hints.list=None
  self.top.after_idle(self.eDemo)
 def eDemo(self):
  if not self.demo or self.demo.keypress:
   self.demo=None
   self.updateMenus()
   return
  finished=self._demoOneMove(self.demo)
  self.top.update_idletasks()
  self.hints.list=None
  status=0
  bitmap='info'
  timeout=10000
  if self.isGameWon():
   finished=1
   if self.stats.player_moves==0:
    d = Dialog(self.top, title="PySol Autopilot",
      text="\nI did it !\n",
      bitmap=bitmap, default=0, strings=("Cool",),
      width='30', timeout=timeout)
   else:
    d = Dialog(self.top, title="PySol Autopilot",
      text="\nGame finished\n",
      bitmap=bitmap, default=0, strings=("Ok",),
      width='30', timeout=timeout)
   status=d.status
  elif finished:
   d = Dialog(self.top, title="PySol Autopilot",
     text="\nThis won't come out...\n",
     bitmap=bitmap, default=0, strings=("Ok",),
     width='30', timeout=timeout)
   status=d.status
  if finished:
   self.updateStats(1)
  if self.demo:
   if status==2:
    assert finished
    while 1:
     id=self.main.getRandomGameId()
     if 1 and id==self.id:
      continue
     break
    if id==self.id:
     self.newGame()
     self.playDemo()
    else:
     self._quitGame(id,startdemo=1)
   elif not finished:
    if 1:
     self.top.update()
    if self.demo:
     self.top.after_idle(self.eDemo)
 def _demoOneMove(self,demo):
  h=self.showHint(demo.level,demo.sleep,taken_hint=demo.hint)
  demo.hint=h
  if not h:
   return 1
  ncards,from_stack,to_stack,score,fill,th=h
  if ncards==0:
   if not self.dealCards():
    return 1
   c=self.talon.getCard()
   if c:
    if c==demo.last_deal:
     return 1
    if not demo.last_deal:
     demo.last_deal=c
  elif from_stack==to_stack:
   self.flipMove(from_stack)
   demo.last_deal=None
  else:
   self.moveMove(ncards,from_stack,to_stack,frames=0)
   demo.last_deal=None
  self.finishMove()
  return 0
 def autoDropCards(self):
  if not self.getHintClass():
   return None
  h=(self.getHintClass())(self)
  from_stacks=self.dropstacks
  to_stacks=self.suits
  while 1:
   stack=h.getDropStack(from_stacks,to_stacks)
   if not stack:
    break
   stack.dropCard(self.suits)
   self.finishMove()
 def isGameWon(self):
  c=0
  for s in self.suits:
   c=c+len(s.cards)
  return c==len(self.cards)
 def checkForWin(self):
  if not self.isGameWon():
   return
  self.finishMove()
  if self.stats.hints>0 or self.stats.demo_moves>0:
   a=0
   d = Dialog(self.top,title="Game finished",
     text="\nGame finished, but you needed my help...\n",
     bitmap='', default=0, strings=("Ok","Save",))
   self.updateStats()
  else:
   a=1
   d = Dialog(self.top,title="Game won",
     text="\nCongratulations, you did it !\n" +
       "You needed " + str(self.moves.index) + " moves this time.\n",
     bitmap='', default=0, strings=("Great","Save"))
   self.updateStats()
  if d.num==1:
   self.mSave()
  if a:
   self._winAnimation()
  self.newGame()
 def getClosestStack(self,card,stacks=None):
  if not stacks:
   stacks=self.openstacks
  closest=None
  cdist=999999999
  for stack in stacks:
   dist=(stack.x-card.x)**2+(stack.y-card.y)**2
   if dist<cdist:
    closest=stack
    cdist=dist
  return closest
 S_DEAL=0
 S_PLAY=1
 S_UNDO=2
 S_REDO=3
 def startMoves(self):
  self.moves=Struct(
   state=self.S_PLAY,
   history=[],
   index=0,
   current=[],
  )
  self.stats.player_moves=0
  self.stats.demo_moves=0
  self.stats.total_moves=0
 def __storeMove(self,am):
  if self.moves.state==self.S_DEAL or self.moves.state==self.S_PLAY:
   self.moves.current.append(am)
 def moveMove(self,ncards,from_stack,to_stack,frames=-1):
  assert ncards>0 and from_stack and to_stack
  assert ncards<=len(from_stack.cards)
  am=AMoveMove(ncards,from_stack,to_stack,frames)
  self.__storeMove(am)
  self.doMoveMove(ncards,from_stack,to_stack,frames)
 def flipMove(self,stack):
  assert stack
  am=AFlipMove(stack)
  self.__storeMove(am)
  self.doFlipMove(stack)
 def finishMove(self):
  if self.moves.current:
   assert len(self.moves.current)>0
   del self.moves.history[self.moves.index:]
   self.moves.history.append(self.moves.current)
   self.moves.index=self.moves.index+1
   assert self.moves.index==len(self.moves.history)
   self.moves.current=[]
   if self.demo:
    self.stats.demo_moves=self.stats.demo_moves+1
   else:
    self.stats.player_moves=self.stats.player_moves+1
   self.stats.total_moves=self.stats.total_moves+1
   self.updateMenus()
   return 1
  return 0
 def undo(self):
  assert self.moves.state==self.S_PLAY and self.moves.current==[]
  assert self.moves.index>=0 and self.moves.index<=len(self.moves.history)
  if self.moves.index==0:
   return
  self.moves.index=self.moves.index-1
  m=self.moves.history[self.moves.index]
  m=m[:]
  m.reverse()
  self.moves.state=self.S_UNDO
  for atomic_move in m:
   atomic_move.undo(self)
  self.moves.state=self.S_PLAY
  self.stats.undo=self.stats.undo+1
  self.stats.total_moves=self.stats.total_moves+1
  self.updateMenus()
 def redo(self):
  assert self.moves.state==self.S_PLAY and self.moves.current==[]
  assert self.moves.index>=0 and self.moves.index<=len(self.moves.history)
  if self.moves.index==len(self.moves.history):
   return
  m=self.moves.history[self.moves.index]
  self.moves.index=self.moves.index+1
  self.moves.state=self.S_REDO
  for atomic_move in m:
   atomic_move.redo(self)
  self.moves.state=self.S_PLAY
  self.stats.redo=self.stats.redo+1
  self.stats.total_moves=self.stats.total_moves+1
  self.updateMenus()
 def doMoveMove(self,ncards,from_stack,to_stack,frames):
  cards=[]
  for i in range(ncards):
   card=from_stack.getCard()
   assert card
   from_stack.delete(card)
   cards.append(card)
  if frames!=0:
   assert ncards==1
   x,y=to_stack.getPositionFor(cards[0])
   self.animatedMoveTo(cards[0],x,y,frames)
  cards.reverse()
  if self.moves.state==self.S_PLAY:
   assert to_stack.acceptsCards(cards)
  for c in cards:
   if self.moves.state==self.S_PLAY:
    assert to_stack.acceptsCards([c])
   to_stack.add(c)
  self.hints.list=None
 def doFlipMove(self,stack):
  card=stack.getCard()
  assert card
  if card.face_up:
   card.showBack()
  else:
   card.showFace()
  self.hints.list=None
 def _loadGame(self,filename,main):
  game=None
  try:
   f = open(filename, 'rb')
   p=Unpickler(f)
   package=p.load()
   version=p.load()
   id=p.load()
   if (type(package) != type('') or package != PACKAGE
     or type(version) != type('')
     or type(id) != type(1) or id <= 0):
    raise Exception, "Not a PySol file"
   if version>VERSION:
    raise Exception, "Cannot load games saved with PySol version " + version
   gameclass=main.getGameClass(id)
   if not gameclass:
    raise Exception, "File damaged"
   game=gameclass(id)
   game.version=version
   game.seed=p.load()
   game.loadstacks=[]
   for i in range(p.load()):
    stack=[]
    for j in range(p.load()):
     card_id=p.load()
     face_up=p.load()
     assert type(card_id)==type(1) and type(face_up)==type(1)
     stack.append((card_id,face_up))
    game.loadstacks.append(stack)
   game.moves=p.load()
   game.stats=p.load()
   game.stats.loaded=game.stats.loaded+1
  finally:
   if f:f.close()
  return game
 def _saveGame(self,filename,binmode=1):
  f=None
  try:
   f = open(filename, 'wb')
   p=Pickler(f,binmode)
   p.dump(PACKAGE)
   p.dump(VERSION)
   p.dump(self.id)
   p.dump(self.seed)
   p.dump(len(self.allstacks))
   for stack in self.allstacks:
    p.dump(len(stack.cards))
    for card in stack.cards:
     p.dump(card.id)
     p.dump(card.face_up)
   p.dump(self.moves)
   self.stats.saved=self.stats.saved+1
   p.dump(self.stats)
  finally:
   if f:f.close()
 def loadGame(self,filename):
  game=None
  try:
   game=self._loadGame(filename,self.main)
  except Exception,ex:
   d = Dialog(self.top,title="Load Error",
        text="Error while loading game:\n\n"+str(ex),
        bitmap="error",default=0,strings=("Ok",))
  else:
   self.filename=filename
   self.changed_index=self.moves.index
   game.filename=filename
   if game.id==self.id:
    self.restoreGame(game)
   else:
    self._quitGame(game.id,loadedgame=game)
 def saveGame(self,filename,binmode=1):
  try:
   self._saveGame(filename,binmode)
  except Exception,ex:
   d = Dialog(self.top,title="Save Error",
        text="Error while saving game:\n\n"+str(ex),
        bitmap="error",default=0,strings=("Ok",))
  else:
   self.filename=filename
   self.changed_index=self.moves.index
  return
class Gypsy_Talon(TalonStack):
 def dealCards(self):
  c=self.dealRow()
  self.game.finishMove()
  return c
class Gypsy(Game):
 def createGame(self):
  images=self.main.images
  XMARGIN=6
  YMARGIN=8
  XSPACE=images.CARDW+XMARGIN
  YSPACE=images.CARDH+YMARGIN
  self.setSize(10*XSPACE+XMARGIN,5*YSPACE+2*YMARGIN)
  x=XMARGIN
  y=YMARGIN
  self.rows=[]
  for i in range(8):
   self.rows.append(RowStack(x,y,self))
   x=x+XSPACE
  self.rows_x=x-images.CARDW/2
  self.rows_y=-1
  self.suits=[]
  for i in range(4):
   self.suits.append(SuitStack(x,y,self,i))
   self.suits.append(SuitStack(x+XSPACE,y,self,i))
   y=y+YSPACE
  x=x+XSPACE/2
  y=y+YMARGIN
  self.talon=Gypsy_Talon(x,y,self)
  self.talon.text=Canvas.CanvasText(self.canvas,
          x+XSPACE,y+images.CARDH,
          anchor='sw')
  self.talon.text_format = "%3d"
  self.cards=self.talon.createCards(2)
  self.talonstacks=[self.talon]
  self.openstacks=self.suits+self.rows
  self.dropstacks=self.rows
  self.reservestacks=[]
 def startGame(self):
  assert len(self.talon.cards)==2*52
  self.moves.state=self.S_DEAL
  for i in range(2):
   self.talon.dealRow(flip=0,frames=0)
  self.talon.dealRow()
 def getClosestStack(self,card):
  if self.rows_x>0:
   if card.x<self.rows_x:
    return Game.getClosestStack(self,card,self.rows)
   else:
    return Game.getClosestStack(self,card,self.suits)
  if self.rows_y>0:
   if card.y<self.rows_y:
    return Game.getClosestStack(self,card,self.rows)
   else:
    return Game.getClosestStack(self,card,self.suits)
  return Game.getClosestStack(self,card)
 def dealCards(self):
  return self.talon.dealCards()
 def getHintClass(self):
  return Gypsy_Hint
class Klondike_Talon(TalonStack):
 def __init__(self,x,y,game,max_rounds=-1):
  TalonStack.__init__(self,x,y,game)
  self.round=1
  self.max_rounds=max_rounds
 def dealCards(self,waste):
  old_state=self.game.moves.state
  self.game.moves.state=self.game.S_DEAL
  card=self.getCard()
  if card:
   assert not self.getCard().face_up
   self.game.flipMove(self)
   self.game.moveMove(1,self,waste,frames=4)
   self.game.moves.state=old_state
   self.game.finishMove()
   return 1
  card=waste.getCard()
  if card and self.round!=self.max_rounds:
   self.round=self.round+1
   while 1:
    card=waste.getCard()
    if not card:
     break
    self.game.moveMove(1,waste,self,frames=0)
    self.game.flipMove(self)
   self.game.moves.state=old_state
   self.game.finishMove()
   return 1
  self.game.moves.state=old_state
  return 0
class Klondike(Game):
 def createGame(self):
  images=self.main.images
  self.FACE_DOWN_OFFSET=6
  MARGIN=10
  XSPACE=images.CARDW+2*MARGIN
  YSPACE=images.CARDH+4*MARGIN
  self.setSize(7*XSPACE,3*YSPACE+20+MARGIN)
  x=MARGIN
  y=MARGIN
  self.talon=Klondike_Talon(x,y,self)
  self.talon.text=Canvas.CanvasText(self.canvas,
          x+images.CARDW/2,y+images.CARDH+MARGIN,
          anchor='center')
  self.talon.text_format = "%d"
  x=x+XSPACE
  self.waste=WasteStack(x,y,self)
  x=x+XSPACE
  self.suits=[]
  for i in range(4):
   x=x+XSPACE
   self.suits.append(SuitStack(x,y,self,i))
  x=MARGIN
  y=y+YSPACE
  self.rows=[]
  for i in range(7):
   self.rows.append(StrictRowStack(x,y,self))
   x=x+XSPACE
  self.cards=self.talon.createCards(1)
  self.talonstacks=[self.talon]+[self.waste]
  self.openstacks=self.suits+self.rows+[self.waste]
  self.dropstacks=self.rows+[self.waste]
  self.reservestacks=[]
 def startGame(self):
  assert len(self.talon.cards)==52
  self.moves.state=self.S_DEAL
  for i in range(1,len(self.rows)):
   self.talon.dealRow(rows=self.rows[i:],flip=0,frames=0,reverse=1)
  self.talon.dealRow(reverse=1)
  self.talon.dealCards(self.waste)
 def dealCards(self):
  return self.talon.dealCards(self.waste)
 def getHintClass(self):
  return Klondike_Hint
class Eight(Gypsy):
 def getName(self):
  return "8 x 8"
 def createGame(self):
  images=self.main.images
  XMARGIN=6
  YMARGIN=8
  XSPACE=images.CARDW+XMARGIN
  YSPACE=images.CARDH+YMARGIN
  self.setSize(10*XSPACE+XMARGIN,5*YSPACE+2*YMARGIN)
  x=XMARGIN
  y=YMARGIN
  self.rows=[]
  for i in range(8):
   self.rows.append(RowStack(x,y,self))
   x=x+XSPACE
  self.rows_x=x-images.CARDW/2
  self.rows_y=-1
  self.suits=[]
  for i in range(4):
   self.suits.append(SuitStack(x,y,self,i))
   self.suits.append(SuitStack(x+XSPACE,y,self,i))
   y=y+YSPACE
  x=x+XSPACE/2
  y=y+YMARGIN
  self.talon=Klondike_Talon(x,y,self)
  self.talon.text=Canvas.CanvasText(self.canvas,
          x+XSPACE,y+images.CARDH,
          anchor='sw')
  self.talon.text_format = "%3d"
  x=x-XSPACE
  self.waste=WasteStack(x,y,self)
  self.cards=self.talon.createCards(2)
  self.talonstacks=[self.talon]+[self.waste]
  self.openstacks=self.suits+self.rows+[self.waste]
  self.dropstacks=self.rows+[self.waste]
  self.reservestacks=[]
 def startGame(self):
  assert len(self.talon.cards)==2*52
  self.moves.state=self.S_DEAL
  for i in range(7):
   self.talon.dealRow(frames=0)
  self.talon.dealRow()
  self.talon.dealCards(self.waste)
 def dealCards(self):
  return self.talon.dealCards(self.waste)
class Irmgard_Talon(TalonStack):
 def dealCards(self):
  if len(self.cards)>0:
   if len(self.cards)>7:
    c=self.dealRow()
   else:
    c=self.dealRow(self.game.rows[1:8])
   self.game.finishMove()
   return c
class Irmgard(Gypsy):
 def createGame(self):
  images=self.main.images
  XMARGIN=6
  YMARGIN=8
  XSPACE=images.CARDW+XMARGIN
  YSPACE=images.CARDH+YMARGIN
  self.setSize(9*XSPACE+5*XMARGIN,5*YSPACE+2*YMARGIN)
  x=XMARGIN
  y=YMARGIN
  self.rows=[]
  for i in range(9):
   self.rows.append(StrictRowStack(x,y,self))
   x=x+XSPACE
  x=2*XMARGIN
  y=y+4*YSPACE
  self.rows_x=-1
  self.rows_y=y-YSPACE/2
  self.suits=[]
  for i in range(8):
   self.suits.append(SuitStack(x,y,self,i/2))
   x=x+XSPACE
  x=x+2*XMARGIN
  self.talon=Irmgard_Talon(x,y,self)
  self.talon.text=Canvas.CanvasText(self.canvas,
          x+images.CARDW/2,y-YMARGIN,
          anchor='center')
  self.talon.text_format = "%d"
  self.cards=self.talon.createCards(2)
  self.talonstacks=[self.talon]
  self.openstacks=self.suits+self.rows
  self.dropstacks=self.rows
  self.reservestacks=[]
 def startGame(self):
  assert len(self.talon.cards)==2*52
  self.moves.state=self.S_DEAL
  r=self.rows
  for i in range(1,5):
   self.talon.dealRow(r[i:len(r)-i],flip=0,frames=0)
  self.talon.dealRow()
class Options:
 def __init__(self):
  self.confirm=1
  self.animations=1
  self.hint_sleep=1.5
  self.demo_sleep=1.5
  self.tablecolor = '#008200'
class Statistics:
 def __init__(self):
  self.gameid=0
  self.resetStats()
  self.resetDemoStats()
 def resetStats(self,user=None):
  if user is None:
   self.stats={}
  else:
   assert user
   self.stats[user]={}
 def getStats(self,user,gameid):
  assert user and gameid>=0
  d=self.stats.get(user)
  if d is None:
   d=self.stats[user]={}
  return d.get(gameid,(0,0))
 def updateStats(self,user,gameid,won,lost):
  assert gameid>0
  w,l=self.getStats(user,0)
  self.stats[user][0]=(w+won,l+lost)
  w,l=self.getStats(user,gameid)
  self.stats[user][gameid]=(w+won,l+lost)
 def resetDemoStats(self):
  self.demo_stats={}
 def getDemoStats(self,gameid):
  assert gameid>=0
  return self.demo_stats.get(gameid,(0,0))
 def updateDemoStats(self,gameid,won,lost):
  assert gameid>0
  w,l=self.getDemoStats(0)
  self.demo_stats[0]=(w+won,l+lost)
  w,l=self.getDemoStats(gameid)
  self.demo_stats[gameid]=(w+won,l+lost)
class Tk(Tkinter.Tk):
 def __init__(self):
  Tkinter.Tk.__init__(self)
  self.extra=Struct(
   main=None,
   bindings=[],
   menus=[],
   menubar=None
  )
 def bind(self,sequence=None,func=None,add=None):
  funcid=Tkinter.Tk.bind(self,sequence,func,add)
  self.extra.bindings.append((sequence,funcid))
  return funcid
 def unbindAll(self):
  self.extra.bindings.reverse()
  for b in self.extra.bindings:
   Tkinter.Tk.unbind(self,b[0],b[1])
  self.extra.bindings=[]
 def unbindMenubar(self):
  for i in range(len(self.extra.menus)):
   if self.extra.menus[i]:
    self.extra.menus[i].destroy()
   self.extra.menus[i]=None
 def wmDeleteWindow(self):
  if self.extra.main and self.extra.main.game:
   self.extra.main.game.mQuit()
class Main:
 def __init__(self,top):
  self.starttimer = Timer("Main.__init__")
  self.firsttime=1
  self.maint=0
  self.top=top
  self.canvas=None
  self.game=None
  self.dataloader=None
  self.images=None
  self.optfilename = os.path.expanduser('~/.pysol.options')
  self.statsfilename = os.path.expanduser('~/.pysol.statistics')
  self.user = ''
  if not self.user:
   self.user = string.strip(os.environ.get('USER',''))
  if not self.user:
   self.user = string.strip(os.environ.get('LOGNAME',''))
  self.initialfilename=self.user
  if not self.user:
   self.user = 'unknown'
  if self.user == 'mfx':
   self.maint=1
  if not self.initialfilename:
   self.initialfilename = 'untitled'
  self.gamerandom=Random()
  self.miscrandom=Random()
  self.opt=Options()
  self.stats=Statistics()
  self.nextgame=Struct(
   id=1,
   startdemo=0,
   loadedgame=None,
  )
 def mainloop(self):
  try:self.loadOptions()
  except:pass
  try:self.loadStatistics()
  except:pass
  while 1:
   id=self.nextgame.id
   self.nextgame.id=0
   self.runGame(id)
   self.freeGame()
   if self.nextgame.id<=0:
    break
  try:self.saveStatistics()
  except:pass
 def runGame(self,id):
  self.canvas=Tkinter.Canvas(self.top,highlightthickness=0)
  g=self.getGameClass(id)
  if not g:
   id=1
   g=self.getGameClass(id)
  assert g and type(g)==type(Main)
  self.game=g(id)
  self.stats.gameid=id
  self.game.create(self)
  if self.nextgame.loadedgame:
   self.game.restoreGame(self.nextgame.loadedgame)
   self.nextgame.loadedgame=None
  else:
   self.game.newGame()
  self.top.extra.main=self
  if self.firsttime and not self.maint:
   status=helpAbout(self.top,timeout=20000)
   if status==2:
    self.nextgame.startdemo=1
  self.firsttime=0
  sys.stdout.flush()
  if self.nextgame.startdemo:
   self.top.after_idle(self.game.playDemo)
  self.nextgame.startdemo=0
  self.top.mainloop()
 def freeGame(self):
  self.top.extra.main=None
  self.top.unbindAll()
  self.top.unbindMenubar()
  if self.canvas:
   self.canvas.destroy()
  self.canvas=None
  if self.game:
   destruct(self.game)
  self.game=None
 def loadOptions(self):
  opt=unpickle(self.optfilename)
  if opt:self.opt=opt
 def saveOptions(self):
  pickle(self.opt,self.optfilename)
 def loadStatistics(self):
  stats=unpickle(self.statsfilename)
  if stats:
   self.stats=stats
   if stats.gameid>0:
    self.nextgame.id=self.stats.gameid
 def saveStatistics(self):
  pickle(self.stats,self.statsfilename)
 __GAMES={
  1:  [ Gypsy,        "Gypsy", ],
  2:  [ Klondike,     "Klondike" ],
  3:  [ Irmgard,      "Irmgard" ],
  4:  [ Eight,        "8 x 8 "],
 }
 __GAMES_ID=__GAMES.keys()
 __GAMES_ID.sort()
 def getGameClass(self,id):
  g=self.__GAMES.get(id,None)
  if g:g=g[0]
  return g
 def getGameName(self,id):
  g = self.__GAMES.get(id,'')
  if g:g=g[1]
  return g
 def getRandomGameId(self):
  return self.miscrandom.choice(self.__GAMES_ID)
def main(args):
 dataloader = DataLoader(args[0], 'pysol.xbm')
 images = Images(dataloader, 'cardset-01')
 top=Tk()
 top.protocol('WM_DELETE_WINDOW', top.wmDeleteWindow)
 top.minsize(200,200)
 try: top.iconbitmap('@' + dataloader.findFile('pysol.xbm'))
 except:pass
 images.load()
 if os.name == 'posix':
  color = '#d9d9d9'
  top.tk_setPalette('background', color, 'activeBackground', color)
 main=Main(top)
 main.dataloader=dataloader
 main.images=images
 main.mainloop()
 main=None
 if total.games!=0 or total.stacks!=0 or total.cards!=0:
  print "Now: games %d, stacks %2d, cards %3d" % (total.games, total.stacks, total.cards)
  print "Max: games %d, stacks %2d, cards %3d" % (total.maxgames, total.maxstacks, total.maxcards)
 return 0
if __name__ == '__main__':
 sys.exit(main(sys.argv))
