Main.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  1. #include <cmath>
  2. #include <cstdio>
  3. #include "data/Array.h"
  4. #include "data/ArrayList.h"
  5. #include "data/HashMap.h"
  6. #include "data/List.h"
  7. #include "math/Vector.h"
  8. #include "rendering/Shader.h"
  9. #include "rendering/Texture.h"
  10. #include "rendering/VertexBuffer.h"
  11. #include "rendering/Window.h"
  12. #include "utils/Color.h"
  13. #include "utils/Logger.h"
  14. #include "utils/Random.h"
  15. static Shader shader;
  16. static Shader fontShader;
  17. static Buffer buffer;
  18. static Buffer fontBuffer;
  19. static VertexBuffer vertexBuffer;
  20. static VertexBuffer fontVertexBuffer;
  21. static Texture fontTexture;
  22. enum class Resource { NOTHING, CLAY, ORE, WHEAT, SHEEP, WOOD };
  23. static const Color4 RESOURCE_COLOR[] = {
  24. Color4(160, 120, 0, 255), Color4(220, 96, 0, 255),
  25. Color4(128, 128, 128, 255), Color4(255, 200, 0, 255),
  26. Color4(40, 200, 40, 255), Color4(0, 120, 0, 255)};
  27. static const Vector2 MIN_BORDER(-0.9f, -0.9f);
  28. static const Vector2 MAX_BORDER(0.9f, 0.9f);
  29. static const Vector2 AREA = MAX_BORDER - MIN_BORDER;
  30. static const float RADIUS =
  31. AREA[0] / (5.0f * 2.0f * cosf(30.0f * static_cast<float>(M_PI) / 180.0f));
  32. static const float LINE_LENGTH =
  33. RADIUS * sinf(30.0f * static_cast<float>(M_PI) / 180.0f) * 2.0f;
  34. static const float WIDTH = AREA[0] / 5.0f;
  35. static void addTriangle(const Vector2 a, const Vector2 b, const Vector2 c,
  36. const Color4& color) {
  37. buffer.add(a).add(color);
  38. buffer.add(b).add(color);
  39. buffer.add(c).add(color);
  40. }
  41. static void addSquare(const Vector2 mid, float size, const Color4& color) {
  42. size *= 0.5f;
  43. Vector2 a = mid + Vector2(-size, size);
  44. Vector2 b = mid + Vector2(size, size);
  45. Vector2 c = mid + Vector2(-size, -size);
  46. Vector2 d = mid + Vector2(size, -size);
  47. addTriangle(a, b, c, color);
  48. addTriangle(b, d, c, color);
  49. }
  50. static void addString(const char* s, const Vector2& pos, float size,
  51. const Color4& color) {
  52. int index = 0;
  53. Vector2 topLeft = pos;
  54. while(s[index] != '\0') {
  55. Vector2 topRight = topLeft + Vector2(size, 0.0f);
  56. Vector2 bottomLeft = topLeft + Vector2(0.0f, -size);
  57. Vector2 bottomRight = topRight + Vector2(0.0f, -size);
  58. int c = s[index];
  59. float minX = static_cast<float>(c % 16) / 16.0f + 1.0f / 128.0f;
  60. float maxX = minX + 6.0f / 128.0f;
  61. float minY = static_cast<float>(c / 16) / 16.0f;
  62. float maxY = minY + 1.0f / 16.0f;
  63. fontBuffer.add(topLeft).add(Vector2(minX, minY)).add(color);
  64. fontBuffer.add(bottomLeft).add(Vector2(minX, maxY)).add(color);
  65. fontBuffer.add(topRight).add(Vector2(maxX, minY)).add(color);
  66. fontBuffer.add(bottomLeft).add(Vector2(minX, maxY)).add(color);
  67. fontBuffer.add(topRight).add(Vector2(maxX, minY)).add(color);
  68. fontBuffer.add(bottomRight).add(Vector2(maxX, maxY)).add(color);
  69. topLeft[0] += size;
  70. index++;
  71. }
  72. }
  73. struct Player {
  74. int id = -1;
  75. Color4 color;
  76. HashMap<int, int> resources;
  77. int getResource(Resource r) const {
  78. const int* i = resources.search(static_cast<int>(r));
  79. return i == nullptr ? 0 : *i;
  80. }
  81. void placeStartSettlement();
  82. };
  83. static Array<Player, 1> players;
  84. static int currentPlayer = 0;
  85. struct Hexagon {
  86. Resource resource = Resource::NOTHING;
  87. Vector2 mid;
  88. Array<int, 6> corners;
  89. int number = 0;
  90. Hexagon() : corners(-1) {
  91. }
  92. void addToBuffer() const {
  93. Color4 color = RESOURCE_COLOR[static_cast<int>(resource)];
  94. float angle = 2.0f * static_cast<float>(M_PI) / 6.0f;
  95. for(int i = 0; i < 6; i++) {
  96. Vector2 a(sinf(angle * static_cast<float>(i)) * RADIUS,
  97. cosf(angle * static_cast<float>(i)) * RADIUS);
  98. Vector2 b(sinf(angle * static_cast<float>(i + 1)) * RADIUS,
  99. cosf(angle * static_cast<float>(i + 1)) * RADIUS);
  100. addTriangle(a + mid, b + mid, mid, color);
  101. }
  102. }
  103. void addStringToBuffer() const {
  104. if(number == 0) {
  105. return;
  106. }
  107. StringBuffer<16> s(number);
  108. constexpr float SIZE = 0.1f;
  109. addString(s,
  110. mid +
  111. Vector2(-SIZE * static_cast<float>(s.getLength()), SIZE) *
  112. 0.5f,
  113. SIZE, Color4(0, 0, 0, 0));
  114. }
  115. Vector2 getTopCorner() const {
  116. return mid + Vector2(0.0f, RADIUS);
  117. }
  118. Vector2 getBottomCorner() const {
  119. return mid - Vector2(0.0f, RADIUS);
  120. }
  121. Vector2 getLeftTopCorner() const {
  122. return mid - Vector2(WIDTH, -LINE_LENGTH) * 0.5f;
  123. }
  124. Vector2 getLeftBottomCorner() const {
  125. return mid - Vector2(WIDTH, LINE_LENGTH) * 0.5f;
  126. }
  127. Vector2 getRightTopCorner() const {
  128. return mid + Vector2(WIDTH, LINE_LENGTH) * 0.5f;
  129. }
  130. Vector2 getRightBottomCorner() const {
  131. return mid + Vector2(WIDTH, -LINE_LENGTH) * 0.5f;
  132. }
  133. };
  134. static Array<Hexagon, 19> hexagons;
  135. struct Corner {
  136. int player = -1;
  137. Vector2 mid;
  138. ArrayList<int, 3> hexagons;
  139. ArrayList<int, 3> paths;
  140. void addToBuffer() const {
  141. if(player < 0 || player >= players.getLength()) {
  142. return;
  143. }
  144. addSquare(mid, 0.05f, players[player].color);
  145. }
  146. bool validForSettlement() const;
  147. };
  148. static Array<Corner, 54> corners;
  149. struct Path {
  150. int player = -1;
  151. Vector2 from;
  152. Vector2 to;
  153. int cornerA = -1;
  154. int cornerB = -1;
  155. void addToBuffer() const {
  156. Vector2 diff = to - from;
  157. Vector2 normal(diff[1], -diff[0]);
  158. normal.normalize();
  159. normal *= 0.01f;
  160. Vector2 a = from + normal;
  161. Vector2 b = from - normal;
  162. Vector2 c = to + normal;
  163. Vector2 d = to - normal;
  164. addTriangle(a, c, b, Color4(255, 255, 255, 255));
  165. addTriangle(c, d, b, Color4(255, 255, 255, 255));
  166. }
  167. Vector2 getMid() const {
  168. return (to + from) * 0.5f;
  169. }
  170. const Corner& getOtherCorner(const Corner& c) const {
  171. if(&(corners[cornerA]) == &c) {
  172. return corners[cornerB];
  173. } else if(&(corners[cornerB]) == &c) {
  174. return corners[cornerA];
  175. }
  176. return c;
  177. }
  178. };
  179. static List<Path> gPaths;
  180. static int getQualityOfNumber(int number) {
  181. if(number < 2 || number > 12 || number == 7) {
  182. return 0;
  183. }
  184. return number < 7 ? number - 1 : 13 - number;
  185. }
  186. static int findBestCorner() {
  187. int current = -1;
  188. int quality = 0;
  189. for(int i = 0; i < corners.getLength(); i++) {
  190. if(!corners[i].validForSettlement()) {
  191. continue;
  192. }
  193. int newQuality = 0;
  194. for(int h : corners[i].hexagons) {
  195. newQuality += getQualityOfNumber(hexagons[h].number);
  196. }
  197. if(newQuality > quality) {
  198. quality = newQuality;
  199. current = i;
  200. }
  201. }
  202. return current;
  203. }
  204. void Player::placeStartSettlement() {
  205. int c = findBestCorner();
  206. if(c == -1) {
  207. LOG_ERROR("Cannot place start settlement");
  208. return;
  209. }
  210. corners[c].player = id;
  211. }
  212. bool Corner::validForSettlement() const {
  213. if(player != -1) {
  214. return false;
  215. }
  216. for(int p : paths) {
  217. if(gPaths[p].getOtherCorner(*this).player != -1) {
  218. return false;
  219. }
  220. }
  221. return true;
  222. }
  223. static void initResources() {
  224. for(int i = 0; i < 4; i++) {
  225. hexagons[i].resource = Resource::WHEAT;
  226. hexagons[i + 4].resource = Resource::WOOD;
  227. hexagons[i + 8].resource = Resource::SHEEP;
  228. }
  229. for(int i = 0; i < 3; i++) {
  230. hexagons[i + 12].resource = Resource::ORE;
  231. hexagons[i + 15].resource = Resource::CLAY;
  232. }
  233. hexagons[18].resource = Resource::NOTHING;
  234. Random r(0);
  235. for(int i = 0; i < hexagons.getLength(); i++) {
  236. std::swap(hexagons[i].resource,
  237. hexagons[r.next(i, hexagons.getLength() - 1)].resource);
  238. }
  239. }
  240. static bool invalidNumbers(int a, int b) {
  241. a = a == 8 ? 6 : a;
  242. b = b == 8 ? 6 : b;
  243. return a == b;
  244. }
  245. static bool invalidNumbersExist() {
  246. for(const Corner& c : corners) {
  247. if(c.hexagons.getLength() == 2) {
  248. int numberA = hexagons[c.hexagons[0]].number;
  249. int numberB = hexagons[c.hexagons[1]].number;
  250. if(invalidNumbers(numberA, numberB)) {
  251. return true;
  252. }
  253. } else if(c.hexagons.getLength() == 3) {
  254. int numberA = hexagons[c.hexagons[0]].number;
  255. int numberB = hexagons[c.hexagons[1]].number;
  256. int numberC = hexagons[c.hexagons[2]].number;
  257. if(invalidNumbers(numberA, numberB) ||
  258. invalidNumbers(numberA, numberC) ||
  259. invalidNumbers(numberB, numberC)) {
  260. return true;
  261. }
  262. }
  263. }
  264. return false;
  265. }
  266. static void initNumbers() {
  267. int index = 0;
  268. int numbers[] = {2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 3, 4, 5, 6, 8, 9, 10, 11};
  269. for(Hexagon& h : hexagons) {
  270. if(h.resource != Resource::NOTHING) {
  271. h.number = numbers[index++];
  272. }
  273. }
  274. Random r(1);
  275. for(int i = 0; i < hexagons.getLength(); i++) {
  276. if(hexagons[i].resource == Resource::NOTHING) {
  277. continue;
  278. }
  279. int ni = r.next(i, hexagons.getLength() - 1);
  280. while(hexagons[ni].resource == Resource::NOTHING) {
  281. ni = r.next(i, hexagons.getLength() - 1);
  282. }
  283. std::swap(hexagons[i].number, hexagons[ni].number);
  284. }
  285. while(invalidNumbersExist()) {
  286. int a = r.next(0, hexagons.getLength() - 1);
  287. while(hexagons[a].resource == Resource::NOTHING) {
  288. a = r.next(0, hexagons.getLength() - 1);
  289. }
  290. int b = r.next(0, hexagons.getLength() - 1);
  291. while(hexagons[b].resource == Resource::NOTHING) {
  292. b = r.next(0, hexagons.getLength() - 1);
  293. }
  294. std::swap(hexagons[a].number, hexagons[b].number);
  295. }
  296. }
  297. static void initHexagonMid() {
  298. Vector2 mid = MIN_BORDER + AREA * 0.5f;
  299. for(int i = 0; i < 3; i++) {
  300. hexagons[i].mid = mid - Vector2(WIDTH * static_cast<float>(i - 1),
  301. -RADIUS * 2.0f - LINE_LENGTH);
  302. hexagons[i + 16].mid = mid - Vector2(WIDTH * static_cast<float>(i - 1),
  303. RADIUS * 2.0f + LINE_LENGTH);
  304. }
  305. for(int i = 3; i < 7; i++) {
  306. hexagons[i].mid = mid - Vector2(WIDTH * (static_cast<float>(i) - 4.5f),
  307. -RADIUS - LINE_LENGTH * 0.5f);
  308. hexagons[i + 9].mid =
  309. mid - Vector2(WIDTH * (static_cast<float>(i) - 4.5f),
  310. RADIUS + LINE_LENGTH * 0.5f);
  311. }
  312. for(int i = 7; i < 12; i++) {
  313. hexagons[i].mid =
  314. mid - Vector2(WIDTH * static_cast<float>(i - 9), 0.0f);
  315. }
  316. }
  317. static void initCornerMid() {
  318. for(int i = 0; i < hexagons.getLength(); i++) {
  319. corners[i].mid = hexagons[i].getLeftTopCorner();
  320. corners[i + 19].mid = hexagons[i].getLeftBottomCorner();
  321. }
  322. corners[38].mid = hexagons[0].getTopCorner();
  323. corners[39].mid = hexagons[1].getTopCorner();
  324. corners[40].mid = hexagons[2].getTopCorner();
  325. corners[41].mid = hexagons[16].getBottomCorner();
  326. corners[42].mid = hexagons[17].getBottomCorner();
  327. corners[43].mid = hexagons[18].getBottomCorner();
  328. corners[44].mid = hexagons[0].getRightTopCorner();
  329. corners[45].mid = hexagons[0].getRightBottomCorner();
  330. corners[46].mid = hexagons[3].getRightTopCorner();
  331. corners[47].mid = hexagons[3].getRightBottomCorner();
  332. corners[48].mid = hexagons[7].getRightTopCorner();
  333. corners[49].mid = hexagons[7].getRightBottomCorner();
  334. corners[50].mid = hexagons[12].getRightTopCorner();
  335. corners[51].mid = hexagons[12].getRightBottomCorner();
  336. corners[52].mid = hexagons[16].getRightTopCorner();
  337. corners[53].mid = hexagons[16].getRightBottomCorner();
  338. }
  339. static int findCorner(const Vector2& mid) {
  340. for(int i = 0; i < corners.getLength(); i++) {
  341. if((mid - corners[i].mid).squareLength() < 0.00001f) {
  342. return i;
  343. }
  344. }
  345. return -1;
  346. }
  347. static void initHexagonCorners() {
  348. for(Hexagon& h : hexagons) {
  349. h.corners[0] = findCorner(h.getTopCorner());
  350. h.corners[1] = findCorner(h.getBottomCorner());
  351. h.corners[2] = findCorner(h.getLeftTopCorner());
  352. h.corners[3] = findCorner(h.getLeftBottomCorner());
  353. h.corners[4] = findCorner(h.getRightTopCorner());
  354. h.corners[5] = findCorner(h.getRightBottomCorner());
  355. }
  356. for(int i = 0; i < hexagons.getLength(); i++) {
  357. for(int c = 0; c < hexagons[c].corners.getLength(); c++) {
  358. if(hexagons[i].corners[c] == -1) {
  359. LOG_WARNING("Could not find a hexagon corner");
  360. } else {
  361. if(corners[hexagons[i].corners[c]].hexagons.add(i)) {
  362. LOG_WARNING("Corner hexagon overflow");
  363. }
  364. }
  365. }
  366. }
  367. }
  368. static bool doesPathExist(const Path& p) {
  369. Vector2 mid = p.getMid();
  370. for(const Path& po : gPaths) {
  371. if((mid - po.getMid()).squareLength() < 0.0001f) {
  372. return true;
  373. }
  374. }
  375. return false;
  376. }
  377. static void initPaths() {
  378. for(int i = 0; i < corners.getLength(); i++) {
  379. for(int k = 0; k < corners.getLength(); k++) {
  380. if(i == k || (corners[i].mid - corners[k].mid).length() >=
  381. LINE_LENGTH * 1.01f) {
  382. continue;
  383. }
  384. Path p;
  385. p.from = corners[i].mid;
  386. p.to = corners[k].mid;
  387. if(doesPathExist(p)) {
  388. continue;
  389. }
  390. gPaths.add(p);
  391. }
  392. }
  393. LOG_INFO(
  394. StringBuffer<256>("Got ").append(gPaths.getLength()).append(" paths"));
  395. }
  396. static void initCornerPaths() {
  397. for(int c = 0; c < corners.getLength(); c++) {
  398. for(int i = 0; i < gPaths.getLength(); i++) {
  399. Vector2 mid = gPaths[i].getMid();
  400. if((corners[c].mid - mid).length() >= RADIUS) {
  401. continue;
  402. }
  403. if(corners[c].paths.add(i)) {
  404. LOG_WARNING("Corner path overflow");
  405. }
  406. if(gPaths[i].cornerA == -1) {
  407. gPaths[i].cornerA = c;
  408. } else if(gPaths[i].cornerB == -1) {
  409. gPaths[i].cornerB = c;
  410. } else {
  411. LOG_WARNING("Path got too much corners");
  412. }
  413. }
  414. }
  415. for(const Path& p : gPaths) {
  416. if(p.cornerA == -1 || p.cornerB == -1) {
  417. LOG_WARNING("Path is missing corners");
  418. }
  419. }
  420. }
  421. static void initPlayers() {
  422. for(int i = 0; i < players.getLength(); i++) {
  423. players[i].id = i;
  424. }
  425. players[0].color = Color4(255, 0, 0, 255);
  426. for(int i = 0; i < players.getLength(); i++) {
  427. players[i].placeStartSettlement();
  428. }
  429. for(int i = players.getLength() - 1; i >= 0; i--) {
  430. players[i].placeStartSettlement();
  431. }
  432. }
  433. static void init() {
  434. initResources();
  435. initHexagonMid();
  436. initCornerMid();
  437. initHexagonCorners();
  438. initPaths();
  439. initCornerPaths();
  440. initNumbers();
  441. initPlayers();
  442. }
  443. static void buildBuffer() {
  444. buffer.clear();
  445. for(const Hexagon& h : hexagons) {
  446. h.addToBuffer();
  447. }
  448. for(const Path& p : gPaths) {
  449. p.addToBuffer();
  450. }
  451. for(const Corner& c : corners) {
  452. c.addToBuffer();
  453. }
  454. vertexBuffer.setData(buffer, GL::BufferUsage::DYNAMIC);
  455. }
  456. static void buildFontBuffer() {
  457. fontBuffer.clear();
  458. for(const Hexagon& h : hexagons) {
  459. h.addStringToBuffer();
  460. }
  461. addString(StringBuffer<16>(currentPlayer), Vector2(-0.9f, 0.9f), 0.1f,
  462. players[currentPlayer].color);
  463. fontVertexBuffer.setData(fontBuffer, GL::BufferUsage::DYNAMIC);
  464. }
  465. static void rebuild() {
  466. buildBuffer();
  467. buildFontBuffer();
  468. }
  469. static int waitTicks = 0;
  470. static void doTurn() {
  471. }
  472. static void tick() {
  473. waitTicks++;
  474. if(waitTicks < 100) {
  475. return;
  476. }
  477. waitTicks = 0;
  478. doTurn();
  479. currentPlayer = (currentPlayer + 1) % players.getLength();
  480. rebuild();
  481. }
  482. static void render(float lag) {
  483. GL::setViewport(Window::getSize()[0], Window::getSize()[1]);
  484. shader.use();
  485. vertexBuffer.draw(vertexBuffer.getSize() /
  486. static_cast<int>(sizeof(Vector2) + sizeof(Color4)));
  487. fontShader.use();
  488. fontTexture.bindTo(0);
  489. GL::enableBlending();
  490. fontVertexBuffer.draw(
  491. vertexBuffer.getSize() /
  492. static_cast<int>(sizeof(Vector2) * 2 + sizeof(Color4)));
  493. (void)lag;
  494. }
  495. static bool shouldRun() {
  496. return !Window::shouldClose();
  497. }
  498. int main() {
  499. Error e = Window::open(
  500. Window::Options(4, 6, IntVector2(400, 300), false, "Catan Simulator"));
  501. if(e.has()) {
  502. e.message.printLine();
  503. return 0;
  504. }
  505. e = shader.compile("resources/vertex.vs", "resources/fragment.fs");
  506. if(e.has()) {
  507. e.message.printLine();
  508. return 0;
  509. }
  510. e = fontShader.compile("resources/font.vs", "resources/font.fs");
  511. if(e.has()) {
  512. e.message.printLine();
  513. return 0;
  514. }
  515. e = fontTexture.load("resources/font8x8.png", 1);
  516. if(e.has()) {
  517. e.message.printLine();
  518. return 0;
  519. }
  520. vertexBuffer.init(VertexBuffer::Attributes().addFloat(2).addColor4());
  521. fontVertexBuffer.init(
  522. VertexBuffer::Attributes().addFloat(2).addFloat(2).addColor4());
  523. init();
  524. rebuild();
  525. Window::show();
  526. Window::run<shouldRun, tick, render>(10'000'000);
  527. return 0;
  528. }