comparison src/plugins/plugin_misc_tarot.py @ 223:86d249b6d9b7

Files reorganisation
author Goffi <goffi@goffi.org>
date Wed, 29 Dec 2010 01:06:29 +0100
parents plugins/plugin_misc_tarot.py@5a2fb307485d
children b1794cbb88e5
comparison
equal deleted inserted replaced
222:3198bfd66daa 223:86d249b6d9b7
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3
4 """
5 SAT plugin for managing xep-0045
6 Copyright (C) 2009, 2010 Jérôme Poisson (goffi@goffi.org)
7
8 This program is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 """
21
22 from logging import debug, info, warning, error
23 from twisted.words.xish import domish
24 from twisted.internet import protocol, defer, threads, reactor
25 from twisted.words.protocols.jabber import client, jid, xmlstream
26 from twisted.words.protocols.jabber import error as jab_error
27 from twisted.words.protocols.jabber.xmlstream import IQ
28 import os.path
29 import pdb
30 import random
31
32 from zope.interface import implements
33
34 from wokkel import disco, iwokkel, data_form
35 from sat.tools.xml_tools import dataForm2xml
36 from sat.tools.games import TarotCard
37
38 try:
39 from twisted.words.protocols.xmlstream import XMPPHandler
40 except ImportError:
41 from wokkel.subprotocols import XMPPHandler
42
43 MESSAGE = '/message'
44 NS_CG = 'http://www.goffi.org/protocol/card_game'
45 CG_TAG = 'card_game'
46 CG_REQUEST = MESSAGE + '/' + CG_TAG + '[@xmlns="' + NS_CG + '"]'
47
48 PLUGIN_INFO = {
49 "name": "Tarot cards plugin",
50 "import_name": "Tarot",
51 "type": "Misc",
52 "protocols": [],
53 "dependencies": ["XEP_0045"],
54 "main": "Tarot",
55 "handler": "yes",
56 "description": _("""Implementation of Tarot card game""")
57 }
58
59
60 class Tarot():
61
62 def __init__(self, host):
63 info(_("Plugin Tarot initialization"))
64 self.host = host
65 self.games={}
66 self.contrats = [_('Passe'), _('Petite'), _('Garde'), _('Garde Sans'), _('Garde Contre')]
67 host.bridge.addMethod("tarotGameCreate", ".communication", in_sign='sass', out_sign='', method=self.createGame) #args: room_jid, players, profile
68 host.bridge.addMethod("tarotGameReady", ".communication", in_sign='sss', out_sign='', method=self.newPlayerReady) #args: player, referee, profile
69 host.bridge.addMethod("tarotGameContratChoosed", ".communication", in_sign='ssss', out_sign='', method=self.contratChoosed) #args: player, referee, contrat, profile
70 host.bridge.addMethod("tarotGamePlayCards", ".communication", in_sign='ssa(ss)s', out_sign='', method=self.play_cards) #args: player, referee, cards, profile
71 host.bridge.addSignal("tarotGameStarted", ".communication", signature='ssass') #args: room_jid, referee, players, profile
72 host.bridge.addSignal("tarotGameNew", ".communication", signature='sa(ss)s') #args: room_jid, hand, profile
73 host.bridge.addSignal("tarotGameChooseContrat", ".communication", signature='sss') #args: room_jid, xml_data, profile
74 host.bridge.addSignal("tarotGameShowCards", ".communication", signature='ssa(ss)a{ss}s') #args: room_jid, type ["chien", "poignée",...], cards, data[dict], profile
75 host.bridge.addSignal("tarotGameCardsPlayed", ".communication", signature='ssa(ss)s') #args: room_jid, player, type ["chien", "poignée",...], cards, data[dict], profile
76 host.bridge.addSignal("tarotGameYourTurn", ".communication", signature='ss') #args: room_jid, profile
77 host.bridge.addSignal("tarotGameScore", ".communication", signature='ssasass') #args: room_jid, xml_data, winners (list of nicks), loosers (list of nicks), profile
78 host.bridge.addSignal("tarotGameInvalidCards", ".communication", signature='ssa(ss)a(ss)s') #args: room_jid, game phase, played_cards, invalid_cards, profile
79 self.deck_ordered = []
80 for value in ['excuse']+map(str,range(1,22)):
81 self.deck_ordered.append(TarotCard(("atout",value)))
82 for suit in ["pique", "coeur", "carreau", "trefle"]:
83 for value in map(str,range(1,11))+["valet","cavalier","dame","roi"]:
84 self.deck_ordered.append(TarotCard((suit, value)))
85
86 def createGameElt(self, to_jid, type="normal"):
87 type = "normal" if to_jid.resource else "groupchat"
88 elt = domish.Element(('jabber:client','message'))
89 elt["to"] = to_jid.full()
90 elt["type"] = type
91 elt.addElement((NS_CG, CG_TAG))
92 return elt
93
94 def __card_list_to_xml(self, cards_list, elt_name):
95 """Convert a card list to domish element"""
96 cards_list_elt = domish.Element(('',elt_name))
97 for card in cards_list:
98 card_elt = domish.Element(('','card'))
99 card_elt['suit'] = card.suit
100 card_elt['value'] = card.value
101 cards_list_elt.addChild(card_elt)
102 return cards_list_elt
103
104 def __xml_to_list(self, cards_list_elt):
105 """Convert a domish element with cards to a list of tuples"""
106 cards_list = []
107 for card in cards_list_elt.elements():
108 cards_list.append((card['suit'], card['value']))
109 return cards_list
110
111 def __create_started_elt(self, players):
112 """Create a game_started domish element"""
113 started_elt = domish.Element(('','started'))
114 idx = 0
115 for player in players:
116 player_elt = domish.Element(('','player'))
117 player_elt.addContent(player)
118 player_elt['index'] = str(idx)
119 idx+=1
120 started_elt.addChild(player_elt)
121 return started_elt
122
123 def __ask_contrat(self):
124 """Create a element for asking contrat"""
125 contrat_elt = domish.Element(('','contrat'))
126 form = data_form.Form('form', title=_('contrat selection'))
127 field = data_form.Field('list-single', 'contrat', options=map(data_form.Option, self.contrats), required=True)
128 form.addField(field)
129 contrat_elt.addChild(form.toElement())
130 return contrat_elt
131
132 def __give_scores(self, scores, winners, loosers):
133 """Create an element to give scores
134 @param scores: unicode (can contain line feed)
135 @param winners: list of unicode nicks of winners
136 @param loosers: list of unicode nicks of loosers"""
137
138 score_elt = domish.Element(('','score'))
139 form = data_form.Form('form', title=_('scores'))
140 for line in scores.split('\n'):
141 field = data_form.Field('fixed', value = line)
142 form.addField(field)
143 score_elt.addChild(form.toElement())
144 for winner in winners:
145 winner_elt = domish.Element(('','winner'))
146 winner_elt.addContent(winner)
147 score_elt.addChild(winner_elt)
148 for looser in loosers:
149 looser_elt = domish.Element(('','looser'))
150 looser_elt.addContent(looser)
151 score_elt.addChild(looser_elt)
152 return score_elt
153
154 def __invalid_cards_elt(self, played_cards, invalid_cards, game_phase):
155 """Create a element for invalid_cards error
156 @param list_cards: list of Card
157 @param game_phase: phase of the game ['ecart', 'play']"""
158 error_elt = domish.Element(('','error'))
159 played_elt = self.__card_list_to_xml(played_cards, 'played')
160 invalid_elt = self.__card_list_to_xml(invalid_cards, 'invalid')
161 error_elt['type'] = 'invalid_cards'
162 error_elt['phase'] = game_phase
163 error_elt.addChild(played_elt)
164 error_elt.addChild(invalid_elt)
165 return error_elt
166
167 def __next_player(self, game_data, next_pl = None):
168 """Increment player number & return player name
169 @param next_pl: if given, then next_player is forced to this one
170 """
171 if next_pl:
172 game_data['current_player'] = game_data['players'].index(next_pl)
173 return next_pl
174 else:
175 pl_idx = game_data['current_player'] = (game_data['current_player'] + 1) % len(game_data['players'])
176 return game_data['players'][pl_idx]
177
178 def __winner(self, game_data):
179 """give the nick of the player who win this trick"""
180 players_data = game_data['players_data']
181 first = game_data['first_player']
182 first_idx = game_data['players'].index(first)
183 suit_asked = None
184 strongest = None
185 winner = None
186 for idx in [(first_idx + i) % 4 for i in range(4)]:
187 player = game_data['players'][idx]
188 card = players_data[player]['played']
189 if card.value == "excuse":
190 continue
191 if suit_asked == None:
192 suit_asked = card.suit
193 if (card.suit == suit_asked or card.suit == "atout") and card > strongest:
194 strongest = card
195 winner = player
196 assert winner
197 return winner
198
199 def __excuse_hack(self, game_data, played, winner):
200 """give a low card to other team and keep excuse if trick is lost
201 @param game_data: data of the game
202 @param played: cards currently on the table
203 @param winner: nick of the trick winner"""
204 #TODO: manage the case where excuse is played on the last trick (and lost)
205 #TODO: gof: manage excuse (fool)
206 players_data = game_data['players_data']
207 excuse = TarotCard(("atout","excuse"))
208
209 #we first check if the Excuse was already player
210 #and if somebody is waiting for a card
211 for player in game_data['players']:
212 if players_data[player]['wait_for_low']:
213 #the excuse owner has to give a card to somebody
214 if winner == player:
215 #the excuse owner win the trick, we check if we have something to give
216 for card in played:
217 if card.points == 0.5:
218 pl_waiting = players_data[player]['wait_for_low']
219 played.remove(card)
220 players_data[pl_waiting]['levees'].append(card)
221 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})
222 return
223 return
224
225 if not excuse in played:
226 #the Excuse is not on the table, nothing to do
227 return
228
229 excuse_player = None #Who has played the Excuse ?
230 for player in game_data['players']:
231 if players_data[player]['played'] == excuse:
232 excuse_player = player
233 break
234
235 if excuse_player == winner:
236 return #the excuse player win the trick, nothing to do
237
238 #first we remove the excuse from played cards
239 played.remove(excuse)
240 #then we give it back to the original owner
241 owner_levees = players_data[excuse_player]['levees']
242 owner_levees.append(excuse)
243 #finally we give a low card to the trick winner
244 low_card = None
245 #We look backward in cards won by the Excuse owner to
246 #find a low value card
247 for card_idx in range(len(owner_levees)-1, -1, -1):
248 if owner_levees[card_idx].points == 0.5:
249 low_card = owner_levees[card_idx]
250 del owner_levees[card_idx]
251 players_data[winner]['levees'].append(low_card)
252 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})
253 break
254 if not low_card: #The player has no low card yet
255 #TODO: manage case when player never win a trick with low card
256 players_data[excuse_player]['wait_for_low'] = winner
257 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})
258
259
260 def __calculate_scores(self, game_data):
261 """The game is finished, time to know who won :)
262 @param game_data: data of the game
263 @return: tuple with (string victory message, list of winners, list of loosers)"""
264 players_data = game_data['players_data']
265 levees = players_data[game_data['attaquant']]['levees']
266 score = 0
267 nb_bouts = 0
268 bouts = []
269 for card in levees:
270 if card.bout:
271 nb_bouts +=1
272 bouts.append(card.value)
273 score += card.points
274
275 #We we do a basic check on score calculation
276 check_score = 0
277 defenseurs = game_data['players'][:]
278 defenseurs.remove(game_data['attaquant'])
279 for defenseur in defenseurs:
280 for card in players_data[defenseur]['levees']:
281 check_score+=card.points
282 if game_data['contrat'] == "Garde Contre":
283 for card in game_data['chien']:
284 check_score+=card.points
285 assert (score + check_score == 91)
286
287 point_limit = None
288 if nb_bouts == 3:
289 point_limit = 36
290 elif nb_bouts == 2:
291 point_limit = 41
292 elif nb_bouts == 1:
293 point_limit = 51
294 else:
295 point_limit = 56
296 if game_data['contrat'] == 'Petite':
297 contrat_mult = 1
298 elif game_data['contrat'] == 'Garde':
299 contrat_mult = 2
300 elif game_data['contrat'] == 'Garde Sans':
301 contrat_mult = 4
302 elif game_data['contrat'] == 'Garde Contre':
303 contrat_mult = 6
304 else:
305 error(_('INTERNAL ERROR: contrat not managed (mispelled ?)'))
306 assert(False)
307
308 victory = (score >= point_limit)
309 margin = abs(score - point_limit)
310 points_defenseur = (margin + 25) * contrat_mult * (-1 if victory else 1)
311 winners = []
312 loosers = []
313 player_score = {}
314 for player in game_data['players']:
315 #TODO: adjust this for 3 and 5 players variants
316 #TODO: manage bonuses (petit au bout, poignée, chelem)
317 player_score[player] = points_defenseur if player != game_data['attaquant'] else points_defenseur * -3
318 players_data[player]['score'] += player_score[player] #we add score of this game to the global score
319 if player_score[player] > 0:
320 winners.append(player)
321 else:
322 loosers.append(player)
323
324 scores_str = _('The attacker (%(attaquant)s) makes %(points)i and needs to make %(point_limit)i (%(nb_bouts)s oulder%(plural)s%(separator)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 '', 'separator':': ' if nb_bouts != 0 else '', 'bouts':','.join(map(str,bouts)), 'victory': 'win' if victory else 'loose'}
325 scores_str+='\n'
326 for player in game_data['players']:
327 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']}
328 debug(scores_str)
329
330 return (scores_str, winners, loosers)
331
332 def __invalid_cards(self, game_data, cards):
333 """Checks that the player has the right to play what he wants to
334 @param game_data: Game data
335 @param cards: cards the player want to play
336 @return forbidden_cards cards or empty list if cards are ok"""
337 forbidden_cards = []
338 if game_data['stage'] == 'ecart':
339 for card in cards:
340 if card.bout or card.value=="roi":
341 forbidden_cards.append(card)
342 #TODO: manage case where atouts (trumps) are in the dog
343 elif game_data['stage'] == 'play':
344 biggest_atout = None
345 suit_asked = None
346 players = game_data['players']
347 players_data = game_data['players_data']
348 idx = players.index(game_data['first_player'])
349 current_idx = game_data['current_player']
350 current_player = players[current_idx]
351 if idx == current_idx:
352 #the player is the first to play, he can play what he wants
353 return forbidden_cards
354 while (idx != current_idx):
355 player = players[idx]
356 played_card = players_data[player]['played']
357 if not suit_asked and played_card.value != "excuse":
358 suit_asked = played_card.suit
359 if played_card.suit == "atout" and played_card > biggest_atout:
360 biggest_atout = played_card
361 idx = (idx + 1) % len(players)
362 has_suit = False #True if there is one card of the asked suit in the hand of the player
363 has_atout = False
364 biggest_hand_atout = None
365
366 for hand_card in game_data['hand'][current_player]:
367 if hand_card.suit == suit_asked:
368 has_suit = True
369 if hand_card.suit == "atout":
370 has_atout = True
371 if hand_card.suit == "atout" and hand_card > biggest_hand_atout:
372 biggest_hand_atout = hand_card
373
374 assert len(cards) == 1
375 card = cards[0]
376 if card.suit != suit_asked and has_suit and card.value != "excuse":
377 forbidden_cards.append(card)
378 return forbidden_cards
379 if card.suit != suit_asked and card.suit != "atout" and has_atout:
380 forbidden_cards.append(card)
381 return forbidden_cards
382 if card.suit == "atout" and card < biggest_atout and biggest_hand_atout > biggest_atout and card.value != "excuse":
383 forbidden_cards.append(card)
384 else:
385 error(_('Internal error: unmanaged game stage'))
386 return forbidden_cards
387
388
389 def __start_play(self, room_jid, game_data, profile):
390 """Start the game (tell to the first player after dealer to play"""
391 game_data['stage'] = "play"
392 next_player_idx = game_data['current_player'] = (game_data['init_player'] + 1) % len(game_data['players']) #the player after the dealer start
393 game_data['first_player'] = next_player = game_data['players'][next_player_idx]
394 to_jid = jid.JID(room_jid.userhost()+"/"+next_player) #FIXME: gof:
395 mess = self.createGameElt(to_jid)
396 yourturn_elt = mess.firstChildElement().addElement('your_turn')
397 self.host.profiles[profile].xmlstream.send(mess)
398
399
400 def createGame(self, room_jid_param, players, profile_key='@DEFAULT@'):
401 """Create a new game"""
402 debug (_("Creating Tarot game"))
403 room_jid = jid.JID(room_jid_param)
404 profile = self.host.memory.getProfileName(profile_key)
405 if not profile:
406 error (_("profile %s is unknown") % profile_key)
407 return
408 if self.games.has_key(room_jid):
409 warning (_("Tarot game already started in room %s") % room_jid.userhost())
410 else:
411 room_nick = self.host.plugins["XEP_0045"].getRoomNick(room_jid.userhost(), profile)
412 if not room_nick:
413 error ('Internal error')
414 return
415 referee = room_jid.userhost() + '/' + room_nick
416 status = {}
417 players_data = {}
418 for player in players:
419 players_data[player] = {'score':0}
420 status[player] = "init"
421 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}
422 for player in players:
423 mess = self.createGameElt(jid.JID(room_jid.userhost()+'/'+player))
424 mess.firstChildElement().addChild(self.__create_started_elt(players))
425 self.host.profiles[profile].xmlstream.send(mess)
426
427 def newPlayerReady(self, player, referee, profile_key='@DEFAULT@'):
428 """Must be called when player is ready to start a new game"""
429 profile = self.host.memory.getProfileName(profile_key)
430 if not profile:
431 error (_("profile %s is unknown") % profile_key)
432 return
433 debug ('new player ready: %s' % profile)
434 mess = self.createGameElt(jid.JID(referee))
435 ready_elt = mess.firstChildElement().addElement('player_ready')
436 ready_elt['player'] = player
437 self.host.profiles[profile].xmlstream.send(mess)
438
439 def contratChoosed(self, player, referee, contrat, profile_key='@DEFAULT@'):
440 """Must be call by player when the contrat is selected
441 @param player: player's name
442 @param referee: arbiter jid
443 @contrat: contrat choosed (must be the exact same string than in the give list options)
444 @profile_key: profile
445 """
446 profile = self.host.memory.getProfileName(profile_key)
447 if not profile:
448 error (_("profile %s is unknown") % profile_key)
449 return
450 debug (_('contrat [%(contrat)s] choosed by %(profile)s') % {'contrat':contrat, 'profile':profile})
451 mess = self.createGameElt(jid.JID(referee))
452 contrat_elt = mess.firstChildElement().addElement(('','contrat_choosed'), content=contrat)
453 contrat_elt['player'] = player
454 self.host.profiles[profile].xmlstream.send(mess)
455
456 def play_cards(self, player, referee, cards, profile_key='@DEFAULT@'):
457 """Must be call by player when the contrat is selected
458 @param player: player's name
459 @param referee: arbiter jid
460 @cards: cards played (list of tuples)
461 @profile_key: profile
462 """
463 profile = self.host.memory.getProfileName(profile_key)
464 if not profile:
465 error (_("profile %s is unknown") % profile_key)
466 return
467 debug (_('Cards played by %(profile)s: [%(cards)s]') % {'profile':profile,'cards':cards})
468 mess = self.createGameElt(jid.JID(referee))
469 playcard_elt = mess.firstChildElement().addChild(self.__card_list_to_xml(TarotCard.from_tuples(cards), 'cards_played'))
470 playcard_elt['player'] = player
471 self.host.profiles[profile].xmlstream.send(mess)
472
473 def newGame(self, room_jid, profile):
474 """Launch a new round"""
475 debug (_('new Tarot game'))
476 deck = self.deck_ordered[:]
477 random.shuffle(deck)
478 game_data = self.games[room_jid.userhost()]
479 players = game_data['players']
480 players_data = game_data['players_data']
481 current_player = game_data['current_player']
482 game_data['stage'] = "init"
483 game_data['first_player'] = None #first player for the current trick
484 game_data['contrat'] = None
485 hand = game_data['hand'] = {}
486 hand_size = game_data['hand_size']
487 chien = game_data['chien'] = []
488 for i in range(4): #TODO: distribute according to real Tarot rules (3 by 3 counter-clockwise, 1 card at once to chien)
489 hand[players[i]] = deck[0:hand_size]
490 del deck[0:hand_size]
491 chien.extend(deck)
492 del(deck[:])
493
494 for player in players:
495 to_jid = jid.JID(room_jid.userhost()+"/"+player) #FIXME: gof:
496 mess = self.createGameElt(to_jid)
497 mess.firstChildElement().addChild(self.__card_list_to_xml(hand[player], 'hand'))
498 self.host.profiles[profile].xmlstream.send(mess)
499 players_data[player]['contrat'] = None
500 players_data[player]['levees'] = [] #cards won
501 players_data[player]['played'] = None #card on the table
502 players_data[player]['wait_for_low'] = None #Used when a player wait for a low card because of excuse
503
504 pl_idx = game_data['current_player'] = (game_data['init_player'] + 1) % len(players) #the player after the dealer start
505 player = players[pl_idx]
506 to_jid = jid.JID(room_jid.userhost()+"/"+player) #FIXME: gof:
507 mess = self.createGameElt(to_jid)
508 mess.firstChildElement().addChild(self.__ask_contrat())
509 self.host.profiles[profile].xmlstream.send(mess)
510
511
512 def card_game_cmd(self, mess_elt, profile):
513 from_jid = jid.JID(mess_elt['from'])
514 room_jid = jid.JID(from_jid.userhost())
515 game_elt = mess_elt.firstChildElement()
516 game_data = self.games[room_jid.userhost()]
517 players_data = game_data['players_data']
518
519 for elt in game_elt.elements():
520
521 if elt.name == 'started': #new game created
522 players = []
523 for player in elt.elements():
524 players.append(unicode(player))
525 self.host.bridge.tarotGameStarted(room_jid.userhost(), from_jid.full(), players, profile)
526
527 elif elt.name == 'player_ready': #ready to play
528 player = elt['player']
529 status = self.games[room_jid.userhost()]['status']
530 nb_players = len(self.games[room_jid.userhost()]['players'])
531 status[player] = 'ready'
532 debug (_('Player %(player)s is ready to start [status: %(status)s]') % {'player':player, 'status':status})
533 if status.values().count('ready') == nb_players: #everybody is ready, we can start the game
534 self.newGame(room_jid, profile)
535
536 elif elt.name == 'hand': #a new hand has been received
537 self.host.bridge.tarotGameNew(room_jid.userhost(), self.__xml_to_list(elt), profile)
538
539 elif elt.name == 'contrat': #it's time to choose contrat
540 form = data_form.Form.fromElement(elt.firstChildElement())
541 xml_data = dataForm2xml(form)
542 self.host.bridge.tarotGameChooseContrat(room_jid.userhost(), xml_data, profile)
543
544 elif elt.name == 'contrat_choosed':
545 #TODO: check we receive the contrat from the right person
546 #TODO: use proper XEP-0004 way for answering form
547 player = elt['player']
548 players_data[player]['contrat'] = unicode(elt)
549 contrats = [players_data[player]['contrat'] for player in game_data['players']]
550 if contrats.count(None):
551 #not everybody has choosed his contrat, it's next one turn
552 player = self.__next_player(game_data)
553 to_jid = jid.JID(room_jid.userhost()+"/"+player) #FIXME: gof:
554 mess = self.createGameElt(to_jid)
555 mess.firstChildElement().addChild(self.__ask_contrat())
556 self.host.profiles[profile].xmlstream.send(mess)
557 else:
558 #TODO: gof: manage "everybody pass" case
559 best_contrat = [None, "Passe"]
560 for player in game_data['players']:
561 contrat = players_data[player]['contrat']
562 idx_best = self.contrats.index(best_contrat[1])
563 idx_pl = self.contrats.index(contrat)
564 if idx_pl > idx_best:
565 best_contrat[0] = player
566 best_contrat[1] = contrat
567 debug (_("%(player)s win the bid with %(contrat)s") % {'player':best_contrat[0],'contrat':best_contrat[1]})
568 game_data['contrat'] = best_contrat[1]
569
570 if game_data['contrat'] == "Garde Sans" or game_data['contrat'] == "Garde Contre":
571 self.__start_play(room_jid, game_data, profile)
572 game_data['attaquant'] = best_contrat[0]
573 else:
574 #Time to show the chien to everybody
575 to_jid = jid.JID(room_jid.userhost()) #FIXME: gof:
576 mess = self.createGameElt(to_jid)
577 chien_elt = mess.firstChildElement().addChild(self.__card_list_to_xml(game_data['chien'], 'chien'))
578 chien_elt['attaquant'] = best_contrat[0]
579 self.host.profiles[profile].xmlstream.send(mess)
580 #the attacker (attaquant) get the chien
581 game_data['hand'][best_contrat[0]].extend(game_data['chien'])
582 del game_data['chien'][:]
583
584 if game_data['contrat'] == "Garde Sans":
585 #The chien go into attaquant's (attacker) levees
586 players_data[best_contrat[0]]['levees'].extend(game_data['chien'])
587 del game_data['chien'][:]
588
589
590 elif elt.name == 'chien': #we have received the chien
591 debug (_("tarot: chien received"))
592 data = {"attaquant":elt['attaquant']}
593 game_data['stage'] = "ecart"
594 game_data['attaquant'] = elt['attaquant']
595 self.host.bridge.tarotGameShowCards(room_jid.userhost(), "chien", self.__xml_to_list(elt), data, profile)
596
597 elif elt.name == 'cards_played':
598 if game_data['stage'] == "ecart":
599 #TODO: show atouts (trumps) if player put some in écart
600 assert (game_data['attaquant'] == elt['player']) #TODO: throw an xml error here
601 list_cards = TarotCard.from_tuples(self.__xml_to_list(elt))
602 #we now check validity of card
603 invalid_cards = self.__invalid_cards(game_data, list_cards)
604 if invalid_cards:
605 mess = self.createGameElt(jid.JID(room_jid.userhost()+'/'+elt['player']))
606 mess.firstChildElement().addChild(self.__invalid_cards_elt(list_cards, invalid_cards, game_data['stage']))
607 self.host.profiles[profile].xmlstream.send(mess)
608 return
609
610 #FIXME: gof: manage Garde Sans & Garde Contre cases
611 players_data[elt['player']]['levees'].extend(list_cards) #we add the chien to attaquant's levées
612 for card in list_cards:
613 game_data['hand'][elt['player']].remove(card)
614
615 self.__start_play(room_jid, game_data, profile)
616
617 elif game_data['stage'] == "play":
618 current_player = game_data['players'][game_data['current_player']]
619 cards = TarotCard.from_tuples(self.__xml_to_list(elt))
620
621 if mess_elt['type'] == 'groupchat':
622 self.host.bridge.tarotGameCardsPlayed(room_jid.userhost(), elt['player'], self.__xml_to_list(elt), profile)
623 else:
624 #we first check validity of card
625 invalid_cards = self.__invalid_cards(game_data, cards)
626 if invalid_cards:
627 mess = self.createGameElt(jid.JID(room_jid.userhost()+'/'+current_player))
628 mess.firstChildElement().addChild(self.__invalid_cards_elt(cards, invalid_cards, game_data['stage']))
629 self.host.profiles[profile].xmlstream.send(mess)
630 return
631 #the card played is ok, we forward it to everybody
632 #first we remove it from the hand and put in on the table
633 game_data['hand'][current_player].remove(cards[0])
634 players_data[current_player]['played'] = cards[0]
635
636 #then we forward the message
637 mess = self.createGameElt(room_jid)
638 playcard_elt = mess.firstChildElement().addChild(elt)
639 self.host.profiles[profile].xmlstream.send(mess)
640
641 #Did everybody played ?
642 played = [players_data[player]['played'] for player in game_data['players']]
643 if all(played):
644 #everybody has played
645 winner = self.__winner(game_data)
646 debug (_('The winner of this trick is %s') % winner)
647 #the winner win the trick
648 self.__excuse_hack(game_data, played, winner)
649 players_data[elt['player']]['levees'].extend(played)
650 #nothing left on the table
651 for player in game_data['players']:
652 players_data[player]['played'] = None
653 if len(game_data['hand'][current_player]) == 0:
654 #no card lef: the game is finished
655 to_jid = jid.JID(room_jid.userhost()) #FIXME: gof:
656 mess = self.createGameElt(to_jid)
657 chien_elt = mess.firstChildElement().addChild(self.__give_scores(*self.__calculate_scores(game_data)))
658 self.host.profiles[profile].xmlstream.send(mess)
659 return
660 #next player is the winner
661 next_player = game_data['first_player'] = self.__next_player(game_data, winner)
662 else:
663 next_player = self.__next_player(game_data)
664
665 #finally, we tell to the next player to play
666 to_jid = jid.JID(room_jid.userhost()+"/"+next_player) #FIXME: gof:
667 mess = self.createGameElt(to_jid)
668 yourturn_elt = mess.firstChildElement().addElement('your_turn')
669 self.host.profiles[profile].xmlstream.send(mess)
670
671 elif elt.name == 'your_turn':
672 self.host.bridge.tarotGameYourTurn(room_jid.userhost(), profile)
673
674 elif elt.name == 'score':
675 form_elt = elt.elements(name='x',uri='jabber:x:data').next()
676 winners = []
677 loosers = []
678 for winner in elt.elements(name='winner', uri=''):
679 winners.append(unicode(winner))
680 for looser in elt.elements(name='looser', uri=''):
681 loosers.append(unicode(looser))
682 form = data_form.Form.fromElement(form_elt)
683 xml_data = dataForm2xml(form)
684 self.host.bridge.tarotGameScore(room_jid.userhost(), xml_data, winners, loosers, profile)
685 elif elt.name == 'error':
686 if elt['type'] == 'invalid_cards':
687 played_cards = self.__xml_to_list(elt.elements(name='played',uri='').next())
688 invalid_cards = self.__xml_to_list(elt.elements(name='invalid',uri='').next())
689 self.host.bridge.tarotGameInvalidCards(room_jid.userhost(), elt['phase'], played_cards, invalid_cards, profile)
690 else:
691 error (_('Unmanaged error type: %s') % elt['type'])
692 else:
693 error (_('Unmanaged card game element: %s') % elt.name)
694
695 def getHandler(self, profile):
696 return CardGameHandler(self)
697
698 class CardGameHandler (XMPPHandler):
699 implements(iwokkel.IDisco)
700
701 def __init__(self, plugin_parent):
702 self.plugin_parent = plugin_parent
703 self.host = plugin_parent.host
704
705 def connectionInitialized(self):
706 self.xmlstream.addObserver(CG_REQUEST, self.plugin_parent.card_game_cmd, profile = self.parent.profile)
707
708 def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
709 return [disco.DiscoFeature(NS_CG)]
710
711 def getDiscoItems(self, requestor, target, nodeIdentifier=''):
712 return []
713