mau_mau_bot/actions.py

284 lines
9.5 KiB
Python
Raw Normal View History

import random
import logging
import card as c
from datetime import datetime
from telegram import Message, Chat
2018-10-19 00:26:36 +08:00
from config import TIME_REMOVAL_AFTER_SKIP, MIN_FAST_TURN_TIME, SCORE_WIN
from errors import DeckEmptyError, NotEnoughPlayersError
from internationalization import __, _
from shared_vars import gm
from user_setting import UserSetting
2018-10-19 00:26:36 +08:00
from utils import send_async, display_name, game_is_running, TIMEOUT
logger = logging.getLogger(__name__)
class Countdown(object):
player = None
job_queue = None
def __init__(self, player, job_queue):
self.player = player
self.job_queue = job_queue
# TODO do_skip() could get executed in another thread (it can be a job), so it looks like it can't use game.translate?
def do_skip(bot, player, job_queue=None):
game = player.game
chat = game.chat
skipped_player = game.current_player
next_player = game.current_player.next
if skipped_player.waiting_time > 0:
skipped_player.anti_cheat += 1
skipped_player.waiting_time -= TIME_REMOVAL_AFTER_SKIP
if (skipped_player.waiting_time < 0):
skipped_player.waiting_time = 0
try:
skipped_player.draw()
except DeckEmptyError:
pass
n = skipped_player.waiting_time
send_async(bot, chat.id,
text="Waiting time to skip this player has "
"been reduced to {time} seconds.\n"
"Next player: {name}"
.format(time=n,
name=display_name(next_player.user))
)
logger.info("{player} was skipped!. "
.format(player=display_name(player.user)))
game.turn()
if job_queue:
start_player_countdown(bot, game, job_queue)
else:
try:
gm.leave_game(skipped_player.user, chat)
send_async(bot, chat.id,
text="{name1} ran out of time "
"and has been removed from the game!\n"
"Next player: {name2}"
.format(name1=display_name(skipped_player.user),
name2=display_name(next_player.user)))
logger.info("{player} was skipped!. "
.format(player=display_name(player.user)))
if job_queue:
start_player_countdown(bot, game, job_queue)
except NotEnoughPlayersError:
send_async(bot, chat.id,
text="{name} ran out of time "
"and has been removed from the game!\n"
"The game ended."
.format(name=display_name(skipped_player.user)))
gm.end_game(chat, skipped_player.user)
def do_play_card(bot, player, result_id):
"""Plays the selected card and sends an update to the group if needed"""
card = c.from_str(result_id)
player.play(card)
game = player.game
chat = game.chat
user = player.user
us = UserSetting.get(id=user.id)
if not us:
us = UserSetting(id=user.id)
if us.stats:
us.cards_played += 1
if game.choosing_color:
send_async(bot, chat.id, text=_("Please choose a color"))
if len(player.cards) == 1:
send_async(bot, chat.id, text="UNO!")
if len(player.cards) == 0:
send_async(bot, chat.id,
text=__("{name} won!", multi=game.translate)
.format(name=user.first_name))
2018-10-19 00:26:36 +08:00
if game.mode == "score":
game.add_score(player)
score = sum([n[0] for n in game.last_round_score])
bot.sendMessage(chat.id,
text=__("Added {pt} point",
"Added {pt} points",
score)
.format(pt=score))
bot.sendMessage(
chat.id,
text=", ".join(
["{cl} {vl} (+{pt})"
.format(cl=c.COLOR_ICONS[n[1].color],
vl=n[1].value or n[1].special,
pt=n[0])
for n in sorted(game.last_round_score,
key=lambda x: x[0])]))
bot.sendMessage(
chat.id,
text=__("Current standings:\n\n") +
"\n".join([
"{no}. {name}: {pt}".format(
no=i + 1,
name=x[0].user.first_name,
pt=x[1])
for i, x in enumerate(
sorted(game.get_scores(),
key=lambda x: x[1],
reverse=True))]))
bot.sendMessage(
chat.id,
text=__("Points to win: {score}").format(score=game.win_score))
players_cache = game.players
highest = sorted(zip(players_cache, map(
game.get_score, players_cache
)), key=lambda x: x[1])[-1]
if highest[1] >= game.win_score:
send_async(bot, chat.id,
text=__("Game ended! {name} wins the game!",
multi=game.translate)
.format(highest[0].user.first_name))
if us.stats:
us.first_places += 1
for player in players_cache:
us_ = UserSetting.get(id=player.user.id)
if not us:
us_ = UserSetting(id=player.user.id)
us_.games_played += 1
else:
game.reset_cards()
game.new_round()
next_round_message = (
__("Next round!", multi=game.translate))
next_player_message = (
__("First player: {name}\n",
multi=game.translate)
.format(name=display_name(game.current_player.user)))
bot.sendMessage(chat.id,
text=next_round_message,
timeout=TIMEOUT)
bot.sendSticker(chat.id,
sticker=c.STICKERS[str(game.last_card)],
timeout=TIMEOUT)
bot.sendMessage(chat.id,
text=next_player_message,
timeout=TIMEOUT)
return
# If game mode is "score", 'do_play_card' should stop here
if us.stats:
us.games_played += 1
if game.players_won is 0:
us.first_places += 1
game.players_won += 1
try:
gm.leave_game(user, chat)
except NotEnoughPlayersError:
send_async(bot, chat.id,
text=__("Game ended!", multi=game.translate))
us2 = UserSetting.get(id=game.current_player.user.id)
if us2 and us2.stats:
us2.games_played += 1
gm.end_game(chat, user)
def do_draw(bot, player):
"""Does the drawing"""
game = player.game
draw_counter_before = game.draw_counter
try:
player.draw()
except DeckEmptyError:
send_async(bot, player.game.chat.id,
text=__("There are no more cards in the deck.",
multi=game.translate))
if (game.last_card.value == c.DRAW_TWO or
game.last_card.special == c.DRAW_FOUR) and \
draw_counter_before > 0:
game.turn()
def do_call_bluff(bot, player):
"""Handles the bluff calling"""
game = player.game
chat = game.chat
if player.prev.bluffing:
send_async(bot, chat.id,
text=__("Bluff called! Giving 4 cards to {name}",
multi=game.translate)
.format(name=player.prev.user.first_name))
try:
player.prev.draw()
except DeckEmptyError:
send_async(bot, player.game.chat.id,
text=__("There are no more cards in the deck.",
multi=game.translate))
else:
game.draw_counter += 2
send_async(bot, chat.id,
text=__("{name1} didn't bluff! Giving 6 cards to {name2}",
multi=game.translate)
.format(name1=player.prev.user.first_name,
name2=player.user.first_name))
try:
player.draw()
except DeckEmptyError:
send_async(bot, player.game.chat.id,
text=__("There are no more cards in the deck.",
multi=game.translate))
game.turn()
def start_player_countdown(bot, game, job_queue):
player = game.current_player
time = player.waiting_time
if time < MIN_FAST_TURN_TIME:
time = MIN_FAST_TURN_TIME
if game.mode == 'fast':
if game.job:
game.job.schedule_removal()
job = job_queue.run_once(
#lambda x,y: do_skip(bot, player),
skip_job,
time,
context=Countdown(player, job_queue)
)
logger.info("Started countdown for player: {player}. {time} seconds."
.format(player=display_name(player.user), time=time))
player.game.job = job
def skip_job(bot, job):
player = job.context.player
game = player.game
if game_is_running(game):
job_queue = job.context.job_queue
do_skip(bot, player, job_queue)