This is the doxygen documentation for gtkboard.
.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