Easy Peasy and Medium Rare

written in rails, ruby, technical

Creating the easy and medium difficulty methods for the ruby tic tac toe game.

Last post I left off with a simple tic tac toe game that had no difficulty settings. For difficulty to be fully integrated into the current game we need to do the simplest thing first, ask the user what difficulty they would like to play at. I chose to place the “choose_difficulty” method at the beginning of the “random_start” method. I chose this for future revisions when I create an extreme difficulty that may take time to finish and may only be used in a 3x3 board.

[game.rb]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  attr_accessor :board, :slots, :difficulty, :boards

  def random_start
    # there should only be a difficulty setting when playing against computer
    choose_difficulty

    ...
  end

  def choose_difficulty
    puts "What difficulty would you like to play at? < beginner / medium >"
    @difficulty = gets.chomp.downcase
    # if difficulty is none of the options then recursion
    # if @difficulty != "beginner" && @difficulty != "medium" && @difficulty != "hard" && @difficulty != "extreme"
    if @difficulty != "beginner" && @difficulty != "medium"
      puts "Invalid input"
      choose_difficulty
    end

  end

As you can see I’ve asked the user which difficulty level they’re like to play at and set that to an instance variable called difficulty. As a side-note this means we must now add difficulty to the attr_accessor list of variables. Additionally, we need to check if @difficulty is valid, if not then through recursion call the “choose_difficulty” method again.

Since difficulty only needs to used for a game against a computer the difficulty methods only need to be used in the computer class. Under the “play_move” method is the best place to call the difficulty methods. You could use any type of test on the difficulty variable to call a specific difficulty method.

[computer.rb]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  #from player
  def play_move(mark)
    #take the symbol "x" or "o"
    puts "The computer is moving..."
    sleep 2

    if @game.difficulty == "beginner"
      move = beginner_move
    # else
    elsif @game.difficulty == "medium"
      move = medium_move
    elsif @game.difficulty == "hard"
      move = hard_move
    else
      move = extreme_move
    end

    #the index of the game board gets that mark
    @game.board[move-1] = mark
  end

The computer’s move in a beginner game should choose a space randomly. We can copy a line from our previous code and use it in our beginner_move method.

[computer.rb]
1
2
3
  def beginner_move
    @game.board.select { |i| i.is_a?(Integer) }.sample - 1
  end

For the “medium_move” method and any following methods we need to take the size of the board into consideration. If the amount of slots%2 == 0 then the number is even, which means there is no finite center on the board. If there is a center slot then we want to take that and the corners first, from there we can take a random spot.

If there is no specific center then we want to take two diagonal slots from the center four slots. From there we can take the corners and then a random slot.

[computer.rb]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
  def medium_move
    # if @slots%2 = 0 then the number is even so there is no center
    if @game.slots%2 == 0
      # check first center.first
      if @game.boards[(@game.slots/2)-1][(@game.slots/2)-1].is_a?(Integer)
      # check second center.second
      elsif @game.boards[@game.slots/2][@game.slots/2].is_a?(Integer)
      # check first center.second
      elsif @game.boards[(@game.slots/2)-1][@game.slots/2].is_a?(Integer)
      # check second center.first
      elsif @game.boards[@game.slots/2][(@game.slots/2)-1].is_a?(Integer)
      # check top left
      elsif @game.boards.first.first.is_a?(Integer)
        @game.boards.first.first
      # check bottom right
      elsif @game.boards.last.last.is_a?(Integer)
        @game.boards.last.last
      # check top right
      elsif @game.boards.first.last.is_a?(Integer)
        @game.boards.first.last
      # check bottom left
      elsif @game.boards.last.first.is_a?(Integer)
        @game.boards.last.first
      else
        @game.board.select { |i| i.is_a?(Integer) }.sample - 1
      end
    else
    # if @slots%2 != 0 then the number is odd so there is a center
      # check center
      if @game.boards[@game.slots/2][@game.slots/2].is_a?(Integer)
        @game.boards[@game.slots/2][@game.slots/2]
      # check top left
      elsif @game.boards.first.first.is_a?(Integer)
        @game.boards.first.first
      # check bottom right
      elsif @game.boards.last.last.is_a?(Integer)
        @game.boards.last.last
      # check top right
      elsif @game.boards.first.last.is_a?(Integer)
        @game.boards.first.last
      # check bottom left
      elsif @game.boards.last.first.is_a?(Integer)
        @game.boards.last.first
      else
        @game.board.select { |i| i.is_a?(Integer) }.sample - 1
      end
    end
  end

I wanted to put the more complicated logic that actively fights against the human user in the “hard_move” method and unbeatable logic in the “extreme_move” method.

[computer.rb]
1
2
3
4
5
6
7
8
9
10
  def hard_move
    # use center, then block user, then corners, then block user, then try to win a line
    # this should be kind of similar to my original-original code
    # should incoporate for all board sizes
    # if @slots%2 = 0 then the number is even so there is no center
  end

  def extreme_move
    # use unbeatable code
  end

Look for another update on finished logic for the hard and extreme difficulties.

Keep on being badass programmers!


Comments