← Back to davo.co
summaryrefslogtreecommitdiffstats
path: root/B9.c
diff options
context:
space:
mode:
authorDavid Faulkner <[email protected]>2026-08-07 23:40:47 -0500
committerDavid Faulkner <[email protected]>2026-08-07 23:40:47 -0500
commitb3e9e62599532050fc776c5e8f076915b56c2235 (patch)
treeaf252346106a61b18cc6fc6fdbd32e962d096f1c /B9.c
Import official C23 code examples for Modern C (Jens Gustedt, 2024)HEADupstream-importmain
- Add official C source files, Makefile, c23-fallback.h, and LICENSE - Update README.md with study mirror notice
Diffstat (limited to 'B9.c')
-rw-r--r--B9.c224
1 files changed, 224 insertions, 0 deletions
diff --git a/B9.c b/B9.c
new file mode 100644
index 0000000..c0ef46e
--- /dev/null
+++ b/B9.c
@@ -0,0 +1,224 @@
+#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 used 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 update_thread(void* Lv) {
+ life*restrict L = Lv;
+ size_t changed = 1;
+ size_t birth9 = 0;
+ while (!L->finished && changed) {
+ // Blocks 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);
+ }
+ return 0;
+}
+
+static
+int draw_thread(void* Lv) {
+ 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 = 10u;
+
+static
+int account_thread(void* Lv) {
+ 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) {
+ 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); /*@\label{lab:birth9}*/
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ cnd_signal(&L->draw);
+ mtx_unlock(&L->mtx);
+ continue;
+ case 'q':
+ case 'Q':
+ case EOF: goto FINISH;
+ }
+ cnd_signal(&L->draw); /*@\label{lab:signal}*/
+ } 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 },
+ { },
+};
+
+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 L = LIFE_INITIALIZER;
+ life_init(&L, n0, n1, M);
+ /* Creates four threads that all operate on that same object
+ and collects their IDs in "thrd" */
+ thrd_t thrd[4];
+ thrd_create(&thrd[0], update_thread, &L);
+ thrd_create(&thrd[1], draw_thread, &L);
+ thrd_create(&thrd[2], input_thread, &L);
+ thrd_create(&thrd[3], account_thread, &L);
+ /* Waits for the update thread to terminate */
+ thrd_join(thrd[0], nullptr);
+ /* Tells everybody that the game is over */
+ L.finished = true;
+ ungetc('q', stdin);
+ /* Waits for the other threads */
+ thrd_join(thrd[1], nullptr);
+ thrd_join(thrd[2], nullptr);
+ thrd_join(thrd[3], nullptr);
+ /* Puts the board in a nice final picture */
+ L.iteration = L.last;
+ life_draw(&L);
+ life_destroy(&L);
+}