comparison 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
comparison
equal deleted inserted replaced
94:1eb5ccead43c 95:be206a3d1a9b
129 host.bridge.addSignal("tarotGameNew", ".communication", signature='sa(ss)s') #args: room_jid, hand, profile 129 host.bridge.addSignal("tarotGameNew", ".communication", signature='sa(ss)s') #args: room_jid, hand, profile
130 host.bridge.addSignal("tarotGameChooseContrat", ".communication", signature='sss') #args: room_jid, xml_data, profile 130 host.bridge.addSignal("tarotGameChooseContrat", ".communication", signature='sss') #args: room_jid, xml_data, profile
131 host.bridge.addSignal("tarotGameShowCards", ".communication", signature='ssa(ss)a{ss}s') #args: room_jid, type ["chien", "poignée",...], cards, data[dict], profile 131 host.bridge.addSignal("tarotGameShowCards", ".communication", signature='ssa(ss)a{ss}s') #args: room_jid, type ["chien", "poignée",...], cards, data[dict], profile
132 host.bridge.addSignal("tarotGameCardsPlayed", ".communication", signature='ssa(ss)s') #args: room_jid, player, type ["chien", "poignée",...], cards, data[dict], profile 132 host.bridge.addSignal("tarotGameCardsPlayed", ".communication", signature='ssa(ss)s') #args: room_jid, player, type ["chien", "poignée",...], cards, data[dict], profile
133 host.bridge.addSignal("tarotGameYourTurn", ".communication", signature='ss') #args: room_jid, profile 133 host.bridge.addSignal("tarotGameYourTurn", ".communication", signature='ss') #args: room_jid, profile
134 host.bridge.addSignal("tarotGameScore", ".communication", signature='ssasass') #args: room_jid, xml_data, winners (list of nicks), loosers (list of nicks), profile
134 self.deck_ordered = [] 135 self.deck_ordered = []
135 for value in ['excuse']+map(str,range(1,22)): 136 for value in ['excuse']+map(str,range(1,22)):
136 self.deck_ordered.append(("atout",value)) 137 self.deck_ordered.append(("atout",value))
137 for suit in ["pique", "coeur", "carreau", "trefle"]: 138 for suit in ["pique", "coeur", "carreau", "trefle"]:
138 for value in map(str,range(1,11))+["valet","cavalier","dame","roi"]: 139 for value in map(str,range(1,11))+["valet","cavalier","dame","roi"]:
181 form = data_form.Form('form', title=_('contrat selection')) 182 form = data_form.Form('form', title=_('contrat selection'))
182 field = data_form.Field('list-single', 'contrat', options=map(data_form.Option, self.contrats), required=True) 183 field = data_form.Field('list-single', 'contrat', options=map(data_form.Option, self.contrats), required=True)
183 form.addField(field) 184 form.addField(field)
184 contrat_elt.addChild(form.toElement()) 185 contrat_elt.addChild(form.toElement())
185 return contrat_elt 186 return contrat_elt
187
188 def __give_scores(self, scores, winners, loosers):
189 """Create an element to give scores
190 @param scores: unicode (can contain line feed)
191 @param winners: list of unicode nicks of winners
192 @param loosers: list of unicode nicks of loosers"""
193
194 score_elt = domish.Element(('','score'))
195 form = data_form.Form('form', title=_('scores'))
196 for line in scores.split('\n'):
197 field = data_form.Field('fixed', value = line)
198 form.addField(field)
199 score_elt.addChild(form.toElement())
200 for winner in winners:
201 winner_elt = domish.Element(('','winner'))
202 winner_elt.addContent(winner)
203 score_elt.addChild(winner_elt)
204 for looser in loosers:
205 looser_elt = domish.Element(('','looser'))
206 looser_elt.addContent(looser)
207 score_elt.addChild(looser_elt)
208 return score_elt
186 209
187 def __next_player(self, game_data, next_pl = None): 210 def __next_player(self, game_data, next_pl = None):
188 """Increment player number & return player name 211 """Increment player number & return player name
189 @param next_pl: if given, then next_player is forced to this one 212 @param next_pl: if given, then next_player is forced to this one
190 """ 213 """
211 if suit_asked == None: 234 if suit_asked == None:
212 suit_asked = card.suit 235 suit_asked = card.suit
213 if (card.suit == suit_asked or card.suit == "atout") and card > strongest: 236 if (card.suit == suit_asked or card.suit == "atout") and card > strongest:
214 strongest = card 237 strongest = card
215 winner = player 238 winner = player
216 assert (winner) 239 assert winner
217 return winner 240 return winner
218 241
219 def __excuse_hack(self, game_data, played, winner): 242 def __excuse_hack(self, game_data, played, winner):
220 """give a low card to other team and keep excuse if trick is lost""" 243 """give a low card to other team and keep excuse if trick is lost
244 @param game_data: data of the game
245 @param played: cards currently on the table
246 @param winner: nick of the trick winner"""
221 #TODO: manage the case where excuse is played on the last trick (and lost) 247 #TODO: manage the case where excuse is played on the last trick (and lost)
222 #TODO: gof: manage excuse (fool) 248 #TODO: gof: manage excuse (fool)
223 players_data = game_data['players_data'] 249 players_data = game_data['players_data']
224 excuse = Card(("atout","excuse")) 250 excuse = Card(("atout","excuse"))
251
252 #we first check if the Excuse was already player
253 #and if somebody is waiting for a card
225 for player in game_data['players']: 254 for player in game_data['players']:
226 if players_data[player]['wait_for_low']: 255 if players_data[player]['wait_for_low']:
227 #the excuse owner has to give a card to somebody 256 #the excuse owner has to give a card to somebody
228 if winner == player: 257 if winner == player:
229 #the excuse owner win the trick, we check if we have something to give 258 #the excuse owner win the trick, we check if we have something to give
231 if card.points == 0.5: 260 if card.points == 0.5:
232 pl_waiting = players_data[player]['wait_for_low'] 261 pl_waiting = players_data[player]['wait_for_low']
233 played.remove(card) 262 played.remove(card)
234 players_data[pl_waiting]['levees'].append(card) 263 players_data[pl_waiting]['levees'].append(card)
235 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}) 264 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})
236 break 265 return
237 return 266 return
238 267
239 if not excuse in played: 268 if not excuse in played:
269 #the Excuse is not on the table, nothing to do
240 return 270 return
241 271
242 excuse_player = None 272 excuse_player = None #Who has played the Excuse ?
243 for player in game_data['players']: 273 for player in game_data['players']:
244 if players_data[player]['played'] == excuse: 274 if players_data[player]['played'] == excuse:
245 excuse_player = player 275 excuse_player = player
246 break 276 break
247 277
253 #then we give it back to the original owner 283 #then we give it back to the original owner
254 owner_levees = players_data[excuse_player]['levees'] 284 owner_levees = players_data[excuse_player]['levees']
255 owner_levees.append(excuse) 285 owner_levees.append(excuse)
256 #finally we give a low card to the trick winner 286 #finally we give a low card to the trick winner
257 low_card = None 287 low_card = None
288 #We look backward in cards won by the Excuse owner to
289 #find a low value card
258 for card_idx in range(len(owner_levees)-1, -1, -1): 290 for card_idx in range(len(owner_levees)-1, -1, -1):
259 if owner_levees[card_idx].points == 0.5: 291 if owner_levees[card_idx].points == 0.5:
260 low_card = owner_levees[card_idx] 292 low_card = owner_levees[card_idx]
261 del owner_levees[card_idx] 293 del owner_levees[card_idx]
262 players_data[winner]['levees'].append(low_card) 294 players_data[winner]['levees'].append(low_card)
267 players_data[excuse_player]['wait_for_low'] = winner 299 players_data[excuse_player]['wait_for_low'] = winner
268 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}) 300 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})
269 301
270 302
271 def __calculate_scores(self, game_data): 303 def __calculate_scores(self, game_data):
272 """The game is finished, time to know who won :)""" 304 """The game is finished, time to know who won :)
305 @param game_data: data of the game
306 @return: tuple with (string victory message, list of winners, list of loosers)"""
273 players_data = game_data['players_data'] 307 players_data = game_data['players_data']
274 levees = players_data[game_data['attaquant']]['levees'] 308 levees = players_data[game_data['attaquant']]['levees']
275 score = 0 309 score = 0
276 nb_bouts = 0 310 nb_bouts = 0
311 bouts = []
277 for card in levees: 312 for card in levees:
278 if card.bout: 313 if card.bout:
279 nb_bouts +=1 314 nb_bouts +=1
315 bouts.append(card.value)
280 score += card.points 316 score += card.points
317
318 #We now check if there is no bug in score calculation
319 check_score = 0
320 defenseurs = game_data['players'][:]
321 defenseurs.remove(game_data['attaquant'])
322 for defenseur in defenseurs:
323 for card in players_data[defenseur]['levees']:
324 check_score+=card.points
325 assert (score + check_score == 91)
326
281 point_limit = None 327 point_limit = None
282 if nb_bouts == 3: 328 if nb_bouts == 3:
283 point_limit = 36 329 point_limit = 36
284 elif nb_bouts == 2: 330 elif nb_bouts == 2:
285 point_limit = 41 331 point_limit = 41
286 elif nb_bouts == 1: 332 elif nb_bouts == 1:
287 point_limit = 51 333 point_limit = 51
288 else: 334 else:
289 point_limit = 56 335 point_limit = 56
336 if game_data['contrat'] == 'Petite':
337 contrat_mult = 1
338 elif game_data['contrat'] == 'Garde':
339 contrat_mult = 2
340 elif game_data['contrat'] == 'Garde Sans':
341 contrat_mult = 4
342 elif game_data['contrat'] == 'Garde Contre':
343 contrat_mult = 6
344 else:
345 error(_('Internal error: contrat not managed (mispelled ?)'))
346
290 victory = (score >= point_limit) 347 victory = (score >= point_limit)
291 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'}) 348 margin = score - point_limit
292 #pdb.set_trace() 349 pdb.set_trace()
293 350 points_defenseur = (-margin + 25) * contrat_mult
351 winners = []
352 loosers = []
353 player_score = {}
354 for player in game_data['players']:
355 #TODO: adjust this for 3 and 5 players variants
356 #TODO: manage bonuses (petit au bout, poignée, chelem)
357 player_score[player] = points_defenseur if player != game_data['attaquant'] else points_defenseur * -3
358 players_data[player]['score'] += player_score[player] #we add score of this game to the global score
359 if player_score[player] > 0:
360 winners.append(player)
361 else:
362 loosers.append(player)
363
364 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'}
365 scores_str+='\n'
366 for player in game_data['players']:
367 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']}
368 debug(scores_str)
369
370 return (scores_str, winners, loosers)
294 371
295 372
296 def createGame(self, room_jid_param, players, profile_key='@DEFAULT@'): 373 def createGame(self, room_jid_param, players, profile_key='@DEFAULT@'):
297 """Create a new game""" 374 """Create a new game"""
298 debug (_("Creating Tarot game")) 375 debug (_("Creating Tarot game"))
310 return 387 return
311 referee = room_jid.userhost() + '/' + room_nick 388 referee = room_jid.userhost() + '/' + room_nick
312 status = {} 389 status = {}
313 players_data = {} 390 players_data = {}
314 for player in players: 391 for player in players:
315 players_data[player] = {} 392 players_data[player] = {'score':0}
316 status[player] = "init" 393 status[player] = "init"
317 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} 394 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}
318 for player in players: 395 for player in players:
319 mess = self.createGameElt(jid.JID(room_jid.userhost()+'/'+player)) 396 mess = self.createGameElt(jid.JID(room_jid.userhost()+'/'+player))
320 mess.firstChildElement().addChild(self.__create_started_elt(players)) 397 mess.firstChildElement().addChild(self.__create_started_elt(players))
321 self.host.profiles[profile].xmlstream.send(mess) 398 self.host.profiles[profile].xmlstream.send(mess)
322 399
375 players = game_data['players'] 452 players = game_data['players']
376 players_data = game_data['players_data'] 453 players_data = game_data['players_data']
377 current_player = game_data['current_player'] 454 current_player = game_data['current_player']
378 game_data['stage'] = "init" 455 game_data['stage'] = "init"
379 game_data['first_player'] = None #first player for the current trick 456 game_data['first_player'] = None #first player for the current trick
457 game_data['contrat'] = None
380 hand = game_data['hand'] = {} 458 hand = game_data['hand'] = {}
381 hand_size = game_data['hand_size'] 459 hand_size = game_data['hand_size']
382 chien = game_data['chien'] = [] 460 chien = game_data['chien'] = []
383 for i in range(4): #TODO: distribute according to real Tarot rules (3 by 3 counter-clockwise, 1 card at once to chien) 461 for i in range(4): #TODO: distribute according to real Tarot rules (3 by 3 counter-clockwise, 1 card at once to chien)
384 hand[players[i]] = deck[0:hand_size] 462 hand[players[i]] = deck[0:hand_size]
459 idx_pl = self.contrats.index(contrat) 537 idx_pl = self.contrats.index(contrat)
460 if idx_pl > idx_best: 538 if idx_pl > idx_best:
461 best_contrat[0] = player 539 best_contrat[0] = player
462 best_contrat[1] = contrat 540 best_contrat[1] = contrat
463 debug (_("%(player)s win the bid with %(contrat)s") % {'player':best_contrat[0],'contrat':best_contrat[1]}) 541 debug (_("%(player)s win the bid with %(contrat)s") % {'player':best_contrat[0],'contrat':best_contrat[1]})
542 game_data['contrat'] = best_contrat[1]
464 #Time to show the chien to everybody 543 #Time to show the chien to everybody
465 to_jid = jid.JID(room_jid.userhost()) #FIXME: gof: 544 to_jid = jid.JID(room_jid.userhost()) #FIXME: gof:
466 mess = self.createGameElt(to_jid) 545 mess = self.createGameElt(to_jid)
467 chien_elt = mess.firstChildElement().addChild(self.__list_to_xml(game_data['chien'], 'chien')) 546 chien_elt = mess.firstChildElement().addChild(self.__list_to_xml(game_data['chien'], 'chien'))
468 chien_elt['attaquant'] = best_contrat[0] 547 chien_elt['attaquant'] = best_contrat[0]
482 elif elt.name == 'cards_played': 561 elif elt.name == 'cards_played':
483 if game_data['stage'] == "ecart": 562 if game_data['stage'] == "ecart":
484 #TODO: check validity of écart (no king, no oulder, cards must be in player hand) 563 #TODO: check validity of écart (no king, no oulder, cards must be in player hand)
485 #TODO: show atouts (trumps) if player put some in écart 564 #TODO: show atouts (trumps) if player put some in écart
486 assert (game_data['attaquant'] == elt['player']) #TODO: throw an xml error here 565 assert (game_data['attaquant'] == elt['player']) #TODO: throw an xml error here
487 players_data[elt['player']]['levees'].extend(Card.from_tuples(self.__xml_to_list(elt))) 566 list_cards = self.__xml_to_list(elt)
567 #FIXME: gof: manage Garde Sans & Garde Contre cases
568 players_data[elt['player']]['levees'].extend(Card.from_tuples(list_cards)) #we add the chien to attaquant's levées
569 for card in list_cards:
570 game_data['hand'][elt['player']].remove(card)
488 game_data['stage'] = "play" 571 game_data['stage'] = "play"
489 next_player_idx = game_data['current_player'] = (game_data['init_player'] + 1) % len(game_data['players']) #the player after the dealer start 572 next_player_idx = game_data['current_player'] = (game_data['init_player'] + 1) % len(game_data['players']) #the player after the dealer start
490 game_data['first_player'] = next_player = game_data['players'][next_player_idx] 573 game_data['first_player'] = next_player = game_data['players'][next_player_idx]
491 to_jid = jid.JID(room_jid.userhost()+"/"+next_player) #FIXME: gof: 574 to_jid = jid.JID(room_jid.userhost()+"/"+next_player) #FIXME: gof:
492 mess = self.createGameElt(to_jid) 575 mess = self.createGameElt(to_jid)
493 yourturn_elt = mess.firstChildElement().addElement('your_turn') 576 yourturn_elt = mess.firstChildElement().addElement('your_turn')
494 self.host.profiles[profile].xmlstream.send(mess) 577 self.host.profiles[profile].xmlstream.send(mess)
495 elif game_data['stage'] == "play": 578 elif game_data['stage'] == "play":
496 current_player = game_data['players'][game_data['current_player']] 579 current_player = game_data['players'][game_data['current_player']]
497 #assert (elt['player'] == current_player) #TODO: throw xml error here
498 cards = self.__xml_to_list(elt) 580 cards = self.__xml_to_list(elt)
499 581
500 if mess_elt['type'] == 'groupchat': 582 if mess_elt['type'] == 'groupchat':
501 self.host.bridge.tarotGameCardsPlayed(room_jid.userhost(), elt['player'], self.__xml_to_list(elt), profile) 583 self.host.bridge.tarotGameCardsPlayed(room_jid.userhost(), elt['player'], self.__xml_to_list(elt), profile)
502 else: 584 else:
511 playcard_elt = mess.firstChildElement().addChild(elt) 593 playcard_elt = mess.firstChildElement().addChild(elt)
512 self.host.profiles[profile].xmlstream.send(mess) 594 self.host.profiles[profile].xmlstream.send(mess)
513 595
514 #Did everybody played ? 596 #Did everybody played ?
515 played = [players_data[player]['played'] for player in game_data['players']] 597 played = [players_data[player]['played'] for player in game_data['players']]
516 if not played.count(None): 598 if all(played):
517 #everybody played 599 #everybody has played
518 winner = self.__winner(game_data) 600 winner = self.__winner(game_data)
519 debug (_('The winner of this trick is %s') % winner) 601 debug (_('The winner of this trick is %s') % winner)
520 #the winner win the trick 602 #the winner win the trick
521 self.__excuse_hack(game_data, played, winner) 603 self.__excuse_hack(game_data, played, winner)
522 players_data[elt['player']]['levees'].extend(played) 604 players_data[elt['player']]['levees'].extend(played)
523 #nothing left on the table 605 #nothing left on the table
524 for player in game_data['players']: 606 for player in game_data['players']:
525 players_data[player]['played'] = None 607 players_data[player]['played'] = None
526 if len(game_data['hand'][current_player]) == 0: 608 if len(game_data['hand'][current_player]) == 0:
527 #no card lef: the game is finished 609 #no card lef: the game is finished
528 self.__calculate_scores(game_data) 610 to_jid = jid.JID(room_jid.userhost()) #FIXME: gof:
611 mess = self.createGameElt(to_jid)
612 chien_elt = mess.firstChildElement().addChild(self.__give_scores(*self.__calculate_scores(game_data)))
613 self.host.profiles[profile].xmlstream.send(mess)
529 return 614 return
530 #next player is the winner 615 #next player is the winner
531 next_player = game_data['first_player'] = self.__next_player(game_data, winner) 616 next_player = game_data['first_player'] = self.__next_player(game_data, winner)
532 else: 617 else:
533 next_player = self.__next_player(game_data) 618 next_player = self.__next_player(game_data)
539 self.host.profiles[profile].xmlstream.send(mess) 624 self.host.profiles[profile].xmlstream.send(mess)
540 625
541 elif elt.name == 'your_turn': 626 elif elt.name == 'your_turn':
542 self.host.bridge.tarotGameYourTurn(room_jid.userhost(), profile) 627 self.host.bridge.tarotGameYourTurn(room_jid.userhost(), profile)
543 628
544 629 elif elt.name == 'score':
630 form_elt = elt.elements(name='x',uri='jabber:x:data').next()
631 winners = []
632 loosers = []
633 for winner in elt.elements(name='winner', uri=''):
634 winners.append(unicode(winner))
635 for looser in elt.elements(name='looser', uri=''):
636 loosers.append(unicode(looser))
637 form = data_form.Form.fromElement(form_elt)
638 xml_data = XMLTools.dataForm2xml(form)
639 self.host.bridge.tarotGameScore(room_jid.userhost(), xml_data, winners, loosers, profile)
640
545 def getHandler(self, profile): 641 def getHandler(self, profile):
546 return CardGameHandler(self) 642 return CardGameHandler(self)
547
548
549 643
550 class CardGameHandler (XMPPHandler): 644 class CardGameHandler (XMPPHandler):
551 implements(iwokkel.IDisco) 645 implements(iwokkel.IDisco)
552 646
553 def __init__(self, plugin_parent): 647 def __init__(self, plugin_parent):