Objected-oriented Programming, Part 1
Published on 21 Dec 2016
by Alexander Garber
I am working on my first text-adventure game as part of Exercise 43 of Learn Ruby the Hard Way. I
have defined a simple class called Scene:
class Scene
def enter()
puts "This scene is not yet configured. Subclass it and implement enter()."
exit(1)
end
end
In other words, the only thing that "Scenes" have in common is that the player "enters" them. What she does and whither she goes thereafter has to be defined.
Within the game there are broadly speaking two kinds of scenes:
- Scenes outside the house
- Scenes inside the house
Scenes Outside the House
The very limited movements afforded at the start of the game serve to illustrate the point that not a lot of thought went into (or had to go into) defining how you
navigate the first part of the map.
Meadow => River
Meadow => Front door
River => Front door
Front door => riddle => Ground floor of the house.
Scenes Inside the House
This is where things get tricky. All the rooms in the house are connected by a staircase, which means that when you leave one room, you have access to all the
others by means of the staircase.
At first I defined the method for getting up the stairs from the ground floor entrance composed of the following elements:
- Text for the player
- Declared value for floorNumber
- While loop
- Case conditional that calls the engine.
def enter()
"""
You are at a spiral staircase. There is a sign on the landing:
0. Ground Floor: Entrance to the castle
1. First Floor: Machine room
2. Second Floor: Kitchen
3. Third Floor: Library
4. Fourth Floor: Royal Bedroom
5. Penthouse: Study
6. Exit the castle and go back to the meadow.
Where would you like to go to?
"""
currentFloor = 0
while currentFloor == 0
print "> "
floorSelection = $stdin.gets.chomp.downcase()
case floorSelection
when /0/, /ground/, /entrance/
if currentFloor == 0
puts "You are already here. Try another floor."
elsif currentFloor != 0
puts "You descend the stairs to the ground floor."
return 'spiral_staircase'
else
puts "Something has clearly gone wrong."
end
when /1/, /first/, /machine/
if currentFloor < 1
puts "You ascend the stairs to the machine room."
currentFloor == 1
return 'machine_room'
elsif currentFloor > 1
puts "You descend the stairs to the machine room."
currentFloor == 1
return 'machine_room'
elsif currentFloor == 1
puts "You are already here. Try another floor."
else
puts "There has clearly been a mistake somewhere."
end
when /2/, /second/, /kitchen/
if currentFloor < 2
puts "You ascend the stairs to the kitchen."
currentFloor == 2
return 'kitchen'
elsif currentFloor > 2
puts "You descend the stairs to the kitchen."
currentFloor == 2
return 'kitchen'
elsif currentFloor == 2
puts "You are already here. Try another floor."
else
puts "There has clearly been a mistake somewhere."
end
when /3/, /third/, /library/
if currentFloor < 3
puts "You ascend the stairs to the library."
currentFloor == 3
return 'library'
elsif currentFloor > 3
puts "You descend the stairs to the library."
currentFloor == 3
return 'library'
elsif currentFloor == 3
puts "You are already here. Try another floor."
else
puts "There has clearly been a mistake somewhere."
end
when /4/, /fourth/, /bedroom/
if currentFloor < 4
puts "You ascend the stairs to the bedroom."
currentFloor == 4
return 'bedroom'
elsif currentFloor > 4
puts "You descend the stairs to the bedroom."
currentFloor == 4
return 'bedroom'
elsif currentFloor == 4
puts "You are already here. Try another floor."
else
puts "There has clearly been a mistake somewhere."
end
when /5/, /fifth/, /study/, /penthouse/
if currentFloor < 5
puts "You ascend the stairs to the study."
currentFloor == 5
return 'study'
elsif currentFloor > 5
puts "You descend the stairs to the study."
currentFloor == 5
return 'study'
elsif currentFloor == 5
puts "You are already here. Try another floor."
else
puts "There has clearly been a mistake somewhere."
end
when /6/, /leave/, /exit/, /back/, /meadow/
if currentFloor != 0
puts WordWrap.ww "You descend the stairs, walk out the door, and keep going until the you reach the edge of the meadow."
currentFloor == 0
return 'meadow'
elsif currentFloor == 0
puts "You walk out the door, and keep going until the you reach the edge of the meadow."
return 'meadow'
else
puts "There has clearly been a mistake somewhere."
end
else
puts "I don't understand your request"
end
end
end
But copy-pasting all of this would violate the DRY principle, so I thought about how to write it better:
- All 'Scenes' use the enter() function.
- All the rooms are 'Scenes'.
- All the rooms are 'Scenes' that require the same function for selecting a floor.
- Ergo, all the rooms are 'Scenes' use a function that could be defined as 'selectFloor()'.
- However, every room is located on a particular floor, which changes whether the player goes up or down the stairs, or is already at that floor.
- Ergo, every 'House Scene' needs to inherit the function 'selectFloor()' but with a variable for its floor, i.e. 'selectFloor(floorNumber)'
class HouseScene < Scene
def floorSelection()
puts "This scene is not yet configured. Subclass it and implement floorSelection()."
exit(1)
end
end
But remember that the floorSelection function is a while loop, so it needs a constant to hold true while it runs. The obvious candidate is the floor number, so the function takes an argument and starts like this:
def floorSelection(floorNumber)
puts "Filler text for the moment"
currentFloor = floorNumber
while currentFloor == floorNumber
From there it's simply a matter of pasting the rest of the floorSelection(floorNumber) function into the sub-class 'HouseScene'.
Then, for each room, call the functions:
- enter()
- floorSelection()
This slightly more complex code brings me finally to the point where testing my program becomes a drag. Until now, I could effortlessly run a straightforward program from start to finish; now, I have many potential routes to follow, and testing them all will take exponentially more time as I add games and puzzles to each room.
In my next post I will share my first experience of testing and Rake files in Ruby.