-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathagents.py
More file actions
890 lines (719 loc) · 35.1 KB
/
agents.py
File metadata and controls
890 lines (719 loc) · 35.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
import mesa
import operator
# calculating average position for swarm movement
# There is a problem with this method when the grid is torodial
# when a swarm scouts targets on both sides of the grid at the same time
# the average is in the middle, which means, they all try to go there
# a better option might be to set the target to the median/ the most occuring position
# this function not very efficient!!!
def get_average_pos(lst):
if len(lst) == 0:
return []
x = 0
y = 0
counter = 0
for pos in lst:
x += pos[0]
y += pos[1]
counter += 1
# returns a list with one tuple, which was needed for after the reset, should maybe be changed but works
return [(round(x/counter), round(y/counter))]
# Every cell on the grid contains has a soil on self.model.grid.get_cell_list_contents([self.pos])[0]
# it contains nutrients and maybe more in the future
class Soil(mesa.Agent):
def __init__(self, unique_id, model, pos):
super().__init__(unique_id, model)
################################
### CUSTOMIZABLE VARIABLES
################################
# amount of turns between refuels of nutrients
self.refuel_timer = 60
# amount of nutrients to refuel
self.refuel_amount = 2
################################
################################
################################
self.age = 0
self.pos = pos
# temperature doesnt do anything
self.temperature = 5
# not that important due to the refuel sources
self.nutrients = {
# Type_a food
"iron": 5,
# Type_b food
"chicken":2,
# Type_c food
"organic":7
}
self.antibiotics = {
}
def step(self):
self.age += 1
if self.age % self.refuel_timer:
self.nutrients = dict.fromkeys(self.nutrients, self.refuel_amount)
# if self.random.random() < 0.001:
# #if self.age % 50 == 0 and self.random.random() < 0.5:
# self.nutrients = dict.fromkeys(self.nutrients, 2)
# possible_postitions = self.model.grid.get_neighborhood(
# self.pos, moore=False, include_center=False, radius=1
# )
# for position in possible_postitions:
# soil = self.model.grid.get_cell_list_contents([position])[0]
# soil.nutrients = dict.fromkeys(soil.nutrients, 1)
# self.nutrients = dict.fromkeys(self.nutrients, 1)
return
# Staphylococcus aureus
class Type_a(mesa.Agent):
def __init__(self, unique_id, model, pos):
super().__init__(unique_id, model)
################################
### CUSTOMIZABLE VARIABLES
################################
# spreads the dying, to not create big bumps in the graph
# example: average 40 turns --> 1/40 = 0.025
self.dying_chance = 0.025
# acts as health of the bacteria
self.sturdiness = 1
# limits the number of bacteria in a single cell for performance and better spreading
self.max_num_bacteria_in_cell = 5
# if no cell with less than self.max_num_bacteria_in_cell is found, reproduction will not take place
self.reproduction_radius = 2
# chance to spread when self.max_num_bacteria_in_cell is not reached, to fasten the spread
self.random_spread_chance = 0.1
################################
################################
################################
self.nutrition_list = ["iron"]
self.pos = pos
self.age = 0
self.has_eaten = False
# doesnt do anything when being eaten
self.is_eaten = False
# Wird bei jedem Durchgang aufgerufen
def step(self):
self.age += 1
# if bacteria is eaten by another thing, it doesnt do anything (it will be killed by the other party)
if not self.is_eaten:
self.eat()
# when maximum number in model is reached, reproduction is paused
if not self.model.reproduction_stop_a:
self.reproduce()
self.die()
def eat(self):
# get soil
soil = self.model.grid.get_cell_list_contents([self.pos])[0]
# Wenn der Boden Nährstoffe enthält, kann gefressen werden
for nutrient in self.nutrition_list:
# look if consumable nutrients in soil
if nutrient in soil.nutrients and soil.nutrients[nutrient] > 0:
# subract nutrient and set has_eaten
soil.nutrients[nutrient] -= 1
self.has_eaten = True
break
def reproduce(self):
if self.has_eaten:
# Wenn bereits mehr als max_num_bacteria_in_cell Bakteriean auf einem Feld sind, oder Zufällig random_spread_chance
if len(self.model.grid.get_cell_list_contents([self.pos])) > self.max_num_bacteria_in_cell or self.random.random() < self.random_spread_chance:
possible_postitions = self.model.grid.get_neighborhood(
self.pos, moore=self.model.reproduction_spread_moore, include_center=False, radius=self.reproduction_radius
)
# random position
self.model.random.shuffle(possible_postitions)
for position in possible_postitions:
if len(self.model.grid.get_cell_list_contents([position])) <= self.max_num_bacteria_in_cell:
new_position = position
break
else:
new_position = None
# my position not full
else:
new_position = self.pos
# if all possible positions already contain max_num_bacteria_in_cell, reproduction is canceled
if new_position != None:
new_bacteria = Type_a(self.model.next_id(), self.model, new_position)
self.model.grid.place_agent(new_bacteria, new_position)
self.model.schedule.add(new_bacteria)
self.has_eaten = False
def die(self):
if self.random.random() < self.dying_chance:
self.model.grid.remove_agent(self)
self.model.schedule.remove(self)
# Staphylococcus Typ 1
class Type_a_1(mesa.Agent):
def __init__(self, unique_id, model, pos):
super().__init__(unique_id, model)
################################
### CUSTOMIZABLE VARIABLES
################################
# spreads the dying, to not create big bumps in the graph
# example: average 40 turns --> 1/40 = 0.025
self.dying_chance = 0.025 # doesnt do anything, cant die on its own at the moment
# acts as health of the bacteria
self.sturdiness = 1
# limits the number of bacteria in a single cell for performance and better spreading
self.max_num_bacteria_in_cell = 2
# if no cell with less than self.max_num_bacteria_in_cell is found, reproduction will not take place
self.reproduction_radius = 2
# chance to spread when self.max_num_bacteria_in_cell is not reached, to fasten the spread
self.random_spread_chance = 0.1
# scouting is done in a moore radius, scouting for stressed_by
self.scouting_radius = 1
# when a object of this type is found in the scouting radius, I get stressed
self.stressed_by = [Type_a_2]
# radius in which the antibiotica will be spread
self.stress_radius = 4
# nutrition and antibiotics need to be in the respective dict in the Soil object
self.nutrition_list = ["iron"]
################################
################################
################################
self.pos = pos
self.age = 0
self.has_eaten = False
self.is_stressed = False
# doesnt do anything when being eaten
self.is_eaten = False
# Wird bei jedem Durchgang aufgerufen
def step(self):
self.age += 1
# looking for problems, triggers stress
self.scout()
# stress reaction
if self.is_stressed:
self.stress_reaction()
# if bacteria is eaten by another thing, it doesnt do anything (it will be killed by the other party)
if not self.is_eaten:
self.eat()
self.reproduce()
self.die()
# scans the area for stress factors, when found, I get stressed
def scout(self):
# scanned positions
positions = self.model.grid.get_neighborhood(
self.pos, moore=True, include_center=True, radius=self.scouting_radius
)
# objects on position
inhabitants = self.model.grid.get_cell_list_contents(positions)
for inhabitant in inhabitants:
for bacteria in self.stressed_by:
# if inhabitant is on stressed_by list, I get stressed
if isinstance(inhabitant, bacteria):
self.is_stressed = True
break
else:
self.is_stressed = False
# eat nutrients from soil
def eat(self):
# get soil
soil = self.model.grid.get_cell_list_contents([self.pos])[0]
# if there are nutrients, first nutrient on nutrition_list gets consumed
for nutrient in self.nutrition_list:
# look if consumable nutrients in soil
if nutrient in soil.nutrients and soil.nutrients[nutrient] > 0:
# subract nutrient and set has_eaten
soil.nutrients[nutrient] -= 1
self.has_eaten = True
break
def reproduce(self):
# reproduces once eaten
if self.has_eaten:
# Wenn bereits mehr als max_num_bacteria_in_cell Bakteriean auf einem Feld sind, oder Zufällig random_spread_chance
if len(self.model.grid.get_cell_list_contents([self.pos])) > self.max_num_bacteria_in_cell or self.random.random() < self.random_spread_chance:
# if it spreads, we randomly look for a position in the neighborhood
possible_postitions = self.model.grid.get_neighborhood(
self.pos, moore=self.model.reproduction_spread_moore, include_center=False, radius=self.reproduction_radius
)
# shuffeling the positions
self.model.random.shuffle(possible_postitions)
for position in possible_postitions:
# checking if the position is already occupied
if len(self.model.grid.get_cell_list_contents([position])) <= self.max_num_bacteria_in_cell:
new_position = position
break
else:
# this is only needed if there are no good positions, so new_position is defined
new_position = None
else:
# own position is good for a new cell
new_position = self.pos
# if all possible positions already contain max_num_bacteria_in_cell, reproduction is canceled
if new_position != None:
# creating and placing new bacteria
new_bacteria = Type_a_1(self.model.next_id(), self.model, new_position)
self.model.grid.place_agent(new_bacteria, new_position)
self.model.schedule.add(new_bacteria)
# has_eaten reset
# if all neighboring positions are occupied, no new cell will be created and has_eaten will be reset anyway
# this was a good was to control the spread, but can be changed if you wish so
self.has_eaten = False
# cannot die at the moment
# function and code is kept here, for easier customization
def die(self):
return
#if self.random.random() < self.dying_chance:
# self.model.grid.remove_agent(self)
# self.model.schedule.remove(self)
# dont know if the antibiotica is a stress reaction or a normal function
# i think its easier to change it from this to a normal function, than the other way around
def stress_reaction(self):
# spread antibiotica in all neighboring cells
neighboring_cells = self.model.grid.get_neighborhood(
self.pos, moore=True, include_center=True, radius=self.stress_radius
)
# add antibiotica up, maybe this should be limited, as now it is like unlimited
for cell in neighboring_cells:
soil = self.model.grid.get_cell_list_contents([cell])[0]
# create or add antibiotica
if 'Type_a_2' in soil.antibiotics:
soil.antibiotics['Type_a_2'] += 1
else:
soil.antibiotics['Type_a_2'] = 1
# Staphylococcus Typ 2
class Type_a_2(mesa.Agent):
def __init__(self, unique_id, model, pos):
super().__init__(unique_id, model)
################################
### CUSTOMIZABLE VARIABLES
################################
# spreads the dying, to not create big bumps in the graph
# example: average 40 turns --> 1/40 = 0.025
self.dying_chance = 0.025
# acts as health of the bacteria
self.sturdiness = 1
# limits the number of bacteria in a single cell for performance and better spreading
self.max_num_bacteria_in_cell = 2
# if no cell with less than self.max_num_bacteria_in_cell is found, reproduction will not take place
self.reproduction_radius = 2
# chance to spread when self.max_num_bacteria_in_cell is not reached, to fasten the spread
self.random_spread_chance = 0.1
# if True it wont spread on fields containing antibiotics against it
# False creates a bacteria free zone between type_a_1 and type_a_2
self.spread_in_antibiotics = False
# nutrition and antibiotics need to be in the respective dict in the Soil object
self.nutrition_list = ["iron"]
self.antibiotics_list = ["Type_a_2"] # is created dynamically by type_a_1
################################
################################
################################
self.pos = pos
self.age = 0
self.has_eaten = False
# doesnt do anything when being eaten
self.is_eaten = False
# Wird bei jedem Durchgang aufgerufen
def step(self):
self.age += 1
# if bacteria is eaten by another thing, it doesnt do anything (it will be killed by the other party)
if not self.is_eaten:
self.eat()
self.reproduce()
self.die()
# eat nutrients from soil
def eat(self):
# get soil
soil = self.model.grid.get_cell_list_contents([self.pos])[0]
# if there are nutrients, first nutrient on nutrition_list gets consumed
for nutrient in self.nutrition_list:
# look if consumable nutrients in soil
if nutrient in soil.nutrients and soil.nutrients[nutrient] > 0:
# subract nutrient and set has_eaten
soil.nutrients[nutrient] -= 1
self.has_eaten = True
break
def reproduce(self):
if self.has_eaten:
# Wenn bereits mehr als max_num_bacteria_in_cell Bakteriean auf einem Feld sind, oder Zufällig random_spread_chance
if len(self.model.grid.get_cell_list_contents([self.pos])) > self.max_num_bacteria_in_cell or self.random.random() < self.random_spread_chance:
# if it spreads, we randomly look for a position in the neighborhood
possible_postitions = self.model.grid.get_neighborhood(
self.pos, moore=self.model.reproduction_spread_moore, include_center=False, radius=self.reproduction_radius
)
# shuffeling the positions
self.model.random.shuffle(possible_postitions)
# checking if the position is already occupied
for position in possible_postitions:
pos_contents = self.model.grid.get_cell_list_contents([position])
if len(pos_contents) <= self.max_num_bacteria_in_cell:
# if it can spread in antibiotics
# creates different outcomes
if not self.spread_in_antibiotics:
# spread not possible if antibiotic is in soil, next position will be checked
for antibiotic in self.antibiotics_list:
if antibiotic in pos_contents[0].antibiotics and pos_contents[0].antibiotics[antibiotic] > 0:
new_position = None
continue
new_position = position
break
else:
# this is only needed if there are no good positions, so new_position is defined
new_position = None
else:
# own position is good for a new cell
new_position = self.pos
# if all possible positions already contain max_num_bacteria_in_cell, reproduction is canceled
if new_position != None:
# creating and placing new bacteria
new_bacteria = Type_a_2(self.model.next_id(), self.model, new_position)
self.model.grid.place_agent(new_bacteria, new_position)
self.model.schedule.add(new_bacteria)
# has_eaten reset
# if all neighboring positions are occupied, no new cell will be created and has_eaten will be reset anyway
# this was a good was to control the spread, but can be changed if you wish so
self.has_eaten = False
# dies if its on the same field as soil that contains an antibiotic from antibiotics_list
def die(self):
soil = self.model.grid.get_cell_list_contents([self.pos])[0]
for antibiotic in self.antibiotics_list:
# check for antibiotic
if antibiotic in soil.antibiotics and soil.antibiotics[antibiotic] > 0:
# die
soil.antibiotics[antibiotic] -= 1
self.model.grid.remove_agent(self)
self.model.schedule.remove(self)
#if self.random.random() < self.dying_chance:
# self.model.grid.remove_agent(self)
# self.model.schedule.remove(self)
# Myxobakterien
class Type_d(mesa.Agent):
def __init__(self, unique_id, model, pos, swarm_id):
super().__init__(unique_id, model)
################################
### CUSTOMIZABLE VARIABLES
################################
# objects this bacteria can eat
self.hunting_list = (Type_a, Type_b, Type_c, Type_a_1, Type_a_2)
# chance of reproduction calculation: reproduciton_rate * eating_multiplier
self.reproduciton_rate = 0.025
# chances to reproduce are calculated reproduciton_rate * eating_multiplier
# more likely to reproduce when bacteria ate, but also randomly possible
# eating multiplier gets decreased after reproduction
self.eating_multiplier = 0.5
# acts as health of the bacteria
self.sturdiness= 5
# amount of turns until it dies
self.time_to_die = 40
# scouting is done in a moore radius, scouting for hunting_list
self.scouting_radius = 4
# chances of moving random, when food has already been found, to possibly find new nutrition grounds
self.random_move_chance = 0.1
# distance of the random move
self.random_move_distance = 2
# chance to spread when self.max_num_bacteria_in_cell is not reached, to fasten the spread
self.random_spread_chance = 0.1
# limits the number of bacteria in a single cell for performance and better spreading
# limit while moving
self.max_num_bacteria_in_cell = 5
# limit while reproducing
self.max_num_bacteria_in_cell_reproduction = 1
# spread nutrition for vicitims so they can reproduce, this is currently done in the Soil with refuel
self.spread_after_death = False
################################
################################
################################
self.nutrition_list = []
self.pos = pos
self.age = 0
self.swarm_id = swarm_id
# Here to make Type_d die, so Type_a can grow again
self.reproduction_counter = 0
self.not_eaten_multiplier = 1
# eat one cell until its dead
self.is_eating = False
self.victim = None
def step(self):
self.age += 1
self.eat()
self.scout()
# doesnt move while eating
if not self.is_eating:
self.move()
# when maximum number in model is reached, reproduction is paused
if not self.model.reproduction_stop_d:
self.reproduce()
self.die()
def eat(self):
self.not_eaten_multiplier += 0
# already eating a cell, but victim is still alive
if (self.is_eating):
# biting through the sturdiness piece by piece
self.victim.sturdiness -= 1
# killing other cell
if self.victim.sturdiness == 0:
# chance of reproducing gets higher
self.not_eaten_multiplier = 0
# better chance to reproduce
self.eating_multiplier += 4
# removing victim
self.model.grid.remove_agent(self.victim)
self.model.schedule.remove(self.victim)
self.is_eating = False
self.victim = None
# new victim
else:
# looking for something easy to eat
cellmates = self.model.grid.get_cell_list_contents([self.pos])
# remove soil
cellmates.pop(0)
# lesat sturdiness first sort
cellmates.sort(key=lambda x: x.sturdiness)
for cellmate in cellmates:
if isinstance(cellmate, self.hunting_list) and not cellmate.is_eaten:
# starting the eating process, first "bite" is done in the next move
self.is_eating = True
cellmate.is_eaten = True
self.victim = cellmate
break
# look for eatable objects
def scout(self):
# all neighborhood positions
postitions = self.model.grid.get_neighborhood(
self.pos, moore=True, include_center=True, radius=self.scouting_radius
)
# looping neighborhood
for position in postitions:
# looping inhabitants
inhabitants = self.model.grid.get_cell_list_contents([position])
for inhabitant in inhabitants:
if isinstance(inhabitant, tuple(self.hunting_list)):
# all possible targets get added to a list, the swarm moves towards the average of this list
self.model.swarm_target[self.swarm_id].append(position)
# swarm movement
def move(self):
# swarm can be slowed down in the model, but will always move together
if self.model.swarm_move:
# if there is a target, the swarm will move towards it
if len(self.model.swarm_target[self.swarm_id]) > 0 and self.random.random() > self.random_move_chance:
# every cell calculates the average target, which is very inefficient
target = get_average_pos(self.model.swarm_target[self.swarm_id])
# calculates shortes path to target, even in torrodial grid
# if target is on the same position, it wont move
new_position = list(self.pos)
# step one: decide if target is left/rigth, up/down
# step two: if torus, calculate distance to target
# if the target is removed more than half the size of the grid
# we go the other direction
# target on the right
if self.pos[0] < target[0][0]:
if (not self.model.grid.torus) or target[0][0] - self.pos[0] < self.model.grid.width / 2:
# move right
new_position[0] += 1
else:
# move left
new_position[0] -= 1
# target on the left
elif self.pos[0] > target[0][0]:
if (not self.model.grid.torus) or self.pos[0] - target[0][0] < self.model.grid.width / 2:
# move left
new_position[0] -= 1
else:
# move right
new_position[0] += 1
# target above
if self.pos[1] > target[0][1]:
if (not self.model.grid.torus) or self.pos[1] - target[0][1] < self.model.grid.height / 2:
# move up
new_position[1] -= 1
else:
# move down
new_position[1] += 1
# target below
elif self.pos[1] < target[0][1]:
if (not self.model.grid.torus) or target[0][1] - self.pos[1] < self.model.grid.height / 2:
# move down
new_position[1] += 1
else:
# move up
new_position[1] -= 1
new_position = self.model.grid.torus_adj(tuple(new_position))
else:
# direction changes in the model, so the swarm stays together
direction = self.model.swarm_direction[self.swarm_id]
# if it is not a torus and tries to go out of bounds, the swarm will go to the opposite direction
if direction == "left":
# check if out of bounds
if (not self.model.grid.torus) and self.model.grid.out_of_bounds((self.pos[0] - self.random_move_distance, self.pos[1])):
# change direction
self.model.swarm_direction[self.swarm_id] = "right"
return
# move
new_position = (self.pos[0] - self.random_move_distance, self.pos[1])
elif direction == "right":
# check if out of bounds
if (not self.model.grid.torus) and self.model.grid.out_of_bounds((self.pos[0] + self.random_move_distance, self.pos[1])):
# change direction
self.model.swarm_direction[self.swarm_id] = "left"
return
# move
new_position = (self.pos[0] + self.random_move_distance, self.pos[1])
elif direction == "down":
# check if out of bounds
if (not self.model.grid.torus) and self.model.grid.out_of_bounds((self.pos[0], self.pos[1] - self.random_move_distance)):
# change direction
self.model.swarm_direction[self.swarm_id] = "up"
return
# move
new_position = (self.pos[0], self.pos[1] - self.random_move_distance)
elif direction == "up":
# check if out of bounds
if (not self.model.grid.torus) and self.model.grid.out_of_bounds((self.pos[0], self.pos[1] + self.random_move_distance)):
# change direction
self.model.swarm_direction[self.swarm_id] = "down"
return
# move
new_position = (self.pos[0], self.pos[1] + self.random_move_distance)
# in a torus, out of bounds means it enters at the other end, torus_adj does this
if self.model.grid.torus:
new_position = self.model.grid.torus_adj(new_position)
# if the new cell is not full yet, it can move, otherwise it will stay
if len(self.model.grid.get_cell_list_contents([new_position])) < self.max_num_bacteria_in_cell:
self.model.grid.move_agent(self, new_position)
self.pos = new_position
def reproduce(self):
# reproduction_rate is a constant, eating multiplier get increased with eating, decreased after reproduction
if self.random.random() < self.reproduciton_rate * self.eating_multiplier:
# Wenn bereits mehr als max_num_bacteria_in_cell Bakteriean auf einem Feld sind, oder Zufällig random_spread_chance
if len(self.model.grid.get_cell_list_contents([self.pos])) > self.max_num_bacteria_in_cell_reproduction or self.random.random() < self.random_spread_chance:
possible_postitions = self.model.grid.get_neighborhood(
self.pos, moore=self.model.reproduction_spread_moore, include_center=False
)
# random position
self.model.random.shuffle(possible_postitions)
for position in possible_postitions:
# checking if the position is already occupied
if len(self.model.grid.get_cell_list_contents([position])) <= self.max_num_bacteria_in_cell:
new_position = position
break
else:
# this is only needed if there are no good positions, so new_position is defined
new_position = None
else:
# own position is good for a new cell
new_position = self.pos
if new_position != None:
new_bacteria = Type_d(self.model.next_id(), self.model, new_position, self.swarm_id)
self.model.grid.place_agent(new_bacteria, new_position)
self.model.schedule.add(new_bacteria)
# decreasing eating_multiplier
self.eating_multiplier -= 4
# not in use at the moment
self.reproduction_counter += 1
self.has_eaten = False
def die(self):
# dies after 40 turns
if self.age > self.time_to_die:
# spread nutrition after death, can be used to help other cells grow again
if self.spread_after_death:
cells = self.model.grid.get_neighborhood(
self.pos, moore=True, include_center=True, radius=2
)
# when type_d dies, it spreads nutrients, so other cells can grow (probably not scientifically correct)
for cell in cells:
# doesnt spread in all fields
if self.random.random() < 0.3:
soil = self.model.grid.get_cell_list_contents([cell])[0]
soil.nutrients = dict.fromkeys(soil.nutrients, 1)
# die
self.model.grid.remove_agent(self)
self.model.schedule.remove(self)
###
### DEPRECATED
### Salmonellen und Klebsiella
### Type_b and Type_c were created in the beginning of the project and have not been updated since
### They are not deleted because they still work
### It would probably be the best to copy Type_a again and work from there
###
###
###
class Type_b(mesa.Agent):
def __init__(self, unique_id, model, pos):
super().__init__(unique_id, model)
self.nutrition_list = ["chicken"]
self.reproduciton_rate = 0.001
self.pos = pos
self.age = 0
self.eating_multiplier = 1000
self.sturdiness = 7
self.has_eaten = False
# doesnt do anything when being eaten
self.is_eaten = False
def step(self):
self.age += 1
if not self.is_eaten:
self.eat()
self.reproduce()
self.die()
def eat(self):
soil = self.model.grid.get_cell_list_contents([self.pos])[0]
for nutrient in self.nutrition_list:
if soil.nutrients[nutrient] > 0:
soil.nutrients[nutrient] -= 1
self.has_eaten = True
#self.age = 1
def reproduce(self):
if self.has_eaten:
self.reproduciton_chance = self.reproduciton_rate * self.eating_multiplier
else:
self.reproduciton_chance = self.reproduciton_rate
if self.random.random() < self.reproduciton_chance:
if len(self.model.grid.get_cell_list_contents([self.pos])) > 1:
possible_postitions = self.model.grid.get_neighborhood(
self.pos, moore=self.model.reproduction_spread_moore, include_center=False
)
new_position = self.random.choice(possible_postitions)
else:
new_position = self.pos
new_bacteria = Type_b(self.model.next_id(), self.model, new_position)
self.model.grid.place_agent(new_bacteria, new_position)
self.model.schedule.add(new_bacteria)
self.has_eaten = False
def die(self):
if self.age > 20:
self.model.grid.remove_agent(self)
self.model.schedule.remove(self)
class Type_c(mesa.Agent):
def __init__(self, unique_id, model, pos):
super().__init__(unique_id, model)
self.nutrition_list = ["organic"]
self.reproduciton_rate = 0.001
self.pos = pos
self.age = 0
self.eating_multiplier = 600
self.sturdiness = 11
self.has_eaten = False
# doesnt do anything when being eaten
self.is_eaten = False
def step(self):
self.age += 1
if not self.is_eaten:
self.eat()
self.reproduce()
self.die()
def eat(self):
soil = self.model.grid.get_cell_list_contents([self.pos])[0]
for nutrient in self.nutrition_list:
if soil.nutrients[nutrient] > 0:
soil.nutrients[nutrient] -= 1
self.has_eaten = True
#self.age = 1
def reproduce(self):
if self.has_eaten:
self.reproduciton_chance = self.reproduciton_rate * self.eating_multiplier
else:
self.reproduciton_chance = self.reproduciton_rate
if self.random.random() < self.reproduciton_chance:
if len(self.model.grid.get_cell_list_contents([self.pos])) > 1 or self.random.random() < 0.1:
possible_postitions = self.model.grid.get_neighborhood(
self.pos, moore=self.model.reproduction_spread_moore, include_center=False
)
new_position = self.random.choice(possible_postitions)
else:
new_position = self.pos
new_bacteria = Type_c(self.model.next_id(), self.model, new_position)
self.model.grid.place_agent(new_bacteria, new_position)
self.model.schedule.add(new_bacteria)
self.has_eaten = False
def die(self):
if self.age > 40:
self.model.grid.remove_agent(self)
self.model.schedule.remove(self)