This is the doxygen documentation for gtkboard.

.
Main Page   Data Structures   File List   Data Fields   Globals  

samegame.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 <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 }