← Back to davo.co
summaryrefslogtreecommitdiffstats
path: root/B9-detach.c
diff options
context:
space:
mode:
Diffstat (limited to 'B9-detach.c')
-rw-r--r--B9-detach.c234
1 files changed, 234 insertions, 0 deletions
diff --git a/B9-detach.c b/B9-detach.c
new file mode 100644
index 0000000..d6ecfb6
--- /dev/null
+++ b/B9-detach.c
@@ -0,0 +1,234 @@
+#include "life.h"
+//#include <stdlib.h>
+#include <stdio.h>
+#include <stdatomic.h>
+//#include <limits.h>
+#include "termin.h"
+//#include "c23-fallback.h"
+//#include <unistd.h>
+
+// The keys that are used for cursor movement.
+// Other keys that are use are
+// b, B, space for birth9
+// -, + to slow down and accelerate
+// q, Q to quit
+#define GO_UP 'k'
+#define GO_DOWN 'l'
+#define GO_RIGHT ';'
+#define GO_LEFT 'j'
+#define GO_HOME 'h'
+
+// We translate escape sequences that we may receive to these standard
+// characters for further processing.
+
+#define ESCAPE '\e'
+
+char const*const termin_trans[UCHAR_MAX+1] = {
+ [GO_UP] = ESC_UP,
+ [GO_DOWN] = ESC_DOWN,
+ [GO_RIGHT] = ESC_FRWD,
+ [GO_LEFT] = ESC_BKWD,
+ [GO_HOME] = ESC_HOME,
+};
+
+static int account_thread(void*);
+
+static
+int update_thread(void* Lv) {
+ /* Nobody should ever wait for this thread. */
+ thrd_detach(thrd_current());
+ /* Delegates part of our job to an auxiliary thread */
+ thrd_create(&(thrd_t){0}, account_thread, Lv);
+ life*restrict L = Lv;
+ size_t changed = 1;
+ size_t birth9 = 0;
+ while (!L->finished && changed) {
+ // block until there is work
+ mtx_lock(&L->mtx);
+ while (!L->finished && (L->accounted < L->iteration))
+ life_wait(&L->upda, &L->mtx);
+
+ // VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
+ if (birth9 != L->birth9) life_torus(L);
+ life_count(L);
+ changed = life_update(L);
+ life_torus(L);
+ birth9 = L->birth9;
+ if (L->iteration != SIZE_MAX) L->iteration++;
+ else L->finished = true;
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ cnd_signal(&L->acco);
+ cnd_signal(&L->draw);
+ mtx_unlock(&L->mtx);
+ life_sleep(1.0/L->frames);
+ }
+ /* Tells everybody that the game is over */
+ L->finished = true;
+ ungetc('q', stdin);
+ return 0;
+}
+
+static
+int draw_thread(void* Lv) {
+ /* Nobody should ever wait for this thread. */
+ thrd_detach(thrd_current());
+ life*restrict L = Lv;
+ size_t x0 = 0;
+ size_t x1 = 0;
+ fputs(ESC_CLEAR ESC_CLRSCR, stdout);
+ while (!L->finished) {
+ // Blocks until there is work
+ mtx_lock(&L->mtx);
+ while (!L->finished
+ && (L->iteration <= L->drawn)
+ && (x0 == L->x0)
+ && (x1 == L->x1))
+ life_wait(&L->draw, &L->mtx);
+
+ // VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
+ if (L->n0 <= 30) life_draw(L);
+ else life_draw4(L);
+ L->drawn++;
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ mtx_unlock(&L->mtx);
+
+ x0 = L->x0;
+ x1 = L->x1;
+ // no need to draw too quickly
+ life_sleep(1.0/40);
+ }
+ return 0;
+}
+
+// This number of consecutive states that are already known (that is,
+// hashed in) decides that this sequence of states is cyclic.
+constexpr unsigned repetition = 10;
+
+static
+int account_thread(void* Lv) {
+ /* Nobody should ever wait for this thread. */
+ thrd_detach(thrd_current());
+ life*restrict L = Lv;
+ while (!L->finished) {
+ // Blocks until there is work
+ mtx_lock(&L->mtx);
+ while (!L->finished && (L->accounted == L->iteration))
+ life_wait(&L->acco, &L->mtx);
+
+ // VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
+ life_account(L);
+ if ((L->last + repetition) < L->accounted) {
+ L->finished = true;
+ }
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ cnd_signal(&L->upda);
+ mtx_unlock(&L->mtx);
+ }
+ return 0;
+}
+
+static
+int input_thread(void* Lv) {
+ /* Nobody should ever wait for this thread. */
+ thrd_detach(thrd_current());
+ termin_unbuffered();
+ life*restrict L = Lv;
+ constexpr size_t len = 32;
+ char command[len];
+ do {
+ auto c = getchar();
+ command[0] = c;
+ switch(c) {
+ case GO_LEFT : life_advance(L, 0, -1); break;
+ case GO_RIGHT: life_advance(L, 0, +1); break;
+ case GO_UP : life_advance(L, -1, 0); break;
+ case GO_DOWN : life_advance(L, +1, 0); break;
+ case GO_HOME : L->x0 = 1; L->x1 = 1; break;
+ case ESCAPE :
+ ungetc(termin_translate(termin_read_esc(len, command)), stdin);
+ continue;
+ case '+': if (L->frames < 128) L->frames++; continue;
+ case '-': if (L->frames > 1) L->frames--; continue;
+ case ' ':
+ case 'b':
+ case 'B':
+ mtx_lock(&L->mtx);
+ // VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
+ life_birth9(L);
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ mtx_unlock(&L->mtx);
+ break;
+ case 'q':
+ case 'Q':
+ case EOF: goto FINISH;
+ }
+ cnd_signal(&L->draw);
+ } while (!(L->finished || feof(stdin)));
+ FINISH:
+ L->finished = true;
+ return 0;
+}
+
+enum { n = 60, m = 160 };
+
+bool M[n][m] = {
+ { },
+ { },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0},
+ { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0},
+ { 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0},
+ { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0},
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0},
+ { },
+ { },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0},
+ { 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, },
+ { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0},
+ { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0},
+ { 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0},
+ { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0},
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0 },
+ { },
+ { },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0},
+ { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0},
+ { 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0 },
+ { },
+};
+
+life L = LIFE_INITIALIZER;
+
+void B9_atexit(void) {
+ /* Puts the board in a nice final picture */
+ L.iteration = L.last;
+ life_draw(&L);
+ life_destroy(&L);
+}
+
+int main(int argc, char* argv[argc+1]) {
+ /* Uses command-line arguments for the size of the board */
+ size_t n0 = 30;
+ size_t n1 = 80;
+ if (argc > 1) n0 = strtoull(argv[1], nullptr, 0);
+ if (argc > 2) n1 = strtoull(argv[2], nullptr, 0);
+ /* Create an object that holds the game's data. */
+ life_init(&L, n0, n1, M);
+ atexit(B9_atexit);
+ /* Creates four threads that operate on the same object and
+ discards their IDs */
+ thrd_create(&(thrd_t){0}, update_thread, &L);
+ thrd_create(&(thrd_t){0}, draw_thread, &L);
+ thrd_create(&(thrd_t){0}, input_thread, &L);
+ /* Ends this thread nicely and lets the threads go on nicely */
+ thrd_exit(0);
+}