Browse Source

Pheromone: object model

Bastien Sevajol 9 years ago
parent
commit
af8f3c0bd4

+ 8 - 0
intelligine/core/exceptions.py View File

@@ -18,6 +18,14 @@ class NoPheromoneMove(PheromoneException, MovementException):
18 18
     pass
19 19
 
20 20
 
21
+class NoTypeInPheromone(NoPheromone):
22
+    pass
23
+
24
+
25
+class NoCategoryInPheromone(NoPheromone):
26
+    pass
27
+
28
+
21 29
 class BestPheromoneHere(PheromoneException):
22 30
 
23 31
     def __init__(self, best_distance,  *args, **kwargs):

+ 11 - 18
intelligine/display/Pygame.py View File

@@ -1,3 +1,4 @@
1
+from intelligine.core.exceptions import NoPheromone
1 2
 from xyzworld.display.Pygame import Pygame as XyzPygame
2 3
 import pygame
3 4
 from intelligine.cst import PHEROMON_INFOS, PHEROMON_DIRECTION, PHEROMON_DIR_HOME, PHEROMON_DIR_EXPLO, PHEROMON_POSITIONS
@@ -11,37 +12,29 @@ class Pygame(XyzPygame):
11 12
         super().__init__(config, context)
12 13
         self._is_display_pheromones = False
13 14
 
14
-
15 15
     def receive(self, synergy_object_manager, context):
16 16
         super().receive(synergy_object_manager, context)
17 17
         if self._is_display_pheromones:
18 18
             self._display_pheromones(context.metas.list.get(PHEROMON_POSITIONS, PHEROMON_POSITIONS, allow_empty=True), context)
19 19
 
20 20
     def _display_pheromones(self, pheromones_positions, context):
21
+        # TODO: Code de test bordelique !
21 22
         for point in pheromones_positions:
22
-            exploration_info = context.pheromones().get_info(point,
23
-                                                             [PHEROMON_DIRECTION,
24
-                                                              PHEROMON_DIR_HOME],
25
-                                                             allow_empty=True,
26
-                                                             empty_value={})
27
-
28
-            # TODO: ne pas avoir a creer d'objet, voir comment dans display
29
-            if exploration_info:
23
+            flavour = context.pheromones().get_flavour(point)
24
+            try:
25
+                flavour.get_pheromone(category=PHEROMON_DIRECTION, type=PHEROMON_DIR_HOME)
30 26
                 pheromon = PheromonHome(object(), context)
31 27
                 pheromon.set_direction(11) # TODO: plus de direction avec ces nlles pheromones
32 28
                 self._draw_objects_with_decal(point, [pheromon])
33
-
34
-
35
-            exploration_info = context.pheromones().get_info(point,
36
-                                                             [PHEROMON_DIRECTION,
37
-                                                              PHEROMON_DIR_EXPLO],
38
-                                                             allow_empty=True,
39
-                                                             empty_value={})
40
-            if exploration_info:
41
-                # TODO: ne pas avoir a creer d'objet, voir comment dans display
29
+            except NoPheromone:
30
+                pass
31
+            try:
32
+                flavour.get_pheromone(category=PHEROMON_DIRECTION, type=PHEROMON_DIR_EXPLO)
42 33
                 pheromon = PheromonExploration(object(), context)
43 34
                 pheromon.set_direction(11) # TODO: plus de direction avec ces nlles pheromones
44 35
                 self._draw_objects_with_decal(point, [pheromon])
36
+            except NoPheromone:
37
+                pass
45 38
 
46 39
     def _key_pressed(self, key):
47 40
         if key == pygame.K_p:

+ 4 - 3
intelligine/simulation/object/pheromone/MovementPheromoneGland.py View File

@@ -1,11 +1,12 @@
1
+from intelligine.cst import PHEROMON_DIRECTION
1 2
 from intelligine.simulation.object.pheromone.PheromoneGland import PheromoneGland
3
+from intelligine.simulation.pheromone.Pheromone import Pheromone
2 4
 
3 5
 
4 6
 class MovementPheromoneGland(PheromoneGland):
5 7
 
6
-
7
-    def get_movement_molecules(self):
8
+    def get_pheromone(self):
8 9
         """
9 10
         :return: pheromone_type, distance_from_objective
10 11
         """
11
-        return self._pheromone_type, self._host.get_brain().get_distance_from_objective()
12
+        return Pheromone(PHEROMON_DIRECTION, self._pheromone_type, self._host.get_brain().get_distance_from_objective(), 1)

+ 4 - 1
intelligine/simulation/object/pheromone/PheromoneGland.py View File

@@ -16,10 +16,13 @@ class PheromoneGland():
16 16
             raise Exception("pheromone_type not specified")
17 17
         return self._pheromone_type
18 18
 
19
+    def get_pheromone(self):
20
+        raise NotImplementedError()
21
+
19 22
     def appose(self):
20 23
         try:
21 24
             DirectionPheromone.appose(self._context,
22 25
                                       self._host.get_position(),
23
-                                      self._host.get_movement_pheromone_gland().get_movement_molecules())
26
+                                      self.get_pheromone())
24 27
         except BestPheromoneHere as best_pheromone_here:
25 28
             self._host.get_brain().set_distance_from_objective(best_pheromone_here.get_best_distance())

+ 26 - 31
intelligine/simulation/pheromone/DirectionPheromone.py View File

@@ -8,48 +8,43 @@ from intelligine.synergy.event.move.direction import get_direction_for_degrees
8 8
 class DirectionPheromone():
9 9
 
10 10
     @staticmethod
11
-    def appose(context, point, movement_molecules):
12
-        pheromone_type, distance_from = movement_molecules
13
-        context.pheromones().increment(point, [PHEROMON_DIRECTION, pheromone_type], distance=distance_from)
11
+    def appose(context, point, pheromone):
12
+        context.pheromones().increment_with_pheromone(point, pheromone)
14 13
         context.metas.list.add(PHEROMON_POSITIONS, PHEROMON_POSITIONS, point, assert_not_in=False)
15 14
 
16 15
     @staticmethod
17 16
     def get_direction_for_point(context, point, pheromone_type):
18
-        try:
19
-            pheromone_info = context.pheromones().get_info(point, [PHEROMON_DIRECTION, pheromone_type])
20
-        except KeyError:
21
-            raise NoPheromone()
22
-
23
-        # DEBUG: On se rettrouve avec un {} ...
24
-        if not pheromone_info:
25
-            raise NoPheromone()
17
+        flavour = context.pheromones().get_flavour(point)
18
+        pheromone = flavour.get_pheromone(category=PHEROMON_DIRECTION, type=pheromone_type)
26 19
 
27
-        point_intensity = pheromone_info[1]
28
-        point_distance = pheromone_info[0]
20
+        distance = pheromone.get_distance()
29 21
         around_points = context.get_around_points_of(point)
30
-
22
+        # TODO: Cet algo around a mettre ailleurs
31 23
         around_pheromones_points = []
32 24
         for around_point in around_points:
33
-            around_pheromone_info = context.pheromones().get_info(around_point,
34
-                                                                   [PHEROMON_DIRECTION, pheromone_type],
35
-                                                                   allow_empty=True,
36
-                                                                   empty_value={})
37
-            if around_pheromone_info and around_pheromone_info[0] < point_distance:
38
-                around_pheromones_points.append((around_point, around_pheromone_info))
25
+            flavour = context.pheromones().get_flavour(around_point)
26
+            try:
27
+                around_pheromone = flavour.get_pheromone(category=PHEROMON_DIRECTION, type=pheromone_type)
28
+                if around_pheromone.get_distance() < distance:
29
+                    around_pheromones_points.append((around_point, around_pheromone))
30
+            except NoPheromone:
31
+                pass  # No pheromone, ok continue to sniff around
39 32
 
40 33
         if not around_pheromones_points:
41 34
             raise NoPheromone()
42 35
 
43 36
         shuffle(around_pheromones_points)
44
-        around_pheromones_sorted = sorted(around_pheromones_points, key=lambda x: x[1][1], reverse=True)
45
-        max_intensity = around_pheromones_sorted[0][1][1]
37
+        around_pheromones_sorted = sorted(around_pheromones_points, key=lambda x: x[1].get_intensity(), reverse=True)
38
+        max_intensity = around_pheromones_sorted[0][1].get_intensity()
46 39
 
47 40
         around_pheromones_max = []
48 41
         for around_pheromone_sorted in around_pheromones_sorted:
49
-            if around_pheromone_sorted[1][1] == max_intensity:
42
+            if around_pheromone_sorted[1].get_intensity() == max_intensity:
50 43
                 around_pheromones_max.append(around_pheromone_sorted)
51 44
 
52
-        around_pheromones_sorted_by_distance = sorted(around_pheromones_max, key=lambda x: x[1][0], reverse=False)
45
+        around_pheromones_sorted_by_distance = sorted(around_pheromones_max,
46
+                                                      key=lambda x: x[1].get_distance(),
47
+                                                      reverse=False)
53 48
 
54 49
         go_to_point = around_pheromones_sorted_by_distance[0][0]
55 50
 
@@ -76,18 +71,18 @@ class DirectionPheromone():
76 71
     def get_best_pheromone_direction_in(context, reference_point, points, pheromone_type):
77 72
         around_pheromones_points = []
78 73
         for around_point in points:
79
-            around_pheromone_info = context.pheromones().get_info(around_point,
80
-                                                                   [PHEROMON_DIRECTION, pheromone_type],
81
-                                                                   allow_empty=True,
82
-                                                                   empty_value={})
83
-            if around_pheromone_info:
84
-                around_pheromones_points.append((around_point, around_pheromone_info))
74
+            flavour = context.pheromones().get_flavour(around_point)
75
+            try:
76
+                around_pheromone = flavour.get_pheromone(category=PHEROMON_DIRECTION, type=pheromone_type)
77
+                around_pheromones_points.append((around_point, around_pheromone))
78
+            except NoPheromone:
79
+                pass  # Ok, no pheromone, continue to sniff around
85 80
 
86 81
         if not around_pheromones_points:
87 82
             raise NoPheromone()
88 83
 
89 84
         shuffle(around_pheromones_points)
90
-        around_pheromones_sorted = sorted(around_pheromones_points, key=lambda x: x[1][1], reverse=True)
85
+        around_pheromones_sorted = sorted(around_pheromones_points, key=lambda x: x[1].get_intensity(), reverse=True)
91 86
         go_to_point = around_pheromones_sorted[0][0]
92 87
 
93 88
         direction_degrees = get_degree_from_north(reference_point, go_to_point)

+ 24 - 1
intelligine/simulation/pheromone/Pheromone.py View File

@@ -1,2 +1,25 @@
1 1
 class Pheromone():
2
-    pass
2
+
3
+    def __init__(self, category, type, distance=None, intensity=0):
4
+        self._category = category
5
+        self._type = type
6
+        self._distance = distance
7
+        self._intensity = intensity
8
+
9
+    def get_category(self):
10
+        return self._category
11
+
12
+    def get_type(self):
13
+        return self._type
14
+
15
+    def get_distance(self):
16
+        return self._distance
17
+
18
+    def set_distance(self, distance):
19
+        self._distance = distance
20
+
21
+    def get_intensity(self):
22
+        return self._intensity
23
+
24
+    def increment_intensity(self, increment_value):
25
+        self._intensity += increment_value

+ 31 - 1
intelligine/simulation/pheromone/PheromoneFlavour.py View File

@@ -1,2 +1,32 @@
1
+from intelligine.core.exceptions import NoTypeInPheromone, NoCategoryInPheromone
2
+from intelligine.simulation.pheromone.Pheromone import Pheromone
3
+
4
+
1 5
 class PheromoneFlavour():
2
-    pass
6
+
7
+    def __init__(self, point_data):
8
+        self._point_data = point_data
9
+
10
+    def get_pheromone(self, category, type):
11
+        types = self.get_types(category)
12
+        if type not in types:
13
+            raise NoTypeInPheromone()
14
+        distance, intensity = types[type]
15
+        return Pheromone(category, type, distance, intensity)
16
+
17
+    def get_types(self, category):
18
+        if category not in self._point_data:
19
+            raise NoCategoryInPheromone()
20
+        return self._point_data[category]
21
+
22
+    def update_pheromone(self, pheromone):
23
+        category = pheromone.get_category()
24
+        type = pheromone.get_type()
25
+
26
+        if category not in self._point_data:
27
+            self._point_data[category] = {}
28
+
29
+        self._point_data[category][type] = (pheromone.get_distance(), pheromone.get_intensity())
30
+
31
+    def get_raw_data(self):
32
+        return self._point_data

+ 1 - 3
intelligine/synergy/object/ant/Ant.py View File

@@ -77,9 +77,7 @@ class Ant(Bug):
77 77
     def initialize(self):
78 78
         super().initialize()
79 79
         try:
80
-            DirectionPheromone.appose(self._context,
81
-                                      self.get_position(),
82
-                                      self.get_movement_pheromone_gland().get_movement_molecules())
80
+            self.get_movement_pheromone_gland().appose()
83 81
         except BestPheromoneHere as best_pheromone_here:
84 82
             pass
85 83
 

+ 33 - 29
intelligine/synergy/stigmergy/PheromonesManager.py View File

@@ -1,6 +1,7 @@
1
-from intelligine.core.exceptions import BestPheromoneHere
1
+from intelligine.core.exceptions import BestPheromoneHere, NoPheromone
2 2
 from intelligine.cst import PHEROMON_INFOS
3
-from intelligine.core.exceptions import NoPheromone
3
+from intelligine.simulation.pheromone.PheromoneFlavour import PheromoneFlavour
4
+from intelligine.simulation.pheromone.Pheromone import Pheromone
4 5
 
5 6
 
6 7
 class PheromonesManager():
@@ -8,11 +9,13 @@ class PheromonesManager():
8 9
     def __init__(self, context):
9 10
         self._context = context
10 11
 
11
-    def get_pheromones(self, position, prepare=[]):
12
+    def get_flavour(self, position):
12 13
         point_pheromones = self._context.metas.value.get(PHEROMON_INFOS,
13 14
                                                          position,
14 15
                                                          allow_empty=True,
15 16
                                                          empty_value={})
17
+        return PheromoneFlavour(point_pheromones)
18
+
16 19
         current_check = point_pheromones
17 20
         for prepare_key in prepare:
18 21
             if prepare_key not in current_check:
@@ -21,11 +24,19 @@ class PheromonesManager():
21 24
 
22 25
         return point_pheromones
23 26
 
24
-    def set_pheromones(self, position, pheromones):
25
-        self._context.metas.value.set(PHEROMON_INFOS, position, pheromones)
27
+    def set_flavour(self, position, flavour):
28
+        self._context.metas.value.set(PHEROMON_INFOS, position, flavour.get_raw_data())
29
+
30
+    def get_pheromone(self, position, category, type, allow_empty=False, empty_value=None):
31
+        # TODO: empty_value as du sens ?
32
+        flavour = self.get_flavour(position)
33
+        try:
34
+            return flavour.get_pheromone(category, type)
35
+        except NoPheromone:
36
+            if allow_empty:
37
+                return Pheromone()
38
+            raise
26 39
 
27
-    def get_info(self, position, address, allow_empty=False, empty_value=None):
28
-        pheromones = self.get_pheromones(position, address[:-1])
29 40
 
30 41
         pheromone = pheromones
31 42
         for key in address[:-1]:
@@ -39,29 +50,22 @@ class PheromonesManager():
39 50
 
40 51
         return pheromone[address[-1]]
41 52
 
42
-    def increment(self, position, address, distance, increment_value=1):
43
-        pheromones = self.get_pheromones(position, address[:-1])
44
-
45
-        pheromone = pheromones
46
-        for key in address[:-1]:
47
-            pheromone = pheromone[key]
48
-
49
-        if address[-1] not in pheromone:
50
-            pheromone[address[-1]] = (distance, 0)
51
-        # On se retrouve avec un {} dans pheromone[address[-1]]. A cause de la recherche de pheromone avant (et main process)
52
-        if not pheromone[address[-1]]:
53
-            pheromone[address[-1]] = (distance, 0)
54
-
55
-        pheromone_distance = pheromone[address[-1]][0]
56
-        pheromone_intensity = pheromone[address[-1]][1]
53
+    def increment_with_pheromone(self, position, apposed_pheromone):
54
+        flavour = self.get_flavour(position)
55
+        try:
56
+            position_pheromone = flavour.get_pheromone(apposed_pheromone.get_category(), apposed_pheromone.get_type())
57
+        except NoPheromone:
58
+            position_pheromone = Pheromone(apposed_pheromone.get_category(),
59
+                                           apposed_pheromone.get_type(),
60
+                                           distance=apposed_pheromone.get_distance())
57 61
 
58
-        pheromone_intensity += increment_value
62
+        position_pheromone.increment_intensity(apposed_pheromone.get_intensity())
59 63
 
60
-        if distance < pheromone_distance:
61
-            pheromone_distance = distance
64
+        if apposed_pheromone.get_distance() < position_pheromone.get_distance():
65
+            position_pheromone.set_distance(apposed_pheromone.get_distance())
62 66
 
63
-        pheromone[address[-1]] = (pheromone_distance, pheromone_intensity)
64
-        self.set_pheromones(position, pheromones)
67
+        flavour.update_pheromone(position_pheromone)
68
+        self.set_flavour(position, flavour)
65 69
 
66
-        if distance > pheromone_distance:
67
-            raise BestPheromoneHere(pheromone_distance)
70
+        if apposed_pheromone.get_distance() > position_pheromone.get_distance():
71
+            raise BestPheromoneHere(position_pheromone.get_distance())

+ 2 - 2
intelligine/tests/simulation/mode/TestChangeMode.py View File

@@ -96,8 +96,8 @@ class TestChangeMode(Base):
96 96
 
97 97
         self._run_and_get_core(2)
98 98
         self.assertEquals((0, 0, -2), self.ant.get_position())
99
-        self.assertEquals((PHEROMON_DIR_EXPLO, 0),
100
-                          self.ant.get_movement_pheromone_gland().get_movement_molecules())
99
+        pheromone = self.ant.get_movement_pheromone_gland().get_pheromone()
100
+        self.assertEquals((PHEROMON_DIR_EXPLO, 0), (pheromone.get_type(), pheromone.get_distance()))
101 101
 
102 102
         # Ant has take Food piece
103 103
         self._run_and_get_core(3)

+ 9 - 4
intelligine/tests/simulation/pheromone/TestDirection.py View File

@@ -1,6 +1,8 @@
1 1
 from os import getcwd
2 2
 from sys import path as ppath
3 3
 from intelligine.core.exceptions import NoPheromone
4
+from intelligine.simulation.pheromone.Pheromone import Pheromone
5
+from intelligine.simulation.pheromone.PheromoneFlavour import PheromoneFlavour
4 6
 
5 7
 ppath.insert(1,getcwd()+'/modules')
6 8
 
@@ -26,7 +28,7 @@ class TestDirection(Base):
26 28
         if re_init:
27 29
             self._context = Context()
28 30
         for position in pheromones:
29
-            self._context.pheromones().set_pheromones(position, pheromones[position])
31
+            self._context.pheromones().set_flavour(position, PheromoneFlavour(pheromones[position]))
30 32
 
31 33
     def _test_direction_for_point(self, pheromones, direction, pheromone_type=PHEROMON_DIR_EXPLO,
32 34
                                   reference_point=_p(CENTER), re_init=True):
@@ -207,7 +209,7 @@ class TestDirection(Base):
207 209
         # Une pheromone au centre
208 210
         DirectionPheromone.appose(self._context,
209 211
                                   _p(CENTER),
210
-                                  (PHEROMON_DIR_EXPLO, 2))
212
+                                  self._get_pheromone(PHEROMON_DIR_EXPLO, 2))
211 213
         # Ne permet pas de trouver une route
212 214
         self._test_point_raise_no_pheromone(re_init=False)
213 215
         self._test_points_raise_no_pheromone(re_init=False)
@@ -215,7 +217,7 @@ class TestDirection(Base):
215 217
         # Une pheromone au nord
216 218
         DirectionPheromone.appose(self._context,
217 219
                                   _p(NORTH),
218
-                                  (PHEROMON_DIR_EXPLO, 1))
220
+                                  self._get_pheromone(PHEROMON_DIR_EXPLO, 1))
219 221
         # le permet
220 222
         self._test_direction_for_points({}, NORTH, re_init=False)
221 223
         self._test_direction_for_point({}, NORTH, re_init=False)
@@ -233,4 +235,7 @@ class TestDirection(Base):
233 235
         try:  # WTF ?
234 236
             self._test_direction_for_points(pheromones, direction, re_init=re_init)
235 237
         except NoPheromone:
236
-            self.assertTrue(True)
238
+            self.assertTrue(True)
239
+
240
+    def _get_pheromone(self, type, distance):
241
+        return Pheromone(PHEROMON_DIRECTION, type, distance=distance)