Assignment 2: Classes and Methods
Errata
- Due date changed to 19-Oct.; the due date for Assignment 3 may get pushed back a bit, but don’t count on it.
- By student request, see below at question/answer 8 for what happens during a tick.
- In the directions below, I note that the World::Map#clear method iterates through all of the members of the map. Our student B. pointed out to me that the #clear method was iterating throught the y’s and then, in the inner loop, through the x’s. But our tests that check #each and #each_with_coordinates do it the other way. Therefore, I’veĀ changed World::Map#clear so that it clears as so:
def clear (@x_min..@x_max).each do |x| (@y_min..@y_max).each do |y| self[x, y] = [] end end endYou can get the updated download from the downloads page.
This iteration strategy fits the test; in other words, when you’re implementing each, consider looping through the elements this way. B. also suggested that we might not need a defined order in which the elements should be enumerated. What do you think? In this assignment, there is an order, but maybe we can imagine a Map as something more like a Set with no specific order.
The purpose of this assignment is to give you some experience with classes (and modules) and methods, and, in particular, classes that must fit into the requirements of a framework. In this project, your code is called by other code. It isn’t standalone.
The basic idea of the code is that there is a Map, and Players move around on the Map. Also on the Map are Pylons, which are essentially obstacles. The Pylons can affect the movement of the Players. There is a ticker that ticks, and at each tick, a GameManager is notified; it, in turn, notifies each of the Players that a tick has happened. The Player can then respond to its environment, and request a change of position. Your work is mostly about Pylons and Players, though the first task regards the Map.
The inspiration for this assignment were some photographs and maps regarding Ariel Pylon Racing. Here’s an example course:

(from Society of Air Racing Historians)
And here’s a picture of a plane rounding a Pylon:

(from Reno Pylon Racing School)
In a nutshell, you have five tasks to accomplish (more detail near the end):
- Implement each and each_with_coordinates methods on the World::Map class (and pass tests). Then you will include Enumerable into this class. This is key for the other tasks.
- Implement some Pylons and Players (and pass tests).
- Invent a Pylon of your own design.
- Invent a Player of your own design.
- Write briefly about the design choices you made.
The download has a number of classes in the lib/world/ directory that manage the game and animate the screen with the ancient and venerable “Curses” library (to learn more, see Wikipedia and the Ruby Cookbook at Safari — you will not need to do any raw Curses programming yourself [--you're welcome :-)]). Open the download, and in the project root, try
ruby example_game.rb
You should see something like this:

Each of the single characters is some kind of Entity in the game. Some are stationary; those are the Pylons. The moving ones are the Players. The Players 1, 2, and 3 are instances of the ExamplePlayer class (for this and others, see lib/world/example_entities.rb). @ is a SlowPlayer (the @ is supposed to evoke a snail), and # is a BlinkyPlayer (not shown in the image above because it’s currently blinked off). The | and = Pylons “reflect” players when they are hit. Notice that Player 3 is relatively unconstrainted, and when it hits an edge, it comes out on the other side: Thus the coordinate system “wraps around.”
Once in the game, there are some commands you can type. Each one is a single key-press:
q: quit the game.
p: pause the game. Once paused, press t to go to the next tick.
t: go to the next tick (only works when game paused).
r: resume from pause.
While the game is running, you should also take a look at the log. To do this, start up two terminal windows. In one window, run the game. In the other window, do
tail -f info.log
“tail -f” means show the end of the file, but “follow” it as new lines are added. By this means, you can see the log grow and grow, focusing on the newest information. The tail command is standard on Unix/Linux, and comes with the Windows Cygwin package. The log will look something like this (truncated on the right):

Before I get into the code, I want you to notice some things about the log. The different components identify their log lines with a prefix. For example, there’s a line prefixed with “GM” (GameManager), that gives information about the internal data structures at a particular tick. The entire map state is logged for each tick. Notice that the big map of positions is centered at 0, 0, and extends from negative to positive coordinates in each direction. In the example game, there are five Players. Each Player gets a limited view of the whole map, centered on the Player itself. So in what you see above, Player 1 sees only a 3×3 grid (-1..1, -1..1) centered on itself. Just to its north-west is Player 3. Player 3 sees the same thing (not shown), but centered on himself. In the views of the maps (both the whole map and the Player-oriented sub-views), if there are two Entities on the same location, you will see a +. Finally, you may have observed that the views “wrap around”; if a Player is in the very upper-right of the map, the rows above the Player will actually be at the bottom of the whole map, and the columns at the right of the player in the limited view will wrap around to the far left side. The upshot of this is that no Player knows exactly where it is; it only has a limited, relativistic view of its position on the map.
One more key piece of information about the Map class and views: At each coordinate of the Map, the GameManager stores an Array. The Array contains all of the Entities at that location. The Array may be empty. Thus, when Player#accept_tick is called, and the Player is given a view, if you want to figure out what players are where, you need to iterate through view, but also through each Array at each location. This point — that at each coordinate in a view is an Array of all of the Entities at that location — is key for implementing the ShyPlayer.
The GameManager manages Entities (you may find the class definitions for Entity, Pylon, and Player in lib/entity.rb). An Entity is initialized with a reference to a GameManager, a preferred starting x coordinate, and a preferred starting y coordinate.
There are two kinds of Entities, Pylons and Players. As I have said above, Pylons are like obstacles. They don’t move, but they can affect the movement of Players. Players move, and they also affect the movement of other Players.
Here is the key information regarding Entities, Pylons, and Players (and some related topics, such as directions). Remember that Pylon and Player both subclass Entity, so anything I say about Entity applies to Pylon and Player as well.
- In the source code for Entity, notice that during initialization, GameManager#setup_entity is called. The reason for this is that Entities don’t get to determine their own location and direction state; then have to ask the GameManager to do it for them. If you look at GameManager#setup_entity, you will see that it picks a location, records the entity and its location, and, finally, if the Entity is a Player, calls #setup back on the entity. When it calls back to the entity, it gives the entity its direction, as well as the default radius. Later on we will see that the Player never knows where it is; it only gets passed “views” centered on itself at a certain radius. It never sees the entire Map.
- When the GameManager sees that a Player is running into another Entity, it calls #modify_requested_move on the “target” entity. This gives the target entity a chance to change the direction of whatever is about to crash into it. For example, the target entity might “reflect” (change the direction to the opposition direction). It is also possible to suggest new coordinates as well as change the direction. Read the comments in the source.
- Each Entity has an “identity” — this is a one-character String that is supposed to be unique through the lifetime of the Entity. Each Entity also has an “appearance.” This is also one-character, but the Entity is allowed to change its appearance whenever it wants. The appearance is what is shown on the Map.
- An Entity can broadcast an information message (with the message method) which will be displayed on the screen so that a human user can be told that something interesting has happened.
- A Pylon is really the same as an Entity, but I decided to just subclass Entity should I want to give any special characteristics later on to Pylons.
- Now, Players. Players have a direction (see next item) and a visual radius. Player#setup is called by the GameManager to start the Player its official direction and visual_radius. The player never sets these directly — it makes proposals to the GameManager, the GameManager evaluates it, and then the GameManager updates the player.Player#request_move calls the GameManager to ask for a specific new direction. Player#request_move is called by Player#accept_tick.Player#accept_tick is called at regular intervals by the GameManager. There are two values that are passed in: The tick_number, and the view. Based on this information, Players can make fun decisions about what kind of move they want to request.
- Directions: There are eight values for directions, World::NORTH, World::NE, World::EAST, World::SE, World::SOUTH, World::SW, World::WEST, World::NW. (In the implementation, these are “Locations,” which are just two-value pairs, so that World::NORTH is actually just the pair (0, +1) meaning that going North leaving the x coordinate the same, and incrementing the y coordinate.
If you turn your attention to entity_examples.rb, you’ll see some example Pylons and Players. For HorizontalPylon, the only method that is altered is modify_requested_move. See how it works? It looks up the approaching Entity’s direction in the NEW_DIRECTION Hash. If the approaching Entity is moving EAST, then it returns a new direction of WEST. And if WEST, it returns EAST. By this means, it bounces approachers in the opposite direction. VerticalPylon does the same thing for the other access. That’s about the only method you can customize for a Pylon, aside from controlling the appearance. (In this version, Pylons don’t accept ticks — we might do that in the future.)
Questions and Answers:
- Q: My player isn’t moving!
A: accept_tick needs to call request_move(@direction). If you’re not doing that in your implementation, then call super - Q: I changed the tick_interval in the Ticker class to be very small, and nothing works.
A: The smallest interval we tested was 0.1. - Q: The WormHolePylon is required to make Players jump to a random location. How do I do that?
A: While Entities don’t know where they are, they have a reference to the GameManager, and on the GameManager, there are methods random_x and random_y. By this means, you can have modify_requested_move return a suggested location that is random_x, random_y - Q: I would like to have my Players affect one another directly. Can I do that?
A: Yes. When accept_tick is called on a Player, it is passed a view (a subset of the big map), as discussed above. You can iterate through the map, examine what’s at each coordinate, and check to see if any of the Entities at that coordinate are of a particular class, or “respond to” a method that interests you; and, if you wanted to, you could call such methods. Clearly, this would only be appropriate for Players you’ve designed yourself, but it’s fair game. NOTE: You might want to think about what kind of access you provide to other players: Should methods open to other players of the same class be public, protected, or private? - Q: What about Pylons? I’d like to write a Pylon that can change the state of a Player.
A: A Pylon is passed a reference to an Entity in modify_requested_move, so why not? - Q: I’m having a hard time with the ShyPlayer, figuring out whether it has come near other Players. What’s going on?
A: When accept_tick is called, a Player is given a limited view of the Map; this limited view is centered on the Player, that is, the Player is at 0, 0. Each location is itself an Array, because more than one Entity can be at the same spot. So, to figure out what’s around, you would need to iterate through the view, and then, for each location, iterate through its Array. Then for each Array element, you would want to determine whether the element is a (use the is_a? method) Player or an Entity. - Q: I’m stumped for what custom Pylon(s) and Player(s) I should write.
A: Here are some ideas: A Player that will “chase” any other player that comes into its view; a Player that will stop moving when it “sees” certain players; a Player that sends a message, changing its appearance successively to different letters (for example, “m” then “i” then “n” then “a” . . . minaswan!); Players that swarm; Pylons that turn themselves on and off based on what Players come in contact with them; Players that respond to “over-population”; A Pylon or Player that provides “interesting” debug information in the log about its history and interactions with other Entities. If you have other ideas, post them in the comments.One thing I would advise is: start simply, and debug each Pylon or Player as you go along.
- Q: What happens during a tick?
A: Some students have been writing Pylons and Players (or thinking about it), and are curious about the exact sequence in which events happen. Here’s how the GameManager (hereafter, GM) works (Remember that instance methods are prefixed with the pound sign (#) and class methods with the period (.)):You should read along in the code!
At periodic intervals, a Thread in the Ticker calls the GM’s #accept_tick method. #accept_tick iterates through all of the Entities in the order in which they were added to the GM. If an Entity is a Player, the Player’s #accept_tick method is called. by this means, at the periodic interval, actions are triggered in the GM and then in each Player. Understand that Players don’t all receive a particular tick “at the same time.” The Players get the ticks in Player order. The upshot of this is that a Player may NOT assume that the state of the GM when it accepts a tick is the way the game was before ANY other Players received that same tick; the game (that is, the state of the Pylons and Players on the Map) may have been changed by other Players who have gotten this very tick earlier.
The GM will call a Player’s #accept_tick method with two arguments: the tick_number and a view of the Map centered on the coordinates of the Player. Notice that for this view, the Player is at 0, 0. Players don’t know where they are in Map coordinates; they only get a “relative” view. The tick_number is important. If a Player wanted to keep a history of all prior views, it would want to save the view for each tick under that tick_number. I didn’t require that you do that, but it could be a neat trick to have a Player that has a memory of what has transpired. You could create a Player that has a specific behavior after it has “seen” some other specific Player more than a certain number of times.
So far we’ve focused on GM#accept_tick: By all means, look at the code. It’s pretty simple.
Now let’s turn to the Player.
The key to Player#accept_tick is that in the default implementation, it calls GM#request_move. This is very important. Players do not move themselves; they ask the GM for a new direction. When they ask for a new direction with #request_move, they can provide two arguments: the direction, and a flag regarding whether the move should be skipped. Notice that Player#accept_tick actually delegates to #request_move, and that #request_move calls the GM’s #request_move (with self as the first argument). OK?
Now the GM gets to think about what the Player has requested (look at the source for GameManager#request_move).
If skip_move is true, it just returns the current direction right back to the Player. So the Player just continues going in whatever direction it was before.
Otherwise:
First the GM picks a new location that is based on the direction that the Player requests. This is provisional — it’s what we’ll use if no Pylons or other Players make changes. For example, the Player might request to go WEST. The GM figures out the current location adjusted by stepping in the WEST direction. Again, this is the provisional new direction; nothing has happened yet.
Then the GM removes the Player from its old location. In the code it looks like this:
@map[old_location.x, old_location.y].delete(entity)
Once it’s gone, it can re-plot what’s at that old location. The Player isn’t there any more, so, typically, it will be a blank spot (empty Array), though it might be that there’s still a Pylon or another Player there, or maybe multiple entities. but in any case, we “plot” what’s at that old location:
plot(old_location, display(old_location.x, old_location.y))
(the #display method figures out the appropriate character to display.)
Now it’s time to decide the true new location of the Player.
The GM picks the “last” Entity (which we call the “dominant_entity”) to have been placed at that location:
if dominant_entity = @map[new_location.x, new_location.y].last modification = *dominant_entity.modify_requested_move(entity, direction) new_direction = modification[0] if modification.size == 3 new_location = World::Location.find(modification[1], modification[2]) end end[Arguably this is not the right way to do it; we could have designed this so that every Entity on a Map location gets to try to modify the Player's request, but that seemed too complicated to me.]
Notice that if @map[new_location.x, new_location.y].last evaluates to nil, the condition on the if statement will be false, and the code block won’t run. Also notice that an assignment of a value of dominant_entity is happening in the “if” statement; I personally use this idiom all the time.
So in any case, we call #modify_requested_move on the dominant_entity. This gives the dominant_entity a chance to change the direction of the Player. In the typical case, it returns one value, the direction. Additionally, it can return an x and y, which is why we splat the return value to get an Array, and handle either the direction alone (modification[0]), or the direction along with the specified x and y (in modification[1] and modification[2]).
So now the Player has a new location. We add the Player to the Map location, update the Hash that correlates Entities with their locations, and plot what’s at the updated location:
@map[new_location.x, new_location.y] << entity @entity_to_location[entity] = new_location plot(new_location, display(new_location.x, new_location.y))
Finally, we return the new_direction to the Player (it may not have gotten what it wanted!).
And that’s it.
Recap of your tasks / what you must hand in:
- Implement each and each_with_coordinates methods on the World::Map class (and pass tests). Then you will include Enumerable into this class. This is key for the other tasks.Hints: (a) Notice that in the implementation of World::Map (in lib/world/map.rb) that the clear method is going through all of the elements. Since an each method goes through all of the elements, you might get some ideas for how to implement each (and each_with_coordinates) from clear. (b) How do determine what is at a particular location on the map? See Map#at.
- Implement some Pylons and Players (and pass tests). Specifically: World::RightMirrorPylon, World::LeftMirrorPylon, World::WormHolePylon, and World::ShyPlayer. The specifications are in the documenation tree at doc/index.html in the download.
- Invent (a) Pylon(s) of your own design.
- Invent (a) Player(s) of your own design.
- Write briefly about the design choices you made in readme-student.txt.
All student code must go in lib/world/student_solution.rb and lib/student_game.rb! student_game.rb should be an example game (like example_game.rb in the project root) that illustrates what you’ve done. We will downgrade submissions that alter lib/world/game_manager.rb, lib/world/map.rb, etc. Take a look at lib/world/student_solution.rb: For the first task (implementing Map#each and Map#each_with_coordinates) we have provided a stub of the class; your code is essentially “monkeypatching” the existing version of Map in map.rb. Do not monkeypatch any other core classes.
All of these tasks are summarized in the doc/index.html file in the download, which is here: http://e168f09.plugh.org/extras/downloads/e168-assignment2-jgn-1.1.001.zip.
@john
well doesnt blow up. effectivly makes the other object invisible. the char doesnt change to '+' but the object does continue on its way now if I can freeze that player maybe pacman can still live in spirit.
@john
I don't see it change on any of the ticks. The way the sample game is set up, it should (I think) change for three ticks, while the player 'passes by' the shy player to its right, and it should loop like that (since the ExamplePlayer runs off the bottom of the screen back to the top, and passes by again, etc.) I only see my 'message's where it says it *should* be changing the appearance.
@john
Yep, it passes the tests.
@Brendan Shea
Brendan: Below is a link to a quick movie of my ShyPlayer in action. Note that I changed it so that when it's shy, it shows a "_" rather than a small s, because it will be easier to see on the screen. Also, when it's not shy, I put in: message "" to blank out the message regarding the change to the shy state.
This is with the ShyPlayer being created SECOND. i.e.,
It only changes for one tick because of the way the successive ticks are distributed to the Players. But it does change! If you create the ShyPlayer first, it show shyness for two ticks.
http://e168f09.plugh.org/extras/screencasts/shy-demo/
@Brian Mantenuto
That sounds pretty cool. It will be interesting to see what you can get out of it. I think Gary might be trying something similar.
In implementing my Shy Player, I'm taking a look at the View Analysis Player (V) first. In the log, my VAP log results differ from my "At Tick Player V sees" results (the little 3x3 tables).
I've been able to narrow it down to the fact that my VAP Log results are behaving as if my player has already moved (but not its neighbors); the log file and the GUI have not indicated any moves for V.
This may be similar to Brendan's issue but I'm not sure.
If/when I write extra unit tests for my new Player and Pylon, should I remove those before I submit, or hand them in along with everything else?
er... I have to ask if the due date is Oct 19th 00:00h or 23:59h or what... I'm really time strapped and need to set some priorities...
thanks,
naomi.
I read comments about WormHolePylon regarding tests. My WormHolePylon code is still failing to verify if my x,y and direction are distributed fairly evenly across the range of possibilities.
Here is the test output that is failing:
Perfect: 250
Observed: 75
Any hints on what am I missing here? Or is it just that my WormHolePylon is not implemented correctly?
@john
Thanks! I'll debug mine to see why my display doesn't update.
@john
Aha -- its obvious now what I was doing wrong. For some reason I had it in my head that a shy player didn't move, and instead just sat there and changed its appearance when somebody came by. However, if your player doesn't move, then display never gets called (line 171 in game_manager.rb). Duh... makes sense now. Thanks!
@Alexander Dawson
The position update, display update, and what you see in the log happen successively, so you may see what seem to be slight anomalies. If your ShyPlayer is "getting shy" occasionally, you're on track.
@Cat
Leave any extra tests in. We may delete them to verify that OUR tests work. We may not take a close look at your tests. If you feel that you've written an especially good test, mention it in your writeup.
@Naomi
Get your assignment submitted by 11:59 PM EST on Monday.
@kasula
The WormHoleTest, unfortuantely, is probabilistic, so will fail very occasionally. The Perfect/Observed ratio of 250/75 is an outlier. Is that occasional or seldom? If seldom, you're ok. If it's failing every time, you probably have a bug.
If your WormHoleTest is consistently failing, I suggest that you try the advice that I gave here in order to improve the failure error messages:
http://e168f09.plugh.org/assignments/assignment-2-classes-and-methods/comment-page-1/#comment-573
If necessary, temporarily comment out the assertions and uncomment the puts() calls, so you can see the entire distribution of values.
I think you can successfully implement a non-moving shy player if you give it a Direction of [0,0] , but still call request_move() or super from your accept_tick() method.
This is how I understood the code: In worm_hole_pylon_test.rb, we are initializing the game_manager with (2,2). Then the game_manager calculates the width and height as 5. One of the hints for WormHolePylon is to use GameManager#random_x, random_y methods. If I use these methods, there is a possibility that my new location can have x/y values 2 and -2.
Is there any requirement that the new location has to be one of the eight World::MOVES?
Semi-serious post: How much extra credit do we get for re-creating the "Game of Life"?
http://en.wikipedia.org/wiki/Conway%27s_Game_of_Life
The WormHolePylon's modify_requested_move() method must return both a new direction AND a new location (new x, new y). These are totally separate and independent from each other. The new location can be anywhere on the game manager's map. It need not be, and usually is not, one of the eight World;:MOVES values.
@kasula
Ron is right: The direction and the location are two different things.
Here's the documentation from lib/world/entity.rb:
# NOTE: VERY IMPORTANT. This method may return in two ways. It may # return just a direction; or it may return a direction, x, and y. # To return just the direction the implementation would be: # # def modify_requested_move(entity, direction) # direction # end # # or, if you wanted to return x, y coordinates as well as a # direction, it would look like: # direction, x, y # So, for instance, it might always return NORTH and 0, 0: # World::NORTH, 0, 0 # Or, more likely, return a direction and new coordinates that # are based on the entity's state. Remember that directions are # indicated with the constants World::NORTH, World::EAST, etc. #@Alexander Dawson
No extra credit for implementing Life, but you would get eternal fame 'cos I'd demo your implementation in lecture. I think it would be hard because there is no defined way to remove entities (though we have discussed workarounds above).
You might want to think about creating your own rules for a cellular automata simulation: something simpler.
@ Brendan Shea - this also means that if your player changes its appearance but then calls request_move with skip_move=true, the displayed appearance won't change. This is arguably a bug.
@Ron Newman
Er, feature?
@Ron Newman
Thanks Ron! Those comments led me to a bug in my method.
I'm not sure if it matter too much, but the new Rakefiles says:
PROJECT_HUMAN = 'Assigment 3 (Standalone ActiveRecord), Harvard CSCI E-168, Fall 2009'
PROJECT = 'e168-assignment3'
I think we may need to modify this before running "rake package" and upping our file.
@Donnie Demuth
Um, I just downloaded the ZIP for assignment 2, and the constants say:
From where are you getting your download? Note that there is a separately download for assignment 3. The ZIP file names look similar but they are different.
Thank you, Ron and John. I overlooked those comments.
@john
Oh god. Duh, that makes sense. I downloaded the wrong assignment.
I'm not sure why I had the assignment 3 package. It may have been a goof on my end. Any hoo, in order to "backport" some of my Players to the assignment 2 version I had to create a new class "HackedGameManager" that subclassed "GameManager" and allow me to access a few of the private methods. :-) I kind of had some Zombies that would eat players. Yum.
What are the exact semantics of each?
I'm confused about whether it's supposed to yield once for every x,y coordinate on the map or whether it's supposed to yield once for each entity in the map.
The specification for each_with_coordinates uses the phrase:
Calls _block_ once for each x, y coordinate
While the specification for each says:
Calls _block_ once for each element in the map
Does "element" mean the same thing as "x,y coordinate" or does element mean entity?
@David
I hear your pain.
Yield once for every x, y coordinate. What is yielded is whatever is at that x, y coordinate.
Element means: What is at x, y. It is not the coordinate. It is what is at the coordinate.
Here is what the rdoc says:
each()
map.each { |element| block } Calls block once for each element in the map, passing whatever is at that element.
[This is modeled on Array#each. The only real difference is that word "at" which is an implementation hint, but could be rephrased as: "passing that element as a parameter."]
each_with_coordinates()
map.each_with_coordinates { |element, x, y| block } Calls block once for each x, y coordinate in the map, passing whatever is at that element, the value of the x coordinate, and the value of the y coordinate.
"element" means whatever is at that x, y on the map. The Map is completely generic (with the exception of the #to_s method, which knows a little bit about Entities). What is at a particular x, y might be an integer; an Array; etc.
I am trying to implement a Stasis Pylon.
The Stasis Pylon would have 2 states:
1. It would be an 'I' before any player hits it (the 'I' is supposed to convey that it is skinny because it isn't holding a player)
2. After a player hits the Stasis Pylon, its appearance would change to an 'O' (indicating that it's holding a player) and it would hold on to the player for a set number of ticks (say, 10 ticks) then it would release the player, and its appearance would change back to an 'I'.
Still open question: what happens if the Stasis Pylon is hit by another player when it's in its pregnant state?
@Glenn Ritz
If you can get that implemented, that's pretty interesting right there -- so, for now, why don't you ignore all other players (if that's easiest). That way, you don't have to keep multiple tick-timers to release each different Player.
I thought about doing something like that, but didn't see an obvious way to do it since Pylons don't have an accept_tick() callback.
@Ron Newman
Good point. I guess you'd want to say: Holds on to that particular Player while X number of other Players crash into it.
I'm trying to remember if a Player gets modified_requested_move . . . at work so can't check, but that would be another way to do it: Let a Player "hold" another Player.
"Write briefly about the design choices you made in readme-student.txt"
What exactly we need to document? Is it about our Player and Pylon, or the student_game.rb?
@kasula
As I wrote in an e-mail which every student should have received (e-mail #27):
True, I think you could have a stationary Player that does what you want.
(John, maybe all the e-mails should go somewhere on this website, too?)
@Ron Newman
It's possible, but it's really risky because information would be appearing in two places. Some of the e-mail information is quasi-private to the course (e.g., coupon codes). I will re-emphasize the message that e-mail from me is important.
@john
My bad. It is my first distance course so I am having hard time keeping up with both emails and comments on the course site.
@kasula
I'm having a hard time keeping up, too. :-)
Can you delete an instance method on an object at run time?
@Glenn Ritz
I am imagining that you want to do this because you want to suppress some kind of behavior.
You can remove a method, but it sounds like what you want is to CHANGE what the method does.
One thing you can do is have an instance variable called, say, @skip
in initialize, set @skip = false
When you detect the state that concerns you, set @skip = true
Then in modify_requested_move you'd have:
if @skip
# do something
else
# do something else
end
Now you have the behavior of that method depending on some state.
Something like that.
-----
As for Ruby, of course you can remove a method . . . But you'll have to read up on it. It is a rather violent act to perform on a class or a module, and shouldn't be necessary for this assignment:
http://www.ruby-doc.org/ruby-1.9/classes/Module.html#M000934
My lib/student_solution.rb keeps getting purged when I run "rake package"
Is that supposed to happen?
@Alexander Dawson
Check that...my lib/student_game.rb (student_solution is fine)
@Alexander Dawson
No -- it should be including it, but let me verify.
@john
The student_solution.rb goes in lib/world/
(Not one directory up, in lib/)
OK?
The main reason it's this way is that since you're monkeymatching World::Map, it made some sense to have the student solution down in there.
John
@Alexander Dawson
Everything in lib/ should be included. Sheesh. Did I screw this up in the e-mail? No:
OK?
@john
I probably goofed something simple somewhere, I'll give it another try.
Thanks