HiveBrain v1.2.0
Get Started
← Back to all entries
patternrubyMinor

Ruby command line Mastermind game with AI

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
linewithmastermindgamerubycommand

Problem

I've created a small command-line 'Mastermind' game using Ruby. The intent of the project (aside from having fun in the build!) was to learn and emphasize OOP concepts and principles.

The game has some simple AI in place. If the computer is the player making guesses, when they get the correct color in the correct index position, that guess is locked in for subsequent turns. When they get the correct color but in an incorrect index position, that color is utilized in the next turn but placed in a different (and unoccupied) index position.

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

Solution

This is only a style issue, but it's first time I see Ruby code with empty lines at end of class body, and it seems confusing, as you are not separating other ends that way:

end

  end


You have pairs of methods that are virtually identical, like #guess_loop_human and #guess_loop_comp. Repeating yourself is bad, this would be solved better if you had two classes HumanPlayer and CPUPlayer, moved player-specific logic there and in Game class referred to your players as 'player_a' and 'player_b', or 'setter' and 'guesser'.

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


Your @guesses_hash is a Hash indexed by Arrays. This is pretty bad, as two identical Arrays will count as the same key - that will mess your logic if player makes the same guess twice.

Your Board is subclass of Game, you used that to share class variables between them. This doesn't make sense though, as a board obviously isn't a game. What makes even less sense is that instantiating this class draws a board.

Both of above problems could be solved by introducing actual Board class that keeps track of guesses, makes this data available to Game, and can present it.

Structure of your methods is somewhat off sometimes. Lets look here:

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


Just because this method happens to be called in single place, it doesn't mean it should call methods that are supposed to be called after that - instead, such function should return a value, and it's caller should determine what to do with it. This makes this function reusable, just in case you would want to ask more yes\no question (like "Play again?(yes\no)", but also more readable - reader of your code wouldn't expect that input_validation means input_validation_and_than_play_the_game. For example you could do:

if get_yes_or_no # old name was confusing IMO
  comp_guesses
else
  play_game
end


#get_yes_or_no above is assumed repeat #gets in a loop until it gets valid answer, and I'd imagine it should also be the one calling chomp.upcase.

Code Snippets

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
def input_validation(response)
  if response == "yes"
    comp_guesses
  elsif response == "no"
    play_game
  else
    puts "Response not valid"
    who_creates_code
  end
end
if get_yes_or_no # old name was confusing IMO
  comp_guesses
else
  play_game
end

Context

StackExchange Code Review Q#105946, answer score: 3

Revisions (0)

No revisions yet.