1
resposta

posicao_valida?': undefined method `[]' for nil:NilClass (NoMethodError)

Oi pessoal,

Não estou conseguindo entender porque que o "mapa[nova_posicao[0]]" esta aparecendo como nulo, como assim ele é nulo? Nós já não definimos o valor da "nova_posicao[0]" retornando o heroi na "calcula_nova_posicao heroi, direcao"?

O meu código está assim:

require_relative 'ui'

def le_mapa(numero)
    arquivo = "mapa#{numero}.txt"
    texto = File.read arquivo
    mapa = texto.split "\n"
end

def encontra_jogador(mapa)
    caractere_do_heroi = "H"
    mapa.each_with_index do |linha_atual, numero_da_linha|
        coluna_do_heroi = linha_atual.index caractere_do_heroi
        if coluna_do_heroi
            return [numero_da_linha, coluna_do_heroi]
        end
    end
    # nao achei
end

def calcula_nova_posicao (heroi, direcao)
    heroi = heroi.dup
    movimentos = {
        "W" => [-1, 0],
        "S" => [+1, 0],
        "A" => [0, -1],
        "D" => [0, +1]
    }

    movimento = movimentos[direcao]
    heroi[0] += movimento[0]
    heroi[1] += movimento[1]
    heroi
end

def posicao_valida? (mapa, nova_posicao)

    estourou_linhas = nova_posicao[0] < 0 || nova_posicao[0] >= mapa.size
    estourou_colunas = nova_posicao[1] < 0 || nova_posicao[1] >= mapa[0].size

    if estourou_linhas || estourou_colunas
        return false
    end


    tem_muro = mapa[nova_posicao[0]][nova_posicao[1]] == "X"
    tem_fantasma = mapa[nova_posicao[0]][nova_posicao[1]] == "F"


    if tem_muro || tem_fantasma
        return false
   end

   true

end



def move_fantasmas(mapa)
    caractere_do_fantasma = "F"
    mapa.each_with_index do |linha_atual, linha|
        linha_atual.chars.each_with_index do |caractere_atual, coluna|
            eh_fantasma = caractere_atual == caractere_do_fantasma
            if eh_fantasma
                move_fantasma mapa, linha, coluna
            end
        end
    end
end

def posicoes_validas_a_partir_de (mapa, nova_posicao)
    posicoes = []

    baixo = [nova_posicao[0] + 1, nova_posicao[1]]
    if posicao_valida? mapa, baixo
        posicoes << baixo
    end

    direita = [nova_posicao[0], nova_posicao[1] + 1]
    if posicao_valida? mapa, direita
        posicoes << direita
    end

    cima = [nova_posicao[0] - 1, nova_posicao[1]]
    if posicao_valida? mapa, cima
        posicoes << cima
    end

    esquerda = [nova_posicao[0], nova_posicao[1] - 1]
    if posicao_valida? mapa, esquerda
        posicoes << esquerda
    end

    posicoes
end

def move_fantasma (mapa, linha, coluna)
    posicoes = posicoes_validas_a_partir_de mapa, [linha, coluna]

    if posicoes.empty?
        return
    end


    nova_posicao = posicoes[0]

    if posicao_valida? mapa, nova_posicao
        mapa[linha][coluna] = " "
        mapa[nova_posicao[0]][nova_posicao[1]] = "F"
    end
end


def joga(nome)
    mapa = le_mapa 2

    while true
        desenha mapa
        direcao = pede_movimento
        heroi = encontra_jogador mapa
        nova_posicao = calcula_nova_posicao heroi, direcao
        if !posicao_valida? mapa, nova_posicao
            next
        end
        mapa[heroi[0]][heroi[1]] = " "
        mapa[nova_posicao[0]][nova_posicao[1]] = "H"

        move_fantasmas mapa
    end

end


def inicia_fogefoge
    nome = da_boas_vindas
    joga nome
end

Quando eu rodo ele no terminal e entro com alguma das letras ("W", "A", "S", "D") para mover o herói funciona perfeitamente, porém, se eu inverter a ordem da checagem das posições validas, ou seja, se eu checar primeiro se tem fantasma e se tem muro e depois checar se estourou linha ou estourou coluna da erro.

A diferença no código seria essa:

def posicao_valida? (mapa, nova_posicao)

    tem_muro = mapa[nova_posicao[0]][nova_posicao[1]] == "X"
    tem_fantasma = mapa[nova_posicao[0]][nova_posicao[1]] == "F"


    if tem_muro || tem_fantasma
        return false
   end

    estourou_linhas = nova_posicao[0] < 0 || nova_posicao[0] >= mapa.size
    estourou_colunas = nova_posicao[1] < 0 || nova_posicao[1] >= mapa[0].size

    if estourou_linhas || estourou_colunas
        return false
    end

   true

end

Se eu rodar o código dessa maneira aparece o seguinte erro:

rb:37:in posicao_valida?': undefined method[]' for nil:NilClass (NoMethodError)

Não consigo entender porque! Porque eu tenho que necessariamente checar primeiro se estourou linha ou coluna antes de checar se tem fantasma ou muro?

Desde já agradeço!!

1 resposta

Nossa Henrique, pra mim também não faz sentido, não entendi. A ordem das verificações não deveria importar nesse caso.

Você consegue subir seu projeto no GitHub do jeito que ele tá para que eu consiga testar com seus arquivos originais e verificar o problema mais de perto.?

Olhando o código de longe assim eu não vou conseguir simular bem o problema.