summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt2
-rw-r--r--bot.json5
-rw-r--r--include/brain/neural_network.h4
-rw-r--r--include/brain/neural_node.h1
-rw-r--r--include/brain/neuron.h4
-rw-r--r--include/building.h12
-rw-r--r--include/game_state.h16
-rw-r--r--src/brain/neural_network.cpp81
-rw-r--r--src/brain/neuron.cpp7
-rw-r--r--src/building.cpp6
-rw-r--r--src/game_state.cpp154
-rw-r--r--src/spacebot.cpp6
-rw-r--r--test/game_state.cpp4
13 files changed, 212 insertions, 90 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2a84f19..fd6a32d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.2.2)
project(Spacebot)
-set(CMAKE_CXX_FLAGS "-std=c++11")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wpedantic -Wextra")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
diff --git a/bot.json b/bot.json
new file mode 100644
index 0000000..d70d03c
--- /dev/null
+++ b/bot.json
@@ -0,0 +1,5 @@
+{
+ "nickName": "Spacebot",
+ "author": "Justin Worthe",
+ "email", "justin.worthe@gmail.com"
+}
diff --git a/include/brain/neural_network.h b/include/brain/neural_network.h
index 22e5e42..f75fdea 100644
--- a/include/brain/neural_network.h
+++ b/include/brain/neural_network.h
@@ -26,8 +26,8 @@ public:
private:
std::map<int, std::shared_ptr<Sensor>> _sensors;
std::shared_ptr<BiasNode> _biasNode;
- std::map<int, std::shared_ptr<Neuron>> _hiddenNodes;
- std::map<int, std::shared_ptr<Neuron>> _outputs;
+ std::map<int, std::shared_ptr<Neuron>> _neurons;
+ std::vector<std::shared_ptr<Neuron>> _outputs;
void parseFile(std::istream &&file);
diff --git a/include/brain/neural_node.h b/include/brain/neural_node.h
index 3c2a6cb..c7b4335 100644
--- a/include/brain/neural_node.h
+++ b/include/brain/neural_node.h
@@ -4,7 +4,6 @@ class NeuralNode
{
public:
NeuralNode(char type, int id);
- virtual ~NeuralNode() {};
virtual double activation() const = 0;
diff --git a/include/brain/neuron.h b/include/brain/neuron.h
index 810ce5b..d017b0d 100644
--- a/include/brain/neuron.h
+++ b/include/brain/neuron.h
@@ -13,9 +13,9 @@ public:
virtual ~Neuron() {}
virtual double activation() const;
- void addInput(std::shared_ptr<NeuralLink> link);
+ void addInput(std::unique_ptr<NeuralLink>&& link);
private:
- std::vector<std::shared_ptr<NeuralLink>> _inputLinks;
+ std::vector<std::unique_ptr<NeuralLink>> _inputLinks;
double sigmoid(double input) const;
};
diff --git a/include/building.h b/include/building.h
new file mode 100644
index 0000000..76d78fd
--- /dev/null
+++ b/include/building.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "game_entity.h"
+
+class Building : public GameEntity
+{
+public:
+ Building(int x, int y);
+ const static char MISSILE_CONTROLLER_CHAR = 'M';
+ const static char ALIEN_FACTORY_CHAR = 'X';
+};
+
diff --git a/include/game_state.h b/include/game_state.h
index 482dd7d..c129d28 100644
--- a/include/game_state.h
+++ b/include/game_state.h
@@ -5,21 +5,27 @@
#include "player_missile.h"
#include "shield.h"
#include "spaceship.h"
+#include "building.h"
#include <vector>
#include <string>
#include <istream>
+#include <memory>
class GameState
{
public:
GameState(std::istream &&mapFile);
- void logState();
+ void logState() const;
const std::vector<Alien>& aliens() const { return _aliens; }
const std::vector<EnemyBullet>& bullets() const { return _bullets; }
const std::vector<PlayerMissile>& missiles() const { return _missiles; }
const std::vector<Shield>& shields() const { return _shields; }
- const std::vector<Spaceship>& spaceships() const { return _spaceships; }
+ const std::vector<Building>& missileControllers() const { return _missileControllers; }
+ const std::vector<Building>& alienFactories() const { return _alienFactories; }
+
+ const std::unique_ptr<Spaceship>& playerSpaceship() const { return _playerSpaceship; }
+ const std::unique_ptr<Spaceship>& enemySpaceship() const { return _enemySpaceship; }
std::vector<bool> toBitArray() const;
@@ -28,7 +34,11 @@ private:
std::vector<EnemyBullet> _bullets;
std::vector<PlayerMissile> _missiles;
std::vector<Shield> _shields;
- std::vector<Spaceship> _spaceships;
+ std::vector<Building> _missileControllers;
+ std::vector<Building> _alienFactories;
+ std::unique_ptr<Spaceship> _playerSpaceship;
+ std::unique_ptr<Spaceship> _enemySpaceship;
+
int addEntity(int x, int y, char type);
};
diff --git a/src/brain/neural_network.cpp b/src/brain/neural_network.cpp
index d6a5e15..9061194 100644
--- a/src/brain/neural_network.cpp
+++ b/src/brain/neural_network.cpp
@@ -1,5 +1,6 @@
#include "brain/neural_network.h"
#include "brain/neuron.h"
+#include <limits>
NeuralNetwork::NeuralNetwork(std::istream &&networkConfigFile, int numberOfSensors, int numberOfOutputs)
{
@@ -11,7 +12,9 @@ NeuralNetwork::NeuralNetwork(std::istream &&networkConfigFile, int numberOfSenso
}
for (int i=0; i<numberOfOutputs; ++i)
{
- _outputs[i] = std::make_shared<Neuron>(i);
+ auto output = std::make_shared<Neuron>(i);
+ _outputs.push_back(output);
+ _neurons[i] = output;
}
parseFile(std::move(networkConfigFile));
@@ -21,15 +24,17 @@ NeuralNetwork::NeuralNetwork(std::istream &&networkConfigFile, std::vector<bool>
{
_biasNode = std::make_shared<BiasNode>();
- for (int i=0; i<sensorInitialValues.size(); ++i)
+ for (unsigned int i=0; i<sensorInitialValues.size(); ++i)
{
auto sensor = std::make_shared<Sensor>(i);
- sensor->setActivation(sensorInitialValues.at(i) ? 1 : 0);
+ sensor->setActivation(sensorInitialValues[i] ? 1 : 0);
_sensors[i] = sensor;
}
for (int i=0; i<numberOfOutputs; ++i)
{
- _outputs[i] = std::make_shared<Neuron>(i);
+ auto output = std::make_shared<Neuron>(i);
+ _outputs.push_back(output);
+ _neurons[i] = output;
}
parseFile(std::move(networkConfigFile));
@@ -38,14 +43,18 @@ NeuralNetwork::NeuralNetwork(std::istream &&networkConfigFile, std::vector<bool>
void NeuralNetwork::parseFile(std::istream &&file)
{
double weight;
- for (std::string src, dest;
- file >> src && file >> dest && file >> weight; )
- {
- char srcType = src.at(0);
- int srcId = std::stoi(src.substr(1));
- char destType = dest.at(0);
- int destId = std::stoi(dest.substr(1));
+ char srcType;
+ int srcId;
+ 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)
@@ -56,21 +65,11 @@ void NeuralNetwork::parseFile(std::istream &&file)
case 'b':
source = _biasNode;
break;
- case 'n':
- source = findOrAddNeuron(srcId);
- break;
default:
- throw 1;
- }
- switch (destType)
- {
- case 'n':
- destination = findOrAddNeuron(destId);
- break;
- default:
- throw 1;
+ source = findOrAddNeuron(srcId);
}
-
+ destination = findOrAddNeuron(destId);
+
addLink(source, destination, weight);
}
@@ -78,34 +77,30 @@ void NeuralNetwork::parseFile(std::istream &&file)
void NeuralNetwork::addLink(std::shared_ptr<NeuralNode> source, std::shared_ptr<Neuron> destination, double weight)
{
- auto link = std::make_shared<NeuralLink>(source, weight);
- destination->addInput(link);
+ std::unique_ptr<NeuralLink> link(new NeuralLink(source, weight));
+ destination->addInput(std::move(link));
}
std::shared_ptr<Sensor> NeuralNetwork::findOrAddSensor(int id)
{
- bool sensorExists = _sensors.count(id) > 0;
- if (!sensorExists)
+ auto sensor = _sensors[id];
+ if (!sensor)
{
- _sensors[id] = std::make_shared<Sensor>(id);
+ sensor = std::make_shared<Sensor>(id);
+ _sensors[id] = sensor;
}
- return _sensors.at(id);
+ return sensor;
}
std::shared_ptr<Neuron> NeuralNetwork::findOrAddNeuron(int id)
{
- bool isOutput = _outputs.count(id) > 0;
- if (isOutput)
- {
- return _outputs.at(id);
- }
-
- bool hiddenNeuronExists = _hiddenNodes.count(id) > 0;
- if (!hiddenNeuronExists)
+ auto neuron = _neurons[id];
+ if (!neuron)
{
- _hiddenNodes[id] = std::make_shared<Neuron>(id);
+ neuron = std::make_shared<Neuron>(id);
+ _neurons[id] = neuron;
}
- return _hiddenNodes.at(id);
+ return neuron;
}
void NeuralNetwork::setInput(int inputIndex, double activation)
@@ -117,13 +112,13 @@ int NeuralNetwork::findMaxOutputIndex() const
{
double currentMaxActivation = 0;
int currentMaxIndex = 0;
- for (std::pair<const int, std::shared_ptr<Neuron>> outputPair : _outputs)
+ for (unsigned int i=0; i<_outputs.size(); ++i)
{
- double activation = outputPair.second->activation();
+ double activation = _outputs[i]->activation();
if (activation >= currentMaxActivation)
{
currentMaxActivation = activation;
- currentMaxIndex = outputPair.first;
+ currentMaxIndex = i;
}
}
return currentMaxIndex;
diff --git a/src/brain/neuron.cpp b/src/brain/neuron.cpp
index 7ea02c6..527e7da 100644
--- a/src/brain/neuron.cpp
+++ b/src/brain/neuron.cpp
@@ -9,21 +9,20 @@ Neuron::Neuron(int id)
double Neuron::sigmoid(double input) const
{
double slope = 4.924273;
- double constant = 2.4621365;
return (1/(1+(std::exp(-(slope*input)))));
}
double Neuron::activation() const
{
double activationSum = 0;
- for (auto link : _inputLinks)
+ for (auto const& link : _inputLinks)
{
activationSum += link->weightedActivation();
}
return sigmoid(activationSum);
}
-void Neuron::addInput(std::shared_ptr<NeuralLink> link)
+void Neuron::addInput(std::unique_ptr<NeuralLink>&& link)
{
- _inputLinks.push_back(link);
+ _inputLinks.push_back(std::move(link));
}
diff --git a/src/building.cpp b/src/building.cpp
new file mode 100644
index 0000000..8da322d
--- /dev/null
+++ b/src/building.cpp
@@ -0,0 +1,6 @@
+#include "building.h"
+
+Building::Building(int x, int y)
+ :GameEntity(x, y)
+{
+}
diff --git a/src/game_state.cpp b/src/game_state.cpp
index e6deae7..eb02005 100644
--- a/src/game_state.cpp
+++ b/src/game_state.cpp
@@ -3,6 +3,7 @@
#include <fstream>
#include <limits>
#include <bitset>
+#include <cmath>
const int OPENING_LINES = 6;
const int GAME_AREA_LINES = 25;
@@ -60,75 +61,170 @@ int GameState::addEntity(int x, int y, char type)
_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:
- _spaceships.push_back(Spaceship(x+1,y));
+ _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()
+void GameState::logState() const
{
- for (auto alien : _aliens)
+ for (auto const& alien : _aliens)
{
std::cout << "Alien " << alien.coords() << std::endl;
}
- for (auto bullet : _bullets)
+ for (auto const& bullet : _bullets)
{
std::cout << "Enemy Bullet" << bullet.coords() << std::endl;
}
- for (auto missile : _missiles)
+ for (auto const& missile : _missiles)
{
std::cout << "Player Missile" << missile.coords() << std::endl;
}
- for (auto shield : _shields)
+ for (auto const& shield : _shields)
{
std::cout << "Shield" << shield.coords() << std::endl;
}
- for (auto spaceship : _spaceships)
+ if (_playerSpaceship)
{
- std::cout << "Spaceship" << spaceship.coords() << std::endl;
+ std::cout << "Player Spaceship" << _playerSpaceship->coords() << std::endl;
}
+ if (_enemySpaceship)
+ {
+ std::cout << "Enemy Spaceship" << _enemySpaceship->coords() << std::endl;
+ }
+}
+
+int getPyramidOffset(int bottomWidth, 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 (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;
+ std::vector<bool> result(172);
- std::vector<bool> alienNodes((GAME_AREA_LINES-2) * (GAME_WIDTH-2), false);
- for (auto alien : _aliens)
+ int alienOffset = 0; //Aliens are 0 to 100
+ int bulletOffset = 101; //Bullets are 101 to 135
+ int shieldOffset = 136; //Shields are 136 to 150
+ int missileOffset = 151; //Missile info is 151 to 152
+ int positionOffset = 153; //Position is 153 to 168
+ int existingBuildingsOffset = 169; //Existing buildings is 169 to 170
+ int canBuildOffset = 171; //Can build here is 171
+
+ int playerX = GAME_WIDTH/2;
+ int playerY = GAME_AREA_LINES-3;
+ if (_playerSpaceship)
{
- alienNodes.at((alien.y()-1)*(GAME_WIDTH-2)+(alien.x()-1)) = true;
+ playerX = _playerSpaceship->x();
}
- result.insert(result.end(), alienNodes.begin(), alienNodes.end());
- std::vector<bool> bulletNodes((GAME_AREA_LINES-2) * (GAME_WIDTH-2), false);
- for (auto bullet : _bullets)
+
+ for (auto const& alien : _aliens)
{
- bulletNodes.at((bullet.y()-1)*(GAME_WIDTH-2)+(bullet.x()-1)) = true;
+ if (alien.y() >= playerY || alien.y() < playerY-9)
+ {
+ continue;
+ }
+
+ result.at(alienOffset + getPyramidOffset(3, alien.x()-playerX, playerY-1-alien.y(), 99, 100)) = true;
}
- result.insert(result.end(), bulletNodes.begin(), bulletNodes.end());
- std::vector<bool> missileNodes((GAME_AREA_LINES-2) * (GAME_WIDTH-2), false);
- for (auto missile : _missiles)
+ for (auto const& bullet : _bullets)
{
- missileNodes.at((missile.y()-1)*(GAME_WIDTH-2)+(missile.x()-1)) = true;
+ if (bullet.y() >= playerY || bullet.y() < playerY-5 || bullet.x() > playerX+3 || bullet.x() < playerX-3)
+ {
+ continue;
+ }
+
+ result.at(bulletOffset + getRectangularOffset(7, bullet.x()-playerX, playerY-1-bullet.y())) = true;
}
- result.insert(result.end(), missileNodes.begin(), missileNodes.end());
- std::vector<bool> shieldNodes(2 * (GAME_WIDTH-2), false);
- for (auto shield : _shields)
+ for (auto const& shield : _shields)
{
- int row = shield.y() > GAME_AREA_LINES/2 ? 1 : 0;
- shieldNodes.at(row*(GAME_WIDTH-2)+(shield.x()-1)) = true;
+ if (shield.y() < playerY-3 || shield.x() < playerX-2 || shield.x() > playerX+2)
+ {
+ continue;
+ }
+
+ result.at(shieldOffset + getRectangularOffset(5, shield.x()-playerX, playerY-1-shield.y())) = true;
}
- for (auto spaceship : _spaceships)
+ if (_missiles.size() > 0)
+ {
+ result.at(missileOffset) = true;
+ }
+ if (_missiles.size() < 1)
+ {
+ result.at(missileOffset + 1) = true;
+ }
+
+ if (_playerSpaceship)
{
- std::bitset<5> spaceshipBits(spaceship.x());
- for (int i=0; i<spaceshipBits.size(); ++i)
+ result.at(positionOffset + playerX-2) = 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;
+ }
+ }
+ for (auto const& alienFactory : _alienFactories)
+ {
+ if (alienFactory.y() < playerY)
+ {
+ continue;
+ }
+ result.at(existingBuildingsOffset + 1) = true;
+ if (abs(alienFactory.x() - playerX) < 3)
{
- result.push_back(spaceshipBits[i]);
+ result.at(canBuildOffset) = false;
}
}
diff --git a/src/spacebot.cpp b/src/spacebot.cpp
index 418756c..15f2221 100644
--- a/src/spacebot.cpp
+++ b/src/spacebot.cpp
@@ -2,6 +2,7 @@
#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"),
@@ -22,10 +23,9 @@ Move Spacebot::chooseMove()
NeuralNetwork network(std::ifstream(_brainFilename),
sensorInputs,
- static_cast<int>(Move::BUILD_SHIELD));
+ 7);
- int moveInt = network.findMaxOutputIndex();
- return static_cast<Move>(moveInt);
+ return static_cast<Move>(network.findMaxOutputIndex());
}
void Spacebot::writeMove(const Move& move)
diff --git a/test/game_state.cpp b/test/game_state.cpp
index 467a665..a9975f2 100644
--- a/test/game_state.cpp
+++ b/test/game_state.cpp
@@ -82,8 +82,8 @@ SCENARIO("game state is read from istream")
THEN("the spaceships are read correctly")
{
- auto spaceships = state.spaceships();
- REQUIRE(spaceships.size() == 2);
+ REQUIRE(state.playerSpaceship()->x() == 9);
+ REQUIRE(state.playerSpaceship()->y() == 22);
}
}
}