Main.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935
  1. #include <cmath>
  2. #include <cstdio>
  3. #include "GLFW/glfw3.h"
  4. #include "data/Array.h"
  5. #include "data/ArrayList.h"
  6. #include "data/HashMap.h"
  7. #include "data/List.h"
  8. #include "math/Vector.h"
  9. #include "rendering/Shader.h"
  10. #include "rendering/Texture.h"
  11. #include "rendering/VertexBuffer.h"
  12. #include "rendering/Window.h"
  13. #include "utils/Color.h"
  14. #include "utils/Logger.h"
  15. #include "utils/Random.h"
  16. static Shader shader;
  17. static Shader fontShader;
  18. static Buffer buffer;
  19. static Buffer fontBuffer;
  20. static VertexBuffer vertexBuffer;
  21. static VertexBuffer fontVertexBuffer;
  22. static Texture fontTexture;
  23. static Random rng(0);
  24. static int restartButton = 0;
  25. static int restartMapButton = 0;
  26. static int turns = 0;
  27. enum class Resource { NOTHING, CLAY, ORE, WHEAT, SHEEP, WOOD };
  28. static const char* RESOURCE_NAME[] = {"Nothing", "Clay", "Ore",
  29. "Wheat", "Sheep", "Wood"};
  30. static const Color4 RESOURCE_COLOR[] = {
  31. Color4(160, 120, 0, 255), Color4(220, 96, 0, 255),
  32. Color4(128, 128, 128, 255), Color4(255, 200, 0, 255),
  33. Color4(40, 200, 40, 255), Color4(0, 120, 0, 255)};
  34. static const Vector2 MIN_BORDER(-0.9f, -0.9f);
  35. static const Vector2 MAX_BORDER(0.9f, 0.9f);
  36. static const Vector2 AREA = MAX_BORDER - MIN_BORDER;
  37. static const float RADIUS =
  38. AREA[0] / (5.0f * 2.0f * cosf(30.0f * static_cast<float>(M_PI) / 180.0f));
  39. static const float LINE_LENGTH =
  40. RADIUS * sinf(30.0f * static_cast<float>(M_PI) / 180.0f) * 2.0f;
  41. static const float WIDTH = AREA[0] / 5.0f;
  42. static void addTriangle(const Vector2 a, const Vector2 b, const Vector2 c,
  43. const Color4& color) {
  44. buffer.add(a).add(color);
  45. buffer.add(b).add(color);
  46. buffer.add(c).add(color);
  47. }
  48. static void addSquare(const Vector2 mid, float size, const Color4& color) {
  49. size *= 0.5f;
  50. Vector2 a = mid + Vector2(-size, size);
  51. Vector2 b = mid + Vector2(size, size);
  52. Vector2 c = mid + Vector2(-size, -size);
  53. Vector2 d = mid + Vector2(size, -size);
  54. addTriangle(a, b, c, color);
  55. addTriangle(b, d, c, color);
  56. }
  57. static void addString(const char* s, const Vector2& pos, float size,
  58. const Color4& color) {
  59. int index = 0;
  60. Vector2 topLeft = pos;
  61. while(s[index] != '\0') {
  62. Vector2 topRight = topLeft + Vector2(size, 0.0f);
  63. Vector2 bottomLeft = topLeft + Vector2(0.0f, -size);
  64. Vector2 bottomRight = topRight + Vector2(0.0f, -size);
  65. int c = s[index];
  66. float minX = static_cast<float>(c % 16) / 16.0f + 1.0f / 128.0f;
  67. float maxX = minX + 6.0f / 128.0f;
  68. float minY = static_cast<float>(c / 16) / 16.0f;
  69. float maxY = minY + 1.0f / 16.0f;
  70. fontBuffer.add(topLeft).add(Vector2(minX, minY)).add(color);
  71. fontBuffer.add(bottomLeft).add(Vector2(minX, maxY)).add(color);
  72. fontBuffer.add(topRight).add(Vector2(maxX, minY)).add(color);
  73. fontBuffer.add(bottomLeft).add(Vector2(minX, maxY)).add(color);
  74. fontBuffer.add(topRight).add(Vector2(maxX, minY)).add(color);
  75. fontBuffer.add(bottomRight).add(Vector2(maxX, maxY)).add(color);
  76. topLeft[0] += size;
  77. index++;
  78. }
  79. }
  80. static int getQualityOfNumber(int number) {
  81. if(number < 2 || number > 12 || number == 7) {
  82. return 0;
  83. }
  84. return number < 7 ? number - 1 : 13 - number;
  85. }
  86. struct Player {
  87. int id = -1;
  88. Color4 color;
  89. HashMap<int, int> resources;
  90. int settlements = 5;
  91. int paths = 15;
  92. int cities = 4;
  93. Player() {
  94. giveResource(Resource::CLAY, 0);
  95. giveResource(Resource::ORE, 0);
  96. giveResource(Resource::SHEEP, 0);
  97. giveResource(Resource::WHEAT, 0);
  98. giveResource(Resource::WOOD, 0);
  99. }
  100. int getResource(Resource r) const {
  101. const int* i = resources.search(static_cast<int>(r));
  102. return i == nullptr ? 0 : *i;
  103. }
  104. void placeStartSettlement();
  105. void giveResource(Resource r, int amount) {
  106. LOG_INFO(StringBuffer<256>("Player ")
  107. .append(id)
  108. .append(" gets ")
  109. .append(amount)
  110. .append(" ")
  111. .append(RESOURCE_NAME[static_cast<int>(r)]));
  112. int* i = resources.search(static_cast<int>(r));
  113. if(i == nullptr) {
  114. resources.tryEmplace(static_cast<int>(r), amount);
  115. } else {
  116. *i += amount;
  117. }
  118. LOG_INFO(StringBuffer<256>(resources));
  119. }
  120. void removeResource(Resource r, int amount) {
  121. LOG_INFO(StringBuffer<256>("Player ")
  122. .append(id)
  123. .append(" pays ")
  124. .append(amount)
  125. .append(" ")
  126. .append(RESOURCE_NAME[static_cast<int>(r)]));
  127. int* i = resources.search(static_cast<int>(r));
  128. if(i == nullptr) {
  129. return;
  130. }
  131. *i -= amount;
  132. LOG_INFO(StringBuffer<256>(resources));
  133. }
  134. void takeTurn();
  135. bool swap() {
  136. int max = 0;
  137. int maxResource = 0;
  138. for(const auto& entry : resources.entries()) {
  139. if(entry.value > max) {
  140. max = entry.value;
  141. maxResource = entry.getKey();
  142. }
  143. }
  144. int min = max;
  145. int minResource = 0;
  146. for(const auto& entry : resources.entries()) {
  147. if(entry.value < min) {
  148. min = entry.value;
  149. minResource = entry.getKey();
  150. }
  151. }
  152. if(max >= 4) {
  153. removeResource(static_cast<Resource>(maxResource), 4);
  154. giveResource(static_cast<Resource>(minResource), 1);
  155. return true;
  156. }
  157. return false;
  158. }
  159. int getPoints() const;
  160. void reset() {
  161. removeResource(Resource::CLAY, getResource(Resource::CLAY));
  162. removeResource(Resource::ORE, getResource(Resource::ORE));
  163. removeResource(Resource::SHEEP, getResource(Resource::SHEEP));
  164. removeResource(Resource::WHEAT, getResource(Resource::WHEAT));
  165. removeResource(Resource::WOOD, getResource(Resource::WOOD));
  166. settlements = 5;
  167. paths = 15;
  168. cities = 4;
  169. }
  170. };
  171. static Array<Player, 4> players;
  172. static int currentPlayer = 0;
  173. struct Hexagon {
  174. Resource resource = Resource::NOTHING;
  175. Vector2 mid;
  176. Array<int, 6> corners;
  177. int number = 0;
  178. Hexagon() : corners(-1) {
  179. }
  180. void addToBuffer() const {
  181. Color4 color = RESOURCE_COLOR[static_cast<int>(resource)];
  182. float angle = 2.0f * static_cast<float>(M_PI) / 6.0f;
  183. for(int i = 0; i < 6; i++) {
  184. Vector2 a(sinf(angle * static_cast<float>(i)) * RADIUS,
  185. cosf(angle * static_cast<float>(i)) * RADIUS);
  186. Vector2 b(sinf(angle * static_cast<float>(i + 1)) * RADIUS,
  187. cosf(angle * static_cast<float>(i + 1)) * RADIUS);
  188. addTriangle(a + mid, b + mid, mid, color);
  189. }
  190. }
  191. void addStringToBuffer() const {
  192. if(number == 0) {
  193. return;
  194. }
  195. StringBuffer<16> s(number);
  196. constexpr float SIZE = 0.1f;
  197. addString(s,
  198. mid +
  199. Vector2(-SIZE * static_cast<float>(s.getLength()), SIZE) *
  200. 0.5f,
  201. SIZE, Color4(0, 0, 0, 0));
  202. }
  203. Vector2 getTopCorner() const {
  204. return mid + Vector2(0.0f, RADIUS);
  205. }
  206. Vector2 getBottomCorner() const {
  207. return mid - Vector2(0.0f, RADIUS);
  208. }
  209. Vector2 getLeftTopCorner() const {
  210. return mid - Vector2(WIDTH, -LINE_LENGTH) * 0.5f;
  211. }
  212. Vector2 getLeftBottomCorner() const {
  213. return mid - Vector2(WIDTH, LINE_LENGTH) * 0.5f;
  214. }
  215. Vector2 getRightTopCorner() const {
  216. return mid + Vector2(WIDTH, LINE_LENGTH) * 0.5f;
  217. }
  218. Vector2 getRightBottomCorner() const {
  219. return mid + Vector2(WIDTH, -LINE_LENGTH) * 0.5f;
  220. }
  221. };
  222. static Array<Hexagon, 19> gHexagons;
  223. struct Corner {
  224. int player = -1;
  225. bool city = false;
  226. Vector2 mid;
  227. ArrayList<int, 3> hexagons;
  228. ArrayList<int, 3> paths;
  229. void addToBuffer() const {
  230. if(player < 0 || player >= players.getLength()) {
  231. return;
  232. }
  233. addSquare(mid, 0.075f * (static_cast<float>(city) + 1.0f),
  234. players[player].color);
  235. }
  236. bool validForSettlement() const;
  237. bool doesAnyPathBelongToPlayer(int player) const;
  238. bool isFree() const {
  239. return player < 0 || player >= players.getLength();
  240. }
  241. void reset() {
  242. player = -1;
  243. city = false;
  244. }
  245. int getQuality() {
  246. int q = 0;
  247. for(int h : hexagons) {
  248. q += getQualityOfNumber(gHexagons[h].number);
  249. }
  250. return q;
  251. }
  252. };
  253. static Array<Corner, 54> gCorners;
  254. struct Path {
  255. int player = -1;
  256. Vector2 from;
  257. Vector2 to;
  258. int cornerA = -1;
  259. int cornerB = -1;
  260. void addToBuffer() const {
  261. if(player < 0 || player >= players.getLength()) {
  262. return;
  263. }
  264. Vector2 diff = to - from;
  265. Vector2 normal(diff[1], -diff[0]);
  266. normal.normalize();
  267. normal *= 0.01f;
  268. Vector2 a = from + normal;
  269. Vector2 b = from - normal;
  270. Vector2 c = to + normal;
  271. Vector2 d = to - normal;
  272. addTriangle(a, c, b, players[player].color);
  273. addTriangle(c, d, b, players[player].color);
  274. }
  275. Vector2 getMid() const {
  276. return (to + from) * 0.5f;
  277. }
  278. const Corner& getOtherCorner(const Corner& c) const {
  279. if(&(gCorners[cornerA]) == &c) {
  280. return gCorners[cornerB];
  281. } else if(&(gCorners[cornerB]) == &c) {
  282. return gCorners[cornerA];
  283. }
  284. return c;
  285. }
  286. bool isFree() const {
  287. return player < 0 || player >= players.getLength();
  288. }
  289. void reset() {
  290. player = -1;
  291. }
  292. };
  293. static List<Path> gPaths;
  294. static int findBestCorner() {
  295. int current = -1;
  296. int quality = 0;
  297. for(int i = 0; i < gCorners.getLength(); i++) {
  298. if(!gCorners[i].validForSettlement()) {
  299. continue;
  300. }
  301. int newQuality = 0;
  302. for(int h : gCorners[i].hexagons) {
  303. newQuality += getQualityOfNumber(gHexagons[h].number);
  304. }
  305. if(newQuality > quality) {
  306. quality = newQuality;
  307. current = i;
  308. }
  309. }
  310. return current;
  311. }
  312. void Player::placeStartSettlement() {
  313. int c = findBestCorner();
  314. if(c == -1) {
  315. LOG_ERROR("Cannot place start settlement");
  316. return;
  317. }
  318. gCorners[c].player = id;
  319. settlements--;
  320. while(true) {
  321. int p =
  322. gCorners[c].paths[rng.next(0, gCorners[c].paths.getLength() - 1)];
  323. if(gPaths[p].player == -1) {
  324. gPaths[p].player = id;
  325. paths--;
  326. return;
  327. }
  328. }
  329. }
  330. static List<int> getPossiblePaths(int player) {
  331. List<int> paths;
  332. for(int i = 0; i < gPaths.getLength(); i++) {
  333. if(!gPaths[i].isFree()) {
  334. continue;
  335. }
  336. const Corner& cornerA = gCorners[gPaths[i].cornerA];
  337. const Corner& cornerB = gCorners[gPaths[i].cornerB];
  338. if(cornerA.player == player || cornerB.player == player ||
  339. cornerA.doesAnyPathBelongToPlayer(player) ||
  340. cornerB.doesAnyPathBelongToPlayer(player)) {
  341. paths.add(i);
  342. }
  343. }
  344. return paths;
  345. }
  346. static List<int> getPossibleCorners(int player) {
  347. List<int> corners;
  348. for(int i = 0; i < gCorners.getLength(); i++) {
  349. if(!gCorners[i].isFree()) {
  350. continue;
  351. }
  352. if(gCorners[i].doesAnyPathBelongToPlayer(player) &&
  353. gCorners[i].validForSettlement()) {
  354. corners.add(i);
  355. }
  356. }
  357. return corners;
  358. }
  359. static List<int> getPossibleCityCorners(int player) {
  360. List<int> corners;
  361. for(int i = 0; i < gCorners.getLength(); i++) {
  362. if(gCorners[i].player == player && !gCorners[i].city) {
  363. corners.add(i);
  364. }
  365. }
  366. return corners;
  367. }
  368. void Player::takeTurn() {
  369. while(true) {
  370. int clay = getResource(Resource::CLAY);
  371. int ore = getResource(Resource::ORE);
  372. int sheep = getResource(Resource::SHEEP);
  373. int wheat = getResource(Resource::WHEAT);
  374. int wood = getResource(Resource::WOOD);
  375. if(wood >= 1 && clay >= 1 && wheat >= 1 && sheep >= 1 &&
  376. settlements > 0) {
  377. LOG_INFO("I can build a settlement.");
  378. List<int> possibleCorners = getPossibleCorners(id);
  379. if(possibleCorners.getLength() >= 1) {
  380. int index = rng.next(0, possibleCorners.getLength() - 1);
  381. gCorners[possibleCorners[index]].player = id;
  382. removeResource(Resource::CLAY, 1);
  383. removeResource(Resource::WOOD, 1);
  384. removeResource(Resource::SHEEP, 1);
  385. removeResource(Resource::WHEAT, 1);
  386. LOG_INFO("I build a settlement.");
  387. settlements--;
  388. continue;
  389. }
  390. }
  391. if(wood >= 1 && clay >= 1 && paths > 0) {
  392. LOG_INFO("I can build a path.");
  393. List<int> possiblePaths = getPossiblePaths(id);
  394. if(possiblePaths.getLength() >= 1 &&
  395. getPossibleCorners(id).getLength() == 0) {
  396. int index = rng.next(0, possiblePaths.getLength() - 1);
  397. gPaths[possiblePaths[index]].player = id;
  398. removeResource(Resource::CLAY, 1);
  399. removeResource(Resource::WOOD, 1);
  400. LOG_INFO("I build a path.");
  401. paths--;
  402. continue;
  403. }
  404. }
  405. if(ore >= 3 && wheat >= 2 && cities > 0) {
  406. LOG_INFO("I can build a city.");
  407. List<int> possibleCityCorners = getPossibleCityCorners(id);
  408. if(possibleCityCorners.getLength() >= 1) {
  409. int bestIndex = -1;
  410. int quality = -1;
  411. for(int index : possibleCityCorners) {
  412. int newQuality = gCorners[index].getQuality();
  413. if(newQuality > quality) {
  414. bestIndex = index;
  415. quality = newQuality;
  416. }
  417. }
  418. gCorners[bestIndex].city = true;
  419. removeResource(Resource::ORE, 3);
  420. removeResource(Resource::WHEAT, 2);
  421. LOG_INFO("I build a city.");
  422. cities--;
  423. settlements++;
  424. continue;
  425. }
  426. }
  427. if(swap()) {
  428. continue;
  429. }
  430. break;
  431. }
  432. }
  433. bool Corner::doesAnyPathBelongToPlayer(int playerId) const {
  434. for(int p : paths) {
  435. if(gPaths[p].player == playerId) {
  436. return true;
  437. }
  438. }
  439. return false;
  440. }
  441. bool Corner::validForSettlement() const {
  442. if(!isFree()) {
  443. return false;
  444. }
  445. for(int p : paths) {
  446. if(!gPaths[p].getOtherCorner(*this).isFree()) {
  447. return false;
  448. }
  449. }
  450. return true;
  451. }
  452. int Player::getPoints() const {
  453. return (4 - cities) * 2 + 5 - settlements;
  454. }
  455. static void initResources() {
  456. for(int i = 0; i < 4; i++) {
  457. gHexagons[i].resource = Resource::WHEAT;
  458. gHexagons[i + 4].resource = Resource::WOOD;
  459. gHexagons[i + 8].resource = Resource::SHEEP;
  460. }
  461. for(int i = 0; i < 3; i++) {
  462. gHexagons[i + 12].resource = Resource::ORE;
  463. gHexagons[i + 15].resource = Resource::CLAY;
  464. }
  465. gHexagons[18].resource = Resource::NOTHING;
  466. for(int i = 0; i < gHexagons.getLength(); i++) {
  467. std::swap(gHexagons[i].resource,
  468. gHexagons[rng.next(i, gHexagons.getLength() - 1)].resource);
  469. }
  470. }
  471. static bool invalidNumbers(int a, int b) {
  472. a = a == 8 ? 6 : a;
  473. b = b == 8 ? 6 : b;
  474. return a == b;
  475. }
  476. static bool invalidNumbersExist() {
  477. for(const Corner& c : gCorners) {
  478. if(c.hexagons.getLength() == 2) {
  479. int numberA = gHexagons[c.hexagons[0]].number;
  480. int numberB = gHexagons[c.hexagons[1]].number;
  481. if(invalidNumbers(numberA, numberB)) {
  482. return true;
  483. }
  484. } else if(c.hexagons.getLength() == 3) {
  485. int numberA = gHexagons[c.hexagons[0]].number;
  486. int numberB = gHexagons[c.hexagons[1]].number;
  487. int numberC = gHexagons[c.hexagons[2]].number;
  488. if(invalidNumbers(numberA, numberB) ||
  489. invalidNumbers(numberA, numberC) ||
  490. invalidNumbers(numberB, numberC)) {
  491. return true;
  492. }
  493. }
  494. }
  495. return false;
  496. }
  497. static void initNumbers() {
  498. int index = 0;
  499. int numbers[] = {2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 3, 4, 5, 6, 8, 9, 10, 11};
  500. for(Hexagon& h : gHexagons) {
  501. if(h.resource != Resource::NOTHING) {
  502. h.number = numbers[index++];
  503. } else {
  504. h.number = 0;
  505. }
  506. }
  507. for(int i = 0; i < gHexagons.getLength(); i++) {
  508. if(gHexagons[i].resource == Resource::NOTHING) {
  509. continue;
  510. }
  511. int ni = rng.next(i, gHexagons.getLength() - 1);
  512. while(gHexagons[ni].resource == Resource::NOTHING) {
  513. ni = rng.next(i, gHexagons.getLength() - 1);
  514. }
  515. std::swap(gHexagons[i].number, gHexagons[ni].number);
  516. }
  517. while(invalidNumbersExist()) {
  518. int a = rng.next(0, gHexagons.getLength() - 1);
  519. while(gHexagons[a].resource == Resource::NOTHING) {
  520. a = rng.next(0, gHexagons.getLength() - 1);
  521. }
  522. int b = rng.next(0, gHexagons.getLength() - 1);
  523. while(gHexagons[b].resource == Resource::NOTHING) {
  524. b = rng.next(0, gHexagons.getLength() - 1);
  525. }
  526. std::swap(gHexagons[a].number, gHexagons[b].number);
  527. }
  528. }
  529. static void initHexagonMid() {
  530. Vector2 mid = MIN_BORDER + AREA * 0.5f;
  531. for(int i = 0; i < 3; i++) {
  532. gHexagons[i].mid = mid - Vector2(WIDTH * static_cast<float>(i - 1),
  533. -RADIUS * 2.0f - LINE_LENGTH);
  534. gHexagons[i + 16].mid = mid - Vector2(WIDTH * static_cast<float>(i - 1),
  535. RADIUS * 2.0f + LINE_LENGTH);
  536. }
  537. for(int i = 3; i < 7; i++) {
  538. gHexagons[i].mid = mid - Vector2(WIDTH * (static_cast<float>(i) - 4.5f),
  539. -RADIUS - LINE_LENGTH * 0.5f);
  540. gHexagons[i + 9].mid =
  541. mid - Vector2(WIDTH * (static_cast<float>(i) - 4.5f),
  542. RADIUS + LINE_LENGTH * 0.5f);
  543. }
  544. for(int i = 7; i < 12; i++) {
  545. gHexagons[i].mid =
  546. mid - Vector2(WIDTH * static_cast<float>(i - 9), 0.0f);
  547. }
  548. }
  549. static void initCornerMid() {
  550. for(int i = 0; i < gHexagons.getLength(); i++) {
  551. gCorners[i].mid = gHexagons[i].getLeftTopCorner();
  552. gCorners[i + 19].mid = gHexagons[i].getLeftBottomCorner();
  553. }
  554. gCorners[38].mid = gHexagons[0].getTopCorner();
  555. gCorners[39].mid = gHexagons[1].getTopCorner();
  556. gCorners[40].mid = gHexagons[2].getTopCorner();
  557. gCorners[41].mid = gHexagons[16].getBottomCorner();
  558. gCorners[42].mid = gHexagons[17].getBottomCorner();
  559. gCorners[43].mid = gHexagons[18].getBottomCorner();
  560. gCorners[44].mid = gHexagons[0].getRightTopCorner();
  561. gCorners[45].mid = gHexagons[0].getRightBottomCorner();
  562. gCorners[46].mid = gHexagons[3].getRightTopCorner();
  563. gCorners[47].mid = gHexagons[3].getRightBottomCorner();
  564. gCorners[48].mid = gHexagons[7].getRightTopCorner();
  565. gCorners[49].mid = gHexagons[7].getRightBottomCorner();
  566. gCorners[50].mid = gHexagons[12].getRightTopCorner();
  567. gCorners[51].mid = gHexagons[12].getRightBottomCorner();
  568. gCorners[52].mid = gHexagons[16].getRightTopCorner();
  569. gCorners[53].mid = gHexagons[16].getRightBottomCorner();
  570. }
  571. static int findCorner(const Vector2& mid) {
  572. for(int i = 0; i < gCorners.getLength(); i++) {
  573. if((mid - gCorners[i].mid).squareLength() < 0.00001f) {
  574. return i;
  575. }
  576. }
  577. return -1;
  578. }
  579. static void initHexagongCorners() {
  580. for(Hexagon& h : gHexagons) {
  581. h.corners[0] = findCorner(h.getTopCorner());
  582. h.corners[1] = findCorner(h.getBottomCorner());
  583. h.corners[2] = findCorner(h.getLeftTopCorner());
  584. h.corners[3] = findCorner(h.getLeftBottomCorner());
  585. h.corners[4] = findCorner(h.getRightTopCorner());
  586. h.corners[5] = findCorner(h.getRightBottomCorner());
  587. }
  588. for(int i = 0; i < gHexagons.getLength(); i++) {
  589. for(int c = 0; c < gHexagons[c].corners.getLength(); c++) {
  590. if(gHexagons[i].corners[c] == -1) {
  591. LOG_WARNING("Could not find a hexagon corner");
  592. } else {
  593. if(gCorners[gHexagons[i].corners[c]].hexagons.add(i)) {
  594. LOG_WARNING("Corner hexagon overflow");
  595. }
  596. }
  597. }
  598. }
  599. }
  600. static bool doesPathExist(const Path& p) {
  601. Vector2 mid = p.getMid();
  602. for(const Path& po : gPaths) {
  603. if((mid - po.getMid()).squareLength() < 0.0001f) {
  604. return true;
  605. }
  606. }
  607. return false;
  608. }
  609. static void initPaths() {
  610. for(int i = 0; i < gCorners.getLength(); i++) {
  611. for(int k = 0; k < gCorners.getLength(); k++) {
  612. if(i == k || (gCorners[i].mid - gCorners[k].mid).length() >=
  613. LINE_LENGTH * 1.01f) {
  614. continue;
  615. }
  616. Path p;
  617. p.from = gCorners[i].mid;
  618. p.to = gCorners[k].mid;
  619. if(doesPathExist(p)) {
  620. continue;
  621. }
  622. gPaths.add(p);
  623. }
  624. }
  625. LOG_INFO(
  626. StringBuffer<256>("Got ").append(gPaths.getLength()).append(" paths"));
  627. }
  628. static void initCornerPaths() {
  629. for(int c = 0; c < gCorners.getLength(); c++) {
  630. for(int i = 0; i < gPaths.getLength(); i++) {
  631. Vector2 mid = gPaths[i].getMid();
  632. if((gCorners[c].mid - mid).length() >= RADIUS) {
  633. continue;
  634. }
  635. if(gCorners[c].paths.add(i)) {
  636. LOG_WARNING("Corner path overflow");
  637. }
  638. if(gPaths[i].cornerA == -1) {
  639. gPaths[i].cornerA = c;
  640. } else if(gPaths[i].cornerB == -1) {
  641. gPaths[i].cornerB = c;
  642. } else {
  643. LOG_WARNING("Path got too much gCorners");
  644. }
  645. }
  646. }
  647. for(const Path& p : gPaths) {
  648. if(p.cornerA == -1 || p.cornerB == -1) {
  649. LOG_WARNING("Path is missing gCorners");
  650. }
  651. }
  652. }
  653. static void initPlayers() {
  654. static const Color4 PLAYER_COLOR[] = {
  655. Color4(255, 0, 0, 255), Color4(0, 0, 255, 255),
  656. Color4(255, 128, 0, 255), Color4(255, 255, 255, 255)};
  657. for(int i = 0; i < players.getLength(); i++) {
  658. players[i].id = i;
  659. players[i].color = PLAYER_COLOR[i];
  660. }
  661. }
  662. static void placeStartSettlement() {
  663. for(int i = 0; i < players.getLength(); i++) {
  664. players[i].placeStartSettlement();
  665. }
  666. for(int i = players.getLength() - 1; i >= 0; i--) {
  667. players[i].placeStartSettlement();
  668. }
  669. }
  670. static void init() {
  671. initResources();
  672. initHexagonMid();
  673. initCornerMid();
  674. initHexagongCorners();
  675. initPaths();
  676. initCornerPaths();
  677. initNumbers();
  678. initPlayers();
  679. placeStartSettlement();
  680. }
  681. static void buildBuffer() {
  682. buffer.clear();
  683. for(const Hexagon& h : gHexagons) {
  684. h.addToBuffer();
  685. }
  686. for(const Path& p : gPaths) {
  687. p.addToBuffer();
  688. }
  689. for(const Corner& c : gCorners) {
  690. c.addToBuffer();
  691. }
  692. vertexBuffer.setData(buffer, GL::BufferUsage::DYNAMIC);
  693. }
  694. static void buildFontBuffer() {
  695. fontBuffer.clear();
  696. for(const Hexagon& h : gHexagons) {
  697. h.addStringToBuffer();
  698. }
  699. for(int i = 0; i < players.getLength(); i++) {
  700. StringBuffer<16> s(players[i].getPoints());
  701. if(i == currentPlayer) {
  702. s.append(" <");
  703. }
  704. addString(s,
  705. Vector2(-0.9f, 0.9f) -
  706. Vector2(0.0f, 0.1f * static_cast<float>(i)),
  707. 0.1f, players[i].color);
  708. }
  709. addString(StringBuffer<16>(turns), Vector2(0.6f, 0.9f), 0.1f,
  710. Color4(255, 255, 255, 255));
  711. fontVertexBuffer.setData(fontBuffer, GL::BufferUsage::DYNAMIC);
  712. }
  713. static void rebuild() {
  714. buildBuffer();
  715. buildFontBuffer();
  716. }
  717. static void giveResources(int dice) {
  718. for(const Hexagon& h : gHexagons) {
  719. if(h.number != dice) {
  720. continue;
  721. }
  722. for(int c : h.corners) {
  723. const Corner& corner = gCorners[c];
  724. if(corner.player < 0 || corner.player >= players.getLength()) {
  725. continue;
  726. }
  727. players[corner.player].giveResource(h.resource, 1 + corner.city);
  728. }
  729. }
  730. }
  731. static void doTurn() {
  732. int dice = rng.next(1, 6) + rng.next(1, 6);
  733. LOG_INFO(StringBuffer<256>("Dice: ").append(dice));
  734. giveResources(dice);
  735. players[currentPlayer].takeTurn();
  736. }
  737. static int waitTicks = 0;
  738. static bool checkForWinner() {
  739. bool won = false;
  740. for(const Player& p : players) {
  741. if(p.getPoints() >= 10) {
  742. return true;
  743. }
  744. }
  745. return won;
  746. }
  747. static void restart() {
  748. for(Corner& c : gCorners) {
  749. c.reset();
  750. }
  751. for(Path& p : gPaths) {
  752. p.reset();
  753. }
  754. for(Player& p : players) {
  755. p.reset();
  756. }
  757. currentPlayer = 0;
  758. turns = 0;
  759. placeStartSettlement();
  760. }
  761. static void restartMap() {
  762. restart();
  763. initResources();
  764. initNumbers();
  765. }
  766. static void tick() {
  767. if(Window::Controls::wasReleased(restartButton)) {
  768. restart();
  769. }
  770. if(Window::Controls::wasReleased(restartMapButton)) {
  771. restartMap();
  772. }
  773. waitTicks++;
  774. if(waitTicks < 1) {
  775. return;
  776. }
  777. waitTicks = 0;
  778. if(checkForWinner()) {
  779. return;
  780. }
  781. turns++;
  782. doTurn();
  783. rebuild();
  784. currentPlayer = (currentPlayer + 1) % players.getLength();
  785. }
  786. static void render(float lag) {
  787. GL::setViewport(Window::getSize()[0], Window::getSize()[1]);
  788. GL::clear();
  789. shader.use();
  790. vertexBuffer.draw(vertexBuffer.getSize() /
  791. static_cast<int>(sizeof(Vector2) + sizeof(Color4)));
  792. fontShader.use();
  793. fontTexture.bindTo(0);
  794. GL::enableBlending();
  795. fontVertexBuffer.draw(
  796. vertexBuffer.getSize() /
  797. static_cast<int>(sizeof(Vector2) * 2 + sizeof(Color4)));
  798. (void)lag;
  799. }
  800. static bool shouldRun() {
  801. return !Window::shouldClose();
  802. }
  803. int main() {
  804. Error e = Window::open(
  805. Window::Options(4, 6, IntVector2(400, 300), false, "Catan Simulator"));
  806. if(e.has()) {
  807. e.message.printLine();
  808. return 0;
  809. }
  810. e = shader.compile("resources/vertex.vs", "resources/fragment.fs");
  811. if(e.has()) {
  812. e.message.printLine();
  813. return 0;
  814. }
  815. e = fontShader.compile("resources/font.vs", "resources/font.fs");
  816. if(e.has()) {
  817. e.message.printLine();
  818. return 0;
  819. }
  820. e = fontTexture.load("resources/font8x8.png", 1);
  821. if(e.has()) {
  822. e.message.printLine();
  823. return 0;
  824. }
  825. vertexBuffer.init(VertexBuffer::Attributes().addFloat(2).addColor4());
  826. fontVertexBuffer.init(
  827. VertexBuffer::Attributes().addFloat(2).addFloat(2).addColor4());
  828. restartButton = Window::Controls::add("Restart");
  829. Window::Controls::bindKey(restartButton, GLFW_KEY_R);
  830. restartMapButton = Window::Controls::add("Restart Map");
  831. Window::Controls::bindKey(restartMapButton, GLFW_KEY_M);
  832. init();
  833. rebuild();
  834. Window::show();
  835. Window::run<shouldRun, tick, render>(10'000'000);
  836. return 0;
  837. }