Commit bf4f385b authored by Pablo R. Mier's avatar Pablo R. Mier

Basic game with evolution implemented

parent a6dd0b9c
......@@ -5,7 +5,7 @@ var DIR_CHANGE_MIN_TIME = 10;
var SHOOT_DELAY = 100;
var MIN_INVADERS = 4;
var INITIAL_INVADERS = 6;
var MIN_GENERATION_TIME = Phaser.Time.SECOND * 2;
var MIN_GENERATION_TIME = Phaser.Timer.SECOND * 2;
invadersApp.Game = function (game) {
......@@ -40,7 +40,6 @@ invadersApp.Game = function (game) {
this.lastShootAt = 0;
this.readyToFire = true;
this.currentGenerationTime = Phaser.Time.SECOND * 5;
};
invadersApp.Game.prototype = {
......@@ -49,6 +48,10 @@ invadersApp.Game.prototype = {
var that = this;
this.currentGenerationTime = Phaser.Timer.SECOND * 5;
this.currentGeneration = 0;
this.lastGenerationTime = this.game.time.now;
// Load game config
this.settings = this.game.cache.getJSON('settings');
......@@ -61,28 +64,12 @@ invadersApp.Game.prototype = {
this.objects.invaders.physicsBodyType = Phaser.Physics.ARCADE;
// Initialize
this.objects.invaders = [];
for (var i = 0; i < INITIAL_INVADERS; i++) this.objects.invaders.push(new invadersApp.Invader(this));
for (var i = 0; i < INITIAL_INVADERS; i++) this.objects.invaders.add(new invadersApp.Invader(this));
this.player = new invadersApp.Player(this);
this.game.add.existing(this.player);
// create a new bitmap data object
var wallBmp = this.game.add.bitmapData(this.game.width, 3);
// draw the wall
wallBmp.ctx.beginPath();
wallBmp.ctx.rect(0,0,this.game.width,3);
wallBmp.ctx.fillStyle = '#ffffff';
wallBmp.ctx.fill();
// use the bitmap data as the texture for the sprite
this.wall = this.add.sprite(0, this.game.height - WALL_MARGIN, wallBmp);
this.game.physics.enable(this.wall, Phaser.Physics.ARCADE);
this.wall.body.immovable = true;
this.createWall();
this.cursors = this.game.input.keyboard.createCursorKeys();
this.fireButton = this.game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR);
......@@ -98,33 +85,40 @@ invadersApp.Game.prototype = {
this.game.paused = true;
//this.pausePhysics();
// New generation event, check for every second
this.game.time.events.loop(Phaser.Timer.SECOND, this.createGeneration, this);
// Gargabe collect killed invaders. If the invaders are
// destroyed on a bullet hit, it may produce an exception
// from physics module if there is a concurrent collision
// detection going on
this.game.time.events.loop(Phaser.Timer.SECOND * 5, function () {
this.objects.invaders.forEachDead(function (invader) {
invader.destroy();
}, this);
}, this);
},
update: function () {
// If physics are paused, skip all
if (this.game.physics.arcade.isPaused) return;
var that = this;
this.game.physics.arcade.overlap(this.player.bullets, this.objects.invaders, function (invader, bullet) {
// If physics are paused, skip all
// if (this.game.physics.arcade.isPaused) return;
this.game.physics.arcade.collide(this.wall, this.objects.invaders);
this.game.physics.arcade.overlap(this.player.bullets, this.objects.invaders, function (bullet, invader) {
bullet.kill();
var alive = that.objects.invaders.filter(function (invader) {
return invader.alive;
}).length;
if (alive > MIN_INVADERS){
invader.kill();
} else {
that.objects.invaders.forEach(function (invader) {
invader.showField(true);
});
var living = that.objects.invaders.countLiving();
if (living > MIN_INVADERS) invader.kill();
if (living == MIN_INVADERS + 1) {
that.objects.invaders.forEachAlive(function (invader) {
invader.showField();
}, that);
}
}, null, this);
this.game.physics.arcade.collide(this.wall, this.objects.invaders);
},
quitGame: function (pointer) {
......@@ -139,6 +133,67 @@ invadersApp.Game.prototype = {
pausePhysics: function (pause) {
if (pause == undefined) pause = true;
this.game.physics.arcade.isPaused = pause;
},
createGeneration: function () {
var that = this;
if (this.game.time.now > this.lastGenerationTime + this.currentGenerationTime) {
this.lastGenerationTime = this.game.time.now;
// The number of invaders
var alive = this.objects.invaders.countLiving();
var aliveInvaders = this.objects.invaders.filter(function(child, index, children) {
return child.alive;
}, true);
aliveInvaders.callAll('increaseFitness');
// The number of new individuals is determined by a box-cox
// transformation with lambda=0.6.
var numPairs = Math.floor((Math.pow(alive, 0.6) - 1) / 0.6);
var pool = invadersApp.evolution.pool(aliveInvaders.list, numPairs);
var offspring = invadersApp.evolution.evolve(pool, this.settings.genes);
// Start an animation
// Draw a line https://codepen.io/codevinsky/pen/dAjDp
offspring.forEach(function (p) {
var x = (p[0].x + p[1].x)/2;
var y = (p[0].y + p[1].y)/2;
that.objects.invaders.add(new invadersApp.Invader(that, p[2], x, y));
});
this.currentGeneration++;
// Disable shields
aliveInvaders.callAll('showField', false);
// Decrease next generation time
if (this.currentGenerationTime > MIN_GENERATION_TIME) {
this.currentGenerationTime -= 150;
console.log(this.currentGenerationTime);
}
}
},
createWall: function () {
// create a new bitmap data object
var wallBmp = this.game.add.bitmapData(this.game.width, 3);
// draw the wall
wallBmp.ctx.beginPath();
wallBmp.ctx.rect(0,0,this.game.width,3);
wallBmp.ctx.fillStyle = '#ffffff';
wallBmp.ctx.fill();
// use the bitmap data as the texture for the sprite
this.wall = this.add.sprite(0, this.game.height - WALL_MARGIN, wallBmp);
this.game.physics.enable(this.wall, Phaser.Physics.ARCADE);
this.wall.body.immovable = true;
}
};
......@@ -10,6 +10,7 @@
<script src="js/Utils.js"></script>
<script src="js/Player.js"></script>
<script src="js/Invader.js"></script>
<script src="js/Evolution.js"></script>
<script src="MainMenu.js"></script>
<script src="Game.js"></script>
</head>
......
......@@ -5,10 +5,57 @@
var invadersApp = invadersApp || {};
invadersApp.evolution = {
evolve: function(individuals){
evolve: function(pool, genes, mutation){
if (mutation === undefined) { mutation = 0.1 };
var offspring = [];
pool.forEach(function (pair) {
var p1 = pair[0];
var p2 = pair[1];
var children = [];
var recombinate = function(){
for(var gen in genes){
if (genes.hasOwnProperty(gen)) {
var gen1 = p1.genes[gen];
var gen2 = p2.genes[gen];
var min = (gen1 < gen2) ? gen1 : gen2;
var max = (gen1 > gen2) ? gen1 : gen2;
children[gen] = Math.random() * (max - min) + min;
if (Math.random() < mutation) {
// Mutate this gene
children[gen] = Math.random() * (genes[gen].max - genes[gen].min) + genes[gen].min;
}
}
}
offspring.push([p1, p2, children]);
});
return offspring;
},
};
pool: function(population, size){
var pool = [];
while (pool.length < size){
var p1 = this.binary_tournament(population);
// Copy and remove the selected invader
var pop2 = population.slice(0);
pop2.splice(pop2.indexOf(p1),1);
var p2 = this.binary_tournament(pop2);
pool.push([p1, p2]);
}
return pool;
},
binary_tournament: function(population){
var invader1 = this.pick(population);
var invader2 = this.pick(population);
if (invader1.fitness > invader2.fitness) {
return invader1;
} else {
return invader2;
}
},
pick: function(array){
return array[Math.floor(Math.random()*array.length)];
}
};
\ No newline at end of file
......@@ -34,6 +34,8 @@ invadersApp.Invader = function (ctx, genes, x, y) {
// Used to control the probability of x-y change in direction
this.lastTimeChanged = 0;
this.fitness = 0;
// Create a shield
var shield = this.game.make.graphics(0,0);
shield.lineStyle(1, 0x15AFF0, 1);
......@@ -76,3 +78,6 @@ invadersApp.Invader.prototype.freeze = function (freeze) {
this.body.velocity.y = this.genes['yvelocity'];
}
};
invadersApp.Invader.prototype.increaseFitness = function () {
this.fitness++;
};
......@@ -42,13 +42,13 @@ invadersApp.Player.prototype.update = function () {
if (this.scale.x > 0){
this.scale.x *= -1;
}
this.body.velocity.x = -250;
this.body.velocity.x = -500;
}
else if (this.ctx.cursors.right.isDown) {
if (this.scale.x < 0){
this.scale.x *= -1;
}
this.body.velocity.x = 250;
this.body.velocity.x = 500;
}
......@@ -68,7 +68,7 @@ invadersApp.Player.prototype.update = function () {
xpos = this.x + 21;
}
bullet.reset(xpos, this.y - 20);
bullet.body.velocity.y = -1200;
bullet.body.velocity.y = -2000;
}
}
}
......
......@@ -11,11 +11,11 @@
},
"xvelocity": {
"min": 0,
"max": 150
"max": 300
},
"yvelocity": {
"min": 0,
"max": 150
"max": 300
},
"x_prob_change_dir": {
"min": 0.00,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment