Reputation: 175
I am working on a Maze Solver using a GA approach. Each genome has directions encoded as chars ('N', 'S', 'E', 'W'), and each genome in the generation are evaluated for fitness and crossbreed using a weighted Roulette.
The assignment is going well so far, but I noticed a quirk with my fitness function that is (I think) giving me longer/less optimal solutions.
Since I am aiming to solve a generic maze without a solution before hand, I set up my fitness function as follows: (please ignore the hardcoded start and end positions, those will be made generic later...)
private static double calculateMazeScore(Genome genome) {
int start_row = 1;
int start_col = 0;
int end_row = 12;
int end_col = 8;
MazePosition pos = new MazePosition(start_row, start_col);
int penalty = 0;
for (char gene : genome.geneString().toCharArray()) {
int n, e, s, w;
n = pos.getNeighborValueNeg1IfEdge(Direction.N);
e = pos.getNeighborValueNeg1IfEdge(Direction.E);
s = pos.getNeighborValueNeg1IfEdge(Direction.S);
w = pos.getNeighborValueNeg1IfEdge(Direction.W);
// System.out.printf("pos:[%d,%d] val:%d next:%s n:%d s:%d e:%d w:%d \n",
// pos.getRowIdx(), pos.getColumnIdx(), pos.getValue(), gene, n, s, e, w);
if (gene == 'N') {
if (n == 0 || n == 2 || n == 3) {
pos = pos.getNeighbor(Direction.N);
penalty -= 1.5;
} else {
penalty++;
}
} else if (gene == 'E') {
if (e == 0 || e == 2 || e == 3) {
pos = pos.getNeighbor(Direction.E);
penalty -= 1.5;
} else {
penalty++;
}
} else if (gene == 'S') {
if (s == 0 || s == 2 || s == 3) {
pos = pos.getNeighbor(Direction.S);
penalty -= 1.5;
} else {
penalty++;
}
} else if (gene == 'W') {
if (w == 0 || w == 2 || w == 3) {
pos = pos.getNeighbor(Direction.W);
penalty -= 1.5;
}
}
}
double x1 = pos.getRowIdx();
double y1 = pos.getColumnIdx();
double distFromStart = Math.sqrt(Math.pow(start_row - x1, 2) + Math.pow(start_col - y1, 2));
double distFromGoal = Math.sqrt(Math.pow(end_row - x1, 2) + Math.pow(end_col - y1, 2));
double mazeScore = distFromStart - distFromGoal - penalty;
if (mazeScore <= 0)
mazeScore = 0;
return mazeScore;
}
As I evaluate the genome string through the maze, I increment a penalty counter when the genome "bumps" into a wall, and decrement the counter when it doesn't. After the genome is done, I do a distance calculation to find the distance from the start and end. The final fitness is dist_from_start - dist_from_end - penalties.
This actually works pretty well, the genomes get really close to the end without bumping into too many walls... but I think I incentivized wandering around the maze too much. I get a lot of "WEWEWEWENSNSNSNSNS" artificially increasing the scores... The most fit of the last 10 generations or so wander around the maze for a long time before making their way to the exit, and sometimes they don't get all the way their because they spent too much time going back and forth.
What I have now is OK... but not great. I would love some advice for improving my fitness function. I will also be experimenting with different crossover and selection processes later. Thanks in advance!
Upvotes: 0
Views: 1204