xref: /netbsd-src/games/testpat/testpat.c (revision 3f351f34c6d827cf017cdcff3543f6ec0c88b420)
1 /* $NetBSD: testpat.c,v 1.6 2021/11/13 20:59:13 nat Exp $ */
2 
3 /*-
4  * Copyright (c) 2016 Nathanial Sloss <nathanialsloss@yahoo.com.au>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 #include <sys/cdefs.h>
29 __RCSID("$NetBSD: testpat.c,v 1.6 2021/11/13 20:59:13 nat Exp $");
30 
31 #include <sys/types.h>
32 #include <sys/time.h>
33 
34 #include <curses.h>
35 #include <err.h>
36 #include <errno.h>
37 #include <math.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <time.h>
42 
43 static int colour_list[6] = {
44 	COLOR_YELLOW,
45 	COLOR_CYAN,
46 	COLOR_GREEN,
47 	COLOR_MAGENTA,
48 	COLOR_RED,
49 	COLOR_BLUE,
50 };
51 static int numcolours = (int)__arraycount(colour_list);
52 
53 int main(int argc, char *argv[]) {
54 	int i, col, colour, line, x_limit, y_limit, colourOK, spacing;
55 	int xpos, ypos, spacing_residual, spacing_start, spacing_end;
56 	int grid_x, grid_y, **circle_pos;
57 	size_t ncpos;
58 	float grid_unit;
59 	const char *title = "NetBSD";
60 	float coord_x, circle_int;
61 	float a_axis, b_axis;
62 
63 	if (!initscr()) {
64 		errx(EXIT_FAILURE, "Unknown terminal type");
65 	}
66 
67 	curs_set(0);
68 
69 	if (argc > 2) {
70 		endwin();
71 		fprintf(stderr, "Usage: %s <title>", getprogname());
72 		return EXIT_FAILURE;
73 	}
74 
75 	if (argc == 2) {
76 		title = argv[1];
77 		if (strlen(title) >= (size_t)COLS) {
78 			endwin();
79 			errx(EXIT_FAILURE,
80 			    "Title string is longer than display cols");
81 		}
82 	}
83 
84 	colourOK = has_colors();
85 
86 	if (COLS < 13 || LINES < 13) {
87 		endwin();
88 		errx(EXIT_FAILURE, "Terminal size must be at least 72x25.");
89 	}
90 
91 	if (colourOK) {
92 		start_color();
93 
94 	    	init_pair(0, COLOR_WHITE, COLOR_BLACK);
95 	    	init_pair(1, COLOR_WHITE, COLOR_RED);
96 	    	init_pair(2, COLOR_WHITE, COLOR_GREEN);
97 	    	init_pair(3, COLOR_WHITE, COLOR_YELLOW);
98 	    	init_pair(4, COLOR_WHITE, COLOR_BLUE);
99 	    	init_pair(5, COLOR_WHITE, COLOR_MAGENTA);
100 	    	init_pair(6, COLOR_WHITE, COLOR_CYAN);
101 	    	init_pair(7, COLOR_BLACK, COLOR_WHITE);
102 
103 		attrset(COLOR_PAIR(0));
104 	}
105 
106 	x_limit = (COLS - 1) / 2;
107 	x_limit = x_limit * 2;
108 	y_limit = (LINES - 2) / 2;
109 	y_limit = y_limit * 2;
110 	spacing = 2 * y_limit / numcolours;
111 	spacing_residual = ((2 * y_limit) % numcolours) / 2;
112 	a_axis = y_limit / 2;
113 	b_axis = y_limit;
114 	grid_unit = b_axis / 13;
115 	grid_y = grid_unit;
116 	grid_x = grid_unit * 2;
117 
118 
119 	ncpos = y_limit * sizeof(*circle_pos)
120 	    + y_limit * 2 * sizeof(**circle_pos);
121 	circle_pos = malloc(ncpos);
122 	if (circle_pos == NULL) {
123 		endwin();
124 		errx(EXIT_FAILURE, "Can't allocate circle positions");
125 	}
126 	for (i = 0; i < y_limit; i++) {
127 	    circle_pos[i] = (void *)&circle_pos[y_limit + i * 2];
128 	    circle_pos[i][0] = circle_pos[i][1] = -1;
129 	}
130 
131 	for (i = 0; i < y_limit; i++) {
132 		/* Draw an ellipse (looks more circular.) */
133 		circle_int = (i - a_axis) / a_axis;
134 		circle_int = 1 - powf(circle_int, 2);
135 		circle_int = circle_int * powf(b_axis, 2);
136 #if 0
137 		/* Draw a circle, commented out as elipse looks better.*/
138 		circle_int = powf(a_axis, 2) - powf(i - a_axis, 2);
139 #endif
140 		coord_x = sqrtf(circle_int);
141 		circle_pos[i][0] = (-coord_x + ((float)x_limit / 2));
142 		circle_pos[i][1] = (coord_x + ((float)x_limit / 2));
143 	}
144 
145 	clear();
146 
147 	attron(A_ALTCHARSET);
148 	move(0, 0);
149 
150 	/* Draw a grid. */
151 	for (line = 1; line < y_limit; line += grid_y) {
152 		for (col = 1; col < x_limit; col = col + grid_x) {
153 			xpos = col;
154 			while ((xpos < col + grid_x - 1) && (xpos <
155 			    x_limit)) {
156 				mvaddch(line + grid_y - 1, xpos, 113 |
157 				    A_ALTCHARSET);
158 				xpos++;
159 			}
160 			if (xpos < x_limit)
161 				mvaddch(line + grid_y - 1, xpos, 110 |
162 				    A_ALTCHARSET);
163 		}
164 		ypos = line;
165 		while (ypos < line + grid_y - 1) {
166 			for (col = grid_x - 1; col < x_limit; col += grid_x) {
167 				mvaddch(ypos, col + 1, 120 | A_ALTCHARSET);
168 			}
169 			ypos++;
170 		}
171 	}
172 
173 	for (line = 1; line < y_limit; line += grid_y) {
174 		mvaddch(line + grid_y - 1, 0, 116 | A_ALTCHARSET);
175 		mvaddch(line + grid_y - 1, x_limit, 117 | A_ALTCHARSET);
176 
177 		ypos = line;
178 		while (ypos < line + grid_y - 1) {
179 			mvaddch(ypos, 0, 120 | A_ALTCHARSET);
180 			mvaddch(ypos, x_limit, 120 | A_ALTCHARSET);
181 			ypos++;
182 		}
183 	}
184 
185 	for (col = 1; col < x_limit; col += grid_x) {
186 		mvaddch(0, col + grid_x - 1, 119 | A_ALTCHARSET);
187 		mvaddch(y_limit, col + grid_x - 1, 118 | A_ALTCHARSET);
188 
189 		xpos = col;
190 		while ((xpos < col + grid_x - 1) && (xpos < x_limit)) {
191 			mvaddch(0, xpos, 113 | A_ALTCHARSET);
192 			mvaddch(y_limit, xpos, 113 | A_ALTCHARSET);
193 			xpos++;
194 		}
195 	}
196 
197 	mvaddch(0, 0, 108 | A_ALTCHARSET);
198 	mvaddch(0, x_limit, 107 | A_ALTCHARSET);
199 	mvaddch(y_limit, 0, 109 | A_ALTCHARSET);
200 	mvaddch(y_limit, x_limit, 106 | A_ALTCHARSET);
201 
202 	/* Draw a white circle. */
203 	for (i = 1; i < y_limit; i++) {
204 		for (col = circle_pos[i][0]; col <= circle_pos[i][1]; col++) {
205 			mvaddch(i, col, 32 | A_REVERSE);
206 		}
207 	}
208 
209 	/* Add title segment. */
210 	for (i = roundf(1 * grid_unit); i < roundf(2 * grid_unit); i++) {
211 		if (colourOK)
212     			attrset(COLOR_PAIR(COLOR_BLACK));
213 		else
214 			attrset(A_NORMAL);
215 
216 		for (col = roundf((4 * grid_unit * 2) +
217 		    circle_pos[y_limit / 2][0]); col <= roundf((9 * grid_unit
218 		    * 2) + circle_pos[y_limit / 2][0]); col++)
219 			mvaddch(i, col, ' ');
220 	}
221 
222 	i = roundf(1.4 * grid_unit);
223 
224 	if (!colourOK)
225 		attrset(A_NORMAL);
226 
227 	col = y_limit - (strlen(title) / 2) + circle_pos[y_limit / 2][0];
228 		mvprintw(i, col, "%s", title);
229 
230 	/* Add black segments at top. */
231 	for (line = roundf(2 * grid_unit); line < 4 * grid_unit; line++) {
232 		if (colourOK)
233     			attrset(COLOR_PAIR(COLOR_BLACK));
234 		else
235 			attrset(A_NORMAL);
236 
237 		for (col = 0; col <= roundf((3.5 * grid_unit * 2)); col++) {
238 				xpos = col + circle_pos[y_limit / 2][0];
239 				if (xpos >= circle_pos[line][0] &&
240 				    xpos <= circle_pos[line][1])
241 					mvaddch(line, xpos, ' ');
242 		}
243 
244 		for (col = roundf((9.5 * grid_unit * 2)); col <
245 		    roundf((13 * grid_unit * 2)); col++) {
246 				xpos = col + circle_pos[y_limit / 2][0];
247 				if (xpos >= circle_pos[line][0] &&
248 				    xpos <= circle_pos[line][1])
249 					mvaddch(line, xpos, ' ');
250 		}
251 	}
252 
253 	/* Add black and white squares close to top. */
254 	int gap = (circle_pos[(int)(5 * grid_unit)][1] -
255 	    circle_pos[(int)(5 * grid_unit)][0]) / 13;
256 
257 	for (i = roundf(3 * grid_unit); i < roundf(4 * grid_unit); i++) {
258 		for (xpos = 0; xpos <= x_limit; xpos += 2 * gap) {
259 			if (colourOK)
260     				attrset(COLOR_PAIR(COLOR_BLACK));
261 			else
262 				attrset(A_NORMAL);
263 
264 			for (col = xpos; col < xpos + gap; col++) {
265 				if (col >= circle_pos[i][0] &&
266 				    col <= circle_pos[i][1])
267 					mvaddch(i, col, ' ');
268 			}
269 
270 			if (colourOK)
271     				attrset(COLOR_PAIR(COLOR_WHITE));
272 			else
273 				attrset(A_REVERSE);
274 
275 			for (col = xpos + gap ; col < xpos + (2 * gap);
276 			    col++) {
277 				if (col >= circle_pos[i][0] &&
278 				    col <= circle_pos[i][1])
279 					mvaddch(i, col, ' ');
280 			}
281 		}
282 	}
283 
284 	/* Add colour bars. */
285 	for (i = 0; i < numcolours; i++) {
286 		colour = colour_list[i];
287 		if (colourOK)
288 	    		attrset(COLOR_PAIR(colour));
289 		else if (i & 1)
290 			attrset(A_NORMAL);
291 		else
292 			attrset(A_REVERSE);
293 
294 		if (i == 0)
295 			spacing_start = 0;
296 		else
297 			spacing_start = (spacing * i) + spacing_residual;
298 
299 		if (i == numcolours - 1)
300 			spacing_end = circle_pos[y_limit / 2][1];
301 		else
302 			spacing_end = (spacing * (i + 1)) + spacing_residual;
303 
304 	    	for (line = roundf(4 * grid_unit); line < (y_limit / 2);
305 		    line++) {
306 			for (col = spacing_start; col < spacing_end; col++) {
307 				xpos = col + circle_pos[y_limit / 2][0];
308 				if (xpos >= circle_pos[line][0] &&
309 				    xpos <= circle_pos[line][1])
310 	    				mvprintw(line, xpos, " ");
311 			}
312 	    	}
313 	}
314 
315 	/* Add black segment under centre line. */
316 	for (line = y_limit / 2; line < (9.5 * grid_unit); line++) {
317 		if (colourOK)
318     			attrset(COLOR_PAIR(COLOR_BLACK));
319 		else
320 			attrset(A_NORMAL);
321 
322 		for (col = circle_pos[line][0]; col <= circle_pos[line][1];
323 		    col++)
324 			mvaddch(line, col, ' ');
325 
326 		for (col = roundf((1.5 * grid_unit * 2)); col <
327 		    roundf((4.3 * grid_unit * 2)); col++) {
328 				xpos = col + circle_pos[y_limit / 2][0];
329 				if (xpos >= circle_pos[line][0] &&
330 				    xpos < circle_pos[line][1])
331 					mvaddch(line, xpos, 120 | A_ALTCHARSET);
332 		}
333 
334 		for (col = roundf((4.3 * grid_unit * 2)); col <
335 		    roundf((7.6 * grid_unit * 2)); col++) {
336 				xpos = col + circle_pos[y_limit / 2][0];
337 				if (xpos >= circle_pos[line][0] &&
338 				    xpos < circle_pos[line][1])
339 					mvaddch(line, xpos, '|');
340 		}
341 
342 		for (col = roundf((7.6 * grid_unit * 2)); col <
343 		    roundf((11.5 * grid_unit * 2)); col++) {
344 				xpos = col + circle_pos[y_limit / 2][0];
345 				if (xpos >= circle_pos[line][0] &&
346 				    xpos < circle_pos[line][1])
347 					mvaddch(line, xpos, 97 | A_ALTCHARSET);
348 		}
349 	}
350 
351 	/* Add black segment close to bottom. */
352 	for (line = roundf(9.5 * grid_unit); line <= (10.5 * grid_unit);
353 	    line++) {
354 		if (colourOK)
355     			attrset(COLOR_PAIR(COLOR_BLACK));
356 		else
357 			attrset(A_NORMAL);
358 
359 		for (col = roundf((0 * grid_unit * 2)); col <
360 		    roundf((4 * grid_unit * 2)); col++) {
361 				xpos = col + circle_pos[y_limit / 2][0];
362 				if (xpos >= circle_pos[line][0] &&
363 				    xpos < circle_pos[line][1])
364 					mvaddch(line, xpos, ' ');
365 		}
366 
367 		for (col = roundf((4 * grid_unit * 2)); col <
368 		    roundf((6.5 * grid_unit * 2)); col++) {
369 				xpos = col + circle_pos[y_limit / 2][0];
370 				if (xpos >= circle_pos[line][0] &&
371 				    xpos < circle_pos[line][1])
372 					mvaddch(line, xpos, 97 | A_ALTCHARSET);
373 		}
374 
375 		if (colourOK)
376     			attrset(COLOR_PAIR(COLOR_WHITE));
377 		else
378 			attrset(A_REVERSE);
379 
380 		for (col = roundf((6.5 * grid_unit * 2)); col <
381 		    roundf((9 * grid_unit * 2)); col++) {
382 				xpos = col + circle_pos[y_limit / 2][0];
383 				if (xpos >= circle_pos[line][0] &&
384 				    xpos < circle_pos[line][1])
385 					mvaddch(line, xpos, 97 | A_ALTCHARSET);
386 		}
387 
388 		for (col = roundf((9 * grid_unit * 2)); col <
389 		    roundf((13 * grid_unit * 2)); col++) {
390 				xpos = col + circle_pos[y_limit / 2][0];
391 				if (xpos >= circle_pos[line][0] &&
392 				    xpos < circle_pos[line][1])
393 					mvaddch(line, xpos, ' ');
394 		}
395 	}
396 
397 	/* Add name segment close to bottom. */
398 	for (line = roundf(10.5 * grid_unit); line < (12 * grid_unit);
399 	    line++) {
400 		if (colourOK)
401     			attrset(COLOR_PAIR(COLOR_BLACK));
402 		else
403 			attrset(A_NORMAL);
404 
405 		for (col = roundf(3.5 * grid_unit * 2); col <= roundf(9.5 *
406 		    grid_unit * 2); col++) {
407 			xpos = col + circle_pos[y_limit / 2][0];
408 			if (xpos >= circle_pos[line][0] &&
409 			    xpos < circle_pos[line][1])
410 				mvaddch(line, xpos, ' ');
411 		}
412 
413 		if (colourOK)
414     			attrset(COLOR_PAIR(COLOR_WHITE));
415 		else
416 			attrset(A_REVERSE);
417 
418 		for (col = roundf(0 * grid_unit * 2); col <= roundf(3.5 *
419 		    grid_unit * 2); col++) {
420 			xpos = col + circle_pos[y_limit / 2][0];
421 			if (xpos >= circle_pos[line][0] &&
422 			    xpos < circle_pos[line][1])
423 				mvaddch(line, xpos, ' ');
424 		}
425 
426 		for (col = roundf(9.5 * grid_unit * 2); col <= roundf(13 *
427 		    grid_unit * 2); col++) {
428 			xpos = col + circle_pos[y_limit / 2][0];
429 			if (xpos >= circle_pos[line][0] &&
430 			    xpos < circle_pos[line][1])
431 				mvaddch(line, xpos, ' ');
432 		}
433 	}
434 
435 	/* Add yellow segment at bottom. */
436 	for (line = 12 * grid_unit; line < y_limit; line++) {
437 		if (colourOK)
438     			attrset(COLOR_PAIR(COLOR_YELLOW));
439 		else
440     			attrset(A_REVERSE);
441 
442 
443 		for (col = circle_pos[line][0]; col <= circle_pos[line][1];
444 		    col++)
445 			mvaddch(line, col, ' ');
446 
447 		if (colourOK)
448     			attrset(COLOR_PAIR(COLOR_RED));
449 		else
450     			attrset(A_NORMAL);
451 
452 		for (col = roundf((6 * grid_unit * 2)); col <
453 		    roundf((7 * grid_unit * 2)); col++) {
454 				xpos = col + circle_pos[y_limit / 2][0];
455 				if (xpos >= circle_pos[line][0] &&
456 				    xpos < circle_pos[line][1])
457 					mvaddch(line, xpos, ' ');
458 		}
459 	}
460 
461 	if (colourOK)
462     		attrset(COLOR_PAIR(COLOR_BLACK));
463 	else
464     		attrset(A_NORMAL);
465 
466 	for (line = 6 * grid_unit; line <= (7 * grid_unit) + 1; line++) {
467 		if (colourOK)
468     			attrset(COLOR_PAIR(COLOR_BLACK));
469 		else
470     			attrset(A_NORMAL);
471 
472 		col = x_limit / 2;
473 		if (line != a_axis) {
474 			mvaddch(line, col - 1, ' ');
475 			mvaddch(line, col, 120 | A_ALTCHARSET);
476 			mvaddch(line, col + 1, ' ');
477 		}
478 	}
479 
480 	line = y_limit / 2;
481 	for (col = 1; col < x_limit; col = col + grid_x) {
482 		xpos = col;
483 		while (xpos < col + grid_x - 1) {
484 			if (xpos >= circle_pos[line][0]
485 			    && xpos < circle_pos[line][1])
486 				mvaddch(line, xpos, 113 | A_ALTCHARSET);
487 			xpos++;
488 		}
489 		if (xpos >= circle_pos[line][0] && xpos < circle_pos[line][1])
490 			mvaddch(line, xpos, 110 | A_ALTCHARSET);
491 	}
492 
493 	line = y_limit / 2;
494 	col = x_limit / 2;
495 	mvaddch(line, col, 110 | A_ALTCHARSET);
496 	mvaddch(line, col - 1, 113 | A_ALTCHARSET);
497 	mvaddch(line, col + 1, 113 | A_ALTCHARSET);
498 
499 	refresh();
500 
501 	getch();
502 
503 	endwin();
504 
505 	return EXIT_SUCCESS;
506 }
507 
508