Reputation: 61
I'm working on a Ruby project for a text based adventure game and have encountered an issue when trying to use the 'find' method in Ruby.
I've got a location class that all locations are instantiated from. Each location has a set of two coordinates, ([x, y] = [1, 1]) and the player also has their own set of coordinates to store where they are actually located.
The class method & initialise method
def self.findLocation(coord, attributeToPrint)
puts coord.inspect
puts @@finder.find { coord }.inspect
end
def initialize(newTitle, newDescription, newCoord)
@title = newTitle
@description = newDescription
@coordinate = newCoord
@@finder << self
end
What I'm trying to do is store all of the locations in an array and have a class method for printing out the title and description of a location by using the find method to select the location with the matching coordinates to the player. The method I currently have passes the player's coordinate in coord
parameter and uses the find method to check the array (which has all location objects within it) for the coordinate.
I've found many questions relating to this method but have had no success with any of the solutions found on these questions and no luck with any solution of my own. If I try and use a comparison statement such as @coordinate == coord
the method will simply return nil
and my current version returns an object, but only the object which is first in the array and does not return the location with the matching @coordinate
attribute.
I would greatly appreciate any help with this issue as it is the main roadblock to making some progress on the text adventure game and allowing some interactivity. I am sure that I am using this method incorrectly and don't understand how it functions but the enumerator documentation hasn't helped me very much after looking at it and there is possibly a much better way of implementing this over a class method.
class Location
@@finder = Array.new
def self.findLocation(coord, attributeToPrint)
puts coord.inspect
puts @@finder.find { coord }.inspect
end
#Initialise locations here
def initialize(newTitle, newDescription, newCoord)
@title = newTitle
@description = newDescription
@coordinate = newCoord
@@finder << self
end
class Player
def initialize(playerHealth, playerLocation, playerInventory)
@health = playerHealth
@location = playerLocation
@inventory = playerInventory
end
require_relative '../lib/player'
require_relative '../lib/location'
start = Location.new('Test 1', 'This is test 1.', [0, 0])
start2 = Location.new('Test 2', 'This is test 2.', [1,1])
start3= Location.new('Test 3', 'This is test 3.', [2, 2])
player = Player.new(100, [1,1], ['sword'])
#Input loop
loop do
Location.findLocation(player.getLocation, 'example')
end
Upvotes: 1
Views: 88
Reputation: 36860
You have to specify how find
will match the stored records against the provided value. Specifically, you need to compare coord
to the record's coord. To access the record's coord you need a getter method.
class Location
def self.findLocation(coord, attributeToPrint)
puts coord.inspect
puts @@finder.find { |location| location.coord == coord }.inspect
end
def coord
@coord
end
end
The way that find
works is that it executes the block for every instance in the array, returning the first array element where the result is 'truthy' (i.e. not nil and not false). When you do { coord } the block returns the coord
value immediately. coord
is not nil and not false, so the first record is selected. When you did @coord == coord
the @coord
is undefined at the class level (it's nil) and so for all records the comparison was false so no record was selected, hence your nil result.
To print a specific attribute (say, title
) you can also access the attribute with the getter method. and then send that method to the object.
class Location
def self.findLocation(coord, attributeToPrint)
puts coord.inspect
found_location = @@finder.find { |location| location.coord == coord }
puts found_location.send(attributeToPrint) if found_location
end
def coord
@coord
end
def title
@title
end
end
So now you can do...
Location.findLocation([1,1], 'title')
However...
It's much more flexible to have findLocation only be responsible for returning the object and then output the attribute outside the method... single responsibility principle...
class Location
def self.findLocation(coord)
@@finder.find { |location| location.coord == coord }
end
def coord
@coord
end
def title
@title
end
end
So now you can do...
player_location = Location.findLocation([1,1])
puts player_location.title if player_location
Upvotes: 1