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}