world.py 20.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
20
21
        :param seed: seed number for new random numbers
        :param max_round: the max round number for terminating the simulator
        :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
Ahmad Reza's avatar
Ahmad Reza committed
24
        :param sim_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
        """
Ahmad Reza's avatar
Ahmad Reza committed
29
        random.seed(config_data.seed_value)
Ahmad Reza's avatar
Ahmad Reza committed
30
31
        self.__round_counter = 1
        self.__end = False
Ahmad Reza's avatar
Ahmad Reza committed
32

33
34
35
36
37
38
39
40
        self.init_particles=[]
        self.particle_num=0
        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
41

42
43
44
45
46
47
48
        self.tiles_num = 0
        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
49
50
        self.new_tile = None

51
52
53
54
55
56
57
        self.markers_num=0
        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
58
59

        self.directory = config_data.dir_name
60
        self.config_data = config_data
61

Ahmad Reza's avatar
Ahmad Reza committed
62
63
64
65
66
67
68
        self.csv_round = csv_generator.CsvRoundData(scenario=config_data.scenario,
                                                    solution=config_data.solution,
                                                    seed=config_data.seed_value,
                                                    tiles_num=0, particle_num=0,
                                                    steps=0, directory=config_data.dir_name)

        mod = importlib.import_module('scenario.' + self.config_data.scenario)
Ahmad Reza's avatar
Ahmad Reza committed
69
        mod.scenario(self)
Ahmad Reza's avatar
Ahmad Reza committed
70
71

        if self.config_data.random_order:
72
73
            random.shuffle(self.particles)

Ahmad Reza's avatar
Ahmad Reza committed
74
75
        if config_data.visualization:
            self.window = vis.VisWindow(config_data.window_size_x, config_data.window_size_y, self)
76

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

84
    def success_termination(self):
Ahmad Reza's avatar
Ahmad Reza committed
85
        self.csv_round.success()
86
        self.set_end()
Ahmad Reza's avatar
Ahmad Reza committed
87
88
89
90
91
92
93

    def get_max_round(self):
        """
        Return the initialized endding round number

        :return: The maximum round number
        """
Ahmad Reza's avatar
Ahmad Reza committed
94
        return self.config_data.max_round
Ahmad Reza's avatar
Ahmad Reza committed
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132

    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


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

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

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

        :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


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

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

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

Ahmad Reza's avatar
Ahmad Reza committed
178
        :return: a list of all the tiles in the world
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
        """
        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

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

202
        :return: The actual number of markers
203
        """
204
        return self.markers_num
205

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

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

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

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

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

226
        :return: a dictionary with markers and their own ids
227
        """
228
        return self.marker_map_id
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243


    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]

    def get_sim_x_size(self):
        """

Ahmad Reza's avatar
Ahmad Reza committed
244
        :return: Returns the maximal x size of the world
245
        """
Ahmad Reza's avatar
Ahmad Reza committed
246
        return self.config_data.size_x
247
248
249

    def get_sim_y_size(self):
        """
Ahmad Reza's avatar
Ahmad Reza committed
250
        :return: Returns the maximal y size of the world
251
        """
Ahmad Reza's avatar
Ahmad Reza committed
252
        return self.config_data.size_y
253
254
255
256
257
258
259

    def get_tile_deleted(self):
        return self.__tile_deleted

    def get_particle_deleted(self):
        return self.__particle_deleted

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

    def set_tile_deleted(self):
        self.__tile_deleted = False

    def set_particle_deleted(self):
        self.__particle_deleted=False

269
270
    def set_marker_deleted(self):
        self.__marker_deleted = False
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291

    def check_coords(self, coords_x, coords_y):
        """
        Checks if the given coordinates are matching the
        hexagon coordinates

        :param coords_x: proposed x coordinate
        :param coords_y: proposed y coordinate
        :return: True: Correct x and y coordinates; False: Incorrect coordinates
        """

        if (coords_x / 0.5) % 2 == 0:
            if coords_y % 2 != 0:
                return False
            else:
                return True
        else:
            if coords_y % 2 == 0:
                return False
            else:
                return True
292
293
294
295
296
    def coords_to_sim(self, coords):
        return coords[0], coords[1] * math.sqrt(3 / 4)

    def sim_to_coords(self, x, y):
        return x, round(y / math.sqrt(3 / 4), 0)
297

298
    def add_particle(self, x, y, color=black, alpha=1):
299
        """
Ahmad Reza's avatar
Ahmad Reza committed
300
        Add a particle to the world database
301
302
303
304
305
306

        :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
307
        :return: Added Matter; False: Unsuccsessful
308
309
310
        """
        if alpha < 0 or alpha >1:
            alpha = 1
Ahmad Reza's avatar
Ahmad Reza committed
311
        if len(self.particles) < self.config_data.max_particles:
312
313
            if  self.check_coords(x,y) == True:
                if (x,y) not in self.get_particle_map_coords():
Ahmad Reza Cheraghi's avatar
Ahmad Reza Cheraghi committed
314
                    new_particle= particle.Particle(self, x, y, color, alpha)
315
316
317
318
319
                    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
320
                    self.csv_round.update_particle_num(len(self.particles))
321
322
323
                    self.init_particles.append(new_particle)
                    new_particle.created=True
                    logging.info("Created particle at %s", new_particle.coords)
324
                    return new_particle
325
326
327
328
329
330
331
332
333
334
335
                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
336
        """ Removes a particle with a given particle id from the world database
337
338
339
340
341
342
343
344
345
346
347
348
349
350


        :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
351
352
            self.csv_round.update_particle_num(len(self.particles))
            self.csv_round.update_metrics(particle_deleted=1)
353
354
355
356
357
358
359
            self.__particle_deleted = True
            return True
        else:
            return False

    def remove_particle_on(self, coords):
        """
Ahmad Reza's avatar
Ahmad Reza committed
360
        Removes a particle on a give coordinat from to the world database
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375

        :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
376
377
            self.csv_round.update_particle_num(len(self.particles))
            self.csv_round.update_metrics( particle_deleted=1)
378
379
380
381
382
383
384
            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
385
        Adds a tile to the world database
386
387
388
389

        :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
390
        :return: Successful added matter; False: Unsuccsessful
391
        """
392
        if alpha < 0 or alpha >1:
393
394
395
            alpha = 1
        if  self.check_coords(x,y) == True:
            if (x,y) not in self.tile_map_coords:
Ahmad Reza Cheraghi's avatar
Ahmad Reza Cheraghi committed
396
                self.new_tile=tile.Tile(self, x, y, color, alpha)
397
398
                print("Before adding ", len(self.tiles) )
                self.tiles.append(self.new_tile)
Ahmad Reza's avatar
Ahmad Reza committed
399
                self.csv_round.update_tiles_num(len(self.tiles))
400
401
402
403
404
405
                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()
406
                return self.new_tile
407
408
409
410
411
412
413
414
415
            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
416
        Adds a tile to the world database
417
418
419
420
421
422
423
424

        :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 self.check_coords(x, y) == True:
            if (x, y) not in self.tile_map_coords:
Ahmad Reza Cheraghi's avatar
Ahmad Reza Cheraghi committed
425
                self.new_tile = tile.Tile(self, x, y, color, alpha)
426
427
428
429
430
                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
431
                print("world.add_tile",self.new_tile.coords)
432
433
434
435
436
437
438
439
440
                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
441
        Removes a tile with a given tile_id from to the world database
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459

        :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
460
461
            self.csv_round.update_tiles_num(len(self.tiles))
            self.csv_round.update_metrics(tile_deleted=1)
462
463
464
465
466
467
468
            self.__tile_deleted = True
            return True
        else:
            return False

    def remove_tile_on(self, coords):
        """
Ahmad Reza's avatar
Ahmad Reza committed
469
        Removes a tile on a give coordinat from to the world database
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484

        :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
485
486
            self.csv_round.update_tiles_num(len(self.tiles))
            self.csv_round.update_metrics( tile_deleted=1)
487
488
489
490
491
492
            self.__tile_deleted = True
            return True
        else:
            return False


493
    def add_marker(self, x, y, color=black, alpha=1):
494
        """
Ahmad Reza's avatar
Ahmad Reza committed
495
        Add a tile to the world database
496
497
498
499
500
501
502
503
504

        :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
        if self.check_coords(x, y) == True:
505
            if (x, y) not in self.marker_map_coords:
506
                self.new_marker = marker.Marker(self, x, y, color, alpha)
507
508
509
                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
510
                self.csv_round.update_markers_num(len(self.markers))
511
512
513
514
515
                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
516
            else:
517
                logging.info("on x %f and y %f coordinates is a marker already", x, y)
518
519
520
                return False
        else:
            logging.info("for x %f and y %f not possible to draw ", x, y)
521
522
            return False

523

524
    def remove_marker(self, id):
525
        """
Ahmad Reza's avatar
Ahmad Reza committed
526
        Removes a tile with a given tile_id from to the world database
527

528
        :param id: The markers id that should be removec
529
530
        :return:  True: Successful removed; False: Unsuccessful
        """
531
532
533
534
535
        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)
536

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


555
    def remove_marker_on(self, coords):
556
        """
Ahmad Reza's avatar
Ahmad Reza committed
557
        Removes a marker on a give coordinat from to the world database
558
559
560
561

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