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}