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.
Related
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
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
Is there an easy way to have the results listed in a single line? For example,
"The numbers outputted were: 99, 85, 70, 50, 35, 20, -2"
def randomMethod()
rand1 = rand(2)
if rand1 == 1
rand2 = rand(1..25)
puts rand2
else
rand2 = 0
puts rand2
end
rand2
end
x = 99
#prints initial x
puts "x = " + "#{x}"
loop do
x -= randomMethod # decrement x by the value returned by randomMethod
puts "x = #{x}"
break if x <= 0
end
Don't use puts in a loop, it terminates with a newline. Instead, accumulate the values in an array and join them with commas once the set is complete:
x_vals = [99]
x_vals << x_vals.last - randomMethod while x_vals.last > 0
puts "The numbers were: #{x_vals.join(", ")}"
While you're at it, you could really tighten up your random method. I'm changing the name to be more conformant with Ruby norms:
def random_method
outcome = rand(1..25) * rand(2)
puts outcome
outcome
end
and if you don't actually need to print the value being generated each time you can completely lose the temporary variables:
def random_method
rand(1..25) * rand(2)
end
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
Is there a better way of handling this in Ruby, while continuing to use the symbols?
pos = :pos1 # can be :pos2, :pos3, etc.
if pos == :pos1 || pos == :pos2 || pos == :pos3
puts 'a'
end
if pos == :pos1 || pos == :pos2
puts 'b'
end
if pos == :pos1
puts 'c'
end
The obvious way would be swapping out the symbols for number constants, but that's not an option.
pos = 3
if pos >= 1
puts 'a'
end
if pos >= 2
puts 'b'
end
if pos >= 3
puts 'c'
end
Thanks.
EDIT
I just figured out that Ruby orders symbols in alpha/num order. This works perfectly.
pos = :pos2 # can be :pos2, :pos3, etc.
if pos >= :pos1
puts 'a'
end
if pos >= :pos2
puts 'b'
end
if pos >= :pos3
puts 'c'
end
Not sure if this is the best way......
I would make use of the include? method from array:
puts 'a' if [:pos1, :pos2, :pos3].include? pos
puts 'b' if [:pos1, :pos2].include? pos
puts 'c' if [:pos1].include? pos
Just use the case statement
pos = :pos1 # can be :pos2, :pos3, etc.
case pos
when :pos1 then %w[a b c]
when :pos2 then %w[a b]
when :pos3 then %w[a]
end.each {|x| puts x }
There are lots of different ways to get your output. Which one you
want depends on your specific objections to your if statements.
I've added a bunch of extra formatting to make the output easier
to read.
If you don't like the logical ORs and how they separate the results
from the output, you can use a lookup table:
puts "Lookup table 1:"
lookup_table1 = {
:pos1 => %w{a b c},
:pos2 => %w{a b },
:pos3 => %w{a },
}
[:pos1, :pos2, :pos3].each { |which|
puts "\t#{which}"
lookup_table1[which].each { |x| puts "\t\t#{x}" }
}
Or, if you want all the "work" in the lookup table:
puts "Lookup table 2:"
lookup_table2 = {
:pos1 => lambda do %w{a b c}.each { |x| puts "\t\t#{x}" } end,
:pos2 => lambda do %w{a b }.each { |x| puts "\t\t#{x}" } end,
:pos3 => lambda do %w{a }.each { |x| puts "\t\t#{x}" } end,
}
[:pos1, :pos2, :pos3].each { |which|
puts "\t#{which}"
lookup_table2[which].call
}
If your problem is that symbols aren't ordinals, then you can
ordinalize them by converting them to strings:
puts "Ordinals by .to_s and <="
[:pos1, :pos2, :pos3].each { |which|
puts "\t#{which}"
if which.to_s <= :pos3.to_s
puts "\t\ta"
end
if which.to_s <= :pos2.to_s
puts "\t\tb"
end
if which.to_s <= :pos1.to_s
puts "\t\tc"
end
}
Or you could monkey patch a comparison operator into the Symbol
class (not recommended):
puts "Ordinals by Symbol#<="
class Symbol
def <= (x)
self.to_s <= x.to_s
end
end
[:pos1, :pos2, :pos3].each { |which|
puts "\t#{which}"
if which <= :pos3
puts "\t\ta"
end
if which <= :pos2
puts "\t\tb"
end
if which <= :pos1
puts "\t\tc"
end
}
Or you could use a lookup table to supply your ordinal values:
puts "Ordinals through a lookup table:"
ordinal = {
:pos1 => 1,
:pos2 => 2,
:pos3 => 3,
}
[:pos1, :pos2, :pos3].each { |which|
puts "\t#{which}"
if ordinal[which] <= 3
puts "\t\ta"
end
if ordinal[which] <= 2
puts "\t\tb"
end
if ordinal[which] <= 1
puts "\t\tc"
end
}
Those are the obvious ones off the top of my head. It is hard to say what would be best without more specifics on what your problem with your if approach is; your second example indicates that what you really want is a way to make symbols into ordinals.
More generically, you can use this:
pos = :pos3
arr = [:pos1,:pos2,:pos3]
curr = 'a'
idx = arr.length
while idx > 0
puts curr if arr.last(idx).include? pos
curr = curr.next
idx -= 1
end
Or this, for your specific example:
puts 'a'
puts 'b' if pos != :pos3
puts 'c' if pos == :pos1