Tetris.cpp
/* File: Tetris.cpp
*
* Author: Ryan Pickelsimer
* Class: Programming 2, Section 1
* Date: September 14, 2014
*
* Purpose: The game of Tetris.
*/
#include "stdafx.h"
#include <iostream>
#include <conio.h>
#include <vector>
#include <string>
#include <windows.h>
#include <cstdlib>
#include <ctime>
using namespace std;
/*********************/
/* program constants */
const static int SCREEN_WIDTH = 365, SCREEN_HEIGHT = 435; // used to size console
const static int COLUMNS = 41, ROWS = 30, MID_COLUMN = COLUMNS - 20; // used for memory map
const static int NEXT_TETRIS_ROW = 9; // position for next piece
const static int NEXT_TETRIS_COLUMN = COLUMNS - 12; // position for next piece
const static int CURRENT_TETRIS_ROW = 5; // position for current piece
const static int CURRENT_TETRIS_COLUMN = (COLUMNS - 20) / 2; // position for current piece
const static int CURRENT = 0, NEXT = 1; // used for vector index
const static int NUM_SHAPES = 7; // number of different Tetris subclasses
const static int LEVEL_1_SPEED = 300; // beginning game speed
const static int SPEED_MULTIPLIER = 20; // speed increment between levels
const static int ROWS_2_NEXT_LEVEL = 10; // rows needed for next level
/***********************************************/
/* program class declaration and definitons */
/* Board object allows for easy board passing and modification */
class Board {
protected:
char board[ROWS][COLUMNS];
public:
friend void gotoXY(int row, int column);
void setBoard(int row, int column, char character) { board[row][column] = character; }
char getBoard(int row, int column) { return board[row][column]; }
void initializeBoard();
void drawBoard();
int testForFullRows();
};
/* initializes a board memory map with borders */
void Board::initializeBoard(){
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLUMNS; j++){
/** create borders and initialize a game board array **/
if ((i == 0) || (j == 0) || (i == ROWS - 1) || (j == COLUMNS - 1) || (j == MID_COLUMN)) {
board[i][j] = (char)219;
gotoXY(i, j);
cout << board[i][j];
}
else
board[i][j] = ' ';
}
}
}
/* draws a current state of the game board. */
void Board::drawBoard(){
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLUMNS; j++){
gotoXY(i, j);
cout << board[i][j];
}
}
}
/* removes full rows and returns an int value equal to number rows removed */
int Board::testForFullRows() {
bool fullRow = false;
int rows = 0;
// test each row for full row.
for (int i = ROWS - 2; i > 4; i--) {
for (int j = MID_COLUMN - 1; j > 0; j--){
if (board[i][j] != ' ')
fullRow = true;
else
fullRow = false;
// skip to next row if there is a space
if (!fullRow)
break;
}
// move rows down if full row
if (fullRow){
rows++;
for (int k = i; k > 4; k--) {
for (int l = MID_COLUMN - 1; l > 0; l--) {
board[k][l] = board[k - 1][l];
}
}
i++; // check same row index with new contents.
}
}
return rows;
}
/* Abstract class for various tetris shapes */
class Tetris {
protected:
int rowPosition, columnPosition;
int rotation;
char graphic;
public:
Tetris(){
rotation = 0;
rowPosition = NEXT_TETRIS_ROW;
columnPosition = NEXT_TETRIS_COLUMN;
}
friend void gotoXY(int row, int column);
/* Display, erase, and manipulate memory map*/
/* bool erase == true removes characters from screen and memory map*/
virtual void tetrisDisplay(Board&, bool) =0;
/* return true for tetris landing on a surface*/
virtual bool testForStop(Board, int) =0;
void incrementRotation(){ rotation++; }
void incrementRowPosition(){ rowPosition++; }
void decrementRowPosition(){ rowPosition--; }
void incrementColumnPosition(){ columnPosition++; }
void decrementColumnPosition(){ columnPosition--; }
int getColumnPosition(){ return columnPosition; }
int getRowPosition(){ return rowPosition; }
void setPosition(int row, int col) {
rowPosition = row;
columnPosition = col;
}
};
/* Square class inherits Tetris */
class Square : public Tetris {
public:
Square() :Tetris(){ graphic = char(219); };
/* simulates tetris motion by erasing at old positions and drawing at new ones */
/* also modifies board array */
void tetrisDisplay(Board &board, bool erase);
bool testForStop(Board board, int horizChange);
};
/* Square class virtual function definition */
void Square::tetrisDisplay(Board &board, bool erase){
char symbol;
if (erase == true)
symbol = ' ';
else symbol = graphic;
board.setBoard(rowPosition, columnPosition, symbol);
gotoXY(rowPosition, columnPosition);
cout << symbol;
board.setBoard(rowPosition, columnPosition + 1, symbol);
gotoXY(rowPosition, columnPosition + 1);
cout << symbol;
board.setBoard(rowPosition - 1, columnPosition, symbol);
gotoXY(rowPosition - 1, columnPosition);
cout << symbol;
board.setBoard(rowPosition - 1, columnPosition + 1, symbol);
gotoXY(rowPosition - 1, columnPosition + 1);
cout << symbol;
gotoXY(0, 0); //moves cursor to corner of screen
};
/* Square class virtual function definition */
bool Square::testForStop(Board board, int horizChange){
if (board.getBoard(rowPosition + 1, columnPosition + horizChange) != ' ' ||
board.getBoard(rowPosition + 1, columnPosition + 1 + horizChange) != ' ')
return true;
else return false;
}
/* Line Class inherits Tetris */
class Line : public Tetris {
public:
Line() :Tetris(){ graphic = char(177); };
/* simulates tetris motion by erasing at old positions and drawing at new ones */
/* also modifies board array */
void tetrisDisplay(Board &board, bool erase);
bool testForStop(Board board, int horizChange);
};
/* Line Class virtual function definition */
void Line::tetrisDisplay(Board &board, bool erase) {
char symbol;
if (erase == true)
symbol = ' ';
else symbol = graphic;
// vertical position
if (rotation % 2 == 0){
for (int i = 0; i < 4; i++) {
board.setBoard(rowPosition - i, columnPosition, symbol);
gotoXY(rowPosition - i, columnPosition);
cout << symbol;
}
}
// horizontal position
else if (rotation % 2 == 1) {
for (int i = 0; i < 4; i++) {
board.setBoard(rowPosition, columnPosition + i, symbol);
gotoXY(rowPosition, columnPosition + i);
cout << symbol;
}
}
gotoXY(0, 0); //moves cursor to corner of screen
}
/* Line Class virtual function definition */
bool Line::testForStop(Board board, int horizChange) {
// test vertical position
if (rotation % 2 == 0) {
if (board.getBoard(rowPosition + 1, columnPosition + horizChange) != ' ')
return true;
}
// test horizontal position
else if (rotation % 2 == 1) {
if (board.getBoard(rowPosition + 1, columnPosition + horizChange) != ' ' ||
board.getBoard(rowPosition + 1, columnPosition + 1 + horizChange) != ' ' ||
board.getBoard(rowPosition + 1, columnPosition + 2 + horizChange) != ' ' ||
board.getBoard(rowPosition + 1, columnPosition + 3 + horizChange) != ' ')
return true;
}
// else piece is still falling
return false;
}
/* Z Class inherits Tetris */
class Z : public Tetris {
public:
Z() :Tetris(){ graphic = char(176); };
/* simulates tetris motion by erasing at old positions and drawing at new ones */
/* also modifies board array */
void tetrisDisplay(Board &board, bool erase);
bool testForStop(Board board, int horizChange);
};
/* Z Class virtual function definition */
void Z::tetrisDisplay(Board &board, bool erase) {
char symbol;
if (erase == true)
symbol = ' ';
else symbol = graphic;
// horizontal position
if (rotation % 2 == 0){
board.setBoard(rowPosition, columnPosition, symbol);
gotoXY(rowPosition, columnPosition);
cout << symbol;
board.setBoard(rowPosition, columnPosition + 1, symbol);
gotoXY(rowPosition, columnPosition + 1);
cout << symbol;
board.setBoard(rowPosition - 1, columnPosition - 1, symbol);
gotoXY(rowPosition - 1, columnPosition - 1);
cout << symbol;
board.setBoard(rowPosition - 1, columnPosition, symbol);
gotoXY(rowPosition - 1, columnPosition);
cout << symbol;
}
// vertical position
else if (rotation % 2 == 1) {
board.setBoard(rowPosition, columnPosition, symbol);
gotoXY(rowPosition, columnPosition);
cout << symbol;
board.setBoard(rowPosition - 1, columnPosition , symbol);
gotoXY(rowPosition - 1, columnPosition);
cout << symbol;
board.setBoard(rowPosition - 1, columnPosition + 1, symbol);
gotoXY(rowPosition - 1, columnPosition + 1);
cout << symbol;
board.setBoard(rowPosition - 2, columnPosition + 1, symbol);
gotoXY(rowPosition - 2, columnPosition +1);
cout << symbol;
}
gotoXY(0, 0); //moves cursor to corner of screen
}
/* Z Class virtual function definition */
bool Z::testForStop(Board board, int horizChange) {
// test horizontal position
if (rotation % 2 == 0) {
if (board.getBoard(rowPosition + 1, columnPosition + horizChange) != ' ' ||
board.getBoard(rowPosition + 1, columnPosition + 1 + horizChange) != ' ')
return true;
else if (horizChange == 0 && board.getBoard(rowPosition + 1 - 1, columnPosition - 1) != ' ')
return true;
else if (horizChange < 0 && board.getBoard(rowPosition + 1 - 1, columnPosition - 2) != ' ')
return true;
}
// test vertical position
else if (rotation % 2 == 1) {
if (board.getBoard(rowPosition + 1, columnPosition + horizChange) != ' ')
return true;
else if (horizChange == 0 && board.getBoard(rowPosition + 1 - 1, columnPosition + 1) != ' ' )
return true;
else if (horizChange > 0 && board.getBoard(rowPosition + 1 - 1, columnPosition + 2) != ' ')
return true;
}
// else piece is still falling
return false;
}
/* S Class inherits Tetris */
class S : public Tetris {
public:
S() :Tetris(){ graphic = char(178); };
/* simulates tetris motion by erasing at old positions and drawing at new ones */
/* also modifies board array */
void tetrisDisplay(Board &board, bool erase);
bool testForStop(Board board, int horizChange);
};
/* S Class virtual function definition */
void S::tetrisDisplay(Board &board, bool erase) {
char symbol;
if (erase == true)
symbol = ' ';
else symbol = graphic;
// horizontal position
if (rotation % 2 == 0){
board.setBoard(rowPosition, columnPosition, symbol);
gotoXY(rowPosition, columnPosition);
cout << symbol;
board.setBoard(rowPosition, columnPosition + 1, symbol);
gotoXY(rowPosition, columnPosition + 1);
cout << symbol;
board.setBoard(rowPosition - 1, columnPosition + 1, symbol);
gotoXY(rowPosition - 1, columnPosition + 1);
cout << symbol;
board.setBoard(rowPosition - 1, columnPosition + 2, symbol);
gotoXY(rowPosition - 1, columnPosition + 2);
cout << symbol;
}
// vertical position
else if (rotation % 2 == 1) {
board.setBoard(rowPosition, columnPosition, symbol);
gotoXY(rowPosition, columnPosition);
cout << symbol;
board.setBoard(rowPosition - 1, columnPosition, symbol);
gotoXY(rowPosition - 1, columnPosition);
cout << symbol;
board.setBoard(rowPosition - 1, columnPosition - 1, symbol);
gotoXY(rowPosition - 1, columnPosition - 1);
cout << symbol;
board.setBoard(rowPosition - 2, columnPosition - 1, symbol);
gotoXY(rowPosition - 2, columnPosition - 1);
cout << symbol;
}
gotoXY(0, 0); //moves cursor to corner of screen
}
/* S Class virtual function definition */
bool S::testForStop(Board board, int horizChange) {
// test horizontal position
if (rotation % 2 == 0) {
if (board.getBoard(rowPosition + 1, columnPosition + horizChange) != ' ' ||
board.getBoard(rowPosition + 1, columnPosition + 1 + horizChange) != ' ')
return true;
else if (horizChange == 0 && board.getBoard(rowPosition + 1 - 1, columnPosition + 2) != ' ')
return true;
else if (horizChange > 0 && board.getBoard(rowPosition + 1 - 1, columnPosition + 3) != ' ')
return true;
}
// test vertical position
else if (rotation % 2 == 1) {
if (board.getBoard(rowPosition + 1, columnPosition + horizChange) != ' ')
return true;
else if (horizChange == 0 && board.getBoard(rowPosition + 1 - 1, columnPosition - 1) != ' ')
return true;
else if (horizChange < 0 && board.getBoard(rowPosition + 1 - 1, columnPosition - 2) != ' ')
return true;
}
// else piece is still falling
return false;
}
/* J Class inherits Tetris */
class J : public Tetris {
public:
J() :Tetris(){ graphic = char(176); };
/* simulates tetris motion by erasing at old positions and drawing at new ones */
/* also modifies board array */
void tetrisDisplay(Board &board, bool erase);
bool testForStop(Board board, int horizChange);
};
/* J Class virtual function definition */
void J::tetrisDisplay(Board &board, bool erase) {
char symbol;
if (erase == true)
symbol = ' ';
else symbol = graphic;
// position 0
if (rotation % 4 == 0){
for (int i = 0; i < 3; i++) {
board.setBoard(rowPosition, columnPosition + i, symbol);
gotoXY(rowPosition, columnPosition + i);
cout << symbol;
}
board.setBoard(rowPosition - 1, columnPosition , symbol);
gotoXY(rowPosition - 1, columnPosition);
cout << symbol;
}
// position 1
else if (rotation % 4 == 1) {
for (int i = 0; i < 3; i++) {
board.setBoard(rowPosition - i, columnPosition, symbol);
gotoXY(rowPosition - i, columnPosition);
cout << symbol;
}
board.setBoard(rowPosition - 2, columnPosition + 1, symbol);
gotoXY(rowPosition - 2, columnPosition + 1);
cout << symbol;
}
// position 2
else if (rotation % 4 == 2) {
for (int i = 0; i < 3; i++) {
board.setBoard(rowPosition - 1, columnPosition - i, symbol);
gotoXY(rowPosition - 1, columnPosition - i);
cout << symbol;
}
board.setBoard(rowPosition, columnPosition, symbol);
gotoXY(rowPosition, columnPosition);
cout << symbol;
}
// position 3
else if (rotation % 4 == 3) {
for (int i = 0; i < 3; i++) {
board.setBoard(rowPosition - i, columnPosition + 1, symbol);
gotoXY(rowPosition - i, columnPosition + 1);
cout << symbol;
}
board.setBoard(rowPosition, columnPosition, symbol);
gotoXY(rowPosition, columnPosition);
cout << symbol;
}
gotoXY(0, 0); //moves cursor to corner of screen
}
/* J Class virtual function definition */
bool J::testForStop(Board board, int horizChange) {
// test position 0
if (rotation % 4 == 0) {
if (board.getBoard(rowPosition + 1, columnPosition + horizChange) != ' ' ||
board.getBoard(rowPosition + 1, columnPosition + 1 + horizChange) != ' ' ||
board.getBoard(rowPosition + 1, columnPosition + 2 + horizChange) != ' ')
return true;
}
// test position 1
else if (rotation % 4 == 1) {
if (board.getBoard(rowPosition + 1, columnPosition + horizChange) != ' ')
return true;
else if (horizChange == 0 && board.getBoard(rowPosition + 1 - 2, columnPosition + 1) != ' ')
return true;
else if (horizChange > 0 && board.getBoard(rowPosition + 1 - 2, columnPosition + 2) != ' ')
return true;
}
// test position 2
else if (rotation % 4 == 2) {
if (board.getBoard(rowPosition + 1, columnPosition + horizChange) != ' ' ||
board.getBoard(rowPosition + 1 - 1, columnPosition - 2 + horizChange) != ' ')
return true;
else if (horizChange == 0 && board.getBoard(rowPosition + 1 - 1, columnPosition - 1) != ' ')
return true;
}
// test position 3
else if (rotation % 4 == 3) {
if (board.getBoard(rowPosition + 1, columnPosition + horizChange) != ' ' ||
board.getBoard(rowPosition + 1, columnPosition + 1 + horizChange) != ' ')
return true;
}
// else piece is still falling
return false;
}
/* L Class inherits Tetris */
class L : public Tetris {
public:
L() :Tetris(){ graphic = char(178); };
/* simulates tetris motion by erasing at old positions and drawing at new ones */
/* also modifies board array */
void tetrisDisplay(Board &board, bool erase);
bool testForStop(Board board, int horizChange);
};
/* L Class virtual function definition */
void L::tetrisDisplay(Board &board, bool erase) {
char symbol;
if (erase == true)
symbol = ' ';
else symbol = graphic;
// position 0
if (rotation % 4 == 0){
for (int i = 0; i < 3; i++) {
board.setBoard(rowPosition, columnPosition + i, symbol);
gotoXY(rowPosition, columnPosition + i);
cout << symbol;
}
board.setBoard(rowPosition - 1, columnPosition + 2, symbol);
gotoXY(rowPosition - 1, columnPosition + 2);
cout << symbol;
}
// position 1
else if (rotation % 4 == 1) {
for (int i = 0; i < 3; i++) {
board.setBoard(rowPosition - i, columnPosition, symbol);
gotoXY(rowPosition - i, columnPosition);
cout << symbol;
}
board.setBoard(rowPosition, columnPosition + 1, symbol);
gotoXY(rowPosition, columnPosition + 1);
cout << symbol;
}
// position 2
else if (rotation % 4 == 2) {
for (int i = 0; i < 3; i++) {
board.setBoard(rowPosition - 1, columnPosition + i, symbol);
gotoXY(rowPosition - 1, columnPosition + i);
cout << symbol;
}
board.setBoard(rowPosition, columnPosition, symbol);
gotoXY(rowPosition, columnPosition);
cout << symbol;
}
// position 3
else if (rotation % 4 == 3) {
for (int i = 0; i < 3; i++) {
board.setBoard(rowPosition - i, columnPosition, symbol);
gotoXY(rowPosition - i, columnPosition);
cout << symbol;
}
board.setBoard(rowPosition - 2, columnPosition - 1, symbol);
gotoXY(rowPosition - 2, columnPosition - 1);
cout << symbol;
}
gotoXY(0, 0); //moves cursor to corner of screen
}
/* L Class virtual function definition */
bool L::testForStop(Board board, int horizChange) {
// test position 0
if (rotation % 4 == 0) {
if (board.getBoard(rowPosition + 1, columnPosition + horizChange) != ' ' ||
board.getBoard(rowPosition + 1, columnPosition + 1 + horizChange) != ' ' ||
board.getBoard(rowPosition + 1, columnPosition + 2 + horizChange) != ' ')
return true;
}
// test position 1
else if (rotation % 4 == 1) {
if (board.getBoard(rowPosition + 1, columnPosition + horizChange) != ' ' ||
board.getBoard(rowPosition + 1, columnPosition + 1 + horizChange) != ' ')
return true;
}
// test position 2
else if (rotation % 4 == 2) {
if (board.getBoard(rowPosition + 1, columnPosition + horizChange) != ' ' ||
board.getBoard(rowPosition + 1 - 1, columnPosition + 2 + horizChange) != ' ')
return true;
else if (horizChange == 0 && board.getBoard(rowPosition + 1 - 1, columnPosition + 1) != ' ')
return true;
}
// test position 3
else if (rotation % 4 == 3) {
if (board.getBoard(rowPosition + 1, columnPosition + horizChange) != ' ')
return true;
else if (horizChange == 0 && board.getBoard(rowPosition + 1 - 2, columnPosition - 1) != ' ')
return true;
else if (horizChange < 0 && board.getBoard(rowPosition + 1 - 2, columnPosition - 2) != ' ')
return true;
}
// else piece is still falling
return false;
}
/* T Class inherits Tetris */
class T : public Tetris {
public:
T() :Tetris(){ graphic = char(219); };
/* simulates tetris motion by erasing at old positions and drawing at new ones */
/* also modifies board array */
void tetrisDisplay(Board &board, bool erase);
bool testForStop(Board board, int horizChange);
};
/* T Class virtual function definition */
void T::tetrisDisplay(Board &board, bool erase) {
char symbol;
if (erase == true)
symbol = ' ';
else symbol = graphic;
// position 0
if (rotation % 4 == 0){
for (int i = 0; i < 3; i++) {
board.setBoard(rowPosition, columnPosition + i, symbol);
gotoXY(rowPosition, columnPosition + i);
cout << symbol;
}
board.setBoard(rowPosition - 1, columnPosition + 1, symbol);
gotoXY(rowPosition - 1, columnPosition + 1);
cout << symbol;
}
// position 1
else if (rotation % 4 == 1) {
for (int i = 0; i < 3; i++) {
board.setBoard(rowPosition - i, columnPosition, symbol);
gotoXY(rowPosition - i, columnPosition);
cout << symbol;
}
board.setBoard(rowPosition - 1, columnPosition + 1, symbol);
gotoXY(rowPosition - 1, columnPosition + 1);
cout << symbol;
}
// position 2
else if (rotation % 4 == 2) {
for (int i = 0; i < 3; i++) {
board.setBoard(rowPosition - 1, columnPosition - 1 + i, symbol);
gotoXY(rowPosition - 1, columnPosition - 1 + i);
cout << symbol;
}
board.setBoard(rowPosition, columnPosition, symbol);
gotoXY(rowPosition, columnPosition);
cout << symbol;
}
// position 3
else if (rotation % 4 == 3) {
for (int i = 0; i < 3; i++) {
board.setBoard(rowPosition - i, columnPosition, symbol);
gotoXY(rowPosition - i, columnPosition);
cout << symbol;
}
board.setBoard(rowPosition - 1, columnPosition - 1, symbol);
gotoXY(rowPosition - 1, columnPosition - 1);
cout << symbol;
}
gotoXY(0, 0); //moves cursor to corner of screen
}
/* T Class virtual function definition */
bool T::testForStop(Board board, int horizChange) {
// test position 0
if (rotation % 4 == 0) {
if (board.getBoard(rowPosition + 1, columnPosition + horizChange) != ' ' ||
board.getBoard(rowPosition + 1, columnPosition + 1 + horizChange) != ' ' ||
board.getBoard(rowPosition + 1, columnPosition + 2 + horizChange) != ' ')
return true;
}
// test position 1
else if (rotation % 4 == 1) {
if (board.getBoard(rowPosition + 1, columnPosition + horizChange) != ' ')
return true;
else if (horizChange == 0 && board.getBoard(rowPosition + 1 - 1, columnPosition + 1) != ' ')
return true;
else if (horizChange > 0 && board.getBoard(rowPosition + 1 - 1, columnPosition + 2) != ' ')
return true;
}
// test position 2
else if (rotation % 4 == 2) {
if (board.getBoard(rowPosition + 1, columnPosition + horizChange) != ' ')
return true;
else if (horizChange == 0 && (board.getBoard(rowPosition + 1 - 1, columnPosition - 1) != ' ' ||
board.getBoard(rowPosition + 1 - 1, columnPosition + 1) != ' '))
return true;
else if (horizChange < 0 && board.getBoard(rowPosition + 1 - 1, columnPosition - 2) != ' ')
return true;
else if (horizChange > 0 && board.getBoard(rowPosition + 1 - 1, columnPosition + 2) != ' ')
return true;
}
// test position 3
else if (rotation % 4 == 3) {
if (board.getBoard(rowPosition + 1, columnPosition + horizChange) != ' ')
return true;
else if (horizChange == 0 && board.getBoard(rowPosition + 1 - 1, columnPosition - 1) != ' ')
return true;
else if (horizChange < 0 && board.getBoard(rowPosition + 1 - 1, columnPosition - 2) != ' ')
return true;
}
// else piece is still falling
return false;
}
/***********************************************/
/* program function declaration and definitons */
/* move cursor to a location */
void gotoXY(int row, int column)
{
COORD coord;
coord.X = column;
coord.Y = row;
SetConsoleCursorPosition(
GetStdHandle(STD_OUTPUT_HANDLE),
coord
);
}
/* clear screen */
void clear() {
COORD topLeft = { 0, 0 };
HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO screen;
DWORD written;
GetConsoleScreenBufferInfo(console, &screen);
FillConsoleOutputCharacterA(
console, ' ', screen.dwSize.X * screen.dwSize.Y, topLeft, &written
);
FillConsoleOutputAttribute(
console, FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE,
screen.dwSize.X * screen.dwSize.Y, topLeft, &written
);
SetConsoleCursorPosition(console, topLeft);
}
/* display game information (use with displayCurrentStats()) */
void gameInformation() {
gotoXY(4, COLUMNS - 12);
cout << "Next";
gotoXY(13, COLUMNS - 13);
cout << "Level";
gotoXY(17, COLUMNS - 13);
cout << "Points";
gotoXY(21, COLUMNS - 13);
cout << "Rows to ";
gotoXY(22, COLUMNS - 15);
cout << "Next Level";
}
/* update game statistics to display */
void displayCurrentStats(int level, int score, int rows2next) {
gotoXY(15, COLUMNS - 11);
cout << level;
gotoXY(19, COLUMNS - 11);
cout << score;
gotoXY(24, COLUMNS - 11);
cout << rows2next;
}
/* game instructions */
void welcomeScreen() {
gotoXY(3, COLUMNS / 2 - 3);
cout << "TETRIS ";
gotoXY(8, COLUMNS / 2 - 7);
cout << "Direction Keys";
gotoXY(9, COLUMNS / 2 - 8);
cout << "----------------";
gotoXY(12, COLUMNS / 2 - 9);
cout << "W -> Rotate Tetris";
gotoXY(16, COLUMNS / 3 - 10);
cout << "A -> Move Left";
gotoXY(16, COLUMNS * 2 / 3 - 4);
cout << "D -> Move Right";
gotoXY(20, COLUMNS / 2 - 8);
cout << "S -> Drop Tetris";
gotoXY(23, COLUMNS / 2 - 5);
cout << "P -> Pause";
gotoXY(25, COLUMNS / 2 - 6);
cout << "ESC -> Quit";
gotoXY(27, COLUMNS / 2 - 13);
cout << "Press enter when ready.";
cin.sync();
cin.get();
}
/* creates and adds a tetris shape to a vector*/
void setTetrisVector(vector<Tetris*> &tetris, int index){
switch (index) {
case 0:
tetris.push_back(new Square());
break;
case 1:
tetris.push_back(new Line());
break;
case 2:
tetris.push_back(new Z());
break;
case 3:
tetris.push_back(new S());
break;
case 4:
tetris.push_back(new L());
break;
case 5:
tetris.push_back(new J());
break;
case 6:
tetris.push_back(new T());
break;
}
}
/* return a score for number of rows removed and current level */
int calculateScore(int rows, int level) {
int score;
switch (rows) {
case 1:
score = level * 40 + 40;
break;
case 2:
score = level * 100 + 100;
break;
case 3:
score = level * 300 + 300;
break;
case 4:
score = level * 1200 + 1200;
break;
default:
score = 0;
}
return score;
}
/* returns true for a piece at row 5 (called after piece stops) */
bool testForGameOver(Tetris* tetris) {
if (tetris->getRowPosition() < 6)
return true;
else return false;
}
/*************************************************/
/*************** main function *****************/
int _tmain(int argc, _TCHAR* argv[])
{
/* resize console window */
HWND console = GetConsoleWindow();
RECT r;
GetWindowRect(console, &r); //stores the console's current dimensions
MoveWindow(console, r.left, r.top, SCREEN_WIDTH, SCREEN_HEIGHT, TRUE);
int rowsToNextLevel = 0;
int keyPressed = 0;
int level = 0;
int score = 0;
bool playAgain = true;
/********************/
/* Begin game loop */
while (playAgain) {
/* random number generator */
srand(static_cast<unsigned int>(time(0)));
Board board;
vector<Tetris*> tetris;
//increment level
level++;
// deduct earned rows from last round
rowsToNextLevel = ROWS_2_NEXT_LEVEL + rowsToNextLevel;
// display graphical elements
if (level < 2)
welcomeScreen();
clear();
board.initializeBoard();
gameInformation();
displayCurrentStats(level, score, rowsToNextLevel);
// display current tetris (random) in starting field
setTetrisVector(tetris, rand() % NUM_SHAPES);
tetris[CURRENT]->setPosition(CURRENT_TETRIS_ROW, CURRENT_TETRIS_COLUMN);
tetris[CURRENT]->tetrisDisplay(board, false);
// show the next tetris (random)
setTetrisVector(tetris, rand() % NUM_SHAPES);
tetris[NEXT]->tetrisDisplay(board, false);
/********************/
/* Begin level loop */
while (rowsToNextLevel > 0){
int horizontalChange = 0;
// display current tetris
tetris[CURRENT]->tetrisDisplay(board, false);
// control game speed
int levelSpeed = LEVEL_1_SPEED - (SPEED_MULTIPLIER * level);
if (keyPressed == 's'){
Sleep(50);
keyPressed = 0;
}
else
Sleep(levelSpeed);
//test for player input
if (_kbhit()) {
// process user request
// only valid input from the keyboard is accepted
try {
keyPressed = tolower(_getch());
// pressed left -> move left if possible
if (keyPressed == 'a'){
horizontalChange = -1;
// no horizontal change if any obstruction
if (tetris[CURRENT]->testForStop(board, horizontalChange))
horizontalChange = 0;
}
// pressed right -> move right if possible
else if (keyPressed == 'd'){
horizontalChange = 1;
// no horizontal change if any obstruction
if (tetris[CURRENT]->testForStop(board, horizontalChange))
horizontalChange = 0;
}
// pressed up -> rotate shape
else if (keyPressed == 'w'){
// erase current tetris position display
tetris[CURRENT]->tetrisDisplay(board, true);
tetris[CURRENT]->incrementRotation();
}
// pressed pause -> pause play
else if (keyPressed == 'p'){
gotoXY(10, COLUMNS / 2 - 13);
cout << "Press enter to continue.";
cin.sync();
cin.get();
// on return reset to previous state
clear();
board.drawBoard();
gameInformation();
displayCurrentStats(level, score, rowsToNextLevel);
tetris[NEXT]->tetrisDisplay(board, false);
}
// pressed ESC -> exit program
else if (keyPressed == 27){
return 0;
}
}
catch (const char* msg){
cerr << msg << endl;
}
}
// if the tetris stops falling
bool stopped = tetris[CURRENT]->testForStop(board, horizontalChange);
if (stopped){
// test for any full rows
int rows = board.testForFullRows();
if (rows > 0) {
// calculate score, rows, and level and update display
score = calculateScore(rows, level);
rowsToNextLevel = rowsToNextLevel - rows;
if (rowsToNextLevel < 1)
board.initializeBoard();
// redraw new state
clear();
board.drawBoard();
gameInformation();
displayCurrentStats(level, score, rowsToNextLevel);
tetris[NEXT]->tetrisDisplay(board, false);
}
else {
// test whether game is over
if (testForGameOver(tetris[CURRENT])){
gotoXY(10, 15);
cout << "GAME OVER";
gotoXY(14, 15);
cout << "Continue? (Y,N)";
int c = tolower(cin.get());
if (c == 'y') {
playAgain = true;
level = 0;
score = 0;
rowsToNextLevel = ROWS_2_NEXT_LEVEL;
}
else
return 0;
}
}
//move next tetris to starting field and replace with new next tetris
tetris[NEXT]->tetrisDisplay(board, true);
tetris[NEXT]->setPosition(CURRENT_TETRIS_ROW, CURRENT_TETRIS_COLUMN);
// delete current element from vector
tetris.erase(tetris.begin());
// display next shape (random)
setTetrisVector(tetris, rand() % NUM_SHAPES);
tetris[NEXT]->tetrisDisplay(board, false);
}
// if still falling
else {
// erase current tetris position display
tetris[CURRENT]->tetrisDisplay(board, true);
// set new position values
tetris[CURRENT]->incrementRowPosition();
if (horizontalChange == -1)
tetris[CURRENT]->decrementColumnPosition();
if (horizontalChange == 1)
tetris[CURRENT]->incrementColumnPosition();
horizontalChange = 0;
}
}
}
return 0;
}