world.py 19.5 KB
Newer Older
Ahmad Reza's avatar
Ahmad Reza committed
1
"""The world module provides the interface of the simulation world. In the simulation world
2
all the data of the particles, tiles, and markers are stored.
3
It also have the the coordination system and stated the maximum of the x and y coordinate.
Ahmad Reza's avatar
Ahmad Reza committed
4

5
 .. todo:: What happens if the maximum y or x axis is passed? Either the start from the other side or turns back.
Ahmad Reza's avatar
Ahmad Reza committed
6
"""
7
import importlib
Ahmad Reza's avatar
Ahmad Reza committed
8
import random
9
import math
10
import logging
11
from lib import csv_generator, particle, tile, marker, vis
12
from lib.swarm_sim_header import *
13
14


Ahmad Reza's avatar
Ahmad Reza committed
15
class World:
16
    def __init__(self, config_data):
Ahmad Reza's avatar
Ahmad Reza committed
17
        """
Ahmad Reza's avatar
Ahmad Reza committed
18
        Initializing the world constructor
Ahmad Reza's avatar
Ahmad Reza committed
19
        :param seed: seed number for new random numbers
20
        :param max_round: the max round number for terminating the worldulator
Ahmad Reza's avatar
Ahmad Reza committed
21
        :param solution: The name of the solution that is going to be used
22
23
        :param size_x: the maximal size of the x axes
        :param size_y: the maximal size of the y axes
24
        :param world_name: the name of the world file that is used to build up the world
25
26
        :param solution_name: the name of the solution file that is only used for the csv file
        :param seed: the seed number it is only used here for the csv file
Ahmad Reza's avatar
Ahmad Reza committed
27
        :param max_particles: the maximal number of particles that are allowed to be or created in this world
Ahmad Reza's avatar
Ahmad Reza committed
28
29
30
        """
        self.__round_counter = 1
        self.__end = False
Ahmad Reza's avatar
Ahmad Reza committed
31

32
        self.init_particles=[]
33
        self.particle_id_counter = 0
34
35
36
37
38
39
        self.particles = []
        self.particles_created = []
        self.particle_rm = []
        self.particle_map_coords = {}
        self.particle_map_id = {}
        self.__particle_deleted=False
Ahmad Reza's avatar
Ahmad Reza committed
40

41
42
43
44
45
46
        self.tiles = []
        self.tiles_created = []
        self.tiles_rm = []
        self.tile_map_coords = {}
        self.tile_map_id = {}
        self.__tile_deleted=False
Ahmad Reza's avatar
Ahmad Reza committed
47
48
        self.new_tile = None

49
50
51
52
53
54
        self.markers = []
        self.markers_created = []
        self.marker_map_coords = {}
        self.marker_map_id = {}
        self.markers_rm = []
        self.__marker_deleted = False
Ahmad Reza's avatar
Ahmad Reza committed
55

56
        self.config_data = config_data
57

Ahmad Reza's avatar
Ahmad Reza committed
58
59
60
        self.csv_round = csv_generator.CsvRoundData(scenario=config_data.scenario,
                                                    solution=config_data.solution,
                                                    seed=config_data.seed_value,
61
                                                    directory=config_data.direction_name)
Ahmad Reza's avatar
Ahmad Reza committed
62
63

        mod = importlib.import_module('scenario.' + self.config_data.scenario)
Ahmad Reza's avatar
Ahmad Reza committed
64
        mod.scenario(self)
Ahmad Reza's avatar
Ahmad Reza committed
65

Ahmad Reza's avatar
Ahmad Reza committed
66
        if self.config_data.particle_random_order:
67
68
            random.shuffle(self.particles)

Ahmad Reza's avatar
Ahmad Reza committed
69
70
        if config_data.visualization:
            self.window = vis.VisWindow(config_data.window_size_x, config_data.window_size_y, self)
71

Ahmad Reza's avatar
Ahmad Reza committed
72
73
    def csv_aggregator(self):
        self.csv_round.aggregate_metrics()
74
        particle_csv = csv_generator.CsvParticleFile(self.config_data.direction_name)
Ahmad Reza's avatar
Ahmad Reza committed
75
        for particle in self.particles:
76
77
            particle_csv.write_particle(particle)
        particle_csv.csv_file.close()
Ahmad Reza's avatar
Ahmad Reza committed
78

79
    def success_termination(self):
Ahmad Reza's avatar
Ahmad Reza committed
80
        self.csv_round.success()
81
        self.set_end()
Ahmad Reza Cheraghi's avatar
Ahmad Reza Cheraghi committed
82
83
84
85
86
87
88
89
        
    def get_max_round(self):
        """
        The max round number
    
        :return: maximum round number
        """
        return self.config_data.max_round
Ahmad Reza's avatar
Ahmad Reza committed
90
91
92
93
94
95
96
97
98

    def get_actual_round(self):
        """
        The actual round number

        :return: actual round number
        """
        return self.__round_counter

99
100
101
102
103
104
105
106
    def get_max_round(self):
        """
        The max round number

        :return: maximum round number
        """
        return self.config_data.max_round

Ahmad Reza's avatar
Ahmad Reza committed
107
108
109
110
111
112
113
114
115
116
117
118
    def set_end(self):
        """
        Allows to terminate before the max round is reached
        """
        self.__end=True

    def get_end(self):
        """
            Returns the end parameter values either True or False
        """
        return self.__end

119
    def inc_round_counter(self):
Ahmad Reza's avatar
Ahmad Reza committed
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
        """
        Increases the the round counter by

        :return:
        """
        self.__round_counter +=  1

    def get_solution(self):
        """
        actual solution name

        :return: actual solution name
        """
        return self.__solution

135
    def get_amount_of_particles(self):
136
        """
Ahmad Reza's avatar
Ahmad Reza committed
137
        Returns the actual number of particles in the world
138
139
140

        :return: The actual number of Particles
        """
141
        return len(self.particles)
142
143
144

    def get_particle_list(self):
        """
Ahmad Reza's avatar
Ahmad Reza committed
145
        Returns the actual number of particles in the world
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166

        :return: The actual number of Particles
        """
        return self.particles

    def get_particle_map_coords(self):
        """
        Get a dictionary with all particles mapped with their actual coordinates

        :return: a dictionary with particles and their coordinates
        """
        return self.particle_map_coords

    def get_particle_map_id(self):
        """
        Get a dictionary with all particles mapped with their own ids

        :return: a dictionary with particles and their own ids
        """
        return self.particle_map_id

167
    def get_amount_of_tiles(self):
168
        """
Ahmad Reza's avatar
Ahmad Reza committed
169
        Returns the actual number of particles in the world
170
171
172

        :return: The actual number of Particles
        """
173
        return len(self.tiles)
174
175
176

    def get_tiles_list(self):
        """
Ahmad Reza's avatar
Ahmad Reza committed
177
        Returns the actual number of tiles in the world
178

Ahmad Reza's avatar
Ahmad Reza committed
179
        :return: a list of all the tiles in the world
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
        """
        return self.tiles

    def get_tile_map_coords(self):
        """
        Get a dictionary with all tiles mapped with their actual coordinates

        :return: a dictionary with particles and their coordinates
        """
        return self.tile_map_coords

    def get_tile_map_id(self):
        """
        Get a dictionary with all particles mapped with their own ids

        :return: a dictionary with particles and their own ids
        """
        return self.tile_map_id

199
    def get_amount_of_markers(self):
200
        """
Ahmad Reza's avatar
Ahmad Reza committed
201
        Returns the actual number of markers in the world
202

203
        :return: The actual number of markers
204
        """
205
        return len(self.markers)
206

207
    def get_marker_list(self):
208
        """
Ahmad Reza's avatar
Ahmad Reza committed
209
        Returns the actual number of markers in the world
210

211
        :return: The actual number of markers
212
        """
213
        return self.markers
214

215
    def get_marker_map_coords(self):
216
        """
217
        Get a dictionary with all markers mapped with their actual coordinates
218

219
        :return: a dictionary with markers and their coordinates
220
        """
221
        return self.marker_map_coords
222

223
    def get_marker_map_id(self):
224
        """
225
        Get a dictionary with all markers mapped with their own ids
226

227
        :return: a dictionary with markers and their own ids
228
        """
229
        return self.marker_map_id
230

231
    def get_world_x_size(self):
232
233
        """

Ahmad Reza's avatar
Ahmad Reza committed
234
        :return: Returns the maximal x size of the world
235
        """
Ahmad Reza's avatar
Ahmad Reza committed
236
        return self.config_data.size_x
237

238
    def get_world_y_size(self):
239
        """
Ahmad Reza's avatar
Ahmad Reza committed
240
        :return: Returns the maximal y size of the world
241
        """
Ahmad Reza's avatar
Ahmad Reza committed
242
        return self.config_data.size_y
243
244
245
246
247
248
249

    def get_tile_deleted(self):
        return self.__tile_deleted

    def get_particle_deleted(self):
        return self.__particle_deleted

250
251
    def get_marker_deleted(self):
        return self.__marker_deleted
252
253
254
255
256
257
258

    def set_tile_deleted(self):
        self.__tile_deleted = False

    def set_particle_deleted(self):
        self.__particle_deleted=False

259
260
    def set_marker_deleted(self):
        self.__marker_deleted = False
261

262
    def add_particle(self, x, y, color=black, alpha=1):
263
        """
Ahmad Reza's avatar
Ahmad Reza committed
264
        Add a particle to the world database
265
266
267
268
269
270

        :param x: The x coordinate of the particle
        :param y: The y coordinate of the particle
        :param state: The state of the particle. Default: S for for Stopped or Not Moving. Other options
                      are the moving directions: E, SE, SW, W, NW, NE
        :param color: The color of the particle. Coloroptions: black, gray, red, green, or blue
271
        :return: Added Matter; False: Unsuccsessful
272
273
274
        """
        if alpha < 0 or alpha >1:
            alpha = 1
Ahmad Reza's avatar
Ahmad Reza committed
275
        if len(self.particles) < self.config_data.max_particles:
276
            if check_values_are_coordinates(x,y) == True:
277
                if (x,y) not in self.get_particle_map_coords():
278
279
280
                    self.particle_id_counter += 1
                    new_particle = particle.Particle(self, x, y, color, alpha, self.particle_id_counter)
                    print(new_particle.number)
281
282
283
284
285
                    self.particles_created.append(new_particle)
                    self.particle_map_coords[new_particle.coords] = new_particle
                    self.particle_map_id[new_particle.get_id()] = new_particle
                    self.particles.append(new_particle)
                    new_particle.touch()
Ahmad Reza's avatar
Ahmad Reza committed
286
                    self.csv_round.update_particle_num(len(self.particles))
287
288
289
                    self.init_particles.append(new_particle)
                    new_particle.created=True
                    logging.info("Created particle at %s", new_particle.coords)
290
                    return new_particle
291
292
293
294
295
296
297
298
299
300
301
                else:
                    print("for x %f and y %f not not possible because Particle exist   ", x, y)
                    return False
            else:
                 print ("for x %f and y %f not possible to draw ", x, y)
                 return False
        else:
            logging.info("Max of particles reached and no more particles can be created")
            return False

    def remove_particle(self,id):
Ahmad Reza's avatar
Ahmad Reza committed
302
        """ Removes a particle with a given particle id from the world database
303
304
305
306
307
308
309
310
311
312
313
314
315
316


        :param particle_id: particle id
        :return: True: Successful removed; False: Unsuccessful
        """
        rm_particle = self.particle_map_id[id]
        if rm_particle:
            self.particles.remove(rm_particle)
            try:
                del self.particle_map_coords[rm_particle.coords]
                del self.particle_map_id[id]
            except:
                pass
            self.particle_rm.append(rm_particle)
Ahmad Reza's avatar
Ahmad Reza committed
317
318
            self.csv_round.update_particle_num(len(self.particles))
            self.csv_round.update_metrics(particle_deleted=1)
319
320
321
322
323
324
325
            self.__particle_deleted = True
            return True
        else:
            return False

    def remove_particle_on(self, coords):
        """
Ahmad Reza's avatar
Ahmad Reza committed
326
        Removes a particle on a give coordinat from to the world database
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341

        :param coords: A tupel that includes the x and y coorindates
        :return: True: Successful removed; False: Unsuccessful
        """
        if coords in self.particle_map_coords:
            self.particles.remove(self.particle_map_coords[coords])
            self.particle_rm.append(self.particle_map_coords[coords])
            try:  # cher: added so the program does not crashed if it does not find any entries in the map
                del self.particle_map_id[self.particle_map_coords[coords].get_id()]
            except KeyError:
                pass
            try:  # cher: added so the program does not crashed if it does not find any entries in the map
                del self.particle_map_coords[coords]
            except KeyError:
                pass
Ahmad Reza's avatar
Ahmad Reza committed
342
343
            self.csv_round.update_particle_num(len(self.particles))
            self.csv_round.update_metrics( particle_deleted=1)
344
345
346
347
348
349
350
            self.__particle_deleted = True
            return True
        else:
            return False

    def add_tile(self, x, y, color=gray, alpha=1):
        """
Ahmad Reza's avatar
Ahmad Reza committed
351
        Adds a tile to the world database
352
353
354
355

        :param color:
        :param x: the x coordinates on which the tile should be added
        :param y: the y coordinates on which the tile should be added
356
        :return: Successful added matter; False: Unsuccsessful
357
        """
358
        if alpha < 0 or alpha >1:
359
            alpha = 1
360
        if check_values_are_coordinates(x,y) == True:
361
            if (x,y) not in self.tile_map_coords:
Ahmad Reza Cheraghi's avatar
Ahmad Reza Cheraghi committed
362
                self.new_tile=tile.Tile(self, x, y, color, alpha)
363
364
                print("Before adding ", len(self.tiles) )
                self.tiles.append(self.new_tile)
Ahmad Reza's avatar
Ahmad Reza committed
365
                self.csv_round.update_tiles_num(len(self.tiles))
366
367
368
369
370
371
                self.tile_map_coords[self.new_tile.coords] = self.new_tile
                self.tile_map_id[self.new_tile.get_id()] = self.new_tile

                print("Afer adding ", len(self.tiles), self.new_tile.coords )
                logging.info("Created tile with tile id %s on coords %s",str(self.new_tile.get_id()), str(self.new_tile.coords))
                self.new_tile.touch()
372
                return self.new_tile
373
374
375
376
377
378
379
380
381
            else:
                logging.info ("on x %f and y %f coordinates is a tile already", x, y)
                return False
        else:
             logging.info ("for x %f and y %f not possible to draw ", x, y)
             return False

    def add_tile_vis(self, x, y, color=gray, alpha=1):
        """
Ahmad Reza's avatar
Ahmad Reza committed
382
        Adds a tile to the world database
383
384
385
386
387
388

        :param color:
        :param x: the x coordinates on which the tile should be added
        :param y: the y coordinates on which the tile should be added
        :return: True: Successful added; False: Unsuccsessful
        """
389
        if check_values_are_coordinates(x, y) == True:
390
            if (x, y) not in self.tile_map_coords:
Ahmad Reza Cheraghi's avatar
Ahmad Reza Cheraghi committed
391
                self.new_tile = tile.Tile(self, x, y, color, alpha)
392
393
394
395
396
                self.tiles.append(self.new_tile)

                self.tile_map_coords[self.new_tile.coords] = self.new_tile
                self.tile_map_id[self.new_tile.get_id()] = self.new_tile

Ahmad Reza's avatar
Ahmad Reza committed
397
                print("world.add_tile",self.new_tile.coords)
398
399
400
401
402
403
404
405
406
                logging.info("Created tile with tile id %s on coords %s", str(self.new_tile.get_id()),
                             str(self.new_tile.coords))
                return True
            else:
                logging.info("on x %f and y %f coordinates is a tile already", x, y)
                return False

    def remove_tile(self,id):
        """
Ahmad Reza's avatar
Ahmad Reza committed
407
        Removes a tile with a given tile_id from to the world database
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425

        :param tile_id: The tiles id that should be removec
        :return:  True: Successful removed; False: Unsuccessful
        """
        if id in self.tile_map_id:
            rm_tile = self.tile_map_id[id]
            rm_tile.touch()
            self.tiles.remove(rm_tile)
            self.tiles_rm.append(rm_tile)
            logging.info("Deleted tile with tile id %s on %s", str(rm_tile.get_id()), str(rm_tile.coords) )
            try:  # cher: added so the program does not crashed if it does not find any entries in the map
                del self.tile_map_id[rm_tile.get_id()]
            except KeyError:
                pass
            try:  # cher: added so the program does not crashed if it does not find any entries in the map
                del self.tile_map_coords[rm_tile.coords]
            except KeyError:
                pass
Ahmad Reza's avatar
Ahmad Reza committed
426
427
            self.csv_round.update_tiles_num(len(self.tiles))
            self.csv_round.update_metrics(tile_deleted=1)
428
429
430
431
432
433
434
            self.__tile_deleted = True
            return True
        else:
            return False

    def remove_tile_on(self, coords):
        """
Ahmad Reza's avatar
Ahmad Reza committed
435
        Removes a tile on a give coordinat from to the world database
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450

        :param coords: A tupel that includes the x and y coorindates
        :return: True: Successful removed; False: Unsuccessful
        """
        if coords in self.tile_map_coords:
            self.tiles.remove(self.tile_map_coords[coords])
            self.tiles_rm.append(self.tile_map_coords[coords])
            try:  # cher: added so the program does not crashed if it does not find any entries in the map
                del self.tile_map_id[self.tile_map_coords[coords].get_id()]
            except KeyError:
                pass
            try:  # cher: added so the program does not crashed if it does not find any entries in the map
                del self.tile_map_coords[coords]
            except KeyError:
                pass
Ahmad Reza's avatar
Ahmad Reza committed
451
452
            self.csv_round.update_tiles_num(len(self.tiles))
            self.csv_round.update_metrics( tile_deleted=1)
453
454
455
456
457
            self.__tile_deleted = True
            return True
        else:
            return False

458
    def add_marker(self, x, y, color=black, alpha=1):
459
        """
Ahmad Reza's avatar
Ahmad Reza committed
460
        Add a tile to the world database
461
462
463
464
465
466
467
468

        :param color:
        :param x: the x coordinates on which the tile should be added
        :param y: the y coordinates on which the tile should be added
        :return: True: Successful added; False: Unsuccsessful
        """
        if alpha < 0 or alpha >1:
            alpha = 1
469
        if check_values_are_coordinates(x, y) == True:
470
            if (x, y) not in self.marker_map_coords:
471
                self.new_marker = marker.Marker(self, x, y, color, alpha)
472
473
474
                self.markers.append(self.new_marker)
                self.marker_map_coords[self.new_marker.coords] = self.new_marker
                self.marker_map_id[self.new_marker.get_id()] = self.new_marker
Ahmad Reza's avatar
Ahmad Reza committed
475
                self.csv_round.update_markers_num(len(self.markers))
476
477
478
479
480
                logging.info("Created marker with id %s on coords %s", str(self.new_marker.get_id()), str(self.new_marker.coords))

                self.new_marker.created = True
                self.new_marker.touch()
                return self.new_marker
481
            else:
482
                logging.info("on x %f and y %f coordinates is a marker already", x, y)
483
484
485
                return False
        else:
            logging.info("for x %f and y %f not possible to draw ", x, y)
486
487
            return False

488
    def remove_marker(self, id):
489
        """
Ahmad Reza's avatar
Ahmad Reza committed
490
        Removes a tile with a given tile_id from to the world database
491

492
        :param id: The markers id that should be removec
493
494
        :return:  True: Successful removed; False: Unsuccessful
        """
495
496
497
498
499
        if id in self.marker_map_id:
            rm_marker = self.marker_map_id[id]
            rm_marker.touch()
            if rm_marker in self.markers:
                self.markers.remove(rm_marker)
500

501
502
            self.markers_rm.append(rm_marker)
            logging.info("Deleted marker with marker id %s on %s", str(id), str(rm_marker.coords))
503
            try:
504
                del self.marker_map_coords[rm_marker.coords]
505
506
507
            except KeyError:
                pass
            try:
508
                del self.marker_map_id[id]
509
510
            except KeyError:
                pass
Ahmad Reza's avatar
Ahmad Reza committed
511
512
            self.csv_round.update_markers_num(len(self.markers))
            self.csv_round.update_metrics( marker_deleted=1)
513
            self.__marker_deleted = True
514
515
516
517
            return True
        else:
            return False

518
    def remove_marker_on(self, coords):
519
        """
Ahmad Reza's avatar
Ahmad Reza committed
520
        Removes a marker on a give coordinat from to the world database
521
522
523
524

        :param coords: A tupel that includes the x and y coorindates
        :return: True: Successful removed; False: Unsuccessful
        """
525
526
527
        if coords in self.marker_map_coords:
            self.markers.remove(self.marker_map_coords[coords])
            self.markers_rm.append(self.marker_map_coords[coords])
528
            try:  # cher: added so the program does not crashed if it does not find any entries in the map
529
                del self.marker_map_id[self.marker_map_coords[coords].get_id()]
530
531
532
            except KeyError:
                pass
            try:  # cher: added so the program does not crashed if it does not find any entries in the map
533
                del self.marker_map_coords[coords]
534
535
            except KeyError:
                pass
Ahmad Reza's avatar
Ahmad Reza committed
536
537
            self.csv_round.update_markers_num(len(self.markers))
            self.csv_round.update_metrics( marker_deleted=1)
538
            self.__marker_deleted = True
539
540
541
            return True
        else:
            return False