diff plugins/plugin_misc_tarot.py @ 95:be206a3d1a9b

Tarot game: score calculation - plugin tarot: score validity is now checked (some of attackers and defender must equal 91) - plugin tarot: new signal tarotGameScore to give the scores and winners/loosers - wix: score are now displayed at the end of the game - xml_tools: 'fixed' type is now managed in dataForm2xml
author Goffi <goffi@goffi.org>
date Wed, 02 Jun 2010 15:57:23 +0930
parents 1eb5ccead43c
children c8518b9a8025
line wrap: on
line diff
--- a/plugins/plugin_misc_tarot.py	Tue Jun 01 18:16:15 2010 +0930
+++ b/plugins/plugin_misc_tarot.py	Wed Jun 02 15:57:23 2010 +0930
@@ -131,6 +131,7 @@
         host.bridge.addSignal("tarotGameShowCards", ".communication", signature='ssa(ss)a{ss}s') #args: room_jid, type ["chien", "poignée",...], cards, data[dict], profile
         host.bridge.addSignal("tarotGameCardsPlayed", ".communication", signature='ssa(ss)s') #args: room_jid, player, type ["chien", "poignée",...], cards, data[dict], profile
         host.bridge.addSignal("tarotGameYourTurn", ".communication", signature='ss') #args: room_jid, profile
+        host.bridge.addSignal("tarotGameScore", ".communication", signature='ssasass') #args: room_jid, xml_data, winners (list of nicks), loosers (list of nicks), profile
         self.deck_ordered = []
         for value in ['excuse']+map(str,range(1,22)):
             self.deck_ordered.append(("atout",value))
@@ -184,6 +185,28 @@
         contrat_elt.addChild(form.toElement())
         return contrat_elt
 
+    def __give_scores(self, scores, winners, loosers):
+        """Create an element to give scores
+        @param scores: unicode (can contain line feed)
+        @param winners: list of unicode nicks of winners
+        @param loosers: list of unicode nicks of loosers"""
+
+        score_elt = domish.Element(('','score'))
+        form = data_form.Form('form', title=_('scores'))
+        for line in scores.split('\n'):
+            field = data_form.Field('fixed', value = line)
+            form.addField(field)
+        score_elt.addChild(form.toElement())
+        for winner in winners:
+            winner_elt = domish.Element(('','winner'))
+            winner_elt.addContent(winner)
+            score_elt.addChild(winner_elt)
+        for looser in loosers:
+            looser_elt = domish.Element(('','looser'))
+            looser_elt.addContent(looser)
+            score_elt.addChild(looser_elt)
+        return score_elt
+
     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
@@ -213,15 +236,21 @@
             if (card.suit == suit_asked or card.suit == "atout") and card > strongest:
                 strongest = card
                 winner = player
-        assert (winner)
+        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"""
+        """give a low card to other team and keep excuse if trick is lost
+        @param game_data: data of the game
+        @param played: cards currently on the table
+        @param winner: nick of the trick winner"""
         #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"))
+
+        #we first check if the Excuse was already player
+        #and if somebody is waiting for a card
         for player in game_data['players']:
             if players_data[player]['wait_for_low']:
                 #the excuse owner has to give a card to somebody
@@ -233,13 +262,14 @@
                             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
                 return
 
         if not excuse in played:
+            #the Excuse is not on the table, nothing to do
             return
         
-        excuse_player = None
+        excuse_player = None #Who has played the Excuse ?
         for player in game_data['players']:
             if players_data[player]['played'] == excuse:
                 excuse_player = player
@@ -255,6 +285,8 @@
         owner_levees.append(excuse)
         #finally we give a low card to the trick winner
         low_card = None
+        #We look backward in cards won by the Excuse owner to
+        #find a low value card
         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]
@@ -269,15 +301,29 @@
 
 
     def __calculate_scores(self, game_data):
-        """The game is finished, time to know who won :)"""
+        """The game is finished, time to know who won :)
+        @param game_data: data of the game
+        @return: tuple with (string victory message, list of winners, list of loosers)"""
         players_data = game_data['players_data']
         levees = players_data[game_data['attaquant']]['levees']
         score = 0
         nb_bouts = 0
+        bouts = []
         for card in levees:
             if card.bout:
                 nb_bouts +=1
+                bouts.append(card.value)
             score += card.points
+        
+        #We now check if there is no bug in score calculation
+        check_score = 0
+        defenseurs = game_data['players'][:]
+        defenseurs.remove(game_data['attaquant'])
+        for defenseur in defenseurs:
+            for card in players_data[defenseur]['levees']:
+                check_score+=card.points
+        assert (score + check_score == 91)
+        
         point_limit = None
         if nb_bouts == 3:
             point_limit = 36
@@ -287,10 +333,41 @@
             point_limit = 51
         else:
             point_limit = 56
+        if game_data['contrat'] == 'Petite':
+            contrat_mult = 1
+        elif game_data['contrat'] == 'Garde':
+            contrat_mult = 2
+        elif game_data['contrat'] == 'Garde Sans':
+            contrat_mult = 4
+        elif game_data['contrat'] == 'Garde Contre':
+            contrat_mult = 6
+        else:
+            error(_('Internal error: contrat not managed (mispelled ?)'))
+
         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()
+        margin = score - point_limit
+        pdb.set_trace()
+        points_defenseur = (-margin + 25) * contrat_mult
+        winners = []
+        loosers = []
+        player_score = {}
+        for player in game_data['players']:
+            #TODO: adjust this for 3 and 5 players variants
+            #TODO: manage bonuses (petit au bout, poignée, chelem)
+            player_score[player] = points_defenseur if player != game_data['attaquant'] else points_defenseur * -3
+            players_data[player]['score'] += player_score[player] #we add score of this game to the global score
+            if player_score[player] > 0:
+                winners.append(player)
+            else:
+                loosers.append(player)
 
+        scores_str = _('The attacker (%(attaquant)s) makes %(points)i and needs to make %(point_limit)i (%(nb_bouts)s oulder%(plural)s: %(bouts)s): he %(victory)s') % {'attaquant':game_data['attaquant'], 'points':score, 'point_limit':point_limit, 'nb_bouts': nb_bouts, 'plural': 's' if nb_bouts>1 else '', 'bouts':','.join(map(str,bouts)), 'victory': 'win' if victory else 'loose'}
+        scores_str+='\n'
+        for player in game_data['players']:
+            scores_str+=_("\n--\n%(player)s:\nscore for this game ==> %(score_game)i\ntotal score ==> %(total_score)i") % {'player':player, 'score_game':player_score[player], 'total_score': players_data[player]['score']}
+        debug(scores_str)
+ 
+        return (scores_str, winners, loosers)
 
 
     def createGame(self, room_jid_param, players, profile_key='@DEFAULT@'):
@@ -312,9 +389,9 @@
             status = {}
             players_data = {}
             for player in players:
-                players_data[player] = {}
+                players_data[player] = {'score':0}
                 status[player] = "init"
-            self.games[room_jid.userhost()] = {'referee':referee, 'players':players, 'status':status, 'players_data':players_data, 'hand_size':18, 'init_player':0, 'current_player': None, 'stage': None}
+            self.games[room_jid.userhost()] = {'referee':referee, 'players':players, 'status':status, 'players_data':players_data, 'hand_size':18, 'init_player':0, 'current_player': None, 'contrat': None, 'stage': None}
             for player in players:
                 mess = self.createGameElt(jid.JID(room_jid.userhost()+'/'+player))
                 mess.firstChildElement().addChild(self.__create_started_elt(players))
@@ -377,6 +454,7 @@
         current_player = game_data['current_player']
         game_data['stage'] = "init"
         game_data['first_player'] = None #first player for the current trick
+        game_data['contrat'] = None
         hand = game_data['hand'] = {}
         hand_size = game_data['hand_size']
         chien = game_data['chien'] = []
@@ -461,6 +539,7 @@
                             best_contrat[0] = player
                             best_contrat[1] = contrat
                     debug (_("%(player)s win the bid with %(contrat)s") % {'player':best_contrat[0],'contrat':best_contrat[1]})
+                    game_data['contrat'] = best_contrat[1]
                     #Time to show the chien to everybody
                     to_jid = jid.JID(room_jid.userhost()) #FIXME: gof:
                     mess = self.createGameElt(to_jid)
@@ -484,7 +563,11 @@
                     #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(Card.from_tuples(self.__xml_to_list(elt)))
+                    list_cards = self.__xml_to_list(elt)
+                    #FIXME: gof: manage Garde Sans & Garde Contre cases
+                    players_data[elt['player']]['levees'].extend(Card.from_tuples(list_cards)) #we add the chien to attaquant's levées
+                    for card in list_cards:
+                        game_data['hand'][elt['player']].remove(card)
                     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
                     game_data['first_player'] = next_player = game_data['players'][next_player_idx]
@@ -494,7 +577,6 @@
                     self.host.profiles[profile].xmlstream.send(mess)
                 elif game_data['stage'] == "play":
                     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)
                     
                     if mess_elt['type'] == 'groupchat':
@@ -513,8 +595,8 @@
                     
                         #Did everybody played ?
                         played = [players_data[player]['played'] for player in game_data['players']]
-                        if not played.count(None):
-                            #everybody played
+                        if all(played):
+                            #everybody has played
                             winner = self.__winner(game_data)
                             debug (_('The winner of this trick is %s') % winner)
                             #the winner win the trick
@@ -525,7 +607,10 @@
                                 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)
+                                to_jid = jid.JID(room_jid.userhost()) #FIXME: gof:
+                                mess = self.createGameElt(to_jid)
+                                chien_elt = mess.firstChildElement().addChild(self.__give_scores(*self.__calculate_scores(game_data)))
+                                self.host.profiles[profile].xmlstream.send(mess)
                                 return
                             #next player is the winner
                             next_player = game_data['first_player'] = self.__next_player(game_data, winner)
@@ -541,11 +626,20 @@
             elif elt.name == 'your_turn':
                 self.host.bridge.tarotGameYourTurn(room_jid.userhost(), profile)
 
-
+            elif elt.name == 'score':
+                form_elt = elt.elements(name='x',uri='jabber:x:data').next()
+                winners = []
+                loosers = []
+                for winner in elt.elements(name='winner', uri=''):
+                    winners.append(unicode(winner))
+                for looser in elt.elements(name='looser', uri=''):
+                    loosers.append(unicode(looser))
+                form = data_form.Form.fromElement(form_elt)
+                xml_data = XMLTools.dataForm2xml(form)
+                self.host.bridge.tarotGameScore(room_jid.userhost(), xml_data, winners, loosers, profile)
+                
     def getHandler(self, profile):
             return CardGameHandler(self)
-   
-
 
 class CardGameHandler (XMPPHandler):
     implements(iwokkel.IDisco)