|
@@ -1,6 +1,7 @@
|
|
|
#include <cmath>
|
|
|
#include <cstdio>
|
|
|
|
|
|
+#include "GLFW/glfw3.h"
|
|
|
#include "data/Array.h"
|
|
|
#include "data/ArrayList.h"
|
|
|
#include "data/HashMap.h"
|
|
@@ -21,8 +22,15 @@ static Buffer fontBuffer;
|
|
|
static VertexBuffer vertexBuffer;
|
|
|
static VertexBuffer fontVertexBuffer;
|
|
|
static Texture fontTexture;
|
|
|
+static Random rng(0);
|
|
|
+static int restartButton = 0;
|
|
|
+static int restartMapButton = 0;
|
|
|
+static int turns = 0;
|
|
|
|
|
|
enum class Resource { NOTHING, CLAY, ORE, WHEAT, SHEEP, WOOD };
|
|
|
+static const char* RESOURCE_NAME[] = {"Nothing", "Clay", "Ore",
|
|
|
+ "Wheat", "Sheep", "Wood"};
|
|
|
+
|
|
|
static const Color4 RESOURCE_COLOR[] = {
|
|
|
Color4(160, 120, 0, 255), Color4(220, 96, 0, 255),
|
|
|
Color4(128, 128, 128, 255), Color4(255, 200, 0, 255),
|
|
@@ -81,10 +89,28 @@ static void addString(const char* s, const Vector2& pos, float size,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static int getQualityOfNumber(int number) {
|
|
|
+ if(number < 2 || number > 12 || number == 7) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ return number < 7 ? number - 1 : 13 - number;
|
|
|
+}
|
|
|
+
|
|
|
struct Player {
|
|
|
int id = -1;
|
|
|
Color4 color;
|
|
|
HashMap<int, int> resources;
|
|
|
+ int settlements = 5;
|
|
|
+ int paths = 15;
|
|
|
+ int cities = 4;
|
|
|
+
|
|
|
+ Player() {
|
|
|
+ giveResource(Resource::CLAY, 0);
|
|
|
+ giveResource(Resource::ORE, 0);
|
|
|
+ giveResource(Resource::SHEEP, 0);
|
|
|
+ giveResource(Resource::WHEAT, 0);
|
|
|
+ giveResource(Resource::WOOD, 0);
|
|
|
+ }
|
|
|
|
|
|
int getResource(Resource r) const {
|
|
|
const int* i = resources.search(static_cast<int>(r));
|
|
@@ -92,9 +118,82 @@ struct Player {
|
|
|
}
|
|
|
|
|
|
void placeStartSettlement();
|
|
|
+
|
|
|
+ void giveResource(Resource r, int amount) {
|
|
|
+ LOG_INFO(StringBuffer<256>("Player ")
|
|
|
+ .append(id)
|
|
|
+ .append(" gets ")
|
|
|
+ .append(amount)
|
|
|
+ .append(" ")
|
|
|
+ .append(RESOURCE_NAME[static_cast<int>(r)]));
|
|
|
+ int* i = resources.search(static_cast<int>(r));
|
|
|
+ if(i == nullptr) {
|
|
|
+ resources.tryEmplace(static_cast<int>(r), amount);
|
|
|
+ } else {
|
|
|
+ *i += amount;
|
|
|
+ }
|
|
|
+ LOG_INFO(StringBuffer<256>(resources));
|
|
|
+ }
|
|
|
+
|
|
|
+ void removeResource(Resource r, int amount) {
|
|
|
+ LOG_INFO(StringBuffer<256>("Player ")
|
|
|
+ .append(id)
|
|
|
+ .append(" pays ")
|
|
|
+ .append(amount)
|
|
|
+ .append(" ")
|
|
|
+ .append(RESOURCE_NAME[static_cast<int>(r)]));
|
|
|
+ int* i = resources.search(static_cast<int>(r));
|
|
|
+ if(i == nullptr) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ *i -= amount;
|
|
|
+ LOG_INFO(StringBuffer<256>(resources));
|
|
|
+ }
|
|
|
+
|
|
|
+ void takeTurn();
|
|
|
+
|
|
|
+ bool swap() {
|
|
|
+ int max = 0;
|
|
|
+ int maxResource = 0;
|
|
|
+ for(const auto& entry : resources.entries()) {
|
|
|
+ if(entry.value > max) {
|
|
|
+ max = entry.value;
|
|
|
+ maxResource = entry.getKey();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ int min = max;
|
|
|
+ int minResource = 0;
|
|
|
+ for(const auto& entry : resources.entries()) {
|
|
|
+ if(entry.value < min) {
|
|
|
+ min = entry.value;
|
|
|
+ minResource = entry.getKey();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if(max >= 4) {
|
|
|
+ removeResource(static_cast<Resource>(maxResource), 4);
|
|
|
+ giveResource(static_cast<Resource>(minResource), 1);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ int getPoints() const;
|
|
|
+
|
|
|
+ void reset() {
|
|
|
+ removeResource(Resource::CLAY, getResource(Resource::CLAY));
|
|
|
+ removeResource(Resource::ORE, getResource(Resource::ORE));
|
|
|
+ removeResource(Resource::SHEEP, getResource(Resource::SHEEP));
|
|
|
+ removeResource(Resource::WHEAT, getResource(Resource::WHEAT));
|
|
|
+ removeResource(Resource::WOOD, getResource(Resource::WOOD));
|
|
|
+ settlements = 5;
|
|
|
+ paths = 15;
|
|
|
+ cities = 4;
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
-static Array<Player, 1> players;
|
|
|
+static Array<Player, 4> players;
|
|
|
static int currentPlayer = 0;
|
|
|
|
|
|
struct Hexagon {
|
|
@@ -156,10 +255,11 @@ struct Hexagon {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-static Array<Hexagon, 19> hexagons;
|
|
|
+static Array<Hexagon, 19> gHexagons;
|
|
|
|
|
|
struct Corner {
|
|
|
int player = -1;
|
|
|
+ bool city = false;
|
|
|
Vector2 mid;
|
|
|
ArrayList<int, 3> hexagons;
|
|
|
ArrayList<int, 3> paths;
|
|
@@ -168,13 +268,32 @@ struct Corner {
|
|
|
if(player < 0 || player >= players.getLength()) {
|
|
|
return;
|
|
|
}
|
|
|
- addSquare(mid, 0.05f, players[player].color);
|
|
|
+ addSquare(mid, 0.075f * (static_cast<float>(city) + 1.0f),
|
|
|
+ players[player].color);
|
|
|
}
|
|
|
|
|
|
bool validForSettlement() const;
|
|
|
+ bool doesAnyPathBelongToPlayer(int player) const;
|
|
|
+
|
|
|
+ bool isFree() const {
|
|
|
+ return player < 0 || player >= players.getLength();
|
|
|
+ }
|
|
|
+
|
|
|
+ void reset() {
|
|
|
+ player = -1;
|
|
|
+ city = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ int getQuality() {
|
|
|
+ int q = 0;
|
|
|
+ for(int h : hexagons) {
|
|
|
+ q += getQualityOfNumber(gHexagons[h].number);
|
|
|
+ }
|
|
|
+ return q;
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
-static Array<Corner, 54> corners;
|
|
|
+static Array<Corner, 54> gCorners;
|
|
|
|
|
|
struct Path {
|
|
|
int player = -1;
|
|
@@ -184,6 +303,9 @@ struct Path {
|
|
|
int cornerB = -1;
|
|
|
|
|
|
void addToBuffer() const {
|
|
|
+ if(player < 0 || player >= players.getLength()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
Vector2 diff = to - from;
|
|
|
Vector2 normal(diff[1], -diff[0]);
|
|
|
normal.normalize();
|
|
@@ -193,8 +315,8 @@ struct Path {
|
|
|
Vector2 b = from - normal;
|
|
|
Vector2 c = to + normal;
|
|
|
Vector2 d = to - normal;
|
|
|
- addTriangle(a, c, b, Color4(255, 255, 255, 255));
|
|
|
- addTriangle(c, d, b, Color4(255, 255, 255, 255));
|
|
|
+ addTriangle(a, c, b, players[player].color);
|
|
|
+ addTriangle(c, d, b, players[player].color);
|
|
|
}
|
|
|
|
|
|
Vector2 getMid() const {
|
|
@@ -202,34 +324,35 @@ struct Path {
|
|
|
}
|
|
|
|
|
|
const Corner& getOtherCorner(const Corner& c) const {
|
|
|
- if(&(corners[cornerA]) == &c) {
|
|
|
- return corners[cornerB];
|
|
|
- } else if(&(corners[cornerB]) == &c) {
|
|
|
- return corners[cornerA];
|
|
|
+ if(&(gCorners[cornerA]) == &c) {
|
|
|
+ return gCorners[cornerB];
|
|
|
+ } else if(&(gCorners[cornerB]) == &c) {
|
|
|
+ return gCorners[cornerA];
|
|
|
}
|
|
|
return c;
|
|
|
}
|
|
|
-};
|
|
|
|
|
|
-static List<Path> gPaths;
|
|
|
+ bool isFree() const {
|
|
|
+ return player < 0 || player >= players.getLength();
|
|
|
+ }
|
|
|
|
|
|
-static int getQualityOfNumber(int number) {
|
|
|
- if(number < 2 || number > 12 || number == 7) {
|
|
|
- return 0;
|
|
|
+ void reset() {
|
|
|
+ player = -1;
|
|
|
}
|
|
|
- return number < 7 ? number - 1 : 13 - number;
|
|
|
-}
|
|
|
+};
|
|
|
+
|
|
|
+static List<Path> gPaths;
|
|
|
|
|
|
static int findBestCorner() {
|
|
|
int current = -1;
|
|
|
int quality = 0;
|
|
|
- for(int i = 0; i < corners.getLength(); i++) {
|
|
|
- if(!corners[i].validForSettlement()) {
|
|
|
+ for(int i = 0; i < gCorners.getLength(); i++) {
|
|
|
+ if(!gCorners[i].validForSettlement()) {
|
|
|
continue;
|
|
|
}
|
|
|
int newQuality = 0;
|
|
|
- for(int h : corners[i].hexagons) {
|
|
|
- newQuality += getQualityOfNumber(hexagons[h].number);
|
|
|
+ for(int h : gCorners[i].hexagons) {
|
|
|
+ newQuality += getQualityOfNumber(gHexagons[h].number);
|
|
|
}
|
|
|
if(newQuality > quality) {
|
|
|
quality = newQuality;
|
|
@@ -245,37 +368,167 @@ void Player::placeStartSettlement() {
|
|
|
LOG_ERROR("Cannot place start settlement");
|
|
|
return;
|
|
|
}
|
|
|
- corners[c].player = id;
|
|
|
+ gCorners[c].player = id;
|
|
|
+ settlements--;
|
|
|
+
|
|
|
+ while(true) {
|
|
|
+ int p =
|
|
|
+ gCorners[c].paths[rng.next(0, gCorners[c].paths.getLength() - 1)];
|
|
|
+ if(gPaths[p].player == -1) {
|
|
|
+ gPaths[p].player = id;
|
|
|
+ paths--;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static List<int> getPossiblePaths(int player) {
|
|
|
+ List<int> paths;
|
|
|
+ for(int i = 0; i < gPaths.getLength(); i++) {
|
|
|
+ if(!gPaths[i].isFree()) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ const Corner& cornerA = gCorners[gPaths[i].cornerA];
|
|
|
+ const Corner& cornerB = gCorners[gPaths[i].cornerB];
|
|
|
+ if(cornerA.player == player || cornerB.player == player ||
|
|
|
+ cornerA.doesAnyPathBelongToPlayer(player) ||
|
|
|
+ cornerB.doesAnyPathBelongToPlayer(player)) {
|
|
|
+ paths.add(i);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return paths;
|
|
|
+}
|
|
|
+
|
|
|
+static List<int> getPossibleCorners(int player) {
|
|
|
+ List<int> corners;
|
|
|
+ for(int i = 0; i < gCorners.getLength(); i++) {
|
|
|
+ if(!gCorners[i].isFree()) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if(gCorners[i].doesAnyPathBelongToPlayer(player) &&
|
|
|
+ gCorners[i].validForSettlement()) {
|
|
|
+ corners.add(i);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return corners;
|
|
|
+}
|
|
|
+
|
|
|
+static List<int> getPossibleCityCorners(int player) {
|
|
|
+ List<int> corners;
|
|
|
+ for(int i = 0; i < gCorners.getLength(); i++) {
|
|
|
+ if(gCorners[i].player == player && !gCorners[i].city) {
|
|
|
+ corners.add(i);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return corners;
|
|
|
+}
|
|
|
+
|
|
|
+void Player::takeTurn() {
|
|
|
+ while(true) {
|
|
|
+ int clay = getResource(Resource::CLAY);
|
|
|
+ int ore = getResource(Resource::ORE);
|
|
|
+ int sheep = getResource(Resource::SHEEP);
|
|
|
+ int wheat = getResource(Resource::WHEAT);
|
|
|
+ int wood = getResource(Resource::WOOD);
|
|
|
+ if(wood >= 1 && clay >= 1 && wheat >= 1 && sheep >= 1 &&
|
|
|
+ settlements > 0) {
|
|
|
+ LOG_INFO("I can build a settlement.");
|
|
|
+ List<int> possibleCorners = getPossibleCorners(id);
|
|
|
+ if(possibleCorners.getLength() >= 1) {
|
|
|
+ int index = rng.next(0, possibleCorners.getLength() - 1);
|
|
|
+ gCorners[possibleCorners[index]].player = id;
|
|
|
+ removeResource(Resource::CLAY, 1);
|
|
|
+ removeResource(Resource::WOOD, 1);
|
|
|
+ removeResource(Resource::SHEEP, 1);
|
|
|
+ removeResource(Resource::WHEAT, 1);
|
|
|
+ LOG_INFO("I build a settlement.");
|
|
|
+ settlements--;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if(wood >= 1 && clay >= 1 && paths > 0) {
|
|
|
+ LOG_INFO("I can build a path.");
|
|
|
+ List<int> possiblePaths = getPossiblePaths(id);
|
|
|
+ if(possiblePaths.getLength() >= 1 &&
|
|
|
+ getPossibleCorners(id).getLength() == 0) {
|
|
|
+ int index = rng.next(0, possiblePaths.getLength() - 1);
|
|
|
+ gPaths[possiblePaths[index]].player = id;
|
|
|
+ removeResource(Resource::CLAY, 1);
|
|
|
+ removeResource(Resource::WOOD, 1);
|
|
|
+ LOG_INFO("I build a path.");
|
|
|
+ paths--;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if(ore >= 3 && wheat >= 2 && cities > 0) {
|
|
|
+ LOG_INFO("I can build a city.");
|
|
|
+ List<int> possibleCityCorners = getPossibleCityCorners(id);
|
|
|
+ if(possibleCityCorners.getLength() >= 1) {
|
|
|
+ int bestIndex = -1;
|
|
|
+ int quality = -1;
|
|
|
+ for(int index : possibleCityCorners) {
|
|
|
+ int newQuality = gCorners[index].getQuality();
|
|
|
+ if(newQuality > quality) {
|
|
|
+ bestIndex = index;
|
|
|
+ quality = newQuality;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ gCorners[bestIndex].city = true;
|
|
|
+ removeResource(Resource::ORE, 3);
|
|
|
+ removeResource(Resource::WHEAT, 2);
|
|
|
+ LOG_INFO("I build a city.");
|
|
|
+ cities--;
|
|
|
+ settlements++;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if(swap()) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+bool Corner::doesAnyPathBelongToPlayer(int playerId) const {
|
|
|
+ for(int p : paths) {
|
|
|
+ if(gPaths[p].player == playerId) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
bool Corner::validForSettlement() const {
|
|
|
- if(player != -1) {
|
|
|
+ if(!isFree()) {
|
|
|
return false;
|
|
|
}
|
|
|
for(int p : paths) {
|
|
|
- if(gPaths[p].getOtherCorner(*this).player != -1) {
|
|
|
+ if(!gPaths[p].getOtherCorner(*this).isFree()) {
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
+int Player::getPoints() const {
|
|
|
+ return (4 - cities) * 2 + 5 - settlements;
|
|
|
+}
|
|
|
+
|
|
|
static void initResources() {
|
|
|
for(int i = 0; i < 4; i++) {
|
|
|
- hexagons[i].resource = Resource::WHEAT;
|
|
|
- hexagons[i + 4].resource = Resource::WOOD;
|
|
|
- hexagons[i + 8].resource = Resource::SHEEP;
|
|
|
+ gHexagons[i].resource = Resource::WHEAT;
|
|
|
+ gHexagons[i + 4].resource = Resource::WOOD;
|
|
|
+ gHexagons[i + 8].resource = Resource::SHEEP;
|
|
|
}
|
|
|
for(int i = 0; i < 3; i++) {
|
|
|
- hexagons[i + 12].resource = Resource::ORE;
|
|
|
- hexagons[i + 15].resource = Resource::CLAY;
|
|
|
+ gHexagons[i + 12].resource = Resource::ORE;
|
|
|
+ gHexagons[i + 15].resource = Resource::CLAY;
|
|
|
}
|
|
|
- hexagons[18].resource = Resource::NOTHING;
|
|
|
+ gHexagons[18].resource = Resource::NOTHING;
|
|
|
|
|
|
- Random r(0);
|
|
|
- for(int i = 0; i < hexagons.getLength(); i++) {
|
|
|
- std::swap(hexagons[i].resource,
|
|
|
- hexagons[r.next(i, hexagons.getLength() - 1)].resource);
|
|
|
+ for(int i = 0; i < gHexagons.getLength(); i++) {
|
|
|
+ std::swap(gHexagons[i].resource,
|
|
|
+ gHexagons[rng.next(i, gHexagons.getLength() - 1)].resource);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -286,17 +539,17 @@ static bool invalidNumbers(int a, int b) {
|
|
|
}
|
|
|
|
|
|
static bool invalidNumbersExist() {
|
|
|
- for(const Corner& c : corners) {
|
|
|
+ for(const Corner& c : gCorners) {
|
|
|
if(c.hexagons.getLength() == 2) {
|
|
|
- int numberA = hexagons[c.hexagons[0]].number;
|
|
|
- int numberB = hexagons[c.hexagons[1]].number;
|
|
|
+ int numberA = gHexagons[c.hexagons[0]].number;
|
|
|
+ int numberB = gHexagons[c.hexagons[1]].number;
|
|
|
if(invalidNumbers(numberA, numberB)) {
|
|
|
return true;
|
|
|
}
|
|
|
} else if(c.hexagons.getLength() == 3) {
|
|
|
- int numberA = hexagons[c.hexagons[0]].number;
|
|
|
- int numberB = hexagons[c.hexagons[1]].number;
|
|
|
- int numberC = hexagons[c.hexagons[2]].number;
|
|
|
+ int numberA = gHexagons[c.hexagons[0]].number;
|
|
|
+ int numberB = gHexagons[c.hexagons[1]].number;
|
|
|
+ int numberC = gHexagons[c.hexagons[2]].number;
|
|
|
if(invalidNumbers(numberA, numberB) ||
|
|
|
invalidNumbers(numberA, numberC) ||
|
|
|
invalidNumbers(numberB, numberC)) {
|
|
@@ -310,93 +563,94 @@ static bool invalidNumbersExist() {
|
|
|
static void initNumbers() {
|
|
|
int index = 0;
|
|
|
int numbers[] = {2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 3, 4, 5, 6, 8, 9, 10, 11};
|
|
|
- for(Hexagon& h : hexagons) {
|
|
|
+ for(Hexagon& h : gHexagons) {
|
|
|
if(h.resource != Resource::NOTHING) {
|
|
|
h.number = numbers[index++];
|
|
|
+ } else {
|
|
|
+ h.number = 0;
|
|
|
}
|
|
|
}
|
|
|
- Random r(1);
|
|
|
- for(int i = 0; i < hexagons.getLength(); i++) {
|
|
|
- if(hexagons[i].resource == Resource::NOTHING) {
|
|
|
+ for(int i = 0; i < gHexagons.getLength(); i++) {
|
|
|
+ if(gHexagons[i].resource == Resource::NOTHING) {
|
|
|
continue;
|
|
|
}
|
|
|
- int ni = r.next(i, hexagons.getLength() - 1);
|
|
|
- while(hexagons[ni].resource == Resource::NOTHING) {
|
|
|
- ni = r.next(i, hexagons.getLength() - 1);
|
|
|
+ int ni = rng.next(i, gHexagons.getLength() - 1);
|
|
|
+ while(gHexagons[ni].resource == Resource::NOTHING) {
|
|
|
+ ni = rng.next(i, gHexagons.getLength() - 1);
|
|
|
}
|
|
|
- std::swap(hexagons[i].number, hexagons[ni].number);
|
|
|
+ std::swap(gHexagons[i].number, gHexagons[ni].number);
|
|
|
}
|
|
|
|
|
|
while(invalidNumbersExist()) {
|
|
|
- int a = r.next(0, hexagons.getLength() - 1);
|
|
|
- while(hexagons[a].resource == Resource::NOTHING) {
|
|
|
- a = r.next(0, hexagons.getLength() - 1);
|
|
|
+ int a = rng.next(0, gHexagons.getLength() - 1);
|
|
|
+ while(gHexagons[a].resource == Resource::NOTHING) {
|
|
|
+ a = rng.next(0, gHexagons.getLength() - 1);
|
|
|
}
|
|
|
|
|
|
- int b = r.next(0, hexagons.getLength() - 1);
|
|
|
- while(hexagons[b].resource == Resource::NOTHING) {
|
|
|
- b = r.next(0, hexagons.getLength() - 1);
|
|
|
+ int b = rng.next(0, gHexagons.getLength() - 1);
|
|
|
+ while(gHexagons[b].resource == Resource::NOTHING) {
|
|
|
+ b = rng.next(0, gHexagons.getLength() - 1);
|
|
|
}
|
|
|
|
|
|
- std::swap(hexagons[a].number, hexagons[b].number);
|
|
|
+ std::swap(gHexagons[a].number, gHexagons[b].number);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
static void initHexagonMid() {
|
|
|
Vector2 mid = MIN_BORDER + AREA * 0.5f;
|
|
|
for(int i = 0; i < 3; i++) {
|
|
|
- hexagons[i].mid = mid - Vector2(WIDTH * static_cast<float>(i - 1),
|
|
|
- -RADIUS * 2.0f - LINE_LENGTH);
|
|
|
- hexagons[i + 16].mid = mid - Vector2(WIDTH * static_cast<float>(i - 1),
|
|
|
- RADIUS * 2.0f + LINE_LENGTH);
|
|
|
+ gHexagons[i].mid = mid - Vector2(WIDTH * static_cast<float>(i - 1),
|
|
|
+ -RADIUS * 2.0f - LINE_LENGTH);
|
|
|
+ gHexagons[i + 16].mid = mid - Vector2(WIDTH * static_cast<float>(i - 1),
|
|
|
+ RADIUS * 2.0f + LINE_LENGTH);
|
|
|
}
|
|
|
for(int i = 3; i < 7; i++) {
|
|
|
- hexagons[i].mid = mid - Vector2(WIDTH * (static_cast<float>(i) - 4.5f),
|
|
|
- -RADIUS - LINE_LENGTH * 0.5f);
|
|
|
- hexagons[i + 9].mid =
|
|
|
+ gHexagons[i].mid = mid - Vector2(WIDTH * (static_cast<float>(i) - 4.5f),
|
|
|
+ -RADIUS - LINE_LENGTH * 0.5f);
|
|
|
+ gHexagons[i + 9].mid =
|
|
|
mid - Vector2(WIDTH * (static_cast<float>(i) - 4.5f),
|
|
|
RADIUS + LINE_LENGTH * 0.5f);
|
|
|
}
|
|
|
for(int i = 7; i < 12; i++) {
|
|
|
- hexagons[i].mid =
|
|
|
+ gHexagons[i].mid =
|
|
|
mid - Vector2(WIDTH * static_cast<float>(i - 9), 0.0f);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
static void initCornerMid() {
|
|
|
- for(int i = 0; i < hexagons.getLength(); i++) {
|
|
|
- corners[i].mid = hexagons[i].getLeftTopCorner();
|
|
|
- corners[i + 19].mid = hexagons[i].getLeftBottomCorner();
|
|
|
- }
|
|
|
- corners[38].mid = hexagons[0].getTopCorner();
|
|
|
- corners[39].mid = hexagons[1].getTopCorner();
|
|
|
- corners[40].mid = hexagons[2].getTopCorner();
|
|
|
- corners[41].mid = hexagons[16].getBottomCorner();
|
|
|
- corners[42].mid = hexagons[17].getBottomCorner();
|
|
|
- corners[43].mid = hexagons[18].getBottomCorner();
|
|
|
- corners[44].mid = hexagons[0].getRightTopCorner();
|
|
|
- corners[45].mid = hexagons[0].getRightBottomCorner();
|
|
|
- corners[46].mid = hexagons[3].getRightTopCorner();
|
|
|
- corners[47].mid = hexagons[3].getRightBottomCorner();
|
|
|
- corners[48].mid = hexagons[7].getRightTopCorner();
|
|
|
- corners[49].mid = hexagons[7].getRightBottomCorner();
|
|
|
- corners[50].mid = hexagons[12].getRightTopCorner();
|
|
|
- corners[51].mid = hexagons[12].getRightBottomCorner();
|
|
|
- corners[52].mid = hexagons[16].getRightTopCorner();
|
|
|
- corners[53].mid = hexagons[16].getRightBottomCorner();
|
|
|
+ for(int i = 0; i < gHexagons.getLength(); i++) {
|
|
|
+ gCorners[i].mid = gHexagons[i].getLeftTopCorner();
|
|
|
+ gCorners[i + 19].mid = gHexagons[i].getLeftBottomCorner();
|
|
|
+ }
|
|
|
+ gCorners[38].mid = gHexagons[0].getTopCorner();
|
|
|
+ gCorners[39].mid = gHexagons[1].getTopCorner();
|
|
|
+ gCorners[40].mid = gHexagons[2].getTopCorner();
|
|
|
+ gCorners[41].mid = gHexagons[16].getBottomCorner();
|
|
|
+ gCorners[42].mid = gHexagons[17].getBottomCorner();
|
|
|
+ gCorners[43].mid = gHexagons[18].getBottomCorner();
|
|
|
+ gCorners[44].mid = gHexagons[0].getRightTopCorner();
|
|
|
+ gCorners[45].mid = gHexagons[0].getRightBottomCorner();
|
|
|
+ gCorners[46].mid = gHexagons[3].getRightTopCorner();
|
|
|
+ gCorners[47].mid = gHexagons[3].getRightBottomCorner();
|
|
|
+ gCorners[48].mid = gHexagons[7].getRightTopCorner();
|
|
|
+ gCorners[49].mid = gHexagons[7].getRightBottomCorner();
|
|
|
+ gCorners[50].mid = gHexagons[12].getRightTopCorner();
|
|
|
+ gCorners[51].mid = gHexagons[12].getRightBottomCorner();
|
|
|
+ gCorners[52].mid = gHexagons[16].getRightTopCorner();
|
|
|
+ gCorners[53].mid = gHexagons[16].getRightBottomCorner();
|
|
|
}
|
|
|
|
|
|
static int findCorner(const Vector2& mid) {
|
|
|
- for(int i = 0; i < corners.getLength(); i++) {
|
|
|
- if((mid - corners[i].mid).squareLength() < 0.00001f) {
|
|
|
+ for(int i = 0; i < gCorners.getLength(); i++) {
|
|
|
+ if((mid - gCorners[i].mid).squareLength() < 0.00001f) {
|
|
|
return i;
|
|
|
}
|
|
|
}
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
-static void initHexagonCorners() {
|
|
|
- for(Hexagon& h : hexagons) {
|
|
|
+static void initHexagongCorners() {
|
|
|
+ for(Hexagon& h : gHexagons) {
|
|
|
h.corners[0] = findCorner(h.getTopCorner());
|
|
|
h.corners[1] = findCorner(h.getBottomCorner());
|
|
|
h.corners[2] = findCorner(h.getLeftTopCorner());
|
|
@@ -404,12 +658,12 @@ static void initHexagonCorners() {
|
|
|
h.corners[4] = findCorner(h.getRightTopCorner());
|
|
|
h.corners[5] = findCorner(h.getRightBottomCorner());
|
|
|
}
|
|
|
- for(int i = 0; i < hexagons.getLength(); i++) {
|
|
|
- for(int c = 0; c < hexagons[c].corners.getLength(); c++) {
|
|
|
- if(hexagons[i].corners[c] == -1) {
|
|
|
+ for(int i = 0; i < gHexagons.getLength(); i++) {
|
|
|
+ for(int c = 0; c < gHexagons[c].corners.getLength(); c++) {
|
|
|
+ if(gHexagons[i].corners[c] == -1) {
|
|
|
LOG_WARNING("Could not find a hexagon corner");
|
|
|
} else {
|
|
|
- if(corners[hexagons[i].corners[c]].hexagons.add(i)) {
|
|
|
+ if(gCorners[gHexagons[i].corners[c]].hexagons.add(i)) {
|
|
|
LOG_WARNING("Corner hexagon overflow");
|
|
|
}
|
|
|
}
|
|
@@ -428,15 +682,15 @@ static bool doesPathExist(const Path& p) {
|
|
|
}
|
|
|
|
|
|
static void initPaths() {
|
|
|
- for(int i = 0; i < corners.getLength(); i++) {
|
|
|
- for(int k = 0; k < corners.getLength(); k++) {
|
|
|
- if(i == k || (corners[i].mid - corners[k].mid).length() >=
|
|
|
+ for(int i = 0; i < gCorners.getLength(); i++) {
|
|
|
+ for(int k = 0; k < gCorners.getLength(); k++) {
|
|
|
+ if(i == k || (gCorners[i].mid - gCorners[k].mid).length() >=
|
|
|
LINE_LENGTH * 1.01f) {
|
|
|
continue;
|
|
|
}
|
|
|
Path p;
|
|
|
- p.from = corners[i].mid;
|
|
|
- p.to = corners[k].mid;
|
|
|
+ p.from = gCorners[i].mid;
|
|
|
+ p.to = gCorners[k].mid;
|
|
|
if(doesPathExist(p)) {
|
|
|
continue;
|
|
|
}
|
|
@@ -448,13 +702,13 @@ static void initPaths() {
|
|
|
}
|
|
|
|
|
|
static void initCornerPaths() {
|
|
|
- for(int c = 0; c < corners.getLength(); c++) {
|
|
|
+ for(int c = 0; c < gCorners.getLength(); c++) {
|
|
|
for(int i = 0; i < gPaths.getLength(); i++) {
|
|
|
Vector2 mid = gPaths[i].getMid();
|
|
|
- if((corners[c].mid - mid).length() >= RADIUS) {
|
|
|
+ if((gCorners[c].mid - mid).length() >= RADIUS) {
|
|
|
continue;
|
|
|
}
|
|
|
- if(corners[c].paths.add(i)) {
|
|
|
+ if(gCorners[c].paths.add(i)) {
|
|
|
LOG_WARNING("Corner path overflow");
|
|
|
}
|
|
|
if(gPaths[i].cornerA == -1) {
|
|
@@ -462,23 +716,28 @@ static void initCornerPaths() {
|
|
|
} else if(gPaths[i].cornerB == -1) {
|
|
|
gPaths[i].cornerB = c;
|
|
|
} else {
|
|
|
- LOG_WARNING("Path got too much corners");
|
|
|
+ LOG_WARNING("Path got too much gCorners");
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
for(const Path& p : gPaths) {
|
|
|
if(p.cornerA == -1 || p.cornerB == -1) {
|
|
|
- LOG_WARNING("Path is missing corners");
|
|
|
+ LOG_WARNING("Path is missing gCorners");
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
static void initPlayers() {
|
|
|
+ static const Color4 PLAYER_COLOR[] = {
|
|
|
+ Color4(255, 0, 0, 255), Color4(0, 0, 255, 255),
|
|
|
+ Color4(255, 128, 0, 255), Color4(255, 255, 255, 255)};
|
|
|
for(int i = 0; i < players.getLength(); i++) {
|
|
|
players[i].id = i;
|
|
|
+ players[i].color = PLAYER_COLOR[i];
|
|
|
}
|
|
|
- players[0].color = Color4(255, 0, 0, 255);
|
|
|
+}
|
|
|
|
|
|
+static void placeStartSettlement() {
|
|
|
for(int i = 0; i < players.getLength(); i++) {
|
|
|
players[i].placeStartSettlement();
|
|
|
}
|
|
@@ -491,22 +750,23 @@ static void init() {
|
|
|
initResources();
|
|
|
initHexagonMid();
|
|
|
initCornerMid();
|
|
|
- initHexagonCorners();
|
|
|
+ initHexagongCorners();
|
|
|
initPaths();
|
|
|
initCornerPaths();
|
|
|
initNumbers();
|
|
|
initPlayers();
|
|
|
+ placeStartSettlement();
|
|
|
}
|
|
|
|
|
|
static void buildBuffer() {
|
|
|
buffer.clear();
|
|
|
- for(const Hexagon& h : hexagons) {
|
|
|
+ for(const Hexagon& h : gHexagons) {
|
|
|
h.addToBuffer();
|
|
|
}
|
|
|
for(const Path& p : gPaths) {
|
|
|
p.addToBuffer();
|
|
|
}
|
|
|
- for(const Corner& c : corners) {
|
|
|
+ for(const Corner& c : gCorners) {
|
|
|
c.addToBuffer();
|
|
|
}
|
|
|
vertexBuffer.setData(buffer, GL::BufferUsage::DYNAMIC);
|
|
@@ -514,11 +774,23 @@ static void buildBuffer() {
|
|
|
|
|
|
static void buildFontBuffer() {
|
|
|
fontBuffer.clear();
|
|
|
- for(const Hexagon& h : hexagons) {
|
|
|
+ for(const Hexagon& h : gHexagons) {
|
|
|
h.addStringToBuffer();
|
|
|
}
|
|
|
- addString(StringBuffer<16>(currentPlayer), Vector2(-0.9f, 0.9f), 0.1f,
|
|
|
- players[currentPlayer].color);
|
|
|
+ for(int i = 0; i < players.getLength(); i++) {
|
|
|
+ StringBuffer<16> s(players[i].getPoints());
|
|
|
+ if(i == currentPlayer) {
|
|
|
+ s.append(" <");
|
|
|
+ }
|
|
|
+ addString(s,
|
|
|
+ Vector2(-0.9f, 0.9f) -
|
|
|
+ Vector2(0.0f, 0.1f * static_cast<float>(i)),
|
|
|
+ 0.1f, players[i].color);
|
|
|
+ }
|
|
|
+
|
|
|
+ addString(StringBuffer<16>(turns), Vector2(0.6f, 0.9f), 0.1f,
|
|
|
+ Color4(255, 255, 255, 255));
|
|
|
+
|
|
|
fontVertexBuffer.setData(fontBuffer, GL::BufferUsage::DYNAMIC);
|
|
|
}
|
|
|
|
|
@@ -527,25 +799,88 @@ static void rebuild() {
|
|
|
buildFontBuffer();
|
|
|
}
|
|
|
|
|
|
-static int waitTicks = 0;
|
|
|
+static void giveResources(int dice) {
|
|
|
+ for(const Hexagon& h : gHexagons) {
|
|
|
+ if(h.number != dice) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ for(int c : h.corners) {
|
|
|
+ const Corner& corner = gCorners[c];
|
|
|
+ if(corner.player < 0 || corner.player >= players.getLength()) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ players[corner.player].giveResource(h.resource, 1 + corner.city);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
static void doTurn() {
|
|
|
+ int dice = rng.next(1, 6) + rng.next(1, 6);
|
|
|
+ LOG_INFO(StringBuffer<256>("Dice: ").append(dice));
|
|
|
+ giveResources(dice);
|
|
|
+ players[currentPlayer].takeTurn();
|
|
|
+}
|
|
|
+
|
|
|
+static int waitTicks = 0;
|
|
|
+
|
|
|
+static bool checkForWinner() {
|
|
|
+ bool won = false;
|
|
|
+ for(const Player& p : players) {
|
|
|
+ if(p.getPoints() >= 10) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return won;
|
|
|
+}
|
|
|
+
|
|
|
+static void restart() {
|
|
|
+ for(Corner& c : gCorners) {
|
|
|
+ c.reset();
|
|
|
+ }
|
|
|
+ for(Path& p : gPaths) {
|
|
|
+ p.reset();
|
|
|
+ }
|
|
|
+ for(Player& p : players) {
|
|
|
+ p.reset();
|
|
|
+ }
|
|
|
+ currentPlayer = 0;
|
|
|
+ turns = 0;
|
|
|
+ placeStartSettlement();
|
|
|
+}
|
|
|
+
|
|
|
+static void restartMap() {
|
|
|
+ restart();
|
|
|
+ initResources();
|
|
|
+ initNumbers();
|
|
|
}
|
|
|
|
|
|
static void tick() {
|
|
|
+ if(Window::Controls::wasReleased(restartButton)) {
|
|
|
+ restart();
|
|
|
+ }
|
|
|
+ if(Window::Controls::wasReleased(restartMapButton)) {
|
|
|
+ restartMap();
|
|
|
+ }
|
|
|
+
|
|
|
waitTicks++;
|
|
|
- if(waitTicks < 100) {
|
|
|
+ if(waitTicks < 1) {
|
|
|
return;
|
|
|
}
|
|
|
waitTicks = 0;
|
|
|
|
|
|
+ if(checkForWinner()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ turns++;
|
|
|
doTurn();
|
|
|
- currentPlayer = (currentPlayer + 1) % players.getLength();
|
|
|
rebuild();
|
|
|
+ currentPlayer = (currentPlayer + 1) % players.getLength();
|
|
|
}
|
|
|
|
|
|
static void render(float lag) {
|
|
|
GL::setViewport(Window::getSize()[0], Window::getSize()[1]);
|
|
|
+ GL::clear();
|
|
|
shader.use();
|
|
|
vertexBuffer.draw(vertexBuffer.getSize() /
|
|
|
static_cast<int>(sizeof(Vector2) + sizeof(Color4)));
|
|
@@ -588,6 +923,10 @@ int main() {
|
|
|
vertexBuffer.init(VertexBuffer::Attributes().addFloat(2).addColor4());
|
|
|
fontVertexBuffer.init(
|
|
|
VertexBuffer::Attributes().addFloat(2).addFloat(2).addColor4());
|
|
|
+ restartButton = Window::Controls::add("Restart");
|
|
|
+ Window::Controls::bindKey(restartButton, GLFW_KEY_R);
|
|
|
+ restartMapButton = Window::Controls::add("Restart Map");
|
|
|
+ Window::Controls::bindKey(restartMapButton, GLFW_KEY_M);
|
|
|
init();
|
|
|
rebuild();
|
|
|
Window::show();
|