repos / gbc

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

gbc / examples
xplshn  ·  2025-08-25

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