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. NumPy arrays.
Leave a Reply