repos / gbc

GBC - Go B Compiler
git clone https://github.com/xplshn/gbc.git

gbc / examples
xplshn  ·  2025-08-13

game_of_b.b

B
  1// To compile this example you need to pass appropriate linker flags to the b compiler:
  2// $ b 65_game_of_b.b -L -lncurses -L -lpanel -run
  3
  4TRUE;
  5FALSE;
  6ALIVE_CHAR;
  7LOOP_TIME_us;
  8NUM_SPEEDS;
  9
 10x_size;
 11y_size;
 12x_chars;
 13y_chars;
 14buf_size;
 15cur_buf;
 16prv_buf;
 17world_win;
 18info_panel;
 19speeds;
 20
 21int32(array, i) {
 22    extrn memcpy;
 23    auto val;
 24    val = 0;
 25    memcpy(&val, array + (i * 4), 4);
 26    return (val);
 27}
 28
 29init_ncurses() {
 30    extrn initscr, noecho, cbreak, curs_set, timeout, keypad, stdscr, mousemask, mouseinterval;
 31    initscr();
 32    noecho();
 33    cbreak();
 34    curs_set(0);
 35    timeout(0);
 36    keypad(stdscr, 1);
 37    mousemask(1, 0); // BUTTON1_RELEASED
 38    mouseinterval(10);
 39}
 40
 41deinit_ncurses() {
 42    extrn endwin, echo, curs_set;
 43    endwin();
 44    echo();
 45    curs_set(1);
 46}
 47
 48init_globals() {
 49    TRUE = 1;
 50    FALSE = 0;
 51    ALIVE_CHAR = 'B';
 52    LOOP_TIME_us = 10000;
 53    NUM_SPEEDS = 16;
 54
 55    extrn getmaxx, getmaxy, stdscr, malloc, lchar, char;
 56    x_size = getmaxx(stdscr) / 2;
 57    y_size = getmaxy(stdscr);
 58    x_chars = x_size * 2;
 59    y_chars = y_chars;
 60    buf_size = x_size * y_size;
 61    cur_buf = malloc(buf_size);
 62    prv_buf = malloc(buf_size);
 63
 64    speeds = malloc(NUM_SPEEDS);
 65    lchar(speeds, 0, 1);
 66    auto prv_speed;
 67    auto i; i = 1; while(i < NUM_SPEEDS) {
 68        prv_speed = char(speeds, i-1);
 69        lchar(speeds, i, prv_speed + (prv_speed / 3) + 1);
 70        i++;
 71    }
 72}
 73
 74init_world() {
 75    extrn newwin, new_panel;
 76    auto world_panel;
 77    world_win = newwin(y_chars, x_chars, 0, 0);
 78    world_panel = new_panel(world_win);
 79
 80    extrn start_color, init_pair, wattron, COLOR_PAIR;
 81    start_color();
 82    init_pair(42, 7, 1); // foreground -> COLOR_WHITE, background -> COLOR_RED
 83    wattron(world_win, COLOR_PAIR(42));
 84}
 85
 86init_info() {
 87    auto x_info_chars, y_info_chars;
 88    x_info_chars = 20;
 89    y_info_chars = 9;
 90
 91    extrn newwin, new_panel;
 92    auto info_win;
 93    info_win = newwin(y_info_chars, x_info_chars, 0, x_chars - x_info_chars - 1);
 94    info_panel = new_panel(info_win);
 95
 96    extrn box, mvwaddstr;
 97    box(info_win, 0, 0);
 98    mvwaddstr(info_win, 1, 2, "i -> toggle info");
 99    mvwaddstr(info_win, 2, 2, "s -> start/stop");
100    mvwaddstr(info_win, 3, 2, "k -> speed up");
101    mvwaddstr(info_win, 4, 2, "j -> slow down");
102    mvwaddstr(info_win, 5, 2, "c -> clear world");
103    mvwaddstr(info_win, 6, 2, "r -> reset world");
104    mvwaddstr(info_win, 7, 2, "q -> quit game");
105}
106
107is_alive(buf, y, x) {
108    extrn lchar, char;
109    return (char(buf, (y * x_size) + x) != 0);
110}
111
112set_alive(buf, y, x, alive) {
113    extrn lchar, char;
114    lchar(buf, (y * x_size) + x, alive);
115}
116
117clear_world() {
118    extrn memset;
119    memset(cur_buf, 0, buf_size);
120}
121
122reset_world() {
123    auto x_mid, y_mid;
124    x_mid = x_size / 2;
125    y_mid = y_size / 2;
126    clear_world();
127    set_alive(cur_buf, y_mid-1, x_mid+0, TRUE);
128    set_alive(cur_buf, y_mid+0, x_mid+1, TRUE);
129    set_alive(cur_buf, y_mid+1, x_mid-1, TRUE);
130    set_alive(cur_buf, y_mid+1, x_mid+0, TRUE);
131    set_alive(cur_buf, y_mid+1, x_mid+1, TRUE);
132}
133
134update_world() {
135    auto tmp_buf;
136    tmp_buf = prv_buf;
137    prv_buf = cur_buf;
138    cur_buf = tmp_buf;
139    clear_world();
140    auto y; y = 0; while(y < y_size) {
141        auto x; x = 0; while(x < x_size) {
142            auto up, down, left, right;
143            up = (y + y_size - 1) % y_size;
144            down = (y + 1) % y_size;
145            left = (x + x_size - 1) % x_size;
146            right = (x + 1) % x_size;
147            auto self, other;
148            self = is_alive(prv_buf, y, x);
149            other = is_alive(prv_buf, up, left)
150                  + is_alive(prv_buf, up, x)
151                  + is_alive(prv_buf, up, right)
152                  + is_alive(prv_buf, y, left)
153                  + is_alive(prv_buf, y, right)
154                  + is_alive(prv_buf, down, left)
155                  + is_alive(prv_buf, down, x)
156                  + is_alive(prv_buf, down, right);
157            if(self) {
158                if(other == 2 | other == 3) {
159                    set_alive(cur_buf, y, x, TRUE);
160                }
161            } else {
162                if(other == 3) {
163                    set_alive(cur_buf, y, x, TRUE);
164                }
165            }
166            x++;
167        }
168        y++;
169    }
170}
171
172print_world() {
173    extrn werase, wmove, waddch, update_panels, doupdate;
174    werase(world_win);
175    auto y; y = 0; while(y < y_size) {
176        auto x; x = 0; while(x < x_size) {
177            if(is_alive(cur_buf, y, x)) {
178                wmove(world_win, y, x*2);
179                waddch(world_win, ALIVE_CHAR);
180                waddch(world_win, ALIVE_CHAR);
181            }
182            x++;
183        }
184        y++;
185    }
186    update_panels();
187    doupdate();
188}
189
190main() {
191    init_ncurses();
192    init_globals();
193    init_world();
194    init_info();
195    reset_world();
196
197    extrn malloc, usleep, getch, show_panel, hide_panel, getmouse, char;
198    auto input, redraw, update_in, show_info, stopped, speed_index, mouse_event;
199    input = -1;
200    redraw = 1;
201    update_in = 0;
202    show_info = 1;
203    stopped = 0;
204    speed_index = 4;
205    mouse_event = malloc(20); // MEVENT
206    while(input != 'q') {
207        if(redraw) {
208            print_world();
209            redraw = 0;
210        }
211        usleep(LOOP_TIME_us);
212        if(!stopped) {
213            update_in--;
214            if(update_in <= 0) {
215                update_world();
216                redraw = 1;
217                update_in = char(speeds, speed_index);
218            }
219        }
220        input = getch();
221        if(input == 'i') {
222            show_info = !show_info;
223            if(show_info) {
224                show_panel(info_panel);
225            } else {
226                hide_panel(info_panel);
227            }
228            redraw = 1;
229        } else if(input == 's') {
230            stopped = !stopped;
231        } else if(input == 'k') {
232            speed_index--;
233            if(speed_index < 0) speed_index = 0;
234        } else if(input == 'j') {
235            speed_index++;
236            if(speed_index >= NUM_SPEEDS) speed_index = NUM_SPEEDS - 1;
237        } else if(input == 'c') {
238            clear_world();
239            redraw = 1;
240        } else if(input == 'r') {
241            reset_world();
242            redraw = 1;
243        } else if(input == 0x199) { // KEY_MOUSE
244            if (getmouse(mouse_event) == 0) { // OK
245                auto x, y;
246                x = int32(mouse_event, 1) / 2;
247                y = int32(mouse_event, 2);
248                set_alive(cur_buf, y, x, !is_alive(cur_buf, y, x));
249                redraw = 1;
250            }
251        }
252    }
253    deinit_ncurses();
254}