Related
I'm creating a battleship game in Ruby.
Let me explain how it works:
The game is played on 5x5 grids (one per player).
Players have 2 ships each to place on their grid. A small ship (3x1 side) and a large one (4x1 size).
Players are asked in turn to place their ships on their board. A ship can't be placed out of bounds nor on the same space as another ship.
Players take turns to shoot at the opponent grid one after the other by selecting coordinates.
The first player to reach 7 points (all boats sinked) wins the game.
As you can see, some parts of my code are very repetitive:
I ask to each player to place its 2 ships so I have the "same" code 4 times (--- PLACEMENT OF THE SIZE 3 SHIP OF #{name_p1.upcase} ---).
I ask players to take turns to shoot at the opponent grid one after the other (this is done twice).
Same problem when I print the result of a shot.
I'm very struggling to refactor my code to make it simpler and shorter.
How can it be simplified?
# Board for player 1
board1 = []
for i in 0..4
board1[i] = []
(0..4).each do
board1[i].append('O')
end
end
# Board for player 2
board2 = []
for i in 0..4
board2[i] = []
(0..4).each do
board2[i].append('O')
end
end
# Display the boards
def display_board(board)
for row in board
puts row.map { |k| "#{k}" }.join(' ')
end
end
def check_obstacle(ship_size, player_array, posX, posY, direction)
check = 0
#ship_present = 0
#space_available = 0
#ship_present += 1 if player_array[posX][posY] == 1
#space_available += 1 if player_array[posX][posY] == 0
while check < ship_size && #space_available < ship_size
case direction
when 'north' then posX -= 1
when 'east' then posY += 1
when 'south' then posX += 1
when 'west' then posY -= 1
end
#space_available += 1 if posX.between?(0, 4) && posY.between?(0, 4)
#ship_present += 1 if posX.between?(0, 4) && posY.between?(0, 4) && player_array[posX][posY] == 1
check += 1
end
#space_available == ship_size && #ship_present == 0
end
def ship_placement(ship_size, player_array, posX, posY, direction)
steps = 0
while steps < ship_size && #ship_present == 0 && #space_available == ship_size
player_array[posX][posY] = 1
case direction
when 'north' then posX -= 1
when 'east' then posY += 1
when 'south' then posX += 1
when 'west' then posY -= 1
end
steps += 1
end
puts "The ship of size #{ship_size} is placed."
end
# Generation of the player boards
array1 = [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
array2 = [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
# A player wins when he/she reaches 7 points (2 ships each to place on their grid : a small ship (3x1 side) and a large one (4x1 size))
solution = 7
# We set the number of points at 0 at the start of the game
points_p1 = 0
points_p2 = 0
#Starting of the game and printing the board
while true do
puts 'Welcome soldiers!'
puts 'To start the game, enter "start". To quit, enter "stop":'
starting = gets.chomp
puts "\n"
case starting.downcase
when 'start'
puts 'Enter the first name of the first player:'
name_p1 = gets.chomp.to_s
puts "\n"
puts 'Enter the first name of the second player:'
name_p2 = gets.chomp.to_s
puts "\n"
puts "--- PLACEMENT OF THE SIZE 3 SHIP OF #{name_p1.upcase} ---"
while true
while true
puts 'Enter LINE number (between 1 and 5):'
placement_row = gets.chomp.to_i-1
break if placement_row.between?(0, 4)
end
while true
puts 'Enter COLUMN number (between 1 and 5):'
placement_column = gets.chomp.to_i-1
break if placement_column.between?(0, 4)
end
while true
puts "Enter ship direction (north, east, south ou west)"
orientation = gets.chomp.downcase
break if %w(north east south west).include? orientation
end
if check_obstacle(3, array1, placement_row, placement_column, orientation)
ship_placement(3, array1, placement_row, placement_column, orientation)
break
else
puts "Unable to place. Please try again."
end
end
puts "--- PLACEMENT OF THE SIZE 4 SHIP OF #{name_p1.upcase} ---"
while true
while true
puts 'Enter LINE number (between 1 and 5):'
placement_row = gets.chomp.to_i-1
break if placement_row.between?(0, 4)
end
while true
puts 'Enter COLUMN number (between 1 and 5):'
placement_column = gets.chomp.to_i-1
break if placement_column.between?(0, 4)
end
while true
puts "Enter ship direction (north, east, south ou west)"
orientation = gets.chomp.downcase
break if %w(north east south west).include? orientation
end
if check_obstacle(4, array1, placement_row, placement_column, orientation)
ship_placement(4, array1, placement_row, placement_column, orientation)
break
else
puts "Unable to place. Please try again."
end
end
###############################################################
puts "--- PLACEMENT OF THE SIZE 3 SHIP OF #{name_p2.upcase} ---"
while true
while true
puts 'Enter LINE number (between 1 and 5):'
placement_row = gets.chomp.to_i-1
break if placement_row.between?(0, 4)
end
while true
puts 'Enter COLUMN number (between 1 and 5):'
placement_column = gets.chomp.to_i-1
break if placement_column.between?(0, 4)
end
while true
puts "Enter ship direction (north, east, south ou west)"
orientation = gets.chomp.downcase
break if %w(north east south west).include? orientation
end
if check_obstacle(3, array2, placement_row, placement_column, orientation)
ship_placement(3, array2, placement_row, placement_column, orientation)
break
else
puts "Unable to place. Please try again."
end
end
puts "--- PLACEMENT OF THE SIZE 4 SHIP OF #{name_p2.upcase} ---"
while true
while true
puts 'Enter LINE number (between 1 and 5):'
placement_row = gets.chomp.to_i-1
break if placement_row.between?(0, 4)
end
while true
puts 'Enter COLUMN number (between 1 and 5):'
placement_column = gets.chomp.to_i-1
break if placement_column.between?(0, 4)
end
while true
puts "Enter ship direction (north, east, south ou west)"
orientation = gets.chomp.downcase
break if %w(north east south west).include? orientation
end
if check_obstacle(4, array2, placement_row, placement_column, orientation)
ship_placement(4, array2, placement_row, placement_column, orientation)
break
else
puts "Unable to place. Please try again."
end
end
while points_p1 < solution || points_p2 < solution
puts "--- #{name_p1.upcase}'S TURN ---"
while true
puts 'Enter LINE number (between 1 and 5):'
row_p1 = gets.chomp.to_i-1
break if row_p1.between?(0, 4)
end
while true
puts 'Enter COLUMN number (between 1 and 5):'
column_p1 = gets.chomp.to_i-1
break if column_p1.between?(0, 4)
end
# Shot fired!
case array1[row_p1][column_p1]
when 1
board1[row_p1][column_p1] = 'X'
array1[row_p1][column_p1] = 'X'
points_p1 += 1
when 0
board1[row_p1][column_p1] = '-'
array1[row_p1][column_p1] = '-'
when 'X', '-'
puts 'Square already played.'
next
end
puts "\n"
puts '--------------'
display_board(board1)
puts '--------------'
puts '----------------------'
puts "#{name_p1} has #{points_p1} point#{"s" if points_p1 > 1}."
puts '----------------------'
puts "\n"
break if points_p1 == solution
puts "--- #{name_p2.upcase}'S TURN ---"
while true
puts 'Enter LINE number (between 1 and 5):'
row_p2 = gets.chomp.to_i-1
break if row_p2.between?(0, 4)
end
while true
puts 'Enter COLUMN number (between 1 and 5):'
column_p2 = gets.chomp.to_i-1
break if column_p2.between?(0, 4)
end
# Shot fired!
case array2[row_p2][column_p2]
when 1
board2[row_p2][column_p2] = 'X'
array2[row_p1][column_p1] = 'X'
points_p2 += 1
when 0
board2[row_p2][column_p2] = '-'
when 'X', '-'
next
end
puts "\n"
puts '--------------'
display_board(board2)
puts '--------------'
puts '----------------------'
puts "#{name_p2} has #{points_p2} point#{"s" if points_p2 > 1}."
puts '----------------------'
puts "\n"
break if points_p2 == solution
end
puts "Congratulations #{name_p1}, you have destroyed all the enemy ships!" if points_p1 == solution
puts "Congratulations #{name_p2}, you have destroyed all the enemy ships!" if points_p2 == solution
puts "\n"
break
when 'stop'
puts 'See you soon!'
break
else
puts 'Please make a choice between "start". To exit, enter "stop".'
puts "\n"
end
end
As David mentions in his comment, you really want to make use of Ruby's object oriented features.
Here are some components I put together and some example usage. (Seemed like a fun exercise ... I guess I got carried away.)
class ShipPoint
attr_reader :row, :column, :owner
attr_accessor :hit
def initialize(row,column, owner)
#row = row
#column = column
#owner = owner
#hit = false
end
def to_s
#hit ? "X" : owner.label
end
end
class Ship
attr_reader :points, :name
attr_accessor :owner
def initialize(row:, column:, size:, direction:, name: nil, owner: nil)
#name = name
#owner = owner
case direction
when "north"
#points = (0...size).map { ShipPoint.new(row - _1, column, self) }
when "south"
#points = (0...size).map { ShipPoint.new(row + _1, column, self) }
when "east"
#points = (0...size).map { ShipPoint.new(row, column + _1, self) }
when "west"
#points = (0...size).map { ShipPoint.new(row, column - _1, self) }
end
end
def sunk?()
#points.all? {_1.hit}
end
def column_range()
Range.new(*#points.minmax_by {_1.column}.map {_1.column})
end
def row_range()
Range.new(*#points.minmax_by {_1.row}.map {_1.row})
end
def point(row,column)
#points.find {_1.row == row && _1.column == column}
end
def label()
return #name[0] if #name
owner.ships.index(self).to_s
end
end
class Board
attr_reader :range, :ships
def initialize(size=5)
#range = Range.new(0, size-1)
#content = Array.new(size) {Array.new(size)}
#ships = []
end
def add_ship(ship)
placeable = range.cover?(ship.column_range) && range.cover?(ship.row_range) && ship.points.all? {available?(_1.row, _1.column)}
if placeable
ship.owner = self
#ships << ship
ship.points.each {|p| #content[p.row][p.column] = ship}
else
puts "ship is unplaceable"
end
end
def to_s
display = ""
#content.each_index do |row|
row_display = ""
#content[row].each_index do |column|
location = #content[row][column]
row_display << (location ? location.point(row,column).to_s : ".")
end
display << "#{row_display}\n"
end
display
end
def available?(row, column)
#content[row][column].nil?
end
def target(row:,column:)
ship = #content[row][column]
point = ship&.point(row,column)
point&.hit = true
sunk = ship&.sunk?
all_sunk = #ships.all? {_1.sunk?}
case
when all_sunk then "all sunk"
when sunk then "Sunk: #{ship.name}"
when point&.hit then :hit
else :miss
end
end
end
b1 = Board.new
b2 = Board.new
s1 = Ship.new(row: 0, column: 1, size: 4, direction: "north")
s2 = Ship.new(row: 0, column: 1, size: 4, direction: "south", name: "Battle Ship")
s3 = Ship.new(row: 0, column: 1, size: 4, direction: "east")
s4 = Ship.new(row: 0, column: 1, size: 4, direction: "west")
s5 = Ship.new(row: 4, column: 0, size: 3, direction: "east")
b1.add_ship(s1)
b1.add_ship(s2)
b1.add_ship(s3)
b1.add_ship(s4)
b1.add_ship(s5)
puts b1
puts "result = #{b1.target(row: 0, column: 0)}"
puts "result = #{b1.target(row: 0, column: 1)}"
puts "result = #{b1.target(row: 1, column: 1)}"
puts "result = #{b1.target(row: 2, column: 1)}"
puts "result = #{b1.target(row: 3, column: 1)}"
puts "result = #{b1.target(row: 4, column: 0)}"
puts "result = #{b1.target(row: 4, column: 1)}"
puts "result = #{b1.target(row: 4, column: 2)}"
puts b1
I'm having trouble with my code here what it's supposed to do is generate a number from 1 to 6 and than ask you who you think the imposter is after it asks you it waits for input and than checks for the input and the random number that was generated if the input is Red and the random number was 1 it would say that red is the imposter and so on with Orange and 2 Green and 3 Yelow and 4 Cyan with 5 and Black with 6 but the problem is that if you put anything that isn't red EX: Orange it just stops for no reason. What did I do wrong with the code? Please help.
susas = rand (1..6)
puts "Who is the imposter?"
sleep (1)
puts "Is it Red, Orange, Green, Yellow, Cyan, Or Black (use capital format EX:Red)"
answr = gets.chomp
if answr == "Red" && susas == 1
puts "Red was the imposter"
elsif answr == "Red" && susas != 1
puts "Red was not the imposter you failed"
if answr == "Orange" && susas == 2
puts "Orange was the imposter"
elsif answr == "Orange" && susas != 2
puts "Orange was not the imposter you failed"
if answr == "Green" && susas == 3
puts "Green was the imposter"
elsif answr == "Green" && susas != 3
puts "Green was not the imposter you failed"
if answr == "Yellow" && susas == 4
puts "Yellow was the imposter"
elsif answr == "Yellow" && susas != 4
puts "Yellow was not the imposter you failed"
if answr == "Cyan" && susas == 5
puts "Cyan was the imposter"
elsif answr == "Cyan" && susas != 5
puts "Cyan was not the imposter you failed"
if answr == "Black" && susas == 6
puts "Black was the imposter"
elsif answr == "Black" && susas != 6
puts "Black was not the imposter you failed"
end
end
end
end
end
end
Here is a repl.it link https://repl.it/#Student_Aiden_K/SmoothFaithfulComputerscience#main.rb
if you put anything that isn't red EX: Orange it just stops for no reason. What did I do wrong with the code?
You have two main branches:
if answr == "Red" && susas == 1
puts "Red was the imposter"
elsif answr == "Red" && susas != 1
puts "Red was not the imposter you failed"
# anything else
end
The other colors are all under the elsif branch but Ruby will not reach them because answr == "Red" will be false for all colors beside red.
What you want is more elsif's on the same level:
if answr == "Red" && susas == 1
puts "Red was the imposter"
elsif answr == "Red" && susas != 1
puts "Red was not the imposter you failed"
elsif answr == "Orange" && susas == 2
puts "Orange was the imposter"
elsif answr == "Orange" && susas != 2
puts "Orange was not the imposter you failed"
# ...
end
Needless to say, there's a lot of repetition in your code. You should try to reduce that.
You could for example add a hash to map color names to numbers:
colors = {
'Red' => 1, 'Orange' => 2, 'Green' => 3,
'Yellow' => 4, 'Cyan' => 5, 'Black' => 6
}
Then fetch the number for the given answer:
answer = gets.chomp
number = colors[answer]
And have just one if to check whether it is correct or not:
if number == susas
puts "#{answer} was the imposter"
else
puts "#{answer} was not the imposter you failed"
end
I am trying to develop a battleship game in console. I need input of coordinates that guess where the game is. I did it:
#array1 and array2 are not obvious boards of player1 and player2 respectly
array1 = [ ['*', 'A', 'B', 'C', 'D', 'E'],['1',0, 1, 1, 1, 0], ['2',1, 0, 0, 0, 0], ['3',1, 0, 1, 0, 0], ['4',0, 0, 1, 0, 1], ['5',0, 0, 0, 0, 0] ]
array2 = [ ['*', 'A', 'B', 'C', 'D', 'E'],['1',1, 0, 1, 1, 0], ['2',0, 0, 0, 0, 1], ['3',0, 1, 0, 0, 1], ['4',0, 1, 0, 0, 1], ['5',0, 0, 0, 0, 0] ]
#arr1 and arr2 are obvious boards of player1 and player2 respectly
arr1 = [['*', 'A', 'B', 'C', 'D', 'E'],['1','.','.','.','.','.'],['2','.','.','.','.','.'],['3','.','.','.','.','.'],['4','.','.','.','.','.'],['5','.','.','.','.','.']]
arr2 = [['*', 'A', 'B', 'C', 'D', 'E'],['1', '.','.','.','.','.'],['2', '.','.','.','.','.'],['3', '.','.','.','.','.'],['4', '.','.','.','.','.'],['5', '.','.','.','.','.']]
coor = ['A', 'B', 'C', 'D', 'E']
#board1 and board2 are the final boards of player1 and player2
#this arrays will be seen during game
#every changes will be seen on these boards
board1 = arr1.map { |x| x.join(' ') }
board2 = arr2.map { |x| x.join(' ') }
#num1 and num2 are the numbers of the parts of ships respectively
num1 = 8
num2 = 8
#count1 and count2 are the numbers of poped ship parts respectively
count1 = 0
count2 = 0
#Starting of the game and the printing the board
#If we type "start" game will be started
#"Reset" game will be ended
#If we type words except "start" or "reset" program will ask "Do you want to start? (start/reset)" again
while true do
puts "Welcome to the game!!!"
puts "Do you want to start? (start/reset):"
respond = gets.chomp
if respond == 'start'
puts "\n"
puts "Player ONE"
puts board1
puts "\n"
puts "Player TWO"
puts board2
while true do
#Burada while ile player1 in shertleri olacaq
while true do
puts "\n"
puts "Turn - Player ONE"
puts "Enter coordinate: "
a = gets.chomp
a1 = a.split('')
b = coor.index(a1[0]) +1
col1 = b
row1= a1[1].to_i
if a == '""'
puts "You have to enter any coordinate"
break
elsif array1[row1][col1] == 'X' or array1[row1][col1] == '0'
puts "You have already bumped this coordinate!"
elsif col1<1 or col1>5 or row1>5 or row1<1
puts "This coordinate is out of board"
else
if array1[row1][col1] == 1
count1 = count1 + 1
arr1[row1][col1] = "X"
elsif array1[row1][col1] == 0
arr1[row1][col1] = "0"
end
board1 = arr1.map { |x| x.join(' ') }
puts "\n"
puts "Player ONE"
puts board1
puts "\n"
puts "Player TWO"
puts board2
if count1 == num1
puts "Player ONE won the game!"
break
end
end
break
end
while true do
#Burada while ile player2 in shertleri olacaq
puts "\n"
puts "Turn - Player TWO"
puts "Enter coordinate: "
c = gets.chomp
c1 = c.split('')
d = coor.index(c1[0]) + 1
col2 = d
row2= c1[1].to_i
if c == '""'
puts "You have to enter any coordinate"
break
elsif array2[row2][col2] == 'X' or array2[row2][col2] == '0'
puts "You have already bumped this coordinate!"
elsif col2<1 or col2>5 or row2>5 or row2<1
puts "This coordinate is out of board"
else
if array2[row2][col2] == 1
count2 = count2 + 1
arr2[row2][col2] = "X"
elsif array2[row2][col2] == 0
arr2[row2][col2] = "0"
end
board2 = arr2.map { |x| x.join(' ') }
puts "Player ONE"
puts board1
puts "\n"
puts "Player TWO"
puts board2
if count2 == num2
puts "Player TWO won the game!"
break
end
end
break
end
end
elsif respond == 'reset'
puts "You are off the game"
break
else
puts "\n"
puts "Answer can be only {start} or {reset}"
end
end
There are some problems, and I added if elsif else conditions to the code to solve them. One of them is for entering coordinates out of board, the second one is for not entering any coordinate, and the last one is for bumped coordinate. If these three condition are bypassed, the players can enter any coordinate.
But these codes don't work. When I check these three conditions, the result is an error. Can anyone tell me what the problem is with these conditions? Why don't they give suitable result?
When entering A8 as a value, the error you get is:
battleship.rb:54:in `<main>': undefined method `[]' for nil:NilClass (NoMethodError)
This is line 54:
elsif array1[row1][col1] == 'X' or array1[row1][col1] == '0'
In this case, the error is that array1["8"] is nil, and nil["A"] is undefined. An easy fix is to move line 56 above this one (the one that checks whether the input is actually within the board range).
elsif col1<1 or col1>5 or row1>5 or row1<1
If you do that, then since 8 is greater than 5, you'll get the "out of board" message rather than an error.
The other error you mention is when you put in an empty string. In that case the error is:
battleship.rb:48:in `<main>': undefined method `+' for nil:NilClass (NoMethodError)
Line 48 says:
b = coor.index(a1[0]) +1
In this case, the index is returning nil, and you can't add 1 to nil.
One way to fix this is to move the section a few lines down that checks whether a is blank to before line 48.
After a good 10 minutes of trying to come up with a more informative title, I ran out of ideas.
I have 2 prolems. The first is that whenever I try to input a position for pos_in, it registers as nul when run through the case statement that determines what m_index will be for that turn.
The second is that whenever the AI takes a turn, it always selects 1 1.
My question is: is there some way to fix this? I've tried switching the input types to no avail and I'm completely clueless on what to do.
My code:
$moves = ["x", "o"]
$move = 1
m_index = 0
$spots = [" ", " ", " ", " ", " ", " ", " ", " ", " "]
possiblesx = (1..3)
possiblesy = (1..3)
def board
puts """
TicTacToe 1.5 by Touka
#{$spots[0]}|#{$spots[1]}|#{$spots[2]}
-----
#{$spots[3]}|#{$spots[4]}|#{$spots[5]}
-----
#{$spots[6]}|#{$spots[7]}|#{$spots[8]}
move: #{$c_move}
"""
end
def ai_move
if $spots[3, 6] == "x" || $spots[4, 8] == "x" || $spots[1, 2] == "x"
pos_in = "1 1"
elsif $spots[1, 3] == "x" || $spots[4, 7] == "x"
pos_in = "1 2"
elsif $spots[0, 1] == "x" || $spots[6, 4] == "x" || $spots[5, 8] == "x"
pos_in = "1 3"
elsif $spots[0, 6] == "x" || $spots[4, 5] == "x"
pos_in = "2 1"
elsif $spots[0, 8] == "x" || $spots[1, 7] == "x" || $spots[2, 6] == "x" || $spots[5, 3] == "x"
pos_in = "2 2"
elsif $spots[2, 8] == "x" || $spots[3, 4] == "x"
pos_in = "2 3"
elsif $spots[0, 3] == "x" || $spots[2, 4] == "x" || $spots[7, 8] == "x"
pos_in = "3 1"
elsif $spots[1, 4] == "x" || $spots[6, 8] == "x"
pos_in = "3 2"
elsif $spots[6, 7] == "x" || $spots[0, 4] == "x" || $spots[2, 5] == "x"
pos_in = "3 3"
else
aimx = possiblesx.to_a.sample
aimy = possiblesy.to_a.sample
pos_in = "#{aimx} #{aimy}"
end
end
def game
system "cls"
if $move == 1
$move = 0
else
$move = 1
end
$c_move = $moves[$move]
board
if $opp == 2 && $c_move == "o"
ai_move
else
puts "Enter move coordinates (ex \"1 1\"):"
pos_in = gets.chomp
end
case pos_in
when ["1 1"]
m_index = 0
when ["1 2"]
m_index = 1
when ["1 3"]
m_index = 2
when ["2 1"]
m_index = 3
when ["2 2"]
m_index = 4
when ["2 3"]
m_index = 5
when ["3 1"]
m_index = 6
when ["3 2"]
m_index = 7
when ["3 3"]
m_index = 8
end
if $spots[m_index] == " "
$spots[m_index] = "#{$c_move}"
else
if $opp == 2 && $c_move == "o"
$move = $c_move
game
end
system "cls"
puts "
Error.
Re-enter $move."
sleep(3)
system "cls"
$move = c_$move
game
end
system "cls"
game
end
puts "Play vs. [1]Friend or [2]AI"
$opp = gets.to_i
game
The problem you had was with your scoping, along with the fact you were trying to match a string to an array as Piotr Kruczek pointed out.
Make the pos_in variable global and change the when statements from an array input to a string.
Here is the working version of your code:
$moves = ["x", "o"]
$move = 1
m_index = 0
$spots = [" ", " ", " ", " ", " ", " ", " ", " ", " "]
$pos_in = nil
def board
puts """
TicTacToe 1.5 by Touka
#{$spots[0]}|#{$spots[1]}|#{$spots[2]}
-----
#{$spots[3]}|#{$spots[4]}|#{$spots[5]}
-----
#{$spots[6]}|#{$spots[7]}|#{$spots[8]}
move: #{$c_move}
"""
end
def ai_move
if $spots[3, 6] == "x" || $spots[4, 8] == "x" || $spots[1, 2] == "x"
$pos_in = "1 1"
elsif $spots[1, 3] == "x" || $spots[4, 7] == "x"
$pos_in = "1 2"
elsif $spots[0, 1] == "x" || $spots[6, 4] == "x" || $spots[5, 8] == "x"
$pos_in = "1 3"
elsif $spots[0, 6] == "x" || $spots[4, 5] == "x"
$pos_in = "2 1"
elsif $spots[0, 8] == "x" || $spots[1, 7] == "x" || $spots[2, 6] == "x" || $spots[5, 3] == "x"
$pos_in = "2 2"
elsif $spots[2, 8] == "x" || $spots[3, 4] == "x"
$pos_in = "2 3"
elsif $spots[0, 3] == "x" || $spots[2, 4] == "x" || $spots[7, 8] == "x"
$pos_in = "3 1"
elsif $spots[1, 4] == "x" || $spots[6, 8] == "x"
$pos_in = "3 2"
elsif $spots[6, 7] == "x" || $spots[0, 4] == "x" || $spots[2, 5] == "x"
$pos_in = "3 3"
else
aimx = (1..3).to_a.sample
aimy = (1..3).to_a.sample
$pos_in = "#{aimx} #{aimy}"
end
end
def game
m_index = nil
system "cls"
if $move == 1
$move = 0
else
$move = 1
end
$c_move = $moves[$move]
board
if $opp == 2 && $c_move == "o"
ai_move
else
puts "Enter move coordinates (ex \"1 1\"):"
$pos_in = gets.chomp
end
case $pos_in
when "1 1"
p m_index = 0
when "1 2"
p m_index = 1
when "1 3"
p m_index = 2
when "2 1"
p m_index = 3
when "2 2"
p m_index = 4
when "2 3"
p m_index = 5
when "3 1"
p m_index = 6
when "3 2"
p m_index = 7
when "3 3"
p m_index = 8
end
if $spots[m_index] == " "
$spots[m_index] = "#{$c_move}"
else
if $opp == 2 && $c_move == "o"
$move = $c_move
game
end
system "cls"
puts "
Error.
Re-enter $move."
sleep(3)
system "cls"
$move = c_$move
game
end
system "cls"
game
end
puts "Play vs. [1]Friend or [2]AI"
$opp = gets.to_i
game
You assign
pos_in = "1 1"
as a String, while in you case statement you check for Array
case pos_in
when ["1 1"]
As Stated by Piotr and Menelik, there were a few obvious errors in this. Oddly enough, the structure of ai_move was one of those. The working code is as follows. It's not the cleanest thing ever and there are some bugs to iron out but it does it's job.
$moves = ["x", "o"]
$move = 1
m_index = 0
$spots = Array.new(9, " ")
$possiblesx = [1, 2, 3]
$possiblesy = [1, 2, 3]
def board
puts """
TicTacToe 1.5 by Touka
#{$spots[0]}|#{$spots[1]}|#{$spots[2]}
-----
#{$spots[3]}|#{$spots[4]}|#{$spots[5]}
-----
#{$spots[6]}|#{$spots[7]}|#{$spots[8]}
move: #{$c_move}
"""
end
def ai_move
if $spots[3] == "x" && $spots[6] == "x"
$pos_in = "1 1"
elsif $spots[4] == "x" && $spots[8] == "x"
$pos_in = "1 1"
elsif $spots[1] == "x" && $spots[2] == "x"
$pos_in = "1 1"
elsif $spots[1] == "x" && $spots[3] == "x"
$pos_in = "1 2"
elsif $spots[4] == "x" && $spots[7] == "x"
$pos_in = "1 2"
elsif $spots[0] == "x" && $spots[1] == "x"
$pos_in = "1 3"
elsif $spots[6] == "x" && $spots[4] == "x"
$pos_in = "1 3"
elsif $spots[5] == "x" && $spots[8] == "x"
$pos_in = "1 3"
elsif $spots[0] == "x" && $spots[6] == "x"
$pos_in = "2 1"
elsif $spots[4] == "x" && $spots[5] == "x"
$pos_in = "2 1"
elsif $spots[0] == "x" && $spots[8] == "x"
$pos_in = "2 2"
elsif $spots[1] == "x" && $spots[7] == "x"
$pos_in = "2 2"
elsif $spots[2] == "x" && $spots[6] == "x"
$pos_in = "2 2"
elsif $spots[5] == "x" && $spots[3] == "x"
$pos_in = "2 2"
elsif $spots[2] == "x" && $spots[8] == "x"
$pos_in = "2 3"
elsif $spots[3] == "x" && $spots[4] == "x"
$pos_in = "2 3"
elsif $spots[0] == "x" && $spots[3] == "x"
$pos_in = "3 1"
elsif $spots[2] == "x" && $spots[4] == "x"
$pos_in = "3 1"
elsif $spots[7] == "x" && $spots[8] == "x"
$pos_in = "3 1"
elsif $spots[1] == "x" && $spots[4] == "x"
$pos_in = "3 2"
elsif $spots[6] == "x" && $spots[8] == "x"
$pos_in = "3 2"
elsif $spots[6] == "x" && $spots[7] == "x"
$pos_in = "3 3"
elsif $spots[0] == "x" && $spots[4] == "x"
$pos_in = "3 3"
elsif $spots[2] == "x" && $spots[5] == "x"
$pos_in = "3 3"
else
aimx = $possiblesx.sample
aimy = $possiblesy.sample
$pos_in = "#{aimx} #{aimy}"
end
end
def game
system "cls"
if $move == 1
$move = 0
else
$move = 1
end
$c_move = $moves[$move]
board
if $opp == 2 && $c_move == "o"
ai_move
else
puts "Enter move coordinates (ex \"1 1\"):"
$pos_in = gets.chomp
end
case $pos_in
when "1 1"
m_index = 0
when "1 2"
m_index = 1
when "1 3"
m_index = 2
when "2 1"
m_index = 3
when "2 2"
m_index = 4
when "2 3"
m_index = 5
when "3 1"
m_index = 6
when "3 2"
m_index = 7
when "3 3"
m_index = 8
end
if $spots[m_index] == " "
$spots[m_index] = "#{$c_move}"
else
if $opp == 2 && $c_move == "o"
$move = $c_move
game
end
system "cls"
puts "
Error.
Re-enter move."
sleep(3)
system "cls"
$move = $c_move
game
end
system "cls"
game
end
puts "Play vs. [1]Friend [2]AI"
$opp = gets.to_i
game
I'm working on Conway's Game of Life. I have some logic issue that it always finishes in 2 or 3 ticks (usually only 2). Most of the time, it is with all cells dead, but occasionally it will have a 1 or 2 still alive. I can't find what part is causing the behavior of the second tick being almost entirely, if not completely, dead.
Do any of you see any major issues that might be causing this behavior?
require 'pry'
class Game
def initialize(size)
#size = size
#ticks = 1
#current_board = Array.new(size) { Array.new(size) { (rand(99) % 5 == 0) ? false : true } }
#future_board = Array.new(size) { Array.new(size) }
# #future_board = #current_board
end
def is_alive?(y, x)
if #current_board[y]
#current_board[y][x]
end
end
def check_neigbors(y, x)
neighbors = {
top: [y-1, x],
top_right: [y-1, x+1],
right: [y, x+1],
bottom_right: [y+1, x+1],
bottom: [y+1, x],
bottom_left: [y+1, x-1],
left: [y, x-1],
top_left: [y-1, x-1]
}
neighbor_state = {
top: false,
top_right: false,
right: false,
bottom_right: false,
bottom: false,
bottom_left: false,
left: false,
top_left: false
}
# binding.pry
neighbors.each { |k, v| neighbor_state[k] = true if is_alive?(v[0], v[1]) }
live_neighbor_count = 0
neighbor_state.each_value { |v| live_neighbor_count += 1 if v }
live_neighbor_count
end
def cell_lives(y, x)
#future_board[y][x] = true
end
def cell_dies(y, x)
#future_board[y][x] = false
end
def display_board
# need to display board here
system("clear")
# #current_board.each do
# |r| puts r.map { |c| c ? 'O' : 'X' }.join(" ")
# |r| puts r.map { |c| c }.join(" ")
puts #current_board.map { |row| row.map { |cell| cell ? 'X' : ' ' }.inspect }
# end
puts "\nTicks: #{#ticks}"
end
def play
loop do
display_board
#current_board.each do |r| # outer loop to iterate through rows
row_index = #current_board.index(r).to_i
r.each do |c| # inner loop to iterate through columns
column_index = r.index(c).to_i
live_neighbor_count = check_neigbors(row_index, column_index) # count the number of live neighbors
cell_dies(row_index, column_index) if ( is_alive?(row_index, column_index) ) && live_neighbor_count < 2 # rule 1
cell_lives(row_index, column_index) if ( is_alive?(row_index, column_index) ) && ( live_neighbor_count == 2 || live_neighbor_count == 3 ) # rule 2
cell_dies(row_index, column_index) if ( is_alive?(row_index, column_index) ) && live_neighbor_count > 3 # rule 3
cell_lives(row_index, column_index) if !( is_alive?(row_index, column_index) ) && live_neighbor_count == 3 # rule 4
end
end
if #current_board == #future_board # board is gridlocked. Game over!
puts "\nGAME OVER!"
exit!
else
#current_board = #future_board # update the board
#ticks += 1
sleep(1)
end
end
end
end
print "How large of a grid do you want: "
grid_size = gets.to_i
game = Game.new grid_size
game.play
In Conway's game of life, if you start with a random arrangement of cells, most of them do die on the second tick just because of the rules of the game, so I have no evidence there is a bug.
How about you make a glider or something and make sure it behaves as expected? If it doesn't work, then please post the output of your program and point out the first frame where the output is wrong, and post what you would expect the output to be.
try :-
#current_board = #futureboard.dup