AIを使ったRubyMastermindゲームプロジェクト

Aug 19 2020

私は自己学習していて、コーディングにかなり慣れておらず、RubyでMastermindゲームを作成しました。一般的なフィードバックやアドバイスは何でも大歓迎です。ゲームは現在完全に機能しており、シンプルなAIを備えています。最初に、プレイヤーは選択したい役割(コードブレーカーまたはコードメーカー)を選択します。

乾杯

https://repl.it/repls/WeirdFrankButtons

編集:固定リンク

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

```

回答

2 user985366 Aug 19 2020 at 18:44

コード1

while i <4
    if @guess[i] == @code[i]
      @updated_comp_guess[i] = @guess[i]
      i+=1
    else
      i +=1
    end
  end

ifelseあなたの両方でi1ずつ増加しています。これは短くすることができます。

while i <4
    if @guess[i] == @code[i]
      @updated_comp_guess[i] = @guess[i]
    end
    i += 1
  end

で同様のguess_checkerさらに下、および中compare_array

1 JörgWMittag Sep 04 2020 at 03:58

一貫性

インデントに1つのスペースを使用している場合もあれば、2を使用している場合もあります。演算子の周囲に空白を使用している場合もあれば、使用していない場合もあります。演算子の片側に空白を使用している場合もありますが、反対側には使用していません。カンマの後にスペースを使用する場合もあれば、使用しない場合もあります。メソッドの後に1行、2行、または何も使用しない場合があります。

あなたは一つのスタイルを選び、それに固執するべきです。既存のコードを編集する場合は、既存のコードと同じになるようにスタイルを調整する必要があります。チームの一員である場合は、チームの他のメンバーと一致するようにスタイルを調整する必要があります。

ほとんどのコミュニティは、標準化されたコミュニティスタイルガイドを開発しました。Rubyには、そのようなスタイルガイドが複数あります。それらはすべて基本に同意します(たとえば、インデントは2スペースです)が、より具体的な点(一重引用符または二重引用符)については同意しない場合があります。

インデント

Rubyの標準のインデントスタイルは2つのスペースです。主に2つのスペースを使用しますが、1つのスペースを使用する場所が1つあります。2つに固執します。

演算子の周りの空白

演算子の両側に1つのスペースが必要です。1つのスペースを使用することもあれば、スペースを使用しないこともあり、片側だけのスペースを使用することもあります。

たとえば、ここでは、2つの異なる間隔スタイルを持つ3行内にまったく同じ式があります。

  i+=1
else
  i +=1

それらはそれぞれに一貫性がなく、どちらもコミュニティのガイドラインに準拠していません。それらは両方ともあるべきです:

  i += 1

カンマの後のスペース

カンマの後にスペースを1つ入れる必要があります。1つのスペースを使用することもあれば、スペースを使用しないこともあります。

例:ここ:

@updated_comp_guess = [" "," "," "," "]

する必要があります

@updated_comp_guess = [" ", " ", " ", " "]

ブロック内のスペース

ブロックリテラルでは、開始中括弧の後にスペースがあり、終了中括弧の前にスペースが必要です。

@code = Array.new(4) { rand(1..6) }

一重引用符で囲まれた文字列

文字列補間を使用しない場合は、文字列に一重引用符を使用すると便利です。そうすれば、文字列補間が行われていないことがすぐにわかります。

特に、これにより、ここで行う必要のあるエスケープも削除されます。

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'

凍結された文字列リテラル

明確さやパフォーマンスのために可変性と副作用が必要な場合を除いて、不変のデータ構造と純粋に機能的なコードが常に優先されます。Rubyでは、文字列は常に変更可能ですが、ファイルに追加できる魔法のコメントがあり(Rubyエンジンのコマンドラインオプションとしても利用可能)、すべてのリテラル文字列が自動的に不変になります。

# frozen_string_literal: true

通常、このコメントをすべてのファイルに追加することをお勧めします。

条件付き修飾子

1つの式のみを実行する条件がある場合は、代わりに修飾子形式を使用する必要があります。例:次のようになります。

if value != " "
  @guess[idx] = value
end

する必要があります

@guess[idx] = value if value != " "

こっちも一緒:

until @code.length == 4
  @code = gets.chomp.each_char.map(&:to_i)
end

する必要があります

@code = gets.chomp.each_char.map(&:to_i) until @code.length == 4

不要な括弧

compare_array[(compare_array.index(@guess[i]))]= " "

かっこcompare_array.index(@guess[i])は不要です。

リンティング

コードに対して、ある種のリンターまたは静的アナライザーを実行する必要があります。Rubocopは人気がありますが、他にもあります。

Rubocopは、私が指摘したすべてのスタイル違反を検出することができ、それらすべてを自動修正することもできました。

繰り返しになりますが、ボタンを押すだけで数ミリ秒以内に実際に修正できる大量の修正方法を指摘するために2ページを費やしました。「保存」を押すとすぐに自動修正でRubocopが自動的に実行されるようにエディターを設定しました。

特に、コードでRubocopを実行すると、98の違反が検出され、そのうち76が自動的に修正されます。これにより、22の違反が残り、そのうち11は非常に単純です。

自動修正の結果は次のようになります。

# 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

そして、Rubocopが自動的に修正できなかった違反は次のとおりです。

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

最初に単純なものを見てみましょう。

大文字小文字の等式演算子

大文字と小文字を区別する演算子は、いくつかの場所で使用します。文字列に対して大文字と小文字を区別する方法が定義されているため、コードは純粋に「偶然」に機能します。代わりに、正規等式演算子を使用する必要があります。

この:

until @comp_guess_mode === "Y" || @comp_guess_mode === "N"

これである必要があります:

until @comp_guess_mode == "Y" || @comp_guess_mode == "N"

ここでは、まったく同じチェックに正しい等式演算子を使用していることに注意してください。

if @comp_guess_mode == "Y"

すべてのブランチで同一の式

条件式の両方のブランチで同じ式を使用する場所は3つあります。これは不要な混乱です。条件から式を引き出すことができます。

if @guess[i] == @code[i]
  @updated_comp_guess[i] = @guess[i]
  i+=1
else
  i +=1
end

する必要があります

if @guess[i] == @code[i]
  @updated_comp_guess[i] = @guess[i]
end

i +=1

そして、上で述べたように、式が1つしかない条件文では、修飾子形式を使用する必要があります(この変換を再度実行すると、Rubocopによって自動的に実行されることに注意してください)。

@updated_comp_guess[i] = @guess[i] if @guess[i] == @code[i]

i +=1

未使用のローカル変数

game = Game.new

gameどこでも使用されることはありません。削除するだけです:

Game.new

ガード条項

メソッドまたはブロック全体が条件付きでラップされている場合は、それを「ガード句」に置き換えて、ネストのレベルを下げることができます。

例:

def something
  if foo
    bar
    baz
    quux
  else
    42
  end
end

これになることができます:

def something
  return 42 unless foo

  bar
  baz
  quux
end

コードでこれを行う機会はいくつかあり、Rubocopのアドバイスに従うことでさらにいくつかの機会が作成されます。

改善がそれほど大きくない1つの例を次に示します。

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

しかし、ここではゲインがやや大きくなっています。

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

冗長チェック

しかし実際には、全体がさらに単純です。ループしてi4回インクリメントするため、常にとなり4、条件は常に真になり、完全に削除できます。

ブール値との同等性

@game_over == false

@game_overはすでにブール値であるため、と等しいかどうかを確認する必要はありませんfalse。これだけ

!@game_over

不要なインスタンス変数

インスタンス変数は@updated_comp_guess@i@h_counter、と@guess_accuracyしか1つの方法で使用されています。代わりにローカル変数にする必要があります。

ループ

Rubyでは、ループはほとんど必要ありません。実際、私はこれまでのところ、Rubyでループを使用している場合、それは間違っていると言います。

次に例を示します。

i = 0
while i < 4
  updated_comp_guess[i] = @guess[i] if @guess[i] == @code[i]

  i += 1
end

次のように書く方がはるかに良いでしょう

4.times do |i|
  updated_comp_guess[i] = @guess[i] if @guess[i] == @code[i]
end

これにより、guess_checkerメソッドは次のようになります。

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

これにより、ガード句を使用する機会が再び与えられます。

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

冗長な表現

computer_guesser、の場合@turn == 1、を初期化@guessしてから、間に使用せずに再度初期化します。最初の初期化は削除するだけで、次のようになります。

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) }

これに:

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) }

コードの重複

Array.new(4) { rand(1..6) }

コードに複数回表示されます。メソッドに抽出する必要があります。

length 対。 size

多くのRubyコレクションにはlengthsizeメソッドの両方がありますが、1つしかないものもあります。一般に、IFFコレクションにはsizeメソッドがあり、そのメソッドは「効率的」(通常は一定の時間)であるlengthことが保証されますが、効率的である場合とそうでない場合があります(コレクションを反復処理してすべての要素をカウントする線形時間)。コレクション。

あなたの場合、両方とも一定時間である配列と文字列を使用していますが、効率を保証したい場合は、size代わりに明示的に使用することをお勧めします。

部屋の中の象

これまで取り上げていないこと、そして残念ながら時間を割く必要がないことの1つは、コードの基本的な設計です。これまでお話ししたのは化粧品だけです。

すべての作業は初期化子で行われます。イニシャライザが行う必要があるのは、オブジェクトを初期化することだけです。ユーザー入力を要求したり、何も印刷したり、ゲームをプレイしたりするべきではありません。

また、どこでもI / Oとロジックを混合しています。メソッドは、何かを印刷するか、何かを実行する必要があります。あなたのデザインは、実際にゲームをプレイせずにコードをテストすることを不可能にします。コードと推測を含むファイルを準備してテストランナーにフィードすることはできません。実際には手動でゲームをプレイする必要があります。

また、何かをしているゲームという「オブジェクト」が1つしかないのも不思議です。ゲームが通常どのようにプレイされるかを考えると、ゲームではなくプレイヤーに積極的に何かをしているオブジェクトではありませんか?あなたのデザインのプレーヤーはどこにいますか?

残念ながら、私にはこれに飛び込む時間がありません。

コードが現在立っている場所は次のとおりです。

# 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
```