antiyoy.js/NeuralNetwork.js

202 lines
6.3 KiB
JavaScript
Raw Permalink Normal View History

2019-04-08 19:54:07 +00:00
class GeneticAlgorithm {
constructor(maxUnits, topUnits, game) {
this.maxUnits = maxUnits;
this.topUnits = topUnits;
if(this.maxUnits < this.topUnits)
this.topUnits = maxUnits;
this.population = [];
this.SCALE_FACTOR = 200;
this.game = game;
}
reset() {
this.iteration = 1;
this.mutateRate = 1;
this.bestPopulation = 0;
this.bestFitness = 0;
this.bestScore = 0;
}
createPopulation() {
this.population.slice(0, this.population.length);
for(let i = 0; i < this.maxUnits; i++) {
const newUnit = new synaptic.Architect.Perceptron(6 * this.game.fieldSize.x * this.game.fieldSize.y, 35, 5);
newUnit.index = i;
newUnit.fitness = 0;
newUnit.score = 0;
newUnit.isWinner = false;
this.population.push(newUnit);
}
}
activateBrain(civilization) {
// TODO: Inputs
const inputs = [civilization.balance, civilization.getBalanceChange()];
for(let x = 0; x < this.game.fieldSize.x; x++) {
for(let y = 0; y < this.game.fieldSize.y; y++) {
const tile = this.game.field[x][y];
const isFriendly = tile.claimedBy === civilization ? 1 : 0;
const hasObject = !!tile.object ? 1 : 0;
const canAttack = tile.object instanceof Warrior ? 1 : 0;
const rank = !!tile.object ? tile.object.rank / 4 : 0;
inputs.push(x, y, isFriendly, hasObject, canAttack, rank);
}
}
console.log(civilization.index);
const outputs = this.population[civilization.index].activate(inputs);
console.log(outputs);
const type = outputs[0];
const x = Math.round(outputs[1] * (this.game.fieldSize.x - 1));
const y = Math.round(outputs[2] * (this.game.fieldSize.y - 1));
const tile = this.game.field[x][y];
if(type < .5) {
if(tile.object && tile.claimedBy === civilization && tile.object instanceof Warrior) {
const deltaX = Math.round(outputs[3] * 4 - 2) / 4;
const deltaY = Math.round(outputs[4] * 4 - 2) / 4;
const targetX = x + deltaX;
const targetY = y + deltaY;
tile.object.move(targetX, targetY);
console.log(deltaX, deltaY);
}
} else {
const itemType = outputs[3];
console.log('hey');
if(itemType > .5) {
console.log('ho');
if(!tile.getAdjacentTiles().find(tile => tile.claimedBy === civilization))
return;
const troopType = Math.round(outputs[4] * 3);
const troopTypes = [Farmer, Hoplite, Servant, Knight];
this.game.addWarrior(x, y, civilization, troopTypes[troopType]);
} else {
if(tile.claimedBy !== civilization)
return;
const buildingType = Math.round(outputs[4] * 2);
const buildingTypes = [Farm, SmallTower, BigTower];
console.log('ho2', buildingType, tile);
this.game.addBuilding(x, y, civilization, buildingTypes[buildingType]);
}
}
}
evolvePopulation() {
const winners = this.selection();
if(this.mutateRate === 1 && winners[0].fitness < 0) {
this.createPopulation();
} else {
this.mutateRate = .2;
}
for(let i = this.topUnits; i < this.maxUnits; i++) {
let parentA, parentB, offspring;
if(i === this.topUnits) {
parentA = winners[0].toJSON();
parentB = winners[1].toJSON();
offspring = this.crossOver(parentA, parentB);
} else if(i < this.maxUnits - 2) {
parentA = this.getRandomUnit(winners).toJSON();
parentB = this.getRandomUnit(winners).toJSON();
offspring = this.crossOver(parentA, parentB);
} else {
offspring = this.getRandomUnit(winners).toJSON();
}
offspring = this.mutation(offspring);
const newUnit = synaptic.Network.fromJSON(offspring);
newUnit.index = this.population[i].index;
newUnit.fitness = 0;
newUnit.score = 0;
newUnit.isWinner = false;
this.population[i] = newUnit;
}
if(winners[0].fitness > this.bestFitness) {
this.bestPopulation = this.iteration;
this.bestFitness = winners[0].fitness;
this.bestScore = winners[0].score;
}
this.population.sort((unitA, unitB) => unitA.index - unitB.index);
}
selection() {
const sortedPopulation = this.population.sort((unitA, unitB) => unitB.fitness - unitA.fitness);
for(let i = 0; i < this.topUnits; i++) {
this.population[i].isWinner = true;
}
return sortedPopulation.slice(0, this.topUnits);
}
crossOver(parentA, parentB) {
const cutPoint = this.random(0, parentA.neurons.length - 19);
for(let i = cutPoint; i < parentA.neurons.length; i++) {
const biasFromParentA = parentA.neurons[i]['bias'];
parentA.neurons[i]['bias'] = parentB.neurons[i]['bias'];
parentB.neurons[i]['bias'] = biasFromParentA;
}
return this.random(0, 1) === 1 ? parentA : parentB;
}
mutation(offspring) {
for(let i = 0; i < offspring.neurons.length; i++) {
offspring.neurons[i]['bias'] = this.mutate(offspring.neurons[i]['bias']);
}
for(let i = 0; i < offspring.connections.length; i++) {
offspring.connections[i]['weight'] = this.mutate(offspring.connections[i]['weight']);
}
return offspring;
}
mutate(gene) {
if(Math.random() < this.mutateRate) {
gene *= 1 + ((Math.random() - .5) * 3 + (Math.random() - .5));
}
return gene;
}
random(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
getRandomUnit(array) {
return array[this.random(0, array.length - 1)];
}
normalize(value, max) {
if(value < -max)
value = -max;
else if(value > max)
value = max;
return value / max;
}
}