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