Tic Tac Toe v1 Flashcards
class : BOARD
Board#initialize
A Board instance must have an instance variable to represent the game grid. For now, Board#initialize does not need to accept any arguments. Initialize the grid so that all positions are empty; this means that every position should contain an underscore (‘_’).
class Board
attr_accessor :grid
def initialize @grid = Array.new(3) { Array.new(3) {"_"} } end
end
class : BOARD
How do you create methods to check if a position (or coordinates) are 1) valid and 2) empty?
- FIRST create bracket only and brackets equal methods to make it easier.
- To check if the position is valid, iterate through the grid with nested iterations to get all the valid index pairs into an indices array. Then see if that indices array includes the position given.
- To check if empty, use the bracket only method (self[position]) to see if equals to the “_” indicating a mark has not already been placed.
def valid?(position) #returns true or false
valid_indices = []
(0…@grid.length).each do |i1|
(0…@grid[0].length).each do |i2|
valid_indices «_space;[i1, i2]
end
end
valid_indices.include?(position)
end
def [](position) row = position[0] column = position[1] @grid[row][column] end
def []=(position, value) row = position[0] column = position[1] @grid[row][column] = value end
def empty?(position) self[position] == "_" end
class : BOARD
Create method #place_mark that takes in a position and mark. This method should assign the given mark to the specified position of the grid. It should raise an error when the position is not #valid? or not #empty?.
- Using the bracket equal method here to reassign the value to the provided mark given that it’s both valid and empty.
def place_mark(position, mark)
if self.valid?(position) && self.empty?(position)
self[position] = mark
else
raise “Your input is not valid. Try again”
end
end
class : BOARD
Create a method that prints out each row on a separate line.
def print @grid.each do |row| puts row.join(" ") end end
class : BOARD
create two methods - 1) check if any of the rows have the same marks and 2) check if any of the columns have the same marks.
- Nested enumerables and like count, my inner enumerable .all? doesnt need a full block code and can just take the one parameter in parenthesis
def win_row?(mark) @grid.any? { |row| row.all?(mark) } end
def win_col?(mark) @grid.transpose.any? { |row| row.all?(mark) } end
class : BOARD
Create a method that checks if the provided mark wins diagonally in either direction.
CHECK WHICH ARRAY YOURE SHOVELING INTO
def win_diagonal?(mark)
forward = [] (0...@grid.length).each do |i| forward << self.grid[i][i] end
backwards = [] reverse = @grid.reverse (0...reverse.length).each do |i| backwards << reverse[i][i] end
forward.all?(mark) || backwards.all?(mark) end
class : BOARD
Create a method that checks to see if there’s w in either via rows, columns, or diagonally
def win?(mark)
win_row?(mark) || win_col?(mark) || win_diagonal?(mark)
end
class : BOARD
Create a method to check if there are any empty positions USING THE .EMPTY method already created.
- Find all the valid positions and iterate through them.
- The valid positions index will be a 2d array and iterating through them will give you a specific position. Use each position as an argument for the #empty? method. and check with #any? if any are true.
def empty?(position)
self[position] == “_”
end
def empty_positions? valid_indices = [] (0...@grid.length).each do |i1| (0...@grid[0].length).each do |i2| valid_indices << [i1, i2] end end valid_indices.any? { |pair| self.empty?(pair) } end
class : HumanPlayer
Create a method to get a position from the Human Player instance.
- Print a statement to ask for coordinates
- Get the user response with gets.chomp
- Convert the user’s response from strings to integer and shovel into an array.
- Raise an error if the array is not a length of 2 exactly
- MAKE SURE YOU RETURN THE POSITION
def get_position
p “Enter coordinates with two numbers separated by a single space.”
response = gets.chomp
position = []
response.each_char do |e|
position «_space;e.to_i if e != “ “
end
if position.length != 2 raise "Not valid position. Try again" end position end
class : GAME
How do you initialize the board for just two players?
- Don’t forget to create attr_reader and attr_accessor for the attributes
- There are 5 attributes to create - 1) player 1, 2) player 2, 3) player 3, and 4) board
- Each of the two players, the attributes will be initialized as INSTANCES of the HumanPlayer class.
- the Current Player will by default be @player_1
- the board attribute will be initialized to an INSTANCE of board class.
class Game
attr_reader :player_1, :player_2 attr_accessor :current_player, :board def initialize(player_1_mark, player_2_mark) @player_1 = HumanPlayer.new(player_1_mark) @player_2 = HumanPlayer.new(player_2_mark) @current_player = @player_1 @board = Board.new end
end
class : GAME
How do you create a method to switch turns?
- if the current player is player_1 then switch to player_2 or vice versa
- USE @ SYMBOLS
def switch_turn if @current_player == @player_1 @current_player = @player_2 else @current_player = @player_1 end end
class : GAME
Create a method to play the game using the following criteria:
while there exists empty positions on the board
print the board
get a position from the current player
place their mark at that position of the board
check if that user has won
if they win, print out a ‘victory’ message saying who won and return to end the game
otherwise they did not win, so switch turns
if we finish the while loop without returning, that means there are no more empty positions on the board and noone has won, so print a ‘draw’ message
- DONT FORGET THE @ SYMBOL.
- Creating a while loop using the #empty_positions method creating under the Board class.
- print the board using the #print method under Board class
- get the position by invoking the #get_position method under the Human_Player class and then assigning it to a position variable to then use elsewhere
- Invoke on the #place_mark method under Board class which requires a position and mark passed through as argument. This will use the position that we just got from the user and THE CURRENT PLAYERS MARK cause the current player is the DYNAMIC variable that changes from player 1 or 2 with the #switch_turn method
- then need to check if there’s win by invoking on the #win? method under Board class which checks to see if there’s a win in either row, column or both diagonals. But the #win? method requires a mark to be passed through as an argument so will use CURRENT PLAYERS’s mark
- create an if/else statement that if there’s a win to print out a victory message and then RETURN to “break” it if there is no win than to switch turns using SELF since it’s a game method and we’re in the game class.
- If we break out of the #empty_positions? loop without a win then the game is over with a draw. print message accordingly but OUTSIDE of the while loop.
def play
while board.empty_positions?
@board.print
position = @current_player.get_position
board.place_mark(position, current_player.mark)
if board.win?(current_player.mark)
p “Game over. #{current_player.mark} won!”
return
else
self.switch_turn
end
end
p "Game over. It's a draw. Play again." end
What is a shortcut of finding all the index pairs in a 2d array that’s square sized?
indices = (0…length).to_a
indices.product(indices)
COMBINATION provides unique combinations but will return an enumerator and needs to converted to an array while PRODUCT provides all possible combinations and returns an array already. For example…
a = [1, 2, 3, 4]
input : a.combination(a).to_a
output : [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]
input : a.product(a)
output : [[1, 1], [1, 2], [1, 3], [1, 4], [2, 1], [2, 2], [2, 3], [2, 4], [3, 1], [3, 2], [3, 3], [3, 4], [4, 1], [4, 2], [4, 3], [4, 4]]
What’s an important thing about bracket methods when using “self”
WHEN CALLING OUTSIDE OF THE INITIAL METHOD DEFINITION, NEED TO USE SELF
SELF [ POSITION ]