class CanastaRound(): """ The main game object, which contains all the logic for playing Canasta. The default locations will be rescaled when the window size changes. """ LOCATIONSXY = ((435,300),(530,300)) + ((300,650),(20,180),(300,10),(920,180)) + ((500,500),(290,115),(365,115),(440,115),(515,115),(590,115),(665,115),(290,475),(365,475),(440,475),(515,475),(590,475),(665,475)) + ((500,500),(125,200),(125,250),(125,300),(125,350),(125,400),(125,450),(795,200),(795,250),(795,300),(795,350),(795,400),(795,450)) + ((10,10),(850,665)) + ((20,600),(60,600),(100,600),(140,600),(20,670),(30,670),(40,670),(50,670),(60,670),(70,670),(80,670),(90,670),(100,670),(110,670)) STAGEXY = (10,600,200,133) SCOREX = (750,0) CHATX = (745,70) XSCALE = 1 YSCALE = 1 MAX_HISTORY = 30 history = [] LOCATIONNAMES = ['pile','discard'] + ['bottom','left','top','right'] + ['3_1','4_1','5_1','6_1','7_1','8_1','9_1','t_1','j_1','q_1','k_1','a_1','wild_1'] + ['3_2','4_2','5_2','6_2','7_2','8_2','9_2','t_2','j_2','q_2','k_2','a_2','wild_2'] + ['red3_1','red3_2'] + ['3_0','4_0','5_0','6_0','7_0','8_0','9_0','t_0','j_0','q_0','k_0','a_0','wild_0'] LOCATIONS = [0,1] + [100,200,300,400] + range(103,116) + range(203,216) + [1000,2000] + range(3,16) positions = ["Bottom","Left","Top","Right"] def __init__(self,images=True): """ Initialize the game object. The server calls this with images=False to avoid using the pygame overhead, while clients call it with images=True in order to use the card image data. """ self.images = images self.chatlist = [] self.curchat = "" self.enterchat = False self.animating = False self.roundOver = False self.res = (1024,768) def debugFunc(self): if not DEBUGGER: return print "Selected locations" for c in self.selectionCards: print [c.color,c.value,c.location,c.rotated,c.order,[c.x,c.y],[c.rect.x,c.rect.y]] print "Has Melded?" print self.hasMelded() print "To go in:" print [self.minmeld1,self.minmeld2] print "Has Canasta?" print self.hasCanasta() print "Scores" print [self.handPoints(1),self.cardPoints(1),self.specialPoints(1,True)] print [self.handPoints(2),self.cardPoints(2),self.specialPoints(2,True)] ############################ #Initialize the game and round ############################# def gameStart(self,playernames,human,options=CanastaOptions()): """ Sets up the game object for the start of a game. """ self.selectedCard = None self.selectionRect = pygame.Rect((0,0,0,0)) self.selectionCards = [] self.selectedCards = [] ci = CardImages(self.images) cards = [] for i in range(0,54): cards.append(Card(ci.getCardNbr(i),ci.getBack(),ci.getColors(i),ci.getValues(i),ci.getCanColors(i),ci.getCanValues(i),ci.images)) #second deck for i in range(0,54): cards.append(Card(ci.getCardNbr(i),ci.getBack(),ci.getColors(i),ci.getValues(i),ci.getCanColors(i),ci.getCanValues(i),ci.images)) self.cardGroup = CardGroup(ci.images,cards) self.cardGroup.shuffle() self.playernames = playernames self.human = human self.myPos = human self.partnerPos = 100*(self.myPos+1) + 200 if self.partnerPos>400: self.partnerPos -= 400 self.options = options self.chatlist = [] self.curchat = "" self.enterchat = False self.team1score = 0 self.team2score = 0 self.round = 0 self.pushHistory("Setup Canasta") def newGame(self): self.team1score = 0 self.team2score = 0 self.round = 0 def initCanasta(self,nextround=True): """ Initialize a round. If the game object belongs to a client, the card locations will be overwritten by the server. """ self.selectionRect = pygame.Rect((0,0,0,0)) self.selectionCards = [] self.selectedCards = [] self.lastMelded = [] self.turnstart = False self.frozen = False self.topPile = None self.roundOver = False self.myPosMelded = False self.concealed = False self.invisible = False self.let_go_out = True if nextround: self.round += 1 self.turn = (self.round - 1) % 4 self.endroundtext = None self.curlocxy = [] self.curstagexy = [] for i in range(len(self.LOCATIONSXY)): self.curlocxy.append(list(self.LOCATIONSXY[i])) for i in range(len(self.STAGEXY)): self.curstagexy.append(self.STAGEXY[i]) self.curscorex = self.SCOREX[0] self.curscale = [self.XSCALE,self.YSCALE] self.locationsUpdate(self.res) self.lastCommand = 0 self.lastReturn = False self.lastArgs = [] self.lastToken = [] self.cardGroup.collectAll(self.curlocxy[0][0],self.curlocxy[0][1]) self.cardGroup.shuffle() for c in self.cardGroup.cards: c.location = 0 c.nofreeze = False for index, c in enumerate(self.cardGroup.cards): c.order = index if (self.team1score < 0) & self.options.negpoints: self.minmeld1 = 15 elif self.team1score <1500: self.minmeld1 = 50 elif (self.team1score < 3000): self.minmeld1 = 90 else: self.minmeld1 = 120 if (self.team2score < 0) & self.options.negpoints: self.minmeld2 = 15 elif self.team2score <1500: self.minmeld2 = 50 elif (self.team2score < 3000): self.minmeld2 = 90 else: self.minmeld2 = 120 def dealRound(self): gt = self.cardGroup.getCardAt cards = 11 self.idx = 107 for cols in range(4): for hc in range(cards): c = gt(self.idx) while c.cancolor==100: c.flip() self.cardGroup.dropCard(c) if cols % 2 == 0: c.location = 1000 if cols % 2 == 1: c.location = 2000 self.idx-=1 c = gt(self.idx) if cols==self.human: c.flip() self.cardGroup.dropCard(c) c.location = 100*(cols+1) self.idx-=1 while self.topPile in [None,0,100]: c = gt(self.idx) c.flip() self.cardGroup.dropCard(c) c.location = 1 self.topPile = c.cancolor self.topPileV = c.canvalue if (c.cancolor==0) & (not self.options.initfreeze): c.nofreeze = True elif (c.cancolor==0) & self.options.initfreeze: self.frozen = True if c.cancolor not in [0,100]: c.isTop = True self.idx-=1 self.handLayout() self.pileLayout() self.redThreeLayout() ############################ #package the current game state for export to a player module ############################# def curState(self,active=True): cardselect = [] if self.turnstart: turnstatus = POST_DRAW else: turnstatus = PRE_DRAW if (len(self.selectionCards)>0) & active: for c in self.selectionCards: if c.location == 100*(self.turn+1): cardselect.append(c) if self.curTeam()==1: meldPoints = self.minmeld1 else: meldPoints = self.minmeld2 numcards = [] for i in [100,200,300,400]: num = 0 for c in self.cardGroup.cards: if c.location == i: num+=1 numcards.append(num) curLocations = [] return_locations = [1000,2000] + range(103,116) + range(203,216) if active: return_locations.append(100*(self.turn+1)) return_locations = return_locations + range(3,16) for c in self.cardGroup.cards: if (c.location in return_locations) | (c.isTop): curLocations.append(c) lastMelded = self.lastMelded result = CanastaStatus(meldPoints,self.turn,turnstatus,cardselect,curLocations,lastMelded,numcards,self.frozen,self.roundOver,self.lastCommand,self.lastReturn,self.lastArgs,self.lastToken) return result def initStatus(self,active=False,fornet=False): top_loc = None locations = [] for i in range(0,108): c = self.cardGroup.cards[i] if c.isTop: top_loc = c.order locations.append([c.color,c.value,c.location,c.order]) result = CanastaInitStatus([self.minmeld1,self.minmeld2],locations,top_loc,self.frozen,self.idx,self.playernames,active,self.turn,[self.team1score,self.team2score],self.turnstart,self.myPosMelded,self.concealed,self.let_go_out) return result def readInit(self,initStatus): self.turn = initStatus.turn self.frozen = initStatus.frozen self.minmeld1, self.minmeld2 = initStatus.meldPoints self.team1score, self.team2score = initStatus.teamscores self.turnstart = initStatus.turnstart self.myPosMelded = initStatus.myPosMelded self.concealed = initStatus.concealed self.let_go_out = initStatus.let_go_out for c in self.cardGroup.cards: c.location = -1 c.isTop = False for I in initStatus.curLocations: found = False for c in self.cardGroup.cards: if (c.color==I[0]) & (c.value==I[1]) & (c.location == -1) & (not found): found = True c.location = I[2] c.order = I[3] if c.order == initStatus.top_loc: c.isTop = True self.topPile = c.cancolor self.topPileV = c.canvalue self.cardGroup.popCard(c) if c.location in [1,(self.myPos+1)*100,1000,2000]: if c.side==0: c.flip() elif c.side==1: c.flip() for c in self.cardGroup.cards: if c.location == 1: if (c.cancolor==0) & (not self.options.initfreeze): c.nofreeze = True elif (c.cancolor==0) & self.options.initfreeze: self.frozen = True c.nofreeze = False else: c.nofreeze = False else: c.nofreeze = False self.idx = initStatus.idx self.allLayout() ############################ #Internal condition functions ############################# def cardsInSet(self): clist = [] for i in range(len(self.selectedCards)): if self.selectedCards[i].cancolor not in clist: clist.append(self.selectedCards[i].cancolor) return clist def curTeam(self): result = (self.turn % 2) + 1 return result def cardsInHand(self,include_stage=True): result = 0 matchlist = [(self.turn+1)*100] if include_stage: matchlist += range(4,16) for c in self.cardGroup.cards: if c.location in matchlist: result += 1 return result def numMelded(self,cancol,team): result = [0,0] if cancol==0: cancol = 15 for c in self.cardGroup.cards: if c.location == 100*team + cancol: result[0] += 1 if c.cancolor == 0: result[1] += 1 return result def hasCanasta(self): result = False for i in range(4,16): if self.numMelded(i,self.curTeam())[0]>=7: result = True return result def hasMelded(self): result = False meldLocations = range(100*self.curTeam()+4,100*self.curTeam() + 16) for c in self.cardGroup.cards: if c.location in meldLocations: result = True return result def pointsToGoIn(self): if self.curTeam() == 1: return self.minmeld1 if self.curTeam() == 2: return self.minmeld2 def cardsOnTable(self): team = (self.turn % 2) result = [] if team == 0: meldLocations = range(103,116) if team == 1: meldLocations = range(203,216) for c in self.cardGroup.cards: if (c.location in meldLocations) & (c.cancolor not in result): result.append(c.cancolor) return result def pileSize(self): result = 0 for c in self.cardGroup.cards: if c.location == 1: result += 1 return result def cardLocations(self): clist = [] for i in range(len(self.selectedCards)): if self.selectedCards[i].location not in clist: clist.append(self.selectedCards[i].location) return clist def validMeld(self): """ Determine whether the currently selected cards can be legally melded by the current player, either by themselves or in combination with an existing meld. Does not check for the point threshold. """ status = [0,0] vals = [] #Determine what naturals and wild cards are selected for c in self.selectedCards: #Invalid if a selected card does not belong to the player if c.location != 100*(self.turn + 1): return False if c.cancolor != 0: status[0] += 1 if c.cancolor not in vals: vals.append(c.cancolor) else: status[1] += 1 #If more than one different natural is selected, not a valid meld if len(vals) > 1: return False elif (len(vals) == 0): val = 15 else: val = vals[0] #Check for currently melded or staged cards of the same value for c in self.cardGroup.cards: if c.location in [100*self.curTeam() + val,val]: if c.cancolor != 0: status[0] += 1 else: status[1] += 1 if self.options.threewilds: limit = 3 else: limit = status[0] - 1 #If the resulting meld would be at least three cards and doesn't violate the maximum meld size or the wild card limit, it's OK if (sum(status)>7) & (not self.options.megamelds): return False elif (sum(status)>2) & (status[1]<=limit): return True #Allow a wild card meld if it's legal for this game elif self.options.wildmeld & (status[0]==0) & (status[1]>2): return True else: return False def selectedNatural(self): """ Returns the natural component of selected cards, or None if there are two different naturals present. """ vals = [] for c in self.selectedCards: if c.cancolor != 0: if c.cancolor not in vals: vals.append(c.cancolor) if len(vals) == 1: return vals[0] elif len(vals) == 0: return 0 else: return None def canGoOut(self): """ Reports whether the current player can legally go out. """ if not self.turnstart: return False elif not self.hasCanasta(): return False elif self.cardsInHand(include_stage=False)>1: return False else: return True def goConcealed(self): """ Reports whether the current player can legally go out concealed. """ if self.cardsInHand(include_stage=False)>1: return False elif self.myPosMelded: return False else: points = 0 vals = [0]*15 for c in self.cardGroup.cards: if c.location in TLIST: vals[c.location] += 1 points += c.canvalue print vals if (points<self.pointsToGoIn()) & (not self.hasMelded()) & (not self.options.concealedfree): return False elif max(vals)==7: return True elif (max(vals)>7) & self.options.megamelds: return True else: return False def guessWildTarget(self): """ Picks a meld to add a wild card to. Will select a meld of wilds if it exists, otherwise the largest meld that isn't already maxed out on wild cards. Returns 0 if there is nowhere to meld a wild. """ if self.numMelded(0,self.curTeam())[1]>0: return 15 result = 0 vals = [0]*16 locs = range(100*self.curTeam()+3,100*self.curTeam()+16) for c in self.cardGroup.cards: if c.location in locs: vals[c.location % 100] += 1 for i in range(3,7): try: which_meld = vals.index(i) cards = self.numMelded(which_meld,self.curTeam()) if (cards[1] < (cards[0] / 2)) & (result == 0): result = which_meld except: pass return result ############################ #Layout functions ############################# def animate(self): """ Animate card motions. Compares the card destinations (c.x,c.y) with the current locations (c.rect.x,c.rect.y) and increments the locations if there is a discrepancy. Adjusts the relative x and y increments so that cards appear to travel in straight lines. The layout functions set the animation flag when they are called, which in turn causes the client to call this method each time through its main loop. This method turns off the animation flag once the destinations and the locations match. Valid only if the class was instantiated with images=True, otherwise calling this will raise an exception. """ rate = self.options.animation did_something = False for c in self.cardGroup.cards: if (abs(c.x-c.rect.x)==0) | (abs(c.y-c.rect.y)==0): ratex = rate ratey = rate elif abs(c.x-c.rect.x) > abs(c.y-c.rect.y): ratex = rate ratey = rate/(abs(c.x-c.rect.x)/abs(c.y-c.rect.y)) elif abs(c.y-c.rect.y) > abs(c.x-c.rect.x): ratey = rate ratex = rate/(abs(c.y-c.rect.y)/abs(c.x-c.rect.x)) else: ratex = rate ratey = rate if c.x != c.rect.x: did_something = True if abs(c.x-c.rect.x)<ratex: c.rect.x = c.x elif c.x<c.rect.x: c.rect.x -= ratex elif c.x>c.rect.x: c.rect.x += ratex if c.y != c.rect.y: did_something = True if abs(c.y-c.rect.y)<ratey: c.rect.y = c.y elif c.y<c.rect.y: c.rect.y -= ratey elif c.y>c.rect.y: c.rect.y += ratey if not did_something: self.animating = False def updateRect(self): for c in self.cardGroup.cards: c.x = c.rect.x c.y = c.rect.y def allLayout(self): self.handLayout() self.stageLayout() self.meldLayout() self.pileLayout() self.redThreeLayout() def handLayout(self): toSort = [100,200,300,400] offsets = [[20,0],[0,20],[20,0],[0,20]] offsets2 = [[0,20],[20,0],[0,20],[20,0]] counts = [0,0,0,0] row = [0,0,0,0] for t in toSort: self.selectedCards = [] for c in self.cardGroup.cards: if c.location == t: whichGroup = toSort.index(c.location) counts[whichGroup] += 1 coords = self.LOCATIONS.index(c.location) if self.images: c.x = self.curlocxy[coords][0] + counts[whichGroup]*offsets[whichGroup][0] + row[whichGroup]*offsets2[whichGroup][0] c.y = self.curlocxy[coords][1] + counts[whichGroup]*offsets[whichGroup][1] + row[whichGroup]*offsets2[whichGroup][1] self.selectedCards.append(c) if counts[whichGroup]>15: counts[whichGroup]=0 row[whichGroup]+=1 self.sortSelection() if self.images & (not self.invisible): self.selectionRect = pygame.Rect((0,0,0,0)) self.selectedCards = [] self.selectionCards = [] if self.images: self.animating = True self.animate() def meldLayout(self): toSort = range(103,116) + range(203,216) offsets = [[0,10],[0,10],[0,10],[0,10],[0,10],[0,10],[0,10],[0,10],[0,10],[0,10],[0,10],[0,10],[0,10],[10,0],[10,0],[10,0],[10,0],[10,0],[10,0],[10,0],[10,0],[10,0],[10,0],[10,0],[10,0],[10,0]] counts = [0]*len(toSort) self.selectedCards = [] for c in self.cardGroup.cards: if c.location in toSort: self.selectedCards.append(c) self.sortSelection() for c in self.selectedCards: whichGroup = toSort.index(c.location) counts[whichGroup] += 1 coords = self.LOCATIONS.index(c.location) this_meld = self.numMelded(c.location % 100,team=c.location / 100) if c.side==0: c.flip() if this_meld[0]<7: cur_offset = offsets[whichGroup] else: cur_offset = [0,0] if (this_meld[1]==0) & (c.color in ['d','h']): self.cardGroup.popCard(c) if (this_meld[1]>0) & (c.color in ['s','c']): self.cardGroup.popCard(c) if c.rotated==0: if DEBUGGER: print "rotating",c.color,c.value c.turn() if self.images: c.x = self.curlocxy[coords][0] + counts[whichGroup]*cur_offset[0] c.y = self.curlocxy[coords][1] + counts[whichGroup]*cur_offset[1] self.selectionCards = [] self.selectedCards = [] if self.images & (not self.invisible): self.selectionRect = pygame.Rect((0,0,0,0)) if self.images: self.animating = True self.animate() def stageLayout(self): if self.invisible: return toSort = range(3,16) offsets = [[0,10],[0,10],[0,10],[0,10],[0,10],[0,10],[0,10],[0,10],[0,10],[0,10],[0,10],[0,10],[0,10]] counts = [0]*len(toSort) for c in self.cardGroup.cards: if c.location in toSort: whichGroup = toSort.index(c.location) counts[whichGroup] += 1 coords = self.LOCATIONS.index(c.location) if self.images: c.x = self.curlocxy[coords][0] + counts[whichGroup]*offsets[whichGroup][0] c.y = self.curlocxy[coords][1] + counts[whichGroup]*offsets[whichGroup][1] if self.images & (not self.invisible): self.selectionRect = pygame.Rect((0,0,0,0)) if self.images: self.animating = True self.animate() def pileLayout(self): toSort = [0,1] offsets = [[0,0],[0,0]] counts = [0]*len(toSort) pile_count = 0 for c in self.cardGroup.cards: if c.location in toSort: whichGroup = toSort.index(c.location) counts[whichGroup] += 1 coords = self.LOCATIONS.index(c.location) if self.images: c.x = self.curlocxy[coords][0] + counts[whichGroup]*offsets[whichGroup][0] c.y = self.curlocxy[coords][1] + counts[whichGroup]*offsets[whichGroup][1] if (c.location == 1): pile_count += 1 if (c.side==0): c.flip() if (c.rotated == 0) & (c.cancolor==0) & (not c.nofreeze): c.turn() if c.nofreeze & self.images: c.rect.x += 20 if self.images & (not self.invisible): self.selectionRect = pygame.Rect((0,0,0,0)) if self.images: self.animating = True self.animate() def redThreeLayout(self): toSort = [1000,2000] offsets = [[10,0],[10,0]] counts = [0]*len(toSort) for c in self.cardGroup.cards: if c.location in toSort: whichGroup = toSort.index(c.location) counts[whichGroup] += 1 coords = self.LOCATIONS.index(c.location) if self.images: c.x = self.curlocxy[coords][0] + counts[whichGroup]*offsets[whichGroup][0] c.y = self.curlocxy[coords][1] + counts[whichGroup]*offsets[whichGroup][1] if self.images & (not self.invisible): self.selectionRect = pygame.Rect((0,0,0,0)) if self.images: self.animating = True self.animate() def locationsUpdate(self,res): """ Change the coordinates of game objects based on the current window size. Called at the start of the round and whenever the window is resized. """ if self.images: temp = [] for i in range(len(self.LOCATIONSXY)): temp.append([self.LOCATIONSXY[i][0],self.LOCATIONSXY[i][1]]) self.res = (res[0],res[1]) resx = float(res[0]) resy = float(res[1]) scalex = float(1024) scaley = float(768) for i in range(len(self.curlocxy)): defaultx = float(temp[i][0]) defaulty = float(temp[i][1]) self.curlocxy[i][0] = int(defaultx*(resx/scalex)) self.curlocxy[i][1] = int(defaulty*(resy/scaley)) temp = [] for i in range(len(self.STAGEXY)): temp.append(self.STAGEXY[i]) for i in range(len(self.curstagexy)): self.curstagexy[0] = int(float(temp[0])*(resx/scalex)) self.curstagexy[1] = int(float(temp[1])*(resy/scaley)) self.curstagexy[2] = int(float(temp[2])*(resx/scalex)) self.curstagexy[3] = int(float(temp[3])*(resy/scaley)) temp = self.SCOREX[0] self.curscorex = int(float(temp)*(resx/scalex)) temp = self.CHATX[0] self.curchatx = int(float(temp)*(resx/scalex)) self.handLayout() self.meldLayout() self.pileLayout() self.redThreeLayout() self.stageLayout() ############################ #History and selection ############################# def clearHistory(self): self.history = [] def pushHistory(self,desc): #print "Pushing: %s" % desc while len(self.history) > self.MAX_HISTORY: self.history.pop(0) # store get the z-order cards = self.cardGroup.cards[:] # store card info cardinfo = [] for c in cards: info = [] info.append(c.side) info.append(c.child) info.append(c.parent) info.append(c.selected) info.append(c.value) info.append(c.color) cardinfo.append(info) self.history.append([cards,cardinfo,desc]) def popHistory(self): if not len(self.history): return #print "Popping: %s" % hi[4] cards = hi[0] i = 0 for ci in hi[1]: cards[i].setSide(ci[0]) cards[i].rect.topleft = ci[1] cards[i].child = ci[2] cards[i].parent = ci[3] cards[i].selected = ci[4] cards[i].color = ci[5] cards[i].value = ci[6] i+=1 self.cardGroup.cards = cards self.selectionRect = hi[2] self.selectionCards = hi[3] def updateSelectionRect(self): r = None for c in self.selectionCards: if not r: r = pygame.Rect(c.rect) else: r.union_ip(c.rect) r.x-=3 r.y-=3 r.width+=6 r.height+=6 self.selectionRect = r def sortSelection(self,pop=True): if (len(self.selectedCards) > 0): self.selectedCards.sort(key=lambda obj: obj.value) self.selectedCards.sort(key=lambda obj: obj.color) self.selectedCards.sort(key=lambda obj: obj.canvalue) self.selectedCards.sort(key=lambda obj: obj.cancolor) if pop: for c in self.selectedCards: self.cardGroup.popCard(c) ############################ #Staging and melding ############################# def cardsInStage(self): """ Return the number of staged cards. """ result = 0 for c in self.cardGroup.cards: if c.location in range(3,16): result += 1 return result def stagedCards(self): """ Return whether there are staged cards. """ result = False for c in self.cardGroup.cards: if c.location in range(3,16): result = True return result def clearStage(self): for c in self.cardGroup.cards: if c.location in range(4,16): c.location = 100*(self.turn+1) self.handLayout() return True def meldWild(self,target,stage=False): if len(self.selectedCards) != 1: return False if (self.numMelded(target,self.curTeam())[0]==7) & (not self.options.megamelds): return False if (self.selectedCards[0].cancolor != 0) or (self.turnstart == False): return False meld_status = self.numMelded(target,self.curTeam()) if stage: location = 0 else: location = 100*self.curTeam() if meld_status[0]>0: if self.options.threewilds: limit = 3 else: limit = meld_status[0]/2 if (meld_status[1] < limit) | (meld_status[0]==meld_status[1]): self.lastMelded = [] self.selectedCards[0].location = location + target self.lastMelded.append(self.selectedCards[0]) self.selectedCards = [] self.meldLayout() return True else: self.handLayout() return False else: self.handLayout() return False def meldStage(self): for c in self.cardGroup.cards: if c.location in range(3,16): team = self.turn c.location += 100*(1 + (self.turn % 2)) self.lastMelded.append(c) self.meldLayout() def meldSelection(self,target=None,stage=False): if target: return self.meldWild(target,stage) cloc = 100*(self.turn + 1) #Check for the point threshold if applicable if (not stage) & (not self.hasMelded()): if self.stagedCards(): test = range(4,16) cards = self.cardGroup.cards else: cards = self.selectedCards test = [cloc] points = 0 for c in cards: if c.location in test: points += c.canvalue else: points = 1000 #Meld stage cards if applicable if (not stage) & self.turnstart & self.stagedCards(): if self.goConcealed() | (points >= self.pointsToGoIn()) & (self.cardsInStage() < self.cardsInHand()): if self.goConcealed(): self.concealed = True self.lastMelded = [] self.meldStage() if self.concealed: self.roundOver=True return True else: return False if (not stage) & (points < self.pointsToGoIn()): return False #Block if the play would leave player with too few cards if self.options.allowpass | self.hasCanasta(): limit = self.cardsInHand() - 1 else: limit = self.cardsInHand() - 2 if len(self.selectedCards)>limit: return False #If it's OK to stage/meld, set locations if (stage | self.turnstart) & (self.validMeld()): cval = self.selectedNatural() if cval == 0: cval = 15 if stage: toMeld = -1*cloc elif cloc > 200: toMeld = -200 else: toMeld = 0 #Allow melding black 3's only if it's the last meld of the hand if cval==-1: if self.cardsInHand() > (len(self.selectedCards)+1): return False elif not self.hasCanasta(): return False else: for c in self.cardGroup.cards: if c.location == (self.turn+1)*100: if c.cancolor == -1: c.location = self.curTeam()*100 + 3 else: c.location = 1 self.roundOver = True return True #Otherwise it's a normal meld, stage or meld it. else: if not stage: self.lastMelded = [] for i in range(len(self.selectedCards)): self.selectedCards[i].location += (toMeld + cval) if not stage: self.lastMelded.append(self.selectedCards[i]) return True else: return False ############################ #Drawing, discarding and pile-picking ############################# def DrawCard(self): if self.idx<0: self.roundOver=True return True if not self.turnstart: done = False while not done: c = None for c_iter in self.cardGroup.cards: if (c_iter.order == self.idx) & (c==None): c = c_iter if c.cancolor==100: c.flip() c.location = 1000*self.curTeam() self.redThreeLayout() if c.cancolor != 100: if not self.invisible: c.flip() c.location = 100*(self.turn+1) self.turnstart = True done = True self.pushHistory("Draw a card") self.idx-=1 self.handLayout() return True else: return False def DiscardCard(self): if (self.cardsInHand()==1) & ((not self.hasCanasta()) | (not self.let_go_out)) : return False if self.turnstart & (len(self.selectedCards) == 1): if self.selectedCards[0].location == 100*(self.turn+1): self.selectedCards[0].location = 1 if self.selectedCards[0].cancolor==0: self.frozen = True self.topPile = self.selectedCards[0].cancolor self.topPileV = self.selectedCards[0].canvalue self.newTop() if self.cardsInHand()==0: self.roundOver = True else: self.nextTurn() self.pushHistory("Discard a card") self.pileLayout() return True else: return False else: return False def passTurn(self): """ Pass without discarding. Legal only if player has exactly one card. """ if self.cardsInHand() == 1: self.nextTurn() self.pushHistory("Pass turn") if self.idx<0: self.roundOver=True return True else: return False def newTop(self): """ Marks the selected card as the top of the discard pile """ self.selectedCards[0].temp = 1 for c in self.cardGroup.cards: c.isTop = False for c in self.cardGroup.cards: if c.temp == 1: c.isTop = True for c in self.cardGroup.cards: c.temp = 0 def canPickItUp(self): """ Can the current player pick up the pile given the current selected/staged cards and the state of the round? Returns True or False. """ result = False stage_cols = [] select_cols = [] stage_points = 0 select_points = 0 select_topmatch = 0 select_wild = 0 select_other = 0 #Get value of staged cards if self.stagedCards(): for c in self.cardGroup.cards: if c.location in range(4,16): stage_cols.append(c.cancolor) stage_points += c.canvalue #Get value of selected cards if len(self.selectedCards)>0: for i in range(len(self.selectedCards)): select_cols.append(self.selectedCards[i].cancolor) select_points += self.selectedCards[i].canvalue if self.selectedCards[i].cancolor == self.topPile: select_topmatch += 1 elif self.selectedCards[i].cancolor == 0: select_wild += 1 else: select_other += 1 if self.options.counttop: top_points = self.topPileV else: top_points = 0 #If the top of the pile matches something in the stage but nothing in #selection, OK to pick up (check for points if this is the first meld) if (self.topPile in stage_cols) & (self.topPile not in select_cols): if (self.hasMelded() or (top_points + stage_points >= self.pointsToGoIn())): result = True #If nothing in the stage matches but there are matches in the selection, OK to pick up if (self.topPile not in stage_cols) & (select_topmatch >= 1) & (select_wild <= select_topmatch) & (select_other==0) & (len(select_cols)>=2): if (self.hasMelded() or ((top_points + stage_points + select_points) >= self.pointsToGoIn())): result = True #Disallow wild cards if the pile is frozen if (self.frozen | self.options.freezealways | (not self.options.pilewithwild) | (self.options.gonatural & (not self.hasMelded()))) & (select_wild>0): result = False #If top of pile matches a meld and it's not frozen, OK to pick up if ((self.numMelded(self.topPile,self.curTeam())[0]>0) & (not (self.frozen|self.options.freezealways)) & self.options.piletocanasta): result = True #Disallow if if picking up the pile would leave player with no cards in their hand. if (self.cardsInStage() + len(self.selectedCards)) == self.cardsInHand(): result = False return result def PickPile(self): """ Check if it is legal to pick up the pile. If so, transfer all pile cards to the current player's hadn. """ if (self.idx<0) & (not self.options.runempty): self.roundOver=True return True if (self.topPile not in [-1,0,100]) & self.canPickItUp(): #Assign pile cards to current hand, and meld the top card psize = self.pileSize() self.lastMelded = [] for c in self.cardGroup.cards: c.nofreeze = False if c.rotated==1: if DEBUGGER: print "de-rotating",c.value,c.color c.turn() if c.location == 1: if c.cancolor==100: c.location = 1000*self.curTeam() else: c.location = 100*(self.turn+1) if self.invisible: c.flip() if c.isTop: c.location = 100*(self.curTeam()) + c.cancolor c.isTop = False self.lastMelded.append(c) #Meld any selected cards, then meld the stage for c in self.selectedCards: c.location = 100*(self.curTeam()) + self.topPile self.lastMelded.append(c) self.meldStage() self.pushHistory("Pick up pile") self.turnstart = True self.frozen = False self.topPile = -1 self.meldLayout() self.handLayout() self.redThreeLayout() return True else: return False def nextTurn(self): """ Pass play to the next position. """ self.clearStage() oldturn = self.turn self.turn += 1 if self.turn == 4: self.turn = 0 self.turnstart = False self.selectedCards = [] self.lastMelded = [] self.let_go_out = True ############################ #Scoring ############################# def cardPoints(self,team): """ Face value of all melded cards. """ result = 0 for c in self.cardGroup.cards: if (c.location > 100*team) & (c.location < 100*(team+1)): result += c.canvalue return result def handPoints(self,team): """ Face value of all cards in hand, evaluated at the end of the round. """ result = 0 for c in self.cardGroup.cards: if (c.location == 100*team) | (c.location == 100*(team+2)): result += c.canvalue return result def specialPoints(self,team,params): """ Points from canastas, red threes, and going out. """ red_canastas = 0 black_canastas = 0 red_threes = 0 going_out = 0 wc_canasta = 0 super_wc = False meldRange = range(100*team + 3,100*team+16) for i in meldRange: jokers = 0 meldlength = 0 dirty = False for c in self.cardGroup.cards: if c.location == i: meldlength += 1 if c.cancolor==0: dirty = True if c.canvalue==50: jokers += 1 if meldlength >= 7: if i % 100 == 15: wc_canasta += 1 if jokers in [0,4]: super_wc = True if dirty: black_canastas += 1 else: red_canastas += 1 for c in self.cardGroup.cards: if c.location == 1000*team: red_threes += 1 if red_threes == 4: red_threes *= 2 if (self.cardPoints(team)==0) & (self.options.red3penalty): red_threes *= -1 if ((self.turn % 2) + 1 == team) & (self.idx>0): going_out = 1 if super_wc: wc_bonus = self.options.wildcanastabonus[0] - 500 else: wc_bonus = self.options.wildcanastabonus[1] - 500 if self.concealed: going_out+=1 result = 500*red_canastas + 300*black_canastas + 100*red_threes + 100*going_out + wc_bonus*wc_canasta if params: return [red_canastas,black_canastas,red_threes,going_out,wc_canasta] else: return result ############################ #Command Interpreter ############################# def addChat(self,command): """ Chats are submitted through the interpreter like any game command, but they are passed to this method for processing. """ ccode = command.action arglist = command.arglist token = command.token if (len(arglist)>1): who = arglist[1] elif token: who = "" else: who = self.turn if token: chatline = token[0] + arglist[0] + token[0] else: print "attempting to write chat",self.playernames,who,ccode,arglist,token chatline = "[" + self.playernames[who] + "] " + arglist[0] print chatline #DEBUGGING: use the hash-bang to print variable states in the chat window. #This is a huge security hole, so it should be disabled for distribution. if (arglist[0][0:2] == "#!") & DEBUGGER: try: chatlines = [str(eval(arglist[0][2:len(arglist[0])]))] except: chatlines = ["no such variable!"] elif (len(chatline)>35) & (" " not in arglist[0]): chatlines = [chatline[0:33]] + [chatline[33:len(chatline)]] else: chatlines = [chatline] for c in chatlines: try:self.chatlist.append(c) except:retcode = False if len(self.chatlist)>10: self.chatlist = self.chatlist[1:] return True def execCode(self,ccommand,invisible=True): """ All changes to the game state are made by executing command codes through this method. Codes are generated by either the human input module or the computer player, and the server determines which codes will be passed to the game engine for execution. The interpreter saves the last command and argument list that was submitted, as well as a return code indicating whether the action was successful. These are all retrieved by the status commands. """ ccode = ccommand.action arglist = ccommand.arglist token = ccommand.token target = None if ccode == DEBUG: self.roundOver = True self.team1score = 10000 retcode = True if invisible: self.invisible = True else: self.invisible = False if ccode == NO_PLAY: retcode = False elif ccode == CHAT: retcode = self.addChat(ccommand) elif ccode == BLOCK_OUT: self.let_go_out = False retcode = True else: if DEBUGGER: print "command",ccommand.action,ccommand.arglist,ccommand.token if (len(arglist)>0): hand = [] counter = 0 for c in self.cardGroup.cards: if c.location == (1+self.turn)*100: hand.append(c) hand.sort(key=lambda obj: obj.value) hand.sort(key=lambda obj: obj.color) hand.sort(key=lambda obj: obj.canvalue) hand.sort(key=lambda obj: obj.cancolor) self.selectedCards = [] counter = 0 for c in hand: counter += 1 if counter in arglist: if DEBUGGER: print ["in argument list:",c.color,c.value] self.selectedCards.append(c) if (ccode in [TO_STAGE,MELD_CARDS]) & (len(self.selectedCards)==1): if (self.selectedCards[0].cancolor==0): if token: target = token[0] else: target = self.guessWildTarget() if target==0: target = None if DEBUGGER: print "Inserting the target",target if ccode == TO_STAGE: retcode = self.meldSelection(target=target,stage=True) if retcode: self.pushHistory("Stage cards") self.selectedCards = [] self.stageLayout() elif ccode == CLEAR_STAGE: retcode = self.clearStage() elif ccode == MELD_CARDS: retcode = self.meldSelection(target=target) if retcode: self.pushHistory("Meld cards") self.selectedCards = [] self.meldLayout() if self.turn == self.myPos: self.myPosMelded = True elif ccode in TLIST: retcode = self.meldWild(ccode) elif ccode == DRAW_CARD: retcode = self.DrawCard() elif ccode == PASS_TURN: retcode = self.passTurn() elif ccode == PICK_PILE: retcode = self.PickPile() elif ccode == DISCARD_CARD: retcode = self.DiscardCard() elif ccode == QUIT_GAME: retcode = True if token: if (ccode not in TLIST): if isinstance(token,list): if retcode & (len(token)==2) & (token[0]==CHAT): chatcommand = CanastaCommand(CHAT,[token[1]],[]) self.addChat(chatcommand) self.lastCommand = ccommand.action self.lastReturn = retcode self.lastArgs = arglist self.lastToken = token
class DeckOfCards: def initKlondike(self): self.mode = self.NOTHING cg = self.cardGroup self.selectionRect = pygame.Rect((0,0,0,0)) self.selectionCards = [] cg.collectAll(15,15) cg.shuffle() gt = cg.getCardAt cards = 0 x = 10 y = 140 idx = 51 for cols in range(7): for hc in range(cards): c = gt(idx) idx-=1 c.rect.x = x c.rect.y = y cg.dropCard(c) y+=20 c = gt(idx) idx-=1 c.flip() c.rect.x = x c.rect.y = y cg.dropCard(c) cards+=1 x+=90 y=140 NOTHING = 0 DRAW_SELECTION = 1 CARD_SELECTED = 2 SELECTION_SELECTED = 3 SELECTION_SPREAD_INIT = 4 SELECTION_SPREAD = 5 MAX_HISTORY = 30 history = [] def clearHistory(self): self.history = [] def pushHistory(self,desc): #print "Pushing: %s" % desc while len(self.history) > self.MAX_HISTORY: self.history.pop(0) # store get the z-order cards = self.cardGroup.cards[:] # store card info cardinfo = [] for c in cards: info = [] info.append(c.side) info.append(c.rect.topleft) info.append(c.child) info.append(c.parent) info.append(c.selected) cardinfo.append(info) if len(self.selectionCards): selrect = pygame.Rect(self.selectionRect) selcards = self.selectionCards[:] else: selrect = pygame.Rect((0,0,0,0)) selcards = [] self.history.append([cards,cardinfo,selrect,selcards,desc]) def popHistory(self): if not len(self.history): return hi = self.history.pop() #print "Popping: %s" % hi[4] cards = hi[0] i = 0 for ci in hi[1]: cards[i].setSide(ci[0]) cards[i].rect.topleft = ci[1] cards[i].child = ci[2] cards[i].parent = ci[3] cards[i].selected = ci[4] i+=1 self.cardGroup.cards = cards self.selectionRect = hi[2] self.selectionCards = hi[3] def updateSelectionRect(self): r = None for c in self.selectionCards: if not r: r = pygame.Rect(c.rect) else: r.union_ip(c.rect) r.x-=3 r.y-=3 r.width+=6 r.height+=6 self.selectionRect = r def shuffleSelection(self): if len(self.selectionCards): rectbuf = [] for c in self.selectionCards: rectbuf.append(pygame.Rect(c.rect)) random.shuffle(self.selectionCards) for i in range(len(rectbuf)): self.selectionCards[i].rect = rectbuf[i] self.cardGroup.popCards(self.selectionCards) text = [ "DeckOfCards v1.0", "-----------------------", "F1 - Display this help text.", "F2 - Collect cards and shuffle deck.", "F3 - Setup for Klondike solitaire.", "Arrow keys - Align selected cards.", "Left mouse - Move or select cards.", "Right mouse - Flip single or selected.", "Middle mouse - Pick single card.", "Mouse click + shift - Collect selected cards.", "Mouse drag + shift - Layout selected cards.", "Ctrl+T - Toggle sticky cards.", "Ctrl+S - Shuffle selected cards.", "Ctrl+Z - Undo latest action.", "-----------------------", "press any key to continue"] def mainLoop(self): pygame.init() self.screen = pygame.display.set_mode((640, 480),HWSURFACE|RESIZABLE) pygame.display.set_caption('DeckOfCards - v1.0') self.selectedCard = None self.selectionRect = pygame.Rect((0,0,0,0)) self.selectionCards = [] ci = CardImages() cards = [] for i in range(0,52): cards.append(Card(ci.getCardNbr(i),ci.getBack(),30,30)) #second deck #for i in range(0,52): # cards.append(Card(ci.getCardNbr(i),ci.getBack(),15,15)) self.cardGroup = CardGroup(cards) self.cardGroup.shuffle() self.mode = self.NOTHING popsingle = 0 self.helptext = pygame.Surface((380,420),1).convert() self.helptext.fill((0x0, 0x0, 0x0)) self.helptext.set_alpha(200); self.helptextRect = self.helptext.get_rect() font = pygame.font.Font("FreeSans.ttf", 18) ty = 8 for t in self.text: img = font.render(t, 1, (0xff, 0xff, 0)) r = img.get_rect() r.top = ty r.centerx = self.helptextRect.centerx ty += 25 self.helptext.blit(img,r.topleft) viewhelp = 1 sr = self.screen.get_rect() self.helptextRect.centerx = sr.centerx self.helptextRect.centery = sr.centery lctrlDown = 0 rctrlDown = 0 lshiftDown = 0 rshiftDown = 0 while 1: for event in pygame.event.get(): if event.type == QUIT: return elif event.type == VIDEORESIZE: self.screen = pygame.display.set_mode(event.size,HWSURFACE|RESIZABLE) if viewhelp: sr = self.screen.get_rect() self.helptextRect.centerx = sr.centerx self.helptextRect.centery = sr.centery elif event.type == KEYDOWN: #print "key = %s" % str(event.key) #print "keyname = %s" % pygame.key.name(event.key) if viewhelp: if event.key == K_ESCAPE: return viewhelp=0 continue if event.key == K_ESCAPE: return elif event.key == K_LCTRL: lctrlDown = 1 elif event.key == K_RCTRL: rctrlDown = 1 elif event.key == K_LSHIFT: lshiftDown = 1 elif event.key == K_RSHIFT: rshiftDown = 1 elif event.key == 122 and (lctrlDown or rctrlDown): self.popHistory() elif event.key == 116 and (lctrlDown or rctrlDown): if popsingle: popsingle = 0 else: popsingle = 1 elif event.key == 115 and (lctrlDown or rctrlDown): self.pushHistory("Selection shuffle") self.shuffleSelection() elif event.key == K_F1: if self.mode == self.NOTHING: sr = self.screen.get_rect() self.helptextRect.centerx = sr.centerx self.helptextRect.centery = sr.centery viewhelp = 1 elif event.key == K_F2: self.pushHistory("F2") self.selectionRect = pygame.Rect((0,0,0,0)) self.selectionCards = [] self.cardGroup.collectAll(30,30) self.cardGroup.shuffle() elif event.key == K_F3: self.pushHistory("Setup Klondike") self.initKlondike() elif event.key == K_LEFT: if len(self.selectionCards): self.pushHistory("AlignLeft") left = self.selectionCards[0].rect.left for c in self.selectionCards: if c.rect.left<left: left = c.rect.left for c in self.selectionCards: c.rect.left = left self.updateSelectionRect() elif event.key == K_RIGHT: if len(self.selectionCards): self.pushHistory("AlignRight") right = self.selectionCards[0].rect.right for c in self.selectionCards: if c.rect.right>right: right = c.rect.right for c in self.selectionCards: c.rect.right = right self.updateSelectionRect() elif event.key == K_UP: if len(self.selectionCards): self.pushHistory("AlignUp") top = self.selectionCards[0].rect.top for c in self.selectionCards: if c.rect.top<top: top = c.rect.top for c in self.selectionCards: c.rect.top = top self.updateSelectionRect() elif event.key == K_DOWN: if len(self.selectionCards): self.pushHistory("AlignDown") bottom = self.selectionCards[0].rect.bottom for c in self.selectionCards: if c.rect.bottom>bottom: bottom = c.rect.bottom for c in self.selectionCards: c.rect.bottom = bottom self.updateSelectionRect() elif event.type == KEYUP: if event.key == K_LCTRL: lctrlDown = 0 elif event.key == K_RCTRL: rctrlDown = 0 elif event.key == K_LSHIFT: lshiftDown = 0 elif event.key == K_RSHIFT: rshiftDown = 0 elif event.type == MOUSEBUTTONDOWN and viewhelp == 0: if self.mode == self.NOTHING and (event.button in [1,2,3]): #Check if we are inside selection. if self.selectionRect.width > 0 and self.selectionRect.height > 0: if self.selectionRect.collidepoint(event.pos[0],event.pos[1]): if lshiftDown or rshiftDown: if len(self.selectionCards) >= 2: self.pushHistory("Collecting/spreading selection") cx = self.selectionCards[0].rect.centerx cy = self.selectionCards[0].rect.centery for c in self.selectionCards: c.rect.centerx = cx c.rect.centery = cy self.updateSelectionRect() if event.button == 3: self.selectionCards.reverse() for c in self.selectionCards: c.flip() self.cardGroup.popCards(self.selectionCards) pygame.mouse.set_pos((cx,cy)) self.mode = self.SELECTION_SPREAD_INIT else: self.pushHistory("Pop/flip selection") self.mode = self.SELECTION_SELECTED if event.button == 3: self.selectionCards.reverse() for c in self.selectionCards: c.flip() self.cardGroup.popCards(self.selectionCards) if self.mode == self.NOTHING: if len(self.selectionCards): self.pushHistory("Drop selection cards") self.cardGroup.popCards(self.selectionCards) self.cardGroup.dropCards(self.selectionCards) self.selectionCards = [] self.selectionRect.size=(0,0) #Check if any card is selected. if self.mode == self.NOTHING: pop = popsingle if event.button == 2: popsingle = 1 self.pushHistory("Pop/flip selected card") self.selectedCard = self.cardGroup.getCard(event.pos[0],event.pos[1],popsingle) if event.button == 2: popsingle = pop if self.selectedCard: self.mode = self.CARD_SELECTED if event.button == 3: self.selectedCard.flip() else: self.history.pop() #Init a new selection rectangle. if self.mode == self.NOTHING: self.selectionStart = (event.pos[0],event.pos[1]) self.mode = self.DRAW_SELECTION elif event.type == MOUSEBUTTONUP and viewhelp == 0: if self.mode == self.SELECTION_SELECTED: self.mode = self.NOTHING elif self.mode == self.SELECTION_SPREAD: self.mode = self.NOTHING elif self.mode == self.SELECTION_SPREAD_INIT: self.mode = self.NOTHING elif self.mode == self.CARD_SELECTED: #self.pushHistory("Drop card") self.cardGroup.dropCard(self.selectedCard) self.selectedCard = None self.mode = self.NOTHING elif self.mode == self.DRAW_SELECTION: #see if we have selected any cards if self.selectionRect.width > 0 and self.selectionRect.height > 0: self.pushHistory("Select cards") self.selectionRect,self.selectionCards = self.cardGroup.getCards(self.selectionRect) if not len(self.selectionCards): self.history.pop() self.mode = self.NOTHING elif event.type == MOUSEMOTION and viewhelp == 0: if event.buttons[0] or event.buttons[1] or event.buttons[2]: if self.mode == self.SELECTION_SELECTED: #Handle the drag of a selection rectangle. if len(self.selectionCards): self.selectionRect.topleft = (self.selectionRect.x+event.rel[0],self.selectionRect.y+event.rel[1]) for c in self.selectionCards: c.move(event.rel[0],event.rel[1]); elif self.mode == self.SELECTION_SPREAD_INIT: self.mode = self.SELECTION_SPREAD elif self.mode == self.SELECTION_SPREAD: #Handle the spread of a selection rectangle. l = len(self.selectionCards) if l>=2: c = self.selectionCards[l-1] fc = self.selectionCards[0] dx = event.pos[0]-fc.rect.centerx dy = event.pos[1]-fc.rect.centery if abs(dx) > abs(dy): cnt = 0 d = float(dx)/float(l-1) for mc in self.selectionCards: mc.rect.centery = fc.rect.centery mc.rect.centerx = fc.rect.centerx+int(d*cnt) cnt += 1 c.rect.centerx = event.pos[0] c.rect.centery = fc.rect.centery else: cnt = 0 d = float(dy)/float(l-1) for mc in self.selectionCards: mc.rect.centerx = fc.rect.centerx mc.rect.centery = fc.rect.centery+int(d*cnt) cnt += 1 c.rect.centery = event.pos[1] c.rect.centerx = fc.rect.centerx r = pygame.Rect(c.rect) r.union_ip(self.selectionCards[0].rect) r.x-=3 r.y-=3 r.width+=6 r.height+=6 self.selectionRect = r elif self.mode == self.CARD_SELECTED: #Handle the drag of a selected card. self.selectedCard.move(event.rel[0],event.rel[1]); elif self.mode == self.DRAW_SELECTION: #Handle the selection rectangle #self.selectionRect.size=(event.pos[0]-self.selectionRect.x,event.pos[1]-self.selectionRect.y) if event.pos[0] <= self.selectionStart[0]: self.selectionRect.x = self.selectionStart[0]-(self.selectionStart[0]-event.pos[0]) self.selectionRect.width = self.selectionStart[0]-event.pos[0] else: self.selectionRect.x=self.selectionStart[0] self.selectionRect.width=event.pos[0]-self.selectionStart[0] if event.pos[1] <= self.selectionStart[1]: self.selectionRect.y = self.selectionStart[1]-(self.selectionStart[1]-event.pos[1]) self.selectionRect.height = self.selectionStart[1]-event.pos[1] else: self.selectionRect.y=self.selectionStart[1] self.selectionRect.height=event.pos[1]-self.selectionStart[1] # DRAWING self.screen.fill((0x00, 0xb0, 0x00)) self.cardGroup.draw(self.screen) if self.selectionRect.width > 0 and self.selectionRect.height > 0: pygame.draw.rect(self.screen,(0xff,0xff,0x00),self.selectionRect,3) if viewhelp: self.screen.blit(self.helptext,self.helptextRect.topleft) pygame.display.flip()