A simple agent-based simulation run, visualized using Matplotlib in Python

In a previous post I constructed a simple agent-based simulation model, containing groups of agents that can be located on a battlefield grid. The model was coded in Python, using matplotlib for visualization.

The agents were modelled as a class, as shown below:

# class, defining agents as abstract data types
class agent:
    # init-method, the constructor method for agents
    def __init__(self,x,y,group):
        self.life = 100 # agent's life score
        self.x = x
        self.y = y
        self.group = group

A 2-dimensional array for modelling the battlefield grid was created with a list comprehension in Python:

# creating empty 100 x 100 list using list comprehension in python
battlefield = [[None for i in range(0,100)] for i in range(0,100)]

A helper function for creating a group of agents with defined group size was implemented as displayed below:

# -- define a function for creating agents and assigning them to grid
def agentCreator(size,group,groupList,field,n,m):
    # loop through entire group, i.e. in this case 1000 units
    for j in range(0,size):
        # select random available location 
        while True:
            # random x coordinate
            x = random.choice(range(0,n))
            # random y coordinate
            y = random.choice(range(0,m))
            # check if spot is available; if not then re-iterate 
            if field[x][y] == None:
                field[x][y] = agent(x=x,y=y,group=group)
                # append agent object reference to group list
                groupList.append(field[x][y])
                # exit while loop; spot on field is taken
                break

Using these model components I created an initial battlefield population and plotted agent locations using matplotlib. This is done in the code below:

# list with available x and y locations
locations = battlefield.copy() # using .copy prevents copying by reference
# create empty list for containing agent references in future, type A & B
agents_A = []
agents_B = []
# assigning random spots to agents of group A and B; 
import random
agentCreator(size = 1000,
                group = "A",
                groupList = agents_A,
                field = battlefield,
                n = 100,
                m = 100)
agentCreator(size = 1000,
                group = "B",
                groupList = agents_B,
                field = battlefield,
                n = 100,
                m = 100) 
#.imshow() needs a matrix with float elements;
population = [[0.0 for i in range(0,100)] for i in range(0,100)]
# if agent is of type A, put a 1.0, if of type B, pyt a 2.0
for i in range(1,100):
    for j in range(1,100):
        if battlefield[i][j] == None: # empty
            pass # leave 0.0 in population cell
        elif battlefield[i][j].group == "A": # group A agents
            population[i][j] = 1.0 # 1.0 means "A"
        else: # group B agents
            population[i][j] = 2.0 # 2.0 means "B"
# import pyplot and colors from matplotlib
from matplotlib import pyplot, colors
# using colors from matplotlib, define a color map
colormap = colors.ListedColormap(["lightgrey","green","blue"])
# define figure size using pyplot
pyplot.figure(figsize = (12,12))
# using pyplot add a title
pyplot.title("battlefield before simulation run (green = A, blue = B)",
            fontsize = 24)
# using pyplot add x and y labels
pyplot.xlabel("x coordinates", fontsize = 20)
pyplot.ylabel("y coordinates", fontsize = 20)
# adjust x and y axis ticks, using pyplot
pyplot.xticks(fontsize = 16)
pyplot.yticks(fontsize = 16)
# use .imshow() method from pyplot to visualize agent locations
pyplot.imshow(X = population,
             cmap = colormap)
<matplotlib.image.AxesImage at 0x22495922ac8>

We can now conduct a simple simulation run which in later posts will turn into an experiment. For this we implement two attack strategies:

Group A has the strategy of always hitting the same agent in each round
Group B has a random and independent strategy for attacking enemies. This means that each agent of type B will attack a randomly selected enemy within that agent’s reach.

The simulation is now conducted under the following conditions:

1) Each round is one iteration
2) In each round, each agent can attack one agent within his reach
3) The reach of an agent is defined at the start of the simulation and defaults to 10
4) If an agent dies he will no longer be located on the battle field
5) An agent dies when his life score equals or goes below zero
6) Each agent has a randomly distributed attack damage, ranging from 10 to 60
7) In each round all agents get to launch an attack

With these rules in place I will now iterate through 50 rounds of fighting. At the end I will print a plot of the remaining agents on the battlefield. The implementation follows below:

for counter in range(0,50): # in this case I am conducting 50 iterations 
    # iterating through all cells on the battlefield
    for i in range(0,len(battlefield)):
        for j in range(0,len(battlefield)):
            #print("top tier iteration, i: "+str(i)+", j: "+str(j))
            # check if there is an agent in the respective cell
            if battlefield[i][j] != None:
                # depending on the type: execute respective attack strategy
                if battlefield[i][j].group == "A":
                    found_i = None
                    found_j = None
                    # look in neigbouring cells in same order for each iteration
                    for k in range(i-10,i+11):
                        for l in range(j-10,j+11):
                            # check for negative index values; if so - break!
                            if k < 0 or l < 0:
                                break
                            # check for index values above 99, if so break!
                            if k > 99 or l > 99:
                                break
                            if battlefield[k][l]:
                                if battlefield[k][l].group == "B": # then this is an enemy
                                    if found_i == None:
                                        found_i = k
                                        found_j = l
                    # deal damage to identified enemy
                    if found_i:
                        battlefield[found_i][found_j].life = battlefield[found_i][found_j].life - random.randint(10,60)
                else: 
                    # first check if there even is an enemy in one of the surrounding cells
                    enemy_found = False
                    for k in range(i-10,i+11):
                        for l in range(j-10,j+11):
                            # check for negative index, if so break to next iteration
                            if k < 0 or l < 0:
                                break
                            # check for index values above 99, if so break
                            if k > 99 or l > 99:
                                break
                            if battlefield[k][l] != None:
                                if battlefield[k][l].group == "A":
                                    enemy_found = True
                    # select a random row and a random column
                    found_i = None
                    found_j = None
                    while enemy_found and found_i == None:
                        k = random.randint(i-10,i+10)
                        l = random.randint(j-10,j+10)
                        # check for negative index, if so continue to next iteration
                        if k < 0 or l < 0:
                            continue
                        # check for index value > 99, if so continue
                        if k > 99 or l > 99:
                            continue
                        if k != i:
                            if battlefield[k][l]: 
                                if battlefield[k][l].group == "A":
                                    found_i = k
                                    found_j = l
                        else:
                            if l != j:
                                if battlefield[k][l]:
                                    if battlefield[k][l].group == "A":
                                        found_i = k
                                        found_j = l
                    # deal damage to identified enemy
                    if found_i:
                        battlefield[found_i][found_j].life = battlefield[found_i][found_j].life - random.randint(10,60)
    # identifying agents with life score of score or below - and removing them from the grid
    for i in range(0,len(battlefield)):
        for j in range(0,len(battlefield)):
            if battlefield[i][j]:
                if battlefield[i][j].life <= 0:
                    battlefield[i][j] = None
# producing a plot of all battlefield locations, 10 iterations after
population = [[0.0 for i in range(0,100)] for i in range(0,100)]
# if agent is of type A, put a 1.0, if of type B, pyt a 2.0
for i in range(1,100):
    for j in range(1,100):
        if battlefield[i][j] == None: # empty
            pass # leave 0.0 in population cell
        elif battlefield[i][j].group == "A": # group A agents
            population[i][j] = 1.0 # 1.0 means "A"
        else: # group B agents
            population[i][j] = 2.0 # 2.0 means "B"
# import pyplot and colors from matplotlib
from matplotlib import pyplot, colors
# using colors from matplotlib, define a color map
colormap = colors.ListedColormap(["lightgrey","green","blue"])
# define figure size using pyplot
pyplot.figure(figsize = (12,12))
# using pyplot add a title
pyplot.title("battlefield after 50 iterations (green = A, blue = B)",
            fontsize = 24)
# using pyplot add x and y labels
pyplot.xlabel("x coordinates", fontsize = 20)
pyplot.ylabel("y coordinates", fontsize = 20)
# adjust x and y axis ticks, using pyplot
pyplot.xticks(fontsize = 16)
pyplot.yticks(fontsize = 16)
# use .imshow() method from pyplot to visualize agent locations
pyplot.imshow(X = population,
             cmap = colormap)
<matplotlib.image.AxesImage at 0x22495be1848>

In some following posts I will clean the code and pack its functionality into re-usable functions. I will then conduct a more comprehensive simulation study in which various parameters are varied to investigate their impact on battle outcome.

In this example I used Python lists as my agent containers of choice. However, similar models can be constructed using e.g. Python arrays or NumPy arrays.

Leave a Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

Close

Meta