Solucionado (ver solução)
Solucionado
(ver solução)
6
respostas

O jogo trava quando o herói bate no muro

Olá, quando o jogador está perto do muro o jogo trava dando o seguinte erro:

/Users/cristianopinheiro/Documents/jogo_em_ruby/fogefoge.rb:31:in `calcula_nova_posicao': undefined method `[]' for nil:NilClass (NoMethodError)
    from /Users/cristianopinheiro/Documents/jogo_em_ruby/fogefoge.rb:107:in `joga'
    from /Users/cristianopinheiro/Documents/jogo_em_ruby/fogefoge.rb:123:in `inicia_fogefoge'
    from main.rb:3:in `<main>'
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, linha|

            coluna_do_heroi = linha_atual.index caractere_do_heroi
                if coluna_do_heroi
                    return [linha, coluna_do_heroi]

                end    
        end
    nil 
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, posicao)
    linhas = mapa.size
    colunas = mapa[0].size
    estoutou_linhas = posicao[0].to_i < 0 || posicao[0].to_i >= linhas
    estouro_colunas = posicao[1].to_i < 0 || posicao[1].to_i >= colunas
    if estoutou_linhas || estouro_colunas
            return false
        end

        valor_atual = mapa[posicao[0]][posicao[1]]
        if  valor_atual == "X" || valor_atual == "P"
            return false
        end

        true
end
def soma_vetor(vetor1, vetor2)
    [vetor1[0] + vetor2[0], vetor1[1] + vetor2[1]]

end
def posicoes_validas_a_partir_de(mapa, novo_mapa, posicao)
    posicoes = []
    movimentos = [[+1, 0], [0, +1], [-1, 0], [0, -1]]
    movimentos.each do |movimento|
        nova_posicao = soma_vetor(movimento, posicao)
         if posicao_valida?(mapa, nova_posicao) && posicao_valida?(novo_mapa, nova_posicao)
            posicoes << nova_posicao             
         end

    end
    posicoes
end

def move_fantasma(mapa, novo_mapa, linha, coluna)
    posicoes = posicoes_validas_a_partir_de mapa, novo_mapa, [linha, coluna]
    if posicoes.empty?
        return
    end    
    aleatoria = rand posicoes.size
    posicao = posicoes[aleatoria]
    mapa[linha][coluna]  = " "
    novo_mapa[posicao[0]][posicao[1]] = "P"

end
def copia_mapa(mapa)
    novo_mapa = mapa.join("\n").tr("P"," ").split "\n"
end
def move_fantasmas(mapa)
    caractere_do_fantasma = "P"
    novo_mapa = copia_mapa mapa
    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, novo_mapa, linha, coluna
            end
        end
    end
    novo_mapa
end
def jogador_perdeu?(mapa)
    perdeu = !encontra_jogador(mapa)
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"
        mapa = move_fantasmas mapa
        if jogador_perdeu? mapa
            game_over
            break            
        end
    end
end

def inicia_fogefoge
    nome = da_boas_vindas
    joga nome

end

É realmente necessária esse bloco de código da função calcula_nova_posicao?

movimento = movimentos[direcao]
        heroi[0] += movimento[0]
        heroi[1] += movimento[1]
        heroi
6 respostas

Olá, Adriano.

Tentei reproduzir o erro aqui e não consegui...

Ao bater no muro, o herói não move... O que está correto!

Consegui reproduzir o mesmo erro teclado uma tecla diferente de W, S, A ou D.

Se teclar F, por exemplo, dá esse erro! Daria pra tratar esse caso. Há um bug no código mesmo...

Obrigado pelo retorno Alexandre. Você pode me ajudar com o código, por favor? Ou não tem como?

Fala, Adriano!

Posso sim! Qual é a sua dúvida?

Fiz da seguinte maneira e funcionou!!!!

class 
movimentos = {
"W" => [-1, 0],
"S" => [+1, 0],
"A" => [0, -1],
"D" => [0, +1],
"Q" => [0, 0],
etc... colocando o restante das letras como [0, 0]
}

Tem alguma função pronta do ruby que faz isso, ou não tem como fugir disso?

solução!

Olá, Adriano!

Você está resolvendo o caso da tecla não existir fazendo com que o herói fique parado, né?

Esse jeito de implementar é meio trabalhoso, não?

Poderíamos fazer o seguinte: caso o movimento retornado seja nulo, colocamos [0,0] no movimento.

Assim:

class Heroi
  attr_accessor :linha, :coluna

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

        movimento = movimentos[direcao]

        movimento = [0,0] if movimento.nil?

        novo_heroi.linha += movimento[0]
        novo_heroi.coluna += movimento[1]
        novo_heroi

    end

  #demais métodos...

end

Outra ideia de implementação seria já retornar o herói antecipadamente, sem movê-lo.

Assim:

class Heroi
  attr_accessor :linha, :coluna

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

        movimento = movimentos[direcao]

        if movimento.nil?
            return novo_heroi
        end

        novo_heroi.linha += movimento[0]
        novo_heroi.coluna += movimento[1]
        novo_heroi

    end

    def to_array
      [linha, coluna]
    end

    def remove_do(mapa)
      mapa[linha][coluna] = " "
    end
    def coloca_no(mapa)
      mapa[linha][coluna] = "H"
    end

end

É incrível como aparece nil nesse joguinho. E tem casos em que eu descobria apenas depois de rodar várias vezes a aplicação. A sorte que o interpretador mostra a pilha de chamadas de funções. Eu também fiquei feito asilado com esses nill surgindo feito fantasmas(trocadilho). Eu nunca tinha percebido esse drama nem mesmo com C . O que você ganha em flexibilidade com tipagem fraca e duck typing também pode facilmente desandar se o cara não tiver consciência do código. Porém o jeito Ruby leva o cara a chegar nesse estágio, por isso os caras são famosos por códigos claros, limpos. E ouvi falar que a disciplina de testes foram popularizados com o framework Rails , é verdade isso? Eu não duvido, pois nesse joguinho eu fico pensando quando é que pode aparecer um nill do nada.

undefined method `[]' for nil:NilClass