From 10b91f2c986c3a536198085880ccaabd2f077bfb Mon Sep 17 00:00:00 2001 From: eaocto Date: Thu, 18 Oct 2018 23:26:36 +0700 Subject: [PATCH] Added "Score Mode" --- actions.py | 75 ++++++++++++++++++++++++++++++++++++++++++++-- bot.py | 13 ++++++-- card.py | 5 ++++ config.py | 4 +++ game.py | 54 ++++++++++++++++++++++++++++++++- results.py | 30 ++++++++++++++++++- simple_commands.py | 3 +- 7 files changed, 177 insertions(+), 7 deletions(-) diff --git a/actions.py b/actions.py index c8c5f1e..5335c20 100644 --- a/actions.py +++ b/actions.py @@ -7,12 +7,12 @@ from datetime import datetime from telegram import Message, Chat -from config import TIME_REMOVAL_AFTER_SKIP, MIN_FAST_TURN_TIME +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 -from utils import send_async, display_name, game_is_running +from utils import send_async, display_name, game_is_running, TIMEOUT logger = logging.getLogger(__name__) @@ -108,6 +108,77 @@ def do_play_card(bot, player, result_id): text=__("{name} won!", multi=game.translate) .format(name=user.first_name)) + 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 diff --git a/bot.py b/bot.py index cdd2ae4..b981de6 100644 --- a/bot.py +++ b/bot.py @@ -30,13 +30,15 @@ import card as c import settings import simple_commands from actions import do_skip, do_play_card, do_draw, do_call_bluff, start_player_countdown -from config import WAITING_TIME, DEFAULT_GAMEMODE, MIN_PLAYERS +from config import (WAITING_TIME, DEFAULT_GAMEMODE, MIN_PLAYERS, SCORE_WIN, + SCORE_DUEL, ADDITIONAL_POINT, PLAYER_THRESHOLD) from errors import (NoGameInChatError, LobbyClosedError, AlreadyJoinedError, NotEnoughPlayersError, DeckEmptyError) from internationalization import _, __, user_locale, game_locales from results import (add_call_bluff, add_choose_color, add_draw, add_gameinfo, add_no_game, add_not_started, add_other_cards, add_pass, - add_card, add_mode_classic, add_mode_fast, add_mode_wild) + add_card, add_mode_classic, add_mode_fast, add_mode_wild, + add_mode_score) from shared_vars import gm, updater, dispatcher from simple_commands import help_handler from start_bot import start_bot @@ -370,6 +372,12 @@ def start_game(bot, update, args, job_queue): "before you can start it").format(minplayers=MIN_PLAYERS)) else: + # Set winning score + player_num = len(game.players) + if player_num == 2: + game.win_score = SCORE_DUEL + else: + game.win_score = ADDITIONAL_POINT * player_num # Starting a game game.start() @@ -594,6 +602,7 @@ def reply_to_query(bot, update): add_mode_classic(results) add_mode_fast(results) add_mode_wild(results) + add_mode_score(results) else: add_not_started(results) diff --git a/card.py b/card.py index 63ddd8e..71fb821 100644 --- a/card.py +++ b/card.py @@ -60,6 +60,11 @@ DRAW_FOUR = 'draw_four' SPECIALS = (CHOOSE, DRAW_FOUR) +# Card score +CARD_SCORES = {v: int(v) for v in VALUES[0:10]} +CARD_SCORES.update({v: 15 for v in VALUES[10:13]}) +CARD_SCORES.update({SPECIALS[0]: 20, SPECIALS[1]: 25}) + STICKERS = { 'b_0': 'BQADBAAD2QEAAl9XmQAB--inQsYcLTsC', 'b_1': 'BQADBAAD2wEAAl9XmQABBzh4U-rFicEC', diff --git a/config.py b/config.py index 6e20066..d55b4b3 100644 --- a/config.py +++ b/config.py @@ -33,3 +33,7 @@ WAITING_TIME = config.get("waiting_time", 120) TIME_REMOVAL_AFTER_SKIP = config.get("time_removal_after_skip", 20) MIN_FAST_TURN_TIME = config.get("min_fast_turn_time", 15) MIN_PLAYERS = config.get("min_players", 2) +SCORE_WIN = config.get("score_mode_win_score", 500) +SCORE_DUEL = config.get("score_mode_win_duel", 100) +ADDITIONAL_POINT = config.get("score_mode_win_per_player_additional", 100) +PLAYER_THRESHOLD = config.get("score_mode_normal_score_threshold_player_count", 5) \ No newline at end of file diff --git a/game.py b/game.py index 3234f9b..d0cbcf4 100644 --- a/game.py +++ b/game.py @@ -19,7 +19,8 @@ import logging -from config import ADMIN_LIST, OPEN_LOBBY, DEFAULT_GAMEMODE, ENABLE_TRANSLATIONS +from config import (ADMIN_LIST, OPEN_LOBBY, DEFAULT_GAMEMODE, + ENABLE_TRANSLATIONS, SCORE_WIN) from datetime import datetime from deck import Deck @@ -39,6 +40,9 @@ class Game(object): owner = ADMIN_LIST open = OPEN_LOBBY translate = ENABLE_TRANSLATIONS + scores = dict() + last_round_score = list() + win_score = SCORE_WIN def __init__(self, chat): self.chat = chat @@ -137,3 +141,51 @@ class Game(object): """Carries out the color choosing and turns the game""" self.last_card.color = color self.turn() + + def add_score(self, player): + """Add total value of all card in every players hand + to the winner's score.""" + scores = 0 + self.last_round_score.clear() + for player in self.players: + for card in player.cards: + sc = c.CARD_SCORES[card.value or card.special] + self.last_round_score.append((sc, card)) + scores += sc + try: + self.scores[player.user.id] += scores + except KeyError: + self.scores[player.user.id] = scores + + def reset_cards(self): + """Reset game deck and player's hand""" + players_cache = self.players + # clear player's card, might as well use player.cards.clear() + for player in players_cache: + for card in player.cards: + self.deck.dismiss(card) + player.cards = list() + # fill deck with normal cards set + self.deck._fill_classic_() + # draw player's hand + for player in players_cache: + player.draw_first_hand() + + def new_round(self): + lc = self.last_card + while self.last_card == lc or not self.last_card or self.last_card.special: + self.last_card = self.deck.draw() + # If the card drawn was special, return it to the deck and loop again + if self.last_card.special: + self.deck.dismiss(self.last_card) + self.play_card(self.last_card) + + def get_score(self, player): + try: + return self.scores[player.user.id] + except KeyError: + self.scores[player.user.id] = 0 + return 0 + + def get_scores(self): + return [(player, self.get_score(player))for player in self.players] diff --git a/results.py b/results.py index d632f48..e66c99a 100644 --- a/results.py +++ b/results.py @@ -130,6 +130,18 @@ def add_mode_wild(results): ) +def add_mode_score(results): + """Change mode to classic""" + results.append( + InlineQueryResultArticle( + "mode_score", + title=_("💯 Score mode"), + input_message_content= + InputTextMessageContent(_('Kaching~ 💯')) + ) + ) + + def add_draw(player, results): """Add option to draw""" n = player.game.draw_counter or 1 @@ -198,7 +210,10 @@ def add_card(game, card, results, can_play): def game_info(game): - players = player_list(game) + if game.mode == 'score': + players = score_list(game) + else: + players = player_list(game) players_numbered = [ '{}. {}'.format(i + 1, p) for i, p in enumerate(players) @@ -214,3 +229,16 @@ def game_info(game): len(players)) .format(player_list="\n".join(players_numbered)) ) + + +def score_list(game): + scores = [game.get_score(player) for player in game.players] + plist = [_("{name} ({number} card)", + "{name} ({number} cards)", + len(player.cards)) + .format(name=player.user.first_name, number=len(player.cards)) + for player in game.players] + return [s + " " + + _("[{point} point]", "[{point} points]", scores[i]) + .format(point=scores[i]) + for i, s in enumerate(plist)] diff --git a/simple_commands.py b/simple_commands.py index 6cc0c23..ed75484 100644 --- a/simple_commands.py +++ b/simple_commands.py @@ -75,7 +75,8 @@ attributions = ("Attributions:\n" modes_explanation = ("This UNO bot has three game modes: Classic, Sanic and Wild.\n\n" " 🎻 The Classic mode uses the conventional UNO deck and there is no auto skip.\n" " 🚀 The Sanic mode uses the conventional UNO deck and the bot automatically skips a player if he/she takes too long to play its turn\n" - " 🐉 The Wild mode uses a deck with more special cards, less number variety and no auto skip.\n\n" + " 🐉 The Wild mode uses a deck with more special cards, less number variety and no auto skip.\n" + " 💯 The Score mode is how Uno meant to be played, scored when one player run out of card.\n\n" "To change the game mode, the GAME CREATOR has to type the bot nickname and a space, just like when playing a card, and all gamemode options should appear.")