Assignment 2: Classes and Methods

October 15th, 2009 Leave a comment Go to comments

Errata

  1. Due date changed to 19-Oct.; the due date for Assignment 3 may get pushed back a bit, but don’t count on it.
  2. By student request, see below at question/answer 8 for what happens during a tick.
  3. 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
        end
    

    You 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:

4 Pylon course 46-47-48

(from Society of Air Racing Historians)

And here’s a picture of a plane rounding a Pylon:

rounding-pylon

(from Reno Pylon Racing School)

In a nutshell, you have five tasks to accomplish (more detail near the end):

  1. 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.
  2. Implement some Pylons and Players (and pass tests).
  3. Invent a Pylon of your own design.
  4. Invent a Player of your own design.
  5. 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:

game-image

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):

game-log

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.

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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.
  7. 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:

  1. 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
  2. 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.
  3. 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
  4. 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?
  5. 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?
  6. 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.
  7. 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.

  8. 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:

  1. 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.
  2. 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.
  3. Invent (a) Pylon(s) of your own design.
  4. Invent (a) Player(s) of your own design.
  5. 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.

  1. Brian Mantenuto
    October 17th, 2009 at 17:23 | #1

    @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.

  2. Brendan Shea
    October 17th, 2009 at 18:01 | #2

    john :
    Do you see the change in the NEXT tick? The screen updates are handled successively by player, you might well not see the change quite when you expect it.

    @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 :
    Also, Brendan: You're passing the tests for the ShyPlayer?

    @john
    Yep, it passes the tests.

  3. October 17th, 2009 at 20:05 | #3

    @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.,

    gm = World::GameManager.new(-8, 9, -5, 5)
    ExamplePlayer.new(gm, -8, 0, World::EAST)
    ShyPlayer.new(gm, 9, -1, World::WEST)
    gm.start
    

    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/

  4. October 17th, 2009 at 20:06 | #4

    @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.

  5. Alexander Dawson
    October 17th, 2009 at 21:02 | #5

    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.

  6. Cat
    October 17th, 2009 at 21:04 | #6

    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?

  7. Naomi
    October 17th, 2009 at 21:43 | #7

    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.

  8. kasula
    October 17th, 2009 at 22:09 | #8

    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?

  9. Brendan Shea
    October 17th, 2009 at 22:33 | #9

    @john
    Thanks! I'll debug mine to see why my display doesn't update.

  10. Brendan Shea
    October 17th, 2009 at 22:49 | #10

    @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!

  11. October 17th, 2009 at 23:36 | #11

    @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.

  12. Ron Newman
    October 18th, 2009 at 00:30 | #12

    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.

  13. Ron Newman
    October 18th, 2009 at 00:33 | #13

    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.

  14. kasula
    October 18th, 2009 at 01:42 | #14

    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?

  15. Alexander Dawson
    October 18th, 2009 at 07:28 | #15

    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

  16. Ron Newman
    October 18th, 2009 at 07:31 | #16

    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.

  17. October 18th, 2009 at 08:19 | #17

    @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.
        #
    
  18. October 18th, 2009 at 08:22 | #18

    @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.

  19. Ron Newman
    October 18th, 2009 at 08:40 | #19

    @ 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.

  20. October 18th, 2009 at 08:43 | #20

    @Ron Newman

    Er, feature?

  21. peter
    October 18th, 2009 at 09:57 | #21

    @Ron Newman
    Thanks Ron! Those comments led me to a bug in my method.

  22. October 18th, 2009 at 10:41 | #22

    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.

  23. October 18th, 2009 at 11:33 | #23

    @Donnie Demuth

    Um, I just downloaded the ZIP for assignment 2, and the constants say:

    PROJECT_HUMAN    = 'Assigment 2 (Classes and Methods), Harvard CSCI E-168, Fall 2009'
    PROJECT          = 'e168-assignment2'
    PROJECT_VERSION  = '1.1.001'
    USER             = (ENV['USER'] || ENV['USERNAME'] || 'Unknown').gsub(/\W+/,'')
    

    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.

  24. kasula
    October 18th, 2009 at 12:53 | #24

    Thank you, Ron and John. I overlooked those comments.

  25. October 18th, 2009 at 18:32 | #25

    @john
    Oh god. Duh, that makes sense. I downloaded the wrong assignment.

  26. October 18th, 2009 at 20:00 | #26

    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.

  27. David
    October 18th, 2009 at 21:57 | #27

    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?

  28. October 18th, 2009 at 22:09 | #28

    @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.

  29. Glenn Ritz
    October 19th, 2009 at 06:12 | #29

    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?

  30. October 19th, 2009 at 07:32 | #30

    @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.

  31. Ron Newman
    October 19th, 2009 at 10:15 | #31

    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.

  32. October 19th, 2009 at 10:24 | #32

    @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.

  33. kasula
    October 19th, 2009 at 10:49 | #33

    "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?

  34. October 19th, 2009 at 10:56 | #34

    @kasula

    As I wrote in an e-mail which every student should have received (e-mail #27):

    As well, you are required to write briefly about your design choices. For example, what (if anything) did you subclass, and why? If you shared code between Pylons or Players, did you introduce extra classes or modules, and why? (or why not?) Did you have any struggles managing the "state" of your Pylon(s) and Player(s), or problems with the superclasses given to you in the assignment? In short, give us some insight into how you designed and implemented your contribution. Think of your audience as another developer who wants to understand how your code works in its particular way. This does NOT mean documenting the details of your code in this document. The proper place for code documentation, and, particularly, classes, modules, and public methods, is in the code. All you need to do is prefix class, module, and public methods with the "#"-style comments.

    Also, please write a brief game that sets up the GameManager in such a way to show off your creations. Call this script student_game.rb and put it in the lib/ directory. If you have already changed example_game.rb (in the root of the project), and want to use that as your game example, that's fine: Just make sure to note it in the readme-student.txt file in the root of the project. Note that other file names in the root directory will not get picked up by the "rake package" command.

    For this assignment and future assignments, you MUST use "rake package" to create your ZIP (which ends up in the pkg/ directory). If "rake package" is not working on your system, or is creating a ZIP with a name you don't like, please contact your TA. ZIPs that don't conform to this expectation will not be graded promptly, or may receive a 0.

  35. Ron Newman
    October 19th, 2009 at 11:10 | #35

    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?)

  36. October 19th, 2009 at 11:24 | #36

    @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.

  37. kasula
    October 19th, 2009 at 12:37 | #37

    @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.

  38. October 19th, 2009 at 12:40 | #38

    @kasula

    I'm having a hard time keeping up, too. :-)

  39. Glenn Ritz
    October 19th, 2009 at 15:24 | #39

    Can you delete an instance method on an object at run time?

  40. October 19th, 2009 at 16:20 | #40

    @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

  41. Alexander Dawson
    October 19th, 2009 at 16:47 | #41

    My lib/student_solution.rb keeps getting purged when I run "rake package"

    Is that supposed to happen?

  42. Alexander Dawson
    October 19th, 2009 at 16:49 | #42

    @Alexander Dawson

    Check that...my lib/student_game.rb (student_solution is fine)

  43. October 19th, 2009 at 16:49 | #43

    @Alexander Dawson

    No -- it should be including it, but let me verify.

  44. October 19th, 2009 at 16:52 | #44

    @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

  45. October 19th, 2009 at 16:54 | #45

    @Alexander Dawson

    Everything in lib/ should be included. Sheesh. Did I screw this up in the e-mail? No:

    Call this script student_game.rb and put it in the lib/ directory. If you have already changed example_game.rb (in the root of the project) and want to use that as your game example, that's fine: Just make sure to note it in the readme-student.txt file in the root of the project. Note that other file names in the root directory will not get picked up by the "rake package" command.

    OK?

  46. Alexander Dawson
    October 19th, 2009 at 17:22 | #46

    @john
    I probably goofed something simple somewhere, I'll give it another try.

    Thanks

Comment pages
1 2 3 272
  1. No trackbacks yet.