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,7 +119,30 @@ def join_room(sid, room):
@sio.event
def chat_message(sid, msg):
ses: Player = sio.get_session(sid)
sio.emit('chat_message', room=ses.game.name, data=f'[{ses.name}]: {msg}')
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.event
def start_game(sid):

View File

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

View File

@ -1,6 +1,6 @@
from enum import IntEnum
import json
from random import randrange
from random import random, randrange, sample, uniform
import socketio
import bang.deck as deck
import bang.roles as r
@ -8,6 +8,7 @@ import bang.cards as cs
import bang.expansions.dodge_city.cards as csd
import bang.characters as chars
import bang.expansions.dodge_city.characters as chd
import eventlet
class PendingAction(IntEnum):
PICK = 0
@ -19,7 +20,7 @@ class PendingAction(IntEnum):
class Player:
def __init__(self, name, sid, sio):
def __init__(self, name, sid, sio, bot=False):
import bang.game as g
super().__init__()
self.name = name
@ -47,6 +48,7 @@ class Player:
self.is_drawing = False
self.mancato_needed = 0
self.molly_discarded_cards = 0
self.is_bot = bot
def reset(self):
self.hand: cs.Card = []
@ -108,8 +110,11 @@ class Player:
def set_available_character(self, available):
self.available_characters = available
print(f'I {self.name} have to choose between {available}')
self.sio.emit('characters', room=self.sid, data=json.dumps(
available, default=lambda o: o.__dict__))
if not self.is_bot:
self.sio.emit('characters', room=self.sid, data=json.dumps(
available, default=lambda o: o.__dict__))
else:
self.set_character(available[randrange(0, len(available))].name)
def notify_card(self, player, card):
mess = {
@ -150,13 +155,108 @@ class Player:
randrange(0, len(self.hand))))
if self.lives <= 0 and self.max_lives > 0:
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)
else:
elif not self.is_bot:
self.sio.emit('self_vis', room=self.sid, data=json.dumps(
self.game.get_visible_players(self), default=lambda o: o.__dict__))
self.sio.emit('self', room=self.sid, data=json.dumps(
if not self.is_bot:
self.sio.emit('self', room=self.sid, data=json.dumps(
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):
if self.lives == 0:
@ -239,6 +339,7 @@ class Player:
self.sio.emit('chat_message', room=self.game.name,
data=f'_special_bart_cassidy|{self.name}')
print(f'{self.name} Boom, -3 hp')
break
else:
self.game.next_player().equipment.append(self.equipment.pop(i))
self.game.next_player().notify_self()
@ -405,7 +506,6 @@ class Player:
if isinstance(self.character, chd.ElenaFuente):
self.expected_response = self.game.deck.all_cards_str
self.on_failed_response_cb = self.take_damage_response
self.notify_self()
return True
def get_indians(self, attacker):
@ -420,7 +520,6 @@ class Player:
self.expected_response = [cs.Bang(0, 0).name]
self.event_type = 'indians'
self.on_failed_response_cb = self.take_damage_response
self.notify_self()
return True
def get_dueled(self, attacker):
@ -435,7 +534,6 @@ class Player:
self.expected_response = [cs.Bang(0, 0).name]
self.event_type = 'duel'
self.on_failed_response_cb = self.take_damage_response
self.notify_self()
return True
def take_damage_response(self):
@ -467,6 +565,7 @@ class Player:
self.attacker = None
def respond(self, hand_index):
if self.pending_action != PendingAction.RESPOND: return
self.pending_action = PendingAction.WAIT
if hand_index != -1 and (
((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:
print(
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
for i in range(len(self.equipment)):
if self.equipment[i].usable_next_turn and not self.equipment[i].can_be_used_now: