Ruby NoMethodError - Undefined method - ruby

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

Related

Iterating over loops in ruby?

I am new to ruby. I am trying to create a letter counter. my intended output was supposed to be [2,"I"] but I keep getting [3,"D]. Any help in understanding where I went wrong would be so helpful, thank you.
class LetterCounter
def initialize(text)
#text = text
end
def calculate_most_common()
counter = Hash.new(1)
most_common = nil
most_common_count = 1
#text.chars.each do |char|
next unless is_letter?(char)
counter[char] = (counter[char] || 1) + 1
if counter[char] > most_common_count
most_common = char
most_common_count += counter[char]
end
end
return [most_common_count, most_common]
end
private
def is_letter?(letter)
return letter =~ /[a-z]/i
end
end
counter = LetterCounter.new("Digital Punk")
p counter.calculate_most_common
# Intended output:
# [2, "i"]
Try:
class LetterCounter
def initialize(text)
#text = text
end
def calculate_most_common()
arr=#text.scan(/[a-z]/i)
arr.each_with_object(Hash.new(0)) { |n,h| h[n] += 1 }.max_by(&:last)
end
end
counter = LetterCounter.new("Digital Punk")
p counter.calculate_most_common
Prints:
["i", 2]
If you want to fix yours, try:
class LetterCounter
def initialize(text)
#text = text
end
def calculate_most_common()
counter = Hash.new(0)
most_common = nil
most_common_count = 0
#text.chars.each do |char|
next unless is_letter?(char)
counter[char] += 1
if counter[char]>most_common_count
most_common=char
most_common_count=counter[char]
end
end
return [most_common_count, most_common]
end
private
def is_letter?(letter)
return letter =~ /[a-z]/i
end
end
counter = LetterCounter.new("Digital Punk")
p counter.calculate_most_common
class LetterCounter
def initialize(text)
#text = text
end
def calculate_most_common()
counter = Hash.new(0) # wrong initialization in your code
most_common = nil
most_common_count = 0
#text.chars.each do |char|
next unless is_letter?(char)
counter[char] = (counter[char] || 1) + 1
puts "#{char} #{counter[char]}"
if counter[char] > most_common_count
puts "most common: #{most_common_count} #{char} #{counter[char]}"
most_common = char
most_common_count = counter[char] # error in your code
end
end
return [most_common_count, most_common]
end
private
def is_letter?(letter)
return letter =~ /[a-z]/i
end
end
counter = LetterCounter.new("Digital Punk")
p counter.calculate_most_common
The initial counters are at 1. These should be 0 since you are incrementing inside your loop anyways.
On the First Iteration -
Hash counter[char] is being initialized to 1 ( From Hash.new(1) ),
Then counter[char] is increased to 1 on first loop and the most_common_count is also increased by 1.
Which leads to D having value 3.
since most_common_count is already at 3 - the loop would no longer go into the if condition - as other characters would reach only 2 ( 1 from Hash.new and 1 from counter[char] +1 )
The if condition is > most_common_count and not >= - hence even if i reaches 3 - condition would not execute.
Hence the output [3, 'D']
Try this instead :
class LetterCounter
def initialize(text)
#text = text
end
def calculate_most_common()
counter = Hash.new(0)
most_common = nil
most_common_count = 0
#text.chars.each do |char|
next unless is_letter?(char)
puts char + " ==> " + counter[char].to_s # To know the value on each iteration
counter[char] += 1
if counter[char] > most_common_count
most_common = char
most_common_count = counter[char]
puts [most_common_count, most_common] # To know when the if condition is executed
end
end
return [most_common_count, most_common]
end
private
def is_letter?(letter)
return letter =~ /[a-z]/i
end
end
counter = LetterCounter.new("Digital Punk")
p counter.calculate_most_common

Minesweeper game board | 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

Creating sort visualiser in Ruby

I'm trying to make a sort visualiser using Ruby and Gosu. When the user left clicks it will sort the numbers out using a insertion sort. When the user right clicks it will sort the numbers out using a bubble sort. So far the only the insertion sort works but the bubble sort crashes after sorting out the first number. Does anyone know why? Thanks.
require 'gosu'
module ZOrder
BACKGROUND, MIDDLE, TOP = *0..2
end
SCREEN_WIDTH = 600 # needs to be COUNT * COLUMN_WIDTH
SCREEN_HEIGHT = 200 # Should be multiple of 100
MAX_VALUE = 100 # the range of the data 1 to 100
COUNT = 30 # Number of items
COL_WIDTH = 20 # suggested at least 10
class Column
# have a pointer to the neighbouring cells
attr_accessor :height, :value
def initialize(value)
#value = value
#height = (SCREEN_HEIGHT/MAX_VALUE) * value
end
end
class GameWindow < Gosu::Window
def initialize
super SCREEN_WIDTH, SCREEN_HEIGHT, false
self.caption = "Sort Visualiser"
#path = nil
#do_insert = false
#do_bubble = false
#columns = Array.new(COUNT)
column_index = 0
while (column_index < COUNT)
col = Column.new(rand(MAX_VALUE + 1))
#columns[column_index] = col
column_index += 1
end
#text_font = Gosu::Font.new(10)
end
def needs_cursor?
true
end
def insertion_sort(loop)
j = loop
done = false
while ((j > 0) and (!done))
if (#columns[j].value < #columns[j - 1].value)
temp = #columns[j - 1]
#columns[j - 1] = #columns[j]
#columns[j] = temp
else
done = true
end
j = j - 1
end
end
def bubble_sort(loop2)
j = loop2
i = loop2 + 1
done = false
while ((j > 0) and (!done))
if (#columns[j].value > #columns[i].value)
temp = #columns[j]
#columns[j] = #columns[i]
#columns[i] = temp
else
done = true
end
j = j - 1
end
end
def button_down(id)
case id
when Gosu::MsLeft
#do_insert = true
#loop = 0
#do_bubble = false
when Gosu::MsRight
#do_bubble = true
#loop2 = 0
#do_insert = false
end
end
def update
if (#do_insert)
puts "Doing insert #{#loop}"
if #loop < (COUNT)
insertion_sort(#loop)
#loop += 1
sleep(0.5)
else
#do_insert = false
end
end
if (#do_bubble)
puts "Doing bubble #{#loop2}"
if #loop2 < (COUNT)
bubble_sort(#loop2)
#loop2 += 1
sleep(0.5)
else
#do_bubble = false
end
end
end
def draw
column_index = 0
while (column_index < #columns.length)
color = Gosu::Color::GREEN
Gosu.draw_rect((column_index * COL_WIDTH), SCREEN_HEIGHT - #columns[column_index].height, COL_WIDTH, #columns[column_index].height, color, ZOrder::MIDDLE, mode=:default)
#text_font.draw("#{#columns[column_index].value}", (column_index * COL_WIDTH) + 5, SCREEN_HEIGHT - 10, ZOrder::TOP, 1.0, 1.0, Gosu::Color::RED)
column_index += 1
end
end
end
window = GameWindow.new
window.show
EDIT: Added in the rest of the code and removed some tags

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!

Conway's Game of Life having logic issues

I'm working on Conway's Game of Life. I have some logic issue that it always finishes in 2 or 3 ticks (usually only 2). Most of the time, it is with all cells dead, but occasionally it will have a 1 or 2 still alive. I can't find what part is causing the behavior of the second tick being almost entirely, if not completely, dead.
Do any of you see any major issues that might be causing this behavior?
require 'pry'
class Game
def initialize(size)
#size = size
#ticks = 1
#current_board = Array.new(size) { Array.new(size) { (rand(99) % 5 == 0) ? false : true } }
#future_board = Array.new(size) { Array.new(size) }
# #future_board = #current_board
end
def is_alive?(y, x)
if #current_board[y]
#current_board[y][x]
end
end
def check_neigbors(y, x)
neighbors = {
top: [y-1, x],
top_right: [y-1, x+1],
right: [y, x+1],
bottom_right: [y+1, x+1],
bottom: [y+1, x],
bottom_left: [y+1, x-1],
left: [y, x-1],
top_left: [y-1, x-1]
}
neighbor_state = {
top: false,
top_right: false,
right: false,
bottom_right: false,
bottom: false,
bottom_left: false,
left: false,
top_left: false
}
# binding.pry
neighbors.each { |k, v| neighbor_state[k] = true if is_alive?(v[0], v[1]) }
live_neighbor_count = 0
neighbor_state.each_value { |v| live_neighbor_count += 1 if v }
live_neighbor_count
end
def cell_lives(y, x)
#future_board[y][x] = true
end
def cell_dies(y, x)
#future_board[y][x] = false
end
def display_board
# need to display board here
system("clear")
# #current_board.each do
# |r| puts r.map { |c| c ? 'O' : 'X' }.join(" ")
# |r| puts r.map { |c| c }.join(" ")
puts #current_board.map { |row| row.map { |cell| cell ? 'X' : ' ' }.inspect }
# end
puts "\nTicks: #{#ticks}"
end
def play
loop do
display_board
#current_board.each do |r| # outer loop to iterate through rows
row_index = #current_board.index(r).to_i
r.each do |c| # inner loop to iterate through columns
column_index = r.index(c).to_i
live_neighbor_count = check_neigbors(row_index, column_index) # count the number of live neighbors
cell_dies(row_index, column_index) if ( is_alive?(row_index, column_index) ) && live_neighbor_count < 2 # rule 1
cell_lives(row_index, column_index) if ( is_alive?(row_index, column_index) ) && ( live_neighbor_count == 2 || live_neighbor_count == 3 ) # rule 2
cell_dies(row_index, column_index) if ( is_alive?(row_index, column_index) ) && live_neighbor_count > 3 # rule 3
cell_lives(row_index, column_index) if !( is_alive?(row_index, column_index) ) && live_neighbor_count == 3 # rule 4
end
end
if #current_board == #future_board # board is gridlocked. Game over!
puts "\nGAME OVER!"
exit!
else
#current_board = #future_board # update the board
#ticks += 1
sleep(1)
end
end
end
end
print "How large of a grid do you want: "
grid_size = gets.to_i
game = Game.new grid_size
game.play
In Conway's game of life, if you start with a random arrangement of cells, most of them do die on the second tick just because of the rules of the game, so I have no evidence there is a bug.
How about you make a glider or something and make sure it behaves as expected? If it doesn't work, then please post the output of your program and point out the first frame where the output is wrong, and post what you would expect the output to be.
try :-
#current_board = #futureboard.dup

Resources