world.py 20.6 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
8
import importlib
import logging
Karol Actun's avatar
Karol Actun committed
9
from lib import csv_generator, particle, tile, marker, vis3d
10
from lib.swarm_sim_header import *
11
12


Ahmad Reza's avatar
Ahmad Reza committed
13
class World:
14
    def __init__(self, config_data):
Ahmad Reza's avatar
Ahmad Reza committed
15
        """
Ahmad Reza's avatar
Ahmad Reza committed
16
        Initializing the world constructor
Karol Actun's avatar
Karol Actun committed
17
        :param config_data: configuration data from config.ini file
Ahmad Reza's avatar
Ahmad Reza committed
18
19
20
        """
        self.__round_counter = 1
        self.__end = False
Ahmad Reza's avatar
Ahmad Reza committed
21

Karol Actun's avatar
Karol Actun committed
22
        self.grid = config_data.grid
Karol Actun's avatar
Karol Actun committed
23

Karol Actun's avatar
Karol Actun committed
24
        self.init_particles = []
25
        self.particle_id_counter = 0
26
27
28
        self.particles = []
        self.particles_created = []
        self.particle_rm = []
29
        self.particle_map_coordinates = {}
30
31
        self.particle_map_id = {}
        self.__particle_deleted=False
Ahmad Reza's avatar
Ahmad Reza committed
32

33
34
35
        self.tiles = []
        self.tiles_created = []
        self.tiles_rm = []
36
        self.tile_map_coordinates = {}
37
38
        self.tile_map_id = {}
        self.__tile_deleted=False
Ahmad Reza's avatar
Ahmad Reza committed
39
40
        self.new_tile = None

41
42
        self.markers = []
        self.markers_created = []
43
        self.marker_map_coordinates = {}
44
45
46
        self.marker_map_id = {}
        self.markers_rm = []
        self.__marker_deleted = False
Ahmad Reza's avatar
Ahmad Reza committed
47

48
        self.config_data = config_data
49

Ahmad Reza's avatar
Ahmad Reza committed
50
51
52
        self.csv_round = csv_generator.CsvRoundData(scenario=config_data.scenario,
                                                    solution=config_data.solution,
                                                    seed=config_data.seed_value,
53
                                                    directory=config_data.direction_name)
Ahmad Reza's avatar
Ahmad Reza committed
54
55

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

Ahmad Reza's avatar
Ahmad Reza committed
58
        if self.config_data.particle_random_order:
59
60
            random.shuffle(self.particles)

Ahmad Reza's avatar
Ahmad Reza committed
61
        if config_data.visualization:
Karol Actun's avatar
Karol Actun committed
62
63
64
            self.vis = vis3d.Visualization(self)
            self.vis.init()
            self.vis.start()
65

Ahmad Reza's avatar
Ahmad Reza committed
66
67
    def csv_aggregator(self):
        self.csv_round.aggregate_metrics()
68
        particle_csv = csv_generator.CsvParticleFile(self.config_data.direction_name)
Ahmad Reza's avatar
Ahmad Reza committed
69
        for particle in self.particles:
70
71
            particle_csv.write_particle(particle)
        particle_csv.csv_file.close()
Ahmad Reza's avatar
Ahmad Reza committed
72

73
    def set_successful_end(self):
Ahmad Reza's avatar
Ahmad Reza committed
74
        self.csv_round.success()
75
        self.set_end()
Ahmad Reza Cheraghi's avatar
Ahmad Reza Cheraghi committed
76
77
78
79
80
81
82
83
        
    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
84
85
86
87
88
89
90
91
92

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

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

93
94
95
96
97
98
99
100
    def get_max_round(self):
        """
        The max round number

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

101
    def set_unsuccessful_end(self):
Ahmad Reza's avatar
Ahmad Reza committed
102
103
104
105
106
107
108
109
110
111
112
        """
        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

113
    def inc_round_counter_by(self, number=1):
Ahmad Reza's avatar
Ahmad Reza committed
114
115
116
117
118
        """
        Increases the the round counter by

        :return:
        """
119
        self.__round_counter +=  number
Ahmad Reza's avatar
Ahmad Reza committed
120
121
122
123
124
125
126
127
128

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

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

129
    def get_amount_of_particles(self):
130
        """
Ahmad Reza's avatar
Ahmad Reza committed
131
        Returns the actual number of particles in the world
132
133
134

        :return: The actual number of Particles
        """
135
        return len(self.particles)
136
137
138

    def get_particle_list(self):
        """
Ahmad Reza's avatar
Ahmad Reza committed
139
        Returns the actual number of particles in the world
140
141
142
143
144

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

145
    def get_particle_map_coordinates(self):
146
147
148
149
150
        """
        Get a dictionary with all particles mapped with their actual coordinates

        :return: a dictionary with particles and their coordinates
        """
151
        return self.particle_map_coordinates
152
153
154
155
156
157
158
159
160

    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

161
    def get_amount_of_tiles(self):
162
        """
Ahmad Reza's avatar
Ahmad Reza committed
163
        Returns the actual number of particles in the world
164
165
166

        :return: The actual number of Particles
        """
167
        return len(self.tiles)
168
169
170

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

Ahmad Reza's avatar
Ahmad Reza committed
173
        :return: a list of all the tiles in the world
174
175
176
        """
        return self.tiles

177
    def get_tile_map_coordinates(self):
178
179
180
181
182
        """
        Get a dictionary with all tiles mapped with their actual coordinates

        :return: a dictionary with particles and their coordinates
        """
183
        return self.tile_map_coordinates
184
185
186
187
188
189
190
191
192

    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

193
    def get_amount_of_markers(self):
194
        """
Ahmad Reza's avatar
Ahmad Reza committed
195
        Returns the actual number of markers in the world
196

197
        :return: The actual number of markers
198
        """
199
        return len(self.markers)
200

201
    def get_marker_list(self):
202
        """
Ahmad Reza's avatar
Ahmad Reza committed
203
        Returns the actual number of markers in the world
204

205
        :return: The actual number of markers
206
        """
207
        return self.markers
208

209
    def get_marker_map_coordinates(self):
210
        """
211
        Get a dictionary with all markers mapped with their actual coordinates
212

213
        :return: a dictionary with markers and their coordinates
214
        """
215
        return self.marker_map_coordinates
216

217
    def get_marker_map_id(self):
218
        """
219
        Get a dictionary with all markers mapped with their own ids
220

221
        :return: a dictionary with markers and their own ids
222
        """
223
        return self.marker_map_id
224

225
    def get_world_x_size(self):
226
227
        """

Ahmad Reza's avatar
Ahmad Reza committed
228
        :return: Returns the maximal x size of the world
229
        """
Ahmad Reza's avatar
Ahmad Reza committed
230
        return self.config_data.size_x
231

232
    def get_world_y_size(self):
233
        """
Ahmad Reza's avatar
Ahmad Reza committed
234
        :return: Returns the maximal y size of the world
235
        """
Ahmad Reza's avatar
Ahmad Reza committed
236
        return self.config_data.size_y
237

238
239
240
241
242
243
    def get_world_size(self):
        """
        :return: Returns the maximal (x,y) size of the world as a tupel
        """
        return (self.config_data.size_x, self.config_data.size_y)

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

Karol Actun's avatar
Karol Actun committed
262
    def add_particle(self, x, y, z=None, color=black, transparency=1):
263
        """
Ahmad Reza's avatar
Ahmad Reza committed
264
        Add a particle to the world database
265
266
267

        :param x: The x coordinate of the particle
        :param y: The y coordinate of the particle
Karol Actun's avatar
Karol Actun committed
268
        :param z: The z coordinate of the particle
269
270
271
        :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
272
        :return: Added Matter; False: Unsuccsessful
273
        """
Karol Actun's avatar
Karol Actun committed
274
275
276
277
278
279
280
        if z is None and self.grid.get_dimension_count() == 3:
            print("z coordinate is missing and grid is 3D!")
            return False
        if z is None:
            coordinates = (x, y)
        else:
            coordinates = (x, y, z)
281
282
        if transparency < 0 or transparency >1:
            transparency = 1
Ahmad Reza's avatar
Ahmad Reza committed
283
        if len(self.particles) < self.config_data.max_particles:
Karol Actun's avatar
Karol Actun committed
284
285
            if self.grid.is_valid_location(coordinates):
                if coordinates not in self.get_particle_map_coordinates():
286
                    self.particle_id_counter += 1
Karol Actun's avatar
Karol Actun committed
287
288
                    new_particle = particle.Particle(self, coordinates, color, transparency, self.particle_id_counter)
                    # print(new_particle.number)
289
                    self.particles_created.append(new_particle)
290
                    self.particle_map_coordinates[new_particle.coordinates] = new_particle
291
292
293
                    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
294
                    self.csv_round.update_particle_num(len(self.particles))
295
296
                    self.init_particles.append(new_particle)
                    new_particle.created=True
297
                    logging.info("Created particle at %s", new_particle.coordinates)
298
                    return new_particle
299
300
301
302
                else:
                    print("for x %f and y %f not not possible because Particle exist   ", x, y)
                    return False
            else:
Karol Actun's avatar
Karol Actun committed
303
304
                print ("for x %f and y %f not possible to draw ", x, y)
                return False
305
306
307
308
309
        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
310
        """ Removes a particle with a given particle id from the world database
311
312
313
314
315
316
317
318
319


        :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:
Karol Actun's avatar
Karol Actun committed
320
                print("deleting a particle")
321
                del self.particle_map_coordinates[rm_particle.coordinates]
322
323
324
325
                del self.particle_map_id[id]
            except:
                pass
            self.particle_rm.append(rm_particle)
Ahmad Reza's avatar
Ahmad Reza committed
326
327
            self.csv_round.update_particle_num(len(self.particles))
            self.csv_round.update_metrics(particle_deleted=1)
328
329
330
331
332
            self.__particle_deleted = True
            return True
        else:
            return False

333
    def remove_particle_on(self, coordinates):
334
        """
Ahmad Reza's avatar
Ahmad Reza committed
335
        Removes a particle on a give coordinat from to the world database
336

337
        :param coordinates: A tupel that includes the x and y coorindates
338
339
        :return: True: Successful removed; False: Unsuccessful
        """
Karol Actun's avatar
Karol Actun committed
340
        print("removing on")
341
342
343
        if coordinates in self.particle_map_coordinates:
            self.particles.remove(self.particle_map_coordinates[coordinates])
            self.particle_rm.append(self.particle_map_coordinates[coordinates])
344
            try:  # cher: added so the program does not crashed if it does not find any entries in the map
345
                del self.particle_map_id[self.particle_map_coordinates[coordinates].get_id()]
346
347
348
            except KeyError:
                pass
            try:  # cher: added so the program does not crashed if it does not find any entries in the map
349
                del self.particle_map_coordinates[coordinates]
350
351
            except KeyError:
                pass
Ahmad Reza's avatar
Ahmad Reza committed
352
353
            self.csv_round.update_particle_num(len(self.particles))
            self.csv_round.update_metrics( particle_deleted=1)
354
355
356
357
358
            self.__particle_deleted = True
            return True
        else:
            return False

Karol Actun's avatar
Karol Actun committed
359
    def add_tile(self, x, y, z=None, color=gray, transparency=1):
360
        """
Ahmad Reza's avatar
Ahmad Reza committed
361
        Adds a tile to the world database
362
363
364
365

        :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
Karol Actun's avatar
Karol Actun committed
366
        :param z: the z coordinates on which the tile should be added
367
        :return: Successful added matter; False: Unsuccsessful
368
        """
Karol Actun's avatar
Karol Actun committed
369
370
371
372
373
374
375
        if z is None and self.grid.get_dimension_count() == 3:
            print("z coordinate is missing and grid is 3D!")
            return False
        if z is None:
            coordinates = (x, y)
        else:
            coordinates = (x, y, z)
376
377
        if transparency < 0 or transparency >1:
            transparency = 1
Karol Actun's avatar
Karol Actun committed
378
379
380
381
        if self.grid.is_valid_location(coordinates):
            if coordinates not in self.tile_map_coordinates:
                self.new_tile=tile.Tile(self, coordinates, color, transparency)
                # print("Before adding ", len(self.tiles) )
382
                self.tiles.append(self.new_tile)
Ahmad Reza's avatar
Ahmad Reza committed
383
                self.csv_round.update_tiles_num(len(self.tiles))
384
                self.tile_map_coordinates[self.new_tile.coordinates] = self.new_tile
385
386
                self.tile_map_id[self.new_tile.get_id()] = self.new_tile

Karol Actun's avatar
Karol Actun committed
387
                # print("Afer adding ", len(self.tiles), self.new_tile.coordinates )
388
                logging.info("Created tile with tile id %s on coordinates %s",str(self.new_tile.get_id()), str(self.new_tile.coordinates))
389
                self.new_tile.touch()
390
                return self.new_tile
391
392
393
394
395
396
397
            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

Karol Actun's avatar
Karol Actun committed
398
    def add_tile_vis(self, coordinates, color=gray, transparency=1):
399
        """
Ahmad Reza's avatar
Ahmad Reza committed
400
        Adds a tile to the world database
401
402

        :param color:
Karol Actun's avatar
Karol Actun committed
403
        :param coordinates: the coordinates on which the tile should be added
404
405
        :return: True: Successful added; False: Unsuccsessful
        """
Karol Actun's avatar
Karol Actun committed
406
407
408
        if grid.is_valid_location(coordinates) == True:
            if coordinates not in self.tile_map_coordinates:
                self.new_tile = tile.Tile(self, coordinates, color, transparency)
409
410
                self.tiles.append(self.new_tile)

411
                self.tile_map_coordinates[self.new_tile.coordinates] = self.new_tile
412
413
                self.tile_map_id[self.new_tile.get_id()] = self.new_tile

Karol Actun's avatar
Karol Actun committed
414
                # print("world.add_tile",self.new_tile.coordinates)
415
416
                logging.info("Created tile with tile id %s on coordinates %s", str(self.new_tile.get_id()),
                             str(self.new_tile.coordinates))
417
418
419
420
421
422
423
                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
424
        Removes a tile with a given tile_id from to the world database
425
426
427
428
429
430
431
432
433

        :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)
434
            logging.info("Deleted tile with tile id %s on %s", str(rm_tile.get_id()), str(rm_tile.coordinates) )
435
436
437
438
439
            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
440
                del self.tile_map_coordinates[rm_tile.coordinates]
441
442
            except KeyError:
                pass
Ahmad Reza's avatar
Ahmad Reza committed
443
444
            self.csv_round.update_tiles_num(len(self.tiles))
            self.csv_round.update_metrics(tile_deleted=1)
445
446
447
448
449
            self.__tile_deleted = True
            return True
        else:
            return False

450
    def remove_tile_on(self, coordinates):
451
        """
Ahmad Reza's avatar
Ahmad Reza committed
452
        Removes a tile on a give coordinat from to the world database
453

454
        :param coordinates: A tupel that includes the x and y coorindates
455
456
        :return: True: Successful removed; False: Unsuccessful
        """
457
458
459
        if coordinates in self.tile_map_coordinates:
            self.tiles.remove(self.tile_map_coordinates[coordinates])
            self.tiles_rm.append(self.tile_map_coordinates[coordinates])
460
            try:  # cher: added so the program does not crashed if it does not find any entries in the map
461
                del self.tile_map_id[self.tile_map_coordinates[coordinates].get_id()]
462
463
464
            except KeyError:
                pass
            try:  # cher: added so the program does not crashed if it does not find any entries in the map
465
                del self.tile_map_coordinates[coordinates]
466
467
            except KeyError:
                pass
Ahmad Reza's avatar
Ahmad Reza committed
468
469
            self.csv_round.update_tiles_num(len(self.tiles))
            self.csv_round.update_metrics( tile_deleted=1)
470
471
472
473
474
            self.__tile_deleted = True
            return True
        else:
            return False

Karol Actun's avatar
Karol Actun committed
475
    def add_marker(self, x, y, z=None, color=black, transparency=1):
476
        """
Ahmad Reza's avatar
Ahmad Reza committed
477
        Add a tile to the world database
478
479
480
481

        :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
Karol Actun's avatar
Karol Actun committed
482
        :param z: the z coordinates on which the tile should be added
483
484
        :return: True: Successful added; False: Unsuccsessful
        """
Karol Actun's avatar
Karol Actun committed
485
486
487
488
489
490
491
        if z is None and self.grid.get_dimension_count() == 3:
            print("z coordinate is missing and grid is 3D!")
            return False
        if z is None:
            coordinates = (x, y)
        else:
            coordinates = (x, y, z)
492
493
        if transparency < 0 or transparency >1:
            transparency = 1
Karol Actun's avatar
Karol Actun committed
494
495
496
        if self.grid.is_valid_location(coordinates):
            if coordinates not in self.marker_map_coordinates:
                self.new_marker = marker.Marker(self, coordinates, color, transparency)
497
                self.markers.append(self.new_marker)
498
                self.marker_map_coordinates[self.new_marker.coordinates] = self.new_marker
499
                self.marker_map_id[self.new_marker.get_id()] = self.new_marker
Ahmad Reza's avatar
Ahmad Reza committed
500
                self.csv_round.update_markers_num(len(self.markers))
501
                logging.info("Created marker with id %s on coordinates %s", str(self.new_marker.get_id()), str(self.new_marker.coordinates))
502
503
504
505

                self.new_marker.created = True
                self.new_marker.touch()
                return self.new_marker
506
            else:
507
                logging.info("on x %f and y %f coordinates is a marker already", x, y)
508
509
510
                return False
        else:
            logging.info("for x %f and y %f not possible to draw ", x, y)
511
512
            return False

513
    def remove_marker(self, id):
514
        """
Ahmad Reza's avatar
Ahmad Reza committed
515
        Removes a tile with a given tile_id from to the world database
516

517
        :param id: The markers id that should be removec
518
519
        :return:  True: Successful removed; False: Unsuccessful
        """
520
521
522
523
524
        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)
525

526
            self.markers_rm.append(rm_marker)
527
            logging.info("Deleted marker with marker id %s on %s", str(id), str(rm_marker.coordinates))
528
            try:
529
                del self.marker_map_coordinates[rm_marker.coordinates]
530
531
532
            except KeyError:
                pass
            try:
533
                del self.marker_map_id[id]
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
542
            return True
        else:
            return False

543
    def remove_marker_on(self, coordinates):
544
        """
Ahmad Reza's avatar
Ahmad Reza committed
545
        Removes a marker on a give coordinat from to the world database
546

547
        :param coordinates: A tupel that includes the x and y coorindates
548
549
        :return: True: Successful removed; False: Unsuccessful
        """
550
551
552
        if coordinates in self.marker_map_coordinates:
            self.markers.remove(self.marker_map_coordinates[coordinates])
            self.markers_rm.append(self.marker_map_coordinates[coordinates])
553
            try:  # cher: added so the program does not crashed if it does not find any entries in the map
554
                del self.marker_map_id[self.marker_map_coordinates[coordinates].get_id()]
555
556
557
            except KeyError:
                pass
            try:  # cher: added so the program does not crashed if it does not find any entries in the map
558
                del self.marker_map_coordinates[coordinates]
559
560
            except KeyError:
                pass
Ahmad Reza's avatar
Ahmad Reza committed
561
562
            self.csv_round.update_markers_num(len(self.markers))
            self.csv_round.update_metrics( marker_deleted=1)
563
            self.__marker_deleted = True
564
565
            return True
        else:
Karol Actun's avatar
Karol Actun committed
566
567
            return False