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 <time.h> 00024 00025 #include "game.h" 00026 #include "aaball.h" 00027 00028 #define SAMEGAME_NUM_ANIM 8 00029 00030 #define SAMEGAME_CELL_SIZE 40 00031 #define SAMEGAME_NUM_PIECES (3*SAMEGAME_NUM_ANIM) 00032 00033 #define SAMEGAME_BOARD_WID 15 00034 #define SAMEGAME_BOARD_HEIT 10 00035 00036 #define SAMEGAME_EMPTY 0 00037 #define SAMEGAME_RP 1 00038 #define SAMEGAME_BP 2 00039 #define SAMEGAME_GP 3 00040 00041 char samegame_colors[6] = {50, 50, 50, 50, 50, 50}; 00042 00043 void samegame_init (); 00044 00045 Game Samegame = { SAMEGAME_CELL_SIZE, SAMEGAME_BOARD_WID, SAMEGAME_BOARD_HEIT, 00046 SAMEGAME_NUM_PIECES, 00047 samegame_colors, NULL, /*samegame_pixmaps*/ NULL, 00048 "Samegame", 00049 samegame_init}; 00050 00051 typedef struct 00052 { 00053 int score; 00054 } Samegame_state; 00055 00056 SCORE_FIELD samegame_score_fields[] = {SCORE_FIELD_RANK, SCORE_FIELD_USER, SCORE_FIELD_SCORE, SCORE_FIELD_TIME, SCORE_FIELD_DATE, SCORE_FIELD_NONE}; 00057 char *samegame_score_field_names[] = {"Rank", "User", "Score", "Time", "Date", NULL}; 00058 00059 static void recursive_delete (byte *board, int x, int y, int val); 00060 static void pull_down (byte *board); 00061 static byte * synth_move (byte *newboard, byte *board); 00062 00063 static int samegame_animate (Pos *pos, byte **movp); 00064 static int samegame_getmove (Pos *, int, int, GtkboardEventType, Player, byte **, int **); 00065 static char **samegame_get_pixmap (int , int); 00066 static void *samegame_newstate (Pos *, byte *); 00067 static ResultType samegame_who_won (Pos *, Player, char **); 00068 static void samegame_search (Pos *pos, byte **movp); 00069 static void samegame_getxy (byte *board, int *x, int *y); 00070 00071 static int anim_curx=-1, anim_cury=-1; 00072 00073 void samegame_set_init_pos (Pos *pos) 00074 { 00075 int i; 00076 for (i=0; i<SAMEGAME_BOARD_WID * SAMEGAME_BOARD_HEIT; i++) 00077 pos->board[i] = ((random () % 3) + 1) * SAMEGAME_NUM_ANIM; 00078 } 00079 00080 void samegame_init () 00081 { 00082 game_single_player = 1; 00083 game_getmove = samegame_getmove; 00084 game_set_init_pos = samegame_set_init_pos; 00085 game_get_pixmap = samegame_get_pixmap; 00086 game_search = samegame_search; 00087 game_animate = samegame_animate; 00088 game_animation_time = 80; 00089 game_animation_use_movstack = FALSE; 00090 game_stateful = TRUE; 00091 game_state_size = sizeof (Samegame_state); 00092 game_newstate = samegame_newstate; 00093 game_who_won = samegame_who_won; 00094 game_scorecmp = game_scorecmp_def_dscore; 00095 game_score_fields = samegame_score_fields; 00096 game_score_field_names = samegame_score_field_names; 00097 game_doc_about = 00098 "Samegame\n" 00099 "Single player game\n" 00100 "Status: Fully implemented\n" 00101 "URL: "GAME_DEFAULT_URL("samegame"); 00102 game_doc_rules = 00103 " Samegame rules\n\n" 00104 " * Two or more balls of the same color that form a connected region constitute a block.\n" 00105 " * Hovering the mouse over a ball will highlight the block that the ball belongs to.\n" 00106 " * Clicking on a ball removes that ball's block.\n" 00107 " * You get (n-2)^2 points for removing a block with n balls in it.\n" 00108 " * You get a 1000 point bonus if you remove all the balls.\n" 00109 " * The goal is to maximize your score.\n" 00110 " * As balls are removed, balls above fall down to take their place. If a column is removed, the column to its right moves to the left.\n" ; 00111 game_doc_strategy = 00112 " Samegame tips\n\n" 00113 " * Try to identify the color which occurs the most number of times.\n" 00114 " * Remove balls of the other 2 colors until all the balls of the most frequent color (or as many of them as possible) form a single block.\n" 00115 " * Remove this block. Now do the same with the remaining two colors.\n" 00116 " * Avoid getting into a situation in which there is a single ball of some color. If this happens you won't be able to clear the entire board.\n" 00117 ; 00118 } 00119 00120 00121 int samegame_over (byte *board) 00122 { 00123 int i, j, k; 00124 int incx[2] = {1, 0}; 00125 int incy[2] = {0, 1}; 00126 for (i=0; i<board_wid; i++) 00127 for (j=0; j<board_heit; j++) 00128 for (k=0; k<2; k++) 00129 { 00130 int x = i + incx[k], y = j + incy[k]; 00131 if (board [j * board_wid + i] == 0) continue; 00132 if (x < 0 || x > board_wid - 1 || y < 0 || y > board_heit - 1) 00133 continue; 00134 if (board [y * board_wid + x] == board [j * board_wid + i]) 00135 return FALSE; 00136 } 00137 return TRUE; 00138 } 00139 00140 ResultType samegame_who_won (Pos *pos, Player to_play, char **commp) 00141 { 00142 static char comment[32]; 00143 gboolean over = samegame_over (pos->board); 00144 char *scorestr = over ? "Game over. Score:" : "Score:"; 00145 snprintf (comment, 32, "%s %d", scorestr, 00146 pos->state ? ((Samegame_state *)pos->state)->score : 0); 00147 *commp = comment; 00148 return over ? RESULT_WON : RESULT_NOTYET; 00149 } 00150 00151 00152 static Samegame_state state = {0}; 00153 00154 void *samegame_newstate (Pos *pos, byte *move) 00155 { 00156 int i, score = 0; 00157 gboolean bonus = FALSE; 00158 for (i=0; move[3*i] >= 0; i++) 00159 { 00160 if (move[3*i] == 0 && move[3*i + 1] == 0 && move[3*i + 2] == 0) 00161 bonus = TRUE; 00162 if (move[3*i+2] == 0) 00163 score++; 00164 } 00165 score -= 2; score *= score; 00166 if (score < 0) score = 0; 00167 state.score = (pos->state ? ((Samegame_state *)pos->state)->score : 0) + score; 00168 if (bonus) 00169 state.score += 1000; 00170 return &state; 00171 } 00172 00173 static byte animgen_buf [SAMEGAME_BOARD_WID * SAMEGAME_BOARD_HEIT]; 00174 00175 static void recursive_animgen (byte *board, int x, int y, int val, int newval, byte **mp) 00176 { 00177 int tmp; 00178 if (x < 0 || y < 0 || x >= board_wid || y >= board_heit) 00179 return; 00180 if (animgen_buf[y * board_wid + x] != 0) 00181 return; 00182 if (board [y * board_wid + x] == 0) return; 00183 if ((board [y * board_wid + x]-1)/SAMEGAME_NUM_ANIM != val) 00184 return; 00185 animgen_buf [y * board_wid + x] = 1; 00186 **mp = x; (*mp)++; **mp = y; (*mp)++; 00187 /* tmp = board[y * board_wid + x]; 00188 if (tmp % SAMEGAME_NUM_ANIM == 0) 00189 tmp -= SAMEGAME_NUM_ANIM; 00190 g_assert (tmp >= 0);*/ 00191 **mp = newval; (*mp)++; 00192 recursive_animgen (board, x - 1, y , val, newval, mp); 00193 recursive_animgen (board, x + 1, y , val, newval, mp); 00194 recursive_animgen (board, x , y - 1, val, newval, mp); 00195 recursive_animgen (board, x , y + 1, val, newval, mp); 00196 } 00197 00198 int samegame_animate (Pos *pos, byte **movp) 00199 { 00200 static byte move[1024]; 00201 static int animdir = 1; 00202 int i, j; 00203 byte *mp = move; 00204 byte *board = pos->board; 00205 memset (animgen_buf, 0, SAMEGAME_BOARD_WID * SAMEGAME_BOARD_HEIT); 00206 if (anim_curx >= 0 && anim_cury >= 0 00207 && board [anim_cury * board_wid + anim_curx] != 0) 00208 { 00209 int val = board[anim_cury * board_wid + anim_curx]; 00210 if (val % SAMEGAME_NUM_ANIM == 0) animdir = -1; 00211 if (val % SAMEGAME_NUM_ANIM == 1) animdir = 1; 00212 recursive_animgen (board, anim_curx, anim_cury, 00213 (val - 1) / SAMEGAME_NUM_ANIM, val + animdir, &mp); 00214 if (mp - move <= 3) 00215 mp = move; 00216 } 00217 for (i=0; i<board_wid; i++) 00218 for (j=0; j<board_heit; j++) 00219 { 00220 if (animgen_buf[j * board_wid + i] == 0 00221 && board [j * board_wid + i] % SAMEGAME_NUM_ANIM != 0) 00222 { 00223 // don't let any balls settle at less than max intensity 00224 // after they have finished animating 00225 int newval = board [j * board_wid + i] + SAMEGAME_NUM_ANIM - 00226 board [j * board_wid + i] % SAMEGAME_NUM_ANIM; 00227 *mp++ = i; 00228 *mp++ = j; 00229 *mp++ = newval; 00230 } 00231 } 00232 *mp = -1; 00233 if (mp - move < 3) return -1; 00234 *movp = move; 00235 return 1; 00236 } 00237 00238 static int getmove_real (Pos *pos, int x, int y, byte **movp) 00239 { 00240 byte *newboard, val; 00241 if ((val = pos->board[y * board_wid + x]) == 0) 00242 return -1; 00243 /* do we have at least 1 neighbor */ 00244 do 00245 { 00246 if (x > 0 && val == pos->board[y * board_wid + x - 1]) 00247 break; 00248 if (x < board_wid -1 && val == pos->board[y * board_wid + x + 1 ]) 00249 break; 00250 if (y > 0 && val == pos->board[(y-1) * board_wid + x]) 00251 break; 00252 if (y < board_heit && val == pos->board[(y+1) * board_wid + x]) 00253 break; 00254 return -1; 00255 } while (0); 00256 00257 newboard = (byte *) malloc (board_wid * board_heit); 00258 assert (newboard); 00259 memcpy (newboard, pos->board, board_wid * board_heit); 00260 recursive_delete (newboard, x, y, newboard[y * board_wid + x]); 00261 pull_down (newboard); 00262 if (movp) 00263 *movp = synth_move (newboard, pos->board); 00264 free (newboard); 00265 00266 return 1; 00267 } 00268 00269 int samegame_getmove 00270 (Pos *pos, int x, int y, GtkboardEventType type, Player to_play, byte **movp, int ** rmovep) 00271 { 00272 if (type == GTKBOARD_MOTION_NOTIFY) 00273 { 00274 anim_curx = x; 00275 anim_cury = y; 00276 } 00277 if (type == GTKBOARD_LEAVE_NOTIFY) 00278 { 00279 anim_curx = -1; 00280 anim_cury = -1; 00281 } 00282 if (type != GTKBOARD_BUTTON_RELEASE) 00283 return 0; 00284 00285 return getmove_real (pos, x, y, movp); 00286 } 00287 00288 static void recursive_delete (byte *board, int x, int y, int val) 00289 { 00290 if (x < 0 || y < 0 || x >= board_wid || y >= board_heit) 00291 return; 00292 if (board[y * board_wid + x] != val) 00293 return; 00294 board[y * board_wid + x] = 0; 00295 recursive_delete (board, x - 1, y , val); 00296 recursive_delete (board, x + 1, y , val); 00297 recursive_delete (board, x , y - 1, val); 00298 recursive_delete (board, x , y + 1, val); 00299 } 00300 00301 static void pull_down (byte *board) 00302 { 00303 int i, j, k, cnt; 00304 byte *tempcol; 00305 tempcol = (byte *) malloc (board_heit); 00306 assert (tempcol); 00307 00308 /* for each column apply gravity */ 00309 for (i=0; i<board_wid; i++) 00310 { 00311 for (j=0, k=0; j<board_heit; j++) 00312 { 00313 if (board[j * board_wid + i]) 00314 tempcol[k++] = board [j * board_wid + i]; 00315 } 00316 for (; k<board_heit; k++) 00317 tempcol[k] = 0; 00318 for (j=0; j<board_heit; j++) 00319 board[j * board_wid + i] = tempcol [j]; 00320 } 00321 00322 for (cnt = 0; cnt < board_wid; cnt++) // ugly, but works 00323 { 00324 /* if a column is empty move the col to its right in its place */ 00325 for (i=0; i<board_wid - 1; i++) 00326 { 00327 for (j=0; j<board_heit; j++) 00328 if (board[j * board_wid + i]) 00329 break; 00330 if (j == board_heit) /* col is empty */ 00331 for (j=0; j<board_heit; j++) 00332 { 00333 board [j * board_wid + i] = board [j * board_wid + i + 1]; 00334 board [j * board_wid + i + 1] = 0; 00335 } 00336 } 00337 } 00338 free (tempcol); 00339 } 00340 00341 static byte * synth_move (byte *newboard, byte *board) 00342 { 00343 static byte movbuf [1024]; 00344 int i, j, m = 0; 00345 for (i=0; i<board_wid; i++) 00346 for (j=0; j<board_heit; j++) 00347 if (newboard [j * board_wid + i] != board [j * board_wid + i]) 00348 { 00349 movbuf[m++] = i; 00350 movbuf[m++] = j; 00351 movbuf[m++] = newboard [j * board_wid + i]; 00352 } 00353 movbuf[m] = -1; 00354 return movbuf; 00355 } 00356 00357 char ** samegame_get_pixmap (int idx, int color) 00358 { 00359 int bg, fg = 0, i, tmp; 00360 static char pixbuf[SAMEGAME_CELL_SIZE*(SAMEGAME_CELL_SIZE+1)]; 00361 static gboolean first = TRUE; 00362 for(i=0, bg=0;i<3;i++) 00363 { int col = samegame_colors[i]; 00364 if (col<0) col += 256; bg += col * (1 << (16-8*i));} 00365 idx--; 00366 tmp = idx%SAMEGAME_NUM_ANIM; 00367 if (tmp == SAMEGAME_NUM_ANIM-1) fg = 255; 00368 else 00369 fg = 256 * (SAMEGAME_NUM_ANIM-1 + tmp) / (2*(SAMEGAME_NUM_ANIM-1)); 00370 switch (idx/SAMEGAME_NUM_ANIM + 1) 00371 { 00372 case SAMEGAME_RP: fg *= 0x010000; break; 00373 case SAMEGAME_GP: fg *= 0x000100; break; 00374 case SAMEGAME_BP: fg *= 0x000001; break; 00375 } 00376 if (first) 00377 { 00378 first = FALSE; 00379 return pixmap_ball_gen (SAMEGAME_CELL_SIZE, pixbuf, fg, bg, 00380 SAMEGAME_CELL_SIZE * 2/7, 24.0); 00381 } 00382 return pixmap_header_gen (SAMEGAME_CELL_SIZE, pixbuf, fg, bg); 00383 } 00384 00385 void samegame_search (Pos *pos, byte **movp) 00386 { 00387 int x, y; 00388 int retval; 00389 samegame_getxy (pos->board, &x, &y); 00390 retval = getmove_real (pos, x, y, movp); 00391 assert (retval > 0); 00392 } 00393 00394 void samegame_getxy (byte *board, int *x, int *y) 00395 { 00396 int i, j; 00397 for (i=0; i<board_wid; i++) 00398 for (j=0; j<board_heit; j++) 00399 { 00400 int val = board [j * board_wid + i]; 00401 if (val == SAMEGAME_EMPTY) continue; 00402 if ((i > 0 && board [j * board_wid + i-1] == val) || 00403 (j > 0 && board [(j-1) * board_wid + i] == val)) 00404 { 00405 *x = i; 00406 *y = j; 00407 return; 00408 } 00409 } 00410 }