1/* Micro Tetris, based on an obfuscated tetris, 1989 IOCCC Best Game 2 * 3 * Copyright (c) 1989 John Tromp <john.tromp@gmail.com> 4 * Copyright (c) 2009, 2010 Joachim Nilsson <troglobit@gmail.com> 5 * 6 * Permission to use, copy, modify, and/or distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 * 18 * See the following URLs for more information, first John Tromp's page about 19 * the game http://homepages.cwi.nl/~tromp/tetris.html then there's the entry 20 * page at IOCCC http://www.ioccc.org/1989/tromp.hint 21 */ 22 23/*! @file 24 @brief RefOS port of Micro Tetris. 25 26 <h1> RefOS Tetris </h1> 27 28 A classic Tetris clone, heavily based on (a port of) 29 Micro Tetris by Joachim Nilsson <joachim.nilsson@vmlinux.org>, 30 which itself is based on the 1989 IOCCC Best Game entry "An obfuscated 31 tetris" by John Tromp <john.tromp@gmail.com>. 32 33 Website about original IOCCC entry: 34 http://homepages.cwi.nl/~tromp/tetris.html 35 36 Website about Micro Tetris: 37 http://freecode.com/projects/micro-tetris 38 http://troglobit.com/tetris.html 39*/ 40 41#include <stdio.h> 42#include <stdlib.h> 43#include <string.h> 44#include <unistd.h> 45 46#include "io.h" 47 48/* Game Settings Definitions. */ 49#define LINES_PER_LEVEL 5 50#define INITIAL_DELAY_MS 300 51#define LEVEL_DECREASE_DELAY_MS 18 52#define RANDOM_SEED 0x177A0929 53#define MAX_LEVEL 8 54 55/* Board points. */ 56#define TL -B_COLS-1 /* Top left. */ 57#define TC -B_COLS /* Top center. */ 58#define TR -B_COLS+1 /* Top right. */ 59#define ML -1 /* Middle left. */ 60#define MR 1 /* Middle right. */ 61#define BL B_COLS-1 /* Bottom left. */ 62#define BC B_COLS /* Bottom center. */ 63#define BR B_COLS+1 /* Bottom right. */ 64 65/* Controls. */ 66#define DEFAULT_KEYS "jkl pq" 67#define KEY_LEFT 0 68#define KEY_RIGHT 2 69#define KEY_ROTATE 1 70#define KEY_DROP 3 71#define KEY_PAUSE 4 72#define KEY_QUIT 5 73 74/* Game variables. */ 75char exitGame = 0; 76char *keys = DEFAULT_KEYS; 77int level = 1; 78int points = 0; 79int linesCleared = 0; 80int pos = 17; 81 82/* Settings. */ 83char enablePreview = 1; 84char enableScoreBoard = 1; 85 86/*! @brief Peek preview of next shape. */ 87int *peekShape = NULL; 88 89/* Screen buffers. */ 90int board[B_SIZE], shadow[B_SIZE]; 91int preview[B_COLS * 10], shadowPreview[B_COLS * 10]; 92 93/*! @brief Current shape. */ 94int *shape; 95 96/*! @brief Shapes list. */ 97int shapes[] = { 98 7, TL, TC, MR, 99 8, TR, TC, ML, 100 9, ML, MR, BC, 101 3, TL, TC, ML, 102 12, ML, BL, MR, 103 15, ML, BR, MR, 104 18, ML, MR, 2, /* sticks out */ 105 0, TC, ML, BL, 106 1, TC, MR, BR, 107 10, TC, MR, BC, 108 11, TC, ML, MR, 109 2, TC, ML, BC, 110 13, TC, BC, BR, 111 14, TR, ML, MR, 112 4, TL, TC, BC, 113 16, TR, TC, BC, 114 17, TL, MR, ML, 115 5, TC, BC, BL, 116 6, TC, BC, 2 * B_COLS, /* sticks out */ 117}; 118int colours[] = { 119 A_BG_R, 120 A_BG_C, 121 A_BG_Y, 122 A_BG_W, 123 A_BG_G, 124 A_BG_B, 125 A_BG_M, 126 A_BG_R, 127 A_BG_C, 128 A_BG_Y, 129 A_BG_Y, 130 A_BG_Y, 131 A_BG_G, 132 A_BG_G, 133 A_BG_G, 134 A_BG_B, 135 A_BG_B, 136 A_BG_B, 137 A_BG_M, 138}; 139 140/*! @brief Render the online help. */ 141void 142show_online_help(void) 143{ 144 static int start = 11; 145 textattr(RESETATTR); 146 gotoxy (26 + 28, start); 147 puts("j - left"); 148 gotoxy (26 + 28, start + 1); 149 puts("k - rotate"); 150 gotoxy (26 + 28, start + 2); 151 puts("l - right"); 152 gotoxy (26 + 28, start + 3); 153 puts("space - drop"); 154 gotoxy (26 + 28, start + 4); 155 puts("p - pause"); 156 gotoxy (26 + 28, start + 5); 157 puts("q - quit"); 158} 159 160/*! @brief Gets the corresponding colour of the shape. */ 161int 162get_col(int *shape) 163{ 164 return colours[(shape - shapes) / 4]; 165} 166 167/*! @brief Render and update the current screen output. */ 168int 169update(void) 170{ 171 int x, y; 172 173 /* Render the next piece preview. */ 174 if (enablePreview) { 175 static int start = 5; 176 memset (preview, 0, sizeof(preview)); 177 178 int c = get_col(peekShape); 179 preview[2 * B_COLS + 1] = c; 180 preview[2 * B_COLS + 1 + peekShape[1]] = c; 181 preview[2 * B_COLS + 1 + peekShape[2]] = c; 182 preview[2 * B_COLS + 1 + peekShape[3]] = c; 183 184 for (y = 0; y < 4; y++) { 185 for (x = 0; x < B_COLS; x++) { 186 if (preview[y * B_COLS + x] - shadowPreview[y * B_COLS + x]) { 187 shadowPreview[y * B_COLS + x] = preview[y * B_COLS + x]; 188 gotoxy (x * 2 + 26 + 28, start + y); 189 printblock(preview[y * B_COLS + x]); 190 } 191 } 192 } 193 } 194 195 /* Render the actual board. */ 196 for (y = 1; y < B_ROWS - 1; y++) { 197 for (x = 0; x < B_COLS; x++) { 198 if (board[y * B_COLS + x] - shadow[y * B_COLS + x]) { 199 shadow[y * B_COLS + x] = board[y * B_COLS + x]; 200 gotoxy (x * 2 + 28, y); 201 printblock(board[y * B_COLS + x]); 202 } 203 } 204 } 205 206 /* Update points and level. */ 207 while (linesCleared >= LINES_PER_LEVEL) { 208 linesCleared -= LINES_PER_LEVEL; 209 level++; 210 if (level > MAX_LEVEL) level = MAX_LEVEL; 211 } 212 213 /* Render the scoreboard. */ 214 if (enableScoreBoard) { 215 textattr(RESETATTR); 216 gotoxy (26 + 28, 2); 217 printf ("Level : %d", level); 218 fflush(stdout); 219 gotoxy (26 + 28, 3); 220 printf ("Points : %d", points); 221 fflush(stdout); 222 } 223 224 if (enablePreview) { 225 gotoxy (26 + 28, 5); 226 printf ("Preview:"); 227 fflush(stdout); 228 } 229 230 gotoxy (26 + 28, 10); 231 printf ("Keys:"); 232 fflush(stdout); 233 return 0; 234} 235 236/*! @brief Returns whether a piece fits in given board position. */ 237int 238fits_in(int *shape, int pos) { 239 if (board[pos] || board[pos + shape[1]] || board[pos + shape[2]] || 240 board[pos + shape[3]]) { 241 return 0; 242 } 243 return 1; 244} 245 246/*! @brief Place a piece on the board at a given position. */ 247void 248place(int *shape, int pos, int b) 249{ 250 board[pos] = b; 251 board[pos + shape[1]] = b; 252 board[pos + shape[2]] = b; 253 board[pos + shape[3]] = b; 254} 255 256/*! @brief Randomly select a next shape. */ 257int 258*next_shape(void) 259{ 260 int *next = peekShape; 261 peekShape = &shapes[rand() % 7 * 4]; 262 if (!next) next = &shapes[rand() % 7 * 4]; 263 return next; 264} 265 266/*! @brief Update game state forward a frame. */ 267void 268game_frame(int c) 269{ 270 int j; 271 int *backup; 272 273 if (c == 0) { 274 if (fits_in (shape, pos + B_COLS)) { 275 pos += B_COLS; 276 } else { 277 place(shape, pos, get_col(shape)); 278 ++points; 279 for (j = 0; j < 252; j = B_COLS * (j / B_COLS + 1)) { 280 for (; board[++j];) { 281 if (j % B_COLS == 10) { 282 linesCleared++; 283 284 for (; j % B_COLS; board[j--] = 0); 285 update(); 286 for (; --j; board[j + B_COLS] = board[j]); 287 update(); 288 } 289 } 290 } 291 shape = next_shape(); 292 if (!fits_in (shape, pos = 17)) c = keys[KEY_QUIT]; 293 } 294 } 295 296 if (c == keys[KEY_LEFT]) { 297 if (!fits_in (shape, --pos)) ++pos; 298 } 299 if (c == keys[KEY_ROTATE]) { 300 backup = shape; 301 shape = &shapes[4 * *shape]; /* Rotate */ 302 /* Check if it fits, if not restore shape from backup. */ 303 if (!fits_in (shape, pos)) shape = backup; 304 } 305 306 if (c == keys[KEY_RIGHT]) { 307 if (!fits_in (shape, ++pos)) --pos; 308 } 309 if (c == keys[KEY_DROP]) { 310 for (; fits_in (shape, pos + B_COLS); ++points) pos += B_COLS; 311 } 312 if (c == keys[KEY_PAUSE] || c == keys[KEY_QUIT]) { 313 exitGame = 1; 314 return; 315 } 316 317 place(shape, pos, get_col(shape)); 318 update(); 319 place(shape, pos, RESETATTR); 320} 321 322static void 323print_welcome_message(void) 324{ 325 printf( 326 " ��������������������������������������������������������������������������������������������������� ���������������������������������\n" 327 " ���������������������������������������������������������������������������������������������������������������������������������������\n" 328 " ��������� ������������������ ��������� ���������������������������������������������������������\n" 329 " ��������� ������������������ ��������� ���������������������������������������������������������\n" 330 " ��������� ������������������������ ��������� ��������� ������������������������������������������\n" 331 " ��������� ������������������������ ��������� ��������� ������������������������������������������\n" 332 ); 333} 334 335int 336main() 337{ 338 /* Future Work 3: 339 How the selfloader bootstraps user processes needs to be modified further. Changes were 340 made to accomodate the new way that muslc expects process's stacks to be set up when 341 processes start, but the one part of this that still needs to changed is how user processes 342 find their system call table. Currently the selfloader sets up user processes so that 343 the selfloader's system call table is used by user processes by passing the address of the 344 selfloader's system call table to the user processes via the user process's environment 345 variables. Ideally, user processes would use their own system call table. 346 */ 347 348 uintptr_t address = strtoll(getenv("SYSTABLE"), NULL, 16); 349 refos_init_selfload_child(address); 350 int c, i, *ptr; 351 int delay = INITIAL_DELAY_MS; 352 refos_initialise(); 353 354 peekShape = NULL; 355 356 /* Initialise board. */ 357 ptr = board; 358 for (i = B_SIZE - 1; i>=0; i--) { 359 *ptr++ = i < 25 || i % B_COLS < 2 ? A_BG_W : RESETATTR; 360 } 361 362 srand((unsigned int) RANDOM_SEED); 363 364 clrscr(); 365 366 print_welcome_message(); 367 printf(" Press the space bar to continue...\n"); 368 show_online_help(); 369 370 while (1) { 371 c = io_nonblock_getkey(); 372 rand(); 373 if (c == ' ') break; 374 } 375 clrscr(); 376 show_online_help(); 377 378 /* Main game loop. */ 379 shape = next_shape(); 380 while (!exitGame) { 381 c = io_nonblock_getkey(); 382 rand(); 383 if (c >= 0) { 384 game_frame(c); 385 } else { 386 game_frame(0); 387 usleep((delay - level * LEVEL_DECREASE_DELAY_MS) * 1000); 388 } 389 } 390 391 gotoxy (0, 25); 392 printf("Game over! You reached %d points.\n", points); 393} 394