home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Games / Tetris Light 1.0.3 / source / tetris.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-07-07  |  13.3 KB  |  473 lines  |  [TEXT/CWIE]

  1. /* ----------------------------------------------------------------------
  2. File: tetris.c
  3.  
  4. Purpose:    This module implements the Tetris game.  It has been written
  5.             in a generic manner to aid porting to other platforms.  Only
  6.             the timer and random number generator are Macintosh specific.
  7.             
  8. Tetris Light - a simple implementation of a Tetris game
  9. Copyright © 1993-1996 Hoylen Sue
  10.  
  11. Updated for CW9 by Paul Celestin
  12. Questions about this version should go to me at celestin@celestin.com
  13.  
  14. This program is free software; you can redistribute it and/or modify
  15. it under the terms of the GNU General Public License as published by
  16. the Free Software Foundation; either version 2 of the License, or
  17. (at your option) any later version.
  18.  
  19. This program is distributed in the hope that it will be useful,
  20. but WITHOUT ANY WARRANTY; without even the implied warranty of
  21. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  22. GNU General Public License for more details.
  23.  
  24. You should have received a copy of the GNU General Public License
  25. along with this program; see the file COPYING.  If not, write to the
  26. Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  27. ---------------------------------------------------------------------- */
  28.  
  29. #include "local.h"
  30. #include "game.h"
  31. #include "tetris.h"
  32.  
  33. /*--------------------------------------------------------------------*/
  34.  
  35. /* These items determine the levels in the game, a table of scores
  36.    which indicate the next level is reached, and the time delay between
  37.    block drops for the levels.  The higher the level, the faster they
  38.    drop.  These time delays are in ticks (60ths of a second). */
  39.  
  40. #define NUMBER_LEVELS        11
  41.  
  42. static unsigned int level_threshold[NUMBER_LEVELS] =
  43.     {  1,  2,  3,  4,  5,  6,  7, 25, 50, 100, 500 };
  44. static long drop_delays[NUMBER_LEVELS + 1] =
  45.     { 60, 50, 40, 30, 27, 25, 23, 20, 19,  18,  17, 16 };
  46.  
  47. /*--------------------------------------------------------------------*/
  48.  
  49. /* Local globals */
  50.  
  51. static Boolean running = FALSE;
  52. static Boolean paused = FALSE;
  53.  
  54. static long drop_time;
  55. static unsigned int score;
  56. static int level = 0;
  57.  
  58. /*--------------------------------------------------------------------*/
  59.  
  60. /* Information on the blocks */
  61.  
  62. typedef struct {
  63.     unsigned char map[MAX_BLOCK_SIZE][MAX_BLOCK_SIZE];
  64.     int width;
  65.     int offset;
  66. } Block_type;
  67.  
  68. static int block;
  69. static int orientation;
  70. static Block_type *current;
  71.  
  72. static int next_block;
  73. static int next_orientation;
  74. static Block_type *next;
  75.  
  76. static int left;
  77. static int top;
  78.  
  79. static Block_type possible[NUMBER_BLOCK_TYPES * 4] = {
  80.  
  81. /* Object 0 - bar */
  82.     
  83. {{{ 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 1, 1, 1, 1 }}, 4, 0 },
  84. {{{ 0, 1, 0, 0 }, { 0, 1, 0, 0 }, { 0, 1, 0, 0 }, { 0, 1, 0, 0 }}, 1, 1 },
  85. {{{ 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 1, 1, 1, 1 }}, 4, 0 },
  86. {{{ 0, 1, 0, 0 }, { 0, 1, 0, 0 }, { 0, 1, 0, 0 }, { 0, 1, 0, 0 }}, 1, 1 },
  87.     
  88. /* Object 1 - L */
  89.     
  90. {{{ 0, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 1, 0, 0 }, { 0, 1, 1, 0 }}, 2, 1 },
  91. {{{ 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 1, 0 }, { 1, 1, 1, 0 }}, 3, 0 },
  92. {{{ 0, 0, 0, 0 }, { 1, 1, 0, 0 }, { 0, 1, 0, 0 }, { 0, 1, 0, 0 }}, 2, 0 },
  93. {{{ 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 1, 1, 1, 0 }, { 1, 0, 0, 0 }}, 3, 0 },
  94.     
  95. /* Object 2 - reverse L */
  96.     
  97. {{{ 0, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 1, 0, 0 }, { 1, 1, 0, 0 }}, 2, 0 },
  98. {{{ 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 1, 1, 1, 0 }, { 0, 0, 1, 0 }}, 3, 0 },
  99. {{{ 0, 0, 0, 0 }, { 0, 1, 1, 0 }, { 0, 1, 0, 0 }, { 0, 1, 0, 0 }}, 2, 1 },
  100. {{{ 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 1, 1, 0 }}, 3, 0 },
  101.     
  102. /* Object 3 - S */
  103.     
  104. {{{ 0, 0, 0, 0 }, { 0, 0, 1, 0 }, { 0, 1, 1, 0 }, { 0, 1, 0, 0 }}, 2, 1 },
  105. {{{ 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 1, 1, 0, 0 }, { 0, 1, 1, 0 }}, 3, 0 },
  106. {{{ 0, 0, 0, 0 }, { 0, 0, 1, 0 }, { 0, 1, 1, 0 }, { 0, 1, 0, 0 }}, 2, 1 },
  107. {{{ 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 1, 1, 0, 0 }, { 0, 1, 1, 0 }}, 3, 0 },
  108.     
  109. /* Object 4 - reverse S */
  110.     
  111. {{{ 0, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 1, 0, 0 }, { 0, 1, 0, 0 }}, 2, 0 },
  112. {{{ 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 1, 1, 0 }, { 1, 1, 0, 0 }}, 3, 0 },
  113. {{{ 0, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 1, 0, 0 }, { 0, 1, 0, 0 }}, 2, 0 },
  114. {{{ 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 1, 1, 0 }, { 1, 1, 0, 0 }}, 3, 0 },
  115.  
  116. /* Object 5 - T */
  117.     
  118. {{{ 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 1, 0, 0 }, { 1, 1, 1, 0 }}, 3, 0 },
  119. {{{ 0, 0, 0, 0 }, { 0, 1, 0, 0 }, { 1, 1, 0, 0 }, { 0, 1, 0, 0 }}, 2, 0 },
  120. {{{ 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 1, 1, 1, 0 }, { 0, 1, 0, 0 }}, 3, 0 },
  121. {{{ 0, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 1, 1, 0 }, { 0, 1, 0, 0 }}, 2, 1 },
  122.     
  123. /* Object 6 - square */
  124.     
  125. {{{ 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 1, 1, 0, 0 }, { 1, 1, 0, 0 }}, 2, 0 },
  126. {{{ 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 1, 1, 0, 0 }, { 1, 1, 0, 0 }}, 2, 0 },
  127. {{{ 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 1, 1, 0, 0 }, { 1, 1, 0, 0 }}, 2, 0 },
  128. {{{ 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 1, 1, 0, 0 }, { 1, 1, 0, 0 }}, 2, 0 }
  129. };
  130.  
  131. /*--------------------------------------------------------------------*/
  132.  
  133. /* Nudge information for rotations. Nudges are used when blocks can't
  134.    be rotated because they are too close to the edge.  The blocks are
  135.    tried at the following positions so that the rotate can succeed. */
  136.  
  137. #define NUM_NUDGES    4
  138.  
  139. static int nudges[NUM_NUDGES] = {0, -1, +1, - 2};
  140.  
  141. /*--------------------------------------------------------------------*/
  142.  
  143. static int random_number(int limit)
  144. /* Returns a random number in the range 0 to limit-1 inclusive. This
  145.    implementation uses the Macintosh QuickDraw random number routine. */
  146. {
  147.     return (Random() & 0x7FFF) % limit;
  148. }
  149.  
  150. /*--------------------------------------------------------------------*/
  151.  
  152. static unsigned long timer(void)
  153. /* Returns the timer clock ticks. This routine uses the Macintosh
  154.    `TickCount' routine. */
  155. {
  156.     return TickCount();
  157. }
  158.  
  159. /*--------------------------------------------------------------------*/
  160.  
  161. static void set_next_drop_time(void)
  162. /* Set the time when the block can next drop.  This time is the current
  163.    time plus the delay time appropriate to the current level. */
  164. {
  165.     drop_time = timer() + drop_delays[level];
  166. }
  167.  
  168. /*--------------------------------------------------------------------*/
  169.  
  170. static void new_block(void)
  171. /* Randomly generates a new block which is stored in the next block
  172.    variables.  The old next block is copied to the current block. */
  173. {
  174.     register int x, y;
  175.     
  176.     /* Copy old next block to current block */
  177.     
  178.     block = next_block;
  179.     orientation = next_orientation;
  180.     current = next;
  181.     
  182.     /* Place the new current block randomly at the top of the field */
  183.     
  184.     left = random_number(NUMBER_COLS - current->width + 1) - current->offset;
  185.     top = -MAX_BLOCK_SIZE;
  186.     
  187.     set_next_drop_time();
  188.     
  189.     /* Randomly generate a next block */
  190.     
  191.     next_block = random_number(NUMBER_BLOCK_TYPES);
  192.     next_orientation = random_number(4);
  193.     next = possible + (4 * next_block + next_orientation);
  194.             
  195.     /* Describe a preview of new block */
  196.     
  197.     describe_next_begin();
  198.     for (x = 0; x < MAX_BLOCK_SIZE; x++)
  199.         for (y = 0; y < MAX_BLOCK_SIZE; y++)
  200.             if (next->map[y][x])
  201.                 describe_next_cell(x, y, next_block + 1);
  202.     describe_next_end();
  203. }
  204.  
  205. /*--------------------------------------------------------------------*/
  206.  
  207. static void touchdown(void)
  208. /* Routine to handle when the block has dropped as far as it can.
  209.    Checks for filled rows, processes them, and generates a new block. */
  210. {
  211.     register int row;
  212.     
  213.     if (top == -MAX_BLOCK_SIZE) {
  214.         running = FALSE;
  215.         game_over();
  216.         return;
  217.     }
  218.  
  219.     /* Check for completed rows */
  220.     
  221.     for (row = NUMBER_ROWS - 1; row >= 0; row--) {
  222.         register int count = 0;
  223.         register int col = 0;
  224.         
  225.         while (col < NUMBER_COLS && game_get(col, row) != 0)
  226.             col++;
  227.         
  228.         if (col == NUMBER_COLS) {
  229.             game_del_row(row);
  230.             score++;
  231.             game_score_changed();
  232.             
  233.             if (level < NUMBER_LEVELS && score >= level_threshold[level])
  234.                 level++;
  235.             row++; /* undo decrement of row to retest this row again */
  236.         }
  237.     }
  238.     
  239.     new_block();
  240. }
  241.  
  242. /*--------------------------------------------------------------------*/
  243.  
  244. void tetris_start(struct Tetris_state *state)
  245. /* This routine is called to reset and start playing the game.  This is
  246.    called when a new game starts, or a game is loaded up for playing.
  247.    If the `state' is null, a new game is started, otherwise the game
  248.    is restored from the `state' information. */
  249. {
  250.     if (! state) {
  251.         /* Generate new blocks */
  252.         
  253.         new_block();
  254.         new_block();
  255.         score = 0;
  256.         level = 0;    /* Score of zero corresponds to level zero */
  257.     } else {
  258.         register int x, y;
  259.         
  260.         /* Restore state */
  261.         
  262.         score = state->score;
  263.         level = 0;
  264.         while (level < NUMBER_LEVELS && score >= level_threshold[level])
  265.             level++;
  266.  
  267.         left = state->left;
  268.         top = state->top;
  269.         block = state->block;
  270.         orientation = state->orientation;
  271.         current = possible + (4 * block + orientation);
  272.  
  273.          next_block = state->next_block;
  274.         next_orientation = state->next_orientation;
  275.         next = possible + (4 * next_block + next_orientation);
  276.     
  277.         /* Describe a preview of new block */
  278.     
  279.         describe_next_begin();
  280.         for (x = 0; x < MAX_BLOCK_SIZE; x++)
  281.             for (y = 0; y < MAX_BLOCK_SIZE; y++)
  282.                 if (next->map[y][x])
  283.                     describe_next_cell(x, y, next_block + 1);
  284.         describe_next_end();
  285.     }
  286.  
  287.     /* Start the game */
  288.     
  289.     paused = FALSE;
  290.     running = TRUE;
  291. }
  292.  
  293. /*--------------------------------------------------------------------*/
  294.  
  295. static Boolean can_move(int new_left, int new_top, Block_type *new_block)
  296. /* Trys changing the current block to the `new_block' in the
  297.    new position. If it does fit, the old object is erased and
  298.    the new object drawn. The current variables are not changed.
  299.    Returns TRUE if it succeeds, FALSE if it failed. */
  300. {
  301.     register int row, col;
  302.  
  303.     /* Check if position is possible */
  304.     
  305.     if (new_top > NUMBER_ROWS - MAX_BLOCK_SIZE ||
  306.         new_left + new_block->offset < 0 ||
  307.         new_left + new_block->offset + new_block->width > NUMBER_COLS)
  308.         return FALSE; /* falls outside field */
  309.     
  310.     for (row = 0; row < MAX_BLOCK_SIZE; row++)
  311.         for (col = new_block->offset;
  312.              col < new_block->offset + new_block->width; col++) {
  313.             if (new_block->map[row][col]) {
  314.                 /* Solid part at this grid position, possible collision */
  315.                 
  316.                 register int ox = col + new_left - left;
  317.                 register int oy = row + new_top - top;
  318.                 
  319.                 if (ox < 0 || ox >= MAX_BLOCK_SIZE ||
  320.                     oy < 0 || oy >= MAX_BLOCK_SIZE || (! current->map[oy][ox])) {
  321.                     /* Old was not here, so could hit */
  322.                     
  323.                     if (game_get(col + new_left, row + new_top) != 0)
  324.                         return FALSE;
  325.                 }
  326.             }
  327.         }
  328.     
  329.     /* Draw in new position */
  330.     
  331.     for (row = 0; row < MAX_BLOCK_SIZE; row++)
  332.         for (col = new_block->offset;
  333.              col < new_block->offset + new_block->width; col++)
  334.             if (new_block->map[row][col])
  335.                 game_set(col + new_left, row + new_top, block + 1);
  336.             
  337.     /* Clear old parts that don't exist in the new */
  338.  
  339.     for (row = 0; row < MAX_BLOCK_SIZE; row++)
  340.         for (col = current->offset; col < current->offset + current->width; col++) {
  341.             register int ox, oy;
  342.             
  343.             ox = left + col - new_left;
  344.             oy = top + row - new_top;
  345.             if (current->map[row][col] &&
  346.                 ((ox < 0) || (ox >= MAX_BLOCK_SIZE) || (oy < 0) || (oy >= MAX_BLOCK_SIZE) ||
  347.                                             ! new_block->map[oy][ox]))
  348.                 game_set(left + col, top + row, 0);
  349.         }
  350.  
  351.     return TRUE;
  352. }
  353.  
  354. /*--------------------------------------------------------------------*/
  355.  
  356. Boolean tetris_try_move(Move_direction direction)
  357. /* Tries to make a move in the given `direction'. Returns FALSE if it failed. */
  358. {
  359.     register int index;
  360.     
  361.     if (!running || paused)
  362.         return FALSE;
  363.     
  364.     switch (direction) {
  365.     case move_left:
  366.         if (can_move(left - 1, top, current)) {
  367.             left--;
  368.             return TRUE;
  369.         }
  370.         break;
  371.     case move_right:
  372.         if (can_move(left + 1, top, current)) {
  373.             left++;
  374.             return TRUE;
  375.         }
  376.         break;
  377.     case move_down:
  378.         if (can_move(left, top + 1, current)) {
  379.             top++;
  380.             return TRUE;
  381.         }
  382.         break;
  383.     case move_drop:
  384.         while (can_move(left, top + 1, current)) {
  385.             /* Small amount of delay for fast machines */
  386.             register unsigned long t = timer();
  387.             while (timer() == t)
  388.                 ;
  389.             
  390.             top++;
  391.         }
  392.         touchdown();
  393.         return FALSE;
  394.         break;
  395.     case move_anticlockwise:
  396.         /* Try in all nudged positions */
  397.         
  398.         for (index = 0; index < NUM_NUDGES; index++) {
  399.             register int new_orient;
  400.             register Block_type *new_block;
  401.             register new_left = left + nudges[index];
  402.  
  403.             new_orient = (orientation + 1) % 4;
  404.             new_block = possible + (4 * block + new_orient);
  405.             if (can_move(new_left, top, new_block)) {
  406.                 orientation = new_orient;
  407.                 current = new_block;
  408.                 left = new_left;
  409.                 return TRUE;
  410.             }
  411.         }
  412.                 
  413.         break;
  414.     }
  415.     
  416.     return FALSE;
  417. }
  418.  
  419. /*--------------------------------------------------------------------*/
  420.  
  421. void tetris_periodic(void)
  422. /* This routine must be called as often as possible when playing the
  423.    game.  It moves the block down when the delay time has been reached. */
  424. {
  425.     register long now;
  426.     
  427.     if (running && !paused) {
  428.         now = timer();
  429.         if (now > drop_time) {
  430.             if (! tetris_try_move(move_down))
  431.                 touchdown();
  432.             set_next_drop_time();
  433.         }
  434.     }
  435. }
  436.  
  437. /*--------------------------------------------------------------------*/
  438.  
  439. void tetris_pause(Boolean set_paused)
  440. /* Pauses the game. */
  441. {
  442.     if (running) {
  443.         if (paused && !set_paused || !paused && set_paused) {
  444.             paused = set_paused;
  445.             if (!paused)
  446.                 set_next_drop_time();
  447.         }
  448.     }
  449. }
  450.  
  451. /*--------------------------------------------------------------------*/
  452.  
  453. unsigned short tetris_score(void)
  454. /* Returns the current score. */
  455. {
  456.     return score;
  457. }
  458.  
  459. /*--------------------------------------------------------------------*/
  460.  
  461. void tetris_state_get(struct Tetris_state *state)
  462. {
  463.     state->score = score;
  464.     
  465.     state->left = left;
  466.     state->top = top;
  467.     state->block = block;
  468.     state->orientation = orientation;
  469.  
  470.     state->next_block = next_block;
  471.     state->next_orientation = next_orientation;
  472. }
  473.