# HG changeset patch # User Goffi # Date 1275381975 -34200 # Node ID 1eb5ccead43c899af451534b897829c300d54238 # Parent 2f87651a5ad8ea03fb061c38daa76e0fa48ae22a Tarot game: basic trick - plugin tarot: it's now possible to make a basic game until the end \o/. Score are calculated but not sent to players yet. diff -r 2f87651a5ad8 -r 1eb5ccead43c frontends/wix/card_game.py --- a/frontends/wix/card_game.py Sun May 30 15:33:08 2010 +0930 +++ b/frontends/wix/card_game.py Tue Jun 01 18:16:15 2010 +0930 @@ -33,7 +33,7 @@ MIN_WIDTH = 950 #Minimum size of the panel MIN_HEIGHT = 500 -suits_order = ['pique', 'coeur', 'trefle', 'carreau', 'atout'] #I have swith the usual order 'trefle' and 'carreau' because card are more easy to see if couleur change (black, red, black, red) +suits_order = ['pique', 'coeur', 'trefle', 'carreau', 'atout'] #I have swith the usual order 'trefle' and 'carreau' because card are more easy to see if suit colour change (black, red, black, red) values_order = map(str,range(1,11))+["valet","cavalier","dame","roi"] class Card(): diff -r 2f87651a5ad8 -r 1eb5ccead43c plugins/plugin_misc_tarot.py --- a/plugins/plugin_misc_tarot.py Sun May 30 15:33:08 2010 +0930 +++ b/plugins/plugin_misc_tarot.py Tue Jun 01 18:16:15 2010 +0930 @@ -59,6 +59,61 @@ "description": _("""Implementation of Tarot card game""") } +suits_order = ['pique', 'coeur', 'trefle', 'carreau', 'atout'] #I have swith the usual order 'trefle' and 'carreau' because card are more easy to see if suit colour change (black, red, black, red) +values_order = map(str,range(1,11))+["valet","cavalier","dame","roi"] + +class Card(): + """This class is used to represent a car logically""" + #TODO: move this in a library in tools, and share this with frontends (e.g. card_game in wix use the same class) + + def __init__(self, tuple_card): + """@param tuple_card: tuple (suit, value)""" + self.suit, self.value = tuple_card + self.bout = True if self.suit=="atout" and self.value in ["1","21","excuse"] else False + if self.bout or self.value == "roi": + self.points = 4.5 + elif self.value == "dame": + self.points = 3.5 + elif self.value == "cavalier": + self.points = 2.5 + elif self.value == "valet": + self.points = 1.5 + else: + self.points = 0.5 + + def get_tuple(self): + return (self.suit,self.value) + + @staticmethod + def from_tuples(tuple_list): + result = [] + for card_tuple in tuple_list: + result.append(Card(card_tuple)) + return result + + def __cmp__(self, other): + if other == None: + return 1 + if self.suit != other.suit: + idx1 = suits_order.index(self.suit) + idx2 = suits_order.index(other.suit) + return idx1.__cmp__(idx2) + if self.suit == 'atout': + if self.value == other.value == 'excuse': + return 0 + if self.value == 'excuse': + return -1 + if other.value == 'excuse': + return 1 + return int(self.value).__cmp__(int(other.value)) + #at this point we have the same suit which is not 'atout' + idx1 = values_order.index(self.value) + idx2 = values_order.index(other.value) + return idx1.__cmp__(idx2) + + def __str__(self): + return "[%s,%s]" % (self.suit, self.value) + class Tarot(): def __init__(self, host): @@ -129,11 +184,114 @@ contrat_elt.addChild(form.toElement()) return contrat_elt - def __next_player(self, game_data): - """It's next player turn - Increment player number & return player name""" - pl_idx = game_data['current_player'] = (game_data['current_player'] + 1) % len(game_data['players']) - return game_data['players'][pl_idx] + def __next_player(self, game_data, next_pl = None): + """Increment player number & return player name + @param next_pl: if given, then next_player is forced to this one + """ + if next_pl: + game_data['current_player'] = game_data['players'].index(next_pl) + return next_pl + else: + pl_idx = game_data['current_player'] = (game_data['current_player'] + 1) % len(game_data['players']) + return game_data['players'][pl_idx] + + def __winner(self, game_data): + """give the nick of the player who win this trick""" + players_data = game_data['players_data'] + first = game_data['first_player'] + first_idx = game_data['players'].index(first) + suit_asked = None + strongest = None + winner = None + for idx in [(first_idx + i) % 4 for i in range(4)]: + player = game_data['players'][idx] + card = players_data[player]['played'] + if card.value == "excuse": + continue + if suit_asked == None: + suit_asked = card.suit + if (card.suit == suit_asked or card.suit == "atout") and card > strongest: + strongest = card + winner = player + assert (winner) + return winner + + def __excuse_hack(self, game_data, played, winner): + """give a low card to other team and keep excuse if trick is lost""" + #TODO: manage the case where excuse is played on the last trick (and lost) + #TODO: gof: manage excuse (fool) + players_data = game_data['players_data'] + excuse = Card(("atout","excuse")) + for player in game_data['players']: + if players_data[player]['wait_for_low']: + #the excuse owner has to give a card to somebody + if winner == player: + #the excuse owner win the trick, we check if we have something to give + for card in played: + if card.points == 0.5: + pl_waiting = players_data[player]['wait_for_low'] + played.remove(card) + players_data[pl_waiting]['levees'].append(card) + debug (_('Player %(excuse_owner)s give %(card_waited)s to %(player_waiting)s for Excuse compensation') % {"excuse_owner":player, "card_waited": card, "player_waiting":pl_waiting}) + break + return + + if not excuse in played: + return + + excuse_player = None + for player in game_data['players']: + if players_data[player]['played'] == excuse: + excuse_player = player + break + + if excuse_player == winner: + return #the excuse player win the trick, nothing to do + + #first we remove the excuse from played cards + played.remove(excuse) + #then we give it back to the original owner + owner_levees = players_data[excuse_player]['levees'] + owner_levees.append(excuse) + #finally we give a low card to the trick winner + low_card = None + for card_idx in range(len(owner_levees)-1, -1, -1): + if owner_levees[card_idx].points == 0.5: + low_card = owner_levees[card_idx] + del owner_levees[card_idx] + players_data[winner]['levees'].append(low_card) + debug (_('Player %(excuse_owner)s give %(card_waited)s to %(player_waiting)s for Excuse compensation') % {"excuse_owner":excuse_player, "card_waited": low_card, "player_waiting":winner}) + break + if not low_card: #The player has no low card yet + #TODO: manage case when player never win a trick with low card + players_data[excuse_player]['wait_for_low'] = winner + debug(_("%(excuse_owner)s keep the Excuse but has not card to give, %(winner)s is waiting for one") % {'excuse_owner':excuse_player, 'winner':winner}) + + + def __calculate_scores(self, game_data): + """The game is finished, time to know who won :)""" + players_data = game_data['players_data'] + levees = players_data[game_data['attaquant']]['levees'] + score = 0 + nb_bouts = 0 + for card in levees: + if card.bout: + nb_bouts +=1 + score += card.points + point_limit = None + if nb_bouts == 3: + point_limit = 36 + elif nb_bouts == 2: + point_limit = 41 + elif nb_bouts == 1: + point_limit = 51 + else: + point_limit = 56 + victory = (score >= point_limit) + debug (_('The attacker make %(points)i and need to make %(point_limit)i (%(nb_bouts)s oulder%(plural)s): he %(victory)s') % {'points':score, 'point_limit':point_limit, 'nb_bouts': nb_bouts, 'plural': 's' if nb_bouts>1 else '', 'victory': 'won' if victory else 'lost'}) + #pdb.set_trace() + + def createGame(self, room_jid_param, players, profile_key='@DEFAULT@'): """Create a new game""" @@ -218,6 +376,7 @@ players_data = game_data['players_data'] current_player = game_data['current_player'] game_data['stage'] = "init" + game_data['first_player'] = None #first player for the current trick hand = game_data['hand'] = {} hand_size = game_data['hand_size'] chien = game_data['chien'] = [] @@ -234,6 +393,8 @@ self.host.profiles[profile].xmlstream.send(mess) players_data[player]['contrat'] = None players_data[player]['levees'] = [] #cards won + players_data[player]['played'] = None #card on the table + players_data[player]['wait_for_low'] = None #Used when a player wait for a low card because of excuse pl_idx = game_data['current_player'] = (game_data['init_player'] + 1) % len(players) #the player after the dealer start player = players[pl_idx] @@ -290,7 +451,7 @@ mess.firstChildElement().addChild(self.__ask_contrat()) self.host.profiles[profile].xmlstream.send(mess) else: - #TODO: manage "everybody pass" case + #TODO: gof: manage "everybody pass" case best_contrat = [None, "Passe"] for player in game_data['players']: contrat = players_data[player]['contrat'] @@ -314,7 +475,6 @@ elif elt.name == 'chien': #we have received the chien debug (_("tarot: chien received")) data = {"attaquant":elt['attaquant']} - players_data = game_data['players_data'] game_data['stage'] = "ecart" game_data['attaquant'] = elt['attaquant'] self.host.bridge.tarotGameShowCards(room_jid.userhost(), "chien", self.__xml_to_list(elt), data, profile) @@ -324,10 +484,10 @@ #TODO: check validity of écart (no king, no oulder, cards must be in player hand) #TODO: show atouts (trumps) if player put some in écart assert (game_data['attaquant'] == elt['player']) #TODO: throw an xml error here - players_data[elt['player']]['levees'].extend(self.__xml_to_list(elt)) + players_data[elt['player']]['levees'].extend(Card.from_tuples(self.__xml_to_list(elt))) game_data['stage'] = "play" next_player_idx = game_data['current_player'] = (game_data['init_player'] + 1) % len(game_data['players']) #the player after the dealer start - next_player = game_data['players'][next_player_idx] + game_data['first_player'] = next_player = game_data['players'][next_player_idx] to_jid = jid.JID(room_jid.userhost()+"/"+next_player) #FIXME: gof: mess = self.createGameElt(to_jid) yourturn_elt = mess.firstChildElement().addElement('your_turn') @@ -336,29 +496,48 @@ current_player = game_data['players'][game_data['current_player']] #assert (elt['player'] == current_player) #TODO: throw xml error here cards = self.__xml_to_list(elt) - #TODO: check card validity and send error mess if necessary - if mess_elt['type'] != 'groupchat': + + if mess_elt['type'] == 'groupchat': + self.host.bridge.tarotGameCardsPlayed(room_jid.userhost(), elt['player'], self.__xml_to_list(elt), profile) + else: + #TODO: check card validity and send error mess if necessary #the card played is ok, we forward it to everybody - #first we remove it from the hand + #first we remove it from the hand and put in on the table game_data['hand'][current_player].remove(cards[0]) + players_data[current_player]['played'] = Card(cards[0]) #then we forward the message mess = self.createGameElt(room_jid) playcard_elt = mess.firstChildElement().addChild(elt) self.host.profiles[profile].xmlstream.send(mess) + #Did everybody played ? + played = [players_data[player]['played'] for player in game_data['players']] + if not played.count(None): + #everybody played + winner = self.__winner(game_data) + debug (_('The winner of this trick is %s') % winner) + #the winner win the trick + self.__excuse_hack(game_data, played, winner) + players_data[elt['player']]['levees'].extend(played) + #nothing left on the table + for player in game_data['players']: + players_data[player]['played'] = None + if len(game_data['hand'][current_player]) == 0: + #no card lef: the game is finished + self.__calculate_scores(game_data) + return + #next player is the winner + next_player = game_data['first_player'] = self.__next_player(game_data, winner) + else: + next_player = self.__next_player(game_data) + #finally, we tell to the next player to play - next_player = self.__next_player(game_data) to_jid = jid.JID(room_jid.userhost()+"/"+next_player) #FIXME: gof: mess = self.createGameElt(to_jid) yourturn_elt = mess.firstChildElement().addElement('your_turn') self.host.profiles[profile].xmlstream.send(mess) - else: - self.host.bridge.tarotGameCardsPlayed(room_jid.userhost(), elt['player'], self.__xml_to_list(elt), profile) - - - elif elt.name == 'your_turn': self.host.bridge.tarotGameYourTurn(room_jid.userhost(), profile)