aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdrian Kummerlaender2016-12-09 23:28:46 +0100
committerAdrian Kummerlaender2016-12-09 23:28:46 +0100
commitb72dcd74717366f145b029d89ae11a085a5f5997 (patch)
treec90959183927ee1e9adc3dc08c631e2ae17f79ef
downloadtermlife-b72dcd74717366f145b029d89ae11a085a5f5997.tar
termlife-b72dcd74717366f145b029d89ae11a085a5f5997.tar.gz
termlife-b72dcd74717366f145b029d89ae11a085a5f5997.tar.bz2
termlife-b72dcd74717366f145b029d89ae11a085a5f5997.tar.lz
termlife-b72dcd74717366f145b029d89ae11a085a5f5997.tar.xz
termlife-b72dcd74717366f145b029d89ae11a085a5f5997.tar.zst
termlife-b72dcd74717366f145b029d89ae11a085a5f5997.zip
Initial working version
...of a plain Game Of Life displayed using `Termbox`.
-rw-r--r--CMakeLists.txt21
-rw-r--r--life.cc46
-rw-r--r--src/world.h146
3 files changed, 213 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..d517ca3
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,21 @@
+cmake_minimum_required(VERSION 2.8)
+project(life)
+
+set(
+ CMAKE_CXX_FLAGS
+ "${CMAKE_CXX_FLAGS} -std=c++14 -W -Wall -Wextra -Winline -pedantic"
+)
+
+include_directories(
+ src/
+)
+
+add_executable(
+ life
+ life.cc
+)
+
+target_link_libraries(
+ life
+ termbox
+)
diff --git a/life.cc b/life.cc
new file mode 100644
index 0000000..7f0ace7
--- /dev/null
+++ b/life.cc
@@ -0,0 +1,46 @@
+#include <termbox.h>
+
+#include "world.h"
+
+int main(int, char*[]) {
+ tb_init();
+
+ tb_select_input_mode(TB_INPUT_ESC | TB_INPUT_MOUSE);
+
+ life::World<20,20> world;
+
+ while ( true ) {
+ struct tb_event ev;
+ int t = tb_poll_event(&ev);
+
+ if ( t == -1 ) {
+ tb_shutdown();
+ return -1;
+ }
+
+ switch (t) {
+ case TB_EVENT_KEY:
+ switch ( ev.key ) {
+ case TB_KEY_ESC:
+ tb_shutdown();
+ return 0;
+ case TB_KEY_SPACE:
+ world.tick();
+ break;
+ }
+ break;
+ case TB_EVENT_MOUSE:
+ if (ev.key == TB_KEY_MOUSE_LEFT) {
+ world.summonLifeAt(ev.x, ev.y);
+ }
+
+ break;
+ case TB_EVENT_RESIZE:
+ break;
+ }
+
+ world.draw();
+ }
+
+ return 0;
+}
diff --git a/src/world.h b/src/world.h
new file mode 100644
index 0000000..297d0c5
--- /dev/null
+++ b/src/world.h
@@ -0,0 +1,146 @@
+#ifndef LIFE_SRC_WORLD_H_
+#define LIFE_SRC_WORLD_H_
+
+#include <array>
+#include <stack>
+#include <tuple>
+#include <cstdint>
+
+#include <termbox.h>
+
+namespace life {
+
+template<
+ std::size_t WIDTH,
+ std::size_t HEIGHT
+>
+class World {
+ public:
+ World() {
+ for ( std::size_t j = 0; j < HEIGHT; j++ ) {
+ for ( std::size_t i = 0; i < WIDTH; i++ ) {
+ this->matrix_[j][i] = false;
+ }
+ }
+ }
+
+ void summonLifeAt(std::size_t x, std::size_t y) {
+ this->matrix_[y][x] = true;
+ }
+
+ void extinguishLifeAt(std::size_t x, std::size_t y) {
+ this->matrix_[y][x] = false;
+ }
+
+ bool isLifeAt(std::ptrdiff_t x, std::ptrdiff_t y) {
+ if ( x >= 0 &&
+ x < WIDTH &&
+ y >= 0 &&
+ y < HEIGHT ) {
+ return this->matrix_[y][x];
+ } else {
+ return false; // end of world is dead
+ }
+ }
+
+ std::uint8_t lifeDensityAt(std::size_t x, std::size_t y) {
+ std::uint8_t counter = 0;
+
+ if ( this->isLifeAt(x - 1, y) ) {
+ counter++;
+ }
+ if ( this->isLifeAt(x + 1, y) ) {
+ counter++;
+ }
+ if ( this->isLifeAt(x, y - 1) ) {
+ counter++;
+ }
+ if ( this->isLifeAt(x, y + 1) ) {
+ counter++;
+ }
+ if ( this->isLifeAt(x - 1, y + 1) ) {
+ counter++;
+ }
+ if ( this->isLifeAt(x - 1, y - 1) ) {
+ counter++;
+ }
+ if ( this->isLifeAt(x + 1, y + 1) ) {
+ counter++;
+ }
+ if ( this->isLifeAt(x + 1, y - 1) ) {
+ counter++;
+ }
+
+ return counter;
+ }
+
+ void tick() {
+ this->age_++;
+
+ std::stack<std::tuple<std::size_t, std::size_t, bool>> updates;
+
+ for ( std::size_t j = 0; j < HEIGHT; j++ ) {
+ for ( std::size_t i = 0; i < WIDTH; i++ ) {
+ const std::uint8_t d = this->lifeDensityAt(i, j);
+
+ if ( this->matrix_[j][i] ) {
+ if ( d < 2 ) {
+ updates.push(std::make_tuple(i, j, false));
+ } else if ( d == 2 || d == 3 ) {
+ updates.push(std::make_tuple(i, j, true));
+ } else if ( d > 3 ) {
+ updates.push(std::make_tuple(i, j, false));
+ }
+ } else {
+ if ( d == 3 ) {
+ updates.push(std::make_tuple(i, j, true));
+ }
+ }
+ }
+ }
+
+ while ( !updates.empty() ) {
+ const auto& update = updates.top();
+
+ if ( std::get<2>(update) ) {
+ this->summonLifeAt(
+ std::get<0>(update),
+ std::get<1>(update)
+ );
+ } else {
+ this->extinguishLifeAt(
+ std::get<0>(update),
+ std::get<1>(update)
+ );
+ }
+
+ updates.pop();
+ }
+ }
+
+ void draw() {
+ tb_clear();
+
+ for ( std::size_t j = 0; j < HEIGHT; j++ ) {
+ for ( std::size_t i = 0; i < WIDTH; i++ ) {
+ if ( this->matrix_[j][i] ) {
+ tb_change_cell(i, j, 0x2588, TB_BLACK, TB_GREEN);
+ } else {
+ tb_change_cell(i, j, 0x2591, TB_BLACK, TB_BLUE);
+ }
+ }
+ }
+
+ tb_present();
+ }
+
+ private:
+ std::size_t age_ = 0;
+
+ std::array<std::array<bool, WIDTH>, HEIGHT> matrix_;
+
+};
+
+}
+
+#endif // LIFE_SRC_WORLD_H_