This is the doxygen documentation for gtkboard.

.
Main Page   Data Structures   File List   Data Fields   Globals  

tetris.c

Go to the documentation of this file.
00001 /*  This file is a part of gtkboard, a board games system.
00002     Copyright (C) 2003, Arvind Narayanan <arvindn@users.sourceforge.net>
00003 
00004     This program is free software; you can redistribute it and/or modify
00005     it under the terms of the GNU General Public License as published by
00006     the Free Software Foundation; either version 2 of the License, or
00007     (at your option) any later version.
00008 
00009     This program is distributed in the hope that it will be useful,
00010     but WITHOUT ANY WARRANTY; without even the implied warranty of
00011     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012     GNU General Public License for more details.
00013 
00014     You should have received a copy of the GNU General Public License
00015     along with this program; if not, write to the Free Software
00016     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
00017 
00018 */
00019 #include <stdio.h>
00020 #include <string.h>
00021 #include <assert.h>
00022 #include <stdlib.h>
00023 #include <math.h>
00024 #include <gdk/gdkkeysyms.h>
00025 
00026 #include "game.h"
00027 #include "aaball.h"
00028 
00029 #define TETRIS_CELL_SIZE 20
00030 #define TETRIS_NUM_PIECES 31
00031 
00032 #define TETRIS_BOARD_WID 10
00033 #define TETRIS_BOARD_HEIT 22
00034 
00035 char tetris_colors[6] = {50, 50, 50, 50, 50, 50};
00036 
00037 int * tetris_init_pos = NULL;
00038 
00039 void tetris_init ();
00040 
00041 Game Tetris = { TETRIS_CELL_SIZE, TETRIS_BOARD_WID, TETRIS_BOARD_HEIT, 
00042         TETRIS_NUM_PIECES,
00043         tetris_colors,  NULL, NULL, "Tetris", tetris_init};
00044 
00045 SCORE_FIELD tetris_score_fields[] = {SCORE_FIELD_USER, SCORE_FIELD_SCORE, SCORE_FIELD_DATE, SCORE_FIELD_NONE};
00046 char *tetris_score_field_names[] = {"User", "Score", "Date", NULL};
00047 
00048 #define TETRIS_EMPTY 0
00049 #define TETRIS_BRICK_4 1 
00050 #define TETRIS_BRICK_22 2 
00051 #define TETRIS_BRICK_121A 3 
00052 #define TETRIS_BRICK_121B 4
00053 #define TETRIS_BRICK_T 5 
00054 #define TETRIS_BRICK_LA 6
00055 #define TETRIS_BRICK_LB 7
00056 #define TETRIS_BRICK_INACTIVE 8
00057 #define TETRIS_BRICK_DYING 9
00058 #define TETRIS_BRICK_MASK 15
00059 #define TETRIS_BRICK_MOTION_MASK 16
00060 
00061 static void tetris_set_init_pos (Pos *pos);
00062 static char ** tetris_get_pixmap (int idx, int color);
00063 static int tetris_getmove_kb (Pos *cur_pos, int key, Player glob_to_play, 
00064                 byte **move, int **);
00065 static int tetris_animate (Pos *pos, byte **movp);
00066 static ResultType tetris_who_won (Pos *pos, Player to_play, char **commp);
00067 static void *tetris_newstate (Pos *, byte *);
00068 static void tetris_free ();
00069 
00070 typedef struct
00071 {
00072         int score;
00073 }
00074 Tetris_state;
00075 
00076 static int num_bricks = 0;
00077 static int level = 1;
00078 static int anim_time_left = 0;
00079 static int anim_time_def = 0;
00080 
00081 void tetris_init ()
00082 {
00083         game_single_player = TRUE;
00084         game_get_pixmap = tetris_get_pixmap;
00085         game_getmove_kb = tetris_getmove_kb;
00086         game_animation_time = 50;
00087         game_animate = tetris_animate;
00088         game_who_won = tetris_who_won;
00089         game_stateful = TRUE;
00090         game_state_size = sizeof (Tetris_state);
00091         game_newstate = tetris_newstate;
00092         game_scorecmp = game_scorecmp_def_dscore;
00093         game_allow_back_forw = FALSE;
00094         game_scorecmp = game_scorecmp_def_dscore;
00095         game_score_fields = tetris_score_fields;
00096         game_score_field_names = tetris_score_field_names;
00097         game_draw_cell_boundaries = TRUE;
00098         game_free = tetris_free;
00099         game_doc_about = 
00100                 "Tetris\n"
00101                 "Single player game\n"
00102                 "Status: Fully implemented (playable)\n"
00103                 "URL: "GAME_DEFAULT_URL ("tetris");
00104 }
00105 
00106 void tetris_free ()
00107 {
00108         num_bricks = 0;
00109         level = 1;
00110         anim_time_left = 0;
00111 }
00112 
00113 void *tetris_newstate (Pos *pos, byte *move)
00114         // TODO: implement points for falling
00115 {
00116         int i, score = 0;
00117         static Tetris_state state;
00118         int linepts[5] = {0, 40, 100, 300, 1200};
00119         for (i=0; move[3*i] >= 0; i++)
00120                 if (move[3*i+2] == 0)
00121                         score++;
00122         score /= board_wid;
00123         score = linepts [score];
00124         state.score = (pos->state ? ((Tetris_state *)pos->state)->score : 0) + score;
00125         return &state;
00126 }
00127 
00128 int tetris_game_over (byte *board)
00129 {
00130         int i;
00131         for (i=0; i<board_wid; i++)
00132                 if (board[(board_heit - 2) * board_wid + i] == TETRIS_BRICK_INACTIVE)
00133                         return 1;
00134         return 0;
00135 }
00136 
00137 
00138 ResultType tetris_who_won (Pos *pos, Player to_play, char **commp)
00139 {
00140         static char comment[32];
00141         int i;
00142         int over = tetris_game_over (pos->board);
00143         snprintf (comment, 32, "%s %s %d", 
00144                         over ? "Game over. " : "", "Score:",
00145                         pos->state ? ((Tetris_state *)pos->state)->score : 0);
00146         *commp = comment;
00147         return over ? RESULT_WON : RESULT_NOTYET;
00148 }
00149 
00150 int tetris_fall (byte *pos, byte **movp, int height)
00151 {
00152         static byte move[32];
00153         byte *mp = move;
00154         int moving = 0;
00155         int canfall = 1;
00156         int i, j, k;
00157         for (j=0; j<board_heit; j++)
00158         for (i=0; i<board_wid; i++)
00159         {
00160                 if (pos [j * board_wid + i] & TETRIS_BRICK_MOTION_MASK)
00161                 {
00162                         int tmp;
00163                         if (height > j) height = j;
00164                         moving = 1;
00165                         if (j == 0) canfall = 0;
00166                         for (k=1; k<=height; k++)
00167                         {
00168                                 tmp = pos [(j-k) * board_wid + i];
00169                                 if (tmp != 0 && !(tmp & TETRIS_BRICK_MOTION_MASK))
00170                                         canfall = 0;
00171                         }
00172                 }
00173         }
00174         if (moving && canfall)
00175         {
00176                 for (i=0; i<board_wid; i++)
00177                 for (j=0; j<board_heit; j++)
00178                 {
00179                         if (j < board_heit - height)
00180                         if (!(pos [j * board_wid + i] & TETRIS_BRICK_MOTION_MASK) 
00181                                 && (pos [(j+height) * board_wid + i] & TETRIS_BRICK_MOTION_MASK))
00182                         {
00183                                 *mp++ = i; *mp++ = j; *mp++ = pos [(j+height) * board_wid + i];
00184                         }
00185                         if (pos [j * board_wid + i] & TETRIS_BRICK_MOTION_MASK 
00186                                 && (j >= board_heit - height || 
00187                                         !(pos [(j+height) * board_wid + i] & TETRIS_BRICK_MOTION_MASK)))
00188                         {
00189                                 *mp++ = i; *mp++ = j; *mp++ = 0;
00190                         }
00191                 }
00192                 *mp++ = -1;
00193                 *movp = move;
00194                 return 1;
00195         }
00196         return -1;
00197 }
00198 
00199 int tetris_animate (Pos *pos, byte **movp)
00200 {
00201         static byte move[1024];
00202         static int count = 0;
00203         byte *mp = move;
00204         byte *board = pos->board;
00205         int i, j;
00206         int level = num_bricks / 20 + 1;
00207         if (level > 10) level = 10;
00208         anim_time_def = (12 - level) / 2;
00209         if (anim_time_left) 
00210         {
00211                 anim_time_left--;
00212                 return 0;
00213         }
00214 
00215         anim_time_left = 12 - level;
00216         if (tetris_fall(board, movp, 1) > 0)
00217                 return 1;
00218 
00219         for (i=0; i<board_wid; i++)
00220         for (j=0; j<board_heit; j++)
00221                 if (board [j * board_wid + i] & TETRIS_BRICK_MOTION_MASK)
00222                 {
00223                         *mp++ = i; *mp++ = j; 
00224                         *mp++ = TETRIS_BRICK_INACTIVE;
00225                 }
00226         for (j=0; j<board_heit; j++)
00227         {
00228                 int nfull = 0;
00229                 while (nfull + j < board_heit)
00230                 {
00231                         int full = 1;
00232                         for (i=0; i<board_wid; i++) 
00233                                 if (board [(j+nfull) * board_wid + i] == 0) { full = 0; break; }
00234                         if (!full) break;
00235                         nfull++;
00236                 }
00237                 if (nfull > 0)
00238                 {
00239                         for (; j+nfull<board_heit; j++)
00240                         {
00241                                 for (i=0; i<board_wid; i++) 
00242                                 {
00243                                         if (board [j * board_wid + i] != 
00244                                                         board [(j+nfull) * board_wid + i])
00245                                         { *
00246                                                 mp++ = i; *mp++ = j; 
00247                                                 //*mp++ = board [(j+nfull) * board_wid + i]; 
00248                                                 if ( board [(j+nfull) * board_wid + i] == 0)
00249                                                         *mp++ = 0;
00250                                                 else *mp++ =  TETRIS_BRICK_INACTIVE;
00251                                         }
00252                                 }
00253                         }
00254                         for (; j<board_heit; j++)
00255                         {
00256                                 int empty = 1;
00257                                 for (i=0; i<board_wid; i++) 
00258                                 {
00259                                         if (board [j * board_wid + i] != 0)
00260                                         {
00261                                                 *mp++ = i; *mp++ = j; *mp++ = 0;
00262                                         }
00263                                         if (board [j * board_wid + i] != 0) empty = 0;
00264                                 }
00265                                 if (empty) break;
00266                         }
00267                         *mp++ = -1;
00268                         *movp = move;
00269                         return 1;
00270                 }
00271         }
00272         
00273         num_bricks ++;
00274 
00275         {
00276         int idx;
00277         byte *saved_m = mp;
00278         // This depends on the #defs!!
00279         // FIXME: change the shapes so that no brick is more than 2 rows tall
00280         byte shapes [][4][2] = {
00281                 { { 3, 1} , {4, 1}, {5, 1}, {6, 1} },
00282                 { { 4, 1} , {5, 1}, {4, 2}, {5, 2} },
00283                 { { 4, 1} , {4, 2}, {5, 2}, {5, 3} },
00284                 { { 5, 2} , {5, 1}, {4, 2}, {4, 3} },
00285                 { { 4, 1} , {5, 1}, {6, 1}, {5, 2} },
00286                 { { 4, 3} , {5, 3}, {4, 2}, {4, 1} },
00287                 { { 4, 3} , {5, 3}, {5, 2}, {5, 1} },
00288         };
00289 
00290         idx = random() % 7;
00291         for (i=0; i<4; i++)
00292         {
00293                 *mp++ = shapes[idx][i][0]; 
00294                 *mp++ = board_heit - shapes[idx][i][1]; 
00295                 if (board [mp[-1] * board_wid + mp[-2]])
00296                 {
00297                         // we need to return the move up to the previous stage
00298                         *saved_m++ = -1;
00299                         *movp = move;
00300                         return 1;
00301                 }
00302                 *mp++ = (idx+1) | TETRIS_BRICK_MOTION_MASK;
00303         }
00304         *mp++ = -1;
00305         *movp = move;
00306         return 1;
00307         }
00308 }
00309 
00310 
00311 int tetris_getmove_kb (Pos *pos, int key, Player glob_to_play, byte **movp, int **rmovp)
00312 {
00313         static byte move[32];
00314         byte *mp = move;
00315         int incx;
00316         int i, j;
00317         byte *board = pos->board;
00318 
00319         if (key == ' ')
00320         {
00321                 for (i = board_heit; i>0; i--)
00322                         if (tetris_fall(board, movp, i) > 0)
00323                         {
00324                                 anim_time_left = anim_time_def;
00325                                 return 1;
00326                         }
00327                 return -1;
00328         }
00329         
00330         if (key == GDK_Down)
00331         {
00332                 int retval = tetris_fall(board, movp, 1);
00333                 if (retval > 0) anim_time_left = anim_time_def;
00334                 return retval;
00335         }
00336         
00337         if (key == GDK_Up)
00338         {
00339                 int sumx = 0, sumy = 0, k = 0, incy;
00340                 int thebrick = 0;
00341                 byte newboard [4][2];
00342                 for (i=0; i<board_wid; i++)
00343                 for (j=0; j<board_heit; j++)
00344                         if (board [j * board_wid + i] & TETRIS_BRICK_MOTION_MASK)
00345                         { 
00346                                 sumx += i; sumy += j; 
00347                                 thebrick = board [j * board_wid + i] | TETRIS_BRICK_MOTION_MASK;
00348                                 if (j == 0 || ((board [(j-1) * board_wid + i] != 0) 
00349                                         && !(board [(j-1) * board_wid + i] & TETRIS_BRICK_MOTION_MASK)))
00350                                         return -1;
00351                         }
00352                 if (sumy == 0) return -1;
00353                 sumx += 3; incy = sumy % 4 > 0 ? 1 : 0; sumx /= 4; sumy /= 4;
00354                 for (i=0; i<board_wid; i++)
00355                 for (j=0; j<board_heit; j++)
00356                         if (board [j * board_wid + i] & TETRIS_BRICK_MOTION_MASK)
00357                         {
00358                                 newboard[k][0] = sumx + sumy - j;
00359                                 newboard[k][1] = sumy - sumx + i + incy;
00360                                 if (newboard[k][0] < 0 || newboard[k][1] < 0 || 
00361                                                 newboard[k][0] >= board_wid || newboard[k][1] >= board_heit)
00362                                         return -1;
00363                                 if (board [newboard [k][1] * board_wid + newboard[k][0]]
00364                                                 == TETRIS_BRICK_INACTIVE)
00365                                         return -1;
00366                                 k++;
00367                         }
00368                 for (i=0; i<board_wid; i++)
00369                 for (j=0; j<board_heit; j++)
00370                 {
00371                         if (!(board [j * board_wid + i] & TETRIS_BRICK_MOTION_MASK))
00372                         {
00373                                 int found = 0;
00374                                 for (k=0; k<4; k++) 
00375                                         if (newboard[k][0] == i && newboard[k][1] == j)
00376                                         { found = 1; break; }
00377                                 if (found)
00378                                 {
00379                                         *mp++ = i; *mp++ = j; *mp++ = thebrick;
00380                                 }
00381                         }
00382                         if (board [j * board_wid + i] & TETRIS_BRICK_MOTION_MASK)
00383                         {
00384                                 int found = 0;
00385                                 for (k=0; k<4; k++) 
00386                                         if (newboard[k][0] == i && newboard[k][1] == j)
00387                                         { found = 1; break; }
00388                                 if (!found)
00389                                 {
00390                                         *mp++ = i; *mp++ = j; *mp++ = 0;
00391                                 }
00392                         }
00393                 }
00394                 *mp++ = -1;
00395                 *movp = move;
00396                 return 1;
00397         }
00398         switch (key)
00399         {
00400                 case GDK_Left: incx = 1; break;
00401                 case GDK_Right: incx = -1; break;
00402                 default: return -1;
00403         }
00404         for (i=0; i<board_wid; i++)
00405         for (j=0; j<board_heit; j++)
00406                 if (board [j * board_wid + i] & TETRIS_BRICK_MOTION_MASK)
00407                 {
00408                         if (i - incx < 0 || i - incx >= board_wid) return -1;
00409                         if (board [j * board_wid + i - incx] != 0 && 
00410                                         !(board [j * board_wid + i - incx] & TETRIS_BRICK_MOTION_MASK))
00411                                 return -1;
00412                 }
00413         for (i=0; i<board_wid; i++)
00414         for (j=0; j<board_heit; j++)
00415         {
00416                 if (i+incx >= 0 && i+incx < board_wid)
00417                 if (!(board [j * board_wid + i] & TETRIS_BRICK_MOTION_MASK) 
00418                         && (board [j * board_wid + i+incx] & TETRIS_BRICK_MOTION_MASK))
00419                 {
00420                         *mp++ = i; *mp++ = j; *mp++ = board [j * board_wid + i+incx];
00421                 }
00422                 if (board [j * board_wid + i] & TETRIS_BRICK_MOTION_MASK 
00423                         && (i+incx < 0 || i+incx >= board_wid || 
00424                                 !(board [j * board_wid + i+incx] & TETRIS_BRICK_MOTION_MASK)))
00425                 {
00426                         *mp++ = i; *mp++ = j; *mp++ = 0;
00427                 }
00428         }
00429         *mp++ = -1;
00430         *movp = move;
00431         return 1;
00432 }
00433 
00434 
00435 char ** tetris_get_pixmap (int idx, int color)
00436 {
00437         int i;
00438         static char *pixmap [TETRIS_CELL_SIZE + 2];
00439         char *line = "                    ";
00440         //pixmap = g_new(char *, TETRIS_CELL_SIZE + 2);
00441         pixmap[0] = "20 20 1 1";
00442         switch (idx & TETRIS_BRICK_MASK)
00443         {
00444                 case TETRIS_BRICK_4: pixmap[1] = "  c blue"; break;
00445                 case TETRIS_BRICK_22: pixmap[1] = "  c red"; break;
00446                 case TETRIS_BRICK_121A: pixmap[1] = "  c yellow"; break;
00447                 case TETRIS_BRICK_121B: pixmap[1] = "  c magenta"; break;
00448                 case TETRIS_BRICK_T: pixmap[1] = "  c green"; break;
00449                 case TETRIS_BRICK_LA: pixmap[1] = "  c pink"; break;
00450                 case TETRIS_BRICK_LB: pixmap[1] = "  c orange"; break;
00451                 case TETRIS_BRICK_INACTIVE: pixmap[1] = "  c gray"; break;
00452                 default: return NULL;
00453         }
00454         for (i=0; i<TETRIS_CELL_SIZE; i++) pixmap[2+i] = line;
00455         return pixmap;
00456 }
00457