Projeto de jogo Ruby Mastermind com IA
Sou autodidata e muito novo em codificação e criei um jogo Mastermind em Ruby. Qualquer feedback geral ou conselho seria muito apreciado. O jogo está totalmente funcional no momento e possui uma IA simples. No início, o jogador escolhe qual função deseja escolher (codebreaker ou code maker).
Felicidades
https://repl.it/repls/WeirdFrankButtons
editar: link fixo
class Game
def initialize
puts "---------------------------------"
puts "Welcome to Mastermind"
puts "The goal is to either create a 4 digit code (Code maker role) containing numbers ranging from 1 through 6 or to guess a code (Codebreaker role) created by the computer within 12 turns to win."
puts "After each guess you will be given an accuracy score indicating how close you were to guessing the code correctly."
puts "The letter \"H\" indicates one of the numbers you guessed is in the correct position. The letter \"h\" indicates you guessed a correct number but it is NOT in the correct position"
puts "----------------------------------"
@game_over = false
@turn = 1
until @comp_guess_mode === "Y" || @comp_guess_mode === "N"
print "Is the computer the code breaker? Y/N"
@comp_guess_mode = gets.chomp.upcase
end
game_mode
turn_sequence
end
def game_mode
if @comp_guess_mode == "Y"
human_code_generator
else
code_generator
end
end
def code_generator
@code = Array.new(4) {rand(1..6)}
end
def human_code_generator
@code = ""
puts "Please enter a 4 digit code"
until @code.length == 4
@code = gets.chomp.each_char.map(&:to_i)
end
end
# computer_guesser method that tests if the computer's guess matches the human's
# by iterating through the array, if a direct match ('H') is found it will keep that number in the next guess
def computer_guesser
@updated_comp_guess = [" "," "," "," "]
if @turn == 1
@guess = Array.new(4) {rand(1..6)}
else
i = 0
while i <4
if @guess[i] == @code[i]
@updated_comp_guess[i] = @guess[i]
i+=1
else
i +=1
end
end
end
@guess = Array.new(4) {rand(1..6)}
@updated_comp_guess.each_with_index do |value, idx|
if value != " "
@guess[idx] = value
end
end
puts "Guess: #{@guess.join}"
end
def codebreaker_guess
@guess = []
until @guess.length == 4
puts "Enter your 4 digit guess"
@guess = gets.chomp.each_char.map(&:to_i)
puts "Guess: #{@guess.join}"
if @guess.length != 4
print "Your guess was not 4 digits long, please guess again \n"
end
end
end
def turn_display
puts "-------------------------"
puts "It's turn number: #{@turn}"
end
#Repeats the following guess/check sequence for 12 turns
# or until the code and guess are matched
def turn_sequence
while @turn <13 && @game_over == false
turn_display
if @comp_guess_mode == "Y"
computer_guesser
else
codebreaker_guess
end
guess_checker
@turn += 1
victory_check
end
end
def guess_checker
@guess_accuracy = []
@i=0
@h_counter = 0
while @i<4
if @guess[@i] == @code[@i]
@guess_accuracy.push("H")
@h_counter += 1
@i+=1
else
@i+=1
end
end
if @i == 4
i = 0
compare_array = @code.clone
while i < 4
if compare_array.include?(@guess[i])
compare_array[(compare_array.index(@guess[i]))]= " "
@guess_accuracy.push("h")
i+=1
else
i+=1
end
end
@guess_accuracy.pop(@h_counter)
puts "Guess accuracy: #{@guess_accuracy.join}"
end
end
def victory_check
if @guess[0..3] == @code[0..3]
puts "Code was guessed correctly, it's #{@code}, codebreaker wins"
@game_over = true
elsif @turn == 13 && @game_over == false
puts "Code was not guessed correctly, code maker wins"
@game_over = true
end
end
end
game = Game.new
```
Respostas
Código 1
while i <4
if @guess[i] == @code[i]
@updated_comp_guess[i] = @guess[i]
i+=1
else
i +=1
end
end
Tanto no if
quanto else
você está incrementando i
em 1. Isso pode ser encurtado.
while i <4
if @guess[i] == @code[i]
@updated_comp_guess[i] = @guess[i]
end
i += 1
end
Semelhante no guess_checker
mais abaixo, e nocompare_array
Consistência
Às vezes você está usando 1 espaço para indentação, às vezes você está usando 2. Às vezes você está usando espaço em branco ao redor dos operadores, às vezes não, às vezes você está usando espaço em branco em um lado do operador, mas não no outro. Às vezes, você usa espaço após uma vírgula, às vezes não. Às vezes você usa uma linha vazia após um método, às vezes duas, às vezes nenhuma.
Você deve escolher um estilo e ficar com ele. Se estiver editando algum código existente, você deve adaptar seu estilo para ser igual ao código existente. Se você faz parte de uma equipe, deve adaptar seu estilo para combinar com o resto da equipe.
A maioria das comunidades desenvolveu guias de estilo de comunidade padronizados. Em Ruby, existem vários desses guias de estilo. Todos eles concordam no básico (por exemplo, o recuo é de 2 espaços), mas podem discordar em pontos mais específicos (aspas simples ou duplas).
Recuo
O estilo de indentação padrão em Ruby é de dois espaços. Você usa principalmente 2 espaços, mas há um lugar onde você usa 1 espaço. Fique com dois.
Espaço em branco ao redor dos operadores
Deve haver 1 espaço de cada lado de um operador. Às vezes você usa 1 espaço, às vezes nenhum espaço e às vezes espaço apenas de um lado.
Por exemplo aqui, você tem exatamente a mesma expressão dentro de três linhas com dois estilos de espaçamento diferentes:
i+=1
else
i +=1
Eles são inconsistentes entre si e ambos não cumprem as diretrizes da comunidade. Ambos devem ser:
i += 1
Espaço após a vírgula
Deve haver 1 espaço após uma vírgula. Às vezes você usa 1 espaço, às vezes nenhum espaço.
Por exemplo aqui:
@updated_comp_guess = [" "," "," "," "]
deveria estar
@updated_comp_guess = [" ", " ", " ", " "]
Espaço em blocos
Em um literal de bloco, deve haver um espaço após a chave de abertura e um antes da chave de fechamento:
@code = Array.new(4) { rand(1..6) }
Strings com aspas simples
Se você não usar interpolação de strings, é útil usar aspas simples para suas strings. Dessa forma, é imediatamente óbvio que nenhuma interpolação de string está ocorrendo.
Em particular, isso também removeria o escape que você precisa fazer aqui:
puts 'The letter "H" indicates one of the numbers you guessed is in the correct position. The letter "h" indicates you guessed a correct number but it is NOT in the correct position'
Literais de string congelados
Estruturas de dados imutáveis e código puramente funcional são sempre preferidos, a menos que mutabilidade e efeitos colaterais sejam necessários para clareza ou desempenho. Em Ruby, as strings são sempre mutáveis, mas há um comentário mágico que você pode adicionar aos seus arquivos (também disponível como uma opção de linha de comando para o mecanismo Ruby), que tornará automaticamente todas as strings literais imutáveis:
# frozen_string_literal: true
Geralmente é preferível adicionar este comentário a todos os seus arquivos.
Modificadores condicionais
Quando você tem uma condicional que executa apenas uma expressão, você deve usar a forma modificadora, por exemplo:
if value != " "
@guess[idx] = value
end
deveria estar
@guess[idx] = value if value != " "
Mesmo aqui:
until @code.length == 4
@code = gets.chomp.each_char.map(&:to_i)
end
deveria estar
@code = gets.chomp.each_char.map(&:to_i) until @code.length == 4
Parênteses desnecessários
compare_array[(compare_array.index(@guess[i]))]= " "
Os parênteses ao redor compare_array.index(@guess[i])
são desnecessários.
Linting
Você deve executar algum tipo de linter ou analisador estático em seu código. Rubocop é um popular, mas existem outros.
O Rubocop foi capaz de detectar todas as violações de estilo que apontei e também foi capaz de corrigi-las automaticamente.
Deixe-me repetir: acabei de passar duas páginas mostrando como corrigir toneladas de coisas que você pode realmente corrigir em milissegundos com o apertar de um botão. Eu configurei meu editor para que ele execute automaticamente o Rubocop com correção automática assim que eu clicar em "salvar".
Em particular, executando Rubocop em seu código, ele detecta 98 ofensas, das quais pode corrigir automaticamente 76. Isso deixa você com 22 ofensas, das quais 11 são muito simples.
Veja como é o resultado da correção automática:
# frozen_string_literal: true
class Game
def initialize
puts '---------------------------------'
puts 'Welcome to Mastermind'
puts 'The goal is to either create a 4 digit code (Code maker role) containing numbers ranging from 1 through 6 or to guess a code (Codebreaker role) created by the computer within 12 turns to win.'
puts 'After each guess you will be given an accuracy score indicating how close you were to guessing the code correctly.'
puts 'The letter "H" indicates one of the numbers you guessed is in the correct position. The letter "h" indicates you guessed a correct number but it is NOT in the correct position'
puts '----------------------------------'
@game_over = false
@turn = 1
until @comp_guess_mode === 'Y' || @comp_guess_mode === 'N'
print 'Is the computer the code breaker? Y/N'
@comp_guess_mode = gets.chomp.upcase
end
game_mode
turn_sequence
end
def game_mode
if @comp_guess_mode == 'Y'
human_code_generator
else
code_generator
end
end
def code_generator
@code = Array.new(4) { rand(1..6) }
end
def human_code_generator
@code = ''
puts 'Please enter a 4 digit code'
@code = gets.chomp.each_char.map(&:to_i) until @code.length == 4
end
# computer_guesser method that tests if the computer's guess matches the human's
# by iterating through the array, if a direct match ('H') is found it will keep that number in the next guess
def computer_guesser
@updated_comp_guess = [' ', ' ', ' ', ' ']
if @turn == 1
@guess = Array.new(4) { rand(1..6) }
else
i = 0
while i < 4
if @guess[i] == @code[i]
@updated_comp_guess[i] = @guess[i]
i += 1
else
i += 1
end
end
end
@guess = Array.new(4) { rand(1..6) }
@updated_comp_guess.each_with_index do |value, idx|
@guess[idx] = value if value != ' '
end
puts "Guess: #{@guess.join}"
end
def codebreaker_guess
@guess = []
until @guess.length == 4
puts 'Enter your 4 digit guess'
@guess = gets.chomp.each_char.map(&:to_i)
puts "Guess: #{@guess.join}"
print "Your guess was not 4 digits long, please guess again \n" if @guess.length != 4
end
end
def turn_display
puts '-------------------------'
puts "It's turn number: #{@turn}"
end
# Repeats the following guess/check sequence for 12 turns
# or until the code and guess are matched
def turn_sequence
while @turn < 13 && @game_over == false
turn_display
if @comp_guess_mode == 'Y'
computer_guesser
else
codebreaker_guess
end
guess_checker
@turn += 1
victory_check
end
end
def guess_checker
@guess_accuracy = []
@i = 0
@h_counter = 0
while @i < 4
if @guess[@i] == @code[@i]
@guess_accuracy.push('H')
@h_counter += 1
@i += 1
else
@i += 1
end
end
if @i == 4
i = 0
compare_array = @code.clone
while i < 4
if compare_array.include?(@guess[i])
compare_array[compare_array.index(@guess[i])] = ' '
@guess_accuracy.push('h')
i += 1
else
i += 1
end
end
@guess_accuracy.pop(@h_counter)
puts "Guess accuracy: #{@guess_accuracy.join}"
end
end
def victory_check
if @guess[0..3] == @code[0..3]
puts "Code was guessed correctly, it's #{@code}, codebreaker wins"
@game_over = true
elsif @turn == 13 && @game_over == false
puts 'Code was not guessed correctly, code maker wins'
@game_over = true
end
end
end
game = Game.new
E aqui estão as ofensas que o Rubocop não conseguiu corrigir automaticamente:
Offenses:
game.rb:3:1: C: Metrics/ClassLength: Class has too many lines. [116/100]
class Game ...
^^^^^^^^^^
game.rb:3:1: C: Style/Documentation: Missing top-level class documentation comment.
class Game
^^^^^
game.rb:4:3: C: Metrics/MethodLength: Method has too many lines. [14/10]
def initialize ...
^^^^^^^^^^^^^^
game.rb:7:121: C: Layout/LineLength: Line is too long. [202/120]
puts 'The goal is to either create a 4 digit code (Code maker role) containing numbers ranging from 1 through 6 or to guess a code (Codebreaker role) created by the computer within 12 turns to win.'
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
game.rb:8:121: C: Layout/LineLength: Line is too long. [125/120]
puts 'After each guess you will be given an accuracy score indicating how close you were to guessing the code correctly.'
^^^^^
game.rb:9:121: C: Layout/LineLength: Line is too long. [186/120]
puts 'The letter "H" indicates one of the numbers you guessed is in the correct position. The letter "h" indicates you guessed a correct number but it is NOT in the correct position'
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
game.rb:13:28: C: Style/CaseEquality: Avoid the use of the case equality operator ===.
until @comp_guess_mode === 'Y' || @comp_guess_mode === 'N'
^^^
game.rb:13:56: C: Style/CaseEquality: Avoid the use of the case equality operator ===.
until @comp_guess_mode === 'Y' || @comp_guess_mode === 'N'
^^^
game.rb:41:3: C: Metrics/AbcSize: Assignment Branch Condition size for computer_guesser is too high. [<12, 12, 11> 20.22/17]
def computer_guesser ...
^^^^^^^^^^^^^^^^^^^^
game.rb:41:3: C: Metrics/MethodLength: Method has too many lines. [19/10]
def computer_guesser ...
^^^^^^^^^^^^^^^^^^^^
game.rb:50:11: C: Style/IdenticalConditionalBranches: Move i += 1 out of the conditional.
i += 1
^^^^^^
game.rb:52:11: C: Style/IdenticalConditionalBranches: Move i += 1 out of the conditional.
i += 1
^^^^^^
game.rb:80:3: C: Metrics/MethodLength: Method has too many lines. [11/10]
def turn_sequence ...
^^^^^^^^^^^^^^^^^
game.rb:94:3: C: Metrics/AbcSize: Assignment Branch Condition size for guess_checker is too high. [<16, 13, 11> 23.37/17]
def guess_checker ...
^^^^^^^^^^^^^^^^^
game.rb:94:3: C: Metrics/MethodLength: Method has too many lines. [27/10]
def guess_checker ...
^^^^^^^^^^^^^^^^^
game.rb:102:9: C: Style/IdenticalConditionalBranches: Move @i += 1 out of the conditional.
@i += 1
^^^^^^^
game.rb:104:9: C: Style/IdenticalConditionalBranches: Move @i += 1 out of the conditional.
@i += 1
^^^^^^^
game.rb:107:5: C: Style/GuardClause: Use a guard clause (return unless @i == 4) instead of wrapping the code inside a conditional expression.
if @i == 4
^^
game.rb:114:11: C: Style/IdenticalConditionalBranches: Move i += 1 out of the conditional.
i += 1
^^^^^^
game.rb:116:11: C: Style/IdenticalConditionalBranches: Move i += 1 out of the conditional.
i += 1
^^^^^^
game.rb:117:8: W: Layout/EndAlignment: end at 117, 7 is not aligned with if at 111, 8.
end
^^^
game.rb:135:1: W: Lint/UselessAssignment: Useless assignment to variable - game.
game = Game.new
^^^^
1 file inspected, 22 offenses detected
Vejamos primeiro os mais simples.
Operador de igualdade de caso
Você usa o operador de igualdade de maiúsculas e minúsculas em alguns lugares. Devido à forma como a igualdade de maiúsculas e minúsculas é definida para strings, seu código funciona puramente "por acidente". Em vez disso, você deve usar o operador de igualdade normal.
Este:
until @comp_guess_mode === "Y" || @comp_guess_mode === "N"
deve ser isso:
until @comp_guess_mode == "Y" || @comp_guess_mode == "N"
Observe que você usa o operador de igualdade correto para exatamente a mesma verificação aqui:
if @comp_guess_mode == "Y"
Expressões idênticas em todos os ramos
Existem três lugares onde você tem a mesma expressão em ambas as ramificações de uma expressão condicional. Isso é uma confusão desnecessária, você pode simplesmente retirar a expressão do condicional:
if @guess[i] == @code[i]
@updated_comp_guess[i] = @guess[i]
i+=1
else
i +=1
end
deveria estar
if @guess[i] == @code[i]
@updated_comp_guess[i] = @guess[i]
end
i +=1
E como dissemos acima, uma condicional com apenas uma expressão deve usar a forma modificadora (observe que essa transformação seria novamente realizada automaticamente pelo Rubocop se você a executasse novamente):
@updated_comp_guess[i] = @guess[i] if @guess[i] == @code[i]
i +=1
Variável local não utilizada
game = Game.new
game
nunca é usado em lugar nenhum. Basta removê-lo:
Game.new
Cláusulas de guarda
Se você tiver um caso em que um método ou bloco inteiro é encapsulado em uma condicional, poderá substituí-lo por uma "cláusula de guarda" e reduzir o nível de aninhamento.
Por exemplo:
def something
if foo
bar
baz
quux
else
42
end
end
pode se tornar isso:
def something
return 42 unless foo
bar
baz
quux
end
Existem algumas oportunidades para fazer isso em seu código, e mais algumas são criadas seguindo o conselho do Rubocop.
Aqui está um exemplo em que a melhoria não é muito grande:
def game_mode
if @comp_guess_mode == "Y"
human_code_generator
else
code_generator
end
end
def game_mode
return human_code_generator if @comp_guess_mode == "Y"
code_generator
end
mas aqui o ganho é um pouco maior:
def guess_checker
@guess_accuracy = []
@i=0
@h_counter = 0
while @i<4
if @guess[@i] == @code[@i]
@guess_accuracy.push("H")
@h_counter += 1
@i+=1
else
@i+=1
end
end
if @i == 4
i = 0
compare_array = @code.clone
while i < 4
if compare_array.include?(@guess[i])
compare_array[(compare_array.index(@guess[i]))]= " "
@guess_accuracy.push("h")
i+=1
else
i+=1
end
end
@guess_accuracy.pop(@h_counter)
puts "Guess accuracy: #{@guess_accuracy.join}"
end
end
def guess_checker
@guess_accuracy = []
@i = 0
@h_counter = 0
while @i < 4
if @guess[@i] == @code[@i]
@guess_accuracy.push('H')
@h_counter += 1
end
@i += 1
end
return unless @i == 4
i = 0
compare_array = @code.clone
while i < 4
if compare_array.include?(@guess[i])
compare_array[compare_array.index(@guess[i])]= ' '
@guess_accuracy.push('h')
end
i += 1
end
@guess_accuracy.pop(@h_counter)
puts "Guess accuracy: #{@guess_accuracy.join}"
end
verificações redundantes
Mas, na verdade, a coisa toda é ainda mais simples: como você faz um loop e incrementa i
4 vezes, sempre será 4
, então a condição sempre será verdadeira e você pode simplesmente removê-la completamente.
Igualdade com booleanos
@game_over == false
@game_over
já é um booleano, não há necessidade de verificar a igualdade para false
. Isso é apenas
!@game_over
Variáveis de instância desnecessárias
As variáveis @updated_comp_guess
de instância , @i
, @h_counter
e @guess_accuracy
são usadas apenas em um método. Em vez disso, eles devem ser variáveis locais.
rotações
Em Ruby, você quase nunca precisa de loops. Na verdade, eu iria tão longe e diria que se você estiver usando um loop em Ruby, você está fazendo errado.
Aqui está um exemplo:
i = 0
while i < 4
updated_comp_guess[i] = @guess[i] if @guess[i] == @code[i]
i += 1
end
seria muito melhor escrito como
4.times do |i|
updated_comp_guess[i] = @guess[i] if @guess[i] == @code[i]
end
Isso fará com que o guess_checker
método fique assim:
def guess_checker
guess_accuracy = []
h_counter = 0
4.times do |i|
if @guess[i] == @code[i]
guess_accuracy.push('H')
h_counter += 1
end
end
compare_array = @code.clone
4.times do |i|
if compare_array.include?(@guess[i])
compare_array[compare_array.index(@guess[i])] = ' '
guess_accuracy.push('h')
end
end
guess_accuracy.pop(h_counter)
puts "Guess accuracy: #{guess_accuracy.join}"
end
o que nos dá novamente a oportunidade de usar cláusulas de guarda:
def guess_checker
guess_accuracy = []
h_counter = 0
4.times do |i|
next unless @guess[i] == @code[i]
guess_accuracy.push('H')
h_counter += 1
end
compare_array = @code.clone
4.times do |i|
next unless compare_array.include?(@guess[i])
compare_array[compare_array.index(@guess[i])] = ' '
guess_accuracy.push('h')
end
guess_accuracy.pop(h_counter)
puts "Guess accuracy: #{guess_accuracy.join}"
end
expressões redundantes
Em computer_guesser
, if @turn == 1
, você inicializa @guess
, e depois inicializa novamente sem nunca usá-lo no meio. A primeira inicialização pode ser simplesmente removida, transformando isso:
if @turn == 1
@guess = Array.new(4) { rand(1..6) }
else
4.times do |i|
updated_comp_guess[i] = @guess[i] if @guess[i] == @code[i]
end
end
@guess = Array.new(4) { rand(1..6) }
nisso:
unless @turn == 1
4.times do |i|
updated_comp_guess[i] = @guess[i] if @guess[i] == @code[i]
end
end
@guess = Array.new(4) { rand(1..6) }
Duplicação de código
Array.new(4) { rand(1..6) }
Aparece várias vezes no seu código. Deve ser extraído em um método.
length
contrasize
Muitas coleções Ruby possuem ambos os métodos length
e , mas algumas possuem apenas um. size
Em geral, IFF uma coleção tem um size
método, então esse método é garantido como "eficiente" (geralmente tempo constante), enquanto length
pode ou não ser eficiente (tempo linear para iterar na coleção e contar todos os elementos), dependendo a coleção.
No seu caso, você está usando arrays e strings, para os quais ambos são de tempo constante, mas se quiser garantir a eficiência, é melhor usar explicitamente size
.
O elefante na sala
Uma coisa que não abordei até agora e que infelizmente não tenho tempo para abordar é o design fundamental do código. Tudo o que mencionei até agora são apenas cosméticos.
Todo o trabalho é feito no inicializador. Tudo o que um inicializador deve fazer é inicializar o objeto. Ele não deve solicitar entrada do usuário, não deve imprimir nada, não deve jogar.
Além disso, você está misturando I/O e lógica em todos os lugares. Um método deve imprimir algo ou fazer algo. Seu design torna impossível testar o código sem realmente jogar o jogo. Não consigo preparar um arquivo com códigos e suposições e alimentá-lo para um executor de teste; na verdade, tenho que jogar o jogo manualmente.
Também é estranho que você tenha apenas um "objeto", ou seja, o jogo, que está fazendo alguma coisa. Se você pensar em como o jogo é normalmente jogado, os objetos que estão fazendo algo ativamente não são os jogadores e não o jogo? Onde estão os jogadores em seu projeto?
Infelizmente, não tenho tempo para mergulhar nisso.
Aqui está onde o código está atualmente:
# frozen_string_literal: true
class Game
def initialize
puts '---------------------------------'
puts 'Welcome to Mastermind'
puts 'The goal is to either create a 4 digit code (Code maker role) containing numbers ranging from 1 through 6 or to guess a code (Codebreaker role) created by the computer within 12 turns to win.'
puts 'After each guess you will be given an accuracy score indicating how close you were to guessing the code correctly.'
puts 'The letter "H" indicates one of the numbers you guessed is in the correct position. The letter "h" indicates you guessed a correct number but it is NOT in the correct position'
puts '----------------------------------'
@game_over = false
@turn = 1
until @comp_guess_mode == 'Y' || @comp_guess_mode == 'N'
print 'Is the computer the code breaker? Y/N'
@comp_guess_mode = gets.chomp.upcase
end
game_mode
turn_sequence
end
def game_mode
return human_code_generator if @comp_guess_mode == 'Y'
code_generator
end
def code_generator
@code = Array.new(4) { rand(1..6) }
end
def human_code_generator
@code = ''
puts 'Please enter a 4 digit code'
@code = gets.chomp.each_char.map(&:to_i) until @code.size == 4
end
# computer_guesser method that tests if the computer's guess matches the human's
# by iterating through the array, if a direct match ('H') is found it will keep that number in the next guess
def computer_guesser
updated_comp_guess = [' ', ' ', ' ', ' ']
unless @turn == 1
4.times do |i|
updated_comp_guess[i] = @guess[i] if @guess[i] == @code[i]
end
end
@guess = Array.new(4) { rand(1..6) }
updated_comp_guess.each_with_index do |value, idx|
@guess[idx] = value if value != ' '
end
puts "Guess: #{@guess.join}"
end
def codebreaker_guess
@guess = []
until @guess.size == 4
puts 'Enter your 4 digit guess'
@guess = gets.chomp.each_char.map(&:to_i)
puts "Guess: #{@guess.join}"
print "Your guess was not 4 digits long, please guess again \n" if @guess.size != 4
end
end
def turn_display
puts '-------------------------'
puts "It's turn number: #{@turn}"
end
# Repeats the following guess/check sequence for 12 turns
# or until the code and guess are matched
def turn_sequence
while @turn < 13 && !@game_over
turn_display
if @comp_guess_mode == 'Y'
computer_guesser
else
codebreaker_guess
end
guess_checker
@turn += 1
victory_check
end
end
def guess_checker
guess_accuracy = []
h_counter = 0
4.times do |i|
next unless @guess[i] == @code[i]
guess_accuracy.push('H')
h_counter += 1
end
compare_array = @code.clone
4.times do |i|
next unless compare_array.include?(@guess[i])
compare_array[compare_array.index(@guess[i])] = ' '
guess_accuracy.push('h')
end
guess_accuracy.pop(h_counter)
puts "Guess accuracy: #{guess_accuracy.join}"
end
def victory_check
if @guess == @code
puts "Code was guessed correctly, it's #{@code}, codebreaker wins"
@game_over = true
elsif @turn == 13 && !@game_over
puts 'Code was not guessed correctly, code maker wins'
@game_over = true
end
end
end
Game.new
```