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.
This commit is contained in:
parent
2e6c6142d5
commit
d663da0526
2 changed files with 198 additions and 19 deletions
|
|
@ -33,7 +33,7 @@ CARD_HEIGHT = 136
|
|||
MIN_WIDTH = 950 #Minimum size of the panel
|
||||
MIN_HEIGHT = 500
|
||||
|
||||
suits_order = ['pique', 'coeur', 'trefle', 'carreau', 'atout'] #I have swith the usual order 'trefle' and 'carreau' because card are more easy to see if couleur change (black, red, black, red)
|
||||
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)
|
||||
values_order = map(str,range(1,11))+["valet","cavalier","dame","roi"]
|
||||
|
||||
class Card():
|
||||
|
|
|
|||
|
|
@ -59,6 +59,61 @@ PLUGIN_INFO = {
|
|||
"description": _("""Implementation of Tarot card game""")
|
||||
}
|
||||
|
||||
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)
|
||||
values_order = map(str,range(1,11))+["valet","cavalier","dame","roi"]
|
||||
|
||||
class Card():
|
||||
"""This class is used to represent a car logically"""
|
||||
#TODO: move this in a library in tools, and share this with frontends (e.g. card_game in wix use the same class)
|
||||
|
||||
def __init__(self, tuple_card):
|
||||
"""@param tuple_card: tuple (suit, value)"""
|
||||
self.suit, self.value = tuple_card
|
||||
self.bout = True if self.suit=="atout" and self.value in ["1","21","excuse"] else False
|
||||
if self.bout or self.value == "roi":
|
||||
self.points = 4.5
|
||||
elif self.value == "dame":
|
||||
self.points = 3.5
|
||||
elif self.value == "cavalier":
|
||||
self.points = 2.5
|
||||
elif self.value == "valet":
|
||||
self.points = 1.5
|
||||
else:
|
||||
self.points = 0.5
|
||||
|
||||
def get_tuple(self):
|
||||
return (self.suit,self.value)
|
||||
|
||||
@staticmethod
|
||||
def from_tuples(tuple_list):
|
||||
result = []
|
||||
for card_tuple in tuple_list:
|
||||
result.append(Card(card_tuple))
|
||||
return result
|
||||
|
||||
def __cmp__(self, other):
|
||||
if other == None:
|
||||
return 1
|
||||
if self.suit != other.suit:
|
||||
idx1 = suits_order.index(self.suit)
|
||||
idx2 = suits_order.index(other.suit)
|
||||
return idx1.__cmp__(idx2)
|
||||
if self.suit == 'atout':
|
||||
if self.value == other.value == 'excuse':
|
||||
return 0
|
||||
if self.value == 'excuse':
|
||||
return -1
|
||||
if other.value == 'excuse':
|
||||
return 1
|
||||
return int(self.value).__cmp__(int(other.value))
|
||||
#at this point we have the same suit which is not 'atout'
|
||||
idx1 = values_order.index(self.value)
|
||||
idx2 = values_order.index(other.value)
|
||||
return idx1.__cmp__(idx2)
|
||||
|
||||
def __str__(self):
|
||||
return "[%s,%s]" % (self.suit, self.value)
|
||||
|
||||
class Tarot():
|
||||
|
||||
def __init__(self, host):
|
||||
|
|
@ -129,11 +184,114 @@ class Tarot():
|
|||
contrat_elt.addChild(form.toElement())
|
||||
return contrat_elt
|
||||
|
||||
def __next_player(self, game_data):
|
||||
"""It's next player turn
|
||||
Increment player number & return player name"""
|
||||
pl_idx = game_data['current_player'] = (game_data['current_player'] + 1) % len(game_data['players'])
|
||||
return game_data['players'][pl_idx]
|
||||
def __next_player(self, game_data, next_pl = None):
|
||||
"""Increment player number & return player name
|
||||
@param next_pl: if given, then next_player is forced to this one
|
||||
"""
|
||||
if next_pl:
|
||||
game_data['current_player'] = game_data['players'].index(next_pl)
|
||||
return next_pl
|
||||
else:
|
||||
pl_idx = game_data['current_player'] = (game_data['current_player'] + 1) % len(game_data['players'])
|
||||
return game_data['players'][pl_idx]
|
||||
|
||||
def __winner(self, game_data):
|
||||
"""give the nick of the player who win this trick"""
|
||||
players_data = game_data['players_data']
|
||||
first = game_data['first_player']
|
||||
first_idx = game_data['players'].index(first)
|
||||
suit_asked = None
|
||||
strongest = None
|
||||
winner = None
|
||||
for idx in [(first_idx + i) % 4 for i in range(4)]:
|
||||
player = game_data['players'][idx]
|
||||
card = players_data[player]['played']
|
||||
if card.value == "excuse":
|
||||
continue
|
||||
if suit_asked == None:
|
||||
suit_asked = card.suit
|
||||
if (card.suit == suit_asked or card.suit == "atout") and card > strongest:
|
||||
strongest = card
|
||||
winner = player
|
||||
assert (winner)
|
||||
return winner
|
||||
|
||||
def __excuse_hack(self, game_data, played, winner):
|
||||
"""give a low card to other team and keep excuse if trick is lost"""
|
||||
#TODO: manage the case where excuse is played on the last trick (and lost)
|
||||
#TODO: gof: manage excuse (fool)
|
||||
players_data = game_data['players_data']
|
||||
excuse = Card(("atout","excuse"))
|
||||
for player in game_data['players']:
|
||||
if players_data[player]['wait_for_low']:
|
||||
#the excuse owner has to give a card to somebody
|
||||
if winner == player:
|
||||
#the excuse owner win the trick, we check if we have something to give
|
||||
for card in played:
|
||||
if card.points == 0.5:
|
||||
pl_waiting = players_data[player]['wait_for_low']
|
||||
played.remove(card)
|
||||
players_data[pl_waiting]['levees'].append(card)
|
||||
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})
|
||||
break
|
||||
return
|
||||
|
||||
if not excuse in played:
|
||||
return
|
||||
|
||||
excuse_player = None
|
||||
for player in game_data['players']:
|
||||
if players_data[player]['played'] == excuse:
|
||||
excuse_player = player
|
||||
break
|
||||
|
||||
if excuse_player == winner:
|
||||
return #the excuse player win the trick, nothing to do
|
||||
|
||||
#first we remove the excuse from played cards
|
||||
played.remove(excuse)
|
||||
#then we give it back to the original owner
|
||||
owner_levees = players_data[excuse_player]['levees']
|
||||
owner_levees.append(excuse)
|
||||
#finally we give a low card to the trick winner
|
||||
low_card = None
|
||||
for card_idx in range(len(owner_levees)-1, -1, -1):
|
||||
if owner_levees[card_idx].points == 0.5:
|
||||
low_card = owner_levees[card_idx]
|
||||
del owner_levees[card_idx]
|
||||
players_data[winner]['levees'].append(low_card)
|
||||
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})
|
||||
break
|
||||
if not low_card: #The player has no low card yet
|
||||
#TODO: manage case when player never win a trick with low card
|
||||
players_data[excuse_player]['wait_for_low'] = winner
|
||||
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})
|
||||
|
||||
|
||||
def __calculate_scores(self, game_data):
|
||||
"""The game is finished, time to know who won :)"""
|
||||
players_data = game_data['players_data']
|
||||
levees = players_data[game_data['attaquant']]['levees']
|
||||
score = 0
|
||||
nb_bouts = 0
|
||||
for card in levees:
|
||||
if card.bout:
|
||||
nb_bouts +=1
|
||||
score += card.points
|
||||
point_limit = None
|
||||
if nb_bouts == 3:
|
||||
point_limit = 36
|
||||
elif nb_bouts == 2:
|
||||
point_limit = 41
|
||||
elif nb_bouts == 1:
|
||||
point_limit = 51
|
||||
else:
|
||||
point_limit = 56
|
||||
victory = (score >= point_limit)
|
||||
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'})
|
||||
#pdb.set_trace()
|
||||
|
||||
|
||||
|
||||
def createGame(self, room_jid_param, players, profile_key='@DEFAULT@'):
|
||||
"""Create a new game"""
|
||||
|
|
@ -218,6 +376,7 @@ class Tarot():
|
|||
players_data = game_data['players_data']
|
||||
current_player = game_data['current_player']
|
||||
game_data['stage'] = "init"
|
||||
game_data['first_player'] = None #first player for the current trick
|
||||
hand = game_data['hand'] = {}
|
||||
hand_size = game_data['hand_size']
|
||||
chien = game_data['chien'] = []
|
||||
|
|
@ -234,6 +393,8 @@ class Tarot():
|
|||
self.host.profiles[profile].xmlstream.send(mess)
|
||||
players_data[player]['contrat'] = None
|
||||
players_data[player]['levees'] = [] #cards won
|
||||
players_data[player]['played'] = None #card on the table
|
||||
players_data[player]['wait_for_low'] = None #Used when a player wait for a low card because of excuse
|
||||
|
||||
pl_idx = game_data['current_player'] = (game_data['init_player'] + 1) % len(players) #the player after the dealer start
|
||||
player = players[pl_idx]
|
||||
|
|
@ -290,7 +451,7 @@ class Tarot():
|
|||
mess.firstChildElement().addChild(self.__ask_contrat())
|
||||
self.host.profiles[profile].xmlstream.send(mess)
|
||||
else:
|
||||
#TODO: manage "everybody pass" case
|
||||
#TODO: gof: manage "everybody pass" case
|
||||
best_contrat = [None, "Passe"]
|
||||
for player in game_data['players']:
|
||||
contrat = players_data[player]['contrat']
|
||||
|
|
@ -314,7 +475,6 @@ class Tarot():
|
|||
elif elt.name == 'chien': #we have received the chien
|
||||
debug (_("tarot: chien received"))
|
||||
data = {"attaquant":elt['attaquant']}
|
||||
players_data = game_data['players_data']
|
||||
game_data['stage'] = "ecart"
|
||||
game_data['attaquant'] = elt['attaquant']
|
||||
self.host.bridge.tarotGameShowCards(room_jid.userhost(), "chien", self.__xml_to_list(elt), data, profile)
|
||||
|
|
@ -324,10 +484,10 @@ class Tarot():
|
|||
#TODO: check validity of écart (no king, no oulder, cards must be in player hand)
|
||||
#TODO: show atouts (trumps) if player put some in écart
|
||||
assert (game_data['attaquant'] == elt['player']) #TODO: throw an xml error here
|
||||
players_data[elt['player']]['levees'].extend(self.__xml_to_list(elt))
|
||||
players_data[elt['player']]['levees'].extend(Card.from_tuples(self.__xml_to_list(elt)))
|
||||
game_data['stage'] = "play"
|
||||
next_player_idx = game_data['current_player'] = (game_data['init_player'] + 1) % len(game_data['players']) #the player after the dealer start
|
||||
next_player = game_data['players'][next_player_idx]
|
||||
game_data['first_player'] = next_player = game_data['players'][next_player_idx]
|
||||
to_jid = jid.JID(room_jid.userhost()+"/"+next_player) #FIXME: gof:
|
||||
mess = self.createGameElt(to_jid)
|
||||
yourturn_elt = mess.firstChildElement().addElement('your_turn')
|
||||
|
|
@ -336,29 +496,48 @@ class Tarot():
|
|||
current_player = game_data['players'][game_data['current_player']]
|
||||
#assert (elt['player'] == current_player) #TODO: throw xml error here
|
||||
cards = self.__xml_to_list(elt)
|
||||
#TODO: check card validity and send error mess if necessary
|
||||
if mess_elt['type'] != 'groupchat':
|
||||
|
||||
if mess_elt['type'] == 'groupchat':
|
||||
self.host.bridge.tarotGameCardsPlayed(room_jid.userhost(), elt['player'], self.__xml_to_list(elt), profile)
|
||||
else:
|
||||
#TODO: check card validity and send error mess if necessary
|
||||
#the card played is ok, we forward it to everybody
|
||||
#first we remove it from the hand
|
||||
#first we remove it from the hand and put in on the table
|
||||
game_data['hand'][current_player].remove(cards[0])
|
||||
players_data[current_player]['played'] = Card(cards[0])
|
||||
|
||||
#then we forward the message
|
||||
mess = self.createGameElt(room_jid)
|
||||
playcard_elt = mess.firstChildElement().addChild(elt)
|
||||
self.host.profiles[profile].xmlstream.send(mess)
|
||||
|
||||
#Did everybody played ?
|
||||
played = [players_data[player]['played'] for player in game_data['players']]
|
||||
if not played.count(None):
|
||||
#everybody played
|
||||
winner = self.__winner(game_data)
|
||||
debug (_('The winner of this trick is %s') % winner)
|
||||
#the winner win the trick
|
||||
self.__excuse_hack(game_data, played, winner)
|
||||
players_data[elt['player']]['levees'].extend(played)
|
||||
#nothing left on the table
|
||||
for player in game_data['players']:
|
||||
players_data[player]['played'] = None
|
||||
if len(game_data['hand'][current_player]) == 0:
|
||||
#no card lef: the game is finished
|
||||
self.__calculate_scores(game_data)
|
||||
return
|
||||
#next player is the winner
|
||||
next_player = game_data['first_player'] = self.__next_player(game_data, winner)
|
||||
else:
|
||||
next_player = self.__next_player(game_data)
|
||||
|
||||
#finally, we tell to the next player to play
|
||||
next_player = self.__next_player(game_data)
|
||||
to_jid = jid.JID(room_jid.userhost()+"/"+next_player) #FIXME: gof:
|
||||
mess = self.createGameElt(to_jid)
|
||||
yourturn_elt = mess.firstChildElement().addElement('your_turn')
|
||||
self.host.profiles[profile].xmlstream.send(mess)
|
||||
else:
|
||||
self.host.bridge.tarotGameCardsPlayed(room_jid.userhost(), elt['player'], self.__xml_to_list(elt), profile)
|
||||
|
||||
|
||||
|
||||
|
||||
elif elt.name == 'your_turn':
|
||||
self.host.bridge.tarotGameYourTurn(room_jid.userhost(), profile)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue