diff plugins/plugin_misc_tarot.py @ 99:63c9067a1499

Tarot game: invalid cards management - tarot plugin: card validity check, new signal tarotGameInvalidCards - wix: when an invalid cards signal is received, the cards are back in the hand, and the state change so the player as to play again.
author Goffi <goffi@goffi.org>
date Fri, 18 Jun 2010 15:19:32 +0800
parents dd556233a1b1
children 783e9d6980ec
line wrap: on
line diff
--- a/plugins/plugin_misc_tarot.py	Thu Jun 03 17:43:49 2010 +0930
+++ b/plugins/plugin_misc_tarot.py	Fri Jun 18 15:19:32 2010 +0800
@@ -132,6 +132,7 @@
         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
+        host.bridge.addSignal("tarotGameInvalidCards", ".communication", signature='ssa(ss)a(ss)s') #args: room_jid, game phase, played_cards, invalid_cards, profile
         self.deck_ordered = []
         for value in ['excuse']+map(str,range(1,22)):
             self.deck_ordered.append(Card(("atout",value)))
@@ -207,6 +208,19 @@
             score_elt.addChild(looser_elt)
         return score_elt
 
+    def __invalid_cards_elt(self, played_cards, invalid_cards, game_phase):
+        """Create a element for invalid_cards error
+        @param list_cards: list of Card
+        @param game_phase: phase of the game ['ecart', 'play']"""
+        error_elt = domish.Element(('','error'))
+        played_elt = self.__card_list_to_xml(played_cards, 'played')
+        invalid_elt = self.__card_list_to_xml(invalid_cards, 'invalid')
+        error_elt['type'] = 'invalid_cards'
+        error_elt['phase'] = game_phase
+        error_elt.addChild(played_elt)
+        error_elt.addChild(invalid_elt)
+        return error_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
@@ -325,8 +339,6 @@
         if game_data['contrat'] == "Garde Contre":
             for card in game_data['chien']:
                 check_score+=card.points
-        if ( score + check_score != 91 ):
-            pdb.set_trace()
         assert (score + check_score == 91)
         
         point_limit = None
@@ -373,6 +385,63 @@
  
         return (scores_str, winners, loosers)
 
+    def __invalid_cards(self, game_data, cards):
+        """Checks that the player has the right to play what he wants to
+        @param game_data: Game data
+        @param cards: cards the player want to play
+        @return forbidden_cards cards or empty list if cards are ok"""
+        forbidden_cards = []
+        if game_data['stage'] == 'ecart':
+            for card in cards:
+                if card.bout or card.value=="roi":
+                    forbidden_cards.append(card)
+                #TODO: manage case where atouts (trumps) are in the dog
+        elif game_data['stage'] == 'play':
+            biggest_atout = None
+            suit_asked = None
+            players = game_data['players']
+            players_data = game_data['players_data']
+            idx = players.index(game_data['first_player'])
+            current_idx = game_data['current_player']
+            current_player = players[current_idx]
+            if idx == current_idx:
+                #the player is the first to play, he can play what he wants
+                return forbidden_cards
+            while (idx != current_idx):
+                player = players[idx]
+                played_card = players_data[player]['played']
+                if not suit_asked and played_card.value != "excuse":
+                    suit_asked = played_card.suit
+                if played_card.suit == "atout" and played_card > biggest_atout:
+                    biggest_atout = played_card
+                idx = (idx + 1) % len(players)
+            has_suit = False #True if there is one card of the asked suit in the hand of the player
+            has_atout = False
+            biggest_hand_atout = None
+
+            for hand_card in game_data['hand'][current_player]:
+                if hand_card.suit == suit_asked:
+                    has_suit = True
+                if hand_card.suit == "atout":
+                    has_atout = True
+                if hand_card.suit == "atout" and hand_card > biggest_hand_atout:
+                    biggest_hand_atout = hand_card
+
+            assert len(cards) == 1
+            card = cards[0]
+            if card.suit != suit_asked and has_suit and card.value != "excuse":
+                forbidden_cards.append(card)
+                return forbidden_cards
+            if card.suit != suit_asked and card.suit != "atout" and has_atout:
+                forbidden_cards.append(card)
+                return forbidden_cards
+            if card.suit == "atout" and card < biggest_atout and biggest_hand_atout > biggest_atout and card.value != "excuse":
+                forbidden_cards.append(card)
+        else:
+            error(_('Internal error: unmanaged game stage'))
+        return forbidden_cards
+
+
     def __start_play(self, room_jid, game_data, profile):
         """Start the game (tell to the first player after dealer to play"""
         game_data['stage'] = "play"
@@ -584,10 +653,17 @@
 
             elif elt.name == 'cards_played':
                 if game_data['stage'] == "ecart":
-                    #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
                     list_cards = Card.from_tuples(self.__xml_to_list(elt))
+                    #we now check validity of card
+                    invalid_cards = self.__invalid_cards(game_data, list_cards)
+                    if invalid_cards:
+                        mess = self.createGameElt(jid.JID(room_jid.userhost()+'/'+elt['player']))
+                        mess.firstChildElement().addChild(self.__invalid_cards_elt(list_cards, invalid_cards, game_data['stage']))
+                        self.host.profiles[profile].xmlstream.send(mess)
+                        return
+
                     #FIXME: gof: manage Garde Sans & Garde Contre cases
                     players_data[elt['player']]['levees'].extend(list_cards) #we add the chien to attaquant's levées
                     for card in list_cards:
@@ -602,7 +678,13 @@
                     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
+                        #we first check validity of card
+                        invalid_cards = self.__invalid_cards(game_data, cards)
+                        if invalid_cards:
+                            mess = self.createGameElt(jid.JID(room_jid.userhost()+'/'+current_player))
+                            mess.firstChildElement().addChild(self.__invalid_cards_elt(cards, invalid_cards, game_data['stage']))
+                            self.host.profiles[profile].xmlstream.send(mess)
+                            return
                         #the card played is ok, we forward it to everybody
                         #first we remove it from the hand and put in on the table
                         game_data['hand'][current_player].remove(cards[0])
@@ -657,6 +739,15 @@
                 form = data_form.Form.fromElement(form_elt)
                 xml_data = XMLTools.dataForm2xml(form)
                 self.host.bridge.tarotGameScore(room_jid.userhost(), xml_data, winners, loosers, profile)
+            elif elt.name == 'error':
+                if elt['type'] == 'invalid_cards':
+                    played_cards = self.__xml_to_list(elt.elements(name='played',uri='').next())
+                    invalid_cards = self.__xml_to_list(elt.elements(name='invalid',uri='').next())
+                    self.host.bridge.tarotGameInvalidCards(room_jid.userhost(), elt['phase'], played_cards, invalid_cards, profile)
+                else:
+                    error (_('Unmanaged error type: %s') % elt['type'])
+            else:
+                error (_('Unmanaged card game element: %s') % elt.name)
                 
     def getHandler(self, profile):
             return CardGameHandler(self)