I made a TicTacToe game in Ruby. I am pretty new to Ruby OOP. My win conditions for checking row, column and diagonals are really heavy and they take too much code line and look pretty horrible. My code also always repeating.
So my question is how to implement the below win conditions into my code?
WIN_COMBINATIONS = [
[0,1,2], # top_row
[3,4,5], # middle_row
[6,7,8], # bottom_row
[0,3,6], # left_column
[1,4,7], # center_column
[2,5,8], # right_column
[0,4,8], # left_diagonal
[6,4,2] # right_diagonal
]
Here is my win conditions:
module CheckWinner
def check_horizontal(player1, player2)
player1_win = false
player2_win = false
#board.each do |row|
player1_win = row.all?(player1)
player2_win = row.all?(player2)
break if player1_win || player2_win
end
puts "#{player1} won!" if player1_win
puts "#{player2} won!" if player2_win
player1_win || player2_win
end
def check_vertical(player1, player2)
player1_win = false
player2_win = false
#board.transpose.each do |row|
player1_win = row.all?(player1)
player2_win = row.all?(player2)
break if player1_win || player2_win
end
puts "#{player1} won!" if player1_win
puts "#{player2} won!" if player2_win
player1_win || player2_win
end
def check_diagonal(player1, player2)
if #board[0][0] == player1 && board[1][1] == player1 && board[2][2] == player1 ||
#board[0][2] == player1 && board[1][1] == player1 && board[2][0] == player1
puts "#{#player1} won!"
true
elsif #board[0][0] == player2 && board[1][1] == player2 && board[2][2] == player2 ||
#board[0][2] == player2 && board[1][1] == player2 && board[2][0] == player2
puts "#{#player2} won!"
true
end
end
end
I invoke these conditions from this line of code:
def game
choosing_player
loop do
player1(#player1)
break if check_vertical(#player1,#player2) == true || check_diagonal(#player1,#player2) == true || check_horizontal(#player1,#player2) == true || full?
player2(#player2)
break if check_vertical(#player1,#player2) == true || check_diagonal(#player1,#player2) == true || check_horizontal(#player1,#player2) == true || full?
end
end
end
Here is my full of project that it is correctly working.
module CheckWinner
def check_horizontal(player1, player2)
player1_win = false
player2_win = false
#board.each do |row|
player1_win = row.all?(player1)
player2_win = row.all?(player2)
break if player1_win || player2_win
end
puts "#{player1} won!" if player1_win
puts "#{player2} won!" if player2_win
player1_win || player2_win
end
def check_vertical(player1, player2)
player1_win = false
player2_win = false
#board.transpose.each do |row|
player1_win = row.all?(player1)
player2_win = row.all?(player2)
break if player1_win || player2_win
end
puts "#{player1} won!" if player1_win
puts "#{player2} won!" if player2_win
player1_win || player2_win
end
def check_diagonal(player1, player2)
if #board[0][0] == player1 && board[1][1] == player1 && board[2][2] == player1 ||
#board[0][2] == player1 && board[1][1] == player1 && board[2][0] == player1
puts "#{#player1} won!"
true
elsif #board[0][0] == player2 && board[1][1] == player2 && board[2][2] == player2 ||
#board[0][2] == player2 && board[1][1] == player2 && board[2][0] == player2
puts "#{#player2} won!"
true
end
end
end
# TicTacToe Board
class Board
include CheckWinner
attr_accessor :board
def initialize
#board = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
end
def print_board
puts '-------------'
#board.each do |row|
print '|'
row.each do |col|
print " #{col}"
print ' | '
end
puts
end
puts '-------------'
end
def twodimentional_board
#board = #board.each_slice(3).map { |el| el }
end
def occupied_error(value)
puts 'There is a value or wrong place! Try Again!'
twodimentional_board
print_board
value == #player1 ? player2(#player1) : player1(#player2) # Stay same player
end
def move_if_possible(place, value)
#board.flatten!
if #board[place - 1] == 'X' || #board[place - 1] == 'O' || !place.between?(1, 9)
occupied_error(value)
else
#board[place - 1] = value
twodimentional_board
#board
end
end
def full?
if #board.flatten.all?(String)
puts 'Draw!'
true
end
end
def choosing_player
puts 'Choose for Player1(X or O)'
loop do
#player1 = gets.chomp!
break if #player1 == 'X' || #player1 == 'O'
puts 'Try Again!(X or O)'
end
puts "Player 1 is: #{#player1}"
#player1 == 'X' ? #player2 = 'O' : #player2 = 'X'
puts "Player 2 is: #{#player2}"
print_board
end
def player1(player1)
puts "Choice #{player1} Place on a board(1 to 10)"
#place = gets.chomp!.to_i
move_if_possible(#place, player1)
print_board
end
def player2(player2)
puts "Choice #{player2} Place on a board(1 to 10)"
#place = gets.chomp!.to_i
move_if_possible(#place, player2)
print_board
end
def game
choosing_player
loop do
player1(#player1)
break if check_vertical(#player1,#player2) == true || check_diagonal(#player1,#player2) == true || check_horizontal(#player1,#player2) == true || full?
player2(#player2)
break if check_vertical(#player1,#player2) == true || check_diagonal(#player1,#player2) == true || check_horizontal(#player1,#player2) == true || full?
end
end
end
board = Board.new
board.game
If you want to live preview here is repl link
I'm not sure if this is exactly what you're after, but there are a few things here.
your #board is an array of 3 rows, but your possible wins is just an array of points. flattening your #board means you can use the index as is.
you already have an array of win conditions, you then ignore this array and work through manually all options
definitions
# example winning board for 'O'
#board = [['O','X','O'], ['X','O','X'], ['X','O','O']]
player1 = 'X'
player2 = 'O'
First, loop through the possible combinations in WIN_COMBINATIONS, and for each of those combinations get those cells from #board as individual arrays, for later checking. store the array of arrays as another array (using #map). the * character is the ruby splat operator, which converts an array into arguments for the called procedure.
shorthand:
winmap = WIN_COMBINATIONS.map {|w| #board.flatten.values_at(*w)}
longhand:
flat_board = #board.flatten
winmap = WIN_COMBINATIONS.map do |win|
flat_board.values_at(*win)
end
which sets up an array like follows:
winmap = [
["O", "X", "O"], # top_row
["X", "O", "X"], # middle_row
["X", "O", "O"], # bottom_row
["O", "X", "X"], # left_column
["X", "O", "O"], # center_column
["O", "X", "O"], # right_column
["O", "O", "O"], # left_diagonal
["X", "O", "O"] # right_diagonal
]
once you have the list of all possible winning cells from the board, you can use your existing row searching code, except rather than looping through each array, we use the array operation #any? to identify matches
player1_win = winmap.any?{|m| m.all?(player1)}
player2_win = winmap.any?{|m| m.all?(player2)}
this should eliminate all the individual subroutines, and give you a true/false answer for if anyone has won.
Here's a different approach to determining if the board indicates a win, and if it does, who wins.
Let board be an array of three elements, each an array of three elements, each element being 'X', 'O' or '_', the last indicating the cell has not yet been marked.
Let's create some methods.
def row_win?(board)
board.each { |row| return row.first if row_uniq.size == 1 }
nil
end
This method returns 'X' if 'X' wins in a row, 'O' if 'O' wins in a row and nil if neither player wins in a row.
def column_win?(board)
row_win?(board.transpose)
end
This method similarly returns 'X', 'O' or nil, indicating if there is a win in a column, and if so, who wins.
def diagonal_win?(board)
row_win? [[board[0][0], board[1][1], board[2][2]],
[board[0][2], board[1][1], board[2][0]]]
end
This method does the same for the two diagonals.
Lastly,
def win?(board)
row_win?(board) || column_win?(board) || diagonal_win?(board)
end
nil is returned if there is no winner.
Let's try it.
win? [['X', '_', 'X'],
['_', 'X', 'O'],
['X', 'O', 'O']]
#=> 'X'
win? [['X', 'O', 'X'],
['_', 'O', 'X'],
['X', 'O', 'O']]
#=> 'O'
win? [['X', 'O', 'X'],
['O', 'O', 'X'],
['X', 'X', 'O']]
#=> nil
Why don't you track state helping you determine winning condition as you mark new moves? That way you don't have to recompute all what you already know.
For instance, if I tell you my board size is 3, that there's already 2 Xs in the first row and now X played a new move in that row leading to 3 Xs, you know X won immediately right? You don't even need to know anything else about the state of the board.
You can apply the same logic here and simply keep track of X & O counts per row, cols & diags as marks are recorded and you won't even have to loop to check who's won.
The above-described approach is easy to implement and takes O(1) time complexity to check the winning condition. You trade a bit of memory (O(n)) for computation speed.
Related
New at Ruby so excuse the poor code. I would like to iterate through the multidimensional array WIN_COMBINATIONS and check whether at least one array has all of its elements equal to 'X' or all equal to 'O'. If so, return the matched array. This is done through the won? function but it seems to only be returning the entire multidimensional array. Any assistance would be appreciated.
class TicTacToe
WIN_COMBINATIONS = [
[0,1,2], # top_row
[3,4,5], # middle_row
[6,7,8], # bottom_row
[0,3,6], # left_column
[1,4,7], # center_column
[2,5,8], # right_column
[0,4,8], # left_diagonal
[6,4,2] # right_diagonal
]
def initialize
#board = Array.new(9, " ")
end
def display_board
puts " #{#board[0]} | #{#board[1]} | #{#board[2]} "
puts "-----------"
puts " #{#board[3]} | #{#board[4]} | #{#board[5]} "
puts "-----------"
puts " #{#board[6]} | #{#board[7]} | #{#board[8]} "
end
def input_to_index(board_position)
user_input = board_position.to_i
user_input - 1
end
def move(board_index, player_token = 'X')
#board[board_index] = player_token
end
def position_taken?(board_position)
if #board[board_position] == ' '
false
else
true
end
end
def valid_move?(board_position)
if board_position >= 0 and board_position <= 8
if #board[board_position] == ' '
true
end
else
false
end
end
def current_player
turn_count % 2 == 0 ? "X" : "O"
end
def turn_count
#board.count{|token| token == "X" || token == "O"}
end
def turn
puts "Select your move (1-9)\n"
move = gets.chomp
move_index = input_to_index(move)
if valid_move?(move_index)
token = current_player
move(move_index, token)
display_board
else
puts "Select your move (1-9)\n"
move = gets.chomp
end
end
def won?
WIN_COMBINATIONS.each do |combinations|
if combinations.all? {|combination| combination == 'X' or combination == 'O'}
combinations
else
false
end
end
end
def draw?
if full? and !won?
true
elsif won?
false
else
false
end
end
def over?
end
def winner
end
def play
end
end
[...] it seems to only be returning the entire multidimensional array.
There are several issues with your attempted solution:
WIN_COMBINATIONS is an array of indices. These indices are numeric, so they will never be 'X' or 'O'. You have to check whether their corresponding values are 'X' or 'O'.
or is a control-flow operator intended for do_this or fail scenarios. The boolean "or" operator is ||. Using or instead of || might work but may have unexpected results due to its lower precedence. You almost always want ||.
The expression array.all? { |element| element == 'X' || element == 'O' } checks whether all elements are either 'X' or 'O'. It would be true for ['X','O','O'] and false for ['X',' ','O']. That's because you put the conditional inside the block. What you want is to check whether the elements are all 'X', or all 'O':
array.all?('X') || array.all?('O')
Your method's return value is the result of WIN_COMBINATIONS.each { ... } and Array#each always returns the array itself (i.e. WIN_COMBINATIONS) regardless of the blocks' result. To get the first element matching a condition, use find.
Let's apply all this to your code. Given this board:
#board = %w[
X - O
O X -
- - X
]
You could get the first matching combination via:
WIN_COMBINATIONS.find do |indices|
values = #board.values_at(*indices)
values.all?('X') || values.all?('O')
end
#=> [0, 4, 8]
values_at returns the values for the corresponding indices (* transforms the indices array to a list of arguments, so values_at(*[0,1,2]) becomes values_at(0,1,2)). The block's 2nd line then checks whether these values are all 'X', or all 'O'. Once this evaluates to true, the loop breaks and find returns the matching element. (or nil if there was no match)
Here is how I would approach the problem:
class TicTacToe
class OccupiedError < StandardError; end
attr_reader :rows
def initialize
#rows = 3.times.map{ Array(3, nil) }
end
def place!(player, x:, y:)
raise ArgumentError, "player must be :x or :o" unless [:x, :o].include?(player)
raise OccupiedError, "slot is already occupied" unless #rows[y][x].nil?
#rows[y][x] = player
end
# gets an array of columns instead of rows.
def columns
(0..2).map { |n| #rows.map {|row| row[n] } }
end
def diagonals
[
[#rows[0][0], #rows[1][1], #rows[2][2]], # lrt
[#rows[0][2], #rows[1][1], #rows[2][0]] # rtl
]
end
def all_combos
rows + columns + diagonals
end
# checks all the horizontal, vertical and diagonal combinations
def check_for_winner
# checks all combos for three in a row
(all_combos.find{ |a| a.all?(:x) || a.all?(:o) })&.first
end
end
In the initialize method we create a 3*3 array which represents all the positions on the board. This makes it a lot easier since its already grouped in rows. Intead of an empty string use nil to represent an empty square as nil is falsy.
When we want to check for a winner we gather up the rows, columns and the two diagonals into an array of arrays:
[1] pry(main)> game.rows
=> [[:o, :o, :o], [nil, :x, :x], [:x, nil, nil]]
[2] pry(main)> game.all_combos
=> [[:o, :o, :o],
[nil, :x, :x],
[:x, nil, nil],
[:o, nil, :x],
[:o, :x, nil],
[:o, :x, nil],
[:o, :x, nil],
[:o, :x, :x]]
From there we just have to check if any of them are all :x or :o. We don't actually have to list the winning combinations. In this case game.check_for_winner will return :o.
I have the following tic-tac-toe game: (I'm a noob, please disregard the design of the class, the game works, that's all I care about for now.)
#a tic tac toe game
class TicTacToe
require "yaml"
attr_accessor :player1, :player2
#crates playes and a game board to play tic tac toe
def initialize()
#player1 = Player.new("Player One", "x")
#player2 = Player.new("Player Two", "o")
#game_board = Board.new
end
#prints the board
def print_board
#game_board.board.each_with_index do |row, index|
puts "#{row.join(" | ")}"
puts "---------" unless index == 2
end
puts
end
#determines whose move it is
def move
if #turn % 2 == 1
player_one_turn
else
player_two_turn
end
#turn += 1
end
def valid_move?(row, col)
if #game_board.board[row][col] == " "
return true
else
return false
end
end
#player ones turn
def player_one_turn
print_board
puts "#{#player1.name} it's your turn:"
puts "Enter a row (0-2)"
row = gets.chomp.to_i
puts "Enter a column (0-2)"
col = gets.chomp.to_i
if valid_move?(row, col)
#game_board.board[row][col] = #player1.shape
else
puts "There's already a shape at that position."
player_one_turn
end
if win?(#player1.shape)
winner(#player1.name)
#winner = true
end
end
#player two's turn
def player_two_turn
print_board
puts "#{#player2.name} it's your turn:"
puts "Enter a row (0-2)"
row = gets.chomp.to_i
puts "Enter a column (0-2)"
col = gets.chomp.to_i
if valid_move?(row, col)
#game_board.board[row][col] = #player2.shape
else
puts "There's already a shape at that position."
player_two_turn
end
if win?(#player2.shape)
winner(#player2.name)
#winner = true
end
end
def win?(shape)
if (#game_board.board[0][0] == shape) && (#game_board.board[0][1] == shape) && (#game_board.board[0][2] == shape)
return true
elsif (#game_board.board[1][0] == shape) && (#game_board.board[1][1] == shape) && (#game_board.board[1][2] == shape)
return true
elsif (#game_board.board[2][0] == shape) && (#game_board.board[2][1] == shape) && (#game_board.board[2][2] == shape)
return true
elsif (#game_board.board[0][0] == shape) && (#game_board.board[1][0] == shape) && (#game_board.board[2][0] == shape)
return true
elsif (#game_board.board[0][1] == shape) && (#game_board.board[1][1] == shape) && (#game_board.board[2][1] == shape)
return true
elsif (#game_board.board[0][2] == shape) && (#game_board.board[1][2] == shape) && (#game_board.board[2][2] == shape)
return true
elsif (#game_board.board[0][0] == shape) && (#game_board.board[1][1] == shape) && (#game_board.board[2][2] == shape)
return true
elsif (#game_board.board[0][2] == shape) && (#game_board.board[1][1] == shape) && (#game_board.board[2][0] == shape)
return true
else
return false
end
end
def draw?
if #turn > 9
print_board
puts "The game ended in a draw. :)"
#winner = true
return true
end
false
end
def winner(winner_name)
puts "#{winner_name}, YOU WIN!!!"
end
def play
#turn = 1
#winner = false
until #winner
move unless draw?
save
end
end
#a class that generates an empty board
class Board
attr_accessor :board
def initialize
#board = [[' ', ' ', ' '], [' ', ' ', ' '], [' ', ' ', ' ']]
end
end
#a class that assigns creates plaers and assigns them a shape "x" or "o"
class Player
attr_accessor :name, :shape
def initialize(name, shape)
#name = name
#shape = shape
end
end
def save
yaml = YAML::dump(self)
File.open("save.txt", "w"){|file| file.write(yaml)}
end
def self.load
file = File.read("save.txt")
YAML::load_file(file)
end
end
game = TicTacToe.new
game.play
I want to start playing the game, quit the program in the middle of the game and then come back and finish it later after I call TicTacToe.load. However, when I do this now, the YAML file is loaded, but program does not resume where it's supposed to.
Can someone tell me if there is a way to do what I'm trying to do?
I regionally thought that doing something like YAML::load(self) would automatically load the save state of the file I was referring to via some kind of magic. However, I have come to learn that that the way that I designed my class and the dependencies in my "play" function would not allow me to load the previous state if my file.
When loading a YAML file, one has to load the file to a variable and then manually assign the object values to values of the class. That way, the current state of the variables are pretty much being manually assigned to the instance variables of the class. For example, I could have done something like this: file = YAML.load("file_name"), then assign variables values like: #board = file.board.
Had I known this before, I would have designed my class with less dependencies so that it would be loadable in a much cleaner and more convenient way.
I'm trying to build a simple 2 player tic tac toe game in Ruby.
Here is the code:
class Morpion
def init
create_grid
get_player
show_grid
end
def get_player
puts "Let play some Tic Tac Toe"
puts ""
#player1 ='X'
#player2='O'
puts ""
puts "Where would you like to move? (check out the grid below and type any number 1-9 to place your symbol): "
puts " 1 | 2 | 3 "
puts "---+---+---"
puts " 4 | 5 | 6 "
puts "---+---+---"
puts " 7 | 8 | 9 "
end
def create_grid
#grid = {
'1' => ' ',
'2' => ' ',
'3' => ' ',
'4' => ' ',
'5' => ' ',
'6' => ' ',
'7' => ' ',
'8' => ' ',
'9' => ' '
}
end
def show_grid
puts ""
puts "#{#grid['1']}|#{#grid['2']}|#{#grid['3']}"
puts "-----"
puts "#{#grid['4']}|#{#grid['5']}|#{#grid['6']}"
puts "-----"
puts "#{#grid['7']}|#{#grid['8']}|#{#grid['9']}"
puts ""
end
def play
number_turns=1
while number_turns < 10
number_turns.odd? ? player_turn(#player1) : player_turn(#player2)
game_checker
if game_checker
break
end
number_turns+=1
end
end
def player_turn(player)
puts player == 'X' ? "It's X's turn!" : "It's O's turn!"
puts ""
cell = gets.chomp
unless #grid.keys.include?(cell) #check if the user entered a number corresponding to the grid
puts ""
puts "it has to be a number from 1 to 9"
player_turn(player)
end
if #grid[cell] == ' ' #check if cell in grid is empty for user input
#grid[cell] = player
else
puts ""
puts "That cell is occupied. Choose again!"
player_turn(player)
end
show_grid
end
def game_checker
end_game = false
if #grid['1'] != ' ' && #grid['5'] != ' ' && #grid['9'] != ' '
if (#grid['1'] == #grid['2'] && #grid['1'] == #grid['3'])
end_game = true
victory = #grid['1']
elsif (#grid['4'] == #grid['5'] && #grid['4'] ==#grid['6'])
end_game = true
victory = #grid['4']
elsif (#grid['7'] == #grid['8'] && #grid['7'] == #grid['9'])
end_game = true
victory = #grid['7']
elsif (#grid['1'] == #grid['4'] && #grid['1'] == #grid['7'])
end_game = true
victory = #grid['1']
elsif (#grid['2'] == #grid['5'] && #grid['2'] == #grid['8'])
end_game= true
victory = #grid['2']
elsif (#grid['3'] == #grid['6'] && #grid['3'] == #grid['9'])
end_game = true
victory = #grid['3']
elsif (#grid['1'] == #grid['5'] && #grid['1'] == #grid['9'])
end_game = true
victory = #grid['1']
elsif (#grid['3'] == #grid['5'] && #grid['3'] == #grid['7'])
end_game = true
victory = #grid['3']
else
end_game = false
end
end
if end_game
puts "the winner of this game is #{victory}"
return true
end
end
end
m=Morpion.new
m.play
So my issue is this:
1. I am asking a player to add his symbol (X or O) in the grid that ranges from 1 to 9 (because there is 9 cells)
if I enter 1 for example, which is the upper left cell I get this error:
(eval):187: undefined method `keys' for nil:NilClass (NoMethodError)
from (eval):168:in `play'
from (eval):245
If you want to run this program I suggest using THIS LINK
EDIT:
The problem as #Paul and #August stated was that I used an incorrect constructor method init instead of using the correct one: initialize.
Now my program works. Thanks to them.
You initialize the #grid hash in a method called init. Ruby won't call this method when you construct a new instance of Game. You should instead rename the init method to initialize, which will be called automatically by Ruby.
The problem is that your #grid variable is never being created; it is nil. Hence the error message, you're attempting to invoke a method on a nil object.
The cause of your woes is because you've misnamed the constructor method. In Ruby, constructors are named initialize, however you named it init. Rename it to the correct name, and it should work.
Basically the computer_make_move method is simple. I just want it to pick an open position and take it. It's meant to be dumb. It's not meant to play skillfully (yet).
Problem is that for some off reason. Every time I make a move, the computer makes a random number of moves.. Sometimes 1, other times 3 or 6. Just strange behavior. I don't understand why.
Note any reference to a method not seen here is in the gameart.rb file and it all art eg. you_win is just art, no logic at all.
Here is all the code:
class TicTacToe
require 'colorize'
require_relative 'lib/gameart.rb'
attr_reader :home_screen_ninja_art
WINS = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [1, 5, 9], [3, 5, 7], [1, 4, 7], [2, 5, 8], [3, 6, 9]]
##scoreboard = {player: 0, computer: 0, tie: 0}
def initialize name, mark
#name, #gameover, #game_memory, #winner = name, false, ['_','_','_','_','_','_','_','_','_'], ''
mark == 'X' ? (#player, #computer = 'X', 'O') : (#player, #computer = 'O', 'X')
end
def menu i
return render_board if i == 99
return about if i == 100
return how_to_play if i == 200
return scoreboard if i == 300
return goodbye if i == 400
end
def action move
return menu(move) if move > 10
current_caller = caller[0] =~ /computer_make_move/ ? #computer : #player
i = move-1
if #game_memory[i] == '_'
render_board(i, current_caller)
else
try_again_art
end
end
def computer_make_move
action(current_board_positions[2].sample+1) # just pick any random open space for now
end
def render_board input=nil, caller=nil
if #gameover == false && !input.nil?
#game_memory[input] = caller
verify_game_state
end
if #gameover == true
game_over_art
case
when #winner == 'TIE' then you_tied
when #winner == #player then you_win
when #winner != #player then you_lose
end
end
board = #game_memory.map {|ps| ps == 'X' ? ps.colorize(:blue) : ps.colorize(:green) }.map {|ps| ps.gsub(/_/, " ")}
show_board board
end
def current_board_positions
x_positions, o_positions = [], []
#game_memory.select.with_index do |v,i|
x_positions << i+1 if v == 'X'
o_positions << i+1 if v == 'O'
end
open_positions = (1..9).to_a - (x_positions + o_positions)
[x_positions, o_positions, open_positions]
end
def verify_game_state
x, o, open = current_board_positions
find_winner = ->side { WINS.map {|win| side.combination(3).to_a.map {|set| set == win }.include? true } }
case
when (find_winner.(x).include? true)
#gameover=true
#winner='X'
when (find_winner.(o).include? true)
#gameover=true
#winner='O'
when !#game_memory.include?('_')
#gameover=true
#winner='TIE'
else
computer_make_move
end
end
end
This is how I initialize the game:
# Start a game
def start_new_game name, mark
#f = TicTacToe.new(name, mark)
puts #f.action 99
loop do
if #f.instance_variable_get :#gameover
puts 'Ready to play again?'.colorize(:white).on_red
print ' y|yes '.colorize(:green)
print ' n|no '.colorize(:red)
valid = false
until valid
startover = gets.chomp
valid = true if startover =~ /y|n|yes|no/i
end
if startover =~ /y|yes/i
start_new_game name, mark
else
#f.goodbye
end
else
print "* "
puts "Make your move #{name}:".colorize(:white).on_red
end
move = gets.chomp
puts #f.action move.to_i
end
end
valid = false
until valid
TicTacToe.home_screen_ninja_art
puts 'What is your name?'.colorize(:white).on_red
name = gets.chomp
valid = true if name.length > 1
end
valid = false
until valid
TicTacToe.home_screen_ninja_art
puts 'Pick your mark?'.colorize(:white).on_red
print ' X '.colorize(:blue)
print ' O '.colorize(:green)
puts ''
mark = gets.chomp.upcase
if mark =~ /X|O/i
valid = true
end
end
start_new_game name, mark
In method verify_game_state that calls after each render_board there is no case when person make move. It has only someone winners or tie, OR computer_make_move, so no noly computer should make a move.
So every time when it's not the end of the game, computer make a move, until it is, as i can see.
i am just starting my journey with ruby and am very new to programming in general as well. This is code for a homework assignment. I am getting syntax error as described in title. I understand that I have added some extra end while end of file was expected? right?
but where is this extra "end".
The code:
class WrongNumberOfPlayersError < StandardError ; end
class NoSuchStrategyError < StandardError ; end
def rps_result(move1,move2)
if (move1 == "r" && move2 == "s") || (move1 == "s" && move2 == p) || (move1 == "p" && move2 == "r")
return true
else
return false
end
end
def rps_game_winner(player1, player2)
strategy = /[rps]/
raise NoSuchStrategyError unless (move1 =~ strategy) & (move2 =~ strategy)
move1 = player1[1].downcase
move2 = player2[2].downcase
rps_result(move1, move2)? player1 : player2
end
end
def rps_tournament_winner(game)
round_winners = []
if game.length < 2
raise WrongNumberOfPlayersError
else
game.each_slice(2) do
|l1 , l2|
round_winners << rps_game_winner(l1, l2)
end
rps_tournament_winner(round_winners)
end
end
rps_tournament_winner([[[["Richard", "S"], ["Dave", "S"]], [["Richard", "R"], ["Michael", "S"]]], [[["Allen", "S"], ["Omer", "P"]], [["David E.", "R"], ["Richard X.", "P"]]]])
The rps_result method is never closed. Move the second end at the end of rps_game_winner to the last intended line of the rps_result method.
P.s. sorry for all the end's :)
I have modified the second block of code. Please check if it works.
def rps_game_winner(player1, player2)
strategy = /[rps]/
raise NoSuchStrategyError unless (move1 =~ strategy) && (move2 =~ strategy)
move1 = player1[1].downcase
move2 = player2[2].downcase
rps_result(move1, move2) ? player1 : player2
end
def rps_tournament_winner(game)
round_winners = []
if game.length < 2
raise WrongNumberOfPlayersError
else
game.each_slice(2) do |l1 , l2|
round_winners << rps_game_winner(l1, l2)
end
rps_tournament_winner(round_winners)
end
end
rps_tournament_winner([[[["Richard", "S"], ["Dave", "S"]], [["Richard", "R"], ["Michael", "S"]]], [[["Allen", "S"], ["Omer", "P"]], [["David E.", "R"], ["Richard X.", "P"]]]])
Did 4 changes:
Removed extra end in rps_game_winner method.
Added space between tertiary operators in rps_game_winner method.
rps_game_winner method, 2nd line, unless condition had only one &.
In rps_tournament_winner method, moved the args |l1, l2| after do.