comparison src/plugins/plugin_misc_tarot.py @ 683:75e4f5e2cc65

plugins radiocol, card_game, quiz: code factorization
author souliane <souliane@mailoo.org>
date Wed, 23 Oct 2013 12:45:13 +0200
parents 2805fa3f4bdf
children f610864eb7a5
comparison
equal deleted inserted replaced
682:2805fa3f4bdf 683:75e4f5e2cc65
26 26
27 from wokkel import disco, iwokkel, data_form 27 from wokkel import disco, iwokkel, data_form
28 from sat.tools.xml_tools import dataForm2XML 28 from sat.tools.xml_tools import dataForm2XML
29 29
30 from sat.tools.frontends.games import TarotCard 30 from sat.tools.frontends.games import TarotCard
31 from sat.tools.plugins.games import RoomGame
31 from time import time 32 from time import time
32 33
33 try: 34 try:
34 from twisted.words.protocols.xmlstream import XMPPHandler 35 from twisted.words.protocols.xmlstream import XMPPHandler
35 except ImportError: 36 except ImportError:
50 "handler": "yes", 51 "handler": "yes",
51 "description": _("""Implementation of Tarot card game""") 52 "description": _("""Implementation of Tarot card game""")
52 } 53 }
53 54
54 55
55 class Tarot(object): 56 class Tarot(RoomGame):
56 57
57 def __init__(self, host): 58 def __init__(self, host):
58 info(_("Plugin Tarot initialization")) 59 info(_("Plugin Tarot initialization"))
59 self.host = host 60 RoomGame.__init__(self, host, PLUGIN_INFO, (NS_CG, CG_TAG), player_init_data={'score': 0},
60 self.games = {} 61 options={'hand_size': 18, 'init_player': 0, 'current_player': None, 'contrat': None, 'stage': None})
61 self.waiting_inv = {} # Invitation waiting for people to join to launch a game
62 self.contrats = [_('Passe'), _('Petite'), _('Garde'), _('Garde Sans'), _('Garde Contre')] 62 self.contrats = [_('Passe'), _('Petite'), _('Garde'), _('Garde Sans'), _('Garde Contre')]
63 host.bridge.addMethod("tarotGameLaunch", ".plugin", in_sign='ass', out_sign='', method=self.launchGame) # args: room_jid, players, profile 63 host.bridge.addMethod("tarotGameLaunch", ".plugin", in_sign='ass', out_sign='', method=self.prepareRoom) # args: room_jid, players, profile
64 host.bridge.addMethod("tarotGameCreate", ".plugin", in_sign='sass', out_sign='', method=self.createGame) # args: room_jid, players, profile 64 host.bridge.addMethod("tarotGameCreate", ".plugin", in_sign='sass', out_sign='', method=self.createGame) # args: room_jid, players, profile
65 host.bridge.addMethod("tarotGameReady", ".plugin", in_sign='sss', out_sign='', method=self.newPlayerReady) # args: player, referee, profile 65 host.bridge.addMethod("tarotGameReady", ".plugin", in_sign='sss', out_sign='', method=self.playerReady) # args: player, referee, profile
66 host.bridge.addMethod("tarotGameContratChoosed", ".plugin", in_sign='ssss', out_sign='', method=self.contratChoosed) # args: player, referee, contrat, profile 66 host.bridge.addMethod("tarotGameContratChoosed", ".plugin", in_sign='ssss', out_sign='', method=self.contratChoosed) # args: player, referee, contrat, profile
67 host.bridge.addMethod("tarotGamePlayCards", ".plugin", in_sign='ssa(ss)s', out_sign='', method=self.play_cards) # args: player, referee, cards, profile 67 host.bridge.addMethod("tarotGamePlayCards", ".plugin", in_sign='ssa(ss)s', out_sign='', method=self.play_cards) # args: player, referee, cards, profile
68 host.bridge.addSignal("tarotGameStarted", ".plugin", signature='ssass') # args: room_jid, referee, players, profile 68 host.bridge.addSignal("tarotGameStarted", ".plugin", signature='ssass') # args: room_jid, referee, players, profile
69 host.bridge.addSignal("tarotGameNew", ".plugin", signature='sa(ss)s') # args: room_jid, hand, profile 69 host.bridge.addSignal("tarotGameNew", ".plugin", signature='sa(ss)s') # args: room_jid, hand, profile
70 host.bridge.addSignal("tarotGameChooseContrat", ".plugin", signature='sss') # args: room_jid, xml_data, profile 70 host.bridge.addSignal("tarotGameChooseContrat", ".plugin", signature='sss') # args: room_jid, xml_data, profile
79 self.deck_ordered.append(TarotCard(("atout", value))) 79 self.deck_ordered.append(TarotCard(("atout", value)))
80 for suit in ["pique", "coeur", "carreau", "trefle"]: 80 for suit in ["pique", "coeur", "carreau", "trefle"]:
81 for value in map(str, range(1, 11)) + ["valet", "cavalier", "dame", "roi"]: 81 for value in map(str, range(1, 11)) + ["valet", "cavalier", "dame", "roi"]:
82 self.deck_ordered.append(TarotCard((suit, value))) 82 self.deck_ordered.append(TarotCard((suit, value)))
83 83
84 def createGameElt(self, to_jid, type="normal"):
85 type = "normal" if to_jid.resource else "groupchat"
86 elt = domish.Element((None, 'message'))
87 elt["to"] = to_jid.full()
88 elt["type"] = type
89 elt.addElement((NS_CG, CG_TAG))
90 return elt
91
92 def __card_list_to_xml(self, cards_list, elt_name): 84 def __card_list_to_xml(self, cards_list, elt_name):
93 """Convert a card list to domish element""" 85 """Convert a card list to domish element"""
94 cards_list_elt = domish.Element((None, elt_name)) 86 cards_list_elt = domish.Element((None, elt_name))
95 for card in cards_list: 87 for card in cards_list:
96 card_elt = domish.Element((None, 'card')) 88 card_elt = domish.Element((None, 'card'))
103 """Convert a domish element with cards to a list of tuples""" 95 """Convert a domish element with cards to a list of tuples"""
104 cards_list = [] 96 cards_list = []
105 for card in cards_list_elt.elements(): 97 for card in cards_list_elt.elements():
106 cards_list.append((card['suit'], card['value'])) 98 cards_list.append((card['suit'], card['value']))
107 return cards_list 99 return cards_list
108
109 def __create_started_elt(self, players):
110 """Create a game_started domish element"""
111 started_elt = domish.Element((None, 'started'))
112 idx = 0
113 for player in players:
114 player_elt = domish.Element((None, 'player'))
115 player_elt.addContent(player)
116 player_elt['index'] = str(idx)
117 idx += 1
118 started_elt.addChild(player_elt)
119 return started_elt
120 100
121 def __ask_contrat(self): 101 def __ask_contrat(self):
122 """Create a element for asking contrat""" 102 """Create a element for asking contrat"""
123 contrat_elt = domish.Element((None, 'contrat')) 103 contrat_elt = domish.Element((None, 'contrat'))
124 form = data_form.Form('form', title=_('contrat selection')) 104 form = data_form.Form('form', title=_('contrat selection'))
402 to_jid = jid.JID(room_jid.userhost() + "/" + next_player) # FIXME: gof: 382 to_jid = jid.JID(room_jid.userhost() + "/" + next_player) # FIXME: gof:
403 mess = self.createGameElt(to_jid) 383 mess = self.createGameElt(to_jid)
404 yourturn_elt = mess.firstChildElement().addElement('your_turn') 384 yourturn_elt = mess.firstChildElement().addElement('your_turn')
405 self.host.profiles[profile].xmlstream.send(mess) 385 self.host.profiles[profile].xmlstream.send(mess)
406 386
407 def userJoinedTrigger(self, room, user, profile):
408 """This trigger is used to check if we are waiting people in this room,
409 and to create a game if everybody is here"""
410 _room_jid = room.occupantJID.userhostJID()
411 if _room_jid in self.waiting_inv and len(room.roster) == 4:
412 #When we have 4 people in the room, we create the game
413 #TODO: check people identity
414 players = room.roster.keys()
415 del self.waiting_inv[_room_jid]
416 self.createGame(_room_jid.userhost(), players, profile_key=profile)
417 return True
418
419 def launchGame(self, players, profile_key='@NONE@'):
420 """Launch a game: helper method to create a room, invite players, and create the tarot game
421 @param players: list for players jid"""
422 debug(_('Launching tarot game'))
423 profile = self.host.memory.getProfileName(profile_key)
424 if not profile:
425 error(_("Unknown profile"))
426 return
427
428 def tarotRoomJoined(room):
429 _room = room.occupantJID.userhostJID()
430 for player in players:
431 self.host.plugins["XEP-0249"].invite(jid.JID(player), room.occupantJID.userhostJID(), {"game": "Tarot"}, profile)
432 self.waiting_inv[_room] = (time(), players) # TODO: remove invitation waiting for too long, using the time data
433
434 def after_init(ignore):
435 room_name = "sat_tarot_%s" % self.host.plugins["XEP-0045"].getUniqueName(profile_key)
436 print "\n\n===> room_name:", room_name
437 #muc_service = self.host.memory.getServerServiceEntity("conference", "text", profile)
438 muc_service = None
439 for service in self.host.memory.getServerServiceEntities("conference", "text", profile):
440 if not ".irc." in service.userhost():
441 #FIXME:
442 #This awfull ugly hack is here to avoid an issue with openfire: the irc gateway
443 #use "conference/text" identity (instead of "conference/irc"), there is certainly a better way
444 #to manage this, but this hack fill do it for test purpose
445 muc_service = service
446 break
447 if not muc_service:
448 error(_("Can't find a MUC service"))
449 return
450
451 _jid, xmlstream = self.host.getJidNStream(profile)
452 d = self.host.plugins["XEP-0045"].join(jid.JID("%s@%s" % (room_name, muc_service.userhost())), _jid.user, {}, profile).addCallback(tarotRoomJoined)
453
454 client = self.host.getClient(profile)
455 if not client:
456 error(_('No client for this profile key: %s') % profile_key)
457 return
458 client.client_initialized.addCallback(after_init)
459
460 def createGame(self, room_jid_param, players, profile_key='@NONE@'):
461 """Create a new game
462 @param room_jid_param: jid of the room
463 @param players: list of players nick (nick must exist in the room)
464 @param profile_key: %(doc_profile_key)s"""
465 debug(_("Creating Tarot game"))
466 room_jid = jid.JID(room_jid_param)
467 profile = self.host.memory.getProfileName(profile_key)
468 if not profile:
469 error(_("profile %s is unknown") % profile_key)
470 return
471 if room_jid in self.games:
472 warning(_("Tarot game already started in room %s") % room_jid.userhost())
473 else:
474 room_nick = self.host.plugins["XEP-0045"].getRoomNick(room_jid.userhost(), profile)
475 if not room_nick:
476 error('Internal error')
477 return
478 referee = room_jid.userhost() + '/' + room_nick
479 status = {}
480 players_data = {}
481 for player in players:
482 players_data[player] = {'score': 0}
483 status[player] = "init"
484 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}
485 for player in players:
486 mess = self.createGameElt(jid.JID(room_jid.userhost() + '/' + player))
487 mess.firstChildElement().addChild(self.__create_started_elt(players))
488 self.host.profiles[profile].xmlstream.send(mess)
489
490 def newPlayerReady(self, player, referee, profile_key='@NONE@'):
491 """Must be called when player is ready to start a new game"""
492 profile = self.host.memory.getProfileName(profile_key)
493 if not profile:
494 error(_("profile %s is unknown") % profile_key)
495 return
496 debug('new player ready: %s' % profile)
497 mess = self.createGameElt(jid.JID(referee))
498 ready_elt = mess.firstChildElement().addElement('player_ready')
499 ready_elt['player'] = player
500 self.host.profiles[profile].xmlstream.send(mess)
501
502 def contratChoosed(self, player, referee, contrat, profile_key='@NONE@'): 387 def contratChoosed(self, player, referee, contrat, profile_key='@NONE@'):
503 """Must be call by player when the contrat is selected 388 """Must be call by player when the contrat is selected
504 @param player: player's name 389 @param player: player's name
505 @param referee: arbiter jid 390 @param referee: arbiter jid
506 @contrat: contrat choosed (must be the exact same string than in the give list options) 391 @contrat: contrat choosed (must be the exact same string than in the give list options)
531 mess = self.createGameElt(jid.JID(referee)) 416 mess = self.createGameElt(jid.JID(referee))
532 playcard_elt = mess.firstChildElement().addChild(self.__card_list_to_xml(TarotCard.from_tuples(cards), 'cards_played')) 417 playcard_elt = mess.firstChildElement().addChild(self.__card_list_to_xml(TarotCard.from_tuples(cards), 'cards_played'))
533 playcard_elt['player'] = player 418 playcard_elt['player'] = player
534 self.host.profiles[profile].xmlstream.send(mess) 419 self.host.profiles[profile].xmlstream.send(mess)
535 420
536 def newGame(self, room_jid, profile): 421 def newRound(self, room_jid, profile):
537 """Launch a new round"""
538 debug(_('new Tarot game'))
539 deck = self.deck_ordered[:]
540 random.shuffle(deck)
541 game_data = self.games[room_jid.userhost()] 422 game_data = self.games[room_jid.userhost()]
542 players = game_data['players'] 423 players = game_data['players']
543 players_data = game_data['players_data']
544 current_player = game_data['current_player']
545 game_data['stage'] = "init"
546 game_data['first_player'] = None # first player for the current trick 424 game_data['first_player'] = None # first player for the current trick
547 game_data['contrat'] = None 425 game_data['contrat'] = None
426 common_data = {'contrat': None,
427 'levees': [], # cards won
428 'played': None, # card on the table
429 'wait_for_low': None # Used when a player wait for a low card because of excuse
430 }
431
548 hand = game_data['hand'] = {} 432 hand = game_data['hand'] = {}
549 hand_size = game_data['hand_size'] 433 hand_size = game_data['hand_size']
550 chien = game_data['chien'] = [] 434 chien = game_data['chien'] = []
435 deck = self.deck_ordered[:]
436 random.shuffle(deck)
551 for i in range(4): 437 for i in range(4):
552 hand[players[i]] = deck[0:hand_size] 438 hand[players[i]] = deck[0:hand_size]
553 del deck[0:hand_size] 439 del deck[0:hand_size]
554 chien.extend(deck) 440 chien.extend(deck)
555 del(deck[:]) 441 del(deck[:])
556 442 msg_elts = {}
557 for player in players: 443 for player in players:
558 to_jid = jid.JID(room_jid.userhost() + "/" + player) # FIXME: gof: 444 msg_elts[player] = self.__card_list_to_xml(hand[player], 'hand')
559 mess = self.createGameElt(to_jid) 445
560 mess.firstChildElement().addChild(self.__card_list_to_xml(hand[player], 'hand')) 446 RoomGame.newRound(self, room_jid, (common_data, msg_elts), profile)
561 self.host.profiles[profile].xmlstream.send(mess)
562 players_data[player]['contrat'] = None
563 players_data[player]['levees'] = [] # cards won
564 players_data[player]['played'] = None # card on the table
565 players_data[player]['wait_for_low'] = None # Used when a player wait for a low card because of excuse
566 447
567 pl_idx = game_data['current_player'] = (game_data['init_player'] + 1) % len(players) # the player after the dealer start 448 pl_idx = game_data['current_player'] = (game_data['init_player'] + 1) % len(players) # the player after the dealer start
568 player = players[pl_idx] 449 player = players[pl_idx]
569 to_jid = jid.JID(room_jid.userhost() + "/" + player) # FIXME: gof: 450 to_jid = jid.JID(room_jid.userhost() + "/" + player) # FIXME: gof:
570 mess = self.createGameElt(to_jid) 451 mess = self.createGameElt(to_jid)
571 mess.firstChildElement().addChild(self.__ask_contrat()) 452 mess.firstChildElement().addChild(self.__ask_contrat())
572 self.host.profiles[profile].xmlstream.send(mess) 453 self.host.profiles[profile].xmlstream.send(mess)
573 454
574 def card_game_cmd(self, mess_elt, profile): 455 def card_game_cmd(self, mess_elt, profile):
456 """
457 @param mess_elt: instance of twisted.words.xish.domish.Element
458 """
575 from_jid = jid.JID(mess_elt['from']) 459 from_jid = jid.JID(mess_elt['from'])
576 room_jid = jid.JID(from_jid.userhost()) 460 room_jid = jid.JID(from_jid.userhost())
577 game_elt = mess_elt.firstChildElement() 461 game_elt = mess_elt.firstChildElement()
578 game_data = self.games[room_jid.userhost()] 462 game_data = self.games[room_jid.userhost()]
579 players_data = game_data['players_data'] 463 players_data = game_data['players_data']
591 status = self.games[room_jid.userhost()]['status'] 475 status = self.games[room_jid.userhost()]['status']
592 nb_players = len(self.games[room_jid.userhost()]['players']) 476 nb_players = len(self.games[room_jid.userhost()]['players'])
593 status[player] = 'ready' 477 status[player] = 'ready'
594 debug(_('Player %(player)s is ready to start [status: %(status)s]') % {'player': player, 'status': status}) 478 debug(_('Player %(player)s is ready to start [status: %(status)s]') % {'player': player, 'status': status})
595 if status.values().count('ready') == nb_players: # everybody is ready, we can start the game 479 if status.values().count('ready') == nb_players: # everybody is ready, we can start the game
596 self.newGame(room_jid, profile) 480 self.newRound(room_jid, profile)
597 481
598 elif elt.name == 'hand': # a new hand has been received 482 elif elt.name == 'hand': # a new hand has been received
599 self.host.bridge.tarotGameNew(room_jid.userhost(), self.__xml_to_list(elt), profile) 483 self.host.bridge.tarotGameNew(room_jid.userhost(), self.__xml_to_list(elt), profile)
600 484
601 elif elt.name == 'contrat': # it's time to choose contrat 485 elif elt.name == 'contrat': # it's time to choose contrat