Beispiel #1
0
 def _handle(cls, msg: 'proto.TileExchange', client: 'Client',
             game: 'Game'):
     if len(game.free_tiles) < 7:
         client.send_msg(
             proto.ActionRejected('There are less than 7 tiles left!'))
     elif not msg.tile_ids:
         client.send_msg(
             proto.ActionRejected(
                 'Tile exchange requires at least one selected tile!'))
     else:
         tiles = [
             tile for tile in client.player.tiles if tile.id in msg.tile_ids
         ]
         tile_count = len(tiles)
         if len(msg.tile_ids) == tile_count:
             client.player.tiles = [
                 tile for tile in client.player.tiles if tile not in tiles
             ]
             game.free_tiles += tiles
             random.shuffle(game.free_tiles)
             client.player.tiles += game.free_tiles[:tile_count]
             game.free_tiles = game.free_tiles[tile_count:]
             game.send_to_all(
                 proto.Notification(f'{client.name} exchanged tiles'),
                 client.player_id)
             client.send_msg(proto.Notification('You exchanged tiles'))
             _end_turn_without_score(client, game)
         else:
             client.send_msg(
                 proto.ActionRejected(
                     'Selected tiles do not belong to player!'))
Beispiel #2
0
def _end_turn_without_score(client: 'Client', game: 'Game'):
    if game.turns_without_score == 5:
        game.send_to_all(
            proto.Notification('6 consecutive scoreless turns have occurred!'))
        for client_ in game.clients:
            deduction = sum(tile.points for tile in client_.player.tiles)
            client_.send_msg(
                proto.Notification(f'Deducted {deduction} points'))
            client_.player.score -= sum(tile.points
                                        for tile in client_.player.tiles)
        end_game = proto.EndGame([
            proto.EndGamePlayer(client.player_id, client.player.score)
            for client in game.clients
        ])
        game.send_to_all(end_game)
        game.lobby = True
    else:
        game.turns_without_score += 1
        game.send_to_all(
            proto.EndTurn(game.turn_player_id, client.player.score, []))
        game.turn_player_id = game.clients[(game.clients.index(client) + 1) %
                                           len(game.clients)].player_id
        player_tile_counts = [
            proto.StartTurnPlayer(client.player_id, len(client.player.tiles))
            for client in game.clients
        ]
        for client_ in game.clients:
            start_turn = proto.StartTurn(game.turn_player_id,
                                         len(game.free_tiles),
                                         client_.player.tiles,
                                         player_tile_counts)
            client_.send_msg(start_turn)
Beispiel #3
0
 def _handle(cls, msg: 'proto.Leave', client: 'Client', game: 'Game'):
     i = game.clients.index(client)
     del game.clients[i]
     game.send_to_all(proto.PlayerLeft(client.player_id))
     if game.lobby:
         if len(game.clients) > 1 and all(client.ready
                                          for client in game.clients):
             _start_game(game)
     elif len(game.clients) < 2:
         for client_ in game.clients:
             deduction = sum(tile.points for tile in client_.player.tiles)
             client_.send_msg(
                 proto.Notification(f'Deducted {deduction} points'))
             client_.player.score -= sum(tile.points
                                         for tile in client_.player.tiles)
         end_game = proto.EndGame([
             proto.EndGamePlayer(client.player_id, client.player.score)
             for client in game.clients
         ])
         game.send_to_all(end_game)
         game.lobby = True
     elif game.turn_player_id == client.player_id:
         game.free_tiles += client.player.tiles
         random.shuffle(game.free_tiles)
         game.turn_player_id = game.clients[i % len(game.clients)].player_id
         tiles_left = len(game.free_tiles)
         player_tile_counts = [
             proto.StartTurnPlayer(client.player_id,
                                   len(client.player.tiles))
             for client in game.clients
         ]
         for client_ in game.clients:
             client_.send_msg(
                 proto.StartTurn(game.turn_player_id, tiles_left,
                                 client_.player.tiles, player_tile_counts))
Beispiel #4
0
def _start_game(game: 'Game'):
    game.board = Board()
    game.load_tiles()
    game.turns_without_score = 0
    for client in game.clients:
        client.ready = False
        player = client.player = Player()
        player.tiles = game.free_tiles[:7]
        game.free_tiles = game.free_tiles[7:]
    game.send_to_all(proto.Notification('Game started!'))
    game.turn_player_id = game.clients[random.randint(0,
                                                      len(game.clients) -
                                                      1)].player_id
    tiles_left = len(game.free_tiles)
    player_tile_counts = [
        proto.StartTurnPlayer(client.player_id, 7) for client in game.clients
    ]
    for client in game.clients:
        start_turn = proto.StartTurn(game.turn_player_id, tiles_left,
                                     client.player.tiles, player_tile_counts)
        client.send_msg(start_turn)
    game.lobby = False
Beispiel #5
0
    def _handle(cls, msg: 'proto.PlaceTiles', client: 'Client', game: 'Game'):
        if not msg.tile_placements:
            game.send_to_all(proto.Notification(f'{client.name} skipped'),
                             client.player_id)
            client.send_msg(proto.Notification('You skipped'))
            _end_turn_without_score(client, game)
            return

        player_tiles_by_id = {tile.id: tile for tile in client.player.tiles}
        tiles = [
            FullTile(player_tiles_by_id[tile.id], tile)
            for tile in msg.tile_placements if tile.id in player_tiles_by_id
        ]
        tile_count = len(tiles)
        if len(msg.tile_placements) != tile_count:
            client.send_msg(
                proto.ActionRejected('Placed tiles do not belong to player!'))
            return

        if any(not tile.letter for tile in tiles):
            client.send_msg(
                proto.ActionRejected('Blank tiles must be assigned a letter!'))
            return

        if all(tile.row == tiles[0].row for tile in tiles):

            def accessor(coord1, coord2):
                return game.board.squares[coord1][coord2]
        elif all(tile.col == tiles[0].col for tile in tiles):

            def accessor(coord1, coord2):
                return game.board.squares[coord2][coord1]

            for tile in tiles:
                tile.row, tile.col = tile.col, tile.row
        else:
            client.send_msg(
                proto.ActionRejected(
                    'Tiles must form a horizontal or vertical line!'))
            return

        row = tiles[0].row
        tiles.sort(key=lambda tile: tile.col)
        tiles_by_col = {tile.col: tile for tile in tiles}
        for tile in tiles:
            if tile.row not in range(15) or tile.col not in range(
                    15) or tiles_by_col.get(tile.col) != tile or accessor(
                        tile.row, tile.col).tile:
                client.send_msg(
                    proto.ActionRejected(
                        'Tiles are overlapping or out of bounds!'))
                return

        for col in range(tiles[0].col + 1, tiles[-1].col + 1):
            if not accessor(row, col).tile and col not in tiles_by_col:
                client.send_msg(
                    proto.ActionRejected('Tiles must form a single line!'))
                return

        if not accessor(7, 7).tile:
            if row != 7 or 7 not in tiles_by_col:
                client.send_msg(
                    proto.ActionRejected(
                        'The center square must be populated!'))
                return
            elif tile_count == 1:
                client.send_msg(
                    proto.ActionRejected(
                        'The first word must be at least 2 characters long!'))
                return

        def count_word(tile_from: 'FullTile',
                       horizontal: bool = False) -> Optional['WordCounter']:
            counter = WordCounter()
            for i in range(
                (tile_from.col if horizontal else tile_from.row) - 1, -1, -1):
                tile = accessor(row, i).tile if horizontal else accessor(
                    i, tile_from.col).tile
                if not tile:
                    break
                counter.points += tile.points
                counter.word = tile.letter + counter.word
                counter.is_connected = True

            for i in range(tile_from.col if horizontal else tile_from.row, 15):
                square = accessor(row, i) if horizontal else accessor(
                    i, tile_from.col)
                if square.tile:
                    tile = square.tile
                    counter.points += tile.points
                    counter.is_connected = True
                elif (i in tiles_by_col) if horizontal else (i
                                                             == tile_from.row):
                    tile = tiles_by_col[i] if horizontal else tile_from
                    if square.type == SquareType.DLS:
                        counter.points += 2 * tile.points
                    elif square.type == SquareType.TLS:
                        counter.points += 3 * tile.points
                    else:
                        counter.points += tile.points
                    if square.type == SquareType.DWS:
                        counter.multiplier *= 2
                    elif square.type == SquareType.TWS:
                        counter.multiplier *= 3
                else:
                    break
                counter.word += tile.letter

            return counter if len(counter.word) > 1 else None

        word_counters = []
        horizontal_counter = count_word(tiles[0], True)
        if horizontal_counter:
            word_counters.append(horizontal_counter)
        for tile in tiles:
            word_counter = count_word(tile)
            if word_counter:
                word_counters.append(word_counter)

        if all(not counter.is_connected
               for counter in word_counters) and accessor(7, 7).tile:
            client.send_msg(
                proto.ActionRejected('Must connect with pre-existing tiles!'))
            return

        global words
        invalid_words = {
            counter.word
            for counter in word_counters if counter.word not in words
        }
        if invalid_words:
            client.send_msg(
                proto.ActionRejected(
                    f'Invalid word{"" if len(invalid_words) == 1 else "s"}: {", ".join(invalid_words)}'
                ))
            return

        for counter in word_counters:
            score = counter.points * counter.multiplier
            client.player.score += score
            game.send_to_all(
                proto.Notification(f'{counter.word} - {score} points'))

        if tile_count == 7:
            client.player.score += 50
            game.send_to_all(proto.Notification('Bingo! - 50 points'))

        for tile in tiles:
            accessor(tile.row, tile.col).tile = tile

        placed_tiles = [
            proto.EndTurnTile(tile.position, tile.points, tile.letter)
            for tile in tiles
        ]
        game.send_to_all(
            proto.EndTurn(game.turn_player_id, client.player.score,
                          placed_tiles))
        game.turns_without_score = 0

        tile_ids = {tile.id for tile in tiles}
        client.player.tiles = [
            tile for tile in client.player.tiles if tile.id not in tile_ids
        ]

        if game.free_tiles:
            take_tiles_count = min(len(game.free_tiles), tile_count)
            client.player.tiles += game.free_tiles[:take_tiles_count]
            game.free_tiles = game.free_tiles[take_tiles_count:]
        elif not client.player.tiles:
            game.send_to_all(
                proto.Notification(f'{client.name} has played out!'),
                client.player_id)
            client.send_msg(proto.Notification('You have played out!'))
            all_sums = 0
            for client_ in game.clients:
                if client_ != client:
                    deduction = sum(tile.points
                                    for tile in client_.player.tiles)
                    client_.player.score -= deduction
                    all_sums += deduction
                    client_.send_msg(
                        proto.Notification(f'Deducted {deduction} points'))
            client.player.score += all_sums
            client.send_msg(proto.Notification(f'Awarded {all_sums} points'))
            end_game = proto.EndGame([
                proto.EndGamePlayer(client.player_id, client.player.score)
                for client in game.clients
            ])
            game.send_to_all(end_game)
            game.lobby = True
            return

        game.turn_player_id = game.clients[(game.clients.index(client) + 1) %
                                           len(game.clients)].player_id
        player_tile_counts = [
            proto.StartTurnPlayer(client.player_id, len(client.player.tiles))
            for client in game.clients
        ]
        for client_ in game.clients:
            start_turn = proto.StartTurn(game.turn_player_id,
                                         len(game.free_tiles),
                                         client_.player.tiles,
                                         player_tile_counts)
            client_.send_msg(start_turn)