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.
Related
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.
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'm trying to program the AI for a Mastermind game in ruby using Donal Knuth's 5 guess algorithm. The game consists of a codemaker, who uses 8 different colored pegs to create a set of 4, and a codebreaker, who guesses at the code and receives feedback (a red square for a peg which is both the right color and in the right spot, and a white square for a peg which is the right color but in the wrong spot).
I've created a set for all possible codes. My goal is to compare feedback from the guess to feedback from all codes in the set, then delete the ones that don't match. It seems to delete the entire set though.
class ComputerPlayer < Player
def initialize(game)
super(game)
#all_possible_codes = create_codes
#turn = 1
end
def get_code
Array.new(4){rand(1..6)}
end
def get_guess
puts #all_possible_codes.length
if #turn == 0
#turn += 1
cull_set([1, 1, 2, 2])
#all_possible_codes.delete("1122")
return [1, 1, 2, 2]
else
random_sample = #all_possible_codes.to_a.sample.split('').map{|str| str.to_i}
#all_possible_codes.delete(random_sample.join(''))
cull_set(random_sample)
random_sample
end
end
def cull_set(guess)
feedback = #game.feedback_on_guess(guess)
puts feedback
#all_possible_codes.delete_if { |str| #game.feedback_on_guess(str.split.map{|num| num.to_i}) != feedback }
end
def create_codes
set = Set.new
(1..8).each do |i|
(1..8).each do |j|
(1..8).each do |k|
(1..8).each do |l|
set << [i, j, k, l].join('')
end
end
end
end
set
end
end
#this is the feedback_on_guess method used by the above class
def feedback_on_guess(code_guess)
code_duplicate = #code
feedback = []
code_duplicate.map.with_index do |entry, i|
if entry == code_guess[i]
feedback.push('r')
code_guess[i] = -1
-2
else
entry
end
end.each do |entry|
found_index = code_guess.find_index(entry)
if found_index
feedback.push('g')
code_guess[found_index] = -1
end
end
puts feedback
feedback
end
Try
copy = something.dup
because after just
copy = something
copy and something are pointing to the same object. You can confirm this by checking the object_id of the object referenced by the variable. If it is the same, then it is the same object.
When you dup an object, you will cretae a copy. Depending on what you want to dup you might need to implement/override the logic to create a copy. For built in Classes like String, Hash and so on it will work out of the box.
Be aware that nested constructs (eq. Hash containing other Hashes) are not duplicated.
h1 = {"a" => {"b" => 2}}
h2 = h1.dup
puts h1.object_id # 70199597610060
puts h2.object_id # 70199597627020
puts h1["a"].object_id # 70199597610080
puts h2["a"].object_id # 70199597610080
I am banging my head against the error message I keep getting, reading that "syntax error, unexpected keyword_end, expecting end-of-input." I cannot find my mistake for the life of me. It's probably sloppy-looking, I am a newbie. And any tips on preventing this specific issue in the future would also be greatly appreciated!
$move_direction_hash = {"N" => [0,1], "E" => [1,0], "S" => [0,-1], "W" => [-1,0]}
$cardinal_directions = ["N", "E", "S", "W"]
class Martian
attr_accessor :coordinate_x, :coordinate_y, :bearing, :direction_string
def initialize (coordinate_x, coordinate_y, bearing, direction_string)
#coordinate_x = coordinate_x
#coordinate_y = coordinate_y
#bearing = bearing
#direction_string = direction_string
end
def check_valid
#coordinate_x.between?(0, $boundaries[0]) && coordinate_y.between?(0, $boundaries[1])
return true
end
end
#will be second and last called in source code
def get_final
return "#{coordinate_x} #{coordinate_y} #{bearing}"
end
def move_forward
# find where in the hash the bearing is
position_array = $move_direction_hash[#bearing]
# returns a temporary variable
# that is the
test_x = #coordinate_x + position_array[0]
test_y = #coordinate_y + position_array[1]
if $rovers_on_grid.include?([test_x.to_s, test_y.to_s])
puts "Stopping Martian. You're about to crash!"
get_final
break
else
#coordinate_x = test_x
#coordinate_y = test_y
if check_valid == false
puts "Stopping Martian. About to run off the plateau."
get_final
break
else
return #coordinate_x, #coordinate_y
end
end
end
def add_to_grid
$rovers_on_grid << [#x_coordinate, #y_coordinate]
end
def read_instructions
#direction_string.each_char do |direction|
if direction == "L" || direction == "R"
position = $cardinal_directions.index(#bearing)
if direction == "L"
position = (position - 1)%4
$cardinal_directions[position]
elsif direction == "R"
position = (position + 1)%4
$cardinal_directions[position]
else
puts "Error!"
end
#bearing = $cardinal_directions[position]
elsif direction == "M"
move_forward
end
end
end
end
This error is located in the check_valid method. You missed the if.
def check_valid
if #coordinate_x.between?(0, $boundaries[0]) && coordinate_y.between?(0, $boundaries[1])
return true
end
end
Like steenslag mentioned the if statement is not required. You can write:
def check_valid
return #coordinate_x.between?(0, $boundaries[0]) && coordinate_y.between?(0, $boundaries[1])
end
Below is my take for the TicTacToe game. So far it works, but it's not perfect. I'm having an issue with one of the methods - Game#invalid_move_check? after the game asked you "where to:"once choose the new destination , the game change the symbol like if it was a new turn .in fact not , suppose to keep the same player symbol until next turn.
P.S the code probably need some refactoring. I'm in a learning phase.
class Game
def initialize(symbol)
#board = Array.new(3){Array.new(3)}
# [0,1,2]
# [3,4,5]
# [6,7,8]
#symbol = ["X", "O"]
end
WINNING_COMBO = [
# Horizontal wins:
[0, 1, 2], [3, 4, 5], [6, 7, 8],
# Vertical wins:
[0, 3, 6], [1, 4, 7], [2, 5, 8],
# Diagonal wins:
[0, 4, 8], [2, 4, 6]
]
def create_players
# create both players
#names = []
print "Please enter the name of the first player: "
#player_1 =gets.chomp
#names << #player_1
print "Please enter the name of the second player: "
#player_2 = gets.chomp
#names << #player_2
puts "\n"
puts"welcome #{#player_1.upcase} and #{#player_2.upcase}"
puts"------------------------------------------------"
puts"\n"
puts "Randomizing who'll start..."
puts"\n"
# assign player by calling the player_assigment function that will determine who will start first
player_assigment
puts"\n"
end
def player_assigment
# merge the names array and symbol array
# with the zip method and return a nested array with player and symbol.
#choice = #names.zip(#symbol)
# iterate over the choice nested array and
# print out each player and their assigned symbol
#choice.each do |player, symbol|
puts "#{player.upcase} will use #{symbol}"
end
end
def current
#current = #names.first
#current
end
def switch_turn
#current = #names.last
#current
end
def first_turn
current
puts "#{#current.upcase} turn"
#marker = #symbol.first
make_move(#marker)
end
def next_turn
switch_turn
puts "#{#current.upcase} turn"
#marker = #symbol.last
make_move(#marker)
end
def check_win?(first_arr, second_arr)
WINNING_COMBO.select do |item|
if
item == first_arr
puts"#{#player_1} won!!"
elsif
item == second_arr
puts "#{#player_2} won!!"
end
end
end
def mapping(move, marker)
case move
when 0
arr_index = 0
index = 0
invalid_move_check?(arr_index,index)
#board[0][0] = marker
when 1
arr_index = 0
index = 1
invalid_move_check?(arr_index,index)
#board[0][1] = marker
when 2
arr_index = 0
index = 2
invalid_move_check?(arr_index,index)
#board[0][2] = marker
when 3
arr_index = 1
index = 0
invalid_move_check?(arr_index,index)
#board[1][0] = marker
when 4
arr_index = 1
index = 1
invalid_move_check?(arr_index,index)
#board[1][1] = marker
when 5
arr_index = 1
index = 2
invalid_move_check?(arr_index,index)
#board[1][2] = marker
when 6
arr_index = 2
index = 0
invalid_move_check?(arr_index,index)
#board[2][0] = marker
when 7
arr_index = 2
index = 1
invalid_move_check?(arr_index,index)
#board[2][1] = marker
when 8
arr_index = 2
index = 2
invalid_move_check?(arr_index,index)
#board[2][2] = marker
end
end
def invalid
puts"move invalid"
end
def invalid_move_check?(arr_index, index)
array = #board
if array[arr_index][index] == "X" ||
array[arr_index][index] == "O"
invalid
puts "Where to :"
#move = gets.chomp.to_i
mapping(#move,#marker)
end
end
def make_move(marker)
# after each turn the make_move method will called to place move on the board
puts "Where to :"
#move = gets.chomp.to_i
mapping(#move,#marker)
print_board
end
# display board in a matrix format
def print_board
#board.each_slice(1) { |a| p a }
puts"\n"
end
def instructions
puts "Instructions :Enter your first move by entering a number 1-9"
puts "corresponding to the grid on the bottom and press enter"
puts"\n"
puts "0 | 1 | 2 ",
"----------",
"3 | 4 | 5 ",
"----------",
"6 | 7 | 8 "
print"\n"
end
def self.start(symbol)
# start a new game
new_game =Game.new(symbol)
# create players
new_game.create_players
new_game.instructions
new_game.print_board
# Checking wining combo for matching patter if none
while new_game.check_win?(#move_first, #move_second) do
new_game.first_turn
# the player switch turn
new_game.next_turn
end
end
loop do
puts"------------------------------------------------"
puts" Welcome to tictactoe ".upcase
puts"------------------------------------------------"
print"\n"
Game.start(#symbol)
end
end
This should do the trick:
#will return true or false to check validity of move
def invalid_move_check?(arr_index, index)
array = #board
if array[arr_index][index] == "X" ||
array[arr_index][index] == "O"
invalid
puts "Where to :"
#move = gets.chomp.to_i
mapping(#move,#marker)
return true
end
return false
end
def mapping(move, marker)
case move
...
when 0
arr_index = 0
index = 0
unless invalid_move_check?(arr_index,index) #change all cases
#board[0][0] = marker #to have the assignment of board
#only if the move is valid
end
...
end
end
The reason of your bug is that the assignment happens even if the move is invalid.
This is just a band-aid solution to your current problem, as for refactoring ,there are things that can be done to optimize your code and make it better :) But you still have to fix first your 'ending'. Refactoring your own code would be a very good practice. I wish you a joyful ruby journey
!
After scratching the previous code and rethink the code i 've come up with this solution , not elegant as i will want it to be but it works .i move the symbol assignment in the if/else block code and therefore when a move is invalid next move will use the symbol of the current player
def mapping(move, symbol)
case move
when 0
if #board[0][0]=="X" || #board[0][0] == "O"
invalid
make_move
else
#board[0][0] = symbol
track_move(#move)
print_board
end
...
end
end