Login Register






Thread Rating:
  • 0 Vote(s) - 0 Average


Ruby - Basic Tutorial filter_list
Author
Message
Ruby - Basic Tutorial #1
Today you're going to make a Tic Tac Toe game by fellowing the steps that i'm going to be sharing with you.

Lets start by defining our initialize method, the first thing we want to do is create a container to hold the 9 places on the tic tac toe board. It is a 3 by 3 grid so we will name them A,B,C across and 1,2,3 down. Lets store them in a Ruby hash object with their default values set to spaces indicating they are empty:

Code:
@places = {
  "a1"=>" ","a2"=>" ","a3"=>" ",
  "b1"=>" ","b2"=>" ","b3"=>" ",
  "c1"=>" ","c2"=>" ","c3"=>" "
}

Now that we have defined the 9 slots, lets define the 8 different possible winning columns. These are the slot (the 3 horizontal, 3 vertical and 2 diagnal) combinations a player must occupy to win the game. We will store these in an array called @columns where each index is a nested array defining the positions needed to win.

Code:
@columns = [
  ['a1','a2','a3'],
  ['b1','b2','b3'],
  ['c1','c2','c3'],

  ['a1','b1','c1'],
  ['a2','b2','c2'],
  ['a3','b3','c3'],

  ['a1','b2','c3'],
  ['c1','b2','a3']
]

Next we want to randomly determine who is X and who is O and assign each to a variable called @cpu and @user:

Code:
@cpu = rand() > 0.5 ? 'X' : 'O'
@user = @cpu == 'X' ? 'O' : 'X'

Now lets give each player a name; lets name cpu as "Ruby" and ask the user to input their name and store each in the variables @cpu_name and @user_name respectively:

Code:
@cpu_name = "Ruby"
put_line
puts " RUBY TIC TAC TOE"
puts " What is your name?"
STDOUT.flush
@user_name = gets.chomp
put_bar

You will notice I called two methods here that are not yet defined; put_line and put_bar these are just convenience methods to print a line or a bar. We will define those methods shortly. For the last part of the initialize method we will let whoever is X make their first move:

Code:
if(@user == 'X')
  user_turn
else
  cpu_turn
end

That concludes the initialize method, the user_turn and cpu_turn methods are what we will call to allow either the user or the cpu make a move. We will define those later; for now, lets define the put_line and put_bar methods:

Code:
def put_line
  puts "-----------------------------------------------------------------------------"
end

def put_bar
  puts "#############################################################################"
  puts "#############################################################################"
end

Now we have enough information to define the method that draws our tic tac toe board. Lets also print out the user name and who is X and who is O to make it easier for the player:

Code:
def draw_game
  puts ""
  puts "#{@cpu_name}: #{@cpu}"
  puts "#{@user_name}: #{@user}"
  puts ""
  puts "   a b c"
  puts ""
  puts " 1 #{@places["a1"]}|#{@places["b1"]}|#{@places["c1"]}"
  puts "   -----"
  puts " 2 #{@places["a2"]}|#{@places["b2"]}|#{@places["c2"]}"
  puts "   -----"
  puts " 3 #{@places["a3"]}|#{@places["b3"]}|#{@places["c3"]}"
end

Remember, the @places hash defaults to having a space for each slot. When a player makes a move it will then contain it (X or O) instead, automatically drawing it to the board the next time we render it. Now lets define the cpu_turn method:

Code:
def cpu_turn
  move = cpu_find_move
  @places[move] = @cpu
  put_line
  puts "#{@cpu_name} marks #{move.upcase}"
  check_game(@user)
end

The first thing happening in this method is we call another function called cpu_find_move (we will define it later) which analyses the positions available to determine the best move. We then assign the returned value to the @places hash and output the move notifying the user what move was just placed. Finally we call a soon to be defined method called check_game which checks to see if anyone has won or if it is a stalemate. This method expects a parameter letting it know whose turn is next if the game is not over. Before we define the cpu_find_move method lets first define two functions that it relies on to analyse the board. The first function is called times_in_column which expects 2 parameters, the first being an array which is the column (of the columns in the @columns array) on the board we want to analyse and the second is what we are looking for (either X or O); and it will return how many times the item is in the column:

Code:
def times_in_column arr, item
  times = 0
  arr.each do |i|
    times += 1 if @places[i] == item
    unless @places[i] == item || @places[i] == " "
      #oppisite piece is in column so column cannot be used for win.
      #therefore, the strategic thing to do is choose a dif column so return 0.
      return 0
    end
  end
  times
end

You will notice we are checking if any of the slots in the column are either a space or the item we are looking for. If they are neither then the only thing it can possibly be is the other player (X or O). And since we cannot win on a column that is occupied by the other player we instantly return 0. The next function we will define is called empty_in_column this one only expects one parameter which is also a column reference and just returns the first empty slot it finds in the column:

Code:
def empty_in_column arr
  arr.each do |i|
    if @places[i] == " "
      return i
    end
  end
end

Now that we have both of those defined we can now define the cpu_find_move method. The first thing we will want to do is determine if there is any move possible that will cause a win and if so, return it. You can determine this by checking to see if any of the @columns that define a win already contain 2 of cpu's moves which indicates a 3rd move into that column will be a win:

Code:
@columns.each do |column|
  if times_in_column(column, @cpu) == 2
    return empty_in_column column
  end
end

If there is no moves available that will cause a win, the next thing to look for is if there are any moves available that will cause a loose. What I mean is, if there is a place the user can move to win. If there is, we need to move there first to block it:

Code:
@columns.each do |column|
  if times_in_column(column, @user) == 2
    return empty_in_column column
  end
end

If neither of these find anything then there is no possible winning moves either way. So what we want to do now is build up to a winning move. To do this we check to see if any column has at least one of cpu's moves so we can add to it:

Code:
@columns.each do |column|
  if times_in_column(column, @cpu) == 1
    return empty_in_column column
  end
end

If that didn't find anything either, then at this point we can just move to any empty slot. To make it seem more natural lets try to find a random empty slot, but if our first random slot is not empty, then at that point lets just move to the first empty slot we find:

Code:
#no strategic spot found so just find a random empty
k = @places.keys;
i = rand(k.length)
if @places[k[i]] == " "
  return k[i]
else
  #random selection is taken so just find the first empty slot
  @places.each { |k,v| return k if v == " " }
end

That is the final part of the cpu_find_move function. So we can move on to creating the user_turn method. What we want to do here is draw the board so the user can decide where to move and then ask them to type in the slot they would like to place a move. We need to check their input to make sure it is either a valid move or the word "exit." If it is a valid move, make the move, or if it is "exit" then exit the program otherwise notify them to try again:

Code:
def user_turn
  put_line
  puts " RUBY TIC TAC TOE"
  draw_game
  puts " #{@user_name}, please make a move or type 'exit' to quit"
  STDOUT.flush
  input = gets.chomp.downcase
  put_bar
  if input.length == 2
    a = input.split("")
    if(['a','b','c'].include? a[0])
      if(['1','2','3'].include? a[1])
        if @places[input] == " "
          @places[input] = @user
          put_line
          puts "#{@user_name} marks #{input.upcase}"
          check_game(@cpu)
        else
          wrong_move
        end
      else
        wrong_input
      end
    else
      wrong_input
    end
  else
    wrong_input unless input == 'exit'
  end
end

We already know we still need to define the check_game method, we also now need to define the wrong_move and wrong_input methods. If the user input a valid slot but it is not empty then we call wrong_move but if it is not a valid slot at all then we call wrong_input. We define these methods like so:

Code:
def wrong_input
  put_line
  puts "Please specify a move with the format 'A1' , 'B3' , 'C2' etc."
  user_turn
end

def wrong_move
  put_line
  puts "You must choose an empty slot"
  user_turn
end

Now we can proceed to creating the check_game method. first we will want to see if the cpu or the user has won, if so, output who won and exit the game. If not, check to see if there are any moves left and call the appropriate players turn function, otherwise, output that the game is over and it is a draw. Here is the method definition:

Code:
def check_game(next_turn)

  game_over = nil

  @columns.each do |column|
    # see if cpu has won
    if times_in_column(column, @cpu) == 3
      put_line
      puts "Game Over -- #{@cpu_name} WINS!!!"
      game_over = true
    end
    # see if user has won
    if times_in_column(column, @user) == 3
      put_line
      puts "Game Over -- #{@user_name} WINS!!!"
      game_over = true
    end
  end

  unless game_over
    if(moves_left > 0)
      if(next_turn == @user)
        user_turn
      else
        cpu_turn
      end
    else
      put_line
      puts "Game Over -- DRAW!"
    end
  end
end

There is now only one method left to define; the moves_left method that determines if it is a stalemate or not. Here it is:

Quote:def moves_left
slots = 0
@places.each do |k, v|
slots += 1 if v == " "
end
slots
end

And there you have it, you can now execute this program on the command line and it will play a game of tic tac toe with you. Like I said, I just started playing with Ruby, so if you see I did something wrong or find a way it could be improved, I hope you enjoyed the tutorial.

Reply

RE: Ruby - Basic Tutorial #2
Very nice tut. Maybe worthy of a sticky when Diss sees it. Anyways, great job, what other languages do you know?
Here to help! - Need anything, pm me.
I can also make graphics for anyone, pm me.
Call me Uncle Spire.

Reply

RE: Ruby - Basic Tutorial #3
im gonna read all of this and learn some shit, im starting to learn bits and pieces of some of the languages
[Image: Durara.png]

Reply

RE: Ruby - Basic Tutorial #4
This is a pretty decent guide. Thank-you for sharing. : )

Reply

RE: Ruby - Basic Tutorial #5
(03-23-2013, 02:48 PM)Paradox Wrote: Very nice tut. Maybe worthy of a sticky when Diss sees it. Anyways, great job, what other languages do you know?

I'm in the official group on LF for coding, around 5 members within :3
  • C/CSharp/C++
  • Java/JS
  • HTML/PHP/CSS/APEX
  • Ruby/Perl

I know some others, but i mainly study around them, as i do web designing and others.. I'm currently running a Image Hosting network too Wink

Reply

RE: Ruby - Basic Tutorial #6
(03-23-2013, 02:57 PM)Stickle™ Wrote: I'm in the official group on LF for coding, around 5 members within :3
  • C/CSharp/C++
  • Java/JS
  • HTML/PHP/CSS/APEX
  • Ruby/Perl

I know some others, but i mainly study around them, as i do web designing and others.. I'm currently running a Image Hosting network too Wink

Sweeeet. I'm almost up there with you, I know:
  • HTML/CSS/PHP
  • Responsive HTML5/CSS3
  • Python/Java/JS

I'm more a web designer/developer myself.
Programming will come along every once in
a while, but not too often.
Here to help! - Need anything, pm me.
I can also make graphics for anyone, pm me.
Call me Uncle Spire.

Reply

RE: Ruby - Basic Tutorial #7
(03-23-2013, 03:00 PM)Paradox Wrote: Sweeeet. I'm almost up there with you, I know:
  • HTML/CSS/PHP
  • Responsive HTML5/CSS3
  • Python/Java/JS

I'm more a web designer/developer myself.
Programming will come along every once in
a while, but not too often.

I looked in to "Python", but just didn't want to start learning something so old that isn't used today like alittle bit of Perl.

But i think you should look into C, i will do a really HQ guide soon and teach you alittle bit about C, but it's going to take awhile as i need to code at the same time as make the guide, just don't feel like doing it yet but i will before i go to sleep.

C is so fun as they are no limited. : P

Reply







Users browsing this thread: 1 Guest(s)