Game and Player Data Systems

Your game will need to store data, and you will run into variable scope issues if you use global variables and functions in the same program. Here are some ways to deal with your game data.

1) Variables

Variables are the simplest system for storing your values in memory.

If you want a function to assign a new value to a global variable, you will need to use the global keyword in each function that needs to change that variable.
(Read about variable scope for more info on this)

def gain_points(x):
    global score
    score = score + x

def lose_points(x):
    global score
    score = score - x

score = 0  # this is a global variable
gain_points(7)
lose_points(3)

If you have multiple values to access and reassign, using individual variables quickly gets tedious. Try another method to make your program easier to work with.

2) Lists

Lists store multiple items, and they are especially useful if everything in the list is of the same type. That lets you iterate (loop) through the list to complete tasks with less code.

If you have a list of items in inventory, a list of enemies in a room, a list of areas where a player can travel, or a list of names to print, you should consider a Python list instead of variables.

If you need some items to stay in order, you should consider a list.

You can manipulate global lists and dictionaries from within a function. The only thing you can’t do is completely reassign the dictionary (such as inventory = {}). But you can look up, add, remove, and reassign individual items.

3) Dictionaries (dict)

Dictionaries can store multiple items, but unlike lists, the entries have names instead of numbers.

Many games also might want to have a game or player dictionary that stores information about the state of the game. You might also require data structures for things like locations, enemies, and items.

  
# Creating a dictionary
player = {'name': 'Arthur', 'score': 0, 'money': 100}

# Add an entry
player['class'] = 'Hitchhiker'
# Change a value
player['score'] += 25

It’s easier to look up a value by name, like player['score'], than to remember the numerical position of each value in the list, like player[3] if the score is in position 3 of a list.

4) Strings

If you have chunks of data that are only one character long, and they need to be in a certain order, you can store this data as a string.

Let’s say you’re storing game history in a game where the player can win or lose a mini-game in each round. It stores “W” for each win and “L” for each loss.

import random
history = ""

for i in range(10):
    if random.random() < 0.5:
        history += "W"
    else:
        history += "L"

print(history)

print(f'Wins: {history.count("W")}')
print(f'Losses: {history.count("L")}')

for result in history:
    if result == "W":
        print("Win")
    elif result == "L":
        print("Loss")

You can iterate through this string and make the program behave according to what character is stored in each position. You can do anything you’d do with a string, including adding to the string, counting characters in the string, and searching for characters within the string.

5) List of lists

If you need a two dimensional grid of values, such as an area map grid, you can use lists inside of lists.

area = [
    ['road', 'road', 'jungle', 'jungle'],
    ['jungle', 'road', 'jungle', 'jungle'],
    ['jungle', 'road', 'road', 'jungle'],
    ['jungle', 'village', 'road', 'jungle']
]
print(area[3][1])  # prints 'village'
print(area[0]) # prints the entire first list  

To look up or assign values from a structure like this, you can use two sets of square brackets to index the lists as shown above. Remember that this can be a variable instead of a literal number, so it could represent a stored player location or a user input choice.

If you look up just one square bracketed number, you get an entire list from with in the list of lists (try the example above).

To print / compare / modify / read every item in the grid, use nested for loops.

6) Dictionary containing lists or dictionaries

A dictionary can store simple values like numbers, but it can also store more dictionaries or lists.

For example, if a player has a list of items in a backpack, you can have a list inside the dictionary as follows:

player = {'name': 'Dora', 'backpack' = ['map', 'rope', 'scissors']}
player['backpack'].append('binoculars')
for item in player['backpack']:
    print(item)

To access the list, you first look it up in the dictionary using player['backpack']. Then you can use it like any other list.

You could also have multiple dictionaries that are each stored as named entries in an overall dictionary. Let’s say you have a party of adventurers, and the dict is named party.

party = {
    'Legolas': {'race': 'elf', 'weapon': 'bow'},
    'Gimli': {'race': 'dwarf', 'weapon': 'axe'}
}

# look up a single value
print(party['Legolas']['weapon'])

# print all values
for name in party:
    print(name)
    print(party['name']['race'])
    print(party['name']['weapon'])

If all of the dictionaries have the same keys, it can be convenient for using the data in loops and functions. If you do it right, the code is shorter and easier to modify.

7) Objects and Classes

A class defines a type of object. If you need custom objects with specific variables and functions, you can define that to according to your exact needs.

Objects have their own collection of variables and functions.

Once you define a class, you can create as many distinct objects (instances) of that class as you like.

https://www.nemoquiz.com/python/#object-oriented
https://www.w3schools.com/python/python_classes.asp

Object Oriented Programming (OOP) is a big topic, but you can start pretty simple if you just want an easy way to access a player’s variables from within a function.

In the example below, you can change the value of a player’s money variable from within a function. Doing this using a global variable would fail. To make it work with a global variable, you would need to use the global keyword every time you want to change a variable inside a function, which is inelegant and tedious.
(Read about variable scope to learn about global and local variables).

class Player():
    pass

def work():
    print('You earn 20 money units.')
    p.money += 20

p = Player()
p.money = 100
p.work()
print(p.money)

The nice thing about this example is that you don’t have to learn all about defining classes to make it work. You just add your variables as attributes to your player object, p. This does approximately the same thing that a dictionary would do, but the syntax is simpler here.

8) List Stored in an Object

Let’s say you want the player object to have an inventory list:

class Player():
    pass

p = Player()
p.inventory = ['pants', 'shirt', 'hat']
p.inventory.append('socks')
for thing in p.inventory:
    print(thing)

9) Objects stored in Lists or Dictionaries

Using the Player class from the previous example, here’s a way to create 100 Player objects and tell them all to work, then print their money.

# create a list of 100 Player objects
players = []
for i in range(100):
    players.append(Player())

# iterate through players list to work
for p in player:
    p.work()
    print(p.money)

This is a simple example of objects in a list, but objects can just as easily be stored in a dictionary.

10) Data from Files

You can store game and player data in files.

If you’re using lists and dictionaries, it’s easy to load and save that data in files using import json.
(Read this: Read and Write JSON Files)

import json
player = {"name": "Alan", "score": 99}

# save file
with open("save.json", "w") as f:
    json.dump(player, f)

# load file
with open("save.json") as f:
    player = json.load(f)

If you’re using objects, you can load and save using import pickle.