#ifdef __APPLE__
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif
#include <sstream>
/* As Tetris runs on a standard 10x20 grid (displayed), each pixel has
width represented by a float value of 0.09f */
int score = 0;
int grid[10][22];
int nextPiece = 0;
int currShape[4][4];
int temp[4][4];
int horz = 0;
int vert = 0;
int ticks = 0;
int level = 1;
int gameOver = 0;
int gamePause = -1;
void drawWord(std::string word, int pos_x, int pos_y, float scale,
float red, float green, float blue) {
glColor3f(red, green, blue);
// Set the input color
glMatrixMode(GL_PROJECTION);
// Move into projection mode
glPushMatrix();
// push a matrix onto the stack
glLoadIdentity();
// load the identity matrix
glMatrixMode(GL_MODELVIEW);
// switch into model view
glPushMatrix();
// push a new matrix onto the stack
glLoadIdentity();
gluOrtho2D(0, 512, 0, 512);
// set our window co-ordinates
glTranslatef(pos_x, pos_y, 0.0f);
// translate by the inputted values
glScalef(scale, scale, 1.0f);
// scale based on inputted scale
int length = word.size();
// find the size of the word
for(int i=0; i<length; i++)
glutStrokeCharacter(GLUT_STROKE_ROMAN, word[i]);
//type out each letter in the word
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
// pop matrices in order to reset the changes and
complete function
}
std::string makeString(int value) {
std::stringstream convert;
convert << value;
return convert.str();
// convert an int into a string
}
void writeScore() {
// for each method, use drawWord to draw what we need
in this postion
drawWord("Score",390,400,0.3f,1.0f,0.0f,0.2f);
drawWord(makeString(score),390,365,0.2f,1.0f,1.0f,1.0f);
}
void writeLevel() {
drawWord("Level",390,256,0.3f,0.0f,0.5f,0.0f);
drawWord(makeString(level),430,225,0.2f,1.0f,1.0f,1.0f);
}
void drawNextPiece() {
float red = 0.0f;
float green = 0.0f;
float blue = 0.0f;
int nextGrid[4][4];
// reset the next piece tracker
for(int x = 0; x < 4; x++) {
for(int y =0; y < 4; y++) {
nextGrid[x][y] = 0;
}
}
switch(nextPiece) {
// based on our chosen next piece, fill in the relevant values in
our 4x4 array and set the colour
case 0: red = 1.0f; nextGrid[1][0] = 1; nextGrid[1][1] =
1; nextGrid[1][2] = 1; nextGrid[1][3] = 1; break;
case 1: blue = 1.0f; nextGrid[1][1] = 1;
nextGrid[1][2] = 1; nextGrid[2][2] = 1; nextGrid[2][1] = 1;
break;
case 2: green = 1.0f; red = 1.0f;
nextGrid[0][2] = 1; nextGrid[1][1] = 1; nextGrid[1][2] = 1;
nextGrid[2][1] = 1; break;
case 3: green = 1.0f; nextGrid[0][1] = 1;
nextGrid[1][1] = 1; nextGrid[1][2] = 1; nextGrid[2][2] = 1;
break;
case 4: red = 1.0f; green = 0.5f;
nextGrid[1][0] = 1; nextGrid[1][1] = 1; nextGrid[1][2] = 1;
nextGrid[2][0] = 1; break;
case 5: green = 1.0f; blue = 1.0f;
nextGrid[2][0] = 1; nextGrid[2][1] = 1; nextGrid[2][2] = 1;
nextGrid[1][0] = 1; break;
case 6: red = 0.5f; blue = 0.5f; nextGrid[0][1] =
1; nextGrid[1][1] = 1; nextGrid[2][1] = 1; nextGrid[1][2] = 1;
break;
}
for(int x = 0; x < 4; x++) {
// for each filled part of our 2d array, we draw a solid
cube and wire grid on screen
for(int y = 0; y < 4; y++) {
if(nextGrid[x][y] == 1) {
// As in drawGrid method, with alternate translation values (see
below)
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glTranslatef((-
0.8125f+(x*0.075f)),(0.1375f+(y*0.075f)),0.0f);
glColor3f(red,green,blue);
glutSolidCube(0.075f);
glColor3f(0.0f,0.0f,0.0f);
glutWireCube(0.075f);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
}
}
}
}
void showNext() {
// we draw a box to enclose our nextPiece and draw the word and
piece required also
drawWord("Next",30,400,0.3f,0.0f,0.0f,1.0f);
glLineWidth(0.2f);
glColor3f(1.0f,1.0f,1.0f);
glBegin(GL_LINE_LOOP);
glVertex2f(-0.9f, 0.45f);
glVertex2f(-0.5f, 0.45f);
glVertex2f(-0.5f,0.05f);
glVertex2f(-0.9f,0.05f);
glEnd();
drawNextPiece();
}
void startGrid() {
// initialise a blank grid
for(int x = 0; x < 10; x++) {
for(int y = 0; y < 22; y++) {
grid[x][y] = 0;
}
}
}
void drawGrid() {
// draw our tetris board based on our 2d array
float red;
float green;
float blue;
for(int y = 0; y < 20; y++) {
for(int x = 0; x < 10; x++) {
red = 0.0f;
green = 0.0f;
blue = 0.0f;
switch(grid[x][y]) {
// set the colour based on the value of the array at this point
case 0: break;
case 1: red = 0.8f; green = 0.8f; blue =
0.8f; break;
case 2: red = 1.0f; break;
case 3: blue = 1.0f; break;
case 4: red = 1.0f; green = 1.0f; break;
case 5: green = 1.0f; break;
case 6: red = 1.0f; green = 0.5f; break;
case 7: green = 1.0f; blue = 1.0f;
break;
case 8: red = 0.5f; blue = 0.5f; break;
}
if(grid[x][y] != 0) {
// if we find a non-blank space, we must draw
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glTranslatef((-0.405f+(x*0.09f)),(-
0.855f+(y*0.09f)),0.0f); // we translate by our bottom left
values corner for x and y, plus the change in x and y defined by the
array position
glColor3f(red,green,blue);
glutSolidCube(0.09f);
// we draw a solid cube of required colour, with single pixel
size
glColor3f(0.0f,0.0f,0.0f);
glutWireCube(0.09f);
// we then draw the black wire frame to show it as a pixel
glPopMatrix();
// we undo the translation by popping the matrix off the stack
glMatrixMode(GL_PROJECTION);
glPopMatrix();
}
}
}
}
void newPiece() {
nextPiece = rand() % 7;
// to randomly choose a piece, we pick a random number
between 0 and 6
}
void drawPiece() {
for(int y = 0; y < 20; y++) {
// this resets all non "dead-block" values in the board,
i.e. erases our live piece
for(int x = 0; x < 10; x++) {
if(grid[x][y] != 1)
grid[x][y] = 0;
}
}
for(int y = 0; y < 4; y++) {
// this then draws the piece into our 2d array, based on
the current shape array and the tracked vertical and horizontal
for(int x = 0; x < 4; x++) {
// values of our bottom left corner
if((horz+x >= 0) && (horz+x < 10) && (vert+y >= 0) && (vert+y <
20)) {
if(grid[horz + x][vert + y] != 1)
grid[horz + x][vert + y] = currShape[x][y];
}
}
}
}
void testGameOver() {
for(int y = 0; y < 4; y++) {
// if our piece is blocked from entering the grid, we state
it as game over
for(int x = 0; x < 4; x++) {
if((currShape[x][y] != 0) & (grid[horz + x][vert + y] == 1)) {
gameOver = 1;
gamePause = 1;
// we put the game into a state of pause to stop idle and user
input
}
}
}
}
void drawNewPiece() {
// this will add our next piece into our current piece array
for(int y = 0; y < 4; y++) {
// we first reset our current piece array
for(int x = 0; x < 4; x++) {
currShape[x][y] = 0;
}
}
switch (nextPiece) {
// then set the relevant values of our array
case 0: currShape[0][2] = 2; currShape[1][2] = 2;
currShape[2][2] = 2; currShape[3][2] = 2; break;
case 1: currShape[1][2] = 3; currShape[1][1] = 3;
currShape[2][2] = 3; currShape[2][1] = 3; break;
case 2: currShape[0][2] = 4; currShape[1][1] = 4;
currShape[1][2] = 4; currShape[2][1] = 4; break;
case 3: currShape[0][1] = 5; currShape[1][1] = 5;
currShape[1][2] = 5; currShape[2][2] = 5; break;
case 4: currShape[1][0] = 6; currShape[1][1] = 6;
currShape[1][2] = 6; currShape[2][0] = 6; break;
case 5: currShape[1][0] = 7; currShape[2][0] = 7;
currShape[2][1] = 7; currShape[2][2] = 7; break;
case 6: currShape[0][1] = 8; currShape[1][1] = 8;
currShape[2][1] = 8; currShape[1][2] = 8; break;
}
horz = 3;
// we must also reset the tracked position of our bottom left
corner
vert = 17;
testGameOver();
// we test if the piece can be inserted into our grid
if(gameOver == 0) {
drawPiece();
// if the piece can be drawn in, we add it to the board and
generate our new "next" piece
newPiece();
}
}
void display() {
glClear(GL_COLOR_BUFFER_BIT);
// we clear the display
glColor3f(1.0f, 1.0f, 1.0f); // set RGB values of colour to draw
glLineWidth(3.0f);
glBegin(GL_LINES);
// and draw the outline of the grid in white
glVertex2f(0.45f,0.9f);
glVertex2f(0.45f,-0.9f);
glVertex2f(0.45f,-0.9f);
glVertex2f(-0.45f,-0.9f);
glVertex2f(-0.45f,-0.9f);
glVertex2f(-0.45f,0.9f);
glEnd();
writeScore();
writeLevel();
showNext();
drawGrid();
drawWord("Press 'q' to quit, 'p' to pause and 'r' to
restart",120,10,0.1f,0.0f,1.0f,1.0f); // this adds some feature
instructions on the bottom of the screen
if(gameOver == 1) {
// if the game is over, draw this in
drawWord("Game",10,150,0.3f,1.0f,0.5f,0.0f);
drawWord("Over",25,120,0.3f,1.0f,0.5f,0.0f);
}
else if(gamePause == 1)
// if we are paused but not over, draw this in
drawWord("Paused",10,150,0.3f,1.0f,0.5f,0.0f);
else if(((ticks % 4500) <= 20) && (ticks > 20)) {
// if we are set to level up, show this message for 20
ticks
drawWord("Level",380,150,0.3f,0.3f,0.3f,1.0f);
drawWord("Up",400,100,0.3f,0.3f,0.3f,1.0f);
}
glutSwapBuffers();
}
int detect() {
// detect if a piece is colliding
int test = 0;
for(int y = 0; y < 20; y++) {
for(int x = 0; x < 10; x++) {
if((grid[x][y] != 0) & (grid[x][y] != 1)) {
// if we have found our live piece
if(y == 0) {
// if we have hit the floor, collision found
test = 1;
}
else if(grid[x][y-1] == 1) {
// if we are resting on a dead block, collision found
test = 1;
}
}
}
}
return test;
}
void removeRow(int y) {
// when a row is removed, this function grabs the rows
above to drop them down
for(int a = y; a < 19; a++) {
for(int x = 0; x < 10; x++) {
grid[x][a] = grid[x][(a+1)];
}
}
}
void testScore() {
// this tests for scoring rows
int change = 0;
// track the row changes
for(int y = 0; y < 20; y++) {
int test = 0;
for(int x = 0; x < 10; x++) {
if(grid[x][y] != 1)
// if we find a non-dead block, then we have no score for
this row
test = 1;
}
if(test == 0) {
// if this row scores, remove it, decrease y (to check row above
after change) and increase row change by 1
removeRow(y);
y--;
change++;
}
}
switch(change) {
// our score change is based on how many rows are removed
this sweep
case 1: score += (40*level); break;
case 2: score += (100*level); break;
case 3: score += (300*level); break;
case 4: score += (1200*level); break;
}
}
void idle() {
glutPostRedisplay();
// our idle function controls the display
if(gamePause == -1) {
// if we have not paused the game
ticks++;
// increase our number of ticks
if((ticks % 4500) == 0) {
// every 4500 ticks, increase the level to a maximum of 10
if(level < 10)
level++;
}
if((ticks % (int)(60/level)) == 0) {
// This controls the speed based on level. Every (60/level)
ticks, we move down a row
int count = 0;
int collision = detect();
if(collision == 0) {
// If we will not collide, decrease the y co-ord and draw
the piece into its new position
vert--;
drawPiece();
}
else {
for(int y = 0; y < 20; y++) {
// If we do collide, then turn each live piece into a dead
piece before resetting the collision
for(int x = 0; x < 10; x++) {
if((grid[x][y] != 0) & (grid[x][y] != 1)) {
grid[x][y] = 1;
count++;
if(count == 4)
collision = 0;
}
}
}
}
testScore();
// As a new dead block has been added, test to see if this scores
if(count == 4)
drawNewPiece();
// If we added a dead block, we need to draw in our new piece
}
}
}
void restart() {
// A restart function for the game, resetting all variables and
the board and pieces
level = 1;
score = 0;
gameOver = 0;
gamePause = -1;
startGrid();
newPiece();
drawNewPiece();
}
void drop() {
// A method to drop until we collide
int collision = 0;
int count = 0;
while(collision == 0) {
// Until we find a collision, continue to drop and update the
piece
vert--;
drawPiece();
collision = detect();
}
for(int y = 0; y < 20; y++) {
// Once we have the colision, turn our live piece into a
dead block
for(int x = 0; x < 10; x++) {
if((grid[x][y] != 0) & (grid[x][y] != 1)) {
grid[x][y] = 1;
count++;
if(count == 4)
collision = 0;
}
}
}
testScore();
// Again, we test score and draw in our new piece
drawNewPiece();
}
void keyboard(unsigned char key, int x, int y) {
// 4 functions. A quit, restart, piece drop and pause built
into the game
switch(key) {
case 'q': exit(1); break;
case 'r': restart(); break;
case ' ': if((gamePause == -1) && (detect() == 0)) drop();
break;
case 'p': if(gameOver == 0) gamePause*=-1; break;
}
}
int wallLeft() {
// Detect a block on the left
int test = 0;
for(int y = 0; y < 20; y++) {
for(int x = 0; x < 10; x++) {
if((grid[x][y] != 0) & (grid[x][y] != 1)) {
if(x == 0) {
// If we are against the left side of the grid, we cannot move
test = 1;
}
else if(grid[x-1][y] == 1) {
// Or if a dead piece lies to the left of us, we cannot
move
test = 1;
}
}
}
}
return test;
}
int wallRight() {
// As above, but with inversion to look to our right
int test = 0;
for(int y = 0; y < 20; y++) {
for(int x = 0; x < 10; x++) {
if((grid[x][y] != 0) & (grid[x][y] != 1)) {
if(x == 9) {
test = 1;
}
else if(grid[x+1][y] == 1) {
test = 1;
}
}
}
}
return test;
}
void moveLeft() {
// If there is no left collision, move the piece left and update
int collision = wallLeft();
if(collision == 0)
horz--;
drawPiece();
}
void moveRight() {
// As above, but with inversion to move right
int collision = wallRight();
if(collision == 0)
horz++;
drawPiece();
}
int rTest() {
// This is to test the validity of a rotation
int test = 0;
for(int x = 0; x < 4; x++) {
for(int y = 0; y < 4; y++) {
if(temp[x][y] != 0) {
if((horz + x < 0) || (horz + x > 9) || (vert + y < 0) ||
(vert + y > 19)) // If our rotation takes us out of range,
the rotation fails so must be blocked
test = 1;
else if(grid[horz + x][vert + y] == 1)
// Or if we would overwrite a dead piece, we again
block the rotation
test = 1;
}
}
}
return test;
}
void rotate() {
for(int x = 0; x < 4; x++) {
// Roatate our current shape 90 degrees and into a
temporary 2D array for testing
for(int y = 0; y < 4; y++) {
temp[y][3-x] = currShape[x][y];
}
}
if(rTest() == 0) {
// If our rotation is not blocked, copy over our temporary array
into our current shape
for(int x = 0; x < 4; x++) {
for(int y = 0; y < 4; y++) {
currShape[x][y] = temp[x][y];
}
}
drawPiece();
// In either case, we can update our current piece
}
}
void moveDown() {
// A function to move down on the user input
int collision = detect();
int count = 0;
if(collision == 0) {
// If we have no collision, we can move down and update the piece
vert--;
drawPiece();
}
else {
// If we do collide, then as above we make our live block dead,
test for score and generate a new piece
for(int y = 0; y < 20; y++) {
for(int x = 0; x < 10; x++) {
if((grid[x][y] != 0) & (grid[x][y] != 1)) {
grid[x][y] = 1;
count++;
if(count == 4)
collision = 0;
}
}
}
testScore();
if(count == 4)
drawNewPiece();
}
}
void special(int key, int x, int y) {
// Move or rotate the live piece based on the user arrow
input
if(gamePause == -1) {
switch(key) {
case GLUT_KEY_LEFT: moveLeft(); break;
case GLUT_KEY_RIGHT: moveRight(); break;
case GLUT_KEY_UP: rotate(); break;
case GLUT_KEY_DOWN: moveDown(); break;
}
}
}
int main(int argc, char* argv[]) {
// A method to initialise our window and set our display
and idle functions, as well as setting up the start of the board
startGrid();
srand(time(NULL));
// Ensure a truly random sequence by basing it off of the time
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA);
glutInitWindowSize(512,512);
glutInitWindowPosition(20,20);
glutCreateWindow("Tetris");
glClearColor(0.0f,0.0f,0.0f,1.0f);
glutDisplayFunc(display);
glutIdleFunc(idle);
newPiece();
drawNewPiece();
glutKeyboardFunc(keyboard);
glutSpecialFunc(special);
gluLookAt(0.04f,0.04f,0.2f,0.0f,0.0f,0.0f,0.0f,1.0f,0.0f);
// This moves the position of our camera to create a
3d effect on the board
glutMainLoop();
}

tetris

  • 1.
    #ifdef __APPLE__ #include <GLUT/glut.h> #else #include<GL/glut.h> #endif #include <sstream> /* As Tetris runs on a standard 10x20 grid (displayed), each pixel has width represented by a float value of 0.09f */ int score = 0; int grid[10][22]; int nextPiece = 0; int currShape[4][4]; int temp[4][4]; int horz = 0; int vert = 0; int ticks = 0; int level = 1; int gameOver = 0; int gamePause = -1; void drawWord(std::string word, int pos_x, int pos_y, float scale, float red, float green, float blue) { glColor3f(red, green, blue); // Set the input color glMatrixMode(GL_PROJECTION); // Move into projection mode glPushMatrix(); // push a matrix onto the stack glLoadIdentity(); // load the identity matrix glMatrixMode(GL_MODELVIEW); // switch into model view glPushMatrix(); // push a new matrix onto the stack glLoadIdentity(); gluOrtho2D(0, 512, 0, 512); // set our window co-ordinates glTranslatef(pos_x, pos_y, 0.0f); // translate by the inputted values glScalef(scale, scale, 1.0f); // scale based on inputted scale int length = word.size(); // find the size of the word for(int i=0; i<length; i++) glutStrokeCharacter(GLUT_STROKE_ROMAN, word[i]); //type out each letter in the word glPopMatrix();
  • 2.
    glMatrixMode(GL_PROJECTION); glPopMatrix(); // pop matricesin order to reset the changes and complete function } std::string makeString(int value) { std::stringstream convert; convert << value; return convert.str(); // convert an int into a string } void writeScore() { // for each method, use drawWord to draw what we need in this postion drawWord("Score",390,400,0.3f,1.0f,0.0f,0.2f); drawWord(makeString(score),390,365,0.2f,1.0f,1.0f,1.0f); } void writeLevel() { drawWord("Level",390,256,0.3f,0.0f,0.5f,0.0f); drawWord(makeString(level),430,225,0.2f,1.0f,1.0f,1.0f); } void drawNextPiece() { float red = 0.0f; float green = 0.0f; float blue = 0.0f; int nextGrid[4][4]; // reset the next piece tracker for(int x = 0; x < 4; x++) { for(int y =0; y < 4; y++) { nextGrid[x][y] = 0; } } switch(nextPiece) { // based on our chosen next piece, fill in the relevant values in our 4x4 array and set the colour case 0: red = 1.0f; nextGrid[1][0] = 1; nextGrid[1][1] = 1; nextGrid[1][2] = 1; nextGrid[1][3] = 1; break; case 1: blue = 1.0f; nextGrid[1][1] = 1; nextGrid[1][2] = 1; nextGrid[2][2] = 1; nextGrid[2][1] = 1; break; case 2: green = 1.0f; red = 1.0f; nextGrid[0][2] = 1; nextGrid[1][1] = 1; nextGrid[1][2] = 1; nextGrid[2][1] = 1; break;
  • 3.
    case 3: green= 1.0f; nextGrid[0][1] = 1; nextGrid[1][1] = 1; nextGrid[1][2] = 1; nextGrid[2][2] = 1; break; case 4: red = 1.0f; green = 0.5f; nextGrid[1][0] = 1; nextGrid[1][1] = 1; nextGrid[1][2] = 1; nextGrid[2][0] = 1; break; case 5: green = 1.0f; blue = 1.0f; nextGrid[2][0] = 1; nextGrid[2][1] = 1; nextGrid[2][2] = 1; nextGrid[1][0] = 1; break; case 6: red = 0.5f; blue = 0.5f; nextGrid[0][1] = 1; nextGrid[1][1] = 1; nextGrid[2][1] = 1; nextGrid[1][2] = 1; break; } for(int x = 0; x < 4; x++) { // for each filled part of our 2d array, we draw a solid cube and wire grid on screen for(int y = 0; y < 4; y++) { if(nextGrid[x][y] == 1) { // As in drawGrid method, with alternate translation values (see below) glMatrixMode(GL_PROJECTION); glPushMatrix(); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glTranslatef((- 0.8125f+(x*0.075f)),(0.1375f+(y*0.075f)),0.0f); glColor3f(red,green,blue); glutSolidCube(0.075f); glColor3f(0.0f,0.0f,0.0f); glutWireCube(0.075f); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); } } } } void showNext() { // we draw a box to enclose our nextPiece and draw the word and piece required also drawWord("Next",30,400,0.3f,0.0f,0.0f,1.0f); glLineWidth(0.2f); glColor3f(1.0f,1.0f,1.0f); glBegin(GL_LINE_LOOP); glVertex2f(-0.9f, 0.45f); glVertex2f(-0.5f, 0.45f); glVertex2f(-0.5f,0.05f); glVertex2f(-0.9f,0.05f); glEnd(); drawNextPiece();
  • 4.
    } void startGrid() { //initialise a blank grid for(int x = 0; x < 10; x++) { for(int y = 0; y < 22; y++) { grid[x][y] = 0; } } } void drawGrid() { // draw our tetris board based on our 2d array float red; float green; float blue; for(int y = 0; y < 20; y++) { for(int x = 0; x < 10; x++) { red = 0.0f; green = 0.0f; blue = 0.0f; switch(grid[x][y]) { // set the colour based on the value of the array at this point case 0: break; case 1: red = 0.8f; green = 0.8f; blue = 0.8f; break; case 2: red = 1.0f; break; case 3: blue = 1.0f; break; case 4: red = 1.0f; green = 1.0f; break; case 5: green = 1.0f; break; case 6: red = 1.0f; green = 0.5f; break; case 7: green = 1.0f; blue = 1.0f; break; case 8: red = 0.5f; blue = 0.5f; break; } if(grid[x][y] != 0) { // if we find a non-blank space, we must draw glMatrixMode(GL_PROJECTION); glPushMatrix(); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glTranslatef((-0.405f+(x*0.09f)),(- 0.855f+(y*0.09f)),0.0f); // we translate by our bottom left values corner for x and y, plus the change in x and y defined by the array position glColor3f(red,green,blue); glutSolidCube(0.09f); // we draw a solid cube of required colour, with single pixel size glColor3f(0.0f,0.0f,0.0f);
  • 5.
    glutWireCube(0.09f); // we thendraw the black wire frame to show it as a pixel glPopMatrix(); // we undo the translation by popping the matrix off the stack glMatrixMode(GL_PROJECTION); glPopMatrix(); } } } } void newPiece() { nextPiece = rand() % 7; // to randomly choose a piece, we pick a random number between 0 and 6 } void drawPiece() { for(int y = 0; y < 20; y++) { // this resets all non "dead-block" values in the board, i.e. erases our live piece for(int x = 0; x < 10; x++) { if(grid[x][y] != 1) grid[x][y] = 0; } } for(int y = 0; y < 4; y++) { // this then draws the piece into our 2d array, based on the current shape array and the tracked vertical and horizontal for(int x = 0; x < 4; x++) { // values of our bottom left corner if((horz+x >= 0) && (horz+x < 10) && (vert+y >= 0) && (vert+y < 20)) { if(grid[horz + x][vert + y] != 1) grid[horz + x][vert + y] = currShape[x][y]; } } } } void testGameOver() { for(int y = 0; y < 4; y++) { // if our piece is blocked from entering the grid, we state it as game over for(int x = 0; x < 4; x++) { if((currShape[x][y] != 0) & (grid[horz + x][vert + y] == 1)) { gameOver = 1; gamePause = 1; // we put the game into a state of pause to stop idle and user input
  • 6.
    } } } } void drawNewPiece() { //this will add our next piece into our current piece array for(int y = 0; y < 4; y++) { // we first reset our current piece array for(int x = 0; x < 4; x++) { currShape[x][y] = 0; } } switch (nextPiece) { // then set the relevant values of our array case 0: currShape[0][2] = 2; currShape[1][2] = 2; currShape[2][2] = 2; currShape[3][2] = 2; break; case 1: currShape[1][2] = 3; currShape[1][1] = 3; currShape[2][2] = 3; currShape[2][1] = 3; break; case 2: currShape[0][2] = 4; currShape[1][1] = 4; currShape[1][2] = 4; currShape[2][1] = 4; break; case 3: currShape[0][1] = 5; currShape[1][1] = 5; currShape[1][2] = 5; currShape[2][2] = 5; break; case 4: currShape[1][0] = 6; currShape[1][1] = 6; currShape[1][2] = 6; currShape[2][0] = 6; break; case 5: currShape[1][0] = 7; currShape[2][0] = 7; currShape[2][1] = 7; currShape[2][2] = 7; break; case 6: currShape[0][1] = 8; currShape[1][1] = 8; currShape[2][1] = 8; currShape[1][2] = 8; break; } horz = 3; // we must also reset the tracked position of our bottom left corner vert = 17; testGameOver(); // we test if the piece can be inserted into our grid if(gameOver == 0) { drawPiece(); // if the piece can be drawn in, we add it to the board and generate our new "next" piece newPiece(); } } void display() { glClear(GL_COLOR_BUFFER_BIT); // we clear the display glColor3f(1.0f, 1.0f, 1.0f); // set RGB values of colour to draw glLineWidth(3.0f); glBegin(GL_LINES); // and draw the outline of the grid in white
  • 7.
    glVertex2f(0.45f,0.9f); glVertex2f(0.45f,-0.9f); glVertex2f(0.45f,-0.9f); glVertex2f(-0.45f,-0.9f); glVertex2f(-0.45f,-0.9f); glVertex2f(-0.45f,0.9f); glEnd(); writeScore(); writeLevel(); showNext(); drawGrid(); drawWord("Press 'q' toquit, 'p' to pause and 'r' to restart",120,10,0.1f,0.0f,1.0f,1.0f); // this adds some feature instructions on the bottom of the screen if(gameOver == 1) { // if the game is over, draw this in drawWord("Game",10,150,0.3f,1.0f,0.5f,0.0f); drawWord("Over",25,120,0.3f,1.0f,0.5f,0.0f); } else if(gamePause == 1) // if we are paused but not over, draw this in drawWord("Paused",10,150,0.3f,1.0f,0.5f,0.0f); else if(((ticks % 4500) <= 20) && (ticks > 20)) { // if we are set to level up, show this message for 20 ticks drawWord("Level",380,150,0.3f,0.3f,0.3f,1.0f); drawWord("Up",400,100,0.3f,0.3f,0.3f,1.0f); } glutSwapBuffers(); } int detect() { // detect if a piece is colliding int test = 0; for(int y = 0; y < 20; y++) { for(int x = 0; x < 10; x++) { if((grid[x][y] != 0) & (grid[x][y] != 1)) { // if we have found our live piece if(y == 0) { // if we have hit the floor, collision found test = 1; } else if(grid[x][y-1] == 1) { // if we are resting on a dead block, collision found test = 1; } } } } return test; }
  • 8.
    void removeRow(int y){ // when a row is removed, this function grabs the rows above to drop them down for(int a = y; a < 19; a++) { for(int x = 0; x < 10; x++) { grid[x][a] = grid[x][(a+1)]; } } } void testScore() { // this tests for scoring rows int change = 0; // track the row changes for(int y = 0; y < 20; y++) { int test = 0; for(int x = 0; x < 10; x++) { if(grid[x][y] != 1) // if we find a non-dead block, then we have no score for this row test = 1; } if(test == 0) { // if this row scores, remove it, decrease y (to check row above after change) and increase row change by 1 removeRow(y); y--; change++; } } switch(change) { // our score change is based on how many rows are removed this sweep case 1: score += (40*level); break; case 2: score += (100*level); break; case 3: score += (300*level); break; case 4: score += (1200*level); break; } } void idle() { glutPostRedisplay(); // our idle function controls the display if(gamePause == -1) { // if we have not paused the game
  • 9.
    ticks++; // increase ournumber of ticks if((ticks % 4500) == 0) { // every 4500 ticks, increase the level to a maximum of 10 if(level < 10) level++; } if((ticks % (int)(60/level)) == 0) { // This controls the speed based on level. Every (60/level) ticks, we move down a row int count = 0; int collision = detect(); if(collision == 0) { // If we will not collide, decrease the y co-ord and draw the piece into its new position vert--; drawPiece(); } else { for(int y = 0; y < 20; y++) { // If we do collide, then turn each live piece into a dead piece before resetting the collision for(int x = 0; x < 10; x++) { if((grid[x][y] != 0) & (grid[x][y] != 1)) { grid[x][y] = 1; count++; if(count == 4) collision = 0; } } } } testScore(); // As a new dead block has been added, test to see if this scores if(count == 4) drawNewPiece(); // If we added a dead block, we need to draw in our new piece } } } void restart() { // A restart function for the game, resetting all variables and the board and pieces level = 1; score = 0; gameOver = 0;
  • 10.
    gamePause = -1; startGrid(); newPiece(); drawNewPiece(); } voiddrop() { // A method to drop until we collide int collision = 0; int count = 0; while(collision == 0) { // Until we find a collision, continue to drop and update the piece vert--; drawPiece(); collision = detect(); } for(int y = 0; y < 20; y++) { // Once we have the colision, turn our live piece into a dead block for(int x = 0; x < 10; x++) { if((grid[x][y] != 0) & (grid[x][y] != 1)) { grid[x][y] = 1; count++; if(count == 4) collision = 0; } } } testScore(); // Again, we test score and draw in our new piece drawNewPiece(); } void keyboard(unsigned char key, int x, int y) { // 4 functions. A quit, restart, piece drop and pause built into the game switch(key) { case 'q': exit(1); break; case 'r': restart(); break; case ' ': if((gamePause == -1) && (detect() == 0)) drop(); break; case 'p': if(gameOver == 0) gamePause*=-1; break; } } int wallLeft() { // Detect a block on the left
  • 11.
    int test =0; for(int y = 0; y < 20; y++) { for(int x = 0; x < 10; x++) { if((grid[x][y] != 0) & (grid[x][y] != 1)) { if(x == 0) { // If we are against the left side of the grid, we cannot move test = 1; } else if(grid[x-1][y] == 1) { // Or if a dead piece lies to the left of us, we cannot move test = 1; } } } } return test; } int wallRight() { // As above, but with inversion to look to our right int test = 0; for(int y = 0; y < 20; y++) { for(int x = 0; x < 10; x++) { if((grid[x][y] != 0) & (grid[x][y] != 1)) { if(x == 9) { test = 1; } else if(grid[x+1][y] == 1) { test = 1; } } } } return test; } void moveLeft() { // If there is no left collision, move the piece left and update int collision = wallLeft(); if(collision == 0) horz--; drawPiece(); } void moveRight() { // As above, but with inversion to move right int collision = wallRight(); if(collision == 0) horz++; drawPiece();
  • 12.
    } int rTest() { //This is to test the validity of a rotation int test = 0; for(int x = 0; x < 4; x++) { for(int y = 0; y < 4; y++) { if(temp[x][y] != 0) { if((horz + x < 0) || (horz + x > 9) || (vert + y < 0) || (vert + y > 19)) // If our rotation takes us out of range, the rotation fails so must be blocked test = 1; else if(grid[horz + x][vert + y] == 1) // Or if we would overwrite a dead piece, we again block the rotation test = 1; } } } return test; } void rotate() { for(int x = 0; x < 4; x++) { // Roatate our current shape 90 degrees and into a temporary 2D array for testing for(int y = 0; y < 4; y++) { temp[y][3-x] = currShape[x][y]; } } if(rTest() == 0) { // If our rotation is not blocked, copy over our temporary array into our current shape for(int x = 0; x < 4; x++) { for(int y = 0; y < 4; y++) { currShape[x][y] = temp[x][y]; } } drawPiece(); // In either case, we can update our current piece } } void moveDown() { // A function to move down on the user input int collision = detect(); int count = 0; if(collision == 0) { // If we have no collision, we can move down and update the piece
  • 13.
    vert--; drawPiece(); } else { // Ifwe do collide, then as above we make our live block dead, test for score and generate a new piece for(int y = 0; y < 20; y++) { for(int x = 0; x < 10; x++) { if((grid[x][y] != 0) & (grid[x][y] != 1)) { grid[x][y] = 1; count++; if(count == 4) collision = 0; } } } testScore(); if(count == 4) drawNewPiece(); } } void special(int key, int x, int y) { // Move or rotate the live piece based on the user arrow input if(gamePause == -1) { switch(key) { case GLUT_KEY_LEFT: moveLeft(); break; case GLUT_KEY_RIGHT: moveRight(); break; case GLUT_KEY_UP: rotate(); break; case GLUT_KEY_DOWN: moveDown(); break; } } } int main(int argc, char* argv[]) { // A method to initialise our window and set our display and idle functions, as well as setting up the start of the board startGrid(); srand(time(NULL)); // Ensure a truly random sequence by basing it off of the time glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA); glutInitWindowSize(512,512); glutInitWindowPosition(20,20); glutCreateWindow("Tetris"); glClearColor(0.0f,0.0f,0.0f,1.0f); glutDisplayFunc(display);
  • 14.