// // CLIP version of tetris, inspired by, but not translated from, // David Tschumperlé's tetrix for CImg. // // John Robinson, 2004 #include "picture.h" const int boardrows = 20; const int boardcols = 12; const int blocksize = 20; void map_to_big(picture_of_int& board, colour_picture& disp, const int lines_removed, picture_of_int& next) { // This function creates the output picture from the current state of // the tetris board. irange block(0,1,blocksize-1); irange line(0,0,0); for (int i = 0, I = 0; i < boardrows; i++, I += blocksize) for (int j = 0, J = 0; j < boardcols; j++, J += blocksize) { int blockval = board[i][j]; // Show next block in top right of the board if (!blockval&&(i < 4)&&(j >= boardcols-4)) blockval = next[i][j-(boardcols-4)]; // Convert the block type index (between 0 and 7) into // a colour for display. A block of index 0 is background // and rendered black. int r = (blockval & 1)<<7; int g = (blockval & 2)<<6; int b = (blockval & 4)<<5; disp.red[block+I][block+J] = r; disp.green[block+I][block+J] = g; disp.blue[block+I][block+J] = b; if (blockval){ // Not background // Make a shiny border r += 64, g += 64; b += 64; disp.red[block+I][line+J] = r; disp.green[block+I][line+J] = g; disp.blue[block+I][line+J] = b; disp.red[line+I][block+J] = r; disp.green[line+I][block+J] = g; disp.blue[line+I][block+J] = b; } } char caption[80]; sprintf(caption,"Lines removed = %d. Next:",lines_removed); picture_of_int capt(caption,255,0); disp.green.paste_centred_on(capt,disp.ncols()/2, 8); disp.red.paste_centred_on(capt,disp.ncols()/2, 8); disp.reshow("CLIP tetris",0); } void rot90(picture_of_int& from, picture_of_int& to) { // Used to generate rotated versions of each piece for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) { to[j][3-i] = from[i][j]; } } int main() { srand((int) time(NULL)); int shapes[8][16] = { // The basic shapes in a 4x4 array: 0 is background // non-0 is foreground. Different shapes are coded // with different numbers so they can be drawn in // different colours. 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // Background 0,0,0,0,1,1,1,0,0,0,1,0,0,0,0,0, // F 0,0,0,0,0,0,2,0,2,2,2,0,0,0,0,0, // L 0,0,0,0,0,3,3,0,3,3,0,0,0,0,0,0, // S 0,0,0,0,0,4,4,0,0,0,4,4,0,0,0,0, // Z 0,0,0,0,5,5,5,0,0,5,0,0,0,0,0,0, // T 0,0,0,0,6,6,6,6,0,0,0,0,0,0,0,0, // Beam 0,0,0,0,0,7,7,0,0,7,7,0,0,0,0,0, // Square }; picture_of_int *shapepic[8][4]; int i, j, k; // Generate basic shape pictures: // (There is no shape 0 because 0 represents background) for (i = 1; i < 8; i++) shapepic[i][0] = new picture_of_int(4,4,&shapes[i][0],0); // Generate rotated shape pictures for (j = 1; j < 4; j++) { for (i = 1; i < 8; i++) { shapepic[i][j] = new picture_of_int(4,4,(const int)0); rot90(*shapepic[i][j-1],*shapepic[i][j]); } } irange b(0,1,3); irange l(0,0,0); int lines_removed = 0; // The counter picture_of_int board(boardrows+2,boardcols+2); // The board picture has borders to detect collisions board = 0; for (i = 0; i < boardrows+2; i++) { // The borders are negative because blocks are positive board[i][0] = board[i][boardcols+1] = -8; } for (i = 0; i < boardcols+2; i++) // The bottom border has very high values board[boardrows+1][i] = 1000; picture_of_int boardpluscurr(boardrows,boardcols); irange bdr(0,1,boardrows-1); irange bdc(0,1,boardcols-1); bdr++; bdc++; // boarddisp is the actual picture rendered on the screen. colour_picture boarddisp(boardrows*blocksize,boardcols*blocksize); boarddisp.show("CLIP Tetris",0); int nextshindex = (rand()%7) + 1; int nextshorient = rand()%4; while (!boarddisp.closerequested()) { int xpos = boardcols/2 - 2; int ypos = 0; int shindex = nextshindex; int shorient = nextshorient; int collision; if (collision = board[ypos+1+b][xpos+1+b] * *shapepic[shindex][shorient]) { break; // Hit a block - game over } else { // Let the block drop // First make the next one nextshindex = (rand()%7) + 1; nextshorient = rand()%4; int clicks = 0; // clicks is the time counter which, together with // lines_removed, defines the speed. do { // Add the board picture and the picture of the // current block to make the current state... boardpluscurr = board[bdr][bdc]; boardpluscurr[ypos+b][xpos+b] += *shapepic[shindex][shorient]; // And render it. map_to_big(boardpluscurr,boarddisp,lines_removed, *shapepic[nextshindex][nextshorient]); // Process keypresses if (boarddisp.key() == keyARROWUP) { if (!(board[ypos+1+b][xpos+1+b] * *shapepic[shindex][(shorient+1)%4])) shorient = (shorient+1)%4; } else if (boarddisp.key() == keyARROWRIGHT) { if (!(board[ypos+1+b][xpos+2+b] * *shapepic[shindex][shorient])) xpos++; } else if (boarddisp.key() == keyARROWLEFT) { if (!(board[ypos+1+b][xpos+b] * *shapepic[shindex][shorient])) xpos--; } else if (boarddisp.key() == keyARROWDOWN) clicks += 25; boarddisp.clearkey(); boarddisp.wait(10); clicks++; if (clicks > (25-lines_removed/2)) { ypos++; clicks = 0; } } while (!(board[ypos+1+b][xpos+1+b] * *shapepic[shindex][shorient])); ypos--; board[ypos+1+b][xpos+1+b] += *shapepic[shindex][shorient]; // Check for a full row and possible removal boardpluscurr = board[bdr][bdc]; for (i = boardrows-1; i > 4; i--) { for (j = 0; j < boardcols; j++) { if (boardpluscurr[i][j] == 0) break; } if (j == boardcols) { // Complete row lines_removed++; // cout << "Detected complete row at " << i << endl; for (k = i; k >4; k--) { for (j = 0; j < boardcols; j++) boardpluscurr[k][j]=boardpluscurr[k-1][j]; } i++; // Because might have to remove // another row now dropped to here } } board[bdr][bdc] = boardpluscurr; } } boarddisp.reshow("Game Over"); while(!boarddisp.closerequested()) ; return 0; }