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