Esempio n. 1
0
def _place_tile(state, tile, brand): 
    if not 0 <= tile.x < int(os.environ['WIDTH']):
        raise models.RuleViolation('x coordinate is off the board!')
    
    if not 0 <= tile.y < int(os.environ['HEIGHT']):
        raise models.RuleViolation('y coordinate is off the board!')

    if state.grid[tile.x][tile.y] is not None:
        raise models.RuleViolation('Space already has tile on it!')

    neighbors = get_unique_neighbors(state.grid, tile)
    locked_neighbors = [neighbor for neighbor in neighbors if neighbor.is_locked()]

    if len(locked_neighbors) > 1:
        raise models.RuleViolation('Cannot place tile adjacent to multiple locked chains!')

    if len(neighbors) == 0:
        if brand:
            raise models.RuleViolation('Cannot specify brand for single tile!')

        _create_chain(state, tile)
        return models.PlaceTileResult(state, [], None, None, tile)

    if len(neighbors) == 1:
        _grow_chain(state, neighbors[0], brand, tile)
        return models.PlaceTileResult(state, [], None, brand, tile)

    acquired_chains, acquiree, new_brand = _merge_chains(state, neighbors, brand, tile)

    return models.PlaceTileResult(state, acquired_chains, acquiree, new_brand, tile)
Esempio n. 2
0
def _merge_chains(state, current_chains, brand, tile):
    new_chain = models.Chain([tile])
    chains = current_chains + [new_chain]

    branded_chains = [chain for chain in chains if chain.brand]

    if not any(branded_chains):
        return _combine_chains(state, chains, brand)

    max_branded_chain_length_example = max(branded_chains, key=lambda c: len(c.tiles))
    max_branded_chain_length = len(max_branded_chain_length_example.tiles)
    largest_brands = [chain.brand for chain in branded_chains if len(chain.tiles) == max_branded_chain_length]

    largest_brand_count = len(largest_brands)

    if largest_brand_count == 1:
        if brand:
            raise models.RuleViolation('Cannot choose a brand when there is uniquely one largest!')

        return _combine_chains(state, chains, largest_brands[0])

    if not brand:
        raise models.RuleViolation('Must choose a brand!')

    if brand not in largest_brands:
        raise models.RuleViolation('Must choose one of the largest brands!')

    return _combine_chains(state, chains, brand)
Esempio n. 3
0
def buy_stock(state, player_id, brand, amount):
    if amount < 0:
        raise models.RuleViolation('Cannot buy negative stock!')

    if amount == 0:
        return state
    
    available_stock_count = state.stock_availability[brand]
    player_cash_amount = state.money_by_player[player_id]

    if amount > available_stock_count:
        raise models.RuleViolation('Insufficient stock available to fulfill this order!')

    price_per_stock = _calculate_price_from_state_and_brand(state, brand)
    total_price = price_per_stock * amount

    if total_price > player_cash_amount:
        raise models.RuleViolation('You cannot afford this order!')

    state.stock_availability[brand] -= amount
    _perform_money_change(state, player_id, -total_price)
    state.stock_by_player[player_id][brand] += amount

    action_display.record_buy_action(state, brand, amount, price_per_stock)

    return state
Esempio n. 4
0
def trade_stock(state, player_id, brand_to_send, brand_to_receive, send_count):
    if send_count < 0:
        raise models.RuleViolation('Cannot trade negative stock!')

    if send_count % 2 != 0:
        raise models.RuleViolation('Cannot trade an odd number of stock!')
    
    if send_count == 0:
        return state

    global_stock_to_receive_count = state.stock_availability[brand_to_receive]
    receive_count = send_count / 2

    if receive_count > global_stock_to_receive_count:
        raise models.RuleViolation('Insufficient stock available to complete trade!')

    state.stock_by_player[player_id][brand_to_send] -= send_count
    state.stock_by_player[player_id][brand_to_receive] += receive_count
    state.stock_availability[brand_to_send] += send_count
    state.stock_availability[brand_to_receive] -= receive_count

    action_display.record_trade_action(
        state, brand_to_send, send_count, brand_to_receive, receive_count)

    return state
Esempio n. 5
0
def _calculate_price_from_state_and_brand(state, brand):
    chain = grid.find_chain(state.grid, brand)

    if not chain:
        raise models.RuleViolation('No chains of this brand found!')

    return calculate_price_from_chain(chain)
Esempio n. 6
0
def _grow_chain(state, chain, brand, tile):
    if brand in state.cost_by_brand:
        raise models.RuleViolation('Cannot use brand already in use!')
    
    if brand is not None and chain.brand is not None:
        raise models.RuleViolation('Cannot rebrand chain!')

    if brand is None and chain.brand is None:
        raise models.RuleViolation('Cannot grow chain without branding it!')

    if brand:
        chain.brand = brand

    chain.tiles.append(tile)
    
    state.grid[tile.x][tile.y] = chain
    return state
Esempio n. 7
0
def start_game():
    game_id = request.json['game_id']

    game_state = persistance.get_game_state(game_id)

    if game_state.is_started:
        raise models.RuleViolation('Cannot start already started game!')

    player_count_min = int(os.environ['PLAYER_COUNT_MIN'])
    player_count_max = int(os.environ['PLAYER_COUNT_MAX'])
    tile_hand_size = int(os.environ['TILE_HAND_SIZE'])

    player_count = len(game_state.player_order)

    if not player_count_min <= player_count <= player_count_max:
        raise models.RuleViolation(
            f'Cannot start game with {player_count} players!')

    shuffle(game_state.player_order)
    starting_player = game_state.player_order[0]

    initial_tiles = tiles.generate_initial_tiles()

    board_starting_tiles = tiles.draw_tiles(initial_tiles, player_count)
    for tile in board_starting_tiles:
        grid.place_tile(game_state, tile)

    game_state.is_started = True
    game_state.current_turn_player = starting_player
    game_state.current_action_player = starting_player

    tiles_by_player_id = {
        player_id: tiles.draw_tiles(initial_tiles, tile_hand_size)
        for player_id in game_state.player_order
    }

    game_state.tiles_remaining = len(initial_tiles)

    persistance.initialize_global_tiles(game_id, initial_tiles)
    persistance.initialize_player_tiles(game_id, tiles_by_player_id)
    persistance.update_game_state(game_id, game_state.to_dict())

    return 'OK'
Esempio n. 8
0
def place_tile():
    game_id = request.json['game_id']
    id_token = request.json['id_token']
    x = int(request.json['x'])
    y = int(request.json['y'])
    raw_brand = request.json['brand']
    brand = None if raw_brand == '' else models.Brand(raw_brand)

    player_id = firebase_admin.auth.verify_id_token(id_token)['uid']
    state = persistance.get_game_state(game_id)

    if state.current_action_player != player_id:
        raise models.RuleViolation(
            "Stop trying to take other player's turns! You cheat!")

    if state.current_action_type != models.ActionType.PLACE_TILE:
        raise models.RuleViolation(
            'It is your turn, but it is not time to place a tile!')

    player_tiles = persistance.get_player_tiles(game_id, player_id)

    if not any(tile.x == x and tile.y == y for tile in player_tiles):
        raise models.RuleViolation(
            'You do not have that tile! Stop trying to cheat!')

    if not state.is_started:
        raise models.RuleViolation('Cannot take turn until game has begun!')

    tile = models.Tile(x, y)
    place_tile_result = grid.place_tile(state, tile, brand)
    stock.apply_majority_bonuses(state, place_tile_result.acquired_chains)
    stock.award_founder_share(state, player_id, place_tile_result.new_brand)
    grid.set_brand_lists(state)
    stock.set_price_table(state)
    turns.transition_from_place(state, place_tile_result, game_id)

    persistance.delete_player_tile(game_id, player_id, models.Tile(x, y))
    # this should all happen atomically, but as a stopgap make sure this happens last
    persistance.update_game_state(game_id, state.to_dict())

    return 'OK'
Esempio n. 9
0
def sell_stock(state, player_id, brand, cost_per_stock, sell_count):
    if sell_count < 0:
        raise models.RuleViolation('Cannot sell negative stock!')

    if sell_count == 0:
        return state

    player_stock_count = state.stock_by_player[player_id][brand]

    if player_stock_count < sell_count:
        raise models.RuleViolation('Cannot sell more stock than you have!')

    total_price = cost_per_stock * sell_count

    state.stock_by_player[player_id][brand] -= sell_count
    state.stock_availability[brand] += sell_count
    _perform_money_change(state, player_id, total_price)

    action_display.record_sell_action(state, player_id, brand, sell_count, cost_per_stock)

    return state
Esempio n. 10
0
def resolve_acquisition():
    game_id = request.json['game_id']
    id_token = request.json['id_token']
    sell_count = int(request.json['sell_count'])
    trade_count = int(request.json['trade_count'])

    user_id = firebase_admin.auth.verify_id_token(id_token)['uid']
    state = persistance.get_game_state(game_id)

    if state.current_action_player != user_id:
        raise models.RuleViolation(
            "Stop trying to take other player's turns! You cheat!")

    if state.current_action_type != models.ActionType.RESOLVE_ACQUISITION:
        raise models.RuleViolation(
            'It is your turn, but it is not time to resolve an acquisition!')

    if not state.is_started:
        raise models.RuleViolation('Cannot take turn until game has begun!')

    acquiree = models.Brand(state.current_action_details['acquiree'])
    player_acquiree_stock_count = state.stock_by_player[user_id][acquiree]

    if sell_count + trade_count > player_acquiree_stock_count:
        raise models.RuleViolation(
            'Cannot trade and sell more stock than you current have!')

    acquirer = models.Brand(state.current_action_details['acquirer'])
    cost_at_acquisition_time = state.current_action_details[
        'acquiree_cost_at_acquisition_time']

    stock.sell_stock(state, user_id, acquiree, cost_at_acquisition_time,
                     sell_count)
    stock.trade_stock(state, user_id, acquiree, acquirer, trade_count)

    turns.transition_from_resolve(state, game_id)

    persistance.update_game_state(game_id, state.to_dict())

    return 'OK'
Esempio n. 11
0
def join_game():
    game_id = request.json['game_id']
    user_id = request.json['user_id']

    game_state = persistance.get_game_state(game_id)

    if user_id in game_state.player_order:
        raise models.RuleViolation('Player is already in this game!')

    if game_state.is_started:
        raise models.RuleViolation('Cannot join a game in progress!')

    user_data = persistance.get_user_data(user_id)

    game_state.player_order.append(user_id)
    game_state.money_by_player[user_id] = 6000
    game_state.stock_by_player[user_id] = {brand: 0 for brand in models.Brand}
    game_state.user_data_by_id[user_id] = user_data

    persistance.update_game_state(game_id, game_state.to_dict())

    return 'OK'
Esempio n. 12
0
def buy_stock():
    max_purchase_amount = int(os.environ['MAX_STOCK_PURCHASE_AMOUNT'])

    game_id = request.json['game_id']
    id_token = request.json['id_token']
    purchase_order = request.json['purchase_order']

    user_id = firebase_admin.auth.verify_id_token(id_token)['uid']
    state = persistance.get_game_state(game_id)

    if state.current_action_player != user_id:
        raise models.RuleViolation(
            "Stop trying to take other player's turns! You cheat!")

    if state.current_action_type != models.ActionType.BUY_STOCK:
        raise models.RuleViolation(
            'It is your turn, but it is not time to buy stock!')

    if not state.is_started:
        raise models.RuleViolation('Cannot take turn until game has begun!')

    total_stock_purchased = 0

    for raw_brand, raw_amount in purchase_order.items():
        parsed_amount = int(raw_amount)
        total_stock_purchased += parsed_amount
        brand = models.Brand(raw_brand)
        stock.buy_stock(state, user_id, brand, parsed_amount)

    if total_stock_purchased > max_purchase_amount:
        raise models.RuleViolation('Too many stock in purchase order!')

    turn_transitioned_state = turns.transition_from_buy(state, game_id)

    persistance.update_game_state(game_id, turn_transitioned_state.to_dict())

    return 'OK'