1
/* This file is a part of gtkboard, a board games system.
2
Copyright (C) 2003, Arvind Narayanan <arvindn@users.sourceforge.net>
4
This program is free software; you can redistribute it and/or modify
5
it under the terms of the GNU General Public License as published by
6
the Free Software Foundation; either version 2 of the License, or
7
(at your option) any later version.
9
This program is distributed in the hope that it will be useful,
10
but WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
GNU General Public License for more details.
14
You should have received a copy of the GNU General Public License
15
along with this program; if not, write to the Free Software
16
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
24
#include <gdk/gdkkeysyms.h>
29
#define TETRIS_CELL_SIZE 20
30
#define TETRIS_NUM_PIECES 32
32
#define TETRIS_REAL_WID 10
33
#define TETRIS_BOARD_WID 16
34
#define TETRIS_BOARD_HEIT 22
35
#define TETRIS_PREVIEW_HEIT 3
37
char tetris_colors[9] = {50, 50, 50, 50, 50, 50, 150, 150, 150};
39
static int tetris_init_pos [TETRIS_BOARD_WID*TETRIS_BOARD_HEIT] =
41
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 32, 32, 32, 32, 32, 32,
42
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 32, 32, 32, 32, 32, 32,
43
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 32, 32, 32, 32, 32, 32,
44
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 32, 0 , 0 , 0 , 0 , 32,
45
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 32, 0 , 0 , 0 , 0 , 32,
46
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 32, 0 , 0 , 0 , 0 , 32,
47
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 32, 0 , 0 , 0 , 0 , 32,
48
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 32, 32, 32, 32, 32, 32,
49
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 32, 32, 32, 32, 32, 32,
50
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 32, 32, 32, 32, 32, 32,
51
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 32, 32, 32, 32, 32, 32,
52
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 32, 32, 32, 32, 32, 32,
53
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 32, 32, 32, 32, 32, 32,
54
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 32, 32, 32, 32, 32, 32,
55
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 32, 32, 32, 32, 32, 32,
56
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 32, 32, 32, 32, 32, 32,
57
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 32, 32, 32, 32, 32, 32,
58
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 32, 32, 32, 32, 32, 32,
59
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 32, 32, 32, 32, 32, 32,
60
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 32, 32, 32, 32, 32, 32,
61
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 32, 32, 32, 32, 32, 32,
62
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 32, 32, 32, 32, 32, 32,
68
Game Tetris = { TETRIS_CELL_SIZE, TETRIS_BOARD_WID, TETRIS_BOARD_HEIT,
70
tetris_colors, tetris_init_pos, NULL, "Tetris", "Arcade", tetris_init};
72
SCORE_FIELD tetris_score_fields[] = {SCORE_FIELD_USER, SCORE_FIELD_SCORE, SCORE_FIELD_DATE, SCORE_FIELD_NONE};
73
char *tetris_score_field_names[] = {"User", "Score", "Date", NULL};
75
#define TETRIS_EMPTY 0
76
#define TETRIS_BRICK_4 1
77
#define TETRIS_BRICK_22 2
78
#define TETRIS_BRICK_121A 3
79
#define TETRIS_BRICK_121B 4
80
#define TETRIS_BRICK_T 5
81
#define TETRIS_BRICK_LA 6
82
#define TETRIS_BRICK_LB 7
83
#define TETRIS_BRICK_INACTIVE 8
84
#define TETRIS_BRICK_DYING 9
85
#define TETRIS_BRICK_MASK 15
86
#define TETRIS_BRICK_MOTION_MASK 16
87
#define TETRIS_UNUSED 32
89
static void tetris_set_init_pos (Pos *pos);
90
static char ** tetris_get_pixmap (int idx, int color);
91
static int tetris_getmove_kb (Pos *cur_pos, int key, byte **move, int **);
92
static int tetris_animate (Pos *pos, byte **movp);
93
static ResultType tetris_who_won (Pos *pos, Player to_play, char **commp);
94
static void *tetris_newstate (Pos *, byte *);
95
static void tetris_free ();
103
static int num_bricks = 0;
104
static int level = 1;
105
static int anim_time_left = 0;
106
static int anim_time_def = 0;
110
game_single_player = TRUE;
111
game_get_pixmap = tetris_get_pixmap;
112
game_getmove_kb = tetris_getmove_kb;
113
game_animation_time = 50;
114
game_animate = tetris_animate;
115
game_who_won = tetris_who_won;
116
game_stateful = TRUE;
117
game_state_size = sizeof (Tetris_state);
118
game_newstate = tetris_newstate;
119
game_scorecmp = game_scorecmp_def_dscore;
120
game_allow_back_forw = FALSE;
121
game_scorecmp = game_scorecmp_def_dscore;
122
game_score_fields = tetris_score_fields;
123
game_score_field_names = tetris_score_field_names;
124
game_draw_cell_boundaries = TRUE;
125
game_free = tetris_free;
126
game_doc_about_status = STATUS_COMPLETE;
129
"Single player game\n"
130
"Status: Fully implemented\n"
131
"URL: "GAME_DEFAULT_URL ("tetris");
133
"This is a game of falling bricks. Your objective is to rotate the pieces as they fall in order to make complete rows of bricks. Use the arrow keys to move left or right, Up to rotate, and Space to fall. Completing a row gives you 40 points, two rows simultaneously 100 points, three rows 300 points, and four rows (a tetris) 1200 points.";
143
void *tetris_newstate (Pos *pos, byte *move)
144
// TODO: implement points for falling
147
static Tetris_state state;
148
int linepts[5] = {0, 40, 100, 300, 1200};
149
for (i=0; move[3*i] >= 0; i++)
150
if (move[3*i+2] == 0)
152
score /= TETRIS_REAL_WID;
153
score = linepts [score];
154
state.score = (pos->state ? ((Tetris_state *)pos->state)->score : 0) + score;
158
int tetris_game_over (byte *board)
161
for (i=0; i<TETRIS_REAL_WID; i++)
162
if (board[(board_heit - 2) * board_wid + i] == TETRIS_BRICK_INACTIVE)
168
ResultType tetris_who_won (Pos *pos, Player to_play, char **commp)
170
static char comment[32];
172
int over = tetris_game_over (pos->board);
173
snprintf (comment, 32, "%s %s %d",
174
over ? "Game over. " : "", "Score:",
175
pos->state ? ((Tetris_state *)pos->state)->score : 0);
177
return over ? RESULT_WON : RESULT_NOTYET;
180
int tetris_fall (byte *pos, byte **movp, int height)
182
static byte move[32];
187
for (j=0; j<board_heit; j++)
188
for (i=0; i<TETRIS_REAL_WID; i++)
190
if (pos [j * board_wid + i] & TETRIS_BRICK_MOTION_MASK)
193
if (height > j) height = j;
195
if (j == 0) canfall = 0;
196
for (k=1; k<=height; k++)
198
tmp = pos [(j-k) * board_wid + i];
199
if (tmp != 0 && !(tmp & TETRIS_BRICK_MOTION_MASK))
204
if (moving && canfall)
206
for (i=0; i<TETRIS_REAL_WID; i++)
207
for (j=0; j<board_heit; j++)
209
if (j < board_heit - height)
210
if (!(pos [j * board_wid + i] & TETRIS_BRICK_MOTION_MASK)
211
&& (pos [(j+height) * board_wid + i] & TETRIS_BRICK_MOTION_MASK))
213
*mp++ = i; *mp++ = j; *mp++ = pos [(j+height) * board_wid + i];
215
if (pos [j * board_wid + i] & TETRIS_BRICK_MOTION_MASK
216
&& (j >= board_heit - height ||
217
!(pos [(j+height) * board_wid + i] & TETRIS_BRICK_MOTION_MASK)))
219
*mp++ = i; *mp++ = j; *mp++ = 0;
229
int tetris_animate (Pos *pos, byte **movp)
231
static byte move[1024];
232
static int count = 0;
234
byte *board = pos->board;
236
int level = num_bricks / 20 + 1;
237
if (level > 10) level = 10;
238
anim_time_def = (12 - level) / 2;
245
anim_time_left = 12 - level;
246
if (tetris_fall(board, movp, 1) > 0)
249
for (i=0; i<TETRIS_REAL_WID; i++)
250
for (j=0; j<board_heit; j++)
251
if (board [j * board_wid + i] & TETRIS_BRICK_MOTION_MASK)
253
*mp++ = i; *mp++ = j;
254
*mp++ = TETRIS_BRICK_INACTIVE;
256
for (j=0; j<board_heit; j++)
259
while (nfull + j < board_heit)
262
for (i=0; i<TETRIS_REAL_WID; i++)
263
if (board [(j+nfull) * board_wid + i] == 0) { full = 0; break; }
269
for (; j+nfull<board_heit; j++)
271
for (i=0; i<TETRIS_REAL_WID; i++)
273
if (board [j * board_wid + i] !=
274
board [(j+nfull) * board_wid + i])
277
//*mp++ = board [(j+nfull) * board_wid + i];
278
if ( board [(j+nfull) * board_wid + i] == 0)
280
else *mp++ = TETRIS_BRICK_INACTIVE;
284
for (; j<board_heit; j++)
287
for (i=0; i<TETRIS_REAL_WID; i++)
289
if (board [j * board_wid + i] != 0)
291
*mp++ = i; *mp++ = j; *mp++ = 0;
293
if (board [j * board_wid + i] != 0) empty = 0;
308
static int next_tile = -1;
309
// This depends on the #defs!!
310
// FIXME: change the shapes so that no brick is more than 2 rows tall
311
byte shapes [][4][2] = {
312
{ { 3, 1} , {4, 1}, {5, 1}, {6, 1} },
313
{ { 4, 1} , {5, 1}, {4, 2}, {5, 2} },
314
{ { 4, 2} , {5, 2}, {5, 1}, {6, 1} },
315
{ { 4, 1} , {5, 1}, {5, 2}, {6, 2} },
316
{ { 4, 1} , {5, 1}, {6, 1}, {5, 2} },
317
{ { 6, 1} , {5, 1}, {4, 2}, {4, 1} },
318
{ { 3, 1} , {4, 1}, {5, 1}, {5, 2} },
322
next_tile = random() % 7;
327
*mp++ = shapes[idx][i][0];
328
*mp++ = board_heit - shapes[idx][i][1];
329
if (board [mp[-1] * board_wid + mp[-2]])
331
// we need to return the move up to the previous stage
336
*mp++ = (idx+1) | TETRIS_BRICK_MOTION_MASK;
341
*mp++ = TETRIS_REAL_WID + 1 + i;
342
*mp++ = board_heit - 1 - TETRIS_PREVIEW_HEIT - j;
347
*mp++ = TETRIS_REAL_WID + shapes[next_tile][i][1] + 1;
348
*mp++ = board_heit - TETRIS_PREVIEW_HEIT + shapes[next_tile][i][0] - 7;
349
*mp++ = (next_tile+1);
358
int tetris_getmove_kb (Pos *pos, int key, byte **movp, int **rmovp)
360
static byte move[32];
364
byte *board = pos->board;
368
for (i = board_heit; i>0; i--)
369
if (tetris_fall(board, movp, i) > 0)
371
anim_time_left = anim_time_def;
379
int retval = tetris_fall(board, movp, 1);
380
if (retval > 0) anim_time_left = anim_time_def;
386
int sumx = 0, sumy = 0, k = 0, incy;
388
byte newboard [4][2];
389
for (i=0; i<TETRIS_REAL_WID; i++)
390
for (j=0; j<board_heit; j++)
391
if (board [j * board_wid + i] & TETRIS_BRICK_MOTION_MASK)
393
sumx += i; sumy += j;
394
thebrick = board [j * board_wid + i] | TETRIS_BRICK_MOTION_MASK;
395
if (j == 0 || ((board [(j-1) * board_wid + i] != 0)
396
&& !(board [(j-1) * board_wid + i] & TETRIS_BRICK_MOTION_MASK)))
399
if (sumy == 0) return -1;
400
sumx += 3; incy = sumy % 4 > 0 ? 1 : 0; sumx /= 4; sumy /= 4;
401
for (i=0; i<TETRIS_REAL_WID; i++)
402
for (j=0; j<board_heit; j++)
403
if (board [j * board_wid + i] & TETRIS_BRICK_MOTION_MASK)
405
newboard[k][0] = sumx + sumy - j;
406
newboard[k][1] = sumy - sumx + i + incy;
407
if (newboard[k][0] < 0 || newboard[k][1] < 0 ||
408
newboard[k][0] >= TETRIS_REAL_WID || newboard[k][1] >= board_heit)
410
if (board [newboard [k][1] * board_wid + newboard[k][0]]
411
== TETRIS_BRICK_INACTIVE)
415
for (i=0; i<TETRIS_REAL_WID; i++)
416
for (j=0; j<board_heit; j++)
418
if (!(board [j * board_wid + i] & TETRIS_BRICK_MOTION_MASK))
422
if (newboard[k][0] == i && newboard[k][1] == j)
423
{ found = 1; break; }
426
*mp++ = i; *mp++ = j; *mp++ = thebrick;
429
if (board [j * board_wid + i] & TETRIS_BRICK_MOTION_MASK)
433
if (newboard[k][0] == i && newboard[k][1] == j)
434
{ found = 1; break; }
437
*mp++ = i; *mp++ = j; *mp++ = 0;
447
case GDK_Left: incx = 1; break;
448
case GDK_Right: incx = -1; break;
451
for (i=0; i<TETRIS_REAL_WID; i++)
452
for (j=0; j<board_heit; j++)
453
if (board [j * board_wid + i] & TETRIS_BRICK_MOTION_MASK)
455
if (i - incx < 0 || i - incx >= TETRIS_REAL_WID) return -1;
456
if (board [j * board_wid + i - incx] != 0 &&
457
!(board [j * board_wid + i - incx] & TETRIS_BRICK_MOTION_MASK))
460
for (i=0; i<TETRIS_REAL_WID; i++)
461
for (j=0; j<board_heit; j++)
463
if (i+incx >= 0 && i+incx < TETRIS_REAL_WID)
464
if (!(board [j * board_wid + i] & TETRIS_BRICK_MOTION_MASK)
465
&& (board [j * board_wid + i+incx] & TETRIS_BRICK_MOTION_MASK))
467
*mp++ = i; *mp++ = j; *mp++ = board [j * board_wid + i+incx];
469
if (board [j * board_wid + i] & TETRIS_BRICK_MOTION_MASK
470
&& (i+incx < 0 || i+incx >= TETRIS_REAL_WID ||
471
!(board [j * board_wid + i+incx] & TETRIS_BRICK_MOTION_MASK)))
473
*mp++ = i; *mp++ = j; *mp++ = 0;
482
char ** tetris_get_pixmap (int idx, int color)
485
static char *pixmap [TETRIS_CELL_SIZE + 2];
487
//pixmap = g_new(char *, TETRIS_CELL_SIZE + 2);
488
pixmap[0] = "20 20 1 1";
489
if (idx == TETRIS_UNUSED)
490
pixmap[1] = " c #969696";
492
switch (idx & TETRIS_BRICK_MASK)
494
case TETRIS_BRICK_4: pixmap[1] = " c blue"; break;
495
case TETRIS_BRICK_22: pixmap[1] = " c red"; break;
496
case TETRIS_BRICK_121A: pixmap[1] = " c yellow"; break;
497
case TETRIS_BRICK_121B: pixmap[1] = " c magenta"; break;
498
case TETRIS_BRICK_T: pixmap[1] = " c green"; break;
499
case TETRIS_BRICK_LA: pixmap[1] = " c pink"; break;
500
case TETRIS_BRICK_LB: pixmap[1] = " c orange"; break;
501
case TETRIS_BRICK_INACTIVE: pixmap[1] = " c gray"; break;
502
default: return NULL;
504
for (i=0; i<TETRIS_CELL_SIZE; i++) pixmap[2+i] = line;