I have written a code for the drawing board for the minesweeper game, can anyone help me to refactor this code more.
Please find my code below
def draw(height, width, mines)
board = Array.new(height) { Array.new(width,0) }
x = Random.rand(height)
y = Random.rand(width)
mines.times do
until board[x][y] != 'x'
x = Random.rand(height)
y = Random.rand(width)
end
board[x][y] = 'x'
end
board.each_with_index do |row, i|
row.each_with_index do |elem, j|
next if board[i][j] == 'x'
count = 0
count += 1 if i+1 < height && board[i+1][j] == 'x'
count += 1 if j+1 < width && board[i][j+1] == 'x'
count += 1 if i-1 >= 0 && board[i-1][j] == 'x'
count += 1 if j-1 >= 0 && board[i][j-1] == 'x'
board[i][j] = count
end
end
board.each do |row|
row.each do |e|
print "#{e} "
end
print "\n"
end
end
draw(4,4,3)
Thanks in advance.
I think you need to check 8 adjacent cells, not 4. Since this is a refactoring, I kept the original behavior.
def draw(height, width, mines)
board = Array.new(height) { Array.new(width, 0) }
mines.times do
x = rand(height)
y = rand(width)
redo if board[x][y] == 'x'
board[x][y] = 'x'
[[x - 1, y], [x + 1, y], [x, y - 1], [x, y + 1]].each do |x, y|
next if x < 0 || x >= height
next if y < 0 || y >= width
next if board[x][y] == 'x'
board[x][y] += 1
end
end
board.each { |row| puts row.join(' ') }
end
Invoice numbers are numeric only with any number of digits. To format one correctly, group the digits in group of three plus a group of any remainder, but never leave one digit by itself, unless it's a one digit number. Eg these are all correct formatting
123
12-34
6
783-907-23-45
And these are not
123-4
98-456
There's one more catch user input is passed directly to the function and you never know what characters users might type. Ignore any part of the input that is not digit
Invoice.format_number should always return a string
module Invoice
def self.format_number(str)
return ""
end
end
puts Invoice.format_number("ab1234")
What I have tried
1st approach
arr = []
str.chars.each do |elem|
val = elem =~ /\A[-+]?[0-9]*\.?[0-9]+\Z/
arr << elem if val == 0
end
num_of_digits = arr.length
pairs_of_two = 0
pairs_of_three = 0
if num_of_digits > 5
while num_of_digits > 0 do
break if num_of_digits <= 3
if num_of_digits >= 3 && (num_of_digits % 3 == 0 || num_of_digits % 3 == 2)
pairs_of_three += 1
num_of_digits -= 3
elsif num_of_digits % 2 == 0 || num_of_digits % 2 == 1
pairs_of_two += 1
num_of_digits -= 2
end
end
end
2nd approach
arr = []
str.chars.each do |elem|
val = elem =~ /\A[-+]?[0-9]*\.?[0-9]+\Z/
arr << elem if val == 0
end
len = arr.length - 1
if arr.length > 4
str = ""
i = 0
while i < len do
if arr[i..i+3].length == 4
str << arr[i..i+2].join + "-"
i += 3
elsif arr[i..i+2].length == 3
str << arr[i..i+1].join + "-"
i += 2
elsif arr[i..i+1].length == 2
str << arr[i..i+1].join
i += 2
elsif !arr[i].nil?
str << arr[i]
i += 1
end
end
puts str
else
if arr.length <= 3
puts arr.join
else
puts arr[0..1].join + "-" + arr[2..3].join
end
end
But none of them is correct
Here is the function invoice_number in python
def invoice_number(invoice):
s = ''.join(x for x in invoice if x <= '9' and x >= '0')
n = len(s)
if n <= 3:
return s
w = ''
i = 0
while i + 3 <= n:
for j in range(0, 3):
w += s[i + j]
i += 3
w += ('-')
m = n - i
if m == 0: return w[:-1]
if m == 1: return w[:m-3] + '-' + s[-2:]
return w + s[i:]
Testing
print(invoice_number('1234567'))
print(invoice_number('12345678'))
print(invoice_number('abc123456789'))
print(invoice_number('1234abc5678xyz9foobar'))
123-45-67
123-456-78
123-456-789
123-456-789
Eliminating non-digits is easy with re. For your format, the key is to figure our the "right" splitting indices.
Here is a try:
import re
def splits(n, k):
idx = [(i, min(n, i+k)) for i in range(0, n, k)]
if len(idx) > 1:
(a, b), (c, d) = idx[-2:]
if d - c < 2:
idx[-2:] = [(a, b - 1), (c - 1, d)]
return idx
def myformat(s):
s = re.sub(r'[^0-9]+', '', s)
parts = [s[a:b] for a, b in splits(len(s), 3)]
return '-'.join(parts)
Tests:
>>> myformat('123')
123
>>> myformat('1234')
12-34
>>> myformat('6')
6
>>> myformat('7839072345')
783-907-23-45
As the question was asked for ruby, adding solution for ruby. (The inspiration of the code is mostly from #yuri answer)
def format_invoice(invoice)
# only numbers are allowed
invoice = invoice.tr("^0-9","")
#puts invoice
return invoice if(invoice.length <= 3)
formatted_invoice = ''
i = 0
# Loop to divide the invoice in group of 3
while i + 3 <= invoice.length do
for j in 0..2 do
formatted_invoice += invoice[i + j]
end
i += 3
formatted_invoice += ('-')
end
m = invoice.length - i
return formatted_invoice[0..-2] if m == 0
return formatted_invoice[0..m-4] + '-' + invoice[-2..-1] if m == 1
return formatted_invoice + invoice[i..-1]
end
Testing
puts format_invoice('abc1') # 1
puts format_invoice('abc123') # 123
puts format_invoice('abc123A4') # 12-34
puts format_invoice('1234567') # 123-45-67
puts format_invoice('12345678') # 123-456-78
puts format_invoice('abc123456789') # 123-456-789
puts format_invoice('1234a#c5678xyz9foobar') # 123-456-789
The following is my code. I tried to only include the code where I think the problem may be.
Class Ship contains getStatus:
class Ship
def initialize(name, size, status, shipNumber, firedUpon)
#name = name
#size = size
#status = status
#shipNumber = shipNumber
#firedUpon = firedUpon
end
def setSize(nSize)
#size = nSize
end
def getSize
return #size
end
def setName(nName)
#name = nName
end
def getName()
return #name
end
def setStatus(nStatus)
#status = nStatus
end
def getStatus
return #status
end
def setFiredUpon(nFired)
#firedUpon = nFired
end
def getFiredUpon
return #firedUpon
end
def setShipNumber(nNum)
#shipNumber = nNum
end
def getShipNumber
return #shipNumber
end
def ==(rhs)
if (#name !=rhs.getName() || #size != rhs.getSize() || #status != rhs.getStatus() || #shipNumber != rhs.getShipNumber() || #firedUpon != rhs.getFiredUpon())
return false
end
return true
end
end
Class Board Calls upon getStatus and contains rangeIsOccupied:
load 'ship.rb'
class Board
def initialize()
#gameBoard = Array.new(100)
#Ships = Array.new(5)
#initialize all ships on board
r = 0
while(r < 100)
#gameBoard[r] = Ship.new('', -1, false, -1, false)
r = r + 1
end
#Ships[0] = Ship.new('Carrier', 5, true,0, false)
#Ships[1] = Ship.new('BattleShip', 4, true,1, false)
#Ships[2] = Ship.new('Cruiser', 3, true,2, false)
#Ships[3] = Ship.new('Submarine', 3, true,3, false)
#Ships[4] = Ship.new('Destroyer', 2, true,4, false)
end
def printBoard()
board_size = 10
alphabet = 'abcdefghijklmnopqrstuvwxyz'
puts 'x = hit ship' "\n" 'o = ship on board' "\n" 'm = empty space' "\n"
puts '---------------------------------------------------------' "\n"
puts ' |A| |B| |C| |D| |E| |F| |G| |H| |I| |J|'
s = 0
rownum = 0
printrow = true
while(s < 100)
if printrow
print rownum
print ' - '
print ' '
printrow = false
end
if #gameBoard[s].getShipNumber() != -1 && #gameBoard[s].getFiredUpon()
print 'x'
elsif(!#gameBoard[s].getStatus)
print 'm'
else
print 'o'
end
if s % 10 == 9
print "\n"
printrow = true
rownum = rownum + 1
else
print ' '
end
s = s + 1
end
end
def isValidDirection(x1, y1, x2, y2)
if x1 == x2 || y1 == y2
return true
end
return false
end
def rangeIsOccupied(x1, y1, x2, y2)
#if horizontal
if(y1 == y2)
while(x1 != x2)
if #gameBoard[(y1*10)+x1].
return true
end
if x1 > x2
x1 = x1 - 1
else
x1 = x1 + 1
end
end
if #gameBoard[(y1*10)+x1].getStatus
return true
end
else
while y1 != y2
if #gameBoard[(y1*10)+x1].getStatus
return true
end
if y1 > y2
y1 = y1 - 1
else
y1 = y1 + 1
end
end
if #gameBoard[(y1*10)+x1].getStatus
return true
end
end
return false
end
Class Game calls getBoard() and randomizeFleet, the start of the problem:
load 'player.rb'
class Game
def initialize()
#p1 = Player.new('', 1, true)
#p2 = Player.new('cpu', 2, false)
gamemode = 0
puts '---------------------------------------------------------'
print "Gamemodes:\n1. Player vs. CPU\n2. CPU vs. CPU\n"
puts '---------------------------------------------------------'
print "Please select a gamemode (1/2):\n: "
gamemode = gets.chomp
#p2.getBoard().randomizeFleet()
When I try to run my codeI get the error:
board:89:in rangeIsOccupied': undefined methodgetStatus' for nil:NilClass (NoMethodError)
I would like to overcome this issue.
if you are not sure that #gameBoard[(y1*10)+x1] will return a ship instance, then you can add another clause to your conditionals, like:
if #gameBoard[(y1*10)+x1] && #gameBoard[(y1*10)+x1].getStatus
I have a board (multidimensional array). You start from the upper left corner and can move right or down. The base case is the lower right corner.
I made a function that looks for all possible moves and their values within the board. When the base case is reached, the moves and values are stored as an array ARR_SUMNUM.
ARR_SUMNUM = []
def arr_all_moves(ary,a=0,b=0,sum_num=0,directions="")
rows = ary.length
cols = ary[0].length
goal = rows-1 + cols-1
curr_num = ary[a][b]
sum_num += curr_num
if [a,b] == [rows-1,cols-1]
ARR_SUMNUM.push([sum_num, directions])
return
end
if a == rows -1
return arr_all_moves(ary,a,b+1,sum_num,directions+="right ")
elsif b == cols -1
return arr_all_moves(ary,a+1,b,sum_num,directions+="down ")
end
arr_all_moves(ary,a,b+1,sum_num,directions+="right ")
directions.chomp!("right ") ##### I realize this was the source of all my issues
arr_all_moves(ary,a+1,b,sum_num,directions+="down ")
return ARR_SUMNUM
end
board = [ [1,3,4],
[5,6,999],
[8,9,10],
[11,12,13],
[2000,42,13]
]
p arr_all_moves(board)
How do I get this function to work without a constant like ARR_SUMNUM and use a local variable instead? I tried to use arr_sumnum instead, but I get an empty array.
The simplest way might just be to pass the array down the line with each call by setting it as one of the method arguments.
def arr_all_moves(ary, a=0, b=0, sum_num=0, directions=" ", arr_sum_num=[])
rows = ary.length
cols = ary[0].length
goal = rows-1 + cols-1
curr_num = ary[a][b]
sum_num += curr_num
if [a,b] == [rows-1,cols-1]
arr_sum_num.push([sum_num, directions])
return
end
if a == rows -1
return arr_all_moves(ary,a,b+1,sum_num,directions+="right ", arr_sum_num)
elsif b == cols -1
return arr_all_moves(ary,a+1,b,sum_num,directions+="left ", arr_sum_num)
end
arr_all_moves(ary,a,b+1,sum_num,directions+="right ", arr_sum_num)
arr_all_moves(ary,a+1,b,sum_num,directions+="left ", arr_sum_num)
return arr_sum_num
end
There's two ways, one was already mentioned that involved propagating the array down to the recursive function calls. Another would be to actually propagate the array upward and add them in the calling function.
def arr_all_moves(ary,a=0,b=0,sum_num=0,directions=" ")
rows = ary.length
cols = ary[0].length
goal = rows-1 + cols-1
curr_num = ary[a][b]
sum_num += curr_num
if [a,b] == [rows-1,cols-1]
return [[sum_num, directions]]
end
if a == rows -1
return arr_all_moves(ary,a,b+1,sum_num,directions+="right ")
elsif b == cols -1
return arr_all_moves(ary,a+1,b,sum_num,directions+="left ")
end
return arr_all_moves(ary,a,b+1,sum_num,directions+="right ") +
arr_all_moves(ary,a+1,b,sum_num,directions+="left ")
end
board = [ [1,3,4],
[5,6,999],
[8,9,10],
[11,12,13],
[2000,42,13]
]
p arr_all_moves(board)
Here is one possible solution, and you should use down instead of left as we are traversing right or down the 2-D array to reach to lower right corner.
def arr_all_moves(ary, a=0, b=0, sum_num=0, directions=" ")
rows = ary.length
cols = ary[0].length
sum_num += ary[a][b]
if [a,b] == [rows-1,cols-1]
[] << [sum_num, directions]
elsif a == rows-1
arr_all_moves(ary, a, b+1, sum_num, directions+="right ")
elsif b == cols-1
arr_all_moves(ary, a+1, b, sum_num, directions+="down ")
else
arr_all_moves(ary, a, b+1, sum_num, directions+="right ") +
arr_all_moves(ary, a+1, b, sum_num, directions+="down ")
end
end
board = [
[1,3,4],
[5,6,999],
[8,9,10],
[11,12,13],
[2000,42,13]
]
p arr_all_moves(board)
if i run the code, it will stop and not do anything and i am unable to type. seems to be an infinite loop.
the problem seems to be the end until loop, however if i take that out, my condition will not be met.
can anyone find a solution? i have tried all the loops that i can think of.
/. 2d array board ./
board = Array.new(10) { Array.new(10, 0) }
/. printing board ./
if board.count(5) != 5 && board.count(4) != 4 && board.count(3) != 3
for i in 0..9
for j in 0..9
board[i][j] = 0
end
end
aircraftcoord1 = (rand*10).floor
aircraftcoord2 = (rand 6).floor
aircraftalign = rand
if aircraftalign < 0.5
for i in 0..4
board[aircraftcoord2+i][aircraftcoord1] = 5
end
else
for i in 0..4
board[aircraftcoord1][aircraftcoord2+i] = 5
end
end
cruisercoord1 = (rand*10).floor
cruisercoord2 = (rand 7).floor
cruiseralign = rand
if cruiseralign < 0.5
for i in 0..3
board[cruisercoord2+i][cruisercoord1] = 4
end
else
for i in 0..3
board[cruisercoord1][cruisercoord2+i] = 4
end
end
destroyercoord1 = (rand*10).floor
destroyercoord2 = (rand 8).floor
destroyeralign = rand
if destroyeralign < 0.5
for i in 0..2
board[destroyercoord2+i][destroyercoord1] = 3
end
else
for i in 0..2
board[destroyercoord1][destroyercoord2+i] = 3
end
end
end until board.count(5) == 5 && board.count(4) == 4 && board.count(3) == 3
print " "
for i in 0..9
print i
end
puts
for i in 0..9
print i
for j in 0..9
print board[i][j]
end
puts
end
The line board.count(5) == 5 ... will never be true because board is a two-dimensional array. I can't tell what the condition should be, but it could look something like:
board[5].count(5) == 5