Advertisement
Guest User

Untitled

a guest
Feb 19th, 2019
75
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.86 KB | None | 0 0
  1. #include "GameOfLife.h"
  2. #include "WorldRenderer.h"
  3. #include <iostream>
  4. #include <SFML/Graphics.hpp>
  5.  
  6. using namespace std;
  7.  
  8. static const int WORLD_SIZE_X = 256;
  9. static const int WORLD_SIZE_Y = 256;
  10.  
  11. int main()
  12. {
  13. // create the window
  14. sf::RenderWindow window(sf::VideoMode(256, 256), "Game of Life");
  15. // scale the image up 2x size
  16. window.setSize(sf::Vector2u(512, 512));
  17.  
  18. // disable vsync and uncap framerate limit
  19. window.setVerticalSyncEnabled(false);
  20. window.setFramerateLimit(0);
  21.  
  22. // Create the game
  23. GameOfLife game(sf::Vector2i(WORLD_SIZE_X, WORLD_SIZE_Y));
  24.  
  25. // Create a world renderer
  26. WorldRenderer worldRenderer;
  27.  
  28. // Track if mouse button is being held down
  29. bool mouseHeld = false;
  30.  
  31. // run the program as long as the window is open
  32. while (window.isOpen())
  33. {
  34. // check all the window's events that were triggered since the last iteration of the loop
  35. sf::Event event;
  36. while (window.pollEvent(event))
  37. {
  38. // "close requested" event: we close the window
  39. if (event.type == sf::Event::Closed)
  40. window.close();
  41.  
  42. // capture if the user is holding left mouse button down
  43. if (event.type == sf::Event::MouseButtonPressed)
  44. {
  45. if (event.mouseButton.button == sf::Mouse::Left)
  46. mouseHeld = true;
  47. } else if (event.type == sf::Event::MouseButtonReleased)
  48. {
  49. if (event.mouseButton.button == sf::Mouse::Left)
  50. mouseHeld = false;
  51. }
  52. }
  53.  
  54. // clear the window with black color
  55. window.clear(sf::Color::Black);
  56.  
  57. // if left mouse button held down then make cells under cursor alive and pause simulation
  58. if (mouseHeld) {
  59. auto mousePosition = sf::Mouse::getPosition(window);
  60.  
  61. // normalize mouse pos
  62. int x = (mousePosition.x / 512.0f) * WORLD_SIZE_X;
  63. int y = (mousePosition.y / 512.0f) * WORLD_SIZE_Y;
  64.  
  65. // set cell under cursor to alive
  66. game.setCell(x, y, true);
  67. }
  68. else {
  69. // update the game world
  70. game.update();
  71. }
  72.  
  73. // render the game
  74. worldRenderer.render(window, game);
  75.  
  76. // end the current frame
  77. window.display();
  78. }
  79.  
  80. return 0;
  81. }
  82.  
  83. #pragma once
  84.  
  85. #include <vector>
  86. #include <SFML/Graphics.hpp>
  87. #include "Cell.h"
  88.  
  89. class GameOfLife
  90. {
  91. public:
  92. GameOfLife(sf::Vector2i size);
  93.  
  94. virtual ~GameOfLife() = default;
  95.  
  96. // Returns a reference to the cell value at the given grid position.
  97. uint8_t & getCell(int x, int y);
  98.  
  99. // Returns a vector of the given cell's grid position by it's cell index.
  100. sf::Vector2i get2D(int index);
  101.  
  102. // Updates the state of the game world by one tick.
  103. void update();
  104.  
  105. // Update the cells from position start (inclusive) to position end (exclusive).
  106. std::vector<Cell> GameOfLife::doUpdate(int start, int end, int coreIdx);
  107.  
  108. // Set the value of the cell at the given grid position to the given alive state.
  109. void setCell(int x, int y, bool alive);
  110.  
  111. // A cache of all the alive cells at the end of the update() call.
  112. std::vector<Cell> aliveCells;
  113.  
  114. // The maximum amount of threads to be used for update().
  115. const int maxThreads;
  116.  
  117. // Represents the width and height of the simulated world.
  118. sf::Vector2i worldSize;
  119.  
  120. // Returns a color to use for cells/backgrounds based on the thread ID #.
  121. sf::Color getThreadColor(int index);
  122.  
  123. private:
  124. // A 1D representation of the 2D grid that is the world.
  125. std::vector<uint8_t> world;
  126.  
  127. // A buffer where the next world state is prepared, swapped with world at end of update().
  128. std::vector<uint8_t> worldBuffer;
  129. };
  130.  
  131. #include "GameOfLife.h"
  132. #include "Cell.h"
  133. #include <iostream>
  134. #include <vector>
  135. #include <math.h>
  136. #include <thread>
  137. #include <mutex>
  138. #include <future>
  139. #include <chrono>
  140.  
  141. GameOfLife::GameOfLife(sf::Vector2i size) : worldSize(size), world(size.x * size.y, false), worldBuffer(world), maxThreads(std::thread::hardware_concurrency())
  142. {
  143. aliveCells.reserve(size.x * size.y); // reserve space for worst-case (all cells are alive)
  144.  
  145. // place an "acorn"
  146. int midX = worldSize.x / 2;
  147. int midY = worldSize.y / 2;
  148. getCell(midX + 0, midY + 0) = 1;
  149. getCell(midX + 1, midY + 0) = 1;
  150. getCell(midX + 4, midY + 0) = 1;
  151. getCell(midX + 5, midY + 0) = 1;
  152. getCell(midX + 6, midY + 0) = 1;
  153. getCell(midX + 3, midY + 1) = 1;
  154. getCell(midX + 1, midY + 2) = 1;
  155. }
  156.  
  157. uint8_t& GameOfLife::getCell(int x, int y)
  158. {
  159. return world[y * worldSize.x + x];
  160. }
  161.  
  162. sf::Vector2i GameOfLife::get2D(int index)
  163. {
  164. int y = index / worldSize.x;
  165. int x = index % worldSize.x;
  166. return { x, y };
  167. }
  168.  
  169. sf::Color GameOfLife::getThreadColor(int index)
  170. {
  171. switch (index % 4) {
  172. case 0:
  173. return sf::Color::Red;
  174. break;
  175. case 1:
  176. return sf::Color::Green;
  177. break;
  178. case 2:
  179. return sf::Color::Blue;
  180. break;
  181. case 3:
  182. return sf::Color::Yellow;
  183. break;
  184. }
  185. }
  186.  
  187. std::vector<Cell> GameOfLife::doUpdate(int start, int end, int coreIdx)
  188. {
  189. std::vector<Cell> aliveCells;
  190. aliveCells.reserve(end - start); // reserve space for worst case (all alive cells)
  191.  
  192. for (int i = start; i < end; i++)
  193. {
  194. auto pos = get2D(i);
  195.  
  196. // # of alive neighbors
  197. int aliveCount = 0;
  198.  
  199. // check all 8 surrounding neighbors
  200. for (int nX = -1; nX <= 1; nX++) // nX = -1, 0, 1
  201. {
  202. for (int nY = -1; nY <= 1; nY++) // nY = -1, 0, 1
  203. {
  204. // make sure to skip the current cell!
  205. if (nX == 0 && nY == 0)
  206. continue;
  207.  
  208. // wrap around to other side if neighbor would be outside world
  209. int newX = (nX + pos.x + worldSize.x) % worldSize.x;
  210. int newY = (nY + pos.y + worldSize.y) % worldSize.y;
  211.  
  212. aliveCount += getCell(newX, newY);
  213. }
  214. }
  215.  
  216. // Evaluate game rules on current cell
  217. bool dies = aliveCount == 2 || aliveCount == 3;
  218. bool lives = aliveCount == 3;
  219. worldBuffer[i] = world[i] ? dies : lives;
  220.  
  221. // if the cell's alive push it into the vector
  222. if (worldBuffer[i])
  223. aliveCells.push_back(Cell(pos, getThreadColor(coreIdx)));
  224. }
  225.  
  226. return aliveCells;
  227. }
  228.  
  229. void GameOfLife::update()
  230. {
  231. // clear aliveCells cache
  232. aliveCells.clear();
  233.  
  234. // divide the grid into horizontal slices
  235. int chunkSize = (worldSize.x * worldSize.y) / maxThreads;
  236.  
  237. // split the work into threads
  238. std::vector<std::future<std::vector<Cell>>> asyncTasks;
  239. for (int i = 0; i < maxThreads; i++)
  240. {
  241. int start = i * chunkSize;
  242.  
  243. int end;
  244. if (i == maxThreads - 1) // if this is the last thread, endPos will be set to cover remaining "height"
  245. end = worldSize.x * worldSize.y;
  246. else
  247. end = (i + 1) * chunkSize;
  248.  
  249. asyncTasks.push_back(
  250. std::async(std::launch::async, [this, start, end, i] { return this->doUpdate(start, end, i); })
  251. );
  252. }
  253.  
  254. // Wait until all async tasks are finished
  255. for (auto&& task : asyncTasks) { // TODO Why use 'auto&&'?
  256. auto aliveCellsPartial = task.get();
  257. aliveCells.insert(std::end(aliveCells), std::begin(aliveCellsPartial), std::end(aliveCellsPartial));
  258. }
  259.  
  260. // apply updates
  261. world.swap(worldBuffer);
  262. }
  263.  
  264. void GameOfLife::setCell(int x, int y, bool alive)
  265. {
  266. // constrain x and y
  267. x = std::max(std::min(x, (int) worldSize.x - 1), 0);
  268. y = std::max(std::min(y, (int) worldSize.y - 1), 0);
  269. getCell(x, y) = alive;
  270. aliveCells.push_back(Cell(sf::Vector2i(x, y), sf::Color::White));
  271. }
  272.  
  273. #pragma once
  274.  
  275. #include <SFML/Graphics.hpp>
  276. #include <vector>
  277. #include "GameOfLife.h"
  278.  
  279. class WorldRenderer
  280. {
  281. public:
  282. WorldRenderer();
  283.  
  284. ~WorldRenderer();
  285.  
  286. // Renders the given game to the given window.
  287. void render(sf::RenderWindow& window, GameOfLife& world);
  288.  
  289. private:
  290. // Vertex points for the pending draw call.
  291. std::vector<sf::Vertex> m_vertexPoints;
  292.  
  293. // Adds a cell-sized quad in the "grid position" specified.
  294. void addQuad(int gridX, int gridY, sf::Color color);
  295.  
  296. // Adds a darker colored quad in the given coordinates.
  297. void addBackgroundQuad(sf::Vector2f topLeft, sf::Vector2f bottomRight, sf::Color color);
  298.  
  299. // Renders the background colors which correspond to the thread ID and the cells they are updating.
  300. void renderBackgrounds(sf::RenderWindow& window, GameOfLife& world);
  301.  
  302. // Returns a darker variant of the given color.
  303. sf::Color darkenColor(sf::Color input);
  304. };
  305.  
  306. #include "WorldRenderer.h"
  307.  
  308. WorldRenderer::WorldRenderer()
  309. {
  310. }
  311.  
  312.  
  313. WorldRenderer::~WorldRenderer()
  314. {
  315. }
  316.  
  317. void WorldRenderer::addQuad(int gridX, int gridY, sf::Color color)
  318. {
  319. sf::Vertex topLeft;
  320. sf::Vertex topRight;
  321. sf::Vertex bottomLeft;
  322. sf::Vertex bottomRight;
  323.  
  324. float gridXFloat = gridX * 1.0f;
  325. float gridYFloat = gridY * 1.0f;
  326.  
  327. topLeft.position = { gridXFloat, gridYFloat };
  328. topRight.position = { gridXFloat + 1, gridYFloat };
  329. bottomLeft.position = { gridXFloat, gridYFloat + 1 };
  330. bottomRight.position = { gridXFloat + 1, gridYFloat + 1 };
  331.  
  332. topLeft.color = color;
  333. topRight.color = color;
  334. bottomLeft.color = color;
  335. bottomRight.color = color;
  336.  
  337. m_vertexPoints.push_back(topLeft);
  338. m_vertexPoints.push_back(bottomLeft);
  339. m_vertexPoints.push_back(bottomRight);
  340. m_vertexPoints.push_back(topRight);
  341. }
  342.  
  343. void WorldRenderer::addBackgroundQuad(sf::Vector2f topLeft, sf::Vector2f bottomRight, sf::Color color)
  344. {
  345. sf::Vertex vTopLeft;
  346. sf::Vertex vTopRight;
  347. sf::Vertex vBottomLeft;
  348. sf::Vertex vBottomRight;
  349.  
  350. vTopLeft.position = topLeft;
  351. vTopRight.position = { bottomRight.x, topLeft.y };
  352. vBottomLeft.position = { topLeft.x, bottomRight.y };
  353. vBottomRight.position = bottomRight;
  354.  
  355. vTopLeft.color = color;
  356. vTopRight.color = color;
  357. vBottomLeft.color = color;
  358. vBottomRight.color = color;
  359.  
  360. m_vertexPoints.push_back(vTopLeft);
  361. m_vertexPoints.push_back(vBottomLeft);
  362. m_vertexPoints.push_back(vBottomRight);
  363. m_vertexPoints.push_back(vTopRight);
  364. }
  365.  
  366. void WorldRenderer::render(sf::RenderWindow & window, GameOfLife & game)
  367. {
  368. // clear m_cellVertexPoints
  369. m_vertexPoints.clear();
  370.  
  371. // draw backgrounds for "core zones"
  372. renderBackgrounds(window, game);
  373.  
  374. // populate m_cellVertexPoints
  375. for (auto cell : game.aliveCells)
  376. {
  377. addQuad(cell.position.x, cell.position.y, cell.color);
  378. }
  379.  
  380. // draw quads to window
  381. window.draw(m_vertexPoints.data(), m_vertexPoints.size(), sf::Quads);
  382. }
  383.  
  384. void WorldRenderer::renderBackgrounds(sf::RenderWindow & window, GameOfLife & world)
  385. {
  386. int cellsPerCore = world.worldSize.x * world.worldSize.y / world.maxThreads;
  387.  
  388. // first draw the background color of the final core index
  389. addBackgroundQuad(
  390. sf::Vector2f(0, 0),
  391. sf::Vector2f(world.worldSize.x, world.worldSize.y),
  392. darkenColor(world.getThreadColor(world.maxThreads - 1))
  393. );
  394.  
  395. // draw the remaining core background colors on top, in reverse order
  396. for (int i = world.maxThreads - 2; i >= 0; i--) {
  397. auto end = world.get2D(cellsPerCore * (i + 1));
  398. addBackgroundQuad(
  399. sf::Vector2f(0, 0),
  400. sf::Vector2f(world.worldSize.x, end.y),
  401. darkenColor(world.getThreadColor(i))
  402. );
  403. }
  404. }
  405.  
  406. sf::Color WorldRenderer::darkenColor(sf::Color input)
  407. {
  408. return sf::Color(input.r / 3, input.g / 3, input.b / 3);
  409. }
  410.  
  411. #pragma once
  412.  
  413. #include <SFML/Graphics.hpp>
  414.  
  415. class Cell
  416. {
  417. public:
  418. Cell(sf::Vector2i position, sf::Color color);
  419. ~Cell();
  420. sf::Vector2i position;
  421. sf::Color color;
  422. };
  423.  
  424. #include "Cell.h"
  425.  
  426. Cell::Cell(sf::Vector2i position, sf::Color color) : position(position), color(color)
  427. {
  428. }
  429.  
  430. Cell::~Cell()
  431. {
  432. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement