Tuesday, February 26, 2019

Building Your World part eleven - seeing what you have

What have you got, and what can you see?

So far there are three items in this new world:
mysql> select * from items;+--------+--------------+-----------------------------+-----------+------------+-----------------+| itemID | itemName | itemDescription | itemValue | itemRoomID | itemCharacterID |+--------+--------------+-----------------------------+-----------+------------+-----------------+| 1 | Handkerchief | a handkerchief | 1 | NULL | 1 || 2 | Rolling Pin | a flour covered rolling pin | 1 | 1 | NULL || 3 | Bag of Rice | a paper bag full of rice | 1 | 1 | NULL |+--------+--------------+-----------------------------+-----------+------------+-----------------+

But you cannot see the items in the room, nor can you check what you have in your pockets.

For the former, we return to the Room class.
 In the initialisation code, a new list is added which contains the items present in the room.

        self.item = []

This is not filled because the room may be loaded to determine the available exits during the movement action, and as the character is leaving the room, the contents are of no interest.

The items are added using the load_contents method.
    def load_contents(self,acursor):
        sql="select itemID, itemName, itemDescription, itemValue from items where itemRoomID=%s"
        roomID=(str(self.roomID),)

        acursor.execute(sql,roomID)

        myresult = acursor.fetchall()

        for x in myresult:
            newitem=Item(x[0],x[1],x[2],x[3])
            self.items.append(newitem)

An additional function is required to display the contents of the room:

    def contents(self):
        result="Items:" +str(len(self.items)) +"\n"
        if len(self.items)>0:
            result="The room contains the following: \n"
            for anItem in self.items:
                result+=anItem.get_name()
                result+="\n"
        else:
            result="There are no items in this room"
        return result

A call is made to this function in the  get_details(self) function.

Kitchen======A bright shiny IKEA kitchenThere is a plain door to the southThe room contains the following: Rolling PinBag of Rice

Saturday, February 23, 2019

Building Your World - part ten: What have I got in my pocketses

Things to pick up and use

Currently the adventure world is rather dull. Besides the rooms, there is nothing to see.

So we need to add some items for our adventurers to pick up, examine, use or give to other people.

For this we will need a tables.

This will contain the details of the item, plus whether it is in someone's possession or is in a particular room.

So the table will contain the following fields.
  • item ID  - an auto increment integer to identify the item
  • item Name - a 20 character string for the name of the item
  • item description - a 100 character string for the item description
  • value - integer to hold the value in gold pieces
  • room ID - integer Null foreign key on rooms.roomID
  • character ID - integer Null foreign key on charcater.characterID
So to create the table:
CREATE TABLE items 
       (
itemID INT NOT NULL AUTO_INCREMENT,
itemName VARCHAR(20) NOT NULL,
itemDescription VARCHAR(100) NOT NULL,
itemValue INT NOT NULL,
itemRoomID INT NULL, 
itemCharacterID INT NULL,
FOREIGN KEY (itemRoomID) REFERENCES rooms(roomID),
FOREIGN KEY (itemCharacterID ) REFERENCES characters(characterID),
PRIMARY KEY (itemID)
       );

Add two items, both in room 1 (the kitchen).
insert into items(itemName,itemDescription,itemValue,itemRoomID) values ('Rolling Pin','a flour covered rolling pin',1,1);
insert into items(itemName,itemDescription,itemValue,itemRoomID) values ('Bag of Rice','a paper bag full of rice',1,1);

mysql> insert into items(itemName,itemDescription,itemValue,itemRoomID) values ('Rolling Pin','a flour covered rolling pin',1,1);Query OK, 1 row affected (0.01 sec)mysql> insert into items(itemName,itemDescription,itemValue,itemRoomID) values ('Bag of Rice','a paper bag full of rice',1,1);Query OK, 1 row affected (0.02 sec)mysql> select * from items;+--------+-------------+-----------------------------+-----------+------------+-----------------+| itemID | itemName | itemDescription | itemValue | itemRoomID | itemCharacterID |+--------+-------------+-----------------------------+-----------+------------+-----------------+| 1 | Rolling Pin | a flour covered rolling pin | 1 | 1 | NULL || 2 | Bag of Rice | a paper bag full of rice | 1 | 1 | NULL |+--------+-------------+-----------------------------+-----------+------------+-----------------+2 rows in set (0.00 sec)

Then add an item in the first characters pocket.
insert into items(itemName,itemDescription,itemValue,itemCharacterID) values ('Handkerchief','a handkerchief',1,1);

mysql> insert into items(itemName,itemDescription,itemValue,itemCharacterID) values ('Handkerchief','a handkerchief',1,1);Query OK, 1 row affected (0.01 sec)

Next, coding the items





Thursday, February 14, 2019

Building Your World - part nine: Where am I now

Storing and retrieving your location

In the last part two tables were created, one to store player information, the other to store player information. The player information is not important at the moment, there is only one player. However as both characters are associated with the single player, we will have to have some way of matching what you want to do with the particular character.

But first, let us start by transferring the character location from the code to the database.

Assigning initial locations

The two characters created in the last part have no location information:
mysql> select * from characters;+-------------+----------+---------------+------------------------------------------------+-----------------+| characterID | playerID | characterName | description | characterRoomID |+-------------+----------+---------------+------------------------------------------------+-----------------+| 1 | 1 | John | a seedy looking gentleman in a long trenchcoat | NULL || 2 | 1 | Philip | a man in a black suit and sunglasses | NULL |+-------------+----------+---------------+------------------------------------------------+-----------------+

For the initial part of this exercise, we will be using "John", character ID 1.

So we start by placing John in the first room, the kitchen.
mysql> update characters set characterRoomID=1 where characterID=1;


ysql> select characterRoomID,characterName,characterID from characters;+-----------------+---------------+-------------+| characterRoomID | characterName | characterID |+-----------------+---------------+-------------+| 1 | John | 1 || NULL | Philip | 2 |+-----------------+---------------+-------------+2 rows in set (0.00 sec)

Getting the character information

So the next stage is to decide what we want, and then build a class to handle it.

At the moment, we want to obtain the location of the the player's current character. Later we will may want to know what they are carrying, any skills they have and other information we have not thought of.

The class will have a creator, that takes the player ID and looks everything else up for us. We will default to taking the first character for that player.

class Character():
    def __init__(self,player_ID,acursor):
        sql="SELECT characterID,playerID,characterName,description,characterRoomID FROM characters WHERE playerID=%s ORDER BY playerID LIMIT 1 "
        playerID=(str(player_ID),)

        acursor.execute(sql,playerID)

        myresult = acursor.fetchall()
        for x in myresult:
            self.characterID=x[0]
            self.playerID=x[1]
            self.name=x[2]
            self.description=x[3]
            self.roomID=x[4]
        self.items={}

    # Getters and setters
    def get_currentRoom(self):
        return self.roomID

    def set_currentRoom(self,newRoom):
        self.roomID=newRoom
        return self.roomID

    def get_name(self):
        return self.name

    def get_description(self):
        return self.description()

    # Actions
    def update_character(self, acursor):
        sql="UPDATE characters SET characterRoomID=%s WHERE playerID=%s AND characterID=%s"
        params=(str(self.roomID),str(self.playerID),str(self.characterID))
        acursor.execute(sql,params)

The update_character method does not include a commit, that needs to be in the calling code.

Modifications to the main program

from room import Room
from character import Character
import adventureconnection
mydb=adventureconnection.make_connection()
mycursor = mydb.cursor()

player_character=Character(1,mycursor)

while player_character.get_currentRoom()!=3:

    current_room = Room(player_character.get_currentRoom(),mycursor)
    current_room.get_details()
    command=input("> ")
    if command in("north","south","east","west"):
        print("Go "+command)
        new_location=current_room.move(command)
        if player_character.get_currentRoom()==new_location:
            print("You cannot go " +command)
        else:
            player_character.set_currentRoom(new_location)
            player_character.update_character(mycursor)
            mydb.commit()


print("And you have left the building...")

Test Run

Kitchen======A bright shiny IKEA kitchenThere is a plain door to the south> southGo southDining Hall======A room containing pale Scandinavian furnitureThere is a discrete door to the northThere is a grand door to the west> west
Go westAnd you have left the building...>>>


mysql> select characterID,playerID,characterName,characterRoomID from characters where playerID='1' order by playerID limit 1;+-------------+----------+---------------+-----------------+| characterID | playerID | characterName | characterRoomID |+-------------+----------+---------------+-----------------+| 1 | 1 | John | 3 |+-------------+----------+---------------+-----------------+1 row in set (0.00 sec)



Tuesday, February 12, 2019

Building your world - part eight: Now where was I?

Remembering where you were

As the program currently exists, the starting location is defined in the code before entering the main loop.

player_location=1
while player_location!=3:

...

Now this is not a problem if you only want to always want to start from the beginning each time (and you only have a few room). It also is not a problem if the player's location is maintained within the program. As this is going to eventually be a web game, this will be a problem, the player's location needs to be maintained between calls to the web page.

In the same way that the Room class is populated from the rooms table, the player's location will be stored in a players table.

For a single player game, you could just have the character location as the only field (and a single row). However the idea is to extend this into a multiple character (and player) adventure, so it is worth creating the table to accommodate multiple character/players. 

Note, there is a difference between a player and a character. The former will have a log in so will need to have some way of securely logging in. The latter will "belong" to a player, a player may have multiple characters.

Later this will be extended further, but for now we are concerned with the following:

A table to contain player information. Initially this will just be:

  • Player ID - an auto increment integer primary key used to identify players
  • Username - a 50 character string
Additional fields will be required when logging in etc is implemented.

A table to contain character information
  • Character ID - an auto increment integer to identify the character
  • Player ID - the owning player (who will be the only player able to operate the character
  • Character name - 20 character field containing the character's name
  • Character description - 100 character description of the character
  • Character Room ID - the roomID of the room that contains the character (it can be NULL as the character may not be in a room yet)

Creating the Players table

In a MySQL console, use the following to create the players table:

create table players( 
     playerID INT NOT NULL AUTO_INCREMENT,
     username VARCHAR(50) NOT NULL,
     PRIMARY KEY(playerID),
     UNIQUE(username)
);

Creating the Characters table

create table characters(
     characterID  INT NOT NULL AUTO_INCREMENT,
     playerID INT NOT NULL,
     characterName VARCHAR(20) NOT NULL,
     description VARCHAR(50) NOT NULL,
     characterRoomID INT NULL,
     PRIMARY KEY(characterID ),
     FOREIGN KEY (playerID) REFERENCES players(playerID),
     FOREIGN KEY (characterRoomID) REFERENCES rooms(roomID),
     UNIQUE(characterName)
);

Check the new tables

mysql> show tables;+--------------------------------------+| Tables_in_technologyisnotd$adventure |+--------------------------------------+| characters || links || players || rooms |+--------------------------------------+4 rows in set (0.00 sec)

Populating the tables

At the moment there is only one player, and the mechanisms for dealing with multiple players is for a later date, so a holding "player" called "Me" will do for now.

insert into players(username)values('Me');

mysql> select * from players;+----------+----------+| playerID | username |+----------+----------+| 1 | Me |+----------+----------+1 row in set (0.00 sec)

For testing purposes there will be two characters "Philip" and "John".

insert into characters(playerID,characterName,description)
values(1,'John','a seedy looking gentleman in a long trenchcoat');

insert into characters(playerID,characterName,description)
values(1,'Philip','a man in a black suit and sunglasses');

mysql> select * from characters;+-------------+----------+---------------+------------------------------------------------+-----------------+| characterID | playerID | characterName | description | characterRoomID |+-------------+----------+---------------+------------------------------------------------+-----------------+| 1 | 1 | John | a seedy looking gentleman in a long trenchcoat | NULL || 2 | 1 | Philip | a man in a black suit and sunglasses | NULL |+-------------+----------+---------------+------------------------------------------------+-----------------+2 rows in set (0.00 sec)



Friday, February 8, 2019

Building your world - part seven: movement

Refactoring what we have

The existing test program is a bit awkward, especially for me as I have to remember to remove the actual username and passwords when pasting them into the blog.

So, first off create a new file called adventureconnection.py.


import mysql.connector
def make_connection():
    # Create a database connection using the host, user name, password and database name
    mydb = mysql.connector.connect(
    host="<yourusername>.mysql.pythonanywhere-services.com",
    user="<yourusername>",
    passwd="<mysqlpassword>",
    database="<yourusername>$adventure"


    )
    return mydb


Now either modify the testroom.py file or create a new one. I have created a new one called testadventure.py.

Import the Room class, then import the contents of adventure connection. The make _connection function can then be called to make the database connection.

from room import Room
import adventureconnection
mydb = adventureconnection.make_connection()
mycursor = mydb.cursor()



aroom=Room(3,mycursor)

print(aroom.get_details())

Output:
Ballroom======A room whose floor is covered in brightly coloured balls.There is a grand door to the eastNone


Movement

At this point there is only one player, so we can store the location in the program.

Modification to the Room class

The Room class needs a method that returns the room id in the supplied direction. If there is no link in that direction, it just returns the current room id.

# Actions
    def move(self,direction):
        if direction in self.linked_rooms:
            return self.linked_rooms[direction].get_destinationID()
        else:
            return self.roomID

Modification to the testadventure code




First create your cursor for the database access.
Then set the player's initial location (room one)
Loop while the player's location is not 3
  Get current room
  Print room description
  Get command
  If command is a direction then 
    Get room number in that direction 
    If the room number is unchanged then
      You cannot go that way
    else
      Change the location
End loop
Print Exit message

from room import Room
import adventureconnection
mydb=adventureconnection.make_connection()
mycursor = mydb.cursor()

player_location=1
while player_location!=3:

    current_room=Room(player_location,mycursor)
    current_room.get_details()
    command=input("> ")
    if command in("north","south","east","west"):
        print("Go "+command)
        new_location=current_room.move(command)
        if player_location==new_location:
            print("You cannot go " +command)
        else:
            player_location=new_location

print("And you have left the building...")

Testing the code:
Kitchen======
A bright shiny IKEA kitchenThere is a plain door to the south> northGo northYou cannot go northKitchen======A bright shiny IKEA kitchenThere is a plain door to the south> southGo southDining Hall======A room containing pale Scandinavian furnitureThere is a discrete door to the northThere is a grand door to the west> westGo westAnd you have left the building...






Building your world - part six: extending the Room class to display the links

Displaying the links

Currently when we instantiate (create) a room, we pass the room id to the creator. Now that room id also identifies the source room, so we can add the code to retrieve the links to the Room class.

The Link class

The links need somewhere to store them, and as the direction will be unique for each room, we will use a Dictionary.

The Link class will store the room id (source room id), where the link goes (destination id), the direction and the description of the link.

class Link():
    def __init__(self,sourceID,destinationID,direction,description):
        self.sourceID=sourceID
        self.destinationID=destinationID
        self.direction=direction
        self.description=description

    # Getters and Setters
    def get_sourceID(self):
        return self.sourceID

    def get_destinationID(self):
        return self.destinationID

    def get_direction(self):
        return self.direction

    def get_description(self):
        return self.description

Adding Links to the Room class

So the first modification is to the creator:
    def __init__(self,room_id,acursor):
        sql="SELECT * FROM rooms where roomID=%s "
        roomID=(str(room_id),)

        acursor.execute(sql,roomID)

        myresult = acursor.fetchall()

        for x in myresult:
            self.roomID=x[0]
            self.name=x[1]
            self.description=x[2]
        self.linked_rooms = {}
    # Add linked rooms
        sql="select destinationRoomID,direction,description from               links where sourceRoomID=%s"

        acursor.execute(sql,roomID)

        myresult = acursor.fetchall()

        for x in myresult:
            newLink=Link(self.roomID,x[0],x[1],x[2])
            self.linked_rooms[newLink.get_direction()]=newLink

The other modification is to extend the get_details method.
    def get_details(self):
        print(self.get_name())
        print("======")
        print(self.get_description())
        for direction in self.linked_rooms:
            link=self.linked_rooms[direction]
            print("There is " + link.get_description()+" to the "+direction)

Now additional changes will need to be made as we continue development, especially once we get to moving between rooms, and when this progresses to being a web application.

Testing the changes

Using the existing testroom.py code, you can examine the rooms by changing the room ID here:
   aroom=Room(1,mycursor)
   print(aroom.get_details())

Kitchen======A bright shiny IKEA kitchenThere is a plain door to the southNone>>>

   aroom=Room(2,mycursor)
   print(aroom.get_details())
Dining Hall======A room containing pale Scandinavian furnitureThere is a discrete door to the NorthThere is a grand door to the westNone>>>

   aroom=Room(3,mycursor)
   print(aroom.get_details())
Ballroom======A room whose floor is covered in brightly coloured balls.There is a grand door to the eastNone>>>

Next, moving between rooms.

References

https://www.w3schools.com/python/python_dictionaries.asp