summaryrefslogtreecommitdiff
path: root/2015-spacebot/src
diff options
context:
space:
mode:
Diffstat (limited to '2015-spacebot/src')
-rw-r--r--2015-spacebot/src/alien.cpp7
-rw-r--r--2015-spacebot/src/brain/neural_link.cpp11
-rw-r--r--2015-spacebot/src/brain/neural_network.cpp163
-rw-r--r--2015-spacebot/src/brain/neuron.cpp40
-rw-r--r--2015-spacebot/src/building.cpp6
-rw-r--r--2015-spacebot/src/enemy_bullet.cpp6
-rw-r--r--2015-spacebot/src/game_entity.cpp13
-rw-r--r--2015-spacebot/src/game_state.cpp234
-rw-r--r--2015-spacebot/src/move_string_mapper.cpp21
-rw-r--r--2015-spacebot/src/player_missile.cpp6
-rw-r--r--2015-spacebot/src/shield.cpp6
-rw-r--r--2015-spacebot/src/spacebot.cpp48
-rw-r--r--2015-spacebot/src/spaceship.cpp6
13 files changed, 567 insertions, 0 deletions
diff --git a/2015-spacebot/src/alien.cpp b/2015-spacebot/src/alien.cpp
new file mode 100644
index 0000000..3fa9b28
--- /dev/null
+++ b/2015-spacebot/src/alien.cpp
@@ -0,0 +1,7 @@
+#include "alien.h"
+
+Alien::Alien(int x, int y)
+ :GameEntity(x, y)
+{
+}
+
diff --git a/2015-spacebot/src/brain/neural_link.cpp b/2015-spacebot/src/brain/neural_link.cpp
new file mode 100644
index 0000000..6997bd6
--- /dev/null
+++ b/2015-spacebot/src/brain/neural_link.cpp
@@ -0,0 +1,11 @@
+#include "brain/neural_link.h"
+
+NeuralLink::NeuralLink(NeuralNode* input, double weight)
+ :_input(input), _weight(weight)
+{
+}
+
+double NeuralLink::weightedActivation() const
+{
+ return _input->activation()*_weight;
+}
diff --git a/2015-spacebot/src/brain/neural_network.cpp b/2015-spacebot/src/brain/neural_network.cpp
new file mode 100644
index 0000000..c8177e8
--- /dev/null
+++ b/2015-spacebot/src/brain/neural_network.cpp
@@ -0,0 +1,163 @@
+#include "brain/neural_network.h"
+#include "brain/neuron.h"
+#include <limits>
+
+NeuralNetwork::NeuralNetwork(std::istream &&networkConfigFile, unsigned int numberOfSensors, unsigned int numberOfOutputs)
+{
+ _neurons.reserve(400);
+ _sensors.reserve(numberOfSensors);
+ _outputs.reserve(numberOfOutputs);
+
+ _biasNode = std::make_shared<BiasNode>();
+
+ for (unsigned int i=0; i<numberOfSensors; ++i)
+ {
+ auto sensor = std::make_shared<Sensor>(i);
+ _sensors.push_back(sensor);
+ }
+ for (unsigned int i=0; i<numberOfOutputs; ++i)
+ {
+ auto output = findOrAddNeuron(i);
+ _outputs.push_back(output);
+ }
+
+ parseFile(std::move(networkConfigFile));
+}
+
+NeuralNetwork::NeuralNetwork(std::istream &&networkConfigFile, std::vector<bool> sensorInitialValues, unsigned int numberOfOutputs)
+{
+ _neurons.reserve(400);
+ _sensors.reserve(sensorInitialValues.size());
+ _outputs.reserve(numberOfOutputs);
+
+ _biasNode = std::make_shared<BiasNode>();
+
+ for (unsigned int i=0; i<sensorInitialValues.size(); ++i)
+ {
+ auto sensor = std::make_shared<Sensor>(i);
+ sensor->setActivation(sensorInitialValues[i] ? 1 : 0);
+ _sensors.push_back(sensor);
+ }
+ for (unsigned int i=0; i<numberOfOutputs; ++i)
+ {
+ auto output = findOrAddNeuron(i);
+ _outputs.push_back(output);
+ }
+
+ parseFile(std::move(networkConfigFile));
+}
+
+void NeuralNetwork::parseFile(std::istream &&file)
+{
+ double weight;
+ char srcType;
+ unsigned int srcId;
+ unsigned int destId;
+
+ while (file.get(srcType) &&
+ file >> srcId &&
+ file.ignore(std::numeric_limits<std::streamsize>::max(), 'n') &&
+ file >> destId &&
+ file >> weight &&
+ file.ignore(std::numeric_limits<std::streamsize>::max(), '\n'))
+ {
+ std::shared_ptr<NeuralNode> source;
+ std::shared_ptr<Neuron> destination;
+ switch (srcType)
+ {
+ case 's':
+ source = findOrAddSensor(srcId);
+ break;
+ case 'b':
+ source = _biasNode;
+ break;
+ default:
+ source = findOrAddNeuron(srcId);
+ }
+ destination = findOrAddNeuron(destId);
+
+ addLink(source, destination, weight);
+ }
+
+}
+
+void NeuralNetwork::addLink(std::shared_ptr<NeuralNode> source, std::shared_ptr<Neuron> destination, double weight)
+{
+ NeuralLink link(source.get(), weight);
+ destination->addInput(std::move(link));
+}
+
+std::shared_ptr<Sensor> NeuralNetwork::findOrAddSensor(unsigned int id)
+{
+ while (_sensors.size() <= id)
+ {
+ auto sensor = std::make_shared<Sensor>(_sensors.size());
+ _sensors.push_back(sensor);
+ }
+
+ return _sensors.at(id);
+}
+
+std::shared_ptr<Neuron> NeuralNetwork::findOrAddNeuron(unsigned int id)
+{
+ while (_neurons.size() <= id)
+ {
+ auto neuron = std::make_shared<Neuron>(_neurons.size());
+ _neurons.push_back(neuron);
+ }
+
+ return _neurons.at(id);
+}
+
+void NeuralNetwork::setInput(unsigned int inputIndex, double activation)
+{
+ _sensors.at(inputIndex)->setActivation(activation);
+}
+
+unsigned int NeuralNetwork::findMaxOutputIndex() const
+{
+ bool anyNodeChanged = true;
+ auto maxIterations = _neurons.size()*10;
+ for (unsigned int iteration=0; anyNodeChanged && iteration<maxIterations; ++iteration)
+ {
+ anyNodeChanged = false;
+ for (auto const& neuron : _neurons)
+ {
+ bool activationChanged = neuron->calculateActivation();
+ anyNodeChanged = anyNodeChanged || activationChanged;
+ }
+ }
+
+ int currentMaxIndex = 0;
+ double currentMaxActivation = _outputs.at(0)->activation();
+
+ for (unsigned int i=1; i<_outputs.size(); ++i)
+ {
+ double activation = _outputs.at(i)->activation();
+ if (activation >= currentMaxActivation)
+ {
+ currentMaxActivation = activation;
+ currentMaxIndex = i;
+ }
+ }
+ return currentMaxIndex;
+}
+
+bool NeuralNetwork::linkExists(std::string srcIdentifier, std::string destIdentifier, double weight) const
+{
+ std::shared_ptr<Neuron> dest;
+
+ for (auto const& node : _neurons)
+ {
+ if (node->identifier() == destIdentifier)
+ {
+ dest = node;
+ }
+ }
+
+ if (!dest)
+ {
+ return false;
+ }
+ return dest->hasInputWithWeight(srcIdentifier, weight);
+}
diff --git a/2015-spacebot/src/brain/neuron.cpp b/2015-spacebot/src/brain/neuron.cpp
new file mode 100644
index 0000000..c7dba2c
--- /dev/null
+++ b/2015-spacebot/src/brain/neuron.cpp
@@ -0,0 +1,40 @@
+#include "brain/neuron.h"
+#include <cmath>
+
+double Neuron::sigmoid(double input) const
+{
+ const double slope = 4.924273;
+ return (1/(1+(std::exp(-(slope*input)))));
+}
+
+bool Neuron::calculateActivation()
+{
+ double newActivation = 0;
+ for (auto const& link : _inputLinks)
+ {
+ newActivation += link.weightedActivation();
+ }
+ newActivation = sigmoid(newActivation);
+
+ const double errorMargin = 0.000001;
+ bool activationChanged = std::abs(newActivation - _activation) > errorMargin;
+ _activation = newActivation;
+ return activationChanged;
+}
+
+void Neuron::addInput(NeuralLink&& link)
+{
+ _inputLinks.push_back(std::move(link));
+}
+
+bool Neuron::hasInputWithWeight(std::string srcIdentifier, double weight) const
+{
+ for (auto const& link : _inputLinks)
+ {
+ if (link.inputIdentifier() == srcIdentifier && link.weight() == weight)
+ {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/2015-spacebot/src/building.cpp b/2015-spacebot/src/building.cpp
new file mode 100644
index 0000000..8da322d
--- /dev/null
+++ b/2015-spacebot/src/building.cpp
@@ -0,0 +1,6 @@
+#include "building.h"
+
+Building::Building(int x, int y)
+ :GameEntity(x, y)
+{
+}
diff --git a/2015-spacebot/src/enemy_bullet.cpp b/2015-spacebot/src/enemy_bullet.cpp
new file mode 100644
index 0000000..6ae5066
--- /dev/null
+++ b/2015-spacebot/src/enemy_bullet.cpp
@@ -0,0 +1,6 @@
+#include "enemy_bullet.h"
+
+EnemyBullet::EnemyBullet(int x, int y)
+ :GameEntity(x, y)
+{
+}
diff --git a/2015-spacebot/src/game_entity.cpp b/2015-spacebot/src/game_entity.cpp
new file mode 100644
index 0000000..8acf8cd
--- /dev/null
+++ b/2015-spacebot/src/game_entity.cpp
@@ -0,0 +1,13 @@
+#include "game_entity.h"
+#include <sstream>
+
+GameEntity::GameEntity(int x, int y)
+ :_x(x), _y(y)
+{}
+
+std::string GameEntity::coords() const
+{
+ std::stringstream ss;
+ ss << "(" << _x << ", " << _y << ")";
+ return ss.str();
+}
diff --git a/2015-spacebot/src/game_state.cpp b/2015-spacebot/src/game_state.cpp
new file mode 100644
index 0000000..952c2f3
--- /dev/null
+++ b/2015-spacebot/src/game_state.cpp
@@ -0,0 +1,234 @@
+#include "game_state.h"
+#include <iostream>
+#include <fstream>
+#include <limits>
+#include <bitset>
+#include <cmath>
+
+const int OPENING_LINES = 6;
+const int GAME_AREA_LINES = 25;
+const int GAME_WIDTH = 19;
+
+void moveToNextChar(int &x, int &y, int &width, char &nextChar, std::istream &mapFile)
+{
+ if (nextChar == '\n')
+ {
+ x = 0;
+ ++y;
+ }
+ else
+ {
+ x += width;
+ }
+ if (width > 1)
+ {
+ mapFile.ignore(width-1);
+ }
+ nextChar = mapFile.get();
+}
+
+GameState::GameState(std::istream &&mapFile)
+{
+ for (int i=0; i<OPENING_LINES; ++i)
+ {
+ mapFile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
+ }
+
+ char nextChar = mapFile.get();
+ for (int x=0, y=0, width=1;
+ y < GAME_AREA_LINES && nextChar != EOF;
+ moveToNextChar(x, y, width, nextChar, mapFile))
+ {
+ width = addEntity(x, y, nextChar);
+ }
+}
+
+int GameState::addEntity(int x, int y, char type)
+{
+ switch (type)
+ {
+ case Alien::MAP_CHAR:
+ _aliens.push_back(Alien(x,y));
+ return 1;
+ case EnemyBullet::ALIEN_MAP_CHAR:
+ case EnemyBullet::ENEMY_MISSILE_MAP_CHAR:
+ _bullets.push_back(EnemyBullet(x,y));
+ return 1;
+ case PlayerMissile::MAP_CHAR:
+ _missiles.push_back(PlayerMissile(x,y));
+ return 1;
+ case Shield::MAP_CHAR:
+ _shields.push_back(Shield(x,y));
+ return 1;
+ case Spaceship::ENEMY_MAP_CHAR:
+ _enemySpaceship = std::unique_ptr<Spaceship>(new Spaceship(x+1, y));
+ return 3;
+ case Spaceship::PLAYER_MAP_CHAR:
+ _playerSpaceship = std::unique_ptr<Spaceship>(new Spaceship(x+1, y));
+ return 3;
+ case Building::MISSILE_CONTROLLER_CHAR:
+ _missileControllers.push_back(Building(x+1, y));
+ return 3;
+ case Building::ALIEN_FACTORY_CHAR:
+ _alienFactories.push_back(Building(x+1, y));
+ return 3;
+ }
+ return 1;
+}
+
+void GameState::logState() const
+{
+ for (auto const& alien : _aliens)
+ {
+ std::cout << "Alien " << alien.coords() << std::endl;
+ }
+ for (auto const& bullet : _bullets)
+ {
+ std::cout << "Enemy Bullet" << bullet.coords() << std::endl;
+ }
+ for (auto const& missile : _missiles)
+ {
+ std::cout << "Player Missile" << missile.coords() << std::endl;
+ }
+ for (auto const& shield : _shields)
+ {
+ std::cout << "Shield" << shield.coords() << std::endl;
+ }
+ if (_playerSpaceship)
+ {
+ std::cout << "Player Spaceship" << _playerSpaceship->coords() << std::endl;
+ }
+ if (_enemySpaceship)
+ {
+ std::cout << "Enemy Spaceship" << _enemySpaceship->coords() << std::endl;
+ }
+}
+
+int getPyramidOffset(int bottomWidth, int maxWidth, int x, int y, int resultIfLeft, int resultIfRight)
+{
+ int currentRowWidth = bottomWidth;
+ int currentRowOffset = 0;
+ for (int i=0; i<y; ++i)
+ {
+ currentRowOffset += currentRowWidth;
+ currentRowWidth += 2;
+ if (currentRowWidth > maxWidth)
+ {
+ currentRowWidth = maxWidth;
+ }
+ }
+
+ if (x > currentRowWidth/2)
+ {
+ return resultIfRight;
+ }
+ else if (x < -currentRowWidth/2)
+ {
+ return resultIfLeft;
+ }
+ else
+ {
+ return currentRowOffset + currentRowWidth/2 + x;
+ }
+}
+
+int getRectangularOffset(int width, int x, int y)
+{
+ int currentRowOffset = width*y;
+ return currentRowOffset + width/2 + x;
+}
+
+std::vector<bool> GameState::toBitArray() const
+{
+ std::vector<bool> result(61);
+
+ int alienOffset = 0; //Aliens are 0 to 35
+ int bulletOffset = 36; //Bullets are 36 to 50
+ int shieldOffset = 51; //Shields are 51 to 53
+ int missileOffset = 54; //Missile info is 54 to 55
+ int positionOffset = 56; //Position is 56 to 58
+ int existingBuildingsOffset = 59; //Existing buildings is 59
+ int canBuildOffset = 60; //Can build here is 60
+
+ int playerX = GAME_WIDTH/2;
+ int playerY = GAME_AREA_LINES-3;
+ if (_playerSpaceship)
+ {
+ playerX = _playerSpaceship->x();
+ }
+
+ for (auto const& alien : _aliens)
+ {
+ if (alien.y() > playerY-4 || alien.y() < playerY-7 || alien.x() > playerX+4 || alien.x() < playerX-4)
+ {
+ continue;
+ }
+
+ result.at(alienOffset + getRectangularOffset(9, alien.x()-playerX, playerY-4-alien.y())) = true;
+ }
+
+ for (auto const& bullet : _bullets)
+ {
+ if (bullet.y() >= playerY || bullet.y() < playerY-3 || bullet.x() > playerX+2 || bullet.x() < playerX-2)
+ {
+ continue;
+ }
+
+ result.at(bulletOffset + getRectangularOffset(5, bullet.x()-playerX, playerY-1-bullet.y())) = true;
+ }
+
+ for (auto const& shield : _shields)
+ {
+ if (shield.y() < playerY-3 || shield.x() < playerX-1 || shield.x() > playerX+1)
+ {
+ continue;
+ }
+
+ result.at(shieldOffset + shield.x()-playerX+1) = true;
+ }
+
+ if (_missiles.size() > 0)
+ {
+ result.at(missileOffset) = true;
+ }
+ if (_missiles.size() < 1)
+ {
+ result.at(missileOffset + 1) = true;
+ }
+
+ if (_playerSpaceship)
+ {
+ if (playerX <= GAME_WIDTH/3)
+ {
+ result.at(positionOffset) = true;
+ }
+ else if (playerX >= GAME_WIDTH*2/3)
+ {
+ result.at(positionOffset+2) = true;
+ }
+ else
+ {
+ result.at(positionOffset+1) = true;
+ }
+ }
+
+ result.at(canBuildOffset) = true;
+ for (auto const& missileController : _missileControllers)
+ {
+ if (missileController.y() < playerY)
+ {
+ continue;
+ }
+ result.at(existingBuildingsOffset + 0) = true;
+ if (_missiles.size() < 2)
+ {
+ result.at(missileOffset+1) = true;
+ }
+ if (abs(missileController.x() - playerX) < 3)
+ {
+ result.at(canBuildOffset) = false;
+ }
+ }
+
+ return result;
+}
diff --git a/2015-spacebot/src/move_string_mapper.cpp b/2015-spacebot/src/move_string_mapper.cpp
new file mode 100644
index 0000000..ed510c1
--- /dev/null
+++ b/2015-spacebot/src/move_string_mapper.cpp
@@ -0,0 +1,21 @@
+#include "move_string_mapper.h"
+
+#include "move.h"
+#include <map>
+#include <string>
+
+MoveStringMapper::MoveStringMapper()
+{
+ moveMap[Move::NOTHING] = "Nothing";
+ moveMap[Move::MOVE_LEFT] = "MoveLeft";
+ moveMap[Move::MOVE_RIGHT] = "MoveRight";
+ moveMap[Move::SHOOT] = "Shoot";
+ moveMap[Move::BUILD_MISSILE_CONTROLLER] = "BuildMissileController";
+ moveMap[Move::BUILD_ALIEN_FACTORY] = "BuildAlienFactory";
+ moveMap[Move::BUILD_SHIELD] = "BuildShield";
+}
+
+std::string MoveStringMapper::toString(const Move &move)
+{
+ return moveMap[move];
+}
diff --git a/2015-spacebot/src/player_missile.cpp b/2015-spacebot/src/player_missile.cpp
new file mode 100644
index 0000000..a95e928
--- /dev/null
+++ b/2015-spacebot/src/player_missile.cpp
@@ -0,0 +1,6 @@
+#include "player_missile.h"
+
+PlayerMissile::PlayerMissile(int x, int y)
+ :GameEntity(x, y)
+{
+}
diff --git a/2015-spacebot/src/shield.cpp b/2015-spacebot/src/shield.cpp
new file mode 100644
index 0000000..7457554
--- /dev/null
+++ b/2015-spacebot/src/shield.cpp
@@ -0,0 +1,6 @@
+#include "shield.h"
+
+Shield::Shield(int x, int y)
+ :GameEntity(x, y)
+{
+}
diff --git a/2015-spacebot/src/spacebot.cpp b/2015-spacebot/src/spacebot.cpp
new file mode 100644
index 0000000..17d20b5
--- /dev/null
+++ b/2015-spacebot/src/spacebot.cpp
@@ -0,0 +1,48 @@
+#include "spacebot.h"
+#include "move_string_mapper.h"
+#include "brain/neural_network.h"
+#include <fstream>
+#include <iostream>
+
+Spacebot::Spacebot(std::string outputPath, std::string brainFilename)
+ : _outputFilename(outputPath+"/move.txt"),
+ _brainFilename(brainFilename),
+ _gameState(std::ifstream(outputPath+"/map.txt"))
+{
+}
+
+void Spacebot::writeNextMove()
+{
+ Move move = chooseMove();
+ writeMove(move);
+}
+
+Move Spacebot::chooseMove()
+{
+ auto sensorInputs = _gameState.toBitArray();
+
+ if (!sensorInputs.at(51) || !sensorInputs.at(53))
+ {
+ return Move::BUILD_SHIELD;
+ }
+ else if (sensorInputs.at(55))
+ {
+ return Move::SHOOT;
+ }
+ else if (sensorInputs.at(60) && !sensorInputs.at(59))
+ {
+ return Move::BUILD_MISSILE_CONTROLLER;
+ }
+ else
+ {
+ return Move::NOTHING;
+ }
+}
+
+void Spacebot::writeMove(const Move& move)
+{
+ std::ofstream resultStream(_outputFilename);
+ resultStream << MoveStringMapper().toString(move) << std::endl;
+ return;
+}
+
diff --git a/2015-spacebot/src/spaceship.cpp b/2015-spacebot/src/spaceship.cpp
new file mode 100644
index 0000000..b1cee9a
--- /dev/null
+++ b/2015-spacebot/src/spaceship.cpp
@@ -0,0 +1,6 @@
+#include "spaceship.h"
+
+Spaceship::Spaceship(int x, int y)
+ :GameEntity(x, y)
+{
+}