ECE 2524 - Python Text Adventure: Part 1

ECE 2524

Introduction to Unix for Engineers

Python Text Adventure: Part 1

Last modified

Preamble

Use the files at /usr/share/txtadventure/ on the ece2524 VM and the copies you have been working on as a reference, but create new files in a new repository for this project to avoid complicated merge conflicts when you work with a partner.

Avoid copying and pasting the commands and code snippets I list here. Type them in and think about what each one does. Think about the steps each user completes and how they generalize to other collaborative projects. You will be following a similar procedure when collaborating for your final project.

Initialization

Pick someone to be user1 and someone to be user2. The initilization steps will vary depending on your role:

  • user1 will initialize a repository and push it to a remote location
  • user2 will fork the repo that user1 pushed and clone a local copy

User 1

  1. Initialize a new repository

    $ git init ~/ece2524/pytxt
    $ cd ~/ece2524/pytxt
  2. Add initial files

    world.py:

    class Room:
        """A single room"""
    
        def __init__(self, data):
            self.description = data['desc']
            self.items = data['items']
    
        def __str__(self):
            return self.description + "\nitems: " +  ", ".join(self.items)
    
    class World:
        """A world contains one or more rooms"""
    
        def __init__(self, data, current_name):
            self.rooms = {}
            for key,value in data.items():
                self.rooms[key] = Room(data[key])
    
            self.current = self.rooms[current_name]
    
        def __str__(self):
            return "World:\n{0}".format( "\n%%\n".join(
                [ name + ": " + str(room) for name,room in self.rooms.items() ]
            ) )

    And a file game.py that

    • imports the world and yaml modules

    • loads yaml file rooms.yaml

      data = yaml.load( open('rooms.yaml', 'r') )
    • creates a World object with the loaded data

      myworld = world.World( data, 'SURGE 104C' )

      The fact that we have to initialize the current room this way is not ideal, why not? What are some possible solutons? Let’s discuss them on Wednesday.

    • prints the World object

    Ensure that game.py has an appropriate shebang line and has execute permission.

  3. Add a remote

    $ git remote add origin git@ece2524.ece.vt.edu:user1/pytxt.git
  4. Make initial push

    $ git push -u origin master
  5. Give read access to user2

    $ ssh git@ece2524.ece.vt.edu perms user1/pytxt + READERS user2

User 2

  1. Fork user1/pytxt

    $ ssh git@ece2524.ece.vt.edu fork user1/pytxt user2/forks/pytxt
  2. Give user1 read access to your fork

    $ ssh git@ece2524.ece.vt.edu perms user2/forks/pytxt + READERS user1
  3. Clone your forked copy

    $ git clone git@ece2524.ece.vt.edu:user2/forks/pytxt.git ~/ece2524/pytxt
  4. Modify world.py

    • Define a new Item class. This should be a very simple class with a single instance variable containing the name of the item. Something like

      class Item:
          def __init__(self, name):
              self.name = name
      
          def __str__(self):
              return self.name
    • Update the Room.__init__ method to populate an instance variable with a list of items wrapped in the Item class. Use the code in World that generates a dict of Room as a reference, though note that you will be creating a list, not a dict. data['items'] is a list of strings, so

      for item in data['items']:
          # do something with item, which is a string

      would iterate over that list. For each thing in that list, create an Item object and append it to a list. Set self.items to that list. This an be done in a single line using list comprehension

    • You will also have to modify the Room.__str__ method since items is now a list of Item objects and not a list of strings. str.join expects a list of strings, but self.items is now a list of Item objects. You will need to transform the list of Items into a list of strings. This can be done with a single list comprehension:

      ...
      list_of_strings = [ str(item) for item in self.items ]
      ...
  5. Create a Python program in a file named test.py that confirms that items are wrapped in the Item class:

    • initialize a World object following the same procedure used in game.py
    • loop through each room in your created world object
    • loop through each item in the room and check that it is an instance of Item.1
    • don’t print any output, but raise a RuntimeError if any of the items are not instances of world.Item
  6. Add and commit your changes and push to your own remote. Notify user1 so that they can pull and merge your additions.

  7. Add a remote named upstream for easy merges from user1 in the future

    $ git remote add upstream git@ece2524.ece.vt.edu:user1/pytxt.git

User 1

  1. Add a remote named as user2’s username for easy merges:

    $ git remote add user2 git@ece2524.ece.vt.edu:user2/forks/pytxt.git
  2. When you receive notification from user2, pull and merge their changes.

    $ git pull user2 master

Testing

I am in the process of writing up automated tests for this assignment. You should always conduct your own tests to confirm your program works according to the specifications. This will provide helpful context and understanding if your program does pass some of the automated tests once they get posted!

Update: I’ve spent more time than anticipated addressing questions and adding to the write-up and I won’t be able to get tests posted before the deadline. Just do the best you can with the specs as is and your own testing.

  1. Note that except in very specific cases such as this one, you should avoid using Python’s isinstance() method. Once we clarify what the Item class should provide, a better check would be to use hasattr to check for interface support, regardless of type.