comparison plugins/plugin_misc_tarot.py @ 94:1eb5ccead43c

Tarot game: basic trick - plugin tarot: it's now possible to make a basic game until the end \o/. Score are calculated but not sent to players yet.
author Goffi <goffi@goffi.org>
date Tue, 01 Jun 2010 18:16:15 +0930
parents 2f87651a5ad8
children be206a3d1a9b
comparison
equal deleted inserted replaced
93:2f87651a5ad8 94:1eb5ccead43c
56 "dependencies": ["XEP_0045"], 56 "dependencies": ["XEP_0045"],
57 "main": "Tarot", 57 "main": "Tarot",
58 "handler": "yes", 58 "handler": "yes",
59 "description": _("""Implementation of Tarot card game""") 59 "description": _("""Implementation of Tarot card game""")
60 } 60 }
61
62 suits_order = ['pique', 'coeur', 'trefle', 'carreau', 'atout'] #I have swith the usual order 'trefle' and 'carreau' because card are more easy to see if suit colour change (black, red, black, red)
63 values_order = map(str,range(1,11))+["valet","cavalier","dame","roi"]
64
65 class Card():
66 """This class is used to represent a car logically"""
67 #TODO: move this in a library in tools, and share this with frontends (e.g. card_game in wix use the same class)
68
69 def __init__(self, tuple_card):
70 """@param tuple_card: tuple (suit, value)"""
71 self.suit, self.value = tuple_card
72 self.bout = True if self.suit=="atout" and self.value in ["1","21","excuse"] else False
73 if self.bout or self.value == "roi":
74 self.points = 4.5
75 elif self.value == "dame":
76 self.points = 3.5
77 elif self.value == "cavalier":
78 self.points = 2.5
79 elif self.value == "valet":
80 self.points = 1.5
81 else:
82 self.points = 0.5
83
84 def get_tuple(self):
85 return (self.suit,self.value)
86
87 @staticmethod
88 def from_tuples(tuple_list):
89 result = []
90 for card_tuple in tuple_list:
91 result.append(Card(card_tuple))
92 return result
93
94 def __cmp__(self, other):
95 if other == None:
96 return 1
97 if self.suit != other.suit:
98 idx1 = suits_order.index(self.suit)
99 idx2 = suits_order.index(other.suit)
100 return idx1.__cmp__(idx2)
101 if self.suit == 'atout':
102 if self.value == other.value == 'excuse':
103 return 0
104 if self.value == 'excuse':
105 return -1
106 if other.value == 'excuse':
107 return 1
108 return int(self.value).__cmp__(int(other.value))
109 #at this point we have the same suit which is not 'atout'
110 idx1 = values_order.index(self.value)
111 idx2 = values_order.index(other.value)
112 return idx1.__cmp__(idx2)
113
114 def __str__(self):
115 return "[%s,%s]" % (self.suit, self.value)
61 116
62 class Tarot(): 117 class Tarot():
63 118
64 def __init__(self, host): 119 def __init__(self, host):
65 info(_("Plugin Tarot initialization")) 120 info(_("Plugin Tarot initialization"))
127 field = data_form.Field('list-single', 'contrat', options=map(data_form.Option, self.contrats), required=True) 182 field = data_form.Field('list-single', 'contrat', options=map(data_form.Option, self.contrats), required=True)
128 form.addField(field) 183 form.addField(field)
129 contrat_elt.addChild(form.toElement()) 184 contrat_elt.addChild(form.toElement())
130 return contrat_elt 185 return contrat_elt
131 186
132 def __next_player(self, game_data): 187 def __next_player(self, game_data, next_pl = None):
133 """It's next player turn 188 """Increment player number & return player name
134 Increment player number & return player name""" 189 @param next_pl: if given, then next_player is forced to this one
135 pl_idx = game_data['current_player'] = (game_data['current_player'] + 1) % len(game_data['players']) 190 """
136 return game_data['players'][pl_idx] 191 if next_pl:
192 game_data['current_player'] = game_data['players'].index(next_pl)
193 return next_pl
194 else:
195 pl_idx = game_data['current_player'] = (game_data['current_player'] + 1) % len(game_data['players'])
196 return game_data['players'][pl_idx]
197
198 def __winner(self, game_data):
199 """give the nick of the player who win this trick"""
200 players_data = game_data['players_data']
201 first = game_data['first_player']
202 first_idx = game_data['players'].index(first)
203 suit_asked = None
204 strongest = None
205 winner = None
206 for idx in [(first_idx + i) % 4 for i in range(4)]:
207 player = game_data['players'][idx]
208 card = players_data[player]['played']
209 if card.value == "excuse":
210 continue
211 if suit_asked == None:
212 suit_asked = card.suit
213 if (card.suit == suit_asked or card.suit == "atout") and card > strongest:
214 strongest = card
215 winner = player
216 assert (winner)
217 return winner
218
219 def __excuse_hack(self, game_data, played, winner):
220 """give a low card to other team and keep excuse if trick is lost"""
221 #TODO: manage the case where excuse is played on the last trick (and lost)
222 #TODO: gof: manage excuse (fool)
223 players_data = game_data['players_data']
224 excuse = Card(("atout","excuse"))
225 for player in game_data['players']:
226 if players_data[player]['wait_for_low']:
227 #the excuse owner has to give a card to somebody
228 if winner == player:
229 #the excuse owner win the trick, we check if we have something to give
230 for card in played:
231 if card.points == 0.5:
232 pl_waiting = players_data[player]['wait_for_low']
233 played.remove(card)
234 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})
236 break
237 return
238
239 if not excuse in played:
240 return
241
242 excuse_player = None
243 for player in game_data['players']:
244 if players_data[player]['played'] == excuse:
245 excuse_player = player
246 break
247
248 if excuse_player == winner:
249 return #the excuse player win the trick, nothing to do
250
251 #first we remove the excuse from played cards
252 played.remove(excuse)
253 #then we give it back to the original owner
254 owner_levees = players_data[excuse_player]['levees']
255 owner_levees.append(excuse)
256 #finally we give a low card to the trick winner
257 low_card = None
258 for card_idx in range(len(owner_levees)-1, -1, -1):
259 if owner_levees[card_idx].points == 0.5:
260 low_card = owner_levees[card_idx]
261 del owner_levees[card_idx]
262 players_data[winner]['levees'].append(low_card)
263 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})
264 break
265 if not low_card: #The player has no low card yet
266 #TODO: manage case when player never win a trick with low card
267 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})
269
270
271 def __calculate_scores(self, game_data):
272 """The game is finished, time to know who won :)"""
273 players_data = game_data['players_data']
274 levees = players_data[game_data['attaquant']]['levees']
275 score = 0
276 nb_bouts = 0
277 for card in levees:
278 if card.bout:
279 nb_bouts +=1
280 score += card.points
281 point_limit = None
282 if nb_bouts == 3:
283 point_limit = 36
284 elif nb_bouts == 2:
285 point_limit = 41
286 elif nb_bouts == 1:
287 point_limit = 51
288 else:
289 point_limit = 56
290 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'})
292 #pdb.set_trace()
293
294
137 295
138 def createGame(self, room_jid_param, players, profile_key='@DEFAULT@'): 296 def createGame(self, room_jid_param, players, profile_key='@DEFAULT@'):
139 """Create a new game""" 297 """Create a new game"""
140 debug (_("Creating Tarot game")) 298 debug (_("Creating Tarot game"))
141 room_jid = jid.JID(room_jid_param) 299 room_jid = jid.JID(room_jid_param)
216 game_data = self.games[room_jid.userhost()] 374 game_data = self.games[room_jid.userhost()]
217 players = game_data['players'] 375 players = game_data['players']
218 players_data = game_data['players_data'] 376 players_data = game_data['players_data']
219 current_player = game_data['current_player'] 377 current_player = game_data['current_player']
220 game_data['stage'] = "init" 378 game_data['stage'] = "init"
379 game_data['first_player'] = None #first player for the current trick
221 hand = game_data['hand'] = {} 380 hand = game_data['hand'] = {}
222 hand_size = game_data['hand_size'] 381 hand_size = game_data['hand_size']
223 chien = game_data['chien'] = [] 382 chien = game_data['chien'] = []
224 for i in range(4): #TODO: distribute according to real Tarot rules (3 by 3 counter-clockwise, 1 card at once to 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)
225 hand[players[i]] = deck[0:hand_size] 384 hand[players[i]] = deck[0:hand_size]
232 mess = self.createGameElt(to_jid) 391 mess = self.createGameElt(to_jid)
233 mess.firstChildElement().addChild(self.__list_to_xml(hand[player], 'hand')) 392 mess.firstChildElement().addChild(self.__list_to_xml(hand[player], 'hand'))
234 self.host.profiles[profile].xmlstream.send(mess) 393 self.host.profiles[profile].xmlstream.send(mess)
235 players_data[player]['contrat'] = None 394 players_data[player]['contrat'] = None
236 players_data[player]['levees'] = [] #cards won 395 players_data[player]['levees'] = [] #cards won
396 players_data[player]['played'] = None #card on the table
397 players_data[player]['wait_for_low'] = None #Used when a player wait for a low card because of excuse
237 398
238 pl_idx = game_data['current_player'] = (game_data['init_player'] + 1) % len(players) #the player after the dealer start 399 pl_idx = game_data['current_player'] = (game_data['init_player'] + 1) % len(players) #the player after the dealer start
239 player = players[pl_idx] 400 player = players[pl_idx]
240 to_jid = jid.JID(room_jid.userhost()+"/"+player) #FIXME: gof: 401 to_jid = jid.JID(room_jid.userhost()+"/"+player) #FIXME: gof:
241 mess = self.createGameElt(to_jid) 402 mess = self.createGameElt(to_jid)
288 to_jid = jid.JID(room_jid.userhost()+"/"+player) #FIXME: gof: 449 to_jid = jid.JID(room_jid.userhost()+"/"+player) #FIXME: gof:
289 mess = self.createGameElt(to_jid) 450 mess = self.createGameElt(to_jid)
290 mess.firstChildElement().addChild(self.__ask_contrat()) 451 mess.firstChildElement().addChild(self.__ask_contrat())
291 self.host.profiles[profile].xmlstream.send(mess) 452 self.host.profiles[profile].xmlstream.send(mess)
292 else: 453 else:
293 #TODO: manage "everybody pass" case 454 #TODO: gof: manage "everybody pass" case
294 best_contrat = [None, "Passe"] 455 best_contrat = [None, "Passe"]
295 for player in game_data['players']: 456 for player in game_data['players']:
296 contrat = players_data[player]['contrat'] 457 contrat = players_data[player]['contrat']
297 idx_best = self.contrats.index(best_contrat[1]) 458 idx_best = self.contrats.index(best_contrat[1])
298 idx_pl = self.contrats.index(contrat) 459 idx_pl = self.contrats.index(contrat)
312 del game_data['chien'][:] 473 del game_data['chien'][:]
313 474
314 elif elt.name == 'chien': #we have received the chien 475 elif elt.name == 'chien': #we have received the chien
315 debug (_("tarot: chien received")) 476 debug (_("tarot: chien received"))
316 data = {"attaquant":elt['attaquant']} 477 data = {"attaquant":elt['attaquant']}
317 players_data = game_data['players_data']
318 game_data['stage'] = "ecart" 478 game_data['stage'] = "ecart"
319 game_data['attaquant'] = elt['attaquant'] 479 game_data['attaquant'] = elt['attaquant']
320 self.host.bridge.tarotGameShowCards(room_jid.userhost(), "chien", self.__xml_to_list(elt), data, profile) 480 self.host.bridge.tarotGameShowCards(room_jid.userhost(), "chien", self.__xml_to_list(elt), data, profile)
321 481
322 elif elt.name == 'cards_played': 482 elif elt.name == 'cards_played':
323 if game_data['stage'] == "ecart": 483 if game_data['stage'] == "ecart":
324 #TODO: check validity of écart (no king, no oulder, cards must be in player hand) 484 #TODO: check validity of écart (no king, no oulder, cards must be in player hand)
325 #TODO: show atouts (trumps) if player put some in écart 485 #TODO: show atouts (trumps) if player put some in écart
326 assert (game_data['attaquant'] == elt['player']) #TODO: throw an xml error here 486 assert (game_data['attaquant'] == elt['player']) #TODO: throw an xml error here
327 players_data[elt['player']]['levees'].extend(self.__xml_to_list(elt)) 487 players_data[elt['player']]['levees'].extend(Card.from_tuples(self.__xml_to_list(elt)))
328 game_data['stage'] = "play" 488 game_data['stage'] = "play"
329 next_player_idx = game_data['current_player'] = (game_data['init_player'] + 1) % len(game_data['players']) #the player after the dealer start 489 next_player_idx = game_data['current_player'] = (game_data['init_player'] + 1) % len(game_data['players']) #the player after the dealer start
330 next_player = game_data['players'][next_player_idx] 490 game_data['first_player'] = next_player = game_data['players'][next_player_idx]
331 to_jid = jid.JID(room_jid.userhost()+"/"+next_player) #FIXME: gof: 491 to_jid = jid.JID(room_jid.userhost()+"/"+next_player) #FIXME: gof:
332 mess = self.createGameElt(to_jid) 492 mess = self.createGameElt(to_jid)
333 yourturn_elt = mess.firstChildElement().addElement('your_turn') 493 yourturn_elt = mess.firstChildElement().addElement('your_turn')
334 self.host.profiles[profile].xmlstream.send(mess) 494 self.host.profiles[profile].xmlstream.send(mess)
335 elif game_data['stage'] == "play": 495 elif game_data['stage'] == "play":
336 current_player = game_data['players'][game_data['current_player']] 496 current_player = game_data['players'][game_data['current_player']]
337 #assert (elt['player'] == current_player) #TODO: throw xml error here 497 #assert (elt['player'] == current_player) #TODO: throw xml error here
338 cards = self.__xml_to_list(elt) 498 cards = self.__xml_to_list(elt)
339 #TODO: check card validity and send error mess if necessary 499
340 if mess_elt['type'] != 'groupchat': 500 if mess_elt['type'] == 'groupchat':
501 self.host.bridge.tarotGameCardsPlayed(room_jid.userhost(), elt['player'], self.__xml_to_list(elt), profile)
502 else:
503 #TODO: check card validity and send error mess if necessary
341 #the card played is ok, we forward it to everybody 504 #the card played is ok, we forward it to everybody
342 #first we remove it from the hand 505 #first we remove it from the hand and put in on the table
343 game_data['hand'][current_player].remove(cards[0]) 506 game_data['hand'][current_player].remove(cards[0])
507 players_data[current_player]['played'] = Card(cards[0])
344 508
345 #then we forward the message 509 #then we forward the message
346 mess = self.createGameElt(room_jid) 510 mess = self.createGameElt(room_jid)
347 playcard_elt = mess.firstChildElement().addChild(elt) 511 playcard_elt = mess.firstChildElement().addChild(elt)
348 self.host.profiles[profile].xmlstream.send(mess) 512 self.host.profiles[profile].xmlstream.send(mess)
349 513
514 #Did everybody played ?
515 played = [players_data[player]['played'] for player in game_data['players']]
516 if not played.count(None):
517 #everybody played
518 winner = self.__winner(game_data)
519 debug (_('The winner of this trick is %s') % winner)
520 #the winner win the trick
521 self.__excuse_hack(game_data, played, winner)
522 players_data[elt['player']]['levees'].extend(played)
523 #nothing left on the table
524 for player in game_data['players']:
525 players_data[player]['played'] = None
526 if len(game_data['hand'][current_player]) == 0:
527 #no card lef: the game is finished
528 self.__calculate_scores(game_data)
529 return
530 #next player is the winner
531 next_player = game_data['first_player'] = self.__next_player(game_data, winner)
532 else:
533 next_player = self.__next_player(game_data)
534
350 #finally, we tell to the next player to play 535 #finally, we tell to the next player to play
351 next_player = self.__next_player(game_data)
352 to_jid = jid.JID(room_jid.userhost()+"/"+next_player) #FIXME: gof: 536 to_jid = jid.JID(room_jid.userhost()+"/"+next_player) #FIXME: gof:
353 mess = self.createGameElt(to_jid) 537 mess = self.createGameElt(to_jid)
354 yourturn_elt = mess.firstChildElement().addElement('your_turn') 538 yourturn_elt = mess.firstChildElement().addElement('your_turn')
355 self.host.profiles[profile].xmlstream.send(mess) 539 self.host.profiles[profile].xmlstream.send(mess)
356 else: 540
357 self.host.bridge.tarotGameCardsPlayed(room_jid.userhost(), elt['player'], self.__xml_to_list(elt), profile)
358
359
360
361
362 elif elt.name == 'your_turn': 541 elif elt.name == 'your_turn':
363 self.host.bridge.tarotGameYourTurn(room_jid.userhost(), profile) 542 self.host.bridge.tarotGameYourTurn(room_jid.userhost(), profile)
364 543
365 544
366 def getHandler(self, profile): 545 def getHandler(self, profile):