This is a part of code of John Conway's GAME OF LIFE
import random
height = 100
width = 100
def randomize(grid, width, height):
for i in range(0, height):
for j in range(0, width):
grid[i][j] = random.randint(0,1)
grid_model = [0] * height
next_grid_model = [0] * height
for i in range(height):
grid_model[i] = [0] * width
next_grid_model[i] = [1] * width
def next_gen():
global grid_model, next_grid_model
for i in range(0, height):
for j in range(0, width):
cell = 0
count = count_neighbors(grid_model, i, j)
if grid_model[i][j] == 0:
if count == 3:
cell = 1
elif grid_model[i][j] == 1:
if count == 2 or count == 3:
cell = 1
next_grid_model[i][j] = cell
temp = grid_model
grid_model = next_grid_model
next_grid_model = temp
def count_neighbors(grid, row, col):
count = 0
if row-1 >= 0:
count = count + grid[row-1][col]
if (row-1 >= 0) and (col-1 >= 0):
count = count + grid[row-1][col-1]
if (row-1 >= 0) and (col+1 < width):
count = count + grid[row-1][col+1]
if col-1 >= 0:
count = count + grid[row][col-1]
if col + 1 < width:
count = count + grid[row][col+1]
if row + 1 < height:
count = count + grid[row+1][col]
if (row + 1 < height) and (col-1 >= 0):
count = count + grid[row+1][col-1]
if (row + 1 < height) and (col+1 < width):
count = count + grid[row+1][col+1]
return count
glider_pattern = [[0, 0, 0, 0, 0],
[0, 0, 1, 0, 0],
[0, 0, 0, 1, 0],
[0, 1, 1, 1, 0],
[0, 0, 0, 0, 0]]
glider_gun_pattern = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0],
[0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
def load_pattern(pattern, x_offset=0, y_offset=0):
global grid_model
for i in range(0, height):
for j in range(0, width):
grid_model[i][j] = 0
j = y_offset
for row in pattern:
i = x_offset
for value in row:
grid_model[i][j] = value
i = i + 1
j = j + 1
if __name__ == '__main__':
next_gen()
temp = grid_model
grid_model = next_grid_model
next_grid_model = temp
What I want to ask is why we need to swap grid_model and next_grid_model?
Consider this small example
000
111
It should be followed by this grid
010
010
upper corners are 0, and surrounded by two 1, so they remain 0
lower corners are 1, and surrounded by one 1, so they become 0
upper middle pixel is 0 and surrounded by three 1, so it becomes 1.
lower middle pixel is 1 and surrounded by two 1, so it stays 1
Now, what would happen if you were using only one grid, that is compute this in situ:
You would start with grid
000
111
First pixel is 0, surround by two 1, so it stays 0, and grid stays
000
111
Second pixel is 0 surrounded by three 1, so it becomes 1. And grid becomes
010
111
Third pixel is 0, surrounded by three 1, so it becomes 1, and grid becomes
011
111
Fourth pixel (on 2nd line) is 1, surrounded by two 1, it stays 1.
011
111
Fifth pixel is 1, surrounded by four 1, it becomes 0
011
101
At last, sixth pixel is 1, surrounded by two 1, it stays 1
011
101
So, result is completely different from the expected result.
You get grid
011
101
Instead of grid
010
010
That is not directly the answer to your question. It is the answer to "why can't I just compute using a single grid, and why I need to compute a next_grid from a current_grid".
And my answer shows that you need to compute a new grid from a current grid.
Now, you have two options to do so.
Either at each step you allocate and compute a new grid, that, once computed becomes the current grid, and the former current grid is simply forgotten. That force you to redo the next_grid_model = [0] * height and for i in range(height): next_grid_model[i] = [1] * width each time. But it would work
Or, you don't want to bother creating a brand new next_grid_model at each step. So you just reuse the grid_model variable, that still exists, and whose, after all, you don't need any more, since you intend to replace it by next_grid_model.
That is the reason for the swap:
next_grid_model becomes grid_model, because, that is the game (I take you get that part. Again, from the first part of my answer, you probably understand why we need to compute a next_grid_model instead of updating grid_model directly. But then next_grid_model, as its name indicate, becomes the new grid_model.
And we need to create a new 2d-array to be the new next_grid_model. And if we don't want to create it from scratch, one solution is to say that the old grid_model will host the future next_grid_model. Hence the swap.
Note that such a trick is more important in other languages than in python. In C, for example, to allocate both grid, we would have to call malloc.
And calling malloc to create a new grid at each stage, while calling free on the former grids is a little bit stupid (allocating exactly the same amount of memory we are freeing — without free, it is even worse, and a memory leak). So it is quite classical in such case, to swap the "current" and "future" memories, instead of allocating a new one each time.
In python, if you were to just create a new next_grid_model each time, it would also work. Garbage collector would take care of all the former grids that you dropped, and everything would be fine. But even if it would work, that still would mean that you have to to the job of creating a new next_grid_model each time.
So a shorter answer could have been:
at each stage you need either to
next_grid_model = [0] * height
for i in range(height):
next_grid_model[i] = [1] * width
Or to
# Just a swap (I write it that way, because it is more compact,
# but it is the same thing as your 3 lines involving a temp variable)
next_grid_model, grid_model = grid_model, next_grid_model
Both works. They are just two way to create a future next_grid_model. Swap is simply faster.
Related
I need to draw a pattern in the image given below without using any libraries in python.
Input: 3 4 3 4 5 1 9 2 2 4
Image:
Below is the code i tried but it does not working
def triangle(*args):
main=[]
for num in range(len(args)):
k = args[num] - 1
patt = []
for i in range(0, args[num]):
hello = ''
for j in range(0, k):
hello = hello + " "
if num%2==0:
k = k - 1
hello = hello+"/"
else:
k = k+ 1
hello = hello + "\\"
patt.append(hello)
main.append(patt)
return main
lists = triangle(3,4,3)
I don't know if I understand your question, but you could print it to the terminal without any modules.
In order to fit in bigger figures, you have to modify the length and amount of the rows. The lines don't connect perfectly, but you can definitely see the graph.
number_input = [4, 3, 2, 2, 5]
# default pattern
pattern = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
height = 5 # starting row (adaptable)
rows = 14 # rows of the pattern
lines = 0 # lines added to the pattern
def figure(height, lines, nums):
print(nums)
for i in range(len(nums)):
# if i is even it adds '/'
if i % 2 == 0:
for j in range(nums[i]):
pattern[rows-height-1][lines] = '/'
height += 1 # increase height by 1
lines += 1 # count the lines added to the pattern
height -= 1
# if i is odd it adds '\'
elif i % 2 == 1:
for j in range(nums[i]):
pattern[rows-height-1][lines] = '\\'
height -= 1 # decrease height by 1
lines += 1
height += 1
figure(height, lines, number_input)
# replace zeros with spaces
for line in pattern:
for i, num in enumerate(line):
if num == 0:
line[i] = ' '
# print the lines
print(*line)
Hope I could help.
I'm trying to make a simple approach to a battleships-bot in python, but I'm having some issues regarding my "shot" function repeating the same coordinates. Here are some of my code:
import numpy as np
import random as rnd
numberlist = [1,3,5,7,9]
numberlist2 = [0,2,4,6,8]
my_shots = np.array([
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
])
def letter_to_column(letter):
"""Takes in lettervalue, and returns what column it corresponds to,
zero-indexed.
Example:
letter_to_column('A') # => 0
"""
return "ABCDEFGHIJ".index(letter.upper())
def find_value(coordinatestring):
"""Takes in a coordinatestring in the form "letternumber" and returns wheter it is a boat
in the coordinate or not
"""
letter = coordinatestring[0]
row = int(coordinatestring[1])
col = letter_to_column(bokstav)
value = my_shots[row][col]
return value
This is the function that repeats the places it shoots, I would appreciate if anyone has any solution to how to make the function not repeat the "lettervalues" it has already given.
def shot(letter = "A", number = "0"):
# Chekcs if I already have shot there
while find_value(letter + number) == 1:
letter = rnd.choice("ABCDEFGHIJ")
# Uses the "chess method" by only shooting on every other route
if letter_to_column(letter) % 2 == 0:
number = str(rnd.choice(numberlist2))
else:
number = str(rnd.choice(numberlist))
If the board is "small" like in Battleship, one strategy you could employ is to generate all the shots at once and then just shuffle them. This works like a champ when you have only 100 locations and avoids a clunky "while this_shot != last_shot" type of setup.
import random
letters = list('ABCDEFGHIJ')
nums = range(9)
all_shots = [''.join([str(num), letter]) for num in nums for letter in letters]
random.shuffle(all_shots)
I have simplified a version of a problem I am attempting to model where I am using Simpy to describe the movement of travellers along a path.
The path is represented by a collection of Node() objects where each node contains a Simpy.Resource. Each node is connected to the next node in the path by the connected_to attribute. In the example code I have created a list of 10 nodes where each node in the list is connected to the preceding node in the list.
When a traveller (represented by an Occupier() object) is instantiated it is allocated the resource of a node. The traveller then moves along the nodes, only taking a step if the next node is available. My aim is for the traveller to simultaneously be allocated its destination node and release the node where it was previously located.
import simpy
class Node(object):
def __init__(self, env):
self.env = env
self.resource = simpy.Resource(self.env)
self.up_connection = None
self.travel_delay = 5
class Occupier(object):
def __init__(self, env):
self.env = env
self.location = None
self.destination = None
self.requests = []
def travel(self, instantiation_loc):
self.requests.append(instantiation_loc.resource.request())
yield(self.requests[-1])
self.location = instantiation_loc
self.destination = instantiation_loc.up_connection
yield self.env.timeout(self.location.travel_delay)
node_occupancy(nodes)
while self.destination.up_connection != None:
self.requests.append(self.destination.resource.request())
yield self.requests[-1]
self.location.resource.release(self.requests[0])
self.requests.pop(0)
self.location = self.destination
self.destination = self.location.up_connection
yield self.env.timeout(self.location.travel_delay)
node_occupancy(nodes)
def node_occupancy(nodes):
print([node.resource.count for node in nodes])
env = simpy.Environment()
nodes = [Node(env) for i in range(10)]
for i in range(len(nodes) - 1):
nodes[i].up_connection = nodes[i + 1]
env.process(Occupier(env).travel(nodes[0]))
env.run()
If I run the above code with one traveller it seems to work fine giving the following output:
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 1, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0]
However, if instantiate a second traveller you can see that there are points in time where one traveller is occupying two resources when it should only occupy one:
env.process(Occupier(env).travel(nodes[3]))
env.process(Occupier(env).travel(nodes[0]))
corresponding output:
[1, 0, 0, 1, 0, 0, 0, 0, 0, 0]
[1, 0, 0, 1, 1, 0, 0, 0, 0, 0]
[0, 1, 0, 0, 1, 0, 0, 0, 0, 0]
[0, 1, 0, 0, 1, 1, 0, 0, 0, 0]
[0, 0, 1, 0, 0, 1, 0, 0, 0, 0]
[0, 0, 1, 0, 0, 1, 1, 0, 0, 0]
[0, 0, 0, 1, 0, 0, 1, 0, 0, 0]
[0, 0, 0, 1, 0, 0, 1, 1, 0, 0]
[0, 0, 0, 0, 1, 0, 0, 1, 0, 0]
[0, 0, 0, 0, 1, 0, 0, 1, 1, 0]
[0, 0, 0, 0, 0, 1, 0, 0, 1, 0]
[0, 0, 0, 0, 0, 1, 0, 0, 1, 0]
[0, 0, 0, 0, 0, 0, 1, 0, 1, 0]
[0, 0, 0, 0, 0, 0, 0, 1, 1, 0]
It is important for my simulation that a traveller only ever occupies one resource as the attributes of the nodes are amended frequently based on this.
Is there any way to prevent this behaviour where a traveller never occupies more than one resource? i.e. a resource is simultaneously released when the traveller is allocated a new resource
In fact, your model is running correctly.
Try to add to the function node_ocupacy, the current execution time and some markers to identify the current stage of the simulation:
def node_occupancy(nodes, node, case):
print(env.now, node, case, [node.resource.count for node in nodes])
Also, I made some changes just to see a better simulation log:
def travel(self, instantiation_loc, loc):
self.requests.append(instantiation_loc.resource.request())
yield(self.requests[-1])
self.location = instantiation_loc
self.destination = instantiation_loc.up_connection
yield self.env.timeout(self.location.travel_delay)
node_occupancy(nodes, loc, 1)
while self.destination.up_connection != None:
self.requests.append(self.destination.resource.request())
node_occupancy(nodes, loc, 2)
yield self.requests[-1]
node_occupancy(nodes, loc, 3)
self.location.resource.release(self.requests[0])
node_occupancy(nodes, loc, 4)
self.requests.pop(0)
self.location = self.destination
self.destination = self.location.up_connection
yield self.env.timeout(self.location.travel_delay)
node_occupancy(nodes, loc, 5)
Now, run the simulation with a marker for the current node:
env.process(Occupier(env).travel(nodes[3], 3))
env.process(Occupier(env).travel(nodes[0], 0))
Look at the results and you will notice that events (request/release) occurs at the same time and the simultaneous resource time occupation is always = 0 (i.e.: the time between stages '3' and '4', for the same entity will be always 0):
5 3 1 [1, 0, 0, 1, 0, 0, 0, 0, 0, 0]
5 3 2 [1, 0, 0, 1, 1, 0, 0, 0, 0, 0]
5 0 1 [1, 0, 0, 1, 1, 0, 0, 0, 0, 0]
5 0 2 [1, 1, 0, 1, 1, 0, 0, 0, 0, 0]
5 3 3 [1, 1, 0, 1, 1, 0, 0, 0, 0, 0]
5 3 4 [1, 1, 0, 0, 1, 0, 0, 0, 0, 0]
5 0 3 [1, 1, 0, 0, 1, 0, 0, 0, 0, 0]
5 0 4 [0, 1, 0, 0, 1, 0, 0, 0, 0, 0]
I am a relative beginner to python, and in order to strengthen my skills, I am (attempting) to write a compiler for the Brainfu** language. All is good, except for the bracket [] loops. The program I am using to test my code is >++[>++<-]>+, which should set cell 2 to 5. When I run this, however, it does this:
0 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 0 >
1 [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 1 +
2 [0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 2 +
3 [0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 2 [
4 [0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 0 >
5 [0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 1 +
6 [0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 2 +
7 [0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 2 <
8 [0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 1 -
3 [0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 1 [
10 [0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 2 >
11 [0, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 3 +
[0, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
(The lines are formatted in the iteration, then the list at that point, then the value it's focused on and then the character it's running.)
My current code is
def generateArray(code):
array = []
for i in range(0,20):
array.append(0);
return array
def run(code):
print code
data = generateArray(code)
chars = list(code)
pointer = 0
for i in range(0, len(chars)):
current = chars[i]
if(current == "+"):
data[pointer] += 1
if(current == ">"):
pointer += 1
if(current == "-"):
data[pointer] -= 1
if(current == "<"):
pointer -= 1
if(current == "."):
print str(chr(data[pointer]))
if(current == ","):
given = raw_input()
data[pointer] = ord( given )
if(current == "["):
posOfEnd = chars[i:len(chars)].index("]")
if(data[pointer] == 0):
i += posOfEnd+1
if(current == "]"):
posOfBegin = len(chars) - 1 - chars[::-1].index('[')
i = posOfBegin
print i, data, data[pointer], chars[i]
return data
print run(">++[>++<-]>+")
posOfEnd is trying to find out where the next bracket is, and posOfBegin is trying to find out where the previous bracket is.
I suppose the problem is your loop variable i which you modify during the loop:
i += posOfEnd+1
and
i = posOfBegin
However python for loops are different from their C/C++ counterparts. In python the variable i will be set to each element of the iterable you provide it, in this case range. range(n) evaluates to a list containing all numbers from 0 up to n-1. If you modify your loop variable during an iteration then this modification remains for only that iteration but for the next iteration the loop variable will be assigned the next element of the iterable (not preserving your modifications).
You might want to use a while loop instead.
I'm working through the O'Reilly book "Exploring Everyday Things in R and Ruby" and am trying to rewrite all of the Ruby code in Python. The first example is a model for figuring out how many bathrooms a building needs. The code that I am working with is below.
When I run the example34.py file, however, I get the following error:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "example34.py", line 39, in <module>
new_restroom.enter(queue.pop(0))
File "restroom.py", line 21, in enter
unoccupied_facilities[0].occupy(person)
File "restroom.py", line 46, in occupy
Person.population.remove(person)
ValueError: list.remove(x): x not in list
I am new to Python, so I suspect this is an issue related to variable scope or some unknown (to me) property of Python. The strange thing is that after it breaks, running person in Person.population returns True, and the command Person.population.remove(person) succeeds in removing that person instance, so the person clearly is in the list.
Any ideas?
example34.py:
from restroom import * # imports the model
# Simulation script 1
frequency = 3 # how many times a person goes to the restroom within the period
facilities_per_restroom = 3
use_duration = 1 # measured in ticks
population_range = range(100, 110, 10) # Creates an array from 10 to 600 by 10s
# Stores data for printing later
data = {}
# Loops for each population size in the above range
for population_size in population_range:
# Starts each loop fresh
Person.population = []
# Creates the population of people
Person.population = [
Person(frequency, use_duration) for
each in range(population_size)]
# Creates the key for this population size
data[population_size] = []
#Create the restroom
new_restroom = Restroom(facilities_per_restroom)
# Iterate over the period
for each in range(duration):
# Records the queue size at this tick
data[population_size].append(len(new_restroom.queue))
# Create a temporary queue so that we can sort people between the
# facilities and the restroom queue for this "tick"
queue = list(new_restroom.queue)
# Clear the queue to prepare for sorting
new_restroom.queue = []
# Take each person from the temporary queue and try adding
# them a facility
while bool(queue):
# De-queue the person at the front of the line, place in an
# unoccupied facility or, if none, back to the restroom queue
new_restroom.enter(queue.pop(0))
# for each person in the population, check if they need to go
for person in Person.population:
if person.need_to_go():
new_restroom.enter(person)
new_restroom.tick()
print(data)
restroom.py:
from random import randint
duration = 9 * 60 # minutes
class Restroom(object):
def __init__(self, facilities_per_restroom=3):
# Start with an empty queue
self.queue = []
# The facilities in this restroom
self.facilities = []
# Creates the facilities
self.facilities = ([Facility() for each in
range(facilities_per_restroom)])
def enter(self, person):
unoccupied_facilities = [
facility for facility in self.facilities
if facility.occupied() == 0]
if unoccupied_facilities:
unoccupied_facilities[0].occupy(person)
else:
self.queue.append(person)
Person.population.remove(person)
def tick(self):
[each.tick() for each in self.facilities]
[f.tick for f in self.facilities]
class Facility(object):
def __init__(self):
self.occupier = None # no one is occupying this facility at the start
self.duration = 0 # how long the facility has been occupied
def occupied(self):
return 1 if (self.occupier is not None) else 0
def occupy(self, person):
# if the facility is unoccupied, add the person. Else, return false.
if not self.occupied():
self.occupier = person
self.duration = 1
#remove the person from the population since
# they're in a facility now
Person.population.remove(person)
return 1 # return true
else:
return 0 # Return false
def vacate(self):
Person.population.append(self.occupier)
self.occupier = None
def tick(self):
# if the facility is occupied and they've been
# there longer than the use duration, have them leave
if self.occupied() and (self.duration > self.occupier.use_duration):
self.vacate()
self.duration = 0
elif self.occupied():
# If occupied, increment the time
self.duration += 1
class Person(object):
#Class variable for storing the entire population
population = []
def __init__(self, frequency=4, use_duration=1):
# Number of times a person uses the facilities per day
self.frequency = frequency
# How long each person uses the facilities
self.use_duration = use_duration
def need_to_go(self):
return randint(1, duration) <= self.frequency
You've encountered a simple bug in your code.
The rest room has a queue, to which Person objects are added for which there is no stall available.
The logic goes:
def enter(self, person):
unoccupied_facilities = [
facility for facility in self.facilities
if facility.occupied() == 0]
if unoccupied_facilities:
unoccupied_facilities[0].occupy(person)
else:
self.queue.append(person)
Person.population.remove(person)
Note that either .occupy() removes a person from the Person.population list, or if there are no stalls available, then the person is added to self.queue and also removed from Person.population.
The example34 code then processes that queue:
queue = list(new_restroom.queue)
# Clear the queue to prepare for sorting
new_restroom.queue = []
# Take each person from the temporary queue and try adding
# them a facility
while bool(queue):
# De-queue the person at the front of the line, place in an
# unoccupied facility or, if none, back to the restroom queue
new_restroom.enter(queue.pop(0))
So people are taken from the queue, and reentered into the restroom. If any of the stalls are empty, unoccupied_facilities[0].occupy(person) tries to remove that person from Person.population again. At this point the exception is raised, because that person is most definitely not in that list anymore.
The solution is to not remove person from the population in two different places, or to re-add the person back into the population when processing the queue.
Changing the queue processing code to:
while bool(queue):
# De-queue the person at the front of the line, place in an
# unoccupied facility or, if none, back to the restroom queue
person = queue.pop(0)
Person.population.append(person)
new_restroom.enter(person)
fixes the issue, for example:
$ python2.7 example34.py
{100: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 1, 0, 0, 0]}
But just by looking at the stacktrace it clearly states ValueError: list.remove(x): x not in list. The person you are trying to remove is not in the list. So You need to catch this exception and handle it appropriately -
#some loop, get the person to be removed
...
try:
Person.population.remove(person)
except ValueError:
pass #silently continue. Either that person is already removed or is not present.
...
Of course this code is not complete. It was put to merely highlight the use of try: except ValueError: usage.