diff options
| author | Bobby <[email protected]> | 2023-09-11 23:22:50 -0400 |
|---|---|---|
| committer | Bobby <[email protected]> | 2023-09-11 23:22:50 -0400 |
| commit | 2876b8b38f699b3062230a7b570b45cc7399716e (patch) | |
| tree | 16044732c690de0b92cdbd1f91135825b3f5c299 | |
| download | gameoflife-2876b8b38f699b3062230a7b570b45cc7399716e.tar.xz gameoflife-2876b8b38f699b3062230a7b570b45cc7399716e.zip | |
Added Game of Life
| -rw-r--r-- | .gitignore | 35 | ||||
| -rw-r--r-- | .idea/vcs.xml | 6 | ||||
| -rw-r--r-- | .idea/workspace.xml | 94 | ||||
| -rw-r--r-- | .vscode/c_cpp_properties.json | 20 | ||||
| -rw-r--r-- | README.md | 25 | ||||
| -rw-r--r-- | cmake-build-debug/CMakeFiles/clion-Debug-log.txt | 1 | ||||
| -rwxr-xr-x | make.sh | 17 | ||||
| -rw-r--r-- | src/constants.h | 3 | ||||
| -rw-r--r-- | src/game.h | 47 | ||||
| -rw-r--r-- | src/gameOfLife.cpp | 40 | ||||
| -rw-r--r-- | src/screen.h | 62 |
11 files changed, 350 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fd31d94 --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ +# Build Directory +build/ + +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app
\ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="VcsDirectoryMappings"> + <mapping directory="$PROJECT_DIR$" vcs="Git" /> + </component> +</project>
\ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..09f0ad0 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,94 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="AutoImportSettings"> + <option name="autoReloadType" value="SELECTIVE" /> + </component> + <component name="CMakePresetLoader">{ + "useNewFormat": true +}</component> + <component name="CMakeReloadState"> + <option name="reloaded" value="true" /> + </component> + <component name="CMakeRunConfigurationManager"> + <generated /> + </component> + <component name="CMakeSettings"> + <configurations> + <configuration PROFILE_NAME="Debug" ENABLED="true" CONFIG_NAME="Debug" /> + </configurations> + </component> + <component name="ChangeListManager"> + <list default="true" id="4bb3a3af-fb57-4015-aff7-69d43c5f5b41" name="Changes" comment="" /> + <option name="SHOW_DIALOG" value="false" /> + <option name="HIGHLIGHT_CONFLICTS" value="true" /> + <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" /> + <option name="LAST_RESOLUTION" value="IGNORE" /> + </component> + <component name="ClangdSettings"> + <option name="formatViaClangd" value="false" /> + </component> + <component name="ExecutionTargetManager" SELECTED_TARGET="CMakeBuildProfile:Debug" /> + <component name="ProjectApplicationVersion"> + <option name="ide" value="CLion" /> + <option name="majorVersion" value="2023" /> + <option name="minorVersion" value="2" /> + </component> + <component name="ProjectColorInfo">{ + "associatedIndex": 6 +}</component> + <component name="ProjectId" id="2VH7VouRNGf67FROi7Q2GtyreiB" /> + <component name="ProjectViewState"> + <option name="hideEmptyMiddlePackages" value="true" /> + <option name="showLibraryContents" value="true" /> + </component> + <component name="PropertiesComponent"><![CDATA[{ + "keyToString": { + "RunOnceActivity.OpenProjectViewOnStart": "true", + "RunOnceActivity.ShowReadmeOnStart": "true", + "RunOnceActivity.cidr.known.project.marker": "true", + "WebServerToolWindowFactoryState": "false", + "cf.first.check.clang-format": "false", + "cidr.known.project.marker": "true", + "node.js.detected.package.eslint": "true", + "node.js.detected.package.tslint": "true", + "node.js.selected.package.eslint": "(autodetect)", + "node.js.selected.package.tslint": "(autodetect)", + "nodejs_package_manager_path": "npm", + "settings.editor.selected.configurable": "editor.preferences.fonts.default", + "vue.rearranger.settings.migration": "true" + } +}]]></component> + <component name="RunManager"> + <configuration default="true" type="CMakeRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true"> + <method v="2"> + <option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" /> + </method> + </configuration> + </component> + <component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" /> + <component name="TaskManager"> + <task active="true" id="Default" summary="Default task"> + <changelist id="4bb3a3af-fb57-4015-aff7-69d43c5f5b41" name="Changes" comment="" /> + <created>1694481177826</created> + <option name="number" value="Default" /> + <option name="presentableId" value="Default" /> + <updated>1694481177826</updated> + <workItem from="1694481179202" duration="32000" /> + <workItem from="1694481218048" duration="7051000" /> + </task> + <servers /> + </component> + <component name="TypeScriptGeneratedFilesManager"> + <option name="version" value="3" /> + </component> + <component name="VCPKGProject"> + <isAutomaticEditVcpkgJson value="false" /> + <isAutomaticCheckingOnLaunch value="false" /> + <isAutomaticFoundErrors value="true" /> + <isAutomaticFoundErrors value="true" /> + </component> + <component name="XSLT-Support.FileAssociations.UIState"> + <expand /> + <select /> + </component> +</project>
\ No newline at end of file diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..47c2f08 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,20 @@ +{ + "configurations": [ + { + "name": "Mac", + "includePath": [ + "${workspaceFolder}/**", + "/opt/homebrew/include", + ], + "defines": [], + "macFrameworkPath": [ + "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks" + ], + "compilerPath": "/usr/bin/clang", + "cStandard": "c20", + "cppStandard": "c++20", + "intelliSenseMode": "macos-clang-arm64" + } + ], + "version": 4 +}
\ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..d43a618 --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +# Conway's Game of Life + +This is a simple implementation of Conway's Game of Life in C++ using SDL2. + +## Prerequisites + +* C++ compiler (clang++, with C++20 is used in this project) +* SDL2 (https:/https://github.com/libsdl-org/SDL/releases) + +> Note: You can install SDL2 using Homebrew on macOS: `brew install sdl2`. Follow the instructions on the SDL2 website for other platforms. + +## Build + +`make.sh` script is provided to build the project. It will create a `build` directory and put the executable in it. + +```bash +chmod +x make.sh # Make the script executable, if needed +./make.sh # Build the project +``` + +## Run + +```bash +./build/gameOfLife +``` diff --git a/cmake-build-debug/CMakeFiles/clion-Debug-log.txt b/cmake-build-debug/CMakeFiles/clion-Debug-log.txt new file mode 100644 index 0000000..e108f28 --- /dev/null +++ b/cmake-build-debug/CMakeFiles/clion-Debug-log.txt @@ -0,0 +1 @@ +CMakeLists.txt not found in /Users/lucifer/CLionProjects/gameOfLife Select CMakeLists.txt @@ -0,0 +1,17 @@ +echo "Creating build directory... (if it doesn't exist)" +mkdir -p build + +echo "Removing old gameOfLife... (if it exists)" +rm -f build/gameOfLife + +echo "Building gameOfLife..." +echo "C++ version: $(clang++ --version)" +echo "SDL2 version: $(sdl2-config --version)" + +echo "Setting C++20 standard and libc++ as the standard library..." +echo "Including SDL2 headers from /opt/homebrew/include..." +echo "Linking SDL2 libraries from /opt/homebrew/lib..." + +clang++ -std=c++20 -stdlib=libc++ src/gameOfLife.cpp -I/opt/homebrew/include -L/opt/homebrew/lib/ -lSDL2 -o build/gameOfLife + +echo "gameOfLife built successfully" diff --git a/src/constants.h b/src/constants.h new file mode 100644 index 0000000..bb73900 --- /dev/null +++ b/src/constants.h @@ -0,0 +1,3 @@ +const int SCREEN_WIDTH = 640; +const int SCREEN_HEIGHT = 480; +const int SCALE_FACTOR = 2;
\ No newline at end of file diff --git a/src/game.h b/src/game.h new file mode 100644 index 0000000..5f14541 --- /dev/null +++ b/src/game.h @@ -0,0 +1,47 @@ +#include <array> +#include <random> +#include "screen.h" + +using Array2D = std::array<std::array<int, SCREEN_HEIGHT>, SCREEN_WIDTH>; + +// Two-dimensional array of ints +struct Game { + Array2D display {}; + Array2D swap {}; +}; + +// random number generator function: +int zeroOrOne() { + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(0, 1); + return dis(gen); +} + +// function to check if the cell will be alive or dead in the next generation +bool isAlive(Array2D &game, int x, int y) { + int alive = 0; + + // left test + if (x > 0 && game[x - 1][y] == 1) alive++; + // right test + if (x < SCREEN_WIDTH && game[x + 1][y] == 1) alive++; + // top test + if (y > 0 && game[x][y - 1] == 1) alive++; + // bottom test + if (y < SCREEN_HEIGHT && game[x][y + 1] == 1) alive++; + // top left test + if (x > 0 && y > 0 && game[x - 1][y - 1] == 1) alive++; + // top right test + if (x < SCREEN_WIDTH && y > 0 && game[x + 1][y - 1] == 1) alive++; + // bottom left test + if (x > 0 && y < SCREEN_HEIGHT && game[x - 1][y + 1] == 1) alive++; + // bottom right test + if (x < SCREEN_WIDTH && y < SCREEN_HEIGHT && game[x + 1][y + 1] == 1) alive++; + + // if the cell is alive and has 2 or 3 neighbors, it stays alive or + // if the cell is dead and has exactly 3 neighbors, it comes to life + if ((game[x][y] == 1 && (alive == 2 || alive == 3)) || (game[x][y] == 0 && alive == 3)) return true; + // otherwise, the cell dies + else return false; +} diff --git a/src/gameOfLife.cpp b/src/gameOfLife.cpp new file mode 100644 index 0000000..976d4ae --- /dev/null +++ b/src/gameOfLife.cpp @@ -0,0 +1,40 @@ +#include "game.h" + +int main() { + Game game; + GameScreen screen; + + // create random points + for (auto &row : game.display) { + std::generate(row.begin(), row.end(), zeroOrOne); + } + + // game loop + for(;;) { + // check for alive cells + for (int i = 0; i < SCREEN_WIDTH; ++i) { + for (int j = 0; j < SCREEN_HEIGHT; ++j) { + // check if the cell will be alive or dead in the next generation + game.swap[i][j] = isAlive(game.display, i, j) ? 1 : 0; + } + } + + // draw + for (int i = 0; i < SCREEN_WIDTH; ++i) { + for (int j = 0; j < SCREEN_HEIGHT; ++j) { + if (game.swap[i][j] == 1) { + screen.drawPixel(i, j); + } + } + } + + // swap buffers + std::copy(game.swap.begin(), game.swap.end(), game.display.begin()); + + // display to screen + screen.update(); + SDL_Delay(20); + screen.input(); + screen.clearPixels(); + } +} diff --git a/src/screen.h b/src/screen.h new file mode 100644 index 0000000..9aa242d --- /dev/null +++ b/src/screen.h @@ -0,0 +1,62 @@ +#include <SDL2/SDL.h> +#include <cstdlib> +#include <iostream> +#include <vector> +#include <algorithm> +#include "constants.h" + +// Game Screen Class -> Uses SDL2 +class GameScreen { + SDL_Window *window{}; + SDL_Renderer *renderer{}; + SDL_bool done; + std::vector<SDL_FPoint> points; + std::vector<SDL_Color> colors; + SDL_Event event{}; + +public: + GameScreen() { + SDL_Init(SDL_INIT_VIDEO); + SDL_CreateWindowAndRenderer(SCREEN_WIDTH * SCALE_FACTOR, + SCREEN_HEIGHT * SCALE_FACTOR, + 0, &window, &renderer); + SDL_SetWindowTitle(window, "Game of Life"); + SDL_RenderSetScale(renderer, SCALE_FACTOR, SCALE_FACTOR); + done = SDL_FALSE; + } + + void drawPixel(double xm, double ym, uint8_t r = 255, uint8_t g = 255, uint8_t b = 255, uint8_t a = 255) { + points.push_back({static_cast<float>(xm), static_cast<float>(ym)}); + colors.push_back({r, g, b, a}); + } + + void clearPixels() { + points.clear(); + } + + void update() { + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); + + for (long unsigned int i = 0; i < points.size(); ++i) { + SDL_SetRenderDrawColor(renderer, colors[i].r, colors[i].g, colors[i].b, colors[i].a); + SDL_RenderDrawPointF(renderer, points[i].x, points[i].y); + } + + SDL_RenderPresent(renderer); + } + + void input() { + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_QUIT: + done = SDL_TRUE; + SDL_Quit(); + exit(0); + break; + default: + break; + } + } + } +}; |
