Mercurial > libervia-backend
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 |