Merge pull request #10 from albertoxamin/dev

fix dynamite and herb, add bots and commands
This commit is contained in:
Alberto Xamin 2020-12-07 15:07:41 +01:00 committed by GitHub
commit e4ca4d7b64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 156 additions and 17 deletions

View File

@ -119,6 +119,29 @@ def join_room(sid, room):
@sio.event @sio.event
def chat_message(sid, msg): def chat_message(sid, msg):
ses: Player = sio.get_session(sid) ses: Player = sio.get_session(sid)
if len(msg) > 0:
if msg[0] == '/':
if '/addbot' in msg and not ses.game.started:
if len(msg.split()) > 1:
for _ in range(int(msg.split()[1])):
ses.game.add_player(Player(f'AI_{random.randint(0,100)}', 'bot', sio, bot=True))
else:
ses.game.add_player(Player(f'AI_{random.randint(0,100)}', 'bot', sio, bot=True))
elif '/removebot' in msg and not ses.game.started:
if any([p.is_bot for p in ses.game.players]):
[p for p in ses.game.players if p.is_bot][-1].disconnect()
elif '/suicide' in msg and ses.game.started:
ses.lives = 0
ses.notify_self()
elif '/cancelgame' in msg and ses.game.started:
ses.game.reset()
elif '/gameinfo' in msg:
sio.emit('chat_message', room=sid, data=f'info: {ses.game.__dict__}')
elif '/meinfo' in msg:
sio.emit('chat_message', room=sid, data=f'info: {ses.__dict__}')
else:
sio.emit('chat_message', room=sid, data=f'{msg} COMMAND NOT FOUND')
else:
sio.emit('chat_message', room=ses.game.name, data=f'[{ses.name}]: {msg}') sio.emit('chat_message', room=ses.game.name, data=f'[{ses.name}]: {msg}')
@sio.event @sio.event

View File

@ -23,6 +23,7 @@ class Game:
self.initial_players = 0 self.initial_players = 0
self.password = '' self.password = ''
self.expansions = [] self.expansions = []
self.shutting_down = False
def notify_room(self): def notify_room(self):
if len([p for p in self.players if p.character == None]) != 0: if len([p for p in self.players if p.character == None]) != 0:
@ -44,6 +45,8 @@ class Game:
self.notify_room() self.notify_room()
def add_player(self, player: players.Player): def add_player(self, player: players.Player):
if player.is_bot and len(self.players) >= 8:
return
if player in self.players or len(self.players) >= 10: if player in self.players or len(self.players) >= 10:
return return
if len(self.players) > 7: if len(self.players) > 7:
@ -124,6 +127,7 @@ class Game:
if p != attacker: if p != attacker:
if p.get_banged(attacker=attacker): if p.get_banged(attacker=attacker):
self.waiting_for += 1 self.waiting_for += 1
p.notify_self()
if self.waiting_for == 0: if self.waiting_for == 0:
attacker.pending_action = players.PendingAction.PLAY attacker.pending_action = players.PendingAction.PLAY
attacker.notify_self() attacker.notify_self()
@ -137,23 +141,26 @@ class Game:
if p != attacker: if p != attacker:
if p.get_indians(attacker=attacker): if p.get_indians(attacker=attacker):
self.waiting_for += 1 self.waiting_for += 1
p.notify_self()
if self.waiting_for == 0: if self.waiting_for == 0:
attacker.pending_action = players.PendingAction.PLAY attacker.pending_action = players.PendingAction.PLAY
attacker.notify_self() attacker.notify_self()
def attack(self, attacker: players.Player, target_username:str, double:bool=False): def attack(self, attacker: players.Player, target_username:str, double:bool=False):
if self.players[self.players_map[target_username]].get_banged(attacker=attacker, double=double): if self.get_player_named(target_username).get_banged(attacker=attacker, double=double):
self.readyCount = 0 self.readyCount = 0
self.waiting_for = 1 self.waiting_for = 1
attacker.pending_action = players.PendingAction.WAIT attacker.pending_action = players.PendingAction.WAIT
attacker.notify_self() attacker.notify_self()
self.get_player_named(target_username).notify_self()
def duel(self, attacker: players.Player, target_username:str): def duel(self, attacker: players.Player, target_username:str):
if self.players[self.players_map[target_username]].get_dueled(attacker=attacker): if self.get_player_named(target_username).get_dueled(attacker=attacker):
self.readyCount = 0 self.readyCount = 0
self.waiting_for = 1 self.waiting_for = 1
attacker.pending_action = players.PendingAction.WAIT attacker.pending_action = players.PendingAction.WAIT
attacker.notify_self() attacker.notify_self()
self.get_player_named(target_username).notify_self()
def emporio(self): def emporio(self):
self.available_cards = [self.deck.draw() for i in range(len(self.players))] self.available_cards = [self.deck.draw() for i in range(len(self.players))]
@ -193,6 +200,7 @@ class Game:
self.players[self.turn].play_turn() self.players[self.turn].play_turn()
def next_turn(self): def next_turn(self):
if self.shutting_down: return
if len(self.players) > 0: if len(self.players) > 0:
self.turn = (self.turn + 1) % len(self.players) self.turn = (self.turn + 1) % len(self.players)
self.play_turn() self.play_turn()
@ -206,9 +214,16 @@ class Game:
def handle_disconnect(self, player: players.Player): def handle_disconnect(self, player: players.Player):
print(f'player {player.name} left the game {self.name}') print(f'player {player.name} left the game {self.name}')
if player in self.players:
self.player_death(player=player, disconnected=True) self.player_death(player=player, disconnected=True)
if len(self.players) == 0: else:
self.dead_players.remove(player)
if len([p for p in self.players if not p.is_bot])+len([p for p in self.dead_players if not p.is_bot]) == 0:
print(f'no players left in game {self.name}') print(f'no players left in game {self.name}')
self.shutting_down = True
self.players = []
self.dead_players = []
self.deck = None
return True return True
else: return False else: return False
@ -242,6 +257,7 @@ class Game:
if self.started: if self.started:
self.sio.emit('chat_message', room=self.name, data=f'_died_role|{player.name}|{player.role.name}') self.sio.emit('chat_message', room=self.name, data=f'_died_role|{player.name}|{player.role.name}')
for p in self.players: for p in self.players:
if not p.is_bot:
p.notify_self() p.notify_self()
self.players_map = {c.name: i for i, c in enumerate(self.players)} self.players_map = {c.name: i for i, c in enumerate(self.players)}
if self.started: if self.started:
@ -278,6 +294,7 @@ class Game:
if len(herb) > 0: if len(herb) > 0:
herb[0].hand.append(self.deck.draw()) herb[0].hand.append(self.deck.draw())
herb[0].hand.append(self.deck.draw()) herb[0].hand.append(self.deck.draw())
herb[0].notify_self()
if died_in_his_turn: if died_in_his_turn:
self.next_turn() self.next_turn()

View File

@ -1,6 +1,6 @@
from enum import IntEnum from enum import IntEnum
import json import json
from random import randrange from random import random, randrange, sample, uniform
import socketio import socketio
import bang.deck as deck import bang.deck as deck
import bang.roles as r import bang.roles as r
@ -8,6 +8,7 @@ import bang.cards as cs
import bang.expansions.dodge_city.cards as csd import bang.expansions.dodge_city.cards as csd
import bang.characters as chars import bang.characters as chars
import bang.expansions.dodge_city.characters as chd import bang.expansions.dodge_city.characters as chd
import eventlet
class PendingAction(IntEnum): class PendingAction(IntEnum):
PICK = 0 PICK = 0
@ -19,7 +20,7 @@ class PendingAction(IntEnum):
class Player: class Player:
def __init__(self, name, sid, sio): def __init__(self, name, sid, sio, bot=False):
import bang.game as g import bang.game as g
super().__init__() super().__init__()
self.name = name self.name = name
@ -47,6 +48,7 @@ class Player:
self.is_drawing = False self.is_drawing = False
self.mancato_needed = 0 self.mancato_needed = 0
self.molly_discarded_cards = 0 self.molly_discarded_cards = 0
self.is_bot = bot
def reset(self): def reset(self):
self.hand: cs.Card = [] self.hand: cs.Card = []
@ -108,8 +110,11 @@ class Player:
def set_available_character(self, available): def set_available_character(self, available):
self.available_characters = available self.available_characters = available
print(f'I {self.name} have to choose between {available}') print(f'I {self.name} have to choose between {available}')
if not self.is_bot:
self.sio.emit('characters', room=self.sid, data=json.dumps( self.sio.emit('characters', room=self.sid, data=json.dumps(
available, default=lambda o: o.__dict__)) available, default=lambda o: o.__dict__))
else:
self.set_character(available[randrange(0, len(available))].name)
def notify_card(self, player, card): def notify_card(self, player, card):
mess = { mess = {
@ -150,13 +155,108 @@ class Player:
randrange(0, len(self.hand)))) randrange(0, len(self.hand))))
if self.lives <= 0 and self.max_lives > 0: if self.lives <= 0 and self.max_lives > 0:
self.pending_action = PendingAction.WAIT self.pending_action = PendingAction.WAIT
ser['hand'] = []
ser['equipment'] = []
self.sio.emit('self', room=self.sid, data=json.dumps(
ser, default=lambda o: o.__dict__))
self.game.player_death(self) self.game.player_death(self)
else: elif not self.is_bot:
self.sio.emit('self_vis', room=self.sid, data=json.dumps( self.sio.emit('self_vis', room=self.sid, data=json.dumps(
self.game.get_visible_players(self), default=lambda o: o.__dict__)) self.game.get_visible_players(self), default=lambda o: o.__dict__))
if not self.is_bot:
self.sio.emit('self', room=self.sid, data=json.dumps( self.sio.emit('self', room=self.sid, data=json.dumps(
ser, default=lambda o: o.__dict__)) ser, default=lambda o: o.__dict__))
self.game.notify_all() self.game.notify_all()
else:
self.game.notify_all()
self.bot_logic()
self.game.notify_all()
def bot_logic(self):
if self.game.shutting_down: return
if self.pending_action != None and self.pending_action != PendingAction.WAIT:
eventlet.sleep(uniform(0.6, 1.5))
else:
return
if self.pending_action == PendingAction.PICK:
self.pick()
elif self.pending_action == PendingAction.DRAW:
self.draw('')
elif self.pending_action == PendingAction.PLAY:
has_played = False
if len([c for c in self.hand if c.is_equipment or c.usable_next_turn]) > 0:
for i in range(len(self.hand)):
if self.hand[i].is_equipment or self.hand[i].usable_next_turn:
self.play_card(i)
has_played = True
break
elif any([isinstance(c, cs.WellsFargo) or isinstance(c, cs.Diligenza) or isinstance(c, cs.Emporio) or isinstance(c, cs.Birra) for c in self.hand]):
for i in range(len(self.hand)):
c = self.hand[i]
if isinstance(c, cs.WellsFargo) or isinstance(c, cs.Diligenza) or isinstance(c, cs.Emporio) or (isinstance(c, cs.Birra) and self.lives < self.max_lives):
self.play_card(i)
has_played = True
break
elif len([c for c in self.hand if c.need_target and not (self.has_played_bang and not any([isinstance(c, cs.Volcanic) for c in self.equipment]))]) > 0:
for i in range(len(self.hand)):
if self.hand[i].need_target and not (self.has_played_bang and not any([isinstance(c, cs.Volcanic) for c in self.equipment])):
if self.hand[i].need_with and len(self.hand) < 2:
continue
_range = self.get_sight() if self.hand[i].name == 'Bang!' or self.hand[i].name == "Pepperbox" else self.hand[i].range
others = [p for p in self.game.get_visible_players(self) if _range >= p['dist'] and not (isinstance(self.role, r.Vice) and p['is_sheriff'])]
if len(others) == 0:
continue
target = others[randrange(0, len(others))]
if target['is_sheriff'] and isinstance(self.role, r.Renegade):
target = others[randrange(0, len(others))]
if not self.hand[i].need_with:
self.play_card(i, against=target['name'])
else:
self.play_card(i, against=target['name'], _with=sample([j for j in range(len(self.hand)) if j != i], 1)[0])
has_played = True
break
elif any([not isinstance(c, cs.Mancato) and c.usable_next_turn and c.can_be_used_now for c in self.equipment]):
print('hmm', [not isinstance(c, cs.Mancato) and c.usable_next_turn and c.can_be_used_now for c in self.equipment])
for i in range(len(self.equipment)):
c = self.equipment[i]
if not isinstance(c, cs.Mancato) and c.usable_next_turn and c.can_be_used_now:
if not c.need_target:
self.play_card(len(self.hand)+i)
else:
_range = self.get_sight() if c.name == "Pepperbox" else c.range
others = [p for p in self.game.get_visible_players(self) if _range >= p['dist'] and not (isinstance(self.role, r.Vice) and p['is_sheriff'])]
if len(others) == 0:
continue
target = others[randrange(0, len(others))]
if target['is_sheriff'] and isinstance(self.role, r.Renegade):
target = others[randrange(0, len(others))]
self.play_card(len(self.hand)+i, against=target['name'])
has_played = True
break
if not has_played and len(self.hand) > self.lives:
self.scrap(0)
else:
self.end_turn()
elif self.pending_action == PendingAction.RESPOND:
did_respond = False
for i in range(len(self.hand)):
if self.hand[i].name in self.expected_response:
self.respond(i)
did_respond = True
break
for i in range(len(self.equipment)):
if self.equipment[i].name in self.expected_response:
self.respond(len(self.hand)+i)
did_respond = True
break
if not did_respond:
self.respond(-1)
elif self.pending_action == PendingAction.CHOOSE:
if not self.target_p:
self.choose(randrange(0, len(self.available_cards)))
else:
target = self.game.get_player_named(self.target_p)
self.choose(randrange(0, len(target.hand)+len(target.equipment)))
def play_turn(self): def play_turn(self):
if self.lives == 0: if self.lives == 0:
@ -239,6 +339,7 @@ class Player:
self.sio.emit('chat_message', room=self.game.name, self.sio.emit('chat_message', room=self.game.name,
data=f'_special_bart_cassidy|{self.name}') data=f'_special_bart_cassidy|{self.name}')
print(f'{self.name} Boom, -3 hp') print(f'{self.name} Boom, -3 hp')
break
else: else:
self.game.next_player().equipment.append(self.equipment.pop(i)) self.game.next_player().equipment.append(self.equipment.pop(i))
self.game.next_player().notify_self() self.game.next_player().notify_self()
@ -405,7 +506,6 @@ class Player:
if isinstance(self.character, chd.ElenaFuente): if isinstance(self.character, chd.ElenaFuente):
self.expected_response = self.game.deck.all_cards_str self.expected_response = self.game.deck.all_cards_str
self.on_failed_response_cb = self.take_damage_response self.on_failed_response_cb = self.take_damage_response
self.notify_self()
return True return True
def get_indians(self, attacker): def get_indians(self, attacker):
@ -420,7 +520,6 @@ class Player:
self.expected_response = [cs.Bang(0, 0).name] self.expected_response = [cs.Bang(0, 0).name]
self.event_type = 'indians' self.event_type = 'indians'
self.on_failed_response_cb = self.take_damage_response self.on_failed_response_cb = self.take_damage_response
self.notify_self()
return True return True
def get_dueled(self, attacker): def get_dueled(self, attacker):
@ -435,7 +534,6 @@ class Player:
self.expected_response = [cs.Bang(0, 0).name] self.expected_response = [cs.Bang(0, 0).name]
self.event_type = 'duel' self.event_type = 'duel'
self.on_failed_response_cb = self.take_damage_response self.on_failed_response_cb = self.take_damage_response
self.notify_self()
return True return True
def take_damage_response(self): def take_damage_response(self):
@ -467,6 +565,7 @@ class Player:
self.attacker = None self.attacker = None
def respond(self, hand_index): def respond(self, hand_index):
if self.pending_action != PendingAction.RESPOND: return
self.pending_action = PendingAction.WAIT self.pending_action = PendingAction.WAIT
if hand_index != -1 and ( if hand_index != -1 and (
((hand_index < len(self.hand) and self.hand[hand_index].name in self.expected_response)) or ((hand_index < len(self.hand) and self.hand[hand_index].name in self.expected_response)) or
@ -540,7 +639,7 @@ class Player:
if len(self.hand) > self.max_lives and not forced: if len(self.hand) > self.max_lives and not forced:
print( print(
f"I {self.name} have to many cards in my hand and I can't end the turn") f"I {self.name} have to many cards in my hand and I can't end the turn")
else: elif self.pending_action == PendingAction.PLAY or forced:
self.is_my_turn = False self.is_my_turn = False
for i in range(len(self.equipment)): for i in range(len(self.equipment)):
if self.equipment[i].usable_next_turn and not self.equipment[i].can_be_used_now: if self.equipment[i].usable_next_turn and not self.equipment[i].can_be_used_now: