world.py 18.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
8
import importlib
import logging
Karol Actun's avatar
Karol Actun committed
9
10
11
import random
import threading

Karol Actun's avatar
Karol Actun committed
12
from lib import csv_generator, particle, tile, marker, vis3d
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
Karol Actun's avatar
Karol Actun committed
19
        :param config_data: configuration data from config.ini file
Ahmad Reza's avatar
Ahmad Reza committed
20
21
22
        """
        self.__round_counter = 1
        self.__end = False
Ahmad Reza's avatar
Ahmad Reza committed
23

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

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

42
43
        self.markers = []
        self.markers_created = []
44
        self.marker_map_coordinates = {}
45
46
47
        self.marker_map_id = {}
        self.markers_rm = []
        self.__marker_deleted = False
Karol Actun's avatar
Karol Actun committed
48
        self.new_marker = None
Ahmad Reza's avatar
Ahmad Reza committed
49

50
        self.config_data = config_data
Karol Actun's avatar
Karol Actun committed
51
        self.grid = config_data.grid
52

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

Karol Actun's avatar
Karol Actun committed
58
59
60
        if config_data.visualization:
            self.vis = vis3d.Visualization(self)

Ahmad Reza's avatar
Ahmad Reza committed
61
        mod = importlib.import_module('scenario.' + self.config_data.scenario)
Karol Actun's avatar
Karol Actun committed
62
63
64
65
66
67
68

        if config_data.visualization:
            import threading
            x = threading.Thread(target=mod.scenario, args=(self,))
            self.vis.wait_for_thread(x, "loading scenario... please wait.", "Loading Scenario")
        else:
            mod.scenario(self)
Ahmad Reza's avatar
Ahmad Reza committed
69

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

Karol Actun's avatar
Karol Actun committed
73
74
75
76
77
78
79
80
81
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
119
120
121
122
123
    def reset(self):
        """
        resets everything (particles, tiles, markers) except for the logging in system.log and in the csv file...
        reloads the scenario.
        :return:
        """
        self.__round_counter = 1
        self.__end = False

        self.init_particles = []
        self.particle_id_counter = 0
        self.particles = []
        self.particles_created = []
        self.particle_rm = []
        self.particle_map_coordinates = {}
        self.particle_map_id = {}
        self.__particle_deleted = False
        self.new_particle = None

        self.tiles = []
        self.tiles_created = []
        self.tiles_rm = []
        self.tile_map_coordinates = {}
        self.tile_map_id = {}
        self.__tile_deleted = False
        self.new_tile = None

        self.markers = []
        self.markers_created = []
        self.marker_map_coordinates = {}
        self.marker_map_id = {}
        self.markers_rm = []
        self.__marker_deleted = False
        self.new_marker = None

        if self.config_data.visualization:
            self.vis.reset()

        mod = importlib.import_module('scenario.' + self.config_data.scenario)

        if self.config_data.visualization:
            # if visualization is on, run the scenario in a separate thread and show that the program runs..
            x = threading.Thread(target=mod.scenario, args=(self,))
            self.vis.wait_for_thread(x, "loading scenario... please wait.", "Loading Scenario")
            self.vis._viewer.update_data()
        else:
            # if no vis, just run the scenario on the main thread
            mod.scenario(self)

        if self.config_data.particle_random_order:
            random.shuffle(self.particles)
124

Ahmad Reza's avatar
Ahmad Reza committed
125
126
    def csv_aggregator(self):
        self.csv_round.aggregate_metrics()
127
        particle_csv = csv_generator.CsvParticleFile(self.config_data.direction_name)
Karol Actun's avatar
Karol Actun committed
128
129
        for p in self.particles:
            particle_csv.write_particle(p)
130
        particle_csv.csv_file.close()
Ahmad Reza's avatar
Ahmad Reza committed
131

132
    def set_successful_end(self):
Ahmad Reza's avatar
Ahmad Reza committed
133
        self.csv_round.success()
Karol Actun's avatar
Karol Actun committed
134
        # self.set_end()
Ahmad Reza Cheraghi's avatar
Ahmad Reza Cheraghi committed
135
136
137
138
139
140
141
142
        
    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
143
144
145
146
147
148
149
150
151

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

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

152
    def set_unsuccessful_end(self):
Ahmad Reza's avatar
Ahmad Reza committed
153
154
155
        """
        Allows to terminate before the max round is reached
        """
Karol Actun's avatar
Karol Actun committed
156
        self.__end = True
Ahmad Reza's avatar
Ahmad Reza committed
157
158
159
160
161
162
163

    def get_end(self):
        """
            Returns the end parameter values either True or False
        """
        return self.__end

164
    def inc_round_counter_by(self, number=1):
Ahmad Reza's avatar
Ahmad Reza committed
165
166
167
168
169
        """
        Increases the the round counter by

        :return:
        """
Karol Actun's avatar
Karol Actun committed
170
        self.__round_counter += number
Ahmad Reza's avatar
Ahmad Reza committed
171
172
173
174
175
176
177

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

        :return: actual solution name
        """
Karol Actun's avatar
Karol Actun committed
178
        return self.config_data.solution
Ahmad Reza's avatar
Ahmad Reza committed
179

180
    def get_amount_of_particles(self):
181
        """
Ahmad Reza's avatar
Ahmad Reza committed
182
        Returns the actual number of particles in the world
183
184
185

        :return: The actual number of Particles
        """
186
        return len(self.particles)
187
188
189

    def get_particle_list(self):
        """
Ahmad Reza's avatar
Ahmad Reza committed
190
        Returns the actual number of particles in the world
191
192
193
194
195

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

196
    def get_particle_map_coordinates(self):
197
198
199
200
201
        """
        Get a dictionary with all particles mapped with their actual coordinates

        :return: a dictionary with particles and their coordinates
        """
202
        return self.particle_map_coordinates
203
204
205
206
207
208
209
210
211

    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

212
    def get_amount_of_tiles(self):
213
        """
Ahmad Reza's avatar
Ahmad Reza committed
214
        Returns the actual number of particles in the world
215
216
217

        :return: The actual number of Particles
        """
218
        return len(self.tiles)
219
220
221

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

Ahmad Reza's avatar
Ahmad Reza committed
224
        :return: a list of all the tiles in the world
225
226
227
        """
        return self.tiles

228
    def get_tile_map_coordinates(self):
229
230
231
232
233
        """
        Get a dictionary with all tiles mapped with their actual coordinates

        :return: a dictionary with particles and their coordinates
        """
234
        return self.tile_map_coordinates
235
236
237
238
239
240
241
242
243

    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

244
    def get_amount_of_markers(self):
245
        """
Ahmad Reza's avatar
Ahmad Reza committed
246
        Returns the actual number of markers in the world
247

248
        :return: The actual number of markers
249
        """
250
        return len(self.markers)
251

252
    def get_marker_list(self):
253
        """
Ahmad Reza's avatar
Ahmad Reza committed
254
        Returns the actual number of markers in the world
255

256
        :return: The actual number of markers
257
        """
258
        return self.markers
259

260
    def get_marker_map_coordinates(self):
261
        """
262
        Get a dictionary with all markers mapped with their actual coordinates
263

264
        :return: a dictionary with markers and their coordinates
265
        """
266
        return self.marker_map_coordinates
267

268
    def get_marker_map_id(self):
269
        """
270
        Get a dictionary with all markers mapped with their own ids
271

272
        :return: a dictionary with markers and their own ids
273
        """
274
        return self.marker_map_id
275

276
    def get_world_x_size(self):
277
278
        """

Ahmad Reza's avatar
Ahmad Reza committed
279
        :return: Returns the maximal x size of the world
280
        """
Ahmad Reza's avatar
Ahmad Reza committed
281
        return self.config_data.size_x
282

283
    def get_world_y_size(self):
284
        """
Ahmad Reza's avatar
Ahmad Reza committed
285
        :return: Returns the maximal y size of the world
286
        """
Ahmad Reza's avatar
Ahmad Reza committed
287
        return self.config_data.size_y
288

289
290
291
292
    def get_world_size(self):
        """
        :return: Returns the maximal (x,y) size of the world as a tupel
        """
Karol Actun's avatar
Karol Actun committed
293
        return self.config_data.size_x, self.config_data.size_y
294

295
296
297
298
299
300
    def get_tile_deleted(self):
        return self.__tile_deleted

    def get_particle_deleted(self):
        return self.__particle_deleted

301
302
    def get_marker_deleted(self):
        return self.__marker_deleted
303
304
305
306
307

    def set_tile_deleted(self):
        self.__tile_deleted = False

    def set_particle_deleted(self):
Karol Actun's avatar
Karol Actun committed
308
        self.__particle_deleted = False
309

310
311
    def set_marker_deleted(self):
        self.__marker_deleted = False
312

Karol Actun's avatar
Karol Actun committed
313
    def add_particle(self, coordinates, color=None):
314
        """
Ahmad Reza's avatar
Ahmad Reza committed
315
        Add a particle to the world database
316

Karol Actun's avatar
Karol Actun committed
317
318
319
        :param coordinates: The x coordinate of the particle
        :param color: The color of the particle
        :return: Added Matter; False: Unsuccessful
320
        """
Karol Actun's avatar
Karol Actun committed
321
322
323
324

        if len(coordinates) == 2:
            coordinates = (coordinates[0], coordinates[1], 0.0)

Ahmad Reza's avatar
Ahmad Reza committed
325
        if len(self.particles) < self.config_data.max_particles:
Karol Actun's avatar
Karol Actun committed
326
327
            if self.grid.is_valid_location(coordinates):
                if coordinates not in self.get_particle_map_coordinates():
Karol Actun's avatar
Karol Actun committed
328
329
                    if color is None:
                        color = self.config_data.particle_color
330
                    self.particle_id_counter += 1
Karol Actun's avatar
Karol Actun committed
331
332
333
334
335
336
337
                    self.new_particle = particle.Particle(self, coordinates, color, self.particle_id_counter)
                    if self.vis is not None:
                        self.vis.particle_changed(self.new_particle)
                    self.particles_created.append(self.new_particle)
                    self.particle_map_coordinates[self.new_particle.coordinates] = self.new_particle
                    self.particle_map_id[self.new_particle.get_id()] = self.new_particle
                    self.particles.append(self.new_particle)
Ahmad Reza's avatar
Ahmad Reza committed
338
                    self.csv_round.update_particle_num(len(self.particles))
Karol Actun's avatar
Karol Actun committed
339
340
341
342
                    self.init_particles.append(self.new_particle)
                    self.new_particle.created = True
                    logging.info("Created particle at %s", self.new_particle.coordinates)
                    return self.new_particle
343
                else:
Karol Actun's avatar
Karol Actun committed
344
                    logging.info("there is already a particle on %s" % str(coordinates))
345
346
                    return False
            else:
Karol Actun's avatar
Karol Actun committed
347
                logging.info("%s is not a valid location!" % str(coordinates))
Karol Actun's avatar
Karol Actun committed
348
                return False
349
350
351
352
        else:
            logging.info("Max of particles reached and no more particles can be created")
            return False

Karol Actun's avatar
Karol Actun committed
353
    def remove_particle(self, particle_id):
Ahmad Reza's avatar
Ahmad Reza committed
354
        """ Removes a particle with a given particle id from the world database
355
356
357
358
359


        :param particle_id: particle id
        :return: True: Successful removed; False: Unsuccessful
        """
Karol Actun's avatar
Karol Actun committed
360
        rm_particle = self.particle_map_id[particle_id]
361
362
        if rm_particle:
            self.particles.remove(rm_particle)
Karol Actun's avatar
Karol Actun committed
363
364
            del self.particle_map_coordinates[rm_particle.coordinates]
            del self.particle_map_id[particle_id]
365
            self.particle_rm.append(rm_particle)
Karol Actun's avatar
Karol Actun committed
366
367
            if self.vis is not None:
                self.vis.remove_particle(rm_particle)
Ahmad Reza's avatar
Ahmad Reza committed
368
369
            self.csv_round.update_particle_num(len(self.particles))
            self.csv_round.update_metrics(particle_deleted=1)
370
371
372
373
374
            self.__particle_deleted = True
            return True
        else:
            return False

375
    def remove_particle_on(self, coordinates):
376
        """
Karol Actun's avatar
Karol Actun committed
377
        Removes a particle on a give coordinate from to the world database
378

Karol Actun's avatar
Karol Actun committed
379
        :param coordinates: A tuple that includes the x and y coordinates
380
381
        :return: True: Successful removed; False: Unsuccessful
        """
382
        if coordinates in self.particle_map_coordinates:
Karol Actun's avatar
Karol Actun committed
383
            return self.remove_particle(self.particle_map_coordinates[coordinates])
384
385
386
        else:
            return False

Karol Actun's avatar
Karol Actun committed
387
    def add_tile(self, coordinates, color=None):
388
        """
Ahmad Reza's avatar
Ahmad Reza committed
389
        Adds a tile to the world database
390

Karol Actun's avatar
Karol Actun committed
391
392
393
        :param color: color of the tile (None for config default)
        :param coordinates: the coordinates on which the tile should be added
        :return: Successful added matter; False: Unsuccessful
394
        """
Karol Actun's avatar
Karol Actun committed
395
396
397
398

        if len(coordinates) == 2:
            coordinates = (coordinates[0], coordinates[1], 0.0)

Karol Actun's avatar
Karol Actun committed
399
400
        if self.grid.is_valid_location(coordinates):
            if coordinates not in self.tile_map_coordinates:
Karol Actun's avatar
Karol Actun committed
401
402
403
                if color is None:
                    color = self.config_data.tile_color
                self.new_tile = tile.Tile(self, coordinates, color)
404
                self.tiles.append(self.new_tile)
Karol Actun's avatar
Karol Actun committed
405
406
                if self.vis is not None:
                    self.vis.tile_changed(self.new_tile)
Ahmad Reza's avatar
Ahmad Reza committed
407
                self.csv_round.update_tiles_num(len(self.tiles))
408
                self.tile_map_coordinates[self.new_tile.coordinates] = self.new_tile
409
                self.tile_map_id[self.new_tile.get_id()] = self.new_tile
Karol Actun's avatar
Karol Actun committed
410
411
                logging.info("Created tile with tile id %s on coordinates %s",
                             str(self.new_tile.get_id()), str(coordinates))
412
                return self.new_tile
413
            else:
Karol Actun's avatar
Karol Actun committed
414
                logging.info("there is already a tile on %s " % str(coordinates))
415
416
                return False
        else:
Karol Actun's avatar
Karol Actun committed
417
418
            logging.info("%s is not a valid location!" % str(coordinates))
            return False
419

Karol Actun's avatar
Karol Actun committed
420
    def remove_tile(self, tile_id):
421
        """
Ahmad Reza's avatar
Ahmad Reza committed
422
        Removes a tile with a given tile_id from to the world database
423

Karol Actun's avatar
Karol Actun committed
424
        :param tile_id: The tiles id that should be removed
425
426
        :return:  True: Successful removed; False: Unsuccessful
        """
Karol Actun's avatar
Karol Actun committed
427
428
        if tile_id in self.tile_map_id:
            rm_tile = self.tile_map_id[tile_id]
429
430
            self.tiles.remove(rm_tile)
            self.tiles_rm.append(rm_tile)
Karol Actun's avatar
Karol Actun committed
431
432
433
            if self.vis is not None:
                self.vis.remove_tile(rm_tile)
            logging.info("Deleted tile with tile id %s on %s", str(rm_tile.get_id()), str(rm_tile.coordinates))
434
435
436
437
438
            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
439
                del self.tile_map_coordinates[rm_tile.coordinates]
440
441
            except KeyError:
                pass
Ahmad Reza's avatar
Ahmad Reza committed
442
443
            self.csv_round.update_tiles_num(len(self.tiles))
            self.csv_round.update_metrics(tile_deleted=1)
444
445
446
447
448
            self.__tile_deleted = True
            return True
        else:
            return False

449
    def remove_tile_on(self, coordinates):
450
        """
Karol Actun's avatar
Karol Actun committed
451
        Removes a tile on a give coordinates from to the world database
452

Karol Actun's avatar
Karol Actun committed
453
        :param coordinates: A tuple that includes the x and y coordinates
454
455
        :return: True: Successful removed; False: Unsuccessful
        """
456
        if coordinates in self.tile_map_coordinates:
Karol Actun's avatar
Karol Actun committed
457
458
            return self.remove_tile(self.tile_map_coordinates[coordinates].get_id())

459
460
461
        else:
            return False

Karol Actun's avatar
Karol Actun committed
462
    def add_marker(self, coordinates, color=None):
463
        """
Ahmad Reza's avatar
Ahmad Reza committed
464
        Add a tile to the world database
465
466

        :param color:
Karol Actun's avatar
Karol Actun committed
467
468
        :param coordinates: the coordinates on which the tile should be added
        :return: True: Successful added; False: Unsuccessful
469
        """
Karol Actun's avatar
Karol Actun committed
470
471
472
473

        if len(coordinates) == 2:
            coordinates = (coordinates[0], coordinates[1], 0.0)

Karol Actun's avatar
Karol Actun committed
474
475
        if self.grid.is_valid_location(coordinates):
            if coordinates not in self.marker_map_coordinates:
Karol Actun's avatar
Karol Actun committed
476
477
478
                if color is None:
                    color = self.config_data.marker_color
                self.new_marker = marker.Marker(self, coordinates, color)
479
                self.markers.append(self.new_marker)
Karol Actun's avatar
Karol Actun committed
480
481
                if self.vis is not None:
                    self.vis.marker_changed(self.new_marker)
482
                self.marker_map_coordinates[self.new_marker.coordinates] = self.new_marker
483
                self.marker_map_id[self.new_marker.get_id()] = self.new_marker
Ahmad Reza's avatar
Ahmad Reza committed
484
                self.csv_round.update_markers_num(len(self.markers))
Karol Actun's avatar
Karol Actun committed
485
486
                logging.info("Created marker with id %s on coordinates %s",
                             str(self.new_marker.get_id()), str(self.new_marker.coordinates))
487
488
                self.new_marker.created = True
                return self.new_marker
489
            else:
Karol Actun's avatar
Karol Actun committed
490
                logging.info("there is already a marker on %s" % str(coordinates))
491
492
                return False
        else:
Karol Actun's avatar
Karol Actun committed
493
            logging.info("%s is not a valid location!" % str(coordinates))
494
495
            return False

Karol Actun's avatar
Karol Actun committed
496
    def remove_marker(self, marker_id):
497
        """
Ahmad Reza's avatar
Ahmad Reza committed
498
        Removes a tile with a given tile_id from to the world database
499

Karol Actun's avatar
Karol Actun committed
500
        :param marker_id: The markers id that should be removed
501
502
        :return:  True: Successful removed; False: Unsuccessful
        """
Karol Actun's avatar
Karol Actun committed
503
504
        if marker_id in self.marker_map_id:
            rm_marker = self.marker_map_id[marker_id]
505
506
            if rm_marker in self.markers:
                self.markers.remove(rm_marker)
Karol Actun's avatar
Karol Actun committed
507
508
            if self.vis is not None:
                self.vis.remove_marker(rm_marker)
509
            self.markers_rm.append(rm_marker)
Karol Actun's avatar
Karol Actun committed
510
            logging.info("Deleted marker with marker id %s on %s", str(marker_id), str(rm_marker.coordinates))
511
            try:
512
                del self.marker_map_coordinates[rm_marker.coordinates]
513
514
515
            except KeyError:
                pass
            try:
Karol Actun's avatar
Karol Actun committed
516
                del self.marker_map_id[marker_id]
517
518
            except KeyError:
                pass
Ahmad Reza's avatar
Ahmad Reza committed
519
            self.csv_round.update_markers_num(len(self.markers))
Karol Actun's avatar
Karol Actun committed
520
            self.csv_round.update_metrics(marker_deleted=1)
521
            self.__marker_deleted = True
522
523
524
525
            return True
        else:
            return False

526
    def remove_marker_on(self, coordinates):
527
        """
Karol Actun's avatar
Karol Actun committed
528
        Removes a marker on a give coordinates from to the world database
529

Karol Actun's avatar
Karol Actun committed
530
        :param coordinates: A tuple that includes the x and y coordinates
531
532
        :return: True: Successful removed; False: Unsuccessful
        """
533
        if coordinates in self.marker_map_coordinates:
Karol Actun's avatar
Karol Actun committed
534
            return self.remove_marker(self.marker_map_coordinates[coordinates].get_id())
535
        else:
Karol Actun's avatar
Karol Actun committed
536
            return False