|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T g
Length: 8962 (0x2302) Types: TextFile Names: »game.c«
└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987 └─⟦this⟧ »EUUGD18/General/Tt/game.c«
/***************************************************************************\ |* *| |* game.c: A version of Tetris to run on ordinary terminals, *| |* (ie., not needing a workstation, so should available *| |* to peasant Newwords+ users. This is the module that *| |* actually plays the game, (ie. moves things down the *| |* screen, select(2)s on the keyboard, and so on) *| |* *| |* Author: Mike Taylor (mirk@uk.ac.warwick.cs) *| |* Started: Fri May 26 12:26:05 BST 1989 *| |* *| \***************************************************************************/ #include <sys/types.h> #include <sys/time.h> #include <stdio.h> #include <errno.h> #include "tt.h" #include "game.h" #include "screen.h" #include "pieces.h" #include "utils.h" /*-------------------------------------------------------------------------*/ extern long int random (); /*-------------------------------------------------------------------------*/ char left_key = LEFT_KEY; /* Move piece left */ char right_key = RIGHT_KEY; /* Move piece right */ char rotate_key = ROTATE_KEY; /* Rotate piece anticlockwise */ char drop_key = DROP_KEY; /* Drop piece to bottom of screen */ char susp_key = SUSP_KEY; /* Suspend. I'm sorry if its confusing */ char quit_key = QUIT_KEY; /* Quit. I'm sorry if its confusing */ char cheat_key = CHEAT_KEY; /* Do whatever I eventaully make it do */ /***************************************************************************\ |* *| |* Oh good grief, what on earth is the point of putting a huge, wobbly *| |* box-comment in front of this titchy little self-explanatory function? *| |* The name tells the whole story, it's perfectly strightforward, it's *| |* not a proposition from Witgenstein. I wouldn't bother commenting it *| |* at all if it wasn't for the fact that I know I'd feel guilty in the *| |* morning. I mean, be fair, you don't really want a program where all *| |* the functions *except one* have box-comments explaining them, do you? *| |* Ah well, here we go for completion's sake: *| |* *| |* The function clear_board() takes no parameters and returns no value. *| |* It clears the board. The end. *| |* *| \***************************************************************************/ void clear_board () { int i, j; for (i = 0; i < GAME_DEPTH+4; i++) for (j = 0; j < GAME_WIDTH; j++) board[i][j] = PI_EMPTY; } /***************************************************************************\ |* *| |* The function play_game is the main loop in which the game of Tetris *| |* is implemented. It takes no parameters, and returns no value. The *| |* time at which it returns no value is the end of a game. The main *| |* loop-component is a select(2) which polls the keyboard in real-time. *| |* If you use a non-Berkeley UNIX(tm), you're well cheesed. *| |* *| |* Actually, I have to admit I'm not proud of this one. It must be one *| |* of the messiest functions I've written in years, in terms of nested *| |* loops with ad-hoc exit conditions, re-used variables, general *| |* obfuscation and so on. I wanna make it quite clear that I make no *| |* apologies for my use of "goto", which remains a highly desirable *| |* language feature, and is still the most elegant way of coding many *| |* things, but I gotta admit, overall this one is bit of a chicken. *| |* *| \***************************************************************************/ void play_game () { int i; /* Position of origin down board */ int j; /* Position of origin across board */ int k; /* Loop variable when i,j are invariant */ int piece_no; /* Type of piece currently falling */ int orient; /* Which way it is facing */ int pause_flag = 0; /* We won't pause unless told to */ int presses_left; /* Futher moves possible this drop */ int free_left = game_level; /* Number of pieces to drop at start */ fd_set read_fds; /* Select must look at stdin */ long int total_time = 200000; /* Allow 1/4 second before falling */ struct timeval timeout; /* Time before piece drops in select(2) */ char ch; /* Keystroke (command) */ score = no_levels = no_pieces = 0; update_scores (); clear_board (); while (1) { piece_no = (int) (random () % NO_PIECES); orient = (int) (random () % NO_ORIENTS); i = -2; /* Start falling from off-screen */ if (free_left > 0) /* If we have random starting-pieces */ j = 1 + (int) (random () % (GAME_WIDTH-2)); else /* places them randomly, otherwise */ j = GAME_WIDTH/2; /* put the piece in the middle */ if (!can_place (piece_no, orient, i, j)) { for (k = 0; k < 9; k++) { /* Crude but (hopefully) effective */ draw_piece (piece_no, orient, i, j, PD_ERASE); myrefresh (); usleep (80000); draw_piece (piece_no, orient, i, j, PD_DRAW); myrefresh (); usleep (80000); } break; /* End of game - piece won't fit */ } if (free_left != 0) { /* If there are pieces to be dropped, */ if (free_left > 0) /* And the number is positiive, */ free_left--; /* Then decrement it, otherwise */ else /* increment it, in any case, bring */ free_left++; /* it closer to zero if it gets to zero */ if (free_left == 0) /* set a flag so that the game will */ pause_flag = 1; /* pause. Then go to the bit of code */ goto DROP_PIECE; /* that drops it. */ } while (1) { presses_left = NO_MOVES; draw_piece (piece_no, orient, i, j, PD_DRAW); update_scores (); myrefresh (); while (1) { FD_ZERO (&read_fds); FD_SET (0, &read_fds); timeout.tv_sec = 0; timeout.tv_usec = total_time; switch (select (((presses_left > 0) && (i >= 0)), &read_fds, (fd_set*) NULL, (fd_set*) NULL, &timeout)) { case -1: if (errno != EINTR) die (LE_SELECT, "select(2) failed in play_game()"); /* Otherwise fall through, goto TIMEOUT */ case 0: goto TIMEOUT; default: if (read (0, &ch, 1) == -1) die (LE_READ, "read(2) failed in play_game()"); if (ch == REFRESH_KEY) hoopy_refresh (); if ((ch != left_key) && (ch != right_key) && (ch != rotate_key) && (ch != drop_key) && (ch != quit_key) && (ch != susp_key) && (ch != cheat_key)) break; presses_left--; if (ch == left_key) { if (can_place (piece_no, orient, i, j-1)) { draw_piece (piece_no, orient, i, j, PD_ERASE); j--; draw_piece (piece_no, orient, i, j, PD_DRAW); myrefresh (); } } else if (ch == right_key) { if (can_place (piece_no, orient, i, j+1)) { draw_piece (piece_no, orient, i, j, PD_ERASE); j++; draw_piece (piece_no, orient, i, j, PD_DRAW); myrefresh (); } } else if (ch == rotate_key) { int new_or = ((rotate_backwards == 0) ? ((orient+1)%NO_ORIENTS) : ((orient-1)%NO_ORIENTS+NO_ORIENTS*(orient == 0))); if (can_place (piece_no, new_or, i, j)) { draw_piece (piece_no, orient, i, j, PD_ERASE); orient = new_or; draw_piece (piece_no, orient, i, j, PD_DRAW); myrefresh (); } } else if (ch == drop_key) { DROP_PIECE: while (can_place (piece_no, orient, i+1, j)) { draw_piece (piece_no, orient, i, j, PD_ERASE); i++; draw_piece (piece_no, orient, i, j, PD_DRAW); myrefresh (); } goto TIMEOUT; } else if (ch == quit_key) return; else if (ch == cheat_key) { print_msg ("CHEAT!"); total_time = 0L; } else if (ch == susp_key) { print_msg ("Paused"); (void) read (0, &ch, 1); print_msg (""); } break; } } TIMEOUT: if (!can_place (piece_no, orient, i+1, j)) { place_piece (piece_no, orient, i, j); score += pieces[piece_no].points; no_pieces++; update_scores (); myrefresh (); for (i = 0; i < GAME_DEPTH; i++) { for (j = 0; j < GAME_WIDTH; j++) if (board[i+4][j] == PI_EMPTY) break; if (j == GAME_WIDTH) { no_levels++; score += 10; update_scores (); for (k = i; k > 0; k--) for (j = 0; j < GAME_WIDTH; j++) board[k+4][j] = board[k+3][j]; for (j = 0; j < GAME_WIDTH; j++) board[4][j] = PI_EMPTY; draw_board (); myrefresh (); i--; /* Check the new row i */ } } if (pause_flag) { /* If we are pausing after this drop ... */ pause_flag = 0; /* Ensure we don't do so next time. */ flush_keyboard (); print_msg ("Continue"); (void) read (0, &ch, 1); print_msg (""); } break; /* End of fall - piece has hit floor */ } draw_piece (piece_no, orient, i, j, PD_ERASE); i++; } myrefresh (); if (total_time != 0) total_time -= 100; } } /*-------------------------------------------------------------------------*/