User:Riviera/Fix the wifi

From XPUB & Lens-Based wiki

Fix the Wi-fi: a non-linear text adventure game

Fix the Wi-fi is a text adventure game which was written in collaboration with Anita and Mania. In Fix the Wi-fi the aim of the game is to turn the wi-fi on. The code for the game was based on the XPUB with rooms script which is (not publicly available) on chopchop. To make the game, maps and diagrams were drawn up to illustrate the world in which the game would take place. This was a constructive approach and working in a non-linear way enabled us to think through aspects of the game in tandem with one another. As we were unable to install the game on chopchop before the end of the prototyping session we met up the following day to finish it. This text discusses the code which feautres in the game, the process of making the game and offers a provocation for SI23. I hope that the changes I have made to the script may be useful in the context of SI23 if we decide to make a python text adventure.

Discussion of the Code

The code begins with the welcome message variable which is printed when the game starts. The current_room is also declared.

welcome_message = """
Oh no! The wifi is not working, try to fix it
You have to find the hidden router and re-start it in order to play more text-adventure games. 
You are on the ground floor and look around, but the router is no where to be seen. You are in
a tall building, with three floors and an attic. You see some steep stairs leading to the
floor above, a closed lift and an emergency ladder out of the window.

        Do you want to:
        --> take the stairs and 'go upstairs'
        --> climb the ladder 'climb ladder'
        --> enter the lift 'enter lift'
        """
current_room = "ground_floor"

One or Several Dictionaries

Next, a lengthy dictionary was defined: a dictionary composed of dictionaries. This way of writing the algorithm is not the easiest to maintain. For brevity, I have not included the dictionary as it was, but have separated out the information into different dictionaries for better readability. Each room is turned into a variable which holds a dictionary. These dictionaries have the following keys which appear more or less consistently:name, short_description, long_description, figlet and hints.

attic = {
    "name": "the Attic",
    "figlet": r"""
   ___  _/_   _/_   `   ___ 
  /   `  |     |    | .'   `
 |    |  |     |    | |     
 `.__/|  \__/  \__/ /  `._.' 
    """,
    "short_description": "You are in the attic.",
    "long_description": """It's dusty here, a tiny mouse is running around. The
    light of the router flashing indicates you are in the right place! There is
    only one thing left to do. Try turning it off and on again.""",
}

floor_2 = {
    "name": "Floor Two",
    "figlet": r""" 
 ,__   .                        ___ 
 /  `  |     __.    __.  .___  /   \
 |__   |   .'   \ .'   \ /   \   _-'
 |     |   |    | |    | |   '  /   
 |    /\__  `._.'  `._.' /     /___,
 /                                  
""",
    "short_description": "You reach the second floor.",
    "long_description": """The router is not here. Nothing is here except for a
    stick and a loop in the ceiling connected to a trapdoor.""",
    "hints": """
    --> open trapdoor
    --> go downstairs
    """
}

floor_1 = {
    "name": "Floor One",
    "figlet": r"""
 ,__   .                        . 
 /  `  |     __.    __.  .___  /| 
 |__   |   .'   \ .'   \ /   \  | 
 |     |   |    | |    | |   '  | 
 |    /\__  `._.'  `._.' /     _|_
 /                                    
    """,
    "short_description": "You reach the first floor.",
    "long_description": """The router is not here. You see an angry teenager
    pacing quickly. Oh I can't believe the wifi is not working! I hate taking
    the stairs, did you check if the lift is working?
    """,
    "hints": """
    --> go upstairs
    --> go downstairs
    --> enter lift
    """
}

ground_floor = {
    "name": "the Ground Floor",
    "figlet": r"""
   ___. .___    __.  ,   . , __     ___/
 .'   ` /   \ .'   \ |   | |'  `.  /   |
 |    | |   ' |    | |   | |    | ,'   |
  `---| /      `._.' `._/| /    | `___,'
  \___/                                `
    """,
    "short_description": "You are on the ground floor.",
    "hints": """
    --> go upstairs
    --> enter lift
    --> climb ladder
    """
}

lift = {
    "name": "the Lift",
    "short_description": "You are in the lift.",
    "long_description": """To your left there is a panel with three buttons
    corresponding to the ground, first and second floors.""",
}

Let’s combine these dictionaries into a new dictionary like so.

rooms = dict(attic = attic,
             floor_1 = floor_1,
             floor_2 = floor_2,
             ground_floor = ground_floor,
             lift = lift)

Doors and go()

Doors haven’t been added yet. Previously, doors were stored as a dictionary inside the rooms dictionary. I suggest making a new dictionary called doors like so:

doors = {
    "upstairs": {
        # In a given room (key) this door leads_to (value)
        "floor_2": "attic",
        "floor_1": "floor_2",
        "ground_floor": "floor_1"
    },
    "downstairs": {
        "attic": "floor_2",
        "floor_2": "floor_1",
        "floor_1": "ground_floor",
    },
    "lift": {
        "floor_2": "lift",
        "floor_1": "lift",
        "ground_floor": "lift",
    },
    "ladder": {
        "attic": "ground_floor",
        "ground_floor": "attic"        
    }
}

The structure of the dictionary has implications for the go() function which will now be implemented. In other words, the function should deduce whether it is possible to go in a given direction on the basis of the doors dictionary, not the rooms dictionary.

def go(direction, current_room):
    if direction in doors.keys() and current_room in doors[direction].keys():
        new_room = doors[direction][current_room]
        current_room = new_room
        message = f"You opened the door to { rooms[new_room]['name'] }..."
    else:
        message = f"Sorry, you can't go { direction }."
    return message, current_room

The adjusted go() function accomodates for three specific scenarios:
1. The door provided does not exist anywhere
2. The door provided does not exist in the current_room
3. The door provided exists in the current_room

If the first two scenarios are false then the door must exist in the current room. In this sense, the and clause in the if statment kills two birds with one stone.

Augusto Boal, an excursion

wifi_is_on = "broken"

In ‘’Games for Actors and Non-Actors’’ Augusto Boal opens his discussion of the ‘dialectical structure of the actor’s role’ by stating that:

The fundamental concept for the actor is not the ‘being’ of the character, but the ‘will’. One should not ask ‘who is this?’, but rather ‘what does he want?’ The first question can lead to the formation of static pools of emotion, while the second is essentially dynamic, dialectical, conflictual, and consequently theatrical.

According to Boal what determines the will is bound up with the core of the performance; what it endeavours to communicate to audiences. Boal offers several examples from Shakespearean tragedies to argue that ‘[o]nce chosen, the central idea of the work must be respected at all costs’ (p.41). From this perspective, it is obligatory that every will contributes to fleshing out the thing the play is about. In text adventure games you are the actor. What does it mean to transpose Boal’s thinking to text adventure games? How might this be done?

I speculate, in relation to Fix the Wi-fi and with Boal in mind, that the will of the actor is limited by the role played. Alternative modes of engagement with the software can therefore be ruled out insofar that these do not reflect the will of the actor. This is in part because it is FLOSS and modes of engagement are broad in scope. For example, it is not the will of the actor to extend the game through contributions to the code which makes it work. Nor is the actors’ will to read the code which makes the game function to understand how to proceed with the game. In the context of Fix the Wi-Fi, the will of the actor is to fix the wifi. There are several reasons to support the conclusion that the actor wants to fix the wifi. During our conversations, Me, Mania and Anita spoke about the lift only working when the wi-fi is enabled. I have not implemented a working lift as doing so is relatively challenging. Nevertheless, the actor may strive to fix the wi-fi because the lift is broken and needs to be fixed. Furthermore, residents are not angry when the wifi is working and they are able to connect to the internet. In other words, the aim of fixing the wi-fi can be understood as the crux of the game and this necessarily shapes the will of the actor.

This will can be instrumentalised through code in game-world mechanisms which consolidate and reify the will of the actor. However, it is simultaneously the case that the creation of in-game mechanisms is a result of the actors’ will. That is, it is a meshwork which supports the actor to realise the object of their will, because that is what is wanted by the actor. There is tension here. On the one hand, a will is imposed on the actor by code. On the other hand the will of the actor determines the code. Preserving agency is of importance in this dynamic. To illustrate this, let’s add some alternative descriptions to the rooms dictionary for when the wifi is working.

rooms['attic']['alt_description'] = """It's dusty here, a tiny mouse is running around. The
light on the router is solid green."""
rooms['floor_1']['alt_description'] = """The router is not here. Nobody is here. Did you 
check if the lift is working?"""

look()

The look function plays into the will of the actor. The function returns a description of the room you are currently in. The state of the wifi only matters when there is an alt_description for the room. Otherwise long descriptions are used by default and, failing that, short descriptions. It also prints a figlet for the current room if there is one. The function retrieves this information for the current room from the rooms dictionary. The if statements provide flexibility insofar that not every room needs to have the same exact keys.

def look(current_room):
    if wifi_is_on == True and 'alt_description' in rooms[current_room].keys():
        desc = rooms[current_room]['alt_description']
    elif 'long_description' in rooms[current_room].keys():
        desc = rooms[current_room]['long_description']
    else:
        desc = rooms[current_room]['short_description']
    if 'figlet' in rooms[current_room].keys():
        figlet = rooms[current_room]['figlet']
    else:
        figlet = " "
    message = f"""
    { figlet }
    { desc }
    """
    return message

Static Objects

In Fix the Wifi we had one object: a trapdoor, which could be opened. I have not implemented the code for the trapdoor here. We speculated about including a router object which could be switched on and off. These objects were stored as dictionaries in the all-encompassing rooms dictionary which I have dismantled in this discussion. How might a router object be implemented in the game? I suggest making it into a dictionary which details its name, location and a list of possible ways of interacting with it.

router = {
    "name": "the router",
    "location":"attic",
    "actions":["enable", "disable"]
}

Let’s add it to a dictionary of objects and set the wifi to a broken state.

objects = dict(router = router)

Now two functions, enable and disable, are defined. Enabling the router will set the value of wifi_is_on to True and vice-versa. The wifi is broken so the game is not won yet. To win the game, the router needs to be reset by disabling and then enabling it. The if statements check three conditions: that the object exists in the objects dictionary, that the object is in the current room, and that the object can be enabled or disabled. Only if all of these are true does it check the value of wifi_is_on to conditionally execute more . The two functions work in much the same way. To some extent, at the moment, switching the wifi off and on again is somewhat inconsequential. However, it is the aim of the game! Accordingly, I’m going to introduce a point system into the game which prints a message that congratulates the player if it’s the first time they’ve turned the wifi on after turning it off.

game_points = 0

def disable(obj, current_room, wifi_is_on, game_points):
    if obj in objects.keys() and current_room in objects[obj]['location']:
        if 'disable' in objects[obj]['actions']:
            if wifi_is_on == "broken" or True:
                message = f"You switch {objects[obj]['name']} off"
                wifi_is_on = False
                game_points += 1
            else: 
                message = f"You try to switch off {objects[obj]['name']} but it is already switched off"
        else:
            message = f"You can't disable {objects[obj]['name']}."
    else:
        message = "You can't disable that."
    return message, wifi_is_on, game_points
def enable(obj, current_room, wifi_is_on, game_points):
    if obj in objects.keys() and current_room in objects[obj]['location']:
        if 'enable' in objects[obj]['actions']:
            if wifi_is_on == True:
                message = f"You try to switch on {objects[obj]['name']} but it is already switched on"
            else:
                message = f"You switch {objects[obj]['name']} on"
                wifi_is_on = True
                game_points += 1
        else:
            message = f"You can't enable {objects[obj]['name']}."
    else:
        message = "You can't enable that."
    return message, wifi_is_on, game_points

While loop

The while loop is where the magic happens. It also offers an easy way of defining aliases for commands.

action = ""
obj = ""

print(welcome_message)
while True:
    # these next few lines could be a function
    reply = input()
    command = reply.split()
    if len(command) >= 1:
        action = command[0]
    if len(command) >= 2:
        obj = command[1]
    # exit and look
    if "exit" in action:
        break
    elif "look" in action:
        print(look(current_room))
    # enable and disable, only win the game once
    elif "enable" in action and game_points == 1:
        message, wifi_is_on, game_points = enable(obj, current_room, wifi_is_on, game_points)
        print(message)
        print("It worked! The wifi works again, congrats!!")
    elif "enable" in action:
        message, wifi_is_on, game_points = enable(obj, current_room, wifi_is_on, game_points)
        print(message)
    elif "disable" in action:
        message, wifi_is_on, game_points = disable(obj, current_room, wifi_is_on, game_points)
        print(message)
    # go and aliases
    elif "go" or "climb" or "ascend" or "descend" or "enter" in action:
        message, current_room = go(obj, current_room)
        print(message)
    else:
        print("Hmm, not sure what to do.")

Sample Output

Oh no! The wifi is not working, try to fix it
You have to find the hidden router and re-start it in order to play more text-adventure games. 
You are on the ground floor and look around, but the router is no where to be seen. You are in
a tall building, with three floors and an attic. You see some steep stairs leading to the
floor above, a closed lift and an emergency ladder out of the window.

        Do you want to:
        --> take the stairs and 'go upstairs'
        --> climb the ladder 'climb ladder'
        --> enter the lift 'enter lift'
        


 go upstairs


You opened the door to Floor One...


 look




 ,__   .                        . 
 /  `  |     __.    __.  .___  /| 
 |__   |   .'   \ .'   \ /   \  | 
 |     |   |    | |    | |   '  | 
 |    /\__  `._.'  `._.' /     _|_
 /                                    
    
    The router is not here. You see an angry teenager
    pacing quickly. Oh I can't believe the wifi is not working! I hate taking
    the stairs, did you check if the lift is working?
    
    


 go upstairs


You opened the door to Floor Two...


 go upstairs


You opened the door to the Attic...


 disable router


You switch the router off


 enable router


You switch the router on
It worked! The wifi works again, congrats!!


 climb ladder


You opened the door to the Ground Floor...


 go upstairs


You opened the door to Floor One...


 look




 ,__   .                        . 
 /  `  |     __.    __.  .___  /| 
 |__   |   .'   \ .'   \ /   \  | 
 |     |   |    | |    | |   '  | 
 |    /\__  `._.'  `._.' /     _|_
 /                                    
    
    The router is not here. Nobody is here. Did you 
check if the lift is working?
    


 exit

A Provocation: Cloud Computing and Fix The Wifi

In order to play Fix the Wifi, one can execute the following command:

$ python3 <(curl -s https://pad.xpub.nl/p/si23-prototyping-wifi/export/txt)

There are some differences between the code in the etherpad and the code above. I have discussed some of these already in seeking to improve aspects of the code to enhance readability and implement additional features. There is, however, in this one line of code, a relationship to a datacenter. Were Fix the Wifi to be installed on Chopchop in this way would it be more space-efficient? This could be debated in terms of how much space an etherpad takes up in contrast to a script. However, there are contingencies which might also be taken into account such as how much space there is avaiable on chopchop in proportion to at the datacenter.