Problema de cobra e escada oops, aplicativo

Oct 16 2020

Estou aprendendo e tentando implementar as melhores práticas para jogos de cobra e escada.

Regras do jogo

  1. O tabuleiro terá 100 células numeradas de 1 a 100.
  2. O jogo terá um dado de seis lados numerado de 1 a 6 e sempre dará um número aleatório ao rolar.
  3. Cada jogador tem uma peça que inicialmente é mantida fora do tabuleiro (ou seja, na posição 0).
  4. Cada jogador lança os dados quando chega sua vez. Com base no valor do dado, o jogador move sua peça para frente naquele número de células. Ex: Se o valor do dado for 5 e a peça estiver na posição 21, o jogador colocará sua peça na posição 26 agora (21 + 5).
  5. Um jogador ganha se atingir exatamente a posição 100 e o jogo terminar aí.
  6. Após o lançamento dos dados, se uma peça deve se mover para fora da posição 100, ela não se move.
  7. O tabuleiro também contém algumas cobras e escadas. Cada cobra terá sua cabeça em algum número e sua cauda em um número menor. Sempre que uma peça acabar em uma posição com a cabeça da cobra, a peça deve descer para a posição da cauda dessa cobra.
  8. Cada escada terá sua posição inicial em algum número e a posição final em um número maior. Sempre que uma peça acabar em uma posição com o início da escada, a peça deve subir até a posição do final dessa escada.
  9. Pode haver outra cobra / escada na cauda da cobra ou na posição final da escada e a peça deve subir / descer de acordo.

Suposições

  • Não haverá uma cobra em 100.
  • Não haverá várias cobras / escadas no mesmo ponto inicial / inicial.
  • É possível chegar a 100, ou seja, é possível ganhar o jogo.
  • Snakes and Ladders não formam um loop infinito.
class Snake:
    def __init__(self, start, end):
        self.start = start
        self.end = end
class Ladder:
    def __init__(self, start, end):
        self.start = start
        self.end = end
class Board:
    def __init__(self, size = 100):
        self.size = size
        self.snake_list = []
        self.ladder_list = []

    def add_snake(self, snake):
        self.snake_list.append(snake)

    def add_ladder(self, ladder):
        self.snake_list.append(ladder)
class Dice:
    dice_count = 1
    @staticmethod
    def roll():
        return random.randint(1 * Dice.dice_count, 6 * Dice.dice_count)
class Player:
    def __init__(self, name):
        self.name = name
class PlayerPosition:
    def __init__(self, player, position):
        self.player = player
        self.position = position

    def update_position(self, new_position):
        self.position = new_position
class Game:
    def __init__(self, board):
        self.board = board
        self.players_position = []

    def add_players(self, player, position=0):
        player_position = PlayerPosition(player, position)
        self.players_position.append(player_position)


    def check_win_condition(self, position):
        if position == self.board.size:
            return True
        return False

    def check_for_snake(self, new_position):
        for snake in self.board.snake_list:
            start, end = snake.start, snake.end
            if start == new_position:
                return end

    def check_for_ladder(self, new_position):
        for ladder in self.board.ladder_list:
            start, end = ladder.start, ladder.end
            if start == new_position:
                return end

    def find_new_position(self, new_position):
        if self.check_for_snake(new_position):
            return self.check_for_snake(new_position)
        elif self.check_for_ladder(new_position):
            return self.check_for_ladder(new_position)
        else:
            return new_position


    def start(self):
        still_playing = len(self.players_position)
        while(still_playing):
            for player_position in self.players_position:
                dice_value = Dice.roll()
                current_position = player_position.position
                new_position = current_position + dice_value

                if new_position < self.board.size:
                    new_position = self.find_new_position(new_position)
                    player_position.update_position(new_position)
                    print(player_position.player.name, 'moved from', current_position, 'to', new_position)

                if self.check_win_condition(new_position):
                    print("player", player_position.player.name, "wins!")
                    player_position.update_position(new_position + 1)
                    still_playing -= 1
class GameRunner:
    @classmethod
    def run_game(cls):
        board = Board()
        s1 = Snake(62, 5)
        s2 = Snake(33, 6)
        s3 = Snake(49, 9)
        s4 = Snake(56, 53)
        s5 = Snake(98, 64)
        s6 = Snake(88, 16)
        s7 = Snake(93, 73)
        s8 = Snake(95, 75)

        l1 = Ladder(2,37)
        l2 = Ladder(27, 46)
        l3 = Ladder(10, 32)
        l4 = Ladder(51, 68)
        l5 = Ladder(61, 79)
        l6 = Ladder(65, 84)
        l7 = Ladder(71, 91)
        l8 = Ladder(81, 100)

        board = Board()
        board.add_ladder(l1)
        board.add_ladder(l2)
        board.add_ladder(l3)
        board.add_ladder(l4)
        board.add_ladder(l5)
        board.add_ladder(l6)
        board.add_ladder(l7)
        board.add_ladder(l8)

        board.add_snake(s1)
        board.add_snake(s2)
        board.add_snake(s3)
        board.add_snake(s4)
        board.add_snake(s5)
        board.add_snake(s6)
        board.add_snake(s7)
        board.add_snake(s8)

        player1 = Player("python")
        player2 = Player("java")
        player3 = Player("go")

        game = Game(board)

        game.add_players(player1)
        game.add_players(player2)
        game.add_players(player3)

        game.start()

GameRunner.run_game()

Por favor, sugira locais de melhorias e correções sobre isso.

Respostas

3 hjpotter92 Oct 16 2020 at 05:15

Aulas desnecessárias

As classes cobra, escada e dados não são úteis. Eles podem ser simplesmente substituídos por um namedtuple ou uma classe de dados.

Da mesma forma, jogador e posição do jogador devem ser um único elemento de classe. Um objeto jogador deve ser responsável por manter o controle de sua posição.

Verbosidade

s1, s2, ... s8 e similarmente l1, l2, ... l8 não são realmente usados. Mantenha uma tupla de posições e itere sobre elas, chamando uma delas add_ladderou de add_snakeacordo.

Controle de fluxo

As atualizações de posição devem acontecer no final do jogador, e não no jogo. A instrução de impressão para quando a posição do jogador é atualizada aconteceria dentro da classe do jogador. O objetivo do jogo é apenas controlar e validar movimentos.

Não há declarações mostrando quando um jogador encontra uma escada ou uma cobra. apenas suas mudanças de posição naquela malha de declaração de impressão detalhada.

Execução dupla

    if self.check_for_snake(new_position):
        return self.check_for_snake(new_position)
    elif self.check_for_ladder(new_position):
        return self.check_for_ladder(new_position)
    else:
        return new_position

Você tem a mesma função sendo chamada duas vezes. Para um grande conjunto de células do tabuleiro, não há cobra nem escada. Ainda assim, você continua chamando ambas as funções.

Alternativas

Rolar os dados

sum(random.choices(range(1, 7), k=dice_count))

Condição de vitória

def check_win_condition(self, position):
    return position == self.board.size

Verificação da cabeça da cobra ou da base da escada

Mantenha uma conta usando um setou tuplepara as cobras e escada do tabuleiro sendo adicionadas. A placa deve validar se uma posição tem cabeça de cobra ou base de escada ali. Você está atualmente iterando sobre todas as cobras e escadas (duas vezes, conforme mencionado acima) para cada posição, enquanto uma pesquisa seria \$ O(1) \$.