Exemplo n.º 1
0
def get_game_type_and_main_player(game_id: int) -> tuple:
    """checks redis round_choices entry and finds which player has chosen the highest game
    and which game is being played.
    Returns a tuple of (game_type, main_player)"""

    # get original player order from redis
    player_order = RedisGetter.round_choices(game_id, 'order')
    player_order = player_order.split(',')

    # get choice of each player
    player1_choice = RedisGetter.round_choices(game_id,
                                               f'{player_order[0]}_chosen')
    player2_choice = RedisGetter.round_choices(game_id,
                                               f'{player_order[1]}_chosen')
    player3_choice = RedisGetter.round_choices(game_id,
                                               f'{player_order[2]}_chosen')

    # if a player's choice is 'pass', it means they are not the main player
    if player1_choice != 'pass':
        return player1_choice, player_order[0]
    elif player2_choice != 'pass':
        return player2_choice, player_order[1]
    elif player3_choice != 'pass':
        return player3_choice, player_order[2]
    else:
        # all three players' choice was 'pass'
        return 'pass', None
Exemplo n.º 2
0
def calculate_score(game_id: str, current_user: str):
    """calculates score based on main_user's score pile, called and game_type"""
    card_pile_main = RedisGetter.current_round(
        game_id, 'main_player_score_pile').split(',')
    card_pile_against = RedisGetter.current_round(
        game_id, 'against_players_score_pile').split(',')
    called = RedisGetter.current_round(game_id, 'called').split(',')
    game_type = RedisGetter.current_round(game_id, 'type')

    counted_cards = count_cards_in_pile(card_pile_main)
    called_calculation = get_called_calculation(card_pile_main, called)
    extras_calculation = calculate_extras(card_pile_main, card_pile_against,
                                          called)
    final_calculation = calculate_final_score(card_pile_main,
                                              card_pile_against, called,
                                              game_type)
    points_difference = CardPile.round_to_five(counted_cards) - 35

    data_to_send = {
        'counted_cards': counted_cards,
        'called_calculation': called_calculation,
        'extras_calculation': extras_calculation,
        'final_calculation': final_calculation,
        'game_worth': POINTS_GAME_TYPE[game_type],
        'points_difference': points_difference
    }
    socketio.emit('calculate score', data_to_send)

    main_player = RedisGetter.current_round(game_id, 'main_player')
    if main_player == current_user:
        update_score_for_user(main_player, final_calculation)
    reset_redis_entries(game_id)
Exemplo n.º 3
0
def update_player_options(game_id: int):
    """updates players' options based on current state of choices in redis db"""
    is_last_choice = RedisGetter.round_choices(game_id, 'last_choice')

    # get original player order from redis
    player_order = RedisGetter.round_choices(game_id, 'order')
    player_order = player_order.split(',')

    # get current choice of each player
    player1_choice = RedisGetter.round_choices(game_id,
                                               f'{player_order[0]}_chosen')
    player2_choice = RedisGetter.round_choices(game_id,
                                               f'{player_order[1]}_chosen')
    player3_choice = RedisGetter.round_choices(game_id,
                                               f'{player_order[2]}_chosen')

    # get available games
    chosen_games = [player1_choice, player2_choice, player3_choice]
    available_games = get_available_games(chosen_games)

    player1_options = ['pass'] + available_games
    if not player1_choice:
        player1_options = available_games
    player2_options = ['pass'] + available_games
    player3_options = ['pass'] + available_games

    # if all players have made first choice, compare choices and find available ones for each player
    if player1_choice and player2_choice and player3_choice:
        # if players 2 and 3 chose a game worth more than what player 1 chose, player 1 can choose again
        if player1_choice == 'pass' or is_last_choice == 'true':
            player1_options = ['chosen']
        else:
            player1_options = calculate_player1_options(
                player1_choice, player2_choice, player3_choice,
                player1_options)

        # if player 3 chose a game worth more than what player 2 chose, player 2 can choose again
        if player2_choice == 'pass' or is_last_choice == 'true':
            player2_options = ['chosen']
        else:
            player2_options = calculate_player2_options(
                player2_choice, player3_choice, player2_options)

        # if player 3 can still choose a game worth more than what they've already chosen, they can choose again
        if player3_choice == 'pass' or is_last_choice == 'true':
            player3_options = ['chosen']

    if chosen_games.count('pass') == 2:
        is_last_choice = 'true'

    # update redis with current options for each player
    RedisSetter.round_choices(game_id, f'{player_order[0]}_options',
                              ','.join(player1_options))
    RedisSetter.round_choices(game_id, f'{player_order[1]}_options',
                              ','.join(player2_options))
    RedisSetter.round_choices(game_id, f'{player_order[2]}_options',
                              ','.join(player3_options))
    if is_last_choice == 'true':
        RedisSetter.round_choices(game_id, 'last_choice', is_last_choice)
Exemplo n.º 4
0
def update_round_state_at_beginning(game_id: str):
    """updates redis db with the state of the round at its beginning"""
    save_game_type(int(game_id))

    game_type = RedisGetter.current_round(game_id, 'type')
    main_player = RedisGetter.current_round(game_id, 'main_player')
    data_to_send = {'game_type': game_type, 'main_player': main_player}
    print(f'[SENDING] new round information: {data_to_send}')
    socketio.emit('round begins', data_to_send)
Exemplo n.º 5
0
def get_players_hand(game_id: str, player_name: str):
    """retrieves the players hand from redis"""
    players_hand = RedisGetter.current_round(game_id,
                                             f'{player_name}_cards').split(',')
    talon_cards = RedisGetter.current_round(game_id, 'talon_cards').split(',')
    data_to_send = {
        'players_hand': players_hand,
        'player_name': player_name,
        'talon_cards': talon_cards
    }
    socketio.emit('get hand of player', data_to_send)
Exemplo n.º 6
0
def update_players_hand(main_player: str, game_id: str, cards_to_add: list,
                        cards_to_remove: list):
    """the chosen cards from talon are either added to or removed from the array of cards the player is already holding.
    The cards are sorted again and returned to the JS component.

    A player's hand will always need to be updated twice, first with cards_to_add and then with cards_to_remove.
    cards_to_add correspond to cards that should be removed from 'talon' and added to the against players' pile.
    cards_to_remove correspond to cards that should be removed from the user's hand and added to the main player's pile.

    To distinguish when the updating of the player's hand is finished, swap_finished is set to True."""
    swap_finished = False
    cards_in_hand = RedisGetter.current_round(game_id, f'{main_player}_cards')
    cards_in_hand = cards_in_hand.split(',')

    if cards_to_add:
        # add chosen talon cards to main player's hand
        cards_in_hand.extend(cards_to_add)

        # add remaining talon cards to 'against' players' score pile
        original_talon = RedisGetter.current_round(game_id, 'talon_cards')
        original_talon = original_talon.split(',')
        remaining_talon = [
            card for card in original_talon if card not in cards_to_add
        ]
        add_to_score_pile(game_id, 'against_players', remaining_talon)
        RedisSetter.current_round(game_id, 'talon_cards', '')

    elif cards_to_remove:
        # remove chosen cards from main player's hand
        cards_in_hand = [
            card for card in cards_in_hand if card not in cards_to_remove
        ]

        # add chosen cards to main player's score pile
        add_to_score_pile(game_id, 'main_player', cards_to_remove)

        swap_finished = True

    updated_hand = sort_player_cards(cards_in_hand)

    # save player's new cards to redis
    value_for_redis = ','.join(str(card_name) for card_name in updated_hand)
    RedisSetter.current_round(game_id, f'{main_player}_cards', value_for_redis)

    data_to_send = {
        'updated_hand': updated_hand,
        'main_player': main_player,
        'swap_finished': swap_finished
    }
    socketio.emit('update players hand', data_to_send)
Exemplo n.º 7
0
def remove_card_from_hand(game_id: str, player_name: str, played_card: str):
    """removes played_card from the player's hand"""
    current_hand = RedisGetter.current_round(game_id, f'{player_name}_cards')
    updated_hand = current_hand.split(',')
    updated_hand.remove(played_card)
    RedisSetter.current_round(game_id, f'{player_name}_cards',
                              ','.join(updated_hand))
Exemplo n.º 8
0
def create_redis_entry_for_round_choices(game_id: int,
                                         players: list,
                                         new_game: bool = False):
    """creates new entry in redis db with empty game choice and assigns players a random order.
     new_game=True should be set only when a new game (not a new round!) begins
     so that the order of the players is set (randomly).
     After that, the same order is kept throughout the game's lifetime (but cycled through)
     """
    if new_game:
        random.shuffle(players)
    else:
        old_order = RedisGetter.round_choices(game_id, 'order')
        order = old_order.split(',')
        order.append(
            order.pop(0)
        )  # move each player up a spot (the previously first player is now in last spot)
        players = order

    for player in players:
        RedisSetter.round_choices(game_id, f'{player}_chosen', '')
        RedisSetter.round_choices(game_id, f'{player}_options',
                                  'three,two,one,pass')

        # player 1 initially shouldn't be able to choose 'pass'
        if player == players[0]:
            RedisSetter.round_choices(game_id, f'{player}_options',
                                      'three,two,one')

    RedisSetter.round_choices(game_id, 'order', ','.join(players))
    RedisSetter.round_choices(game_id, 'new_order', ','.join(players))
    RedisSetter.round_choices(game_id, 'last_choice', 'false')
Exemplo n.º 9
0
def is_round_in_progress(game_id: int) -> bool:
    """if all players have 'chosen' in their options in redis it means
    the page was reloaded and the game is in progress."""
    players = get_all_players(game_id)
    for player in players:
        opt = RedisGetter.round_choices(game_id, f'{player}_options')
        if opt == 'chosen':
            return True
    return False
Exemplo n.º 10
0
def update_round_call_options(game_id: str, call_options: list):
    """adds call options to the corresponding entry in redisdb"""
    called = ','.join(call_options)
    RedisSetter.current_round(game_id, 'called', called)

    whose_turn = RedisGetter.current_round(game_id, 'whose_turn')

    # as this triggers the first round for the first player, all cards from their hand can be played
    can_be_played = RedisGetter.current_round(game_id, f'{whose_turn}_cards')
    players_cards = can_be_played.split(',')

    data_to_send = {
        'called': called,
        'whose_turn': whose_turn,
        'can_be_played': players_cards,
        'players_hand': players_cards
    }
    socketio.emit('round call options', data_to_send)
Exemplo n.º 11
0
def check_for_end_of_round(game_id: str) -> bool:
    """if all players have played all of their cards, the round is over and the redis entries are reset.
    Return indicates whether the round is finished or not"""
    players_in_game = get_all_players(int(game_id))
    for player in players_in_game:
        players_hand = RedisGetter.current_round(game_id, f'{player}_cards')
        if players_hand:
            return False

    return True
Exemplo n.º 12
0
def update_order_of_players(game_id: str, new_first_player: str = None) -> str:
    """this method updates the order of players in the :current_round hash in redis.

    If new_first_player=None, it means that a round is currently in progress (there are cards on the table)
    and so the first player is simply removed from the order.

    If new_first_player='some player' it means that the table has been cleared and the first player is known.
    The other players are determined based on who comes after the known player
    in the original order found in the :round_choices hash.

    Returns the player whose turn it is next and updates the whose_turn entry in redis"""

    original_order = RedisGetter.round_choices(game_id, 'order')
    original_order = original_order.split(',')

    if new_first_player is None:
        order_in_redis = RedisGetter.current_round(game_id, 'order')
        new_order = order_in_redis.split(',')
        new_order.pop(0)
    else:
        if original_order.index(new_first_player) == 0:
            new_order = original_order
        elif original_order.index(new_first_player) == 1:
            new_order = [
                new_first_player, original_order[2], original_order[0]
            ]
        elif original_order.index(new_first_player) == 2:
            new_order = [
                new_first_player, original_order[0], original_order[1]
            ]
        else:
            raise RuntimeError(
                f'Error on trying to retrieve {new_first_player} from {original_order}.'
            )

    RedisSetter.current_round(game_id, 'order', ','.join(new_order))
    RedisSetter.current_round(game_id, 'whose_turn', new_order[0])

    return new_order[0]
Exemplo n.º 13
0
def get_cards_on_table(game_id: str) -> list:
    """retrieves the on_table information from redis and returns a list of cards currently on the table.
    If all 3 cards on the table are present, it means that the mini round is over
    and the on_table entry is reset to an empty string."""
    cards_on_table = RedisGetter.current_round(game_id, 'on_table')
    if not cards_on_table:
        return []

    cards_on_table = cards_on_table.split(',')
    if len(cards_on_table) == 3:
        RedisSetter.current_round(game_id, 'on_table', '')
        return []
    return cards_on_table
Exemplo n.º 14
0
def get_players_that_need_to_choose_game(game_id: int) -> Union[list, None]:
    """queries redis db and returns players that can still make a choice of game for round"""
    if is_round_in_progress(game_id):
        return None

    update_player_options(game_id)

    player_order = RedisGetter.round_choices(game_id, 'new_order')
    player_order = player_order.split(',')
    current_player = player_order[0]

    # remove player from first choosing position if they've already made a choice
    choice_of_current_player = RedisGetter.round_choices(
        game_id, f'{current_player}_chosen')
    if choice_of_current_player:
        player_order.pop(0)

    # add player to end of choosing queue if they still have options available
    options_of_current_player = RedisGetter.round_choices(
        game_id, f'{current_player}_options')
    if not options_of_current_player:
        return None
    if options_of_current_player != 'chosen':
        if player_order:
            if player_order[-1] != current_player:
                player_order.append(current_player)
        else:
            # remove player's _options if player_order is empty
            RedisSetter.round_choices(game_id, f'{current_player}_options', '')
    else:
        # remove player from order if their _choices are chosen
        if current_player in player_order:
            player_order.remove(current_player)

    # save new revised order in redis db
    RedisSetter.round_choices(game_id, 'new_order', ','.join(player_order))

    return player_order
Exemplo n.º 15
0
def determine_who_clears_table(game_id: str, cards_on_table: list) -> str:
    """determines which card is the highest (takes the round) and queries redis to determine
    which player played that card. Returns their username"""
    highest_card = determine_winning_card(cards_on_table)

    players_in_game = get_all_players(int(game_id))
    for player in players_in_game:
        players_card = RedisGetter.current_round(game_id, f'{player}_played')
        if players_card == highest_card:
            return player

    raise RuntimeError(
        f'Couldn\'t determine who clears table. Highest card played: {highest_card}'
    )
Exemplo n.º 16
0
def get_players_choices(game_id: int) -> dict:
    """returns dictionary containing choices made so far by players"""
    all_players = get_all_players(game_id)

    players_choice = {}
    for player_username in all_players:
        p_choice = RedisGetter.round_choices(game_id,
                                             f'{player_username}_chosen')
        if not p_choice:
            p_choice = 'čaka na izbiro'
        else:
            p_choice = TRANSLATION_GAME_TYPE[p_choice]
        players_choice[player_username] = p_choice

    return players_choice
Exemplo n.º 17
0
def get_dealt_cards(game_id: int, all_players: list) -> list:
    """checks redisdb to see if a game is in progress and either returns the cards that have been previously dealt
    or deals a new round if a current_round doesn't exist in redis (and creates the entry in redis)"""
    key_exists = redis_db.exists(f'{game_id}:current_round')

    if key_exists:  # it means that a round is currently in progress
        all_players_with_talon = all_players + ['talon']
        dealt_cards = {}
        for player in all_players_with_talon:
            cards = RedisGetter.current_round(game_id, f'{player}_cards')
            dealt_cards[player] = cards.split(',')
    else:
        dealt_cards = deal_new_round(all_players)
        create_redis_entry_for_current_round(game_id, dealt_cards)

    return dealt_cards
Exemplo n.º 18
0
def add_to_score_pile(game_id: int, identifier: str, cards_to_add: list):
    """add cards to a score pile. The identifier must be either 'main_player' or 'against_players'."""
    if identifier not in ['main_player', 'against_players']:
        raise RuntimeError(
            f'Unknown identifier: {identifier}. Should be either "main_player" or "against_players".'
        )

    current_pile = RedisGetter.current_round(game_id,
                                             f'{identifier}_score_pile')
    cards_to_add = ','.join(cards_to_add)
    if current_pile:
        updated_pile = current_pile + ',' + cards_to_add
    else:
        updated_pile = cards_to_add
    RedisSetter.current_round(game_id, f'{identifier}_score_pile',
                              updated_pile)
Exemplo n.º 19
0
def update_player_choosing():
    """handler for sending information of which player can choose again and what choices
    are available to them"""
    user = User.query.filter_by(id=session['user_id']).first()
    game_id = user.current_game
    player_order = get_players_that_need_to_choose_game(game_id)
    player_to_choose_game = None
    player_options = None
    if player_order:
        player_to_choose_game = player_order[0]
        player_options = RedisGetter.round_choices(
            game_id, f'{player_to_choose_game}_options')

    co_players_choice = get_players_choices(game_id)

    data_to_send = {
        'player': player_to_choose_game,
        'player_options': player_options,
        'co_players_choice': co_players_choice
    }

    print(f'[SENDING] player to choose: {data_to_send}')
    socketio.emit('player game options', data_to_send)
Exemplo n.º 20
0
def create_redis_entry_for_current_round(game_id: int,
                                         dealt_cards: dict,
                                         reset: bool = False):
    """creates new entry in redis db to start a new round. Saves the order of players and the state of the dealt cards.
    The redis key {game_id}:current_round should be reset once the round is complete (reset=True)"""
    # save order of players
    order = RedisGetter.round_choices(game_id, 'order')

    RedisSetter.current_round(game_id, 'order', order)
    RedisSetter.current_round(game_id, 'whose_turn', order.split(',')[0])
    RedisSetter.current_round(game_id, 'called', '')
    RedisSetter.current_round(game_id, 'on_table', '')
    RedisSetter.current_round(game_id, 'main_player_score_pile', '')
    RedisSetter.current_round(game_id, 'against_players_score_pile', '')

    # save dealt cards
    for user, value in dealt_cards.items():
        value_for_redis = ','.join(str(card_name) for card_name in value)
        RedisSetter.current_round(game_id, f'{user}_cards', value_for_redis)
        RedisSetter.current_round(game_id, f'{user}_played', '')

    if reset:
        redis_db.hdel(f'{game_id}:current_round', 'main_player')
        redis_db.hdel(f'{game_id}:current_round', 'type')
Exemplo n.º 21
0
def play():
    """handler for main page of game"""
    update_user_in_game(session['user_id'], True)
    user = User.query.filter_by(id=session['user_id']).first()
    game_id = user.current_game
    co_players = get_co_players(game_id, session['user_id'])
    all_players = list(co_players.keys()) + [user.username]

    dealt_cards = get_dealt_cards(game_id, all_players)
    choose_order = get_players_that_need_to_choose_game(game_id)
    player_to_choose = None
    player_to_choose_opts = None
    if choose_order:
        player_to_choose = choose_order[0]
        player_to_choose_opts = RedisGetter.round_choices(
            game_id, f'{player_to_choose}_options')

    called = RedisGetter.current_round(game_id, 'called')
    whose_turn = RedisGetter.current_round(game_id, 'whose_turn')
    main_player = RedisGetter.current_round(game_id, 'main_player')
    game_type = RedisGetter.current_round(game_id, 'type')
    cards_on_table = RedisGetter.current_round(game_id, 'on_table')

    connect_handler()
    return render_template('play.html',
                           player=user.username,
                           co_players=co_players,
                           round_state=dealt_cards,
                           player_to_choose=player_to_choose,
                           player_to_choose_opts=player_to_choose_opts,
                           game_id=game_id,
                           called=called,
                           whose_turn=whose_turn,
                           main_player=main_player,
                           game_type=game_type,
                           on_table=cards_on_table)
Exemplo n.º 22
0
def play_round(game_id: str, user_whose_card: str, card_played: Union[str,
                                                                      None]):
    """
    This method is called continuously by the JS side for the lifetime of a round in a game.
    It needs to be called once every time a player plays a card.

    Once all players have played their card, the round has ended and the redis data is cleared for the game_id.
    This means that the players once again need to choose their round options, see talon and call round attributes
    before this method is called again and a new round is played.

    If card_played=None, this means that the page was reloaded and the frontend side needs information
    on the current state (namely, cards that can be played) and so information should be sent
    but no data in redis should be changed.
    """
    print(f'[RECEIVED] card played by {user_whose_card}: {card_played}')

    whose_turn = RedisGetter.current_round(game_id, 'whose_turn')
    cards_on_table = get_cards_on_table(game_id)
    pile_to_add_to = None

    if card_played:
        # check that the received card is from the expected user
        assert user_whose_card == whose_turn

        # add card_played to redis for the relevant user and remove it from their hand
        RedisSetter.current_round(game_id, f'{user_whose_card}_played',
                                  card_played)
        remove_card_from_hand(game_id, user_whose_card, card_played)

        # if the card played is the third card on the table, determine who clears the table
        cards_on_table.append(card_played)
        RedisSetter.current_round(game_id, 'on_table',
                                  ','.join(cards_on_table))
        if len(cards_on_table) == 3:
            pile_to_add_to = determine_who_clears_table(
                game_id, cards_on_table)

            # add cleared cards to the correct user's (or user group's) pile
            main_player = RedisGetter.current_round(game_id, 'main_player')
            identifier = 'main_player' if pile_to_add_to == main_player else 'against_players'
            add_to_score_pile(game_id, identifier, cards_on_table)

            updated_whose_turn = update_order_of_players(
                game_id, new_first_player=pile_to_add_to)
        else:
            updated_whose_turn = update_order_of_players(game_id)
    else:
        updated_whose_turn = whose_turn

    is_round_finished = check_for_end_of_round(game_id)

    if is_round_finished:
        players_cards = []
        can_be_played = []
    else:
        players_cards = RedisGetter.current_round(
            game_id, f'{updated_whose_turn}_cards')
        players_cards = players_cards.split(',')
        can_be_played = get_possible_card_plays(cards_on_table, players_cards)

    data_to_send = {
        'is_round_finished': is_round_finished,
        'whose_turn': updated_whose_turn,
        'pile_to_add_to': pile_to_add_to,
        'can_be_played': can_be_played,
        'players_hand': players_cards,
        'on_table': cards_on_table
    }
    socketio.emit('gameplay for round', data_to_send)