source: mainline/uspace/app/tetris/tetris.c@ 87822ce

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 87822ce was 87822ce, checked in by Jiri Svoboda <jiri@…>, 4 years ago

Avoid infinite loop when console communication is broken

Need to make sure callers of console_get_event_timeout() can distinguish
between timeout and I/O error. Fix all callers of console_get_event()
and console_get_event_timeout() not to enter infinite loop when console
connection is broken. Also avoid setting of errno variable.

  • Property mode set to 100644
File size: 8.5 KB
RevLine 
[79ae36dd]1/*
2 * Copyright (c) 2011 Martin Decky
3 * All rights reserved.
[e9a3c52]4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
[79ae36dd]9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** Attributations
30 *
31 * tetris.c 8.1 (Berkeley) 5/31/93
32 * NetBSD: tetris.c,v 1.2 1995/04/22 07:42:47 cgd
33 * OpenBSD: tetris.c,v 1.21 2006/04/20 03:24:12 ray
34 *
35 * Based upon BSD Tetris
36 *
37 * Copyright (c) 1992, 1993
38 * The Regents of the University of California.
39 * Distributed under BSD license.
40 *
41 * This code is derived from software contributed to Berkeley by
42 * Chris Torek and Darren F. Provine.
[e9a3c52]43 *
44 */
45
[a63966d]46/** @addtogroup tetris
[ebe70f1]47 * @{
[b2951e2]48 */
49/** @file
50 */
51
[9260102]52static volatile const char copyright[] =
[3bacee1]53 "@(#) Copyright (c) 1992, 1993\n"
54 "\tThe Regents of the University of California. All rights reserved.\n";
[e9a3c52]55
[bd41ac52]56#include <time.h>
[d4b9d28]57#include <errno.h>
[76d0981d]58#include <stdbool.h>
[e9a3c52]59#include <stdio.h>
60#include <stdlib.h>
[582a0b8]61#include <stdint.h>
[19f857a]62#include <str.h>
[ebe70f1]63#include <getopt.h>
[e9a3c52]64#include "scores.h"
65#include "screen.h"
66#include "tetris.h"
67
[ebe70f1]68cell board[B_SIZE];
69
70int Rows;
71int Cols;
72
[e9a3c52]73const struct shape *curshape;
74const struct shape *nextshape;
75
[ebe70f1]76long fallrate;
77int score;
[02246b8]78char key_msg[116];
[ebe70f1]79int showpreview;
80int classic;
81
82static void elide(void);
83static void setup_board(void);
84static const struct shape *randshape(void);
85
86static void usage(void);
87
88static int firstgame = 1;
[e9a3c52]89
90/*
[ebe70f1]91 * Set up the initial board. The bottom display row is completely set,
92 * along with another (hidden) row underneath that. Also, the left and
[e9a3c52]93 * right edges are set.
94 */
[ebe70f1]95static void setup_board(void)
[e9a3c52]96{
97 int i;
[ebe70f1]98 cell *p = board;
[a35b458]99
[e9a3c52]100 for (i = B_SIZE; i; i--)
[ebe70f1]101 *p++ = (i <= (2 * B_COLS) || (i % B_COLS) < 2) ? 0x0000ff : 0x000000;
[e9a3c52]102}
103
104/*
105 * Elide any full active rows.
106 */
[ebe70f1]107static void elide(void)
[e9a3c52]108{
109 int rows = 0;
[ebe70f1]110 int i;
111 int j;
112 int base;
[e9a3c52]113 cell *p;
[a35b458]114
[e9a3c52]115 for (i = A_FIRST; i < A_LAST; i++) {
116 base = i * B_COLS + 1;
117 p = &board[base];
[84239b1]118 j = B_COLS - 2;
119 while (*p++ != 0) {
[e9a3c52]120 if (--j <= 0) {
[ebe70f1]121 /* This row is to be elided */
[e9a3c52]122 rows++;
[ebe70f1]123 memset(&board[base], 0, sizeof(cell) * (B_COLS - 2));
[a35b458]124
[e9a3c52]125 scr_update();
126 tsleep();
[a35b458]127
[e9a3c52]128 while (--base != 0)
129 board[base + B_COLS] = board[base];
[a35b458]130
[e9a3c52]131 scr_update();
132 tsleep();
[a35b458]133
[e9a3c52]134 break;
135 }
136 }
137 }
[a35b458]138
[e9a3c52]139 switch (rows) {
140 case 1:
141 score += 10;
142 break;
143 case 2:
144 score += 30;
145 break;
146 case 3:
147 score += 70;
148 break;
149 case 4:
150 score += 150;
151 break;
152 default:
153 break;
154 }
155}
156
[ebe70f1]157const struct shape *randshape(void)
[e9a3c52]158{
[c718bda]159 const struct shape *tmp = &shapes[rand() % 7];
[ebe70f1]160 int i;
[c718bda]161 int j = rand() % 4;
[a35b458]162
[e9a3c52]163 for (i = 0; i < j; i++)
[ebe70f1]164 tmp = &shapes[classic ? tmp->rotc : tmp->rot];
[a35b458]165
[e9a3c52]166 return (tmp);
167}
[c594489]168
169static void srandomdev(void)
170{
[bd41ac52]171 struct timespec ts;
[a35b458]172
[bd41ac52]173 getrealtime(&ts);
174 srand(ts.tv_sec + ts.tv_nsec / 100000000);
[c594489]175}
[e9a3c52]176
[1b20da0]177static void tetris_menu_draw(int level)
[9996ed5]178{
[ebe70f1]179 clear_screen();
180 moveto(5, 10);
[1abcf1d]181 puts("Tetris\n");
[a35b458]182
[ebe70f1]183 moveto(8, 10);
184 printf("Level = %d (press keys 1 - 9 to change)", level);
185 moveto(9, 10);
[3bacee1]186 printf("Preview is %s (press 'p' to change)", (showpreview ? "on " : "off"));
[ebe70f1]187 moveto(12, 10);
188 printf("Press 'h' to show hiscore table.");
189 moveto(13, 10);
190 printf("Press 's' to start game.");
191 moveto(14, 10);
192 printf("Press 'q' to quit game.");
193 moveto(20, 10);
194 printf("In game controls:");
195 moveto(21, 0);
[1abcf1d]196 printf("%s", key_msg);
[9996ed5]197}
198
[ebe70f1]199static int tetris_menu(int *level)
[9996ed5]200{
201 tetris_menu_draw(*level);
[76d0981d]202 while (true) {
[ebe70f1]203 int i = getchar();
[87822ce]204 if (i < 0)
205 return 0;
[a35b458]206
[3bacee1]207 switch (i) {
208 case 'p':
209 showpreview = !showpreview;
210 moveto(9, 21);
211 if (showpreview)
212 printf("on ");
213 else
214 printf("off");
215 break;
216 case 'h':
217 loadscores();
218 showscores(firstgame);
219 tetris_menu_draw(*level);
220 break;
221 case 's':
222 firstgame = 0;
223 return 1;
224 case 'q':
225 return 0;
226 case '1':
227 case '2':
228 case '3':
229 case '4':
230 case '5':
231 case '6':
232 case '7':
233 case '8':
234 case '9':
235 *level = i - '0';
236 moveto(8, 18);
237 printf("%d", *level);
238 break;
[9996ed5]239 }
240 }
241}
242
[ebe70f1]243int main(int argc, char *argv[])
[e9a3c52]244{
[ebe70f1]245 int pos;
246 int c;
[a405563]247 const char *keys;
[e9a3c52]248 int level = 2;
249 char key_write[6][10];
[ebe70f1]250 int i;
251 int j;
252 int ch;
[a35b458]253
[79ae36dd]254 console = console_init(stdin, stdout);
[a35b458]255
[e9a3c52]256 keys = "jkl pq";
[a35b458]257
[9996ed5]258 classic = 0;
[79ae36dd]259 showpreview = 1;
[a35b458]260
[ebe70f1]261 while ((ch = getopt(argc, argv, "ck:ps")) != -1)
[3bacee1]262 switch (ch) {
[ebe70f1]263 case 'c':
264 /*
265 * this means:
266 * - rotate the other way
267 * - no reverse video
268 */
269 classic = 1;
270 break;
271 case 'k':
272 if (str_size(keys = optarg) != 6)
273 usage();
274 break;
275 case 'p':
276 showpreview = 1;
277 break;
278 case 's':
279 showscores(0);
280 exit(0);
281 default:
282 usage();
283 }
[a35b458]284
[ebe70f1]285 argc -= optind;
286 argv += optind;
[a35b458]287
[ebe70f1]288 if (argc)
289 usage();
[a35b458]290
[e9a3c52]291 for (i = 0; i <= 5; i++) {
[ebe70f1]292 for (j = i + 1; j <= 5; j++) {
[f538ef3]293 if (keys[i] == keys[j]) {
294 fprintf(stderr, "duplicate command keys specified.");
295 abort();
296 }
[e9a3c52]297 }
[a35b458]298
[e9a3c52]299 if (keys[i] == ' ')
[ebe70f1]300 str_cpy(key_write[i], sizeof(key_write[i]), "<space>");
[e9a3c52]301 else {
302 key_write[i][0] = keys[i];
303 key_write[i][1] = '\0';
304 }
305 }
[a35b458]306
[ebe70f1]307 snprintf(key_msg, sizeof(key_msg),
308 "%s - left %s - rotate %s - right %s - drop %s - pause %s - quit",
309 key_write[0], key_write[1], key_write[2], key_write[3],
310 key_write[4], key_write[5]);
[a35b458]311
[9996ed5]312 scr_init();
[d4b9d28]313 if (loadscores() != EOK)
314 initscores();
315
[9996ed5]316 while (tetris_menu(&level)) {
[237867d]317 fallrate = 1000000 / level;
[a35b458]318
[9996ed5]319 scr_clear();
320 setup_board();
[a35b458]321
[9996ed5]322 srandomdev();
323 scr_set();
[a35b458]324
[ebe70f1]325 pos = A_FIRST * B_COLS + (B_COLS / 2) - 1;
[9996ed5]326 nextshape = randshape();
327 curshape = randshape();
[a35b458]328
[9996ed5]329 scr_msg(key_msg, 1);
[a35b458]330
[76d0981d]331 while (true) {
[9996ed5]332 place(curshape, pos, 1);
333 scr_update();
334 place(curshape, pos, 0);
335 c = tgetchar();
336 if (c < 0) {
337 /*
338 * Timeout. Move down if possible.
339 */
340 if (fits_in(curshape, pos + B_COLS)) {
341 pos += B_COLS;
342 continue;
343 }
[a35b458]344
[9996ed5]345 /*
346 * Put up the current shape `permanently',
347 * bump score, and elide any full rows.
348 */
349 place(curshape, pos, 1);
350 score++;
351 elide();
[a35b458]352
[9996ed5]353 /*
354 * Choose a new shape. If it does not fit,
355 * the game is over.
356 */
357 curshape = nextshape;
358 nextshape = randshape();
[ebe70f1]359 pos = A_FIRST * B_COLS + (B_COLS / 2) - 1;
[a35b458]360
[9996ed5]361 if (!fits_in(curshape, pos))
362 break;
[a35b458]363
[e9a3c52]364 continue;
365 }
[a35b458]366
[e9a3c52]367 /*
[9996ed5]368 * Handle command keys.
[e9a3c52]369 */
[9996ed5]370 if (c == keys[5]) {
371 /* quit */
[e9a3c52]372 break;
373 }
[a35b458]374
[9996ed5]375 if (c == keys[4]) {
376 static char msg[] =
377 "paused - press RETURN to continue";
[a35b458]378
[9996ed5]379 place(curshape, pos, 1);
380 do {
381 scr_update();
382 scr_msg(key_msg, 0);
383 scr_msg(msg, 1);
[79ae36dd]384 console_flush(console);
385 } while (!twait());
[a35b458]386
[9996ed5]387 scr_msg(msg, 0);
388 scr_msg(key_msg, 1);
389 place(curshape, pos, 0);
390 continue;
391 }
[a35b458]392
[9996ed5]393 if (c == keys[0]) {
394 /* move left */
395 if (fits_in(curshape, pos - 1))
396 pos--;
397 continue;
398 }
[a35b458]399
[9996ed5]400 if (c == keys[1]) {
401 /* turn */
[ebe70f1]402 const struct shape *new =
403 &shapes[classic ? curshape->rotc : curshape->rot];
[a35b458]404
[9996ed5]405 if (fits_in(new, pos))
406 curshape = new;
407 continue;
408 }
[a35b458]409
[9996ed5]410 if (c == keys[2]) {
411 /* move right */
412 if (fits_in(curshape, pos + 1))
413 pos++;
414 continue;
415 }
[a35b458]416
[9996ed5]417 if (c == keys[3]) {
418 /* move to bottom */
419 while (fits_in(curshape, pos + B_COLS)) {
420 pos += B_COLS;
421 score++;
422 }
423 continue;
424 }
[a35b458]425
[9996ed5]426 if (c == '\f') {
427 scr_clear();
428 scr_msg(key_msg, 1);
429 }
430 }
[a35b458]431
[9996ed5]432 scr_clear();
[d4b9d28]433 loadscores();
[9996ed5]434 insertscore(score, level);
[d4b9d28]435 savescores();
[ebe70f1]436 score = 0;
[0aa024b1]437 }
[a35b458]438
[0aa024b1]439 scr_clear();
[ebe70f1]440 printf("\nGame over.\n");
[9996ed5]441 scr_end();
[a35b458]442
[501a8ba]443 return 0;
[e9a3c52]444}
445
[ebe70f1]446void usage(void)
[e9a3c52]447{
[0b2d369]448 fprintf(stderr, "%s", copyright);
[ebe70f1]449 fprintf(stderr, "usage: tetris [-ps] [-k keys]\n");
[e9a3c52]450 exit(1);
451}
[b2951e2]452
453/** @}
454 */
Note: See TracBrowser for help on using the repository browser.