com.ahsgaming.valleyofbones.ai.AIPlayer.java Source code

Java tutorial

Introduction

Here is the source code for com.ahsgaming.valleyofbones.ai.AIPlayer.java

Source

/**
 * Copyright 2012 Jami Couch
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * This project uses:
 * 
 * LibGDX
 * Copyright 2011 see LibGDX AUTHORS file
 * Licensed under Apache License, Version 2.0 (see above).
 * 
 */
package com.ahsgaming.valleyofbones.ai;

import com.ahsgaming.valleyofbones.GameController;
import com.ahsgaming.valleyofbones.Player;
import com.ahsgaming.valleyofbones.Utils;
import com.ahsgaming.valleyofbones.VOBGame;
import com.ahsgaming.valleyofbones.map.HexMap;
import com.ahsgaming.valleyofbones.network.*;
import com.ahsgaming.valleyofbones.units.AbstractUnit;
import com.ahsgaming.valleyofbones.units.Prototypes;
import com.ahsgaming.valleyofbones.units.Unit;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Json;

import java.text.DecimalFormat;
import java.util.HashMap;

/**
 * @author jami
 *
 */
public class AIPlayer extends Player {
    public String LOG = "AIPlayer";

    float countdown = 0.5f;
    float timer = 0.5f;

    float[] goalMatrix, threatMatrix, valueMatrix;
    boolean[] visibilityMatrix;

    NetController netController;
    GenomicAI genome;

    /**
     * @param id
     * @param name
     * @param color
     */
    public AIPlayer(NetController netController, int id, String name, Color color) {
        super(id, name, color);
        this.netController = netController;
    }

    /**
     * @param id
     * @param color
     */
    public AIPlayer(NetController netController, int id, Color color) {
        super(id, color);
        this.netController = netController;
    }

    @Override
    public void update(GameController controller, float delta) {
        super.update(controller, delta);

        if (genome == null) {
            Json json = new Json();
            genome = json.fromJson(GenomicAI.class, Gdx.files.internal("ai/xjix3xuv").readString());
            Gdx.app.log(LOG, "Loaded ai: " + genome.id);
        }

        if (controller.getCurrentPlayer().getPlayerId() == getPlayerId()) {
            timer -= delta;
            if (timer < 0) {
                timer = countdown;

                if (VOBGame.DEBUG_AI)
                    Gdx.app.log(LOG, "start...");
                // first, create the visibility matrix
                if (visibilityMatrix == null) {
                    if (VOBGame.DEBUG_AI)
                        Gdx.app.log(LOG, "...create visibility matrix");
                    visibilityMatrix = createVisibilityMatrix(controller.getMap(),
                            controller.getUnitsByPlayerId(getPlayerId()));
                    return;
                }

                // next, create the value matrix and threat matrix
                if (valueMatrix == null) {
                    Array<AbstractUnit> visibleUnits = new Array<AbstractUnit>();
                    for (AbstractUnit unit : controller.getUnits()) {
                        if (visibilityMatrix[(int) (unit.getView().getBoardPosition().y
                                * controller.getMap().getWidth() + unit.getView().getBoardPosition().x)]) {
                            visibleUnits.add(unit);
                        }
                    }
                    if (VOBGame.DEBUG_AI)
                        Gdx.app.log(LOG, "...create value/threat matrices");
                    valueMatrix = createUnitMatrix(controller.getMap(), visibleUnits, false);
                    threatMatrix = createUnitMatrix(controller.getMap(), visibleUnits, true);
                    return;
                }

                // create goalMatrix (based on knowledge of the map)
                if (goalMatrix == null) {
                    if (VOBGame.DEBUG_AI)
                        Gdx.app.log(LOG, "...create goal matrix");
                    goalMatrix = createGoalMatrix(controller.getMap(), controller.getUnits());

                    if (VOBGame.DEBUG_AI) {
                        DecimalFormat formatter = new DecimalFormat("00.0");
                        for (int y = 0; y < controller.getMap().getHeight(); y++) {
                            if (y % 2 == 1) {
                                System.out.print("   ");
                            }
                            for (int x = 0; x < controller.getMap().getWidth(); x++) {
                                int i = y * controller.getMap().getWidth() + x;
                                float sum = (visibilityMatrix[i] ? goalMatrix[i] + valueMatrix[i] + threatMatrix[i]
                                        : 0);
                                System.out.print((sum >= 0 ? " " : "") + formatter.format(sum) + " ");
                            }
                            System.out.println("\n");
                        }
                    }

                    return;
                }

                // move units
                for (AbstractUnit unit : controller.getUnitsByPlayerId(getPlayerId())) {
                    if (unit.getData().getMovesLeft() < 1)
                        continue;
                    Vector2[] adjacent = HexMap.getAdjacent((int) unit.getView().getBoardPosition().x,
                            (int) unit.getView().getBoardPosition().y);
                    Vector2 finalPos = new Vector2(unit.getView().getBoardPosition());
                    float finalSum = goalMatrix[(int) finalPos.y * controller.getMap().getWidth()
                            + (int) finalPos.x]
                            + valueMatrix[(int) finalPos.y * controller.getMap().getWidth() + (int) finalPos.x]
                            + threatMatrix[(int) finalPos.y * controller.getMap().getWidth() + (int) finalPos.x];
                    for (Vector2 v : adjacent) {
                        if (v.x < 0 || v.x >= controller.getMap().getWidth() || v.y < 0
                                || v.y >= controller.getMap().getHeight())
                            continue;
                        float curSum = goalMatrix[(int) v.y * controller.getMap().getWidth() + (int) v.x]
                                + valueMatrix[(int) v.y * controller.getMap().getWidth() + (int) v.x]
                                + threatMatrix[(int) v.y * controller.getMap().getWidth() + (int) v.x];
                        if (curSum > finalSum && controller.isBoardPosEmpty(v)) {
                            finalPos.set(v);
                            finalSum = curSum;
                        }
                    }
                    if (finalPos.x != unit.getView().getBoardPosition().x
                            || finalPos.y != unit.getView().getBoardPosition().y) {
                        // move!
                        Move mv = new Move();
                        mv.owner = getPlayerId();
                        mv.turn = controller.getGameTurn();
                        mv.unit = unit.getId();
                        mv.toLocation = finalPos;
                        netController.sendAICommand(mv);
                        valueMatrix = null;
                        threatMatrix = null;
                        return;
                    }
                }

                // attack
                Array<AbstractUnit> visibleUnits = new Array<AbstractUnit>();
                Array<AbstractUnit> friendlyUnits = controller.getUnitsByPlayerId(this.getPlayerId());
                for (AbstractUnit unit : controller.getUnits()) {
                    if (unit.getOwner() == this || unit.getOwner() == null)
                        continue;
                    if (visibilityMatrix[(int) (unit.getView().getBoardPosition().y * controller.getMap().getWidth()
                            + unit.getView().getBoardPosition().x)] && !unit.getData().isInvisible()
                            || controller.getMap().detectorCanSee(this, friendlyUnits,
                                    unit.getView().getBoardPosition())) {
                        visibleUnits.add(unit);
                    }
                }
                for (AbstractUnit unit : controller.getUnitsByPlayerId(getPlayerId())) {
                    if (unit.getData().getAttacksLeft() < 1)
                        continue;
                    AbstractUnit toAttack = null;
                    float toAttackThreat = 0;
                    for (AbstractUnit o : visibleUnits) {
                        float thisThreat = threatMatrix[controller.getMap().getWidth()
                                * (int) o.getView().getBoardPosition().y + (int) o.getView().getBoardPosition().x];
                        if (controller.getUnitManager().canAttack(unit, o)
                                && (toAttack == null || thisThreat > toAttackThreat)) {
                            toAttack = o;
                            toAttackThreat = thisThreat;
                        }
                    }
                    if (toAttack != null) {
                        //                        Gdx.app.log(LOG + "$attack", String.format("%s --> %s", unit.getProtoId(), toAttack.getProtoId()));
                        Attack at = new Attack();
                        at.owner = getPlayerId();
                        at.turn = controller.getGameTurn();
                        at.unit = unit.getId();
                        at.target = toAttack.getId();
                        netController.sendAICommand(at);
                        valueMatrix = null;
                        threatMatrix = null;
                        return;
                    }
                }

                // build units // TODO build other than marines (ie: implement chromosome 11)

                int positionToBuild = -1;
                for (int i = 0; i < valueMatrix.length; i++) {
                    if (visibilityMatrix[i]) {
                        float sum = threatMatrix[i] + valueMatrix[i] + goalMatrix[i];

                        if ((positionToBuild == -1 || valueMatrix[positionToBuild] + valueMatrix[positionToBuild]
                                + goalMatrix[positionToBuild] < sum)) {
                            //                                Gdx.app.log(LOG, i + ":" + controller.isBoardPosEmpty(
                            //                                        i % controller.getMap().getWidth(),
                            //                                        i / controller.getMap().getWidth()));
                            if (controller.isBoardPosEmpty(i % controller.getMap().getWidth(),
                                    i / controller.getMap().getWidth())) {
                                positionToBuild = i;
                            }
                        }
                    }
                }
                Prototypes.JsonProto unitToBuild = null;

                if (positionToBuild >= 0) {
                    Vector2 buildPosition = new Vector2(positionToBuild % controller.getMap().getWidth(),
                            positionToBuild / controller.getMap().getWidth());
                    Array<String> protoIds = new Array<String>();
                    HashMap<String, Float> buildScores = new HashMap<String, Float>();
                    for (Prototypes.JsonProto proto : Prototypes.getAll(getRace())) {
                        protoIds.add(proto.id);
                        if (proto.cost > 0) {
                            buildScores.put(proto.id, 0f);
                        }
                    }

                    for (AbstractUnit unit : controller.getUnits()) {
                        if (unit.getOwner() == this || !visibilityMatrix[(int) (unit.getView().getBoardPosition().y
                                * controller.getMap().getWidth() + unit.getView().getBoardPosition().x)])
                            continue;

                        int unitIndex = protoIds.indexOf(unit.getProto().id, false);
                        int unitDistance = controller.getMap().getMapDist(unit.getView().getBoardPosition(),
                                buildPosition);
                        for (String key : buildScores.keySet()) {
                            buildScores.put(key, buildScores.get(key)
                                    + ((Array<Float>) genome.chromosomes.get(10).genes.get(key)).get(unitIndex)
                                            / unitDistance);
                        }
                    }

                    String maxScore = null;
                    float maxBuildScore = 0;
                    while (unitToBuild == null && buildScores.keySet().size() > 0) {
                        for (String id : buildScores.keySet()) {
                            if (maxScore == null || buildScores.get(id) > buildScores.get(maxScore)) {
                                maxScore = id;
                                if (buildScores.get(id) > maxBuildScore) {
                                    maxBuildScore = buildScores.get(id);
                                }
                            }
                        }

                        if (!maxScore.equals("saboteur") && buildScores.get(maxScore) > 0
                                && buildScores.get(maxScore) >= maxBuildScore
                                        - (Float) genome.chromosomes.get(10).genes.get("wait")
                                && getBankMoney() >= Prototypes.getProto(getRace(), maxScore).cost) {
                            unitToBuild = Prototypes.getProto(getRace(), maxScore);
                        } else {
                            buildScores.remove(maxScore);
                            maxScore = null;
                        }

                    }
                }

                if (unitToBuild != null) {
                    Build build = new Build();
                    build.owner = getPlayerId();
                    build.turn = controller.getGameTurn();
                    build.building = unitToBuild.id;
                    build.location = new Vector2(positionToBuild % controller.getMap().getWidth(),
                            positionToBuild / controller.getMap().getWidth());
                    netController.sendAICommand(build);
                    valueMatrix = null;
                    return;
                }

                EndTurn et = new EndTurn();
                et.owner = getPlayerId();
                et.turn = controller.getGameTurn();
                netController.sendAICommand(et);
            }
        } else {
            visibilityMatrix = null;
            valueMatrix = null;
            threatMatrix = null;
            goalMatrix = null;
        }

    }

    public float[] createGoalMatrix(HexMap map, Array<AbstractUnit> units) {
        int mapWidth = map.getWidth(), mapHeight = map.getHeight();
        float[] goalMatrix = new float[mapWidth * mapHeight];

        for (int y = 0; y < mapHeight; y++) {
            for (int x = 0; x < mapWidth; x++) {
                goalMatrix[y * mapWidth + x] = (map.getMapData().isBoardPositionTraversible(x, y)
                        ? calcGoalMatrix(x, y, map, units)
                        : -1);
            }
        }

        return goalMatrix;
    }

    public float calcGoalMatrix(int x, int y, HexMap map, Array<AbstractUnit> units) {
        float total = 0;
        for (AbstractUnit unit : units) {
            if (unit.getData().getType().equals("building")) {
                int dist = map.getMapDist(unit.getView().getBoardPosition(), new Vector2(x, y));
                Float value = 0f;
                Array<Float> coeff_array = new Array<Float>();
                if (unit.getProto().id.equals("tower")) {
                    value = (Float) genome.chromosomes.get(9).genes.get("tower_val");
                    if (!visibilityMatrix[(int) (unit.getView().getBoardPosition().y * map.getWidth()
                            + unit.getView().getBoardPosition().x)] || unit.getOwner() == null) {
                        // neutral tower
                        coeff_array = (Array<Float>) genome.chromosomes.get(9).genes.get("tower_n_coeff");
                    } else if (unit.getOwner() == this) {
                        // friendly tower
                        coeff_array = (Array<Float>) genome.chromosomes.get(9).genes.get("tower_f_coeff");
                    } else {
                        // enemy tower
                        coeff_array = (Array<Float>) genome.chromosomes.get(9).genes.get("tower_e_coeff");
                    }
                } else {

                    if (unit.getOwner() == this) {
                        // friendly castle
                        value = threatMatrix[(int) (unit.getView().getBoardPosition().y * map.getWidth()
                                + unit.getView().getBoardPosition().x)];
                        coeff_array = (Array<Float>) genome.chromosomes.get(9).genes.get("castle_f_coeff");
                    } else {
                        // enemy castle
                        value = (Float) genome.chromosomes.get(9).genes.get("castle_e_value");
                        coeff_array = (Array<Float>) genome.chromosomes.get(9).genes.get("castle_e_coeff");
                    }
                }
                total += calc_value(value, dist, coeff_array);
            }
        }
        return total;
    }

    public float calc_value(float value, int distance, Array<Float> coeff_array) {
        float total = 0;
        if (distance == 0) {
            if (coeff_array.size > 0) {
                return value * coeff_array.get(0);
            }
            return 0;
        }

        for (int i = 0; i < coeff_array.size; i++) {
            total += value / Math.pow(distance, i) * coeff_array.get(i);
        }

        return total;
    }

    public boolean[] createVisibilityMatrix(HexMap map, Array<AbstractUnit> units) {
        boolean[] matrix = new boolean[map.getWidth() * map.getHeight()];
        for (AbstractUnit unit : units) {
            if (unit.getData().isBuilding())
                continue;
            int range = unit.getData().getSightRange();
            for (int x = (int) Math.max(unit.getView().getBoardPosition().x - range - 1, 0); x < Math
                    .min(unit.getView().getBoardPosition().x + range + 1, map.getWidth()); x++) {
                for (int y = (int) Math.max(unit.getView().getBoardPosition().y - range - 1, 0); y < Math
                        .min(unit.getView().getBoardPosition().y + range + 1, map.getHeight()); y++) {
                    if (map.getMapDist(new Vector2(x, y), unit.getView().getBoardPosition()) <= range)
                        matrix[y * map.getWidth() + x] = true;
                }
            }
        }
        return matrix;
    }

    public float[] createUnitMatrix(HexMap map, Array<AbstractUnit> units, boolean threat) {
        float[] unitMatrix = new float[map.getWidth() * map.getHeight()];
        for (int y = 0; y < map.getHeight(); y++) {
            for (int x = 0; x < map.getWidth(); x++) {
                unitMatrix[y * map.getWidth() + x] = calcUnitMatrix(x, y, map, units, threat);
            }
        }
        return unitMatrix;
    }

    public float calcUnitMatrix(int x, int y, HexMap map, Array<AbstractUnit> units, boolean threat) {
        float total = 0;
        for (AbstractUnit unit : units) {
            if (unit.getOwner() == this) {
                int i = Prototypes.getAll(getRace()).indexOf(Prototypes.getProto(getRace(), unit.getProto().id),
                        true);
                total += calc_value((Float) genome.chromosomes.get(i).genes.get((threat ? "threat" : "value")), // TODO this is a horrible way to look this up
                        map.getMapDist(unit.getView().getBoardPosition(), new Vector2(x, y)),
                        (Array<Float>) genome.chromosomes.get(i).genes
                                .get((threat ? "threat_coeff" : "value_coeff")));
            }
        }
        return total;
    }

    public GenomicAI getGenome() {
        return genome;
    }

    public void setGenome(GenomicAI genome) {
        this.genome = genome;
    }

    @Override
    public boolean isLoaded() {
        return true;
    }

    @Override
    public boolean isReady() {
        return true;
    }

    public static class GenomicAI {
        public String id;
        public int wins, losses;
        public Array<Chromosome> chromosomes = new Array<Chromosome>();

        public void mutate(float rate) {
            for (Chromosome chromo : chromosomes) {
                for (String gene : chromo.genes.keySet()) {
                    Object val = chromo.genes.get(gene);
                    if (val instanceof Float) {
                        chromo.genes.put(gene, mutatedValue((Float) val, rate, 10f));
                    } else if (val instanceof Array) {
                        Array<Float> valArray = (Array<Float>) val;
                        for (int i = 0; i < valArray.size; i++) {
                            valArray.set(i, mutatedValue(valArray.get(i), rate, 10f));
                        }
                    }
                }
            }
        }

        float mutatedValue(float currentValue, float mutationRate, float mutationCoeff) {
            if (Math.random() < mutationRate) {
                return currentValue + (float) ((Math.random() < 0.5 ? 1 : -1) * Math.pow((Math.random() * 2 - 1), 2)
                        * mutationCoeff);
            }
            return currentValue;
        }

        public static GenomicAI generateRandom() {
            GenomicAI ai = new GenomicAI();

            ai.id = Utils.getRandomId(8);
            ai.wins = 0;
            ai.losses = 0;

            float range = 10f;

            for (Prototypes.JsonProto proto : Prototypes.getAll("terran")) {
                Chromosome chromo = new Chromosome();

                chromo.genes.put("threat", (float) Math.random() * range - (range / 2));
                Array<Float> coeff_array = new Array<Float>(5);
                for (int i = 1; i <= 5; i++)
                    coeff_array.add((float) (Math.random() * range - (range / 2)) / i);
                chromo.genes.put("threat_coeff", coeff_array);
                coeff_array = new Array<Float>(5);
                ;

                chromo.genes.put("value", (float) (Math.random() * range - (range / 2)));
                for (int i = 1; i <= 5; i++)
                    coeff_array.add((float) (Math.random() * range - (range / 2)));
                chromo.genes.put("value_coeff", coeff_array);

                ai.chromosomes.add(chromo);
            }

            Chromosome goal_chromo = new Chromosome();
            Array<Float> coeff_array = new Array<Float>(5);

            goal_chromo.genes.put("tower_val", (float) (Math.pow(Math.random() * range, 2)));

            for (int i = 1; i <= 5; i++)
                coeff_array.add((float) (Math.random() * range - (range / 2)) / i);
            goal_chromo.genes.put("tower_e_coeff", coeff_array);
            coeff_array = new Array<Float>(5);
            ;

            for (int i = 1; i <= 5; i++)
                coeff_array.add((float) (Math.random() * range - (range / 2)) / i);
            goal_chromo.genes.put("tower_n_coeff", coeff_array);
            coeff_array = new Array<Float>(5);
            ;

            for (int i = 1; i <= 5; i++)
                coeff_array.add((float) (Math.random() * range - (range / 2)) / i);
            goal_chromo.genes.put("tower_f_coeff", coeff_array);
            coeff_array = new Array<Float>(5);
            ;

            goal_chromo.genes.put("castle_e_value", (float) (Math.pow(Math.random() * range, 2)));

            for (int i = 1; i <= 5; i++)
                coeff_array.add((float) (Math.random() * range - (range / 2)) / i);
            goal_chromo.genes.put("castle_e_coeff", coeff_array);
            coeff_array = new Array<Float>(5);
            ;

            for (int i = 1; i <= 5; i++)
                coeff_array.add((float) (Math.random() * range - (range / 2)) / i);
            goal_chromo.genes.put("castle_f_coeff", coeff_array);
            coeff_array = new Array<Float>(5);
            ;

            ai.chromosomes.add(goal_chromo);

            Chromosome build_chromo = new Chromosome();

            Array<Prototypes.JsonProto> protoArray = Prototypes.getAll("terran");

            for (Prototypes.JsonProto proto : protoArray) {
                if (proto.cost > 0) {
                    // buildable
                    for (int i = 0; i < protoArray.size; i++)
                        coeff_array.add((float) Math.random() * range - (range / 2));
                    build_chromo.genes.put(proto.id, coeff_array);
                    coeff_array = new Array<Float>(5);
                    ;
                }
            }

            build_chromo.genes.put("wait", (float) Math.random() * 10f);

            ai.chromosomes.add(build_chromo);

            return ai;
        }

        public static GenomicAI clone(GenomicAI genome) {
            Json json = new Json();

            GenomicAI child = json.fromJson(GenomicAI.class, json.toJson(genome));
            child.id = Utils.getRandomId(8);
            child.wins = 0;
            child.losses = 0;

            return child;
        }

        public static Array<GenomicAI> breed(int offspring, Array<GenomicAI> parents, float mutationRate) {
            Array<GenomicAI> children = new Array<GenomicAI>();
            for (int i = 0; i < offspring; i++) {
                GenomicAI child = new GenomicAI();
                child.id = Utils.getRandomId(8);
                children.add(child);

                for (int c = 0; c < parents.get(0).chromosomes.size; c++) {
                    Chromosome chromo = new Chromosome();
                    child.chromosomes.add(chromo);
                    for (String gene : parents.get(0).chromosomes.get(c).genes.keySet()) {
                        int p = (int) Math.floor(Math.random() * parents.size);
                        chromo.genes.put(gene, parents.get(p).chromosomes.get(c).genes.get(gene));
                    }
                }
            }

            Json json = new Json();
            for (int i = 0; i < children.size; i++) {
                children.set(i, json.fromJson(GenomicAI.class, json.toJson(children.get(i))));
                children.get(i).mutate(mutationRate);
            }

            return children;
        }
    }

    public static class Chromosome {
        HashMap<String, Object> genes = new HashMap<String, Object>();
    }
}