diff options
Diffstat (limited to '2015-spacebot/src')
-rw-r--r-- | 2015-spacebot/src/alien.cpp | 7 | ||||
-rw-r--r-- | 2015-spacebot/src/brain/neural_link.cpp | 11 | ||||
-rw-r--r-- | 2015-spacebot/src/brain/neural_network.cpp | 163 | ||||
-rw-r--r-- | 2015-spacebot/src/brain/neuron.cpp | 40 | ||||
-rw-r--r-- | 2015-spacebot/src/building.cpp | 6 | ||||
-rw-r--r-- | 2015-spacebot/src/enemy_bullet.cpp | 6 | ||||
-rw-r--r-- | 2015-spacebot/src/game_entity.cpp | 13 | ||||
-rw-r--r-- | 2015-spacebot/src/game_state.cpp | 234 | ||||
-rw-r--r-- | 2015-spacebot/src/move_string_mapper.cpp | 21 | ||||
-rw-r--r-- | 2015-spacebot/src/player_missile.cpp | 6 | ||||
-rw-r--r-- | 2015-spacebot/src/shield.cpp | 6 | ||||
-rw-r--r-- | 2015-spacebot/src/spacebot.cpp | 48 | ||||
-rw-r--r-- | 2015-spacebot/src/spaceship.cpp | 6 |
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) +{ +} |