Artificial Ant Problem

Source Code

  1from deap_er import creator
  2from deap_er import tools
  3from deap_er import base
  4from deap_er import gp
  5from functools import partial
  6import random
  7import numpy
  8import copy
  9
 10
 11random.seed(1234)  # disables randomization
 12
 13
 14class AntSimulator:
 15    direction = ["north", "east", "south", "west"]
 16    dir_row = [1, 0, -1, 0]
 17    dir_col = [0, 1, 0, -1]
 18
 19    def __init__(self, max_moves):
 20        self.max_moves = max_moves
 21
 22    def _reset(self):
 23        self.matrix_exc = copy.deepcopy(self.matrix)
 24        self.row = self.row_start
 25        self.col = self.col_start
 26        self.dir = 1
 27        self.moves = 0
 28        self.eaten = 0
 29
 30    @staticmethod
 31    def _if_then_else(condition, out1, out2):
 32        out1() if condition() else out2()
 33
 34    @property
 35    def position(self):
 36        return self.row, self.col, self.direction[self.dir]
 37
 38    def turn_left(self):
 39        if self.moves < self.max_moves:
 40            self.moves += 1
 41            self.dir = (self.dir - 1) % 4
 42
 43    def turn_right(self):
 44        if self.moves < self.max_moves:
 45            self.moves += 1
 46            self.dir = (self.dir + 1) % 4
 47
 48    def move_forward(self):
 49        if self.moves < self.max_moves:
 50            self.moves += 1
 51            self.row = (self.row + self.dir_row[self.dir]) % self.matrix_row
 52            self.col = (self.col + self.dir_col[self.dir]) % self.matrix_col
 53            if self.matrix_exc[self.row][self.col] == "food":
 54                self.eaten += 1
 55            self.matrix_exc[self.row][self.col] = "passed"
 56
 57    def sense_food(self):
 58        ahead_row = (self.row + self.dir_row[self.dir]) % self.matrix_row
 59        ahead_col = (self.col + self.dir_col[self.dir]) % self.matrix_col
 60        return self.matrix_exc[ahead_row][ahead_col] == "food"
 61
 62    def if_food_ahead(self, out1, out2):
 63        return partial(self._if_then_else, self.sense_food, out1, out2)
 64
 65    def run(self, routine):
 66        self._reset()
 67        while self.moves < self.max_moves:
 68            routine()
 69
 70    def parse_matrix(self, matrix):
 71        self.matrix = list()
 72        for i, line in enumerate(matrix):
 73            self.matrix.append(list())
 74            for j, col in enumerate(line):
 75                if col == "#":
 76                    self.matrix[-1].append("food")
 77                elif col == ".":
 78                    self.matrix[-1].append("empty")
 79                elif col == "S":
 80                    self.matrix[-1].append("empty")
 81                    self.row_start = self.row = i
 82                    self.col_start = self.col = j
 83                    self.dir = 1
 84        self.matrix_row = len(self.matrix)
 85        self.matrix_col = len(self.matrix[0])
 86        self.matrix_exc = copy.deepcopy(self.matrix)
 87
 88
 89def prog(*args):
 90    for arg in args:
 91        arg()
 92
 93
 94def prog2(out1, out2):
 95    return partial(prog, out1, out2)
 96
 97
 98def prog3(out1, out2, out3):
 99    return partial(prog, out1, out2, out3)
100
101
102def evaluate(individual, ant, prim_set):
103    routine = gp.compile_tree(individual, prim_set)
104    ant.run(routine)
105    return ant.eaten,  # The comma is essential here.
106
107
108def setup():
109    ant = AntSimulator(max_moves=600)
110    with open("art_ant_trail.txt") as trail_file:
111        ant.parse_matrix(trail_file)
112
113    pset = gp.PrimitiveSet("MAIN", 0)
114    pset.add_primitive(ant.if_food_ahead, 2)
115    pset.add_primitive(prog2, 2)
116    pset.add_primitive(prog3, 3)
117    pset.add_terminal(ant.move_forward)
118    pset.add_terminal(ant.turn_left)
119    pset.add_terminal(ant.turn_right)
120
121    creator.create("FitnessMax", base.Fitness, weights=(1.0,))
122    creator.create("Individual", gp.PrimitiveTree, fitness=creator.FitnessMax)
123
124    toolbox = base.Toolbox()
125    toolbox.register("expr_init", gp.gen_full, prim_set=pset, min_depth=1, max_depth=2)
126    toolbox.register("individual", tools.init_iterate, creator.Individual, toolbox.expr_init)
127    toolbox.register("population", tools.init_repeat, list, toolbox.individual)
128    toolbox.register("evaluate", evaluate, ant=ant, prim_set=pset)
129    toolbox.register("select", tools.sel_tournament, contestants=7)
130    toolbox.register("mate", gp.cx_one_point)
131    toolbox.register("expr_mut", gp.gen_full, min_depth=0, max_depth=2)
132    toolbox.register("mutate", gp.mut_uniform, expr=toolbox.expr_mut, prim_set=pset)
133
134    stats = tools.Statistics(lambda ind: ind.fitness.values)
135    stats.register("avg", numpy.mean)
136    stats.register("std", numpy.std)
137    stats.register("min", numpy.min)
138    stats.register("max", numpy.max)
139
140    return toolbox,  stats
141
142
143def print_results(best_ind):
144    if not best_ind.fitness.values > (50,):
145        raise RuntimeError('Evolution failed to converge.')
146    print(f'\nEvolution converged correctly.')
147
148
149def main():
150    toolbox, stats = setup()
151    pop = toolbox.population(size=300)
152    hof = tools.HallOfFame(1)
153    args = dict(
154        toolbox=toolbox,
155        population=pop,
156        generations=40,
157        cx_prob=0.5,
158        mut_prob=0.1,
159        hof=hof,
160        stats=stats,
161        verbose=True  # prints stats
162    )
163    tools.ea_simple(**args)
164    print_results(hof[0])
165
166
167if __name__ == "__main__":
168    main()

Food Trail

 1S###............................
 2...#............................
 3...#.....................###....
 4...#....................#....#..
 5...#....................#....#..
 6...####.#####........##.........
 7............#................#..
 8............#.......#...........
 9............#.......#........#..
10............#.......#...........
11....................#...........
12............#................#..
13............#...................
14............#.......#.....###...
15............#.......#..#........
16.................#..............
17................................
18............#...........#.......
19............#...#..........#....
20............#...#...............
21............#...#...............
22............#...#.........#.....
23............#..........#........
24............#...................
25...##..#####....#...............
26.#..............#...............
27.#..............#...............
28.#......#######.................
29.#.....#........................
30.......#........................
31..####..........................
32................................