I want to access the ogre's object's swings attribute from the Human's class. However, all I am getting is:
NameError: undefined local variable or method ogre for
**<Human:0x007fdb452fb4f8 #encounters=3, #saw_ogre=true>
Most likely a simple solution, and my brain is just not operating this morning. I am running tests with minitest. The test and classes are below:
ogre_test.rb
def test_it_swings_the_club_when_the_human_notices_it
ogre = Ogre.new('Brak')
human = Human.new
ogre.encounter(human)
assert_equal 0, ogre.swings
refute human.notices_ogre?
ogre.encounter(human)
ogre.encounter(human)
assert_equal 1, ogre.swings
assert human.notices_ogre?
end
ogre.rb
class Ogre
attr_accessor :swings
def initialize(name, home='Swamp')
#name = name
#home = home
#encounters = 0
#swings = 0
end
def name
#name
end
def home
#home
end
def encounter(human)
human.encounters
end
def encounter_counter
#encounters
end
def swing_at(human)
#swings += 1
end
def swings
#swings
end
end
class Human
def initialize(encounters=0)
#encounters = encounters
#saw_ogre = false
end
def name
"Jane"
end
def encounters
#encounters += 1
if #encounters % 3 == 0 and #encounters != 0
#saw_ogre = true
else
#saw_ogre = false
end
if #saw_ogre == true
ogre.swings += 1 # <----issue
end
end
def encounter_counter
#encounters
end
def notices_ogre?
#saw_ogre
end
end
The easy fix would be to pass the ogre object as an argument to encounters - assuming encounters isn't used anywhere else without the argument.
class Ogre
...
def encounter(human)
human.encounters(self)
end
...
end
class Human
...
def encounters(ogre)
#encounters += 1
if #encounters % 3 == 0 and #encounters != 0
#saw_ogre = true
else
#saw_ogre = false
end
if #saw_ogre == true
ogre.swings += 1 # <----issue
end
end
...
end
Related
I am doing a practice problem black jack card game in ruby, references I am using are
https://medium.com/quick-code/using-ruby-classes-to-implement-a-game-of-blackjack-535a786c417
I am getting error that says undefined method "make_card"
Code for my Deck Class
class Deck
def initialize
#faces = [*(2..10),'Jack','Queen','King','Ace']
#suits = ['clubs','spades','hearts','diamonds']
#cards = []
#faces.each do |face|
if face.class == "Integer"
value = face
elsif face == 'Ace'
value = 11
else
value = 10
end
#suits.each do |suit|
#cards << Card.new(face,suit,value)
end
end
#cards.shuffle!
end
def make_card(participant)
fresh_card = Card.new(face,suit,value)
participant.turn << fresh_card
participant.total = participant.total + fresh_card.value
end
def deal(number,participant)
number.times{#cards.shift.make_card(participant)}
end
end
As both methods are in same class I am still getting that error
Solved it by placing method in Card class and accessing it via its instance variable
class Card
attr_accessor :face, :suit, :value
def initialize(face,suit,value)
#face = face
#suit = suit
#value = value
end
def make_card(participant)
fresh_card = Card.new(face,suit,value)
participant.turn << fresh_card
participant.total = participant.total + fresh_card.value
end
end
class Deck
def initialize
#faces = [*(2..10),'Jack','Queen','King','Ace']
#suits = ['clubs','spades','hearts','diamonds']
#cards = []
#faces.each do |face|
if face.class == Integer
value = face
elsif face == 'Ace'
value = 11
else
value = 10
end
#suits.each do |suit|
#cards << Card.new(face,suit,value)
end
end
#cards.shuffle!
end
def deal(number,participant)
number.times{#cards.shift.make_card(participant)}
end
end
I have this code which is not giving the desired output and shows the above error.
require 'gosu'
def media_path(file)
File.join(File.dirname(File.dirname(
__FILE__)),'media', file)
end
#def media_path(file); File.expand_path "media/#{file}", File.dirname(__FILE__)
# end
class Explosion
FRAME_DELAY = 10 # ms
SPRITE = media_path('space.png')
def self.load_animation(window)
Gosu::Image.load_tiles(
window, SPRITE, 128, 128, false)
end
def initialize(animation, x, y)
#animation = animation
#x, #y = x, y
#current_frame = 0
end
def update
#current_frame += 1 if frame_expired?
end
def draw
return if done?
image = current_frame
image.draw(
#x - image.width / 2.0,
#y - image.height / 2.0,
0)
end
def done?
#done ||= #current_frame == #animation.size
end
private
def current_frame
#animation[#current_frame % #animation.size]
end
def frame_expired?
now = Gosu.milliseconds
#last_frame ||= now
if (now - #last_frame) > FRAME_DELAY
#last_frame = now
end
end
end
class GameWindow < Gosu::Window
BACKGROUND = media_path('image.jpg')
def initialize(width=800, height=600, fullscreen=false)
super
self.caption = 'Hello Animation'
#background = Gosu::Image.new(
self, BACKGROUND, false)
#animation = Explosion.load_animation(self)
#explosions = []
end
def update
#explosions.reject!(&:done?)
#explosions.map(&:update)
end
def button_down(id)
close if id == Gosu::KbEscape
if id == Gosu::MsLeft
#explosions.push(
Explosion.new(
#animation, mouse_x, mouse_y))
end
end
def needs_cursor?
true
end
def needs_redraw?
!#scene_ready || #explosions.any?
end
def draw
#scene_ready ||= true
#background.draw(0, 0, 0)
#explosions.map(&:draw)
end
end
window = GameWindow.new
window.show
It can't find ./media/image.jpg are you sure it is there, and can be opened, and is the current directory "." what you think it is?
What's the difference that the parameter "amount" being defined to 0 has on the overall instance creation in this context, since the code below the commented line does the same thing with out "amount=0"?
class Account
attr_accessor :balance
def initialize(amount=0)
self.balance = amount
end
def +(x)
self.balance += x
end
def -(x)
self.balance -= x
end
def to_s
balance.to_s
end
end
acc = Account.new(20)
acc -= 5
puts acc
class Account
attr_accessor :balance
def initialize(amount)
self.balance = amount
end
def +(x)
self.balance += x
end
def -(x)
self.balance -= x
end
def to_s
balance.to_s
end
end
acc = Account.new(20)
acc -= 5
puts acc
I'm a beginner. Thanks for any help!
Specifying amount = 0 in the parameter list make the amount parameter become optional (with 0 as its default value).
If you don't specify amount argument, it will be 0.
account = Account.new # without amount argument
account.balance # => 0
account = Account.new 10 # with amount argument
account.balance # => 10
The only difference is:
The first class sets the default value
class Account
attr_accessor :balance
def initialize(amount=0)
self.balance = amount
end
... class omitted
end
account = Account.new
account.balance # should be equal to 0
class Account
attr_accessor :balance
def initialize(amount)
self.balance = amount
end
... class omitted
end
account = Account.new nil
account.balance # should be equal to nil
Is there a way to define an object that can be coerced to either a String or a Fixnum or a Float? This is for use in a system that collects values and evaluates a restricted set of simple expressions with them.
I tried:
class EmptyValue < Numeric
def to_s; ''; end
def to_str; ''; end
def to_i; 0; end
def to_int; 0; end
def to_f; 0.0; end
end
but this fails for
1 + e
TypeError: EmptyValue can't be coerced into Fixnum
A little more poking and this worked for all my use cases:
class EmptyValue < Numeric
def to_s; ''; end
def to_str; ''; end
def to_i; 0; end
def to_int; 0; end
def to_f; 0.0; end
def +(other)
case other
when String
to_s + other
when Fixnum
to_i + other
when Float
to_f + other
end
end
end
This is what I've done long time ago:
class NullObject
attr_reader :null_object_type, :recorded_messages
alias ρ recorded_messages
def initialize( type_of_null_object = nil )
#null_object_type = type_of_null_object
#recorded_messages = []
end
def null_object? null_type = nil
null_object_type == null_type
end
alias null? null_object?
def to_a; [] end
def to_s; "null #{null_object_type}".strip end
def to_f; 0.0 end
def to_i; 0 end
def present?; false end
def empty?; true end
def blank?; true end
def inspect
"NullObject #{null_object_type}".strip
end
def method_missing ß, *aj, &b # :nodoc:
#recorded_messages << [ ß, aj, b ]; self
end
def respond_to? ß, *aj, &b # :nodoc:
true
end
protected
def == other # :nodoc:
null_object_type == other.null_object_type
end
end # class NullObject
# Strong zero.
#
ZERO = NullObject.new
ZERO.instance_exec {
ɪ = self
singleton_class.class_exec do
define_method :zero do ɪ end
end
def * other; other.class.zero end
def / other
self unless other.zero?
raise ZeroDivisionError, "The divisor is zero! (#{other})"
end
def + other; other end
def - other; -other end
def coerce other
return other, other.class.zero
end
def zero?; true end
def to_s; "∅" end
def inspect; to_s end
def to_f; 0.0 end
def to_i; 0 end
def == other
z = begin
other.class.zero
rescue NoMethodError
return false
end
other == z
end
}
class << Numeric; def zero; 0.0 end end
class << Integer; def zero; 0 end end
class << Float; def zero; 0.0 end end
class << Rational; def zero; Rational 0, 1 end end
class << Complex; def zero; Complex 0, 0 end end
class << String; def zero; '' end end
class << Array; def zero; [] end end
class << Hash; def zero; {} end end
So, now you have 0 + ZERO #=> 0. I called this "strong zero". But it was a hack. I have a gut feeling, that this is not such a good practice.
I am making a short, text-based game as an extra credit exercise based on the Ruby I have learned so far and I'm having trouble getting classes to read and write variables between each other. I have read extensively and searched for clarification on how to do this but I haven't had much luck. I have tried using # instance variables and attr_accessible but I can't figure it out. Here is my code so far:
class Game
attr_accessor :room_count
def initialize
#room_count = 0
end
def play
while true
puts "\n--------------------------------------------------"
if #room_count == 0
go_to = Entrance.new()
go_to.start
elsif #room_count == 1
go_to = FirstRoom.new()
go_to.start
elsif #room_count == 2
go_to = SecondRoom.new()
go_to.start
elsif #room_count == 3
go_to = ThirdRoom.new()
go_to.start
end
end
end
end
class Entrance
def start
puts "You are at the entrance."
#room_count += 1
end
end
class FirstRoom
def start
puts "You are at the first room."
#room_count += 1
end
end
class SecondRoom
def start
puts "You are at the second room."
#room_count += 1
end
end
class ThirdRoom
def start
puts "You are at the third room. You have reached the end of the game."
Process.exit()
end
end
game = Game.new()
game.play
I want to have the different Room classes change the #room_count variable so that Game class knows which room to go to next. I am also trying to do this without implementing class inheritance. Thanks!
class Room
def initialize(game)
#game = game
#game.room_count += 1
end
def close
#game.room_count -= 1
end
end
class Game
attr_accessor :room_count
def initialize
#room_count = 0
end
def new_room
Room.new self
end
end
game = Game.new
game.room_count # => 0
room = game.new_room
game.room_count # => 1
room.close
game.room_count # => 0