AI ile Ruby Mastermind oyun projesi
Kendi kendine öğreniyorum ve kodlamada oldukça yeniyim ve Ruby'de bir Mastermind oyunu yarattım. Herhangi bir genel geri bildirim veya tavsiye çok takdir edilecektir. Oyun şu anda tamamen işlevsel ve basit bir yapay zekaya sahip. Başlangıçta oyuncu seçmek istediği rolü seçer (kod kırıcı veya kod oluşturucu).
Şerefe
https://repl.it/repls/WeirdFrankButtons
düzenleme: sabit bağlantı
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
```
Yanıtlar
Kod 1
while i <4
if @guess[i] == @code[i]
@updated_comp_guess[i] = @guess[i]
i+=1
else
i +=1
end
end
Her iki if
ve else
sen artan edilir i
kısa yapılabilir 1. tarafından.
while i <4
if @guess[i] == @code[i]
@updated_comp_guess[i] = @guess[i]
end
i += 1
end
Aşağıdakine benzer guess_checker
vecompare_array
Tutarlılık
Bazen girinti için 1 boşluk kullanıyorsunuz, bazen 2 kullanıyorsunuz. Bazen operatörlerin etrafında boşluk kullanıyorsunuz, bazen kullanmıyorsunuz, bazen operatörün bir tarafında beyaz boşluk kullanıyorsunuz, ancak diğer tarafında kullanmıyorsunuz. Bazen virgülden sonra boşluk kullanırsınız, bazen kullanmazsınız. Bazen bir yöntemden sonra bir boş satır kullanırsınız, bazen iki, bazen hiçbiri.
Bir stil seçmeli ve ona bağlı kalmalısınız. Mevcut kodlardan bazılarını düzenliyorsanız, stilinizi mevcut kodla aynı olacak şekilde uyarlamalısınız. Bir takımın parçasıysanız, tarzınızı takımın geri kalanına uyacak şekilde uyarlamalısınız.
Çoğu topluluk, standartlaştırılmış topluluk stili kılavuzları geliştirmiştir. Ruby'de bu tür birden çok stil kılavuzu vardır. Hepsi temel konularda hemfikirdir (örn. Girinti 2 boşluktur), ancak daha spesifik noktalarda (tek tırnak veya çift tırnak) anlaşamayabilirler.
Girinti
Ruby'deki standart girinti stili iki boşluktur. Çoğunlukla 2 boşluk kullanıyorsunuz, ancak 1 alanı kullandığınız bir yer var. İkiyle devam edin.
Operatörlerin etrafındaki boşluk
Bir işlecin her iki tarafında 1 boşluk olmalıdır. Bazen 1 boşluk, bazen hiç boşluk ve bazen sadece bir tarafta boşluk kullanırsınız.
Örneğin burada, iki farklı aralık stiliyle üç satırda aynı ifadeye sahipsiniz:
i+=1
else
i +=1
Her biri ile tutarsızlar ve ikisi de topluluk kurallarına uymuyor. İkisi de şöyle olmalıdır:
i += 1
Virgülden sonra boşluk
Virgülden sonra 1 boşluk bırakılmalıdır. Bazen 1 boşluk kullanırsınız, bazen boşluk kullanmazsınız.
Örneğin burada:
@updated_comp_guess = [" "," "," "," "]
olmalı
@updated_comp_guess = [" ", " ", " ", " "]
Bloklarda boşluk
Bir blok değişmezinde, açılış küme ayracından sonra ve kapanış küme ayracından önce bir boşluk olmalıdır:
@code = Array.new(4) { rand(1..6) }
Tek tırnaklı dizeler
Dize enterpolasyonu kullanmıyorsanız, dizeleriniz için tek tırnak işaretleri kullanmanız yararlıdır. Bu şekilde, hiçbir dizge enterpolasyonunun gerçekleşmediği hemen anlaşılır.
Özellikle bu, burada yapmanız gereken kaçışları da ortadan kaldıracaktır:
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'
Dondurulmuş dize değişmezleri
Açıklık veya performans için değişkenlik ve yan etkiler gerekmedikçe değişmez veri yapıları ve tamamen işlevsel kod her zaman tercih edilir. Ruby'de dizeler her zaman değiştirilebilir, ancak dosyalarınıza ekleyebileceğiniz sihirli bir yorum vardır (Ruby motoru için bir komut satırı seçeneği olarak da mevcuttur), bu da tüm değişmez dizeleri otomatik olarak değiştirilemez hale getirir:
# frozen_string_literal: true
Genellikle bu yorumun tüm dosyalarınıza eklenmesi tercih edilir.
Koşullu değiştiriciler
Yalnızca bir ifadeyi çalıştıran bir koşulunuz olduğunda, bunun yerine değiştirici formunu kullanmalısınız, örneğin şu:
if value != " "
@guess[idx] = value
end
olmalı
@guess[idx] = value if value != " "
Burada aynı:
until @code.length == 4
@code = gets.chomp.each_char.map(&:to_i)
end
olmalı
@code = gets.chomp.each_char.map(&:to_i) until @code.length == 4
Gereksiz parantezler
compare_array[(compare_array.index(@guess[i]))]= " "
Etrafındaki parantezler compare_array.index(@guess[i])
gereksizdir.
Linting
Kodunuzda bir çeşit linter veya statik analizci çalıştırmalısınız. Rubocop popülerdir, ancak başkaları da vardır.
Rubocop, belirttiğim tüm stil ihlallerini tespit edebildi ve ayrıca hepsini otomatik olarak düzeltebildi.
Şunu tekrar edeyim: Bir düğmeye basarak milisaniyeler içinde gerçekten düzeltebileceğiniz tonlarca şeyi nasıl düzelteceğinizi gösteren iki sayfa harcadım . Editörümü, "kaydet" düğmesine basar basmaz otomatik düzeltmeli Rubocop'u çalıştıracak şekilde ayarladım.
Özellikle, Rubocop'u kodunuz üzerinde çalıştırarak 98 suçu tespit eder ve bunlardan 76'sını otomatik olarak düzeltir. Bu size 11'i çok basit 22 suçla kalır.
Otomatik düzeltmenin sonucu şu şekildedir:
# 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
Ve işte Rubocop'un otomatik olarak düzeltemediği suçlar:
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
Önce basit olanlara bakalım.
Durum eşitliği operatörü
Birkaç yerde durum eşitliği operatörünü kullanıyorsunuz. Dizeler için durum eşitliğinin tanımlanma şekli nedeniyle, kodunuz tamamen "kazara" çalışır. Bunun yerine normal eşitlik operatörünü kullanmalısınız.
Bu:
until @comp_guess_mode === "Y" || @comp_guess_mode === "N"
şu olmalı:
until @comp_guess_mode == "Y" || @comp_guess_mode == "N"
Aynı kontrol için burada doğru eşitlik operatörünü kullandığınızı unutmayın:
if @comp_guess_mode == "Y"
Tüm dallarda aynı ifadeler
Koşullu bir ifadenin her iki dalında da aynı ifadeye sahip olduğunuz üç yer vardır. Bu gereksiz bir dağınıklıktır, ifadeyi koşullu ifadeden çıkarabilirsiniz:
if @guess[i] == @code[i]
@updated_comp_guess[i] = @guess[i]
i+=1
else
i +=1
end
olmalı
if @guess[i] == @code[i]
@updated_comp_guess[i] = @guess[i]
end
i +=1
Ve yukarıda da söylediğimiz gibi, yalnızca bir ifadeye sahip bir koşul, değiştirici formunu kullanmalıdır (bu dönüşümün, eğer tekrar çalıştırırsanız, Rubocop tarafından tekrar otomatik olarak gerçekleştirileceğini unutmayın):
@updated_comp_guess[i] = @guess[i] if @guess[i] == @code[i]
i +=1
Kullanılmayan yerel değişken
game = Game.new
game
hiçbir yerde kullanılmaz. Çıkarın:
Game.new
Koruma hükümleri
Tüm bir yöntemin veya bloğun bir koşullu olarak sarıldığı bir durumunuz varsa, bunu bir "koruma cümlesiyle" değiştirebilir ve iç içe geçme düzeyini azaltabilirsiniz.
Örneğin bu:
def something
if foo
bar
baz
quux
else
42
end
end
şuna dönüşebilir:
def something
return 42 unless foo
bar
baz
quux
end
Bunu kodunuzda yapmak için birkaç fırsat vardır ve Rubocop tavsiyesine uyarak birkaç tane daha oluşturulur.
İşte gelişmenin çok büyük olmadığı bir örnek:
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
ancak burada kazanç biraz daha büyük:
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
Gereksiz kontroller
Ama aslında, her şey daha da basit: i
4 kez döngü yapıp artırdığınızdan , bu her zaman olacaktır 4
, böylece koşul her zaman doğru olacaktır ve onu tamamen kaldırabilirsiniz.
Boolelerle eşitlik
@game_over == false
@game_over
zaten bir boole, için eşitliği kontrol etmeye gerek yok false
. Bu yalnızca
!@game_over
Gereksiz örnek değişkenleri
Örnek değişkenler @updated_comp_guess
, @i
, @h_counter
ve @guess_accuracy
sadece hiç bir yöntemde kullanılır. Bunun yerine yerel değişkenler olmalıdır.
Döngüler
Ruby'de neredeyse hiç döngülere ihtiyacınız yoktur. Aslında, o kadar ileri gidip Ruby'de bir döngü kullanıyorsanız, yanlış yapıyorsunuz demektir.
İşte bir örnek:
i = 0
while i < 4
updated_comp_guess[i] = @guess[i] if @guess[i] == @code[i]
i += 1
end
çok daha iyi yazılırdı
4.times do |i|
updated_comp_guess[i] = @guess[i] if @guess[i] == @code[i]
end
Bu, guess_checker
yöntemi şöyle gösterecektir :
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
bu da bize yine koruma hükümlerini kullanma fırsatı verir:
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
Gereksiz ifadeler
İçinde computer_guesser
, eğer @turn == 1
, başlatırsanız @guess
ve daha sonra aralarında hiç kullanmadan yeniden başlatırsınız. İlk başlatma şu şekilde döndürülerek kaldırılabilir:
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) }
bunun içine:
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) }
Kod kopyalama
Array.new(4) { rand(1..6) }
Kodunuzda birden çok kez görünür. Bir yönteme çıkarılmalıdır.
length
vs. size
Birçok Yakut koleksiyonları her ikisine de sahip length
ve size
yöntemleri, ancak bazıları sadece bir tane var. Genel olarak, IFF bir koleksiyonun bir size
yöntemi vardır, daha sonra bu yöntemin "verimli" olması garanti edilir (genellikle sabit süre), ancak length
verimli olabilir veya olmayabilir (toplama boyunca yineleme ve tüm öğeleri sayma için doğrusal zaman) koleksiyon.
Sizin durumunuzda, her ikisinin de sabit zamanlı olduğu dizileri ve dizeleri kullanıyorsunuz, ancak verimliliği garanti etmek istiyorsanız, size
bunun yerine açıkça kullanmak daha iyidir .
Odadaki Fil
Şimdiye kadar değinmediğim ve maalesef ele almak için zamanım olmadığı bir şey, kodun temel tasarımıdır. Şimdiye kadar bahsettiğim her şey sadece kozmetik.
Tüm çalışma başlatıcıda yapılır. Bir başlatıcının yapması gereken tek şey nesneyi başlatmaktır. Kullanıcı girişi istememeli, hiçbir şey yazdırmamalı, oyun oynamamalı.
Ayrıca, G / Ç ve mantığı her yerde karıştırıyorsunuz. Bir yöntem ya bir şeyi yazdırmalı ya da bir şeyler yapmalıdır. Tasarımınız, oyunu gerçekten oynamadan kodu test etmeyi imkansız kılıyor. Kodlar ve tahminler içeren bir dosya hazırlayıp onu bir test koşucusuna besleyemiyorum, aslında oyunu manuel olarak oynamam gerekiyor.
Tek bir "nesneye" sahip olmanız, yani bir şeyler yapan oyun da garip. Oyunun tipik olarak nasıl oynandığını düşünürseniz, aktif olarak bir şeyler yapan nesneler oyun değil oyuncular değil mi? Tasarımınızdaki oyuncular nerede?
Ne yazık ki, buna dalacak vaktim yok.
Kod şu anda burada:
# 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
```