Battlefield game in ruby [closed] - ruby

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
This is a classic battleship game for two players:
#board1 and board2 arrays are boards that players see only dots (not bumped yet), O (not ship part) and X (bumped ship part)
board1 = []
board2 = []
#Create the board that players can see through with terminal
for i in 0..4
board1.append("O")
end
for i in 0..4
board2.append("O")
end
def print_board(board1)
for row in board1
puts board1.map { |k| "#{k}" }.join(" ")
end
end
def print_board(board2)
for row in board2
puts board2.map { |k| "#{k}" }.join(" ")
end
end
print_board(board1)
puts "\n"
print_board(board2)
#array1 and array2 are obvious boards of player1 and player2 respectly
array1 = [ [0, 1, 1, 1, 0], [1, 0, 0, 0, 0], [1, 0, 1, 0, 0], [0, 0, 1, 0, 1], [0, 0, 0, 0, 0] ]
array2 = [ [1, 0, 1, 1, 0], [0, 0, 0, 0, 1], [0, 1, 0, 0, 1], [0, 1, 0, 0, 1], [0, 0, 0, 0, 0] ]
#Starting of the game and the printing the board
while true do
puts "Welcome to the game!!!"
puts "Do you want to start? (start/reset):"
a = gets.chomp
if a == 'start'
for i in 0..100
puts "Turn - Player1: "
puts "Enter row: "
q = gets.chomp
p1_row = q.to_i
puts "Enter coloumn: "
w = gets.chomp
p1_col= w.to_i
if array2[p1_row][p1_col] == 1
array2[p1_row][p1_col] ="X"
board2[p1_row][p1_col] ="X"
elsif array2[p1_row][p1_col] == 0
array2[p1_row][p1_col] ="-"
board2[p1_row][p1_col] ="-"
elsif array2[p1_row][p1_col] =="X" or array2[p1_row][p1_col] =="-"
next
end
print_board(board2)
puts "Turn - Player2: "
puts "Enter row: "
e = gets.chomp
p2_row = e.to_i
puts "Enter coloumn: "
r = gets.chomp
p2_col= r.to_i
if array1[p2_row][p2_col] == 1
array1[p2_row][p2_col] ="X"
board1[p2_row][p2_col] ="X"
elsif array1[p2_row][p2_col] == 0
array1[p2_row][p2_col] ="-"
board1[p2_row][p2_col] ="-"
elsif array1[p2_row][p2_col] =="X" or array1[p2_row][p2_col] =="-"
next
end
print_board(board1)
end
elsif a == 'reset'
puts "You are off the game"
break
else
puts "\n"
puts "Answer can be only {start} or {reset}"
end
end
I have two problems with this code. When I entered index 4 for player 2, I get "index 4 out of string (IndexError)", but I did not find why. The other problem is that, when if statement finds 1 or 0, it changes all columns, and does not change only the element of the array.

The main problem is in your board set-up. You have
for i in 0..4
board1.append("O")
end
But that only creates one dimension. Try this:
for i in 0..4
board1[i] = []
(0..4).each do
board1[i].append("O")
end
end
A secondary problem is the subroutine print_board. First, you only need one definition of the subroutine, then second, the map needs to apply to "row" not "board", like this:
def print_board(board)
for row in board
puts row.map { |k| "#{k}" }.join(" ")
end
end
There are many other problems with your code. I assume you are learning Ruby and this is an exercise to learn the Array API. In such case, it will be best for you continue the exercise yourself, learning as you go.
However, one additional hint: Learn about rubocop and run it on your code. Doing this consistently will teach you about good Ruby style while also improving your code. To be specific: Install the rubocop gem, then run rubocop against your code like this:
rubocop -SEa battleship.rb

Related

Battleship game - how to let players place their boats?

I'm creating a battleship game in Ruby. Currently, players have 2 boats (size 3 and size 4) and I arbitrarily placed the boats by myself in the code.
Each player has its own board (array1/array2): I put a 0 when the spot is empty and 1 when there's a boat.
My question is, how can I make the players to place their ships on their board by themselves?
My idea was to ask them to enter the first point of each boat and then ask them the side (north, east, south and west) to set the orientation.
Obviously, a ship can't be placed out of bounds nor on the same space as another ship.
How can I make sure of that with my code? I have no idea where to start...
Thanks!
# 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
# Generation of the player boards
array1 = [ [0, 1, 1, 1, 0], [1, 0, 0, 0, 0], [1, 0, 0, 0, 0], [1, 0, 0, 0, 0], [1, 0, 0, 0, 0] ]
array2 = [ [0, 0, 0, 0, 0], [0, 0, 1, 0, 0], [0, 0, 1, 0, 1], [0, 0, 1, 0, 1], [0, 0, 1, 0, 1] ]
# 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
wins_p1 = 0
wins_p2 = 0
#Starting of the game and printing the board
while true do
puts 'Welcome!'
puts 'Enter start or stop'
starting = gets.chomp
puts "\n"
case starting.downcase
when 'start'
puts 'Enter the name of P1'
name_p1 = gets.chomp
puts "\n"
puts 'Enter the name of P2'
name_p2 = gets.chomp
puts "\n"
while wins_p1 < solution || wins_p2 < solution
puts "--- #{name_p1.upcase}'S TURN ---"
puts 'Enter line number (between 1 and 5):'
row_p1 = gets.chomp.to_i-1
puts 'Enter column number (between 1 and 5):'
column_p1 = gets.chomp.to_i-1
case array1[row_p1][column_p1]
when 1
board1[row_p1][column_p1] = 'X'
wins_p1 += 1
when 0
board1[row_p1][column_p1] = '-'
when 'X', '-'
next
end
puts "\n"
puts '--------------'
display_board(board1)
puts '--------------'
puts '----------------------'
puts "#{name_p1} has #{wins_p1} point#{"s" if wins_p1 > 1}."
puts '----------------------'
puts "\n"
break if wins_p1 == solution
puts "--- #{name_p2.upcase}'S TURN ---"
puts 'Enter line number (between 1 and 5):'
row_p2 = gets.chomp.to_i-1
puts 'Enter column number (between 1 and 5):'
column_p2 = gets.chomp.to_i-1
case array2[row_p2][column_p2]
when 1
board2[row_p2][column_p2] = 'X'
wins_p2 += 1
when 0
board2[row_p2][column_p2] = '-'
when 'X', '-'
next
end
puts "\n"
puts '--------------'
display_board(board2)
puts '--------------'
puts '----------------------'
puts "#{name_p2} a #{wins_p2} point#{"s" if wins_p2 > 1}."
puts '----------------------'
puts "\n"
break if wins_p2 == solution
end
puts "#{name_p1}, you are the winner!" if wins_p1 == solution
puts "#{name_p2}, you are the winner!" if wins_p2 == solution
puts "\n"
break
when 'stop'
puts 'See you soon!'
break
else
puts 'Enter start or stop only!'
puts "\n"
end
end
Going with your idea.
Get user input (x,y) for start of ship
Make sure that space is free (0)
Create a function that checks if a ship of size N (3 or 4) would fit in each orientation. For example in pseudocode (and this can be generalized with a direction parameter):
def wouldFitEast(board, anArray, posX, posY):
steps = 0
while (steps < N)
posX = posX + 1
if !validArrayIndex(posX, posY) or anArray[posX][posY] == 1
return false;
steps = steps + 1
return true;
if there is at least one direction the ship will fit, then ask which direction the user would like, otherwise, they need to pick a different location

Refactoring battleship game

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

Condition operators don't give correct result

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.

How do I get this each loop to apply to every item?

I'm working on a problem called Image Blur. I need to have my code take items (either 1s or 0s) from a 2D array and, for every 1, change the adjacent 0s to 1s. My code thus far does this well to the first 1 that it comes across, but for some reason it does not loop over the others.
class Image
def initialize(image)
#values = image
end
def find_ones
ones = []
#values.each_with_index do |row, row_index|
row.each_with_index do |pixel, column_index|
if pixel == 1
coord = [row_index, column_index]
ones << coord
end
puts "#{pixel} #{row_index} #{column_index}" if pixel == 1
end
end
ones
end
def transform
ones = find_ones
ri = ones[0][0]
ci = ones[0][1]
ones.each do
#values[ri + 1][ci] = 1 if (ri + 1) <= 3
#values[ri - 1][ci] = 1 if (ri - 1) >= 0
#values[ri][ci + 1] = 1 if (ci + 1) <= 3
#values[ri][ci - 1] = 1 if (ci - 1) >= 0
end
end
def output_image
#values.each do |row|
puts row.join
end
end
end
image = Image.new([
[0, 0, 0, 1],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 1, 0, 0]
])
image.transform
image.output_image
Thanks for the help in advance!
Well your ones.each do goes through the ones but ignores them, since your block doesn't have parameters. You essentially just use ones to determine how often to run the block.
Your
ri = ones[0][0]
ci = ones[0][1]
ones.each do
should be
ones.each do |ri, ci|
I'm surprised you got find_ones right (where you do something similar) but do something so strange in transform (setting ri and ci like that).

How to replace element in multidimensional array in ruby

I am having some difficulty with my code and I am hoping for some insight:
I have a 2d array for a board and I am attempting to replace a number with "X" when called, but am having struggles achieving this.
class BingoBoard
def initialize
#bingo_board = Array.new(5) {Array (5.times.map{rand(1..100)})}
#bingo_board[2][2] = 'X'
end
def new_board
#bingo_board.each{|row| p row}
end
def ball
#letter = ["B","I","N","G","O"].shuffle.first
#ball = rand(1..100)
puts "The ball is #{#letter}#{#ball}"
end
def verify
#ball
#bingo_board.each{|row| p row}
#bingo_board.collect! { |i| (i == #ball) ? "X" : i}
end
end
newgame = BingoBoard.new
puts newgame.ball
newgame.verify
I am aware that when verify is called it is iterating only through the array1, but I am unsure how to go about making the fix. Any help appreciated.
This is the root of the problem:
#bingo_board.collect! { |i| (i == #ball) ? "X" : i}
In this example, i is an array. So what you might want to do is to replace your code with something like:
#bingo_board.collect! do |i| # you're iterating over a double array here
if i.include?(#ball) # i is a single array, so we're checking if the ball number is included
i[i.index(#ball)] = 'X'; i # find the index of the included element, replace with X
else
i
end
end
Or if you prefer one-liner:
#bingo_board.collect! { |i| i.include?(#ball) ? (i[i.index(#ball)] = 'X'; i) : i }
Be aware that this is going to only replace the first occurrence of the element. So, say if your ball is 10, and you have:
[8, 9, 9, 10, 10]
you will get:
[8, 9, 9, "X", 10]
If you want ALL of the 10s to be replaced, then do something like:
#bingo_board.collect! do |i|
if i.include?(#ball)
i.collect! { |x| x == #ball ? 'X' : x }
else
i
end
end

Resources