world.py 21.1 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
Karol Actun's avatar
Karol Actun committed
11
from lib import csv_generator, particle, tile, marker, vis3d
12
from lib.swarm_sim_header import *
Karol Actun's avatar
Karol Actun committed
13
from lib.visualization.grid_models import CubicGrid
14
15


Ahmad Reza's avatar
Ahmad Reza committed
16
class World:
17
    def __init__(self, config_data):
Ahmad Reza's avatar
Ahmad Reza committed
18
        """
Ahmad Reza's avatar
Ahmad Reza committed
19
        Initializing the world constructor
Ahmad Reza's avatar
Ahmad Reza committed
20
        :param seed: seed number for new random numbers
21
        :param max_round: the max round number for terminating the worldulator
Ahmad Reza's avatar
Ahmad Reza committed
22
        :param solution: The name of the solution that is going to be used
23
24
        :param size_x: the maximal size of the x axes
        :param size_y: the maximal size of the y axes
25
        :param world_name: the name of the world file that is used to build up the world
26
27
        :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
28
        :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
29
30
31
        """
        self.__round_counter = 1
        self.__end = False
Ahmad Reza's avatar
Ahmad Reza committed
32

Karol Actun's avatar
Karol Actun committed
33
34
        self.grid = CubicGrid(5)

35
        self.init_particles=[]
36
        self.particle_id_counter = 0
37
38
39
        self.particles = []
        self.particles_created = []
        self.particle_rm = []
40
        self.particle_map_coordinates = {}
41
42
        self.particle_map_id = {}
        self.__particle_deleted=False
Ahmad Reza's avatar
Ahmad Reza committed
43

44
45
46
        self.tiles = []
        self.tiles_created = []
        self.tiles_rm = []
47
        self.tile_map_coordinates = {}
48
49
        self.tile_map_id = {}
        self.__tile_deleted=False
Ahmad Reza's avatar
Ahmad Reza committed
50
51
        self.new_tile = None

52
53
        self.markers = []
        self.markers_created = []
54
        self.marker_map_coordinates = {}
55
56
57
        self.marker_map_id = {}
        self.markers_rm = []
        self.__marker_deleted = False
Ahmad Reza's avatar
Ahmad Reza committed
58

59
        self.config_data = config_data
60

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

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

Ahmad Reza's avatar
Ahmad Reza committed
69
        if self.config_data.particle_random_order:
70
71
            random.shuffle(self.particles)

Ahmad Reza's avatar
Ahmad Reza committed
72
        if config_data.visualization:
Karol Actun's avatar
Karol Actun committed
73
            self.window = vis3d.Visualization(self)
74

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

82
    def set_successful_end(self):
Ahmad Reza's avatar
Ahmad Reza committed
83
        self.csv_round.success()
84
        self.set_end()
Ahmad Reza Cheraghi's avatar
Ahmad Reza Cheraghi committed
85
86
87
88
89
90
91
92
        
    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
93
94
95
96
97
98
99
100
101

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

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

102
103
104
105
106
107
108
109
    def get_max_round(self):
        """
        The max round number

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

110
    def set_unsuccessful_end(self):
Ahmad Reza's avatar
Ahmad Reza committed
111
112
113
114
115
116
117
118
119
120
121
        """
        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

122
    def inc_round_counter_by(self, number=1):
Ahmad Reza's avatar
Ahmad Reza committed
123
124
125
126
127
        """
        Increases the the round counter by

        :return:
        """
128
        self.__round_counter +=  number
Ahmad Reza's avatar
Ahmad Reza committed
129
130
131
132
133
134
135
136
137

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

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

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

        :return: The actual number of Particles
        """
144
        return len(self.particles)
145
146
147

    def get_particle_list(self):
        """
Ahmad Reza's avatar
Ahmad Reza committed
148
        Returns the actual number of particles in the world
149
150
151
152
153

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

154
    def get_particle_map_coordinates(self):
155
156
157
158
159
        """
        Get a dictionary with all particles mapped with their actual coordinates

        :return: a dictionary with particles and their coordinates
        """
160
        return self.particle_map_coordinates
161
162
163
164
165
166
167
168
169

    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

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

        :return: The actual number of Particles
        """
176
        return len(self.tiles)
177
178
179

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

Ahmad Reza's avatar
Ahmad Reza committed
182
        :return: a list of all the tiles in the world
183
184
185
        """
        return self.tiles

186
    def get_tile_map_coordinates(self):
187
188
189
190
191
        """
        Get a dictionary with all tiles mapped with their actual coordinates

        :return: a dictionary with particles and their coordinates
        """
192
        return self.tile_map_coordinates
193
194
195
196
197
198
199
200
201

    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

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

206
        :return: The actual number of markers
207
        """
208
        return len(self.markers)
209

210
    def get_marker_list(self):
211
        """
Ahmad Reza's avatar
Ahmad Reza committed
212
        Returns the actual number of markers in the world
213

214
        :return: The actual number of markers
215
        """
216
        return self.markers
217

218
    def get_marker_map_coordinates(self):
219
        """
220
        Get a dictionary with all markers mapped with their actual coordinates
221

222
        :return: a dictionary with markers and their coordinates
223
        """
224
        return self.marker_map_coordinates
225

226
    def get_marker_map_id(self):
227
        """
228
        Get a dictionary with all markers mapped with their own ids
229

230
        :return: a dictionary with markers and their own ids
231
        """
232
        return self.marker_map_id
233

234
    def get_world_x_size(self):
235
236
        """

Ahmad Reza's avatar
Ahmad Reza committed
237
        :return: Returns the maximal x size of the world
238
        """
Ahmad Reza's avatar
Ahmad Reza committed
239
        return self.config_data.size_x
240

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

247
248
249
250
251
252
    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)

253
254
255
256
257
258
    def get_tile_deleted(self):
        return self.__tile_deleted

    def get_particle_deleted(self):
        return self.__particle_deleted

259
260
    def get_marker_deleted(self):
        return self.__marker_deleted
261
262
263
264
265
266
267

    def set_tile_deleted(self):
        self.__tile_deleted = False

    def set_particle_deleted(self):
        self.__particle_deleted=False

268
269
    def set_marker_deleted(self):
        self.__marker_deleted = False
270

Karol Actun's avatar
Karol Actun committed
271
    def add_particle(self, x, y, z=None, color=black, transparency=1):
272
        """
Ahmad Reza's avatar
Ahmad Reza committed
273
        Add a particle to the world database
274
275
276

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


        :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:
329
                del self.particle_map_coordinates[rm_particle.coordinates]
330
331
332
333
                del self.particle_map_id[id]
            except:
                pass
            self.particle_rm.append(rm_particle)
Ahmad Reza's avatar
Ahmad Reza committed
334
335
            self.csv_round.update_particle_num(len(self.particles))
            self.csv_round.update_metrics(particle_deleted=1)
336
337
338
339
340
            self.__particle_deleted = True
            return True
        else:
            return False

341
    def remove_particle_on(self, coordinates):
342
        """
Ahmad Reza's avatar
Ahmad Reza committed
343
        Removes a particle on a give coordinat from to the world database
344

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

Karol Actun's avatar
Karol Actun committed
366
    def add_tile(self, x, y, z=None, color=gray, transparency=1):
367
        """
Ahmad Reza's avatar
Ahmad Reza committed
368
        Adds a tile to the world database
369
370
371
372

        :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
373
        :param z: the z coordinates on which the tile should be added
374
        :return: Successful added matter; False: Unsuccsessful
375
        """
Karol Actun's avatar
Karol Actun committed
376
377
378
379
380
381
382
        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)
383
384
        if transparency < 0 or transparency >1:
            transparency = 1
Karol Actun's avatar
Karol Actun committed
385
386
387
388
        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) )
389
                self.tiles.append(self.new_tile)
Ahmad Reza's avatar
Ahmad Reza committed
390
                self.csv_round.update_tiles_num(len(self.tiles))
391
                self.tile_map_coordinates[self.new_tile.coordinates] = self.new_tile
392
393
                self.tile_map_id[self.new_tile.get_id()] = self.new_tile

Karol Actun's avatar
Karol Actun committed
394
                # print("Afer adding ", len(self.tiles), self.new_tile.coordinates )
395
                logging.info("Created tile with tile id %s on coordinates %s",str(self.new_tile.get_id()), str(self.new_tile.coordinates))
396
                self.new_tile.touch()
397
                return self.new_tile
398
399
400
401
402
403
404
            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
405
    def add_tile_vis(self, coordinates, color=gray, transparency=1):
406
        """
Ahmad Reza's avatar
Ahmad Reza committed
407
        Adds a tile to the world database
408
409

        :param color:
Karol Actun's avatar
Karol Actun committed
410
        :param coordinates: the coordinates on which the tile should be added
411
412
        :return: True: Successful added; False: Unsuccsessful
        """
Karol Actun's avatar
Karol Actun committed
413
414
415
        if grid.is_valid_location(coordinates) == True:
            if coordinates not in self.tile_map_coordinates:
                self.new_tile = tile.Tile(self, coordinates, color, transparency)
416
417
                self.tiles.append(self.new_tile)

418
                self.tile_map_coordinates[self.new_tile.coordinates] = self.new_tile
419
420
                self.tile_map_id[self.new_tile.get_id()] = self.new_tile

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

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

457
    def remove_tile_on(self, coordinates):
458
        """
Ahmad Reza's avatar
Ahmad Reza committed
459
        Removes a tile on a give coordinat from to the world database
460

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

Karol Actun's avatar
Karol Actun committed
482
    def add_marker(self, x, y, z=None, color=black, transparency=1):
483
        """
Ahmad Reza's avatar
Ahmad Reza committed
484
        Add a tile to the world database
485
486
487
488

        :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
489
        :param z: the z coordinates on which the tile should be added
490
491
        :return: True: Successful added; False: Unsuccsessful
        """
Karol Actun's avatar
Karol Actun committed
492
493
494
495
496
497
498
        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)
499
500
        if transparency < 0 or transparency >1:
            transparency = 1
Karol Actun's avatar
Karol Actun committed
501
502
503
        if self.grid.is_valid_location(coordinates):
            if coordinates not in self.marker_map_coordinates:
                self.new_marker = marker.Marker(self, coordinates, color, transparency)
504
                self.markers.append(self.new_marker)
505
                self.marker_map_coordinates[self.new_marker.coordinates] = self.new_marker
506
                self.marker_map_id[self.new_marker.get_id()] = self.new_marker
Ahmad Reza's avatar
Ahmad Reza committed
507
                self.csv_round.update_markers_num(len(self.markers))
508
                logging.info("Created marker with id %s on coordinates %s", str(self.new_marker.get_id()), str(self.new_marker.coordinates))
509
510
511
512

                self.new_marker.created = True
                self.new_marker.touch()
                return self.new_marker
513
            else:
514
                logging.info("on x %f and y %f coordinates is a marker already", x, y)
515
516
517
                return False
        else:
            logging.info("for x %f and y %f not possible to draw ", x, y)
518
519
            return False

520
    def remove_marker(self, id):
521
        """
Ahmad Reza's avatar
Ahmad Reza committed
522
        Removes a tile with a given tile_id from to the world database
523

524
        :param id: The markers id that should be removec
525
526
        :return:  True: Successful removed; False: Unsuccessful
        """
527
528
529
530
531
        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)
532

533
            self.markers_rm.append(rm_marker)
534
            logging.info("Deleted marker with marker id %s on %s", str(id), str(rm_marker.coordinates))
535
            try:
536
                del self.marker_map_coordinates[rm_marker.coordinates]
537
538
539
            except KeyError:
                pass
            try:
540
                del self.marker_map_id[id]
541
542
            except KeyError:
                pass
Ahmad Reza's avatar
Ahmad Reza committed
543
544
            self.csv_round.update_markers_num(len(self.markers))
            self.csv_round.update_metrics( marker_deleted=1)
545
            self.__marker_deleted = True
546
547
548
549
            return True
        else:
            return False

550
    def remove_marker_on(self, coordinates):
551
        """
Ahmad Reza's avatar
Ahmad Reza committed
552
        Removes a marker on a give coordinat from to the world database
553

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