首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Ruby命令行Mastermind与AI游戏

Ruby命令行Mastermind与AI游戏
EN

Code Review用户
提问于 2015-09-28 19:51:12
回答 1查看 1.1K关注 0票数 6

我用Ruby创建了一个小命令行'Mastermind‘游戏。项目的意图(除了在构建中获得乐趣!)就是学习和强调面向对象的概念和原则。

游戏有一些简单的人工智能到位。如果计算机是进行猜测的播放器,当它们在正确的索引位置得到正确的颜色时,该猜测将锁定为随后的回合。当他们得到正确的颜色,但在一个不正确的索引位置,该颜色将使用在下一轮,但放置在一个不同(和无人)的索引位置。

代码语言:javascript
复制
module Mastermind

  class Player
    attr_accessor :color_choices

    def initialize(color_choices)
      @color_choices = color_choices
    end

  end

  class Game
    attr_accessor :computer, :human, :color_choices

    def initialize
      welcome
    end

    def play_game
      @available = [0,1,2,3]
      @computer = Player.new(get_random_choice)
      human_instructions
      choose_message
      @human = Player.new(get_human_choice)
      compare_with_index
      @@guess_iterations = 1
      @@guesses_hash = Hash.new
      guess_loop_human
    end

    def comp_guesses
      @available = [0,1,2,3]
      choose_message
      @human = Player.new(get_human_choice)
      @computer = Player.new(get_random_choice)
      compare_with_index
      @@guess_iterations = 1
      @@guesses_hash = Hash.new
      guess_loop_comp
    end

    def welcome
      puts "Welcome to Mastermind."
      puts "========================================================"
      who_creates_code
    end

    def human_instructions
      puts "You will be given 12 chances to guess the code that was chosen by the computer."
      puts "========================================================"
      puts "There are 6 colors from which to choose (Red, Blue, Green, Yellow, Orange, Purple)"
      puts "========================================================"
    end

    def who_creates_code
      puts "Would you like to choose the code and have the computer guess? (yes/no)"
      chooser = gets.chomp.downcase 
      input_validation(chooser)
    end

    def input_validation(response)
      if response == "yes"
        comp_guesses
      elsif response == "no"
        play_game
      else
        puts "Response not valid"
        who_creates_code
      end
    end

    def choose_message
      puts "Please choose four colors, separated by a comma (ex: B,O,Y,G)"  
    end

    def get_random_choice
      puts "The computer is now choosing..."
      colors = ["R", "B", "G", "Y", "O", "P"]
      choice = colors.sample(4)
    end

    def get_human_choice
      answer = gets.chomp.upcase
      human_colors = answer.split(",")
    end

    def compare_with_index
      @count_index = 0
      @color_count = 0
      computer.color_choices.each_with_index do |n, index|
        if human.color_choices[index] == n
          @count_index += 1
          @available -= [index]
        elsif color_match(n) && color_available(n)
          @color_count += 1
        end
      end
      @count_index
    end

    def color_match(color)
      human.color_choices.include?(color)
    end

    def color_available(color)
      @available.include?(human.color_choices.index(color))
    end

    def matches_message
      "You have #{@count_index} color(s) in the right spot and #{@color_count} correctly chosen color(s)"
    end

    def guess_loop_comp
      while @@guess_iterations <= 12 && !victory
        store_guess_comp
        board
        puts matches_message
        computer.color_choices = new_choice
        compare_with_index
        @@guess_iterations += 1
      end
      game_over
    end

    def guess_loop_human
      while @@guess_iterations <= 12 && !victory
        store_guess_human
        board
        puts matches_message
        guess_again_message
        human.color_choices = get_human_choice
        compare_with_index
        @@guess_iterations += 1
      end
      game_over
    end

    def store_guess_human
      @@guesses_hash[human.color_choices] = matches_message
    end

    def store_guess_comp
      @@guesses_hash[computer.color_choices] = matches_message
    end

    def board
      Board.new
    end

    def guess_again_message
      puts "Guess again, please choose four colors, separated by a comma (ex: B,O,Y,G)"
    end

    def available
      @available.shuffle!.pop
    end

    def new_choice
      colors = ["R", "B", "G", "Y", "O", "P"]
      new_color = []
      computer.color_choices.each_with_index do |n, index|
        if human.color_choices[index] != n && !color_available(n)
          new_color[index] = colors.sample
        else
          new_color[index] = n
        end
      end
      new_color
      keep_color(new_color)
    end

    def keep_color(new_array)
      computer.color_choices.each do |i|
        if color_available(i) 
          new_array[available.to_i] = i
        end
      end
      p new_array
      new_array
    end

    def victory
      if compare_with_index == 4
        store_guess_comp
        board
        puts "Victory!"
        true
      end
    end

    def game_over
      if @@guess_iterations > 12 && !victory
        puts "Game Over"
      end
    end

  end

  class Board < Game

    def initialize
      render_board
    end

    def render_board
      (13- @@guess_iterations.to_i).times do 
        puts "| X | X | X | X |"
      end
      display_hash
    end 

    def display_hash
      @@guesses_hash.each do |k,v|
        puts "================="
        puts "| " + k.join(" | ") + " | " + v
        puts "================="
      end
    end

  end

end

Mastermind::Game.new
EN

回答 1

Code Review用户

回答已采纳

发布于 2015-10-01 01:33:35

这只是一个样式问题,但这是我第一次看到Ruby代码在类主体的末尾有空行,这似乎令人困惑,因为您没有以这种方式分隔其他端点:

代码语言:javascript
复制
    end

  end

您有几对几乎相同的方法,如#guess_loop_human#guess_loop_comp。重复自己是不好的,如果你有两个类HumanPlayerCPUPlayer,移动特定于玩家的逻辑,并且在游戏类中将玩家称为'player_a‘和'player_b',或者'setter’和'guesser‘,这个问题会得到更好的解决。

代码语言:javascript
复制
module Player # could be a class
  # common logic if any
end

class HumanPlayer # and CPUPlayer elsewhere
  include Player

  def get_choice
    # possibly print messages here
  end
end

def guess_loop # not guess_loop_human and guess_loop_comp
  while @@guess_iterations <= 12 && !victory
    store_guess
    board
    puts matches_message
    guesser.get_choice # this works differently for HumanPlayer and CPUPlayer
    compare_with_index
    @@guess_iterations += 1
  end
  game_over
end

您的@guesses_hash是一个由数组索引的散列。这是非常糟糕的,因为两个相同的数组将被计算为相同的键--如果玩家进行两次相同的猜测,这将扰乱您的逻辑。

您的董事会是游戏的子类,您使用它在它们之间共享类变量。不过,这是没有道理的,因为董事会显然不是一种游戏。更没有意义的是实例化这个类可以画一个板。

上述两个问题都可以通过引入实际的Board类来解决,该类可以跟踪猜测,使这些数据可供游戏使用,并且可以呈现出来。

您的方法的结构有时有些偏离。让我们看看这里:

代码语言:javascript
复制
def input_validation(response)
  if response == "yes"
    comp_guesses
  elsif response == "no"
    play_game
  else
    puts "Response not valid"
    who_creates_code
  end
end

仅仅因为这个方法碰巧是在一个地方调用的,这并不意味着它应该调用在该方法之后应该被调用的方法-相反,这个函数应该返回一个值,它的调用者应该决定如何处理它。这使得这个函数可以重用,以防您想问更多的是\没有问题(比如"Play again?(yes\no)",但是代码的可读性更强)--您的代码的读者不会期望input_validation意味着input_validation_and_than_play_the_game。例如,您可以:

代码语言:javascript
复制
if get_yes_or_no # old name was confusing IMO
  comp_guesses
else
  play_game
end

假设上面的#get_yes_or_no在循环中重复#gets,直到得到有效的答案为止,我想它也应该是调用chomp.upcase的那个。

票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/105946

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档