Minesweeper game board | ruby - ruby

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

Related

Algorithm Challenge number formatting problem

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

Why does it need two end statement on the last 3 and 5 lines?

According to this tutorial, I only need one end statement for the if, elsif, and else loop:
def dasherize_number(num)
nums = num.to_s
final_string = ''
i = 0
while i < nums.length
if i == 0
final_string += nums[i]
if nums[i].to_i % 2 == 1
final_string += '-'
end
elsif i == nums.length
final_string += nums[i]
else
if i % 2 == 1
final_string += '-'
final_string += nums[i]
end
end
i += 1
end
puts final_string
end
Why do there need to be two end statement between i += 1?
Indentation is important:
def dasherize_number(num)
nums = num.to_s
final_string = ''
i = 0
while i < nums.length
if i == 0
final_string += nums[i]
if nums[i].to_i % 2 == 1
final_string += '-'
end
elsif i == nums.length
final_string += nums[i]
else
if i % 2 == 1
final_string += '-'
final_string += nums[i]
end
end
i += 1
end
puts final_string
end

How to improve time complexity of this code piece?

I am having a hard time trying to down down the time. If this can be done in O(n^2) thatd be awesome.
def printDistance(file)
line = file.gets
if line == nil then return end
sz, sx, sy, ex, ey = line.split(/\s/)
#counter = 0
while line = file.gets do
if line[0...4] == "path"
else
x, y, ds, w = line.split(/\s/,4)
x = x.to_i
y = y.to_i
matrix= Array.new
later = Array.new
tmp = Array.new
processing = Array.new
if matrix[y].class != Array
matrix[y] = Array.new
end
if ds == "" #this cell has NO way to get to
matrix[y][x] = nil
else
matrix[y][x] = ds
end
end
end
for y in 0...matrix.length
processing[y] = Array.new
tmp[y] = Array.new
later[y] = Array.new
end
printing = Array.new
counter = 0
sy = sy.to_i
sx = sx.to_i
processing[sy][sx] = matrix[sy][sx]
matrix[sy][sx] = nil
puts "#{counter},(#{sx},#{sy})" #first one
counter += 1
loop do
print "#{counter},"
counter += 1
for y in 0...processing.length
for x in 0...processing[y].length
if processing[y][x].class != nil
tmp[y][x] = processing[y][x]
dirArr = tmp[y][x].to_s.split(//)
dirArr.each { |c|
if c == "u"
newY = y - 1
newX = x
elsif c == "d"
newY = y + 1
newX = x
elsif c == "l"
newY = y
newX = x - 1
else #c == r
newY = y
newX = x + 1
end
if matrix[newY][newX] != nil
tmpStr = "(#{newX},#{newY})"
printing.unshift(tmpStr)
later[newY][newX] = matrix[newY][newX]
matrix[newY][newX] = nil
end
}
end
end
end
printing.sort!
for i in 0...printing.length
print printing[i]
if i < printing.length - 1
print ","
end
end
printing = [] #resetting
puts
for i in 0...later.length
for j in 0...later[i].length
if later[i][j] != nil
processing[i][j] = later[i][j]
end
end
end
break if NotEmpty(matrix) == false
end
end
def NotEmpty (a)
for i in 0...a.length
for j in 0...a[i].length
if a[i][j] != nil
return true
end
end
end
return false
end
Basically this code reads in a file and places it in a 2-d array representing a maze, then based on maze's starting point it will perform a BFS to put all cells in order from closest to the start to the farthest, then print everything out
This code is now O(n^3) but I am trying to shrink it to O(n^2), is there anyway I can traverse a 2-d array and keeping track of the x and y values without using two forloops?
Any help is appreciated!! Thanks!

How to return an array from a recursive function

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)

ruby code stopping at run time, seemingly an infinite loop

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

Resources