Greeting, I just started learning Ruby a few weeks with this noob book titled Computer Science Programming Basics in Ruby. I've practiced the exercises in each chapter, haven't had too many error, but I keep getting this error in a class I wrote up for the tic-tac-toe game in the last chapter. Here is the board.rb class file I made:
class Board
BOARD_MAX_INDEX = 2
EMPTY_POS = ' '
def initialize(current_player)
#current_player = current_player
#board = Array.new(BOARD_MAX_INDEX + 1) {
Array.new(BOARD_MAX_INDEX + 1) { EMPTY_POS }
}
end
end
def display
puts "+------+"
for row in 0..BOARD_MAX_INDEX
print "| "
for col in 0..BOARD_MAX_INDEX
s = #board[row][col]
if s == EMPTY_POS
print col + (row * 3) + 1
else
print s
end
print " | "
end
puts "\n+------+"
end
end
The class runs fine, but this is the error message in irb when I attempt to access
the display method call:
irb(main):004:0> require '/home/nick/board.rb'
=> true
irb(main):005:0> puts "Starting tic-tac-toe..."
Starting tic-tac-toe...
=> nil
irb(main):006:0> players = ['X', 'O']
=> ["X", "O"]
irb(main):007:0> current_player = players[rand(2)]
=> "O"
irb(main):008:0> b = Board.new(current_player)
=> #<Board:0x00000001c64868 #current_player="O", #board=[[" ", " ", " "], [" ", " ", " "], [" ", " ", " "]]>
irb(main):009:0> b.display()
NoMethodError: private method `display' called for #<Board:0x00000001c64868>
from (irb):9
from /usr/bin/irb:12:in `<main>'
Why am I getting this error, what do I have to do to get it to display the board?
You should put the definition of the display method inside the class:
end # of class
def display
...
end
Should be:
def display
...
end
end # of class
Hope it helps.
Related
I have a class Board that creates a hash:
class Board
attr_accessor :board
def initialize
#board = {}
end
def generate_board
i = 0
until i == 8
#board[i] = [" . ", " . ", " . ", " . ", " . ", " . ", " . "," . "]
i += 1
end
end
end
and i'm trying to access it from a different class:
class Pawn
def legal_moves
possible_moves = []
if #board[#position[0] + 1][#position[1] + 1].is_a? Piece
possible_moves << [[#position[0] + 1, #position[1] + 1]]
end
if #board[#position[0] + 1][#position[1] - 1].is_a? Piece
possible_moves << [[#position[0] + 1, #position[1] - 1]]
end
#legal_moves = possible_moves
end
end
How can I access this saved board state without creating a new board object? I can't create a new board object because the state of the board will need to be persistent. I already created an original instance in a different class that plays the game.
You pass the board to the new pawn instance.
class Pawn
attr_reader :board, :location_x, :location_y
def initialize(board)
#board = board
#location_x = nil
#location_y = nil
end
def move(x, y)
if board.get(x, y).nil?
board.set(location_x, location_y, nil)
self.location_x = x
self.location_y = y
board.set(x, y, self)
else
false
end
end
end
> board = Board.new
> pawn = Pawn.new(board)
> pawn.move(0,0)
> board.get(0,0) == pawn
=> true
If you want to get additional experience with object-oriented programming, you can use Singleton pattern:
require 'singleton'
class Board
include Singleton
attr_accessor :board
def generate_board
#board = {}
i = 0
until i == 8
#board[i] = [" . ", " . ", " . ", " . ", " . ", " . ", " . "," . "]
i += 1
end
end
end
In this case, to generate board you should call: Board.instance.generate_board
And when you need access to board from other class, you can add one line to init:
class Pawn
def initialize
#board = Board.instance.board
end
end
Now you can use #board inside Pawn object just as you do it in your example:
#board[#position[0] + 1][#position[1] + 1]
Good luck!
I'm a total beginner in ruby but I can't get out of this issue
I get these when I run the code, it all works well until the end:
INPUT TEXT: It all works well until
INPUT SUBTEXT: ll
TEXT: It all works well until SUBTEXT: ll
OUTPUT:
4
15
undefined method +' for nil:NilClass
(repl):18:ininitialize'
puts "\nINPUT TEXT:"
#text = gets.chomp
puts "\nINPUT SUBTEXT:"
#subtext = gets.chomp
puts "\nTEXT: " + #text
puts "SUBTEXT: " + #subtext
puts "\n"
i = #text.index (#subtext)
puts "OUTPUT:"
while i != -1
puts i.to_s + ' '
i = #text.index #subtext, i+1
end
In Ruby, String#index doesn't return -1 when the substring is not found; it returns nil. Change your condition from while i != -1 to while i. (This works because, unlike some other languages, Ruby considers the value 0 to be true; only false and nil are false.)
Index return nil if substrings doesn't exist. So this should solve this issue
#text = gets.chomp
puts "\nINPUT SUBTEXT:"
#subtext = gets.chomp
puts "\nTEXT: " + #text
puts "SUBTEXT: " + #subtext
puts "\n"
i = #text.index (#subtext)
puts "OUTPUT:"
while i
puts i.to_s + ' '
i = #text.index #subtext, i+1
end
I have a method known_args being called from the main class of my script, it is included in the module, Validations, but for some reason I'm getting:
undefined method `known_args' for PasswordGenerator:Class (NoMethodError)
Strange thing is, even when I place it back in the main class, I get the same error.
password_generator.rb
require 'securerandom'
require_relative 'validations'
class PasswordGenerator
include Validations
class Array
# If +number+ is greater than the size of the array, the method
# will simply return the array itself sorted randomly
def randomly_pick(number)
sort_by{ rand }.slice(0...number)
end
end
def password_generator length, charsets
#Set allowed characters
valid_characters = charsets
#Intitialise empty string
password = ""
length.times do
#we cannot do "randomly_pick(length)" because we want repeating characters
password << valid_characters.randomly_pick(1).first
end
#Return password
password
end
def username_generator
SecureRandom.urlsafe_base64(calc_usename_length)
end
def calc_usename_length
length = (12..32).to_a
length.sample
end
############### Input, checks and validations
def charsets
special_chars = [ '#', '£', "#", "%", "?", "€", "-", "+", "=", "*", "_", "~", "^", "|", ".", "[","]", "{", "}" ]
other = ('0'..'9').to_a + ('A'..'Z').to_a + ('a'..'z').to_a
combined = use_special? ? insert_special(special_chars, other) : no_special(other)
combined
end
def use_special?
return true if #use_special_argument == 's' || #use_special_argument == 'S'
return false if #use_special_argument == 'b' || #use_special_argument == 'B'
end
def insert_special(special_chars, other)
other + special_chars
end
def no_special(other)
other
end
def argument_check length
is_integer(length) && check_length(length) ? pass(length) : fail
end
def pass length
password_generator(length, charsets)
end
length = ARGV[0].to_i
#use_special_argument = ARGV[1]
unless known_args.include? #use_special_argument
puts
puts "Specify whether or not to use special characters."
puts "Use special characters: 's' || 'S'"
puts "No special characters: 'b' || 'B'"
puts
puts "Unknown argument: '#{#use_special_argument}'. Aborting"
exit_script
end
puts
puts "Username: " + username_generator
puts "Password: " + argument_check(length)
puts
puts "Complete. Exiting"
exit_script
end
validations.rb
module Validations
def is_integer length
length.is_a? Integer
end
def check_length length
length > 0
end
def fail
'Argument must be integer and greater than 0. Recommend max length available by application. 64 if unknown'
end
def known_args
known_args_array = ['s', 'S', 'b', 'B' ]
known_args_array
end
def exit_script
Kernel.exit(false)
end
end
Look closely at the error message:
undefined method `known_args' for PasswordGenerator:Class (NoMethodError)
That message is saying that Ruby can't find a known_args method in PasswordGenerator, not in an instance of PasswordGenerator.
Saying include Validations adds known_args from Validations as an instance method but you're trying to call it as a class method.
You can either set up a Validations.included method to add known_args the includer when include Validations happens or, better IMO, move all your code into an instance method:
class PasswordGenerator
#...
def perform
length = ARGV[0].to_i
#use_special_argument = ARGV[1]
unless known_args.include? #use_special_argument
puts
puts "Specify whether or not to use special characters."
puts "Use special characters: 's' || 'S'"
puts "No special characters: 'b' || 'B'"
puts
puts "Unknown argument: '#{#use_special_argument}'. Aborting"
exit_script
end
puts
puts "Username: " + username_generator
puts "Password: " + argument_check(length)
puts
puts "Complete. Exiting"
exit_script
end
end
and then say:
PasswordGenerator.new.perform
to crank things up.
Hi Im getting an core error which is really standard I suppose in Ruby but dont know what to make of it. I have a program that I have written. Its purpose is to register guests at a camping. You have a menu with 5 different options. 1. Checkin. When i do this I get a undefined method generateParkingLot' for #<Camping:0x10030a768> (NoMethodError)
When I choose Checkout I get a undefined local variable or methoddeparture' for Menu:Class (NameError).
So please i someone has a clue to my problem it would be great. I will here paste all my code. The code is separated in different files and I have used require for the different files. Here though I will paste all the code in one trace. Thankful for all help.
-Sebastien
require 'guest'
require 'parking_lot'
class Camping
attr_accessor :current_guests, :parking_lots, :all_guests, :staticGuests
def initialize(current_guests, parking_lots, all_guests, staticGuests)
#current_guests = Array.new()
# initiera husvagnsplatserna
#parking_lots = Array.new(32)
32.times do |nr|
#parking_lots[nr] = Parking_Lot.new(nr)
#staticGuests = Array[
Guest.new("Logan Howlett", "Tokyo", "07484822",1, #parking_lots[0]),
Guest.new("Scott Summers", "Chicago", "8908332", 2, #parking_lots[1]),
Guest.new("Hank Moody", "Boston", "908490590", 3, #parking_lots[2]),
Guest.new("Jean Grey", "Detroit", "48058221", 4, #parking_lots[3]),
Guest.new("Charles Xavier","Washington DC", "019204822",5, #parking_lots[4])
]
end
#all_guests = []
#staticGuests.each do |guest|
#current_guests[guest.plot.nr] = guest
#all_guests.push(guest)
end
end
def to_s
# creates an empty string
list = " "
# loop from 1 to 32
(1..32).each do |n|
if (!#current_guests[n-1].nil?)
list += #current_guests[n-1].to_s
else
# else adds the text "Vacant"
list += n.to_s + ": Vacant!\n"
end
return list
end
def generateParkingLot
randomNr = 1+rand(32)
# exists a guest at the (0-based) position?
if (!#current_guests[randomNr-1].nil?)
# if so generate a new figure
generateEmpty(array)
else
# returns the generated number
return randomNr
end
end
end
end
class Guest
attr_accessor :firstname, :lastname, :address, :phone, :departure
attr_reader :arrived, :plot
def initialize (firstName, lastName, address, phone, plot)
#firstName = firstName
#lastName = lastName
#address = address
#phone = phone
#arrived = arrived
#plot = plot
end
def to_s
"Personal information:
(#{#firstName}, #{#lastName}, #{#address}, #{#phone}, #{#arrived}, #{#departure}, #{#plot})"
end
end
require 'ruby_camping'
require 'camping_guests'
class Main
if __FILE__ == $0
$camping = Camping.new(#current_guests, #all_guests, #parking_lots,#staticGuests)
puts "\n"
puts "Welcome to Ruby Camping!"
while (true)
Menu.menu
end
end
end
require 'date'
require 'camping_guests'
require 'guest'
class Menu
def initialize(guests = [])
#camping = Camping.new(guests)
end
def self.menu
puts "---------------------------"
puts " Menu"
puts " 1. Checkin"
puts " 2. Checkout"
puts " 3. List current guests"
puts " 4. List all guests"
puts " 5. Exit\n"
puts ""
puts " What do you want to do?"
puts "---------------------------"
print ": "
action = get_input
do_action(action)
end
# fetches menu choice and returns chosen alternativ
def self.get_input
input = gets.chomp.to_i
while (input > 5 || input < 1) do
puts "Ooups, please try again."
input = gets.chomp.to_i
end
return input
end
def self.do_action(action)
case action
when 1:
check_in
when 2:
check_out
when 3:
puts $camping.current_guests
when 4:
puts $camping.all_guests
when 5:
puts "You are now leaving the camping, welcome back!"
exit
end
end
def self.check_in
puts "Welcome to the checkin"
puts "Please state your first name: "
firstName = gets.chomp
puts "Please state your last name:"
lastName = gets.chomp
puts "Write your address: "
address = gets.chomp
puts "and your phone number: "
phone = gets.chomp
puts "finally, your arrival date!"
arrived = gets.chomp
newPLot = $camping.generateParkingLot
newGuest = Guest.new(firstName, lastName, address, phone,arrived,$camping.parking_lots[newPLot-1])
$camping.current_guests[newPLot-1] = newGuest
#all_guests.push(newGuest)
puts "The registration was a success!! You have received the " + newPLot.to_s + "."
end
def self.check_out
puts "Welcome to checkout!"
puts $camping.all_guests
puts "State plot of the person to checkout!"
plot = gets.chomp.to_i
puts "Ange utcheckningsdatum: "
departureDate = gets.chomp.to_i
guest = $camping.current_guests[plot-1]
#departure = departure
guest.departure = departureDate
guestStayedDays = departureDate - guest.arrived
guest.plot.increase(guestStayedDays)
puts guest
$camping.current_guests[plot-1] = nil
end
end
class Parking_Lot
attr_accessor :nr
attr_reader :electricity_meter
def initialize (nr)
#nr = nr
#electricity_meter = 4000-rand(2000)
end
def increase_meter(days)
generatedUse = (10+rand(70))*days
puts "Increases the meter with " + generatedUse.to_s + " kWh."
#electricity_meter += generatedUse
end
def to_s
"Plot #{#nr+1} Electricity meter: #{#electricity_meter} kWh"
end
end
It looks (although I haven't tried this out) like some of your your 'end's are wrong.
The one which is causing your first error (generateParkingLot undefined) is that generateParkingLot is actually defined inside to_s, so you need an extra 'end' at the end of your to_s method.
As for the second error (departure not recognised), the folowing line in self.check_out is at fault:
#departure = departure
because there is no 'departure' variable. (Perhaps you meant DepartureDate?). I suspect there may be a few other issues with this code, but I'm afraid I don't really have time to check now.
One I noticed was that when you have
32.times do |nr|
#parking_lots[nr] = Parking_Lot.new(nr)
I think you might want to end that, either with an 'end' or curly brackets, e.g.
32.times do |nr|
#parking_lots[nr] = Parking_Lot.new(nr)
end
Although that would make your other blocks not match.. In general, just try and make sure your blocks are all defined properly (e.g. everything has a matching end).
class ArrayMine < Array
def join( sep = $,, format = "%s" )
collect do |item|
sprintf( format, item )
end.join( sep )
end
end
=> rooms = ArrayMine[3, 4, 6] #i couldn't understand how this line works
print "We have " + rooms.join( ", ", "%d bed" ) + " rooms available."
i have tried the same with String, but it comes up with an error.
thank you...
ArrayMine inherits from Array and you can initialize Ruby arrays like that.
>> rooms = Array[3,4,6]
=> [3, 4, 6]
is the same as
>> rooms = [3,4,6]
=> [3, 4, 6]
Also the quite strange looking def join( sep = $,, format = "%s" )
is using the pre-defined variable $, that is the output field separator for the print and Array#join.
It could also have been done like this
rooms=["b",52,"s"]
print "We have " + rooms.map{|s| s.to_s+" bed"}.join(", ") + " rooms available."
The reason you can't do what you are trying to do with String is because assignment is not a class method but [] on Array is. Just new it instead.
>> s = Substring.new("abcd")
=> "abcd"
>> s.checking_next
=> "abce"
You can't override assignment in Ruby, it's only so that setter methods looks like assignment but they are actually method calls and as such they can be overridden.
If you are feeling like being tricky and still want a similar behaviour as a=SubArray[1,2,3] you could create a << class method something like this:
class Substring < String
def next_next()
self.next().next()
end
def self.<<(val)
self.new(val)
end
end
>> sub = Substring<<"abcb"
=> "abcb"
>> sub.next_next
=> "abcd"
>> sub<<" the good old <<"
=> "abcb the good old <<"
>> sub.class<<"this is a new string"
=> "this is a new string"
For String, change %d bed to %s bed
irb(main):053:0> rooms = ArrayMine["a","b","c"]
=> ["a", "b", "c"]
irb(main):055:0> print "We have " + rooms.join( ", ", "%s bed" ) + " rooms available."
We have a bed, b bed, c bed rooms available.=> nil
You're just invoking a [] class method:
class Spam
def self.[]( *args )
p args
end
end
>> Spam[3,4,5]
[3, 4, 5]