world.py 20.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
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
        self.particles = []
        self.particles_created = []
        self.particle_rm = []
37
        self.particle_map_coordinates = {}
38
39
        self.particle_map_id = {}
        self.__particle_deleted=False
Ahmad Reza's avatar
Ahmad Reza committed
40

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

49
50
        self.markers = []
        self.markers_created = []
51
        self.marker_map_coordinates = {}
52
53
54
        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 set_successful_end(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

107
    def set_unsuccessful_end(self):
Ahmad Reza's avatar
Ahmad Reza committed
108
109
110
111
112
113
114
115
116
117
118
        """
        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_by(self, number=1):
Ahmad Reza's avatar
Ahmad Reza committed
120
121
122
123
124
        """
        Increases the the round counter by

        :return:
        """
125
        self.__round_counter +=  number
Ahmad Reza's avatar
Ahmad Reza committed
126
127
128
129
130
131
132
133
134

    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

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

151
    def get_particle_map_coordinates(self):
152
153
154
155
156
        """
        Get a dictionary with all particles mapped with their actual coordinates

        :return: a dictionary with particles and their coordinates
        """
157
        return self.particle_map_coordinates
158
159
160
161
162
163
164
165
166

    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
        """
        return self.tiles

183
    def get_tile_map_coordinates(self):
184
185
186
187
188
        """
        Get a dictionary with all tiles mapped with their actual coordinates

        :return: a dictionary with particles and their coordinates
        """
189
        return self.tile_map_coordinates
190
191
192
193
194
195
196
197
198

    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_coordinates(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_coordinates
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_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)

250
251
252
253
254
255
    def get_tile_deleted(self):
        return self.__tile_deleted

    def get_particle_deleted(self):
        return self.__particle_deleted

256
257
    def get_marker_deleted(self):
        return self.__marker_deleted
258
259
260
261
262
263
264

    def set_tile_deleted(self):
        self.__tile_deleted = False

    def set_particle_deleted(self):
        self.__particle_deleted=False

265
266
    def set_marker_deleted(self):
        self.__marker_deleted = False
267

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

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


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

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

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

355
    def add_tile(self, x, y, color=gray, transparency=1):
356
        """
Ahmad Reza's avatar
Ahmad Reza committed
357
        Adds a tile to the world database
358
359
360
361

        :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
362
        :return: Successful added matter; False: Unsuccsessful
363
        """
364
365
        if transparency < 0 or transparency >1:
            transparency = 1
366
        if check_values_are_coordinates(x,y) == True:
367
368
            if (x,y) not in self.tile_map_coordinates:
                self.new_tile=tile.Tile(self, x, y, color, transparency)
369
370
                print("Before adding ", len(self.tiles) )
                self.tiles.append(self.new_tile)
Ahmad Reza's avatar
Ahmad Reza committed
371
                self.csv_round.update_tiles_num(len(self.tiles))
372
                self.tile_map_coordinates[self.new_tile.coordinates] = self.new_tile
373
374
                self.tile_map_id[self.new_tile.get_id()] = self.new_tile

375
376
                print("Afer adding ", len(self.tiles), self.new_tile.coordinates )
                logging.info("Created tile with tile id %s on coordinates %s",str(self.new_tile.get_id()), str(self.new_tile.coordinates))
377
                self.new_tile.touch()
378
                return self.new_tile
379
380
381
382
383
384
385
            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

386
    def add_tile_vis(self, x, y, color=gray, transparency=1):
387
        """
Ahmad Reza's avatar
Ahmad Reza committed
388
        Adds a tile to the world database
389
390
391
392
393
394

        :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
        """
395
        if check_values_are_coordinates(x, y) == True:
396
397
            if (x, y) not in self.tile_map_coordinates:
                self.new_tile = tile.Tile(self, x, y, color, transparency)
398
399
                self.tiles.append(self.new_tile)

400
                self.tile_map_coordinates[self.new_tile.coordinates] = self.new_tile
401
402
                self.tile_map_id[self.new_tile.get_id()] = self.new_tile

403
404
405
                print("world.add_tile",self.new_tile.coordinates)
                logging.info("Created tile with tile id %s on coordinates %s", str(self.new_tile.get_id()),
                             str(self.new_tile.coordinates))
406
407
408
409
410
411
412
                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
413
        Removes a tile with a given tile_id from to the world database
414
415
416
417
418
419
420
421
422

        :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)
423
            logging.info("Deleted tile with tile id %s on %s", str(rm_tile.get_id()), str(rm_tile.coordinates) )
424
425
426
427
428
            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
429
                del self.tile_map_coordinates[rm_tile.coordinates]
430
431
            except KeyError:
                pass
Ahmad Reza's avatar
Ahmad Reza committed
432
433
            self.csv_round.update_tiles_num(len(self.tiles))
            self.csv_round.update_metrics(tile_deleted=1)
434
435
436
437
438
            self.__tile_deleted = True
            return True
        else:
            return False

439
    def remove_tile_on(self, coordinates):
440
        """
Ahmad Reza's avatar
Ahmad Reza committed
441
        Removes a tile on a give coordinat from to the world database
442

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

464
    def add_marker(self, x, y, color=black, transparency=1):
465
        """
Ahmad Reza's avatar
Ahmad Reza committed
466
        Add a tile to the world database
467
468
469
470
471
472

        :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
        """
473
474
        if transparency < 0 or transparency >1:
            transparency = 1
475
        if check_values_are_coordinates(x, y) == True:
476
477
            if (x, y) not in self.marker_map_coordinates:
                self.new_marker = marker.Marker(self, x, y, color, transparency)
478
                self.markers.append(self.new_marker)
479
                self.marker_map_coordinates[self.new_marker.coordinates] = self.new_marker
480
                self.marker_map_id[self.new_marker.get_id()] = self.new_marker
Ahmad Reza's avatar
Ahmad Reza committed
481
                self.csv_round.update_markers_num(len(self.markers))
482
                logging.info("Created marker with id %s on coordinates %s", str(self.new_marker.get_id()), str(self.new_marker.coordinates))
483
484
485
486

                self.new_marker.created = True
                self.new_marker.touch()
                return self.new_marker
487
            else:
488
                logging.info("on x %f and y %f coordinates is a marker already", x, y)
489
490
491
                return False
        else:
            logging.info("for x %f and y %f not possible to draw ", x, y)
492
493
            return False

494
    def remove_marker(self, id):
495
        """
Ahmad Reza's avatar
Ahmad Reza committed
496
        Removes a tile with a given tile_id from to the world database
497

498
        :param id: The markers id that should be removec
499
500
        :return:  True: Successful removed; False: Unsuccessful
        """
501
502
503
504
505
        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)
506

507
            self.markers_rm.append(rm_marker)
508
            logging.info("Deleted marker with marker id %s on %s", str(id), str(rm_marker.coordinates))
509
            try:
510
                del self.marker_map_coordinates[rm_marker.coordinates]
511
512
513
            except KeyError:
                pass
            try:
514
                del self.marker_map_id[id]
515
516
            except KeyError:
                pass
Ahmad Reza's avatar
Ahmad Reza committed
517
518
            self.csv_round.update_markers_num(len(self.markers))
            self.csv_round.update_metrics( marker_deleted=1)
519
            self.__marker_deleted = True
520
521
522
523
            return True
        else:
            return False

524
    def remove_marker_on(self, coordinates):
525
        """
Ahmad Reza's avatar
Ahmad Reza committed
526
        Removes a marker on a give coordinat from to the world database
527

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