Genetic Algorithm Experiment #1 Phase 1


I read about genetic algorithms a while ago and I've wanted to try them for a while, but didn't have the right platform to do so in.
But luckily this year I'm taking AP Computer Science and we're using a case study called GridWorld, which is a perfect way to test things.

Without further ado, here's my write up of my experiment.

---

Time to start work on the first experiment of my first genetic algorithm experiments.
Here's my setup:
Gridworld critters
Four genomes:
G1: Atk 1, Def 1, Con 1
G2: Atk 2, Def 1, Con 1
G3: Atk 1, Def 2, Con 1
G4: Atk 1, Def 1, Con 2

Attack is amount of damage, defence is how much damage is negated when attacked, regenerates 1 point of def every (two?) steps. Con is how many hitpoints it has. Regenerates 1 HP per step if not attacking. Whoever gets to their step first will regen first, tough luck if a genome isn't regened when it gets attacked. Def regeneration isn't cumulative, a genome must spend two full turns regernating one point of Def.

All genomes attack each other.
If another genome is within range (1 square) of another it determines randomly, at this point, which to attack. Then attacks, it gets first strike, so it does its' damage first then the other genome does its' damage.
First experiment, only four bugs with genomes.

I think that one of the 3 stronger ones are going to prevail simply because they have higher stats at this point. Of those three, I would have to say that G2 is probably going to win because of how hard it hits.


Problems
First problem I've run into is that processActors returns actors and not GenomeBases, so I need to figure out how to turn an Actor into it's GenomeBase.
I believe I've figured it out, and the answer surprised me. If you loop through the actors in processActors and then check if they're an instanceof GenomeBase it actually counts it as a GenomeBase. And yes, it does return the GenomeBase of Actor. That's excellent.

Result
It takes a lot longer than I thought it would for them to actually die, even G1. But they do die, mostly.
In the end, after letting the simulation run for around a minute or so, G2 and G4 were the two left. It makes sense once I think about it because G4 can absorb 3 points of damage, and it regens all of that in two steps.
I think that defence should take longer to regen, or maybe health should take longer, not sure exactly.


And here is my GenomeBase code, if you're interested in it. It's in Java and required the GridWorld jar.
import java.awt.Color;
import java.util.ArrayList;

import info.gridworld.actor.Actor;
import info.gridworld.actor.Critter;
import info.gridworld.grid.Location;


public class GenomeBase extends Critter
{
 private static int DEFAULT_ATK = 1, DEFAULT_DEF = 1, DEFAULT_CON = 1;
 private int dAtk, dDef, dCon;
 public int Atk, Def, Con;
 private byte DefRegenStep;
 private boolean canAttack;
 
 /**
  * Default constructor of GenomeBase, all stats default to 1
  */
 public GenomeBase()
 {
  dAtk = Atk = DEFAULT_ATK;
  dDef = Def = DEFAULT_DEF;
  dCon = Con = DEFAULT_CON;
  
  canAttack = true;
  
  setColor(Color.white);
 }
 public GenomeBase(int Atk, int Def, int Con)
 {
  // Set all default and current stats
  this.dAtk = this.Atk = Atk;
  this.dDef = this.Def = Def;
  this.dCon = this.Con = Con;
  
  canAttack = true;
  
  setColor(Color.white); // Default GenomeBase color is white
 }
 
 /**
  * Attack another GenomeBase
  * @param toAttack, the GenomeBase to attack
  */
 public void attack(GenomeBase toAttack)
 {
  if(canAttack)
  {
   canAttack = false;
   toAttack.attacked(this);
  }
 }
 
 /**
  * Being attacked by another GenomBase
  * This is private to avoid confusion, attack another GenomeBase through attack()
  * @param attacker the GenomeBase who is attacking
  */
 private void attacked(GenomeBase attacker)
 {
  if(attacker.Atk > 0)
  {
   int DmgIncoming = attacker.Atk;
   if(Def > 0)
   {
    DmgIncoming -= Def;
    Def -= DmgIncoming;
    
    if(Def < 0)
     Def = 0;
    
    if(DmgIncoming > 0)
    {
     Con -= DmgIncoming;
     if(Con <= 0)
     {
      removeSelfFromGrid();
     }
    }
   }
  }
  
  // Repoiste, if still alive
  attack(attacker);
 }
 
 /**
  * GenomeBase regeneration, gains Con and Def with conditions.
  */
 private void regen()
 {
  if(Con < dCon)
   Con++;
  if(DefRegenStep == 2)
  {
   DefRegenStep = 0;
   Def++;
  }
  else
   DefRegenStep++;
 }
 
 // Override Methods
    @Override
    public void act()
    {
        if (getGrid() == null)
            return;
        ArrayList actors = getActors();
        processActors(actors);
        
        if(getLocation() == null) return; // Apparently stuff goes wrong when you remove an actor from the grid in the middle of stuff?
        
        ArrayList moveLocs = getMoveLocations();
        Location loc = selectMoveLocation(moveLocs);
        makeMove(loc);
    }

    @Override
    public ArrayList getActors()
    {
        return getGrid().getNeighbors(getLocation());
    }

    @Override
    public void processActors(ArrayList actors)
    {
     canAttack = true;
     
     if(Def == dDef && Con == dCon)
     {
         // We're ready, let's attack if we can
      
      ArrayList genomeList = new ArrayList();
      for(Actor a : actors)
      {
       if(a instanceof GenomeBase) // Get all other genomes around so we can attack them
       {
        genomeList.add((GenomeBase) a);
       }
      }
      
      int num = genomeList.size();
      
      if(num > 0) // There are other actors around us, let's attack
      {
       GenomeBase toAttack = genomeList.get(((int) Math.random() * num));
       attack(toAttack);
      }
     }
     else
     {
      regen(); // Spend a turn regenerating Con and Def
     }
    }

    @Override
    public ArrayList getMoveLocations()
    {
        return getGrid().getEmptyAdjacentLocations(getLocation());
    }

    @Override
    public Location selectMoveLocation(ArrayList locs)
    {
        int n = locs.size();
        if (n == 0)
            return getLocation();
        int r = (int) (Math.random() * n);
        return locs.get(r);
    }

    @Override
    public void makeMove(Location loc)
    {
     setDirection(getLocation().getDirectionToward(loc));
        super.makeMove(loc);
    }
}

0 comments:

Post a Comment