tea_cah/gameloop.py
Juhani Krekelä 396b3c0185 Ignore leave-events from ppl not in the game.
Since leave-event was hooked up into ppl quiting and parting from the
channel as well as doing !leave, the message telling ppl were not in the
game would get triggered unintentionally.
2019-05-14 22:00:06 +03:00

1173 lines
29 KiB
Python

import enum
import random
from collections import namedtuple
import cardcast_api
class events(enum.Enum):
(quit, nick_change,
status, start, ready, unready, kill,
join, leave, players, kick,
deck_add, deck_add_random, deck_remove, deck_list,
bot_add_rando, bot_remove,
limit,
card, cards, origins, redeal) = range(22)
class limit_types(enum.Enum):
points, rounds = range(2)
Deck = namedtuple('Deck', ['code', 'name', 'author', 'call_count', 'response_count', 'calls', 'responses'])
Limit = namedtuple('Limit', ['type', 'number'])
Card = namedtuple('Card', ['deck', 'text'])
class Player:
def __init__(self, nick):
self.nick = nick
self.hand = []
self.points = 0
self.message = None
def __repr__(self):
if __name__ == '__main__':
return 'Player(%s)' % repr(self.nick)
else:
return '%s.Player(%s)' % (__name__, repr(self.nick))
def __hash__(self):
return id(self)
class Rando:
def __init__(self, name):
self.nick = '<%s>' % name
self.hand = []
self.points = 0
self.message = None
def num_need_cards(self, num_blanks):
return max(num_blanks - len(self.hand) + self.hand.count(None), 0)
def give_cards(self, cards):
self.hand.extend(cards)
self.hand = [i for i in self.hand if i is not None]
def play(self, num_blanks):
return list(range(num_blanks))
class Error: pass
def game(send, notice, voice, devoice, get_event):
def error(message):
send('Error: %s' % message)
def errwrapper(message, f, *args, **kwargs):
try:
return f(*args, **kwargs)
except Exception as err:
error(message % ('%s, %s' % (type(err), err)))
return Error
def players_bots():
nonlocal players, bots
yield from players.values()
yield from bots.values()
def add_player(nick):
nonlocal players
assert nick not in players
players[nick] = Player(nick)
def remove_player(nick):
nonlocal players
del players[nick]
def change_player_nick(old, new):
nonlocal players
player = players[old]
del players[old]
player.nick = new
players[new] = player
def list_players():
nonlocal players, bots
send(', '.join(sorted(players) + sorted(i.nick for i in bots.values())))
def add_deck(code):
nonlocal decks
assert code not in decks
# Colondeck lives elsewhere
if code == 'colondeck':
base_url = 'https://dl.puckipedia.com/'
else:
base_url = None
# First get info for the deck we're adding
info = cardcast_api.info(code, base_url = base_url)
# Extract the information we want to keep of the deck
name = info['name']
author = info['author']['username']
# Get cards
calls, responses = cardcast_api.cards(code, base_url = base_url)
call_count = len(calls)
response_count = len(responses)
# Preprocess calls so that ___ becomes only one _
# _ are indicated by splitting the card at that point, e.g.
# ['foo ', '.'] is "foo _."
# Two blanks a row will thus be ['foo ', '', '.']
# We can't just remove every single '', since it can be valid
# at the start and the end of a card
for i in range(len(calls)):
call = []
for index, part in enumerate(calls[i]):
if index == 0 or index == len(calls[i]) - 1:
# Always pass these ones through
call.append(part)
elif part == '':
# Remove '' in the middle
continue
else:
call.append(part)
calls[i] = call
# Preprocess calls so that they are cut short if they're >200 chars
for i in range(len(calls)):
call = []
combined_length = 0
for index, part in enumerate(calls[i]):
if combined_length + len(part) > 200:
part = part[:200 - combined_length] + ''
call.append(part)
combined_length += len(part) + 1
calls[i] = call
# Preprocess responses so that they are at max. 160 chars
for i in range(len(responses)):
if len(responses[i]) > 160:
responses[i] = responses[i][:159] + ''
# Add a new deck to list of decks
decks[code] = Deck(
code = code,
name = name,
author = author,
call_count = call_count,
response_count = response_count,
calls = calls,
responses = responses
)
def get_random_deck_code():
nonlocal cardcast_deck_count
# Provide the count on subsequent calls
# First time around cardcast_deck_count will be None, so it
# gets requested from Cardcast, like if we didn't pass the
# `count` parameter
# This will update cardcast_deck_count for each call
# unnecessarily, but I think it simplifies the code and is not
# too bad
code, cardcast_deck_count = cardcast_api.random_code(count = cardcast_deck_count)
return code
def remove_deck(code):
nonlocal decks, round_call_card
# Purge all the cards from the deck from the game
for player_bot in players_bots():
for index, card in enumerate(player_bot.hand):
if card is not None and card.deck.code == code:
player_bot.hand[index] = None
if round_call_card is not None and round_call_card.deck.code == code:
round_call_card = None
del decks[code]
def list_decks():
nonlocal decks
if len(decks) == 0:
send('No decks')
return
for deck in decks.values():
call_count = deck.call_count
calls_left = len(deck.calls)
calls = str(call_count) if call_count == calls_left else '%i/%i' % (calls_left, call_count)
response_count = deck.response_count
responses_left = len(deck.responses)
responses = str(response_count) if response_count == responses_left else '%i/%i' % (responses_left, response_count)
send('%s (%s, by %s, %s black, %s white)' % (
deck.name,
deck.code,
deck.author,
calls,
responses
))
def deck_add_handler(code):
nonlocal decks
if code not in decks:
errwrapper('Failure adding deck: %s (%%s)' % code, add_deck, code)
else:
send('Deck already added')
def deck_add_random_handler():
nonlocal decks
# Let's hope this never bites us in the butt
while True:
code = errwrapper('Failure getting random code for a deck. (%s)', get_random_deck_code)
if code is Error: return
if code not in decks: break
send('That was weird, got %s randomly but it was already added' % code)
errwrapper('Failure adding deck: %s (%%s)' % code, add_deck, code)
send('Added deck %s (%s)' % (decks[code].name, code))
def get_hand_origins(player):
hand_origins = []
for card in player.hand:
if card is None:
hand_origins.append('<empty>')
else:
hand_origins.append(card.deck.code)
return ', '.join('%i: %s' % (index, i) for index, i in enumerate(hand_origins))
def common_handler(event, args):
nonlocal players, bots, decks, limit
if event == events.kill:
send('Stopping game')
return no_game
elif event == events.quit:
return quit
elif event == events.nick_change:
old, new = args
if old in players:
change_player_nick(old, new)
elif event == events.join:
if len(args) == 2:
nick, message = args
if nick not in players:
add_player(nick)
voice(nick)
players[nick].message = message
send('%s has joined %s' % (nick, message))
else:
nick, = args
if nick not in players:
add_player(nick)
voice(nick)
players[nick].message = None
send('%s has joined' % nick)
elif event == events.leave:
nick, = args
if nick not in players:
# Ignore those not in the game
pass
elif errwrapper('Could not remove player %s (%%s)' % nick, remove_player, nick) is not Error:
devoice(nick)
send('%s has left the game' % nick)
elif event == events.players:
list_players()
elif event == events.kick:
kicker, kickee = args
if kicker not in players:
# Ignore those not in the game
pass
elif kickee not in players:
send('No such player %s' % kickee)
elif errwrapper('Could not remove player %s (%%s)' % kickee, remove_player, kickee) is not Error:
devoice(kickee)
send('%s has been removed from the game' % kickee)
elif event == events.deck_add:
code, = args
deck_add_handler(code)
elif event == events.deck_add_random:
deck_add_random_handler()
elif event == events.deck_remove:
code, = args
if code in decks:
errwrapper('Failure removing deck %s (%%s)' % code, remove_deck, code)
else:
send('No such deck %s' % code)
elif event == events.deck_list:
list_decks()
elif event == events.bot_add_rando:
name, = args
if name not in bots:
bots[name] = Rando(name)
send('Bot %s added' % name)
else:
send('Bot named %s already exists' % name)
elif event == events.bot_remove:
name, = args
if name in bots:
del bots[name]
else:
send('No such bot %s' % name)
elif event == events.limit:
if len(args) == 0:
limit_type = {limit_types.rounds: 'rounds', limit_types.points: 'points'}[limit.type]
send('Limit is %i %s' % (limit.number, limit_type))
else:
limit_type, number = args
limit = Limit(limit_type, number)
limit_type = {limit_types.rounds: 'rounds', limit_types.points: 'points'}[limit.type]
send('Limit set to %i %s' % (limit.number, limit_type))
elif event == events.origins:
nick, = args
if nick in players:
origins = get_hand_origins(players[nick])
if origins != '':
notice(nick, origins)
elif event == events.card or event == events.cards or event == events.redeal:
# Ignore card commands if no cards are available yet
pass
elif event == events.ready or event == events.unready:
# Ignore readiness commands by default
pass
else:
error('Unknown event type: %s' % event)
def start_game(rest):
expert = False
if len(rest) == 0 or rest[0] == 'default':
send('Adding the default CAH deck (A5DCM)')
deck_add_handler('A5DCM')
elif rest[0] == 'offtopia':
send('Adding the default CAH deck (A5DCM), offtopia injoke deck (PXWKC), :Deck (colondeck) and three random decks')
deck_add_handler('A5DCM')
deck_add_handler('PXWKC')
deck_add_handler('colondeck')
deck_add_random_handler()
deck_add_random_handler()
deck_add_random_handler()
elif rest[0] == 'offtopia-norandom':
send('Adding the default CAH deck (A5DCM), offtopia injoke deck (PXWKC), and :Deck (colondeck)')
deck_add_handler('A5DCM')
deck_add_handler('PXWKC')
deck_add_handler('colondeck')
elif rest[0] == 'empty':
pass
elif rest[0] == 'expert':
expert = True
else:
send('Unknown preset %s' % rest[0])
if not expert:
limit_type = {limit_types.rounds: 'rounds', limit_types.points: 'points'}[limit.type]
send('Limit is %i %s, change with !limit' % (limit.number, limit_type))
send('Once you are ready to start the game, everyone send !ready')
def no_game():
nonlocal players, bots, decks, limit, round_number, round_call_card, czar, card_choices
if players is not None:
devoice(players)
players = {}
bots = {}
decks = {}
limit = Limit(limit_types.points, 5)
round_number = 1
round_call_card = None
czar = None
card_choices = None
while True:
event, *args = get_event()
if event == events.status:
send('Idle')
elif event == events.start:
nick, *rest = args
add_player(nick)
voice(nick)
send('%s started a game, !join to join!' % nick)
start_game(rest)
return game_setup
elif event == events.join:
nick = args[0]
send('Started game, !join to join!')
common_handler(event, args)
start_game([])
return game_setup
elif event == events.quit:
return quit
else:
pass
def game_setup():
nonlocal players
players_ready = set()
while True:
if len(players) == 0:
send('Lost all players, quiting game setup')
return no_game
players_ready = set(i for i in players_ready if i in players.values())
players_unready = [i for i in players.values() if i not in players_ready]
if len(players_unready) == 0: break
event, *args = get_event()
if event == events.status:
if len(players_ready) == 0:
send('Game setup')
else:
send('Game setup, waiting for %s to be ready' % ', '.join(i.nick for i in players_unready))
elif event == events.start:
if len(args) == 1:
break
else:
send('Can\'t apply presets once the game setup has started. Here !start begins the game without waiting for !ready')
elif event == events.ready:
nick, = args
# Ignore if not in the game
if nick not in players:
continue
player = players[nick]
if player not in players_ready:
players_ready.add(player)
elif event == events.unready:
nick, = args
# Ignore if not in the game
if nick not in players:
continue
player = players[nick]
if player in players_ready:
players_ready.remove(player)
else:
r = common_handler(event, args)
if r is not None: return r
if len(players) < 2:
send('Not enough players')
return game_setup
else:
return setup_round
def total_calls():
nonlocal decks
return sum(len(deck.calls) for deck in decks.values())
def total_responses():
nonlocal decks
return sum(len(deck.responses) for deck in decks.values())
def deal_call():
nonlocal decks
deck_objs = list(decks.values())
while True:
deck = random.choice(deck_objs)
if len(deck.calls) != 0: break
# See comment about mutation in deal_responses()
index = random.randrange(len(deck.calls))
return Card(deck, deck.calls.pop(index))
def deal_responses(need_responses):
nonlocal decks
responses = []
deck_objs = list(decks.values())
for i in range(need_responses):
while True:
deck = random.choice(deck_objs)
if len(deck.responses) != 0: break
# We generate an index and pop that, since that makes
# it easier to mutate the list in place
index = random.randrange(len(deck.responses))
responses.append(Card(deck, deck.responses.pop(index)))
# Shuffle the responses at the end, as otherwise the first
# cards are more likely to have come from small decks than
# the last cards
random.shuffle(responses)
return responses
def setup_round():
nonlocal players, bots, round_call_card, czar, card_choices
# Select a czar randomly, if we need to
if czar not in players.values():
czar = random.choice(list(players.values()))
# Clear out previous round's cards
card_choices = {}
# Check that we have a call card for next round, should we need one
if round_call_card is None:
available_calls = total_calls()
if available_calls == 0:
send('Need a black card, none available. Add decks and continue with !start')
return game_setup
# Select call card for the next round
round_call_card = deal_call()
# See note above num_blanks in top_of_round()
num_blanks = len(round_call_card.text) - 1
# Find out how many response cards we need
hand_size = 9 + num_blanks
need_responses = 0
for player in players.values():
# Don't deal cards to the czar this round
if player is czar: continue
need_responses += max(hand_size - len(player.hand) + player.hand.count(None), 0)
for bot in bots.values():
need_responses += bot.num_need_cards(num_blanks)
# If we don't have enough, kick back to setup
available_responses = total_responses()
if available_responses < need_responses:
send('Need %i white cards, only %i available. Add decks and continue with !start' % (need_responses, available_responses))
return game_setup
# Get the cards
responses = deal_responses(need_responses)
# Add responses to players' inventories
for player in players.values():
# We skipped the czar in the counts, so skip here too
if player is czar: continue
# Move the cards outside of the current hand size into
# the hand
overflow = [i for i in player.hand[hand_size:] if i is not None]
player.hand = player.hand[:hand_size]
for index in range(len(player.hand)):
if len(overflow) == 0:
break
if player.hand[index] is None:
# .pop(0) instead of .pop() since we
# want to keep the same order
player.hand[index] = overflow.pop(0)
# Do we still have some overflow cards we couldn't fit
# into the hand? If so, just stick them at the end and
# we'll just have an oversized hand this round
player.hand.extend(overflow)
# Fill any remaining empty spots with dealt cards
while len(player.hand) < hand_size:
player.hand.append(responses.pop())
for index in range(hand_size):
if player.hand[index] is None:
player.hand[index] = responses.pop()
# Give cards to bots
for bot in bots.values():
needed = bot.num_need_cards(num_blanks)
fed = responses[:needed]
responses = responses[needed:]
bot.give_cards(fed)
return top_of_round
def sanitize(text):
return ''.join(i if ord(i) >= 32 and ord(i) != 127 else '^' + chr(ord(i) ^ 64) for i in text)
def send_cards(nick):
nonlocal players
cards = ' | '.join('%i: [%s]' % (index, sanitize(card.text)) for index, card in enumerate(players[nick].hand))
notice(nick, cards)
def combine_cards(call, responses):
def handle_call_part(call_part):
nonlocal responses
r = []
after_dollar = False
for char in call_part:
if after_dollar and ord('0') <= ord(char) <= ord('9'):
# Handle $0 .. $9
# Hopefully we won't run into more backreferences
# in one card
index = int(char)
if 0 <= index < len(responses):
r.append('[' + responses[index] + ']')
else:
# Not valid backreference, copy verbatim
r.append('$' + char)
after_dollar = False
elif after_dollar:
# Wasn't a backreference, copy verbatim
r.append('$' + char)
after_dollar = False
elif char == '$':
after_dollar = True
else:
r.append(char)
return sanitize(''.join(r))
combined = [handle_call_part(call[0])]
for i in range(len(call) - 1):
combined.append('[' + sanitize(responses[i]) + ']')
combined.append(handle_call_part(call[i + 1]))
return ''.join(combined)
def combine_played(call, player_bot, selected_cards):
return combine_cards(call.text, [player_bot.hand[i].text for i in selected_cards])
def top_of_round():
nonlocal players, bots, round_number, round_call_card, czar, card_choices
choosers = [i for i in players.values() if i is not czar]
send('Round %i. %s is czar. %s choose your cards' % (round_number, czar.nick, ', '.join(i.nick for i in choosers)))
send('[%s]' % '_'.join(sanitize(part) for part in round_call_card.text))
# Round call card has N parts. Between each of those parts
# goes one response card. Therefore there should be N - 1
# response cards
num_blanks = len(round_call_card.text) - 1
# Have bots choose first
for bot in bots.values():
card_choices[bot] = bot.play(num_blanks)
for nick in players:
if players[nick] is not czar:
send_cards(nick)
while len(choosers) > 0:
# Make sure that if a chooser leaves, they won't be waited on
choosers = [i for i in choosers if i in players.values()]
if len(players) < 2:
send('Not enough players to continue, quiting game')
return no_game
if czar not in players.values():
send('Czar left the game, restarting round')
return setup_round
event, *args = get_event()
if event == events.status:
send('Waiting for %s to choose' % ', '.join(i.nick for i in choosers))
elif event == events.start:
send('Game already in progress')
elif event == events.card:
nick, choices = args
# Ignore those not in the game
if nick not in players:
continue
player = players[nick]
if player is czar:
notice(nick, 'Czar can\'t choose now')
continue
elif player not in choosers and player not in card_choices:
notice(nick, 'You\'ll get to choose next round')
continue
if len(choices) != num_blanks:
notice(nick, 'Select %i card(s)' % (len(round_call_card.text) - 1))
continue
selected_cards = []
for choice in choices:
if 0 <= choice < len(player.hand):
if choice not in selected_cards:
selected_cards.append(choice)
else:
notice(nick, 'Can\'t play the same card twice')
break
else:
notice(nick, '%i not in your hand' % choice)
break
if len(selected_cards) != len(choices):
# Failed to use some choice
continue
card_choices[player] = selected_cards
if player in choosers:
choosers.remove(player)
notice(nick, combine_played(round_call_card, player, selected_cards))
elif event == events.cards:
nick, = args
if nick not in players:
# Ignore those not in the game
continue
player = players[nick]
if player in choosers or player in card_choices:
send_cards(nick)
else:
notice(nick, 'You can\'t choose now')
elif event == events.origins:
nick, = args
if nick not in players:
notice(nick, 'call: %s' % round_call_card.deck.code)
else:
notice(nick, 'call: %s, %s' % (round_call_card.deck.code, get_hand_origins(players[nick])))
elif event == events.redeal:
nick, = args
if nick not in players:
# Ignore those not in the game
continue
player = players[nick]
for index in range(len(player.hand)):
player.hand[index] = None
if player in choosers or player in card_choices:
send('Dealing out a new hand to %s, restarting round' % nick)
return setup_round
else:
notice(nick, 'New hand will be dealt next round')
elif event == events.deck_remove:
common_handler(event, args)
# Did we lose our call card?
if round_call_card is None:
# Yes, restart round
send('Lost the black card, restarting round')
return setup_round
# Did it remove a card from someone voting this round?
for player in choosers:
if None in player.hand:
# Yes, restart round
send('Lost a card from player\'s hand, restarting round')
return setup_round
for player_bot in card_choices:
# We are checking all cards here, not
# just the ones chosen. This is because
# a player may change their selection,
# in which case we might hit a None
if None in player_bot.hand:
# Yes, restart round
send('Lost a card from player\'s hand, restarting round')
return setup_round
else:
r = common_handler(event, args)
if r is not None: return r
return bottom_of_round
def bottom_of_round():
nonlocal players, round_call_card, czar, card_choices
send('Everyone has chosen. %s, now\'s your time to choose.' % czar.nick)
# Display the cards
choosers = random.sample(card_choices.keys(), k = len(card_choices))
for index, player_bot in enumerate(choosers):
send('%i: %s' % (index, combine_played(round_call_card, player_bot, card_choices[player_bot])))
while True:
if len(players) < 2:
send('Not enough players to continue, quiting game')
return no_game
if czar not in players.values():
send('Czar left the game, restarting round')
return setup_round
event, *args = get_event()
if event == events.status:
send('Waiting for czar %s to choose' % czar.nick)
elif event == events.start:
send('Game already in progress')
elif event == events.card:
nick, choices = args
# Ignore those not in the game
if nick not in players:
continue
player = players[nick]
if player is not czar:
notice(nick, 'Only the czar can choose now')
continue
if len(choices) == 1:
choice = choices[0]
if 0 <= choice < len(choosers):
player_bot = choosers[choice]
player_bot.points += 1
# Winner is Czar semantics if a
# player won, random otherwise
if player_bot in players.values():
czar = player_bot
else:
czar = None
send('The winner is %s with: %s' % (player_bot.nick, combine_played(round_call_card, player_bot, card_choices[player_bot])))
break
else:
notice(nick, '%i not in range' % choice)
elif len(choices) == 0:
# Special case: award everyone a point
# and randomize czar
for player_bot in card_choices:
player_bot.points += 1
# If we set czar to None, setup_round()
# will handle ramdomizing it for us
czar = None
send('Everyone is a winner!')
break
else:
notice(nick, 'Select one or zero choices')
elif event == events.origins:
nick, = args
if nick not in players:
notice(nick, 'call: %s' % round_call_card.deck.code)
else:
answers_origins = []
for index, player_bot in enumerate(choosers):
answer_origins = [player_bot.hand[i].deck.code for i in card_choices[player_bot]]
answers_origins.append('%i: %s' % (index, ', '.join(answer_origins)))
notice(nick, 'call: %s; %s' % (round_call_card.deck.code, '; '.join(answers_origins)))
elif event == events.redeal:
nick, = args
if nick not in players:
# Ignore those not in the game
continue
player = players[nick]
for index in range(len(player.hand)):
player.hand[index] = None
if player in card_choices:
send('Lost a card played this round, restarting round')
return setup_round
else:
notice(nick, 'New hand will be dealt next round')
elif event == events.deck_remove:
common_handler(event, args)
# Did we lose our call card?
if round_call_card is None:
# Yes, restart round
send('Lost the black card, restarting round')
return setup_round
# Did it affect any response cards on this round?
for player_bot in card_choices:
for index in card_choices[player_bot]:
if player_bot.hand[index] is None:
# Yes, restart round
send('Lost a card played this round, restarting round')
return setup_round
else:
r = common_handler(event, args)
if r is not None: return r
points = []
for player_bot in players_bots():
if player_bot in choosers:
points.append('%s: %i (%i)' % (player_bot.nick, player_bot.points, choosers.index(player_bot)))
else:
points.append('%s: %i' % (player_bot.nick, player_bot.points))
send('Points: %s' % ' | '.join(points))
return teardown_round
def teardown_round():
nonlocal players, limit, round_number, round_call_card, card_choices
if limit.type == limit_types.rounds:
if round_number >= limit.number:
return end_game
elif limit.type == limit_types.points:
if max(i.points for i in players_bots()) >= limit.number:
return end_game
# Remove the cards that were played this round from hands
for player_bot in card_choices:
for index in card_choices[player_bot]:
player_bot.hand[index] = None
# Increase the number of the round and clear the call card
# These are not done in setup_round() since we might want to
# restart a round in case the czar leaves
round_number += 1
round_call_card = None
return setup_round
def end_game():
nonlocal players
max_score = max(i.points for i in players_bots())
winners = [i for i in players_bots() if i.points == max_score]
if len(winners) == 1:
winner, = winners
if winner.message is not None:
send('We have a winner! %s won %s' % (winner.nick, winner.message))
else:
send('We have a winner! %s' % winner.nick)
else:
send('We have the winners! %s' % ', '.join(i.nick for i in winners))
return no_game
def quit():
pass
players = None
bots = None
decks = None
limit = None
round_number = None
round_call_card = None
czar = None
card_choices = None
cardcast_deck_count = None
state = no_game
while state != quit:
state = state()
if __name__ == '__main__':
def get_event():
while True:
try:
t = input('> ')
except EOFError:
return (events.quit,)
if t == 'nick':
old = input('old> ')
new = input('new> ')
return (events.nick_change, old, new)
elif t == 'start':
nick = input('nick> ')
return (events.start, nick)
elif t == 'start_preset':
nick = input('nick> ')
preset = input('preset> ')
return (events.start, nick, preset)
elif t == 'ready':
nick = input('nick> ')
return (events.ready, nick)
elif t == 'unready':
nick = input('nick> ')
return (events.unready, nick)
elif t == 'status':
return (events.status,)
elif t == 'kill':
return (events.kill,)
elif t == 'join':
nick = input('nick> ')
return (events.join, nick)
elif t == 'join_message':
nick = input('nick> ')
message = input('message> ')
return (events.join, nick, message)
elif t == 'leave':
nick = input('nick> ')
return (events.leave, nick)
elif t == 'players':
return (events.players,)
elif t == 'kick':
kicker = input('kicker> ')
kickee = input('kickee> ')
return (events.kick, kicker, kickee)
elif t == 'deck add':
code = input('code> ')
return (events.deck_add, code)
elif t == 'deck add random':
return (events.deck_add_random,)
elif t == 'deck remove':
code = input('code> ')
return (events.deck_remove, code)
elif t == 'deck list':
return (events.deck_list,)
elif t == 'bot add rando':
name = input('name> ')
return (events.bot_add_rando, name)
elif t == 'bot remove':
name = input('name> ')
return (events.bot_remove, name)
elif t == 'limit':
return (events.limit,)
elif t == 'limit_set':
limit_type = {'r': limit_types.rounds, 'p': limit_types.points}[input('type (p/r)> ')]
number = int(input('limit> '))
return (events.limit, limit_type, number)
elif t == 'card':
nick = input('nick> ')
choice = [int(i) for i in input('choice> ').split()]
return (events.card, nick, choice)
elif t == 'cards':
nick = input('nick> ')
return (events.cards, nick)
elif t == 'origins':
nick = input('nick> ')
return (events.origins, nick)
elif t == 'redeal':
nick = input('nick> ')
return (events.redeal, nick)
else:
print('?')
def send(text):
print(text)
def notice(nick, text):
print('\t', nick, text)
def nop(*args, **kwargs): pass
game(send, notice, nop, nop, get_event)