world.py 19.4 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
Ahmad Reza's avatar
Ahmad Reza committed
12
from lib.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.dir_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.dir_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's avatar
Ahmad Reza committed
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118

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

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

    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

    def inc_round_cnter(self):
        """
        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

119
    def get_amount_of_particles(self):
120
        """
Ahmad Reza's avatar
Ahmad Reza committed
121
        Returns the actual number of particles in the world
122
123
124

        :return: The actual number of Particles
        """
125
        return len(self.particles)
126
127
128

    def get_particle_list(self):
        """
Ahmad Reza's avatar
Ahmad Reza committed
129
        Returns the actual number of particles in the world
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150

        :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

151
    def get_amount_of_tiles(self):
152
        """
Ahmad Reza's avatar
Ahmad Reza committed
153
        Returns the actual number of particles in the world
154
155
156

        :return: The actual number of Particles
        """
157
        return len(self.tiles)
158
159
160

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

Ahmad Reza's avatar
Ahmad Reza committed
163
        :return: a list of all the tiles in the world
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
        """
        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

183
    def get_amount_of_markers(self):
184
        """
Ahmad Reza's avatar
Ahmad Reza committed
185
        Returns the actual number of markers in the world
186

187
        :return: The actual number of markers
188
        """
189
        return len(self.markers)
190

191
    def get_marker_list(self):
192
        """
Ahmad Reza's avatar
Ahmad Reza committed
193
        Returns the actual number of markers in the world
194

195
        :return: The actual number of markers
196
        """
197
        return self.markers
198

199
    def get_marker_map_coords(self):
200
        """
201
        Get a dictionary with all markers mapped with their actual coordinates
202

203
        :return: a dictionary with markers and their coordinates
204
        """
205
        return self.marker_map_coords
206

207
    def get_marker_map_id(self):
208
        """
209
        Get a dictionary with all markers mapped with their own ids
210

211
        :return: a dictionary with markers and their own ids
212
        """
213
        return self.marker_map_id
214
215
216
217
218
219
220
221
222
223
224

    def get_coords_in_dir(self, coords, dir):
        """
        Returns the coordination data of the pointed directions

        :param coords: particles actual staying coordination
        :param dir: The direction. Options:  E, SE, SW, W, NW, or NE
        :return: The coordinaiton of the pointed directions
        """
        return coords[0] + x_offset[dir], coords[1] + y_offset[dir]

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_tile_deleted(self):
        return self.__tile_deleted

    def get_particle_deleted(self):
        return self.__particle_deleted

244
245
    def get_marker_deleted(self):
        return self.__marker_deleted
246
247
248
249
250
251
252

    def set_tile_deleted(self):
        self.__tile_deleted = False

    def set_particle_deleted(self):
        self.__particle_deleted=False

253
254
    def set_marker_deleted(self):
        self.__marker_deleted = False
255

256
    def add_particle(self, x, y, color=black, alpha=1):
257
        """
Ahmad Reza's avatar
Ahmad Reza committed
258
        Add a particle to the world database
259
260
261
262
263
264

        :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
265
        :return: Added Matter; False: Unsuccsessful
266
267
268
        """
        if alpha < 0 or alpha >1:
            alpha = 1
Ahmad Reza's avatar
Ahmad Reza committed
269
        if len(self.particles) < self.config_data.max_particles:
270
            if check_coords(x,y) == True:
271
                if (x,y) not in self.get_particle_map_coords():
272
273
274
                    self.particle_id_counter += 1
                    new_particle = particle.Particle(self, x, y, color, alpha, self.particle_id_counter)
                    print(new_particle.number)
275
276
277
278
279
                    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
280
                    self.csv_round.update_particle_num(len(self.particles))
281
282
283
                    self.init_particles.append(new_particle)
                    new_particle.created=True
                    logging.info("Created particle at %s", new_particle.coords)
284
                    return new_particle
285
286
287
288
289
290
291
292
293
294
295
                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
296
        """ Removes a particle with a given particle id from the world database
297
298
299
300
301
302
303
304
305
306
307
308
309
310


        :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
311
312
            self.csv_round.update_particle_num(len(self.particles))
            self.csv_round.update_metrics(particle_deleted=1)
313
314
315
316
317
318
319
            self.__particle_deleted = True
            return True
        else:
            return False

    def remove_particle_on(self, coords):
        """
Ahmad Reza's avatar
Ahmad Reza committed
320
        Removes a particle on a give coordinat from to the world database
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335

        :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
336
337
            self.csv_round.update_particle_num(len(self.particles))
            self.csv_round.update_metrics( particle_deleted=1)
338
339
340
341
342
343
344
            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
345
        Adds a tile to the world database
346
347
348
349

        :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
350
        :return: Successful added matter; False: Unsuccsessful
351
        """
352
        if alpha < 0 or alpha >1:
353
            alpha = 1
354
        if check_coords(x,y) == True:
355
            if (x,y) not in self.tile_map_coords:
Ahmad Reza Cheraghi's avatar
Ahmad Reza Cheraghi committed
356
                self.new_tile=tile.Tile(self, x, y, color, alpha)
357
358
                print("Before adding ", len(self.tiles) )
                self.tiles.append(self.new_tile)
Ahmad Reza's avatar
Ahmad Reza committed
359
                self.csv_round.update_tiles_num(len(self.tiles))
360
361
362
363
364
365
                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()
366
                return self.new_tile
367
368
369
370
371
372
373
374
375
            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
376
        Adds a tile to the world database
377
378
379
380
381
382

        :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
        """
383
        if check_coords(x, y) == True:
384
            if (x, y) not in self.tile_map_coords:
Ahmad Reza Cheraghi's avatar
Ahmad Reza Cheraghi committed
385
                self.new_tile = tile.Tile(self, x, y, color, alpha)
386
387
388
389
390
                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
391
                print("world.add_tile",self.new_tile.coords)
392
393
394
395
396
397
398
399
400
                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
401
        Removes a tile with a given tile_id from to the world database
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419

        :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
420
421
            self.csv_round.update_tiles_num(len(self.tiles))
            self.csv_round.update_metrics(tile_deleted=1)
422
423
424
425
426
427
428
            self.__tile_deleted = True
            return True
        else:
            return False

    def remove_tile_on(self, coords):
        """
Ahmad Reza's avatar
Ahmad Reza committed
429
        Removes a tile on a give coordinat from to the world database
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444

        :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
445
446
            self.csv_round.update_tiles_num(len(self.tiles))
            self.csv_round.update_metrics( tile_deleted=1)
447
448
449
450
451
            self.__tile_deleted = True
            return True
        else:
            return False

452
    def add_marker(self, x, y, color=black, alpha=1):
453
        """
Ahmad Reza's avatar
Ahmad Reza committed
454
        Add a tile to the world database
455
456
457
458
459
460
461
462

        :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
463
        if check_coords(x, y) == True:
464
            if (x, y) not in self.marker_map_coords:
465
                self.new_marker = marker.Marker(self, x, y, color, alpha)
466
467
468
                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
469
                self.csv_round.update_markers_num(len(self.markers))
470
471
472
473
474
                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
475
            else:
476
                logging.info("on x %f and y %f coordinates is a marker already", x, y)
477
478
479
                return False
        else:
            logging.info("for x %f and y %f not possible to draw ", x, y)
480
481
            return False

482
    def remove_marker(self, id):
483
        """
Ahmad Reza's avatar
Ahmad Reza committed
484
        Removes a tile with a given tile_id from to the world database
485

486
        :param id: The markers id that should be removec
487
488
        :return:  True: Successful removed; False: Unsuccessful
        """
489
490
491
492
493
        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)
494

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

512
    def remove_marker_on(self, coords):
513
        """
Ahmad Reza's avatar
Ahmad Reza committed
514
        Removes a marker on a give coordinat from to the world database
515
516
517
518

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