Тетрис

Чувашский государственный университет  им. И.Н.Ульянова

Кафедра вычислительной техники

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Курсовая работа

по объектно-ориентированному программированию

на тему

«Тетрис»

 

 

 

 

 

 

 

 

Выполнил: студент группы

ИВТ – 41-07

Руденко К.С.

 

Проверил: Обломов И.А.

 

 

 

 

 

 

 

 

Чебоксары 2010 год.

 

Содержание

Оглавление

Содержание 2

Аннотация 3

Введение 4

Постановка задачи 5

Метод решения задачи 5

Алгоритм работы программы 6

Алгоритм процедуры обработки сообщения от таймера 7

Проектирование иерархии классов 8

Пример работы программы 11

Выводы по работе 11

Список используемой литературы 12

Приложение 13

 

 

Аннотация

В данном документе  описывается программа, написанная в соответствии с постановкой  задачи на курсовое проектирование по теме "Тетрис" по дисциплине "Объектно-ориентированное  программирование". Данная программа  осуществляет вывод на экран случайным  образом падение различных фигур. Входными данными является управление падающими фигурами.

Назначение программы - развлечение играющих, совершенствование  их координации и логического  мышления. Программа может применяться  в качестве игровой на разных типах  персональных компьютеров.

Требованием к  аппаратному и программному обеспечению  является наличие персонального  компьютера, видеоадаптера класса VGA и наличием достаточного количества оперативной памяти (достаточно 1.5Мб). На компьютере должна быть установлена  операционная система Windows 2000 или более поздняя версия.

 

 

Введение

По окончанию  курса “Объектно-ориентированное  программирование” стоит задача в закреплении навыков объектном  программировании на ЭВМ. Она состоит  в написании программы.

В современном  мире обработки информации очень  удобно использование диалогового  режима работы, а также целесообразно  использовать компилятор который более  экономно распределяет память и ресурсы  центрального процессора. Поэтому, в  качестве среды разработки программы  была выбрана Microsoft Visual Studio – программы, управляемая событиями (события от клавиатуры и др.).

 

Постановка  задачи

Требуется написать программу – тетрис, осуществляющую падение фигур и наложение  их друг на друга, возможность поворота фигур, а также подсчет заполненных  линий.

Метод решения  задачи

Фигуры представляются в виде объектов, состоящие из 4 квадратов  и хранящиеся в матрице размерностью 4x4. Соответственно существует класс, описывающий  каждую фигуру. В программе нужно контролировать движения фигур, что осуществляется с помощью соответствующих классов. Для работы с графикой, можно использовать следующие библиотеки: OpenGL, DirectX и GDI. OpenGL позволяет работать как с двумерной, так и трехмерной графикой, что дает доступ к большому числу аппаратных возможностей видеокарты. DirectX аналогичен по возможностям с OpenGL. GDI (Graphics Device Interface) - интерфейс Windows для представления графических объектов и передачи их на устройства отображения, такие как мониторы и принтеры.

Поскольку в  нашем случае работа с трехмерной графикой не нужна, то не имеет смысла использовать OpenGL и DirectX. Для нашего двухмерного приложения вполне подойдет интерфейс GDI.

Для облегчения программирования под WinAPI было создано  некоторое количество объектно-ориентированных  надстроек для него. В числе  самых распространенных - Microsoft Foundation Class Library (MFC) от Microsoft (используемая в MS Visual Studio). Для облегчения работы с функциями и структурами GDI в MFC создан набор классов, являющихся обертками для WinAPI структур и дескрипторов. Собственно, эта настройка и будет применена при разработке приложения. 

Алгоритм  работы программы

Так как создается  оконное приложение, то работа с  ним идет через сообщения, поэтому  работа программы – это бесконечный  цикл с проверкой новых сообщений  и их обработкой.

 

Начало

Обработать сообщение

true

Конец

Определение класса главного окна

Инициализация параметров класса окна

Создание экземпляра окна

       true

Есть сообщения в очереди

true

false


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Алгоритм  процедуры обработки сообщения  от таймера

Таймер в программе  задает скорость падения фигуры вниз. Также производится проверка возможности  движения фигуры вниз.

Начало

Конец

Движение вниз

Добавление квадратов фигуры к  объекту пола

Достигли пола ?

false

 

Отрисовка всего поля

true

Уничтожение заполненных строк

Создание новой фигуры

Отрисовка всего игрового поля

 

 

 

Проектирование  иерархии классов

В процессе разработки программы, помимо собственных классов, наследовались классы, определенные в файле afxwin.h, который необходим при использовании MFC, т.к.  определяет некоторые расширения MFC.

Конкретно, наследовались  следующие классы: класс, предназначенный для создания приложения, и класс, предназначенный для создания окна. Другими словами, для создания минимальной программы необходимо породить один класс от CWinApp, а другой - от CFrameWnd. Эти два класса обязательны для любой программы.

CWinApp это один из фундаментальных классов, поскольку предназначен для создания самого приложения. В каждой программе имеется один и только один объект этого класса. Как только он будет создан, приложение начнет выполняться.

 

// Класс приложения. Должен существовать только один  экземпляр этого класса.

// Функция InitInstance() вызывается при запуске приложения.

class MyApp : public CWinApp

{public:

virtual BOOL InitInstance()

{

// Создание главного  окна приложения и его отображение.

// Член MyApp::m_pMainWnd - это  указатель на объект главного  окна.

m_pMainWnd = new TetrisWnd;

m_pMainWnd->ShowWindow(SW_SHOWNORMAL);

return TRUE;

}

};

// Создание одного  и только одного экземпляра  приложения

MyApp app;

 

Класс CMenu содержит большинство функциональных возможностей меню.

 

class CMenu : public CObject

{

DECLARE_DYNCREATE(CMenu)

public:

 

// Constructors

CMenu();

 

BOOL CreateMenu();

BOOL CreatePopupMenu();

BOOL LoadMenu(LPCTSTR lpszResourceName);

BOOL LoadMenu(UINT nIDResource);

BOOL LoadMenuIndirect(const void* lpMenuTemplate);

BOOL DestroyMenu();

}

 

Собственные разработанные  классы:

 

Класс Shape необходим для построения форм фигур и их поворота.

 

//--------------------------------

// Класс формы  фигуры

//--------------------------------

class Shape

{

public:

bool  cells[4][4]; // Матрица 4x4, которая содержит форму

ShapeType curType; // Текущий тип формы

 

void NewShape( ShapeType ); // Создание новой формы

void Rotate(); // Поворот формы

void ClearShape( bool c[4][4] ); // Очистить матрицу формы

};

 

Класс TetrisWnd - класс главного окна приложения. Данный класс отвечает за создание главного окна приложения, его перерисовку, а также содержит методы контроля положения фигур в пределах окна.

 

//-----------------------------------

// Класс главного  окна тетриса

//-----------------------------------

class TetrisWnd : public CFrameWnd

{

public:

TetrisWnd();

void NewShape();  // Создание новой формы наверху

COLORREF ShapeColor(int color); // Зарезервированные цвета фигур

bool CheckLeft();  // Проверка возможности переместиться влево

bool CheckRight();  // Проверка возможности переместиться вправо

void CheckForLine();  // Проверка на завершенную (заполненную) линию

bool IsAtBottom();  // Проверка возможности двигать фигуру вниз

void ClearStock();  // Очистить игровое поле

CRect GetRect();  // Получить прямоугольник размещения формы

int color;   // Текущий цвет фигуры

 

private:

CMenu menu;   // Создание экземпляра класса Меню

Shape shape;   // Создание экземпляра класса Формы

short stock[20][10]; // Размеры главного окна

int curY;   // Текущая позиция по Y

int curX;   // Текущая позиция по X

bool bGame;   // True - есть место для падение фигур. False - нет

int score;   // Подсчет счета

int speed;   // Скорость падения фигур

 

protected:

//{{AFX_VIRTUAL(TetrisWnd)

protected:

 // Создание окна

 // 1 - Создать окно

 // 0 - В процессе создания окна возникла ошибка

virtual BOOL PreCreateWindow(CREATESTRUCT& cs);

//}}AFX_VIRTUAL

 

protected:

//{{AFX_MSG(TetrisWnd)

afx_msg void OnPaint();  // Перерисовка окна 

afx_msg void OnTimer(UINT nIDEvent); // Таймер (для задание скорости падения фигур)

afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); // Обработчик нажатия клавиши

afx_msg void OnGameNew(); // Новая игра

afx_msg void OnGameExit(); // Выход из игры

afx_msg void OnClose();  // Завершение процесса

afx_msg void OnHelpAbout(); // Вызок справки

//}}AFX_MSG

DECLARE_MESSAGE_MAP()

};

 

Пример  работы программы

На ниже представленных рисунках можно увидеть пользовательский интерфейс разработанной программы. Он довольно таки прост для понимания  и использования.

 


 

Выводы  по работе

В процессе выполнения курсовой работы закреплены знания языка С++, изучена концепция ООП, получены навыки разработки программ под управлением ОС Windows.

 

Список  используемой литературы

 

  1. Б.Страуструп. Язык программирования С++, 3-е изд./Пер. с англ.- СПб.; М.; «Невский Диалект» - «Издательство БИНОМ», 1999 г. – 991с., ил.
  2. Т.А. Павловская. C/C++. Программирование на языке высокого уровня, Москва, 2003г.
  3. MSDN

 

Приложение

#include<afxwin.h>

#include<stdio.h>

#include<time.h>

#include"resource.h"

 

enum ShapeType { line, square, rightL, leftL, pyramide, leftZ, rightZ}; // Типы форм

#define size 20 // Размер квадрата формы

 

//--------------------------------

// Класс формы  фигуры

//--------------------------------

class Shape

{

public:

bool  cells[4][4]; // Матрица 4x4, которая содержит форму

ShapeType curType; // Текущий тип формы

 

void NewShape( ShapeType );  // Создание новой формы

void Rotate();     // Поворот формы

void ClearShape( bool c[4][4] ); // Очистить матрицу формы

};

 

// Очистить матрицу  формы

void Shape::ClearShape( bool c[4][4] )

{

for( int i=0; i<4; i++)

for( int j=0; j<4; j++)

c[i][j]=false;

}

 

// Создание новой  формы 

void Shape::NewShape( ShapeType type )

{

ClearShape(cells); // Очистить матрицу формы

int i;

curType=type;

 

switch(type)

{       // 1,1,1,1

case line: for(i=0; i<4; i++)   // 0,0,0,0

cells[0][i]=true;  // 0,0,0,0

break;     // 0,0,0,0

 

case square: for(i=0; i<2; i++)   // 1,1,0,0

for(int j=0; j<2; j++) // 1,1,0,0

cells[i][j]=true; // 0,0,0,0

break;     // 0,0,0,0

 

case leftL: for(i=0; i<3; i++)   // 1,1,1,0

cells[0][i]=true;  // 0,0,1,0

cells[1][2]=true;   // 0,0,0,0

break;     // 0,0,0,0

 

case rightL: for(i=0; i<3; i++)   // 1,1,1,0

cells[0][i]=true;  // 1,0,0,0

cells[1][0]=true;   // 0,0,0,0

break;     // 0,0,0,0

 

case pyramide: for(i=0; i<3; i++)  // 0,1,0,0

cells[1][i]=true;  // 1,1,1,0

cells[0][1]=true;   // 0,0,0,0

break;     // 0,0,0,0

 

case leftZ: cells[0][0]=true; cells[1][0]=true;  // 1,0,0,0

cells[1][1]=true; cells[2][1]=true; // 1,1,0,0

break;      // 0,1,0,0

// 0,0,0,0

 

case rightZ: cells[0][1]=true; cells[1][0]=true;   // 0,1,0,0

cells[1][1]=true; cells[2][0]=true; // 1,1,0,0

break;      // 1,0,0,0

// 0,0,0,0

}

}

 

// Поворот формы

void Shape::Rotate()

{

switch(curType)

{

case line:

{

int k;

if(cells[0][0]==true)

{

ClearShape(cells);

for(k=0; k<4; k++)

cells[k][1]=true;

}

else

{

ClearShape(cells);

for(k=0; k<4; k++)

cells[0][k]=true;

}

}

case square: return;

}

 

bool tempShape[4][4];

ClearShape(tempShape);

 

for(int j=3-1 , c=0; j>=0 ; j-- , c++)

for(int i=0; i<3; i++)

tempShape[c][i]=cells[i][j];

 

ClearShape(cells);

 

for(int f=0; f<3; f++)

for(int d=0; d<3; d++)

cells[f][d]=tempShape[f][d];

}

 

//-----------------------------------

// Класс главного окна тетриса

//-----------------------------------

 

class TetrisWnd : public CFrameWnd

{

public:

TetrisWnd();

void NewShape();  // Создание новой формы наверху

COLORREF ShapeColor(int color); // Зарезервированные цвета фигур

bool CheckLeft();  // Проверка возможности переместиться влево

bool CheckRight();  // Проверка возможности переместиться вправо

void CheckForLine(); // Проверка на завершенную (заполненную) линию

bool IsAtBottom();  // Проверка возможности двигать фигуру вниз

void ClearStock();  // Очистить игровое поле

CRect GetRect();  // Получить прямоугольник размещения формы

int color;    // Текущий цвет фигуры

 

private:

CMenu menu;   // Создание экземпляра класса Меню

Shape shape;   // Создание экземпляра класса Формы

short stock[20][10]; // Размеры главного окна

int curY;    // Текущая позиция по Y

int curX;    // Текущая позиция по X

bool bGame;   // True - есть место для падение фигур. False - нет

int score;    // Подсчет счета

int speed;    // Скорость падения фигур

 

protected:

//{{AFX_VIRTUAL(TetrisWnd)

protected:

 // Создание окна

 // 1 - Создать окно

 // 0 - В процессе создания окна возникла ошибка

virtual BOOL PreCreateWindow(CREATESTRUCT& cs);

//}}AFX_VIRTUAL

 

protected:

//{{AFX_MSG(TetrisWnd)

afx_msg void OnPaint();  // Перерисовка окна 

afx_msg void OnTimer(UINT nIDEvent); // Таймер (для задание скорости падения фигур)

afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); // Обработчик нажатия клавиши

afx_msg void OnGameNew(); // Новая игра

afx_msg void OnGameExit(); // Выход из игры

afx_msg void OnClose();  // Завершение процесса

afx_msg void OnHelpAbout(); // Вызок справки

//}}AFX_MSG

 

DECLARE_MESSAGE_MAP();

};

 

BEGIN_MESSAGE_MAP(TetrisWnd,CFrameWnd)

//{{AFX_MSG_MAP(TetrisWnd)

ON_WM_PAINT()  // Сообщение для перерисовки окна  

ON_WM_TIMER()  // Реагировать на таймер

ON_WM_KEYDOWN()  // Реакция на нажатие клавиши

ON_COMMAND(ID_GAME_NEW, OnGameNew)  // Связываем ID код с обработчиком

ON_COMMAND(ID_GAME_EXIT, OnGameExit) // Связываем ID код с обработчиком

ON_WM_CLOSE()  // Завершение процесса

ON_COMMAND(ID_HELP_ABOUT, OnHelpAbout)  // Связываем ID код с обработчиком

//}}AFX_MSG_MAP

END_MESSAGE_MAP()

 

TetrisWnd::TetrisWnd()

{

CString s=AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW , // Перерисовка окна при изменении по height и width

AfxGetApp()->LoadStandardCursor(IDC_ARROW), // Загрузка стандартного указателя мыши

(HBRUSH)GetStockObject(BLACK_BRUSH), // Цвет фона окна

AfxGetApp()->LoadIcon(IDI_IC));  // Идентификатор ресурса для значка приложения

Create(s,"Tetris");    // Cтрока, отображаемая в заголовке окна.

menu.LoadMenu(IDR_MENU);  // Идентификатор ресурса для меню

SetMenu(&menu);  // Устанавливаем и перерисовываем меню окна

speed=300;   // Скорость падения фигуры

srand(time(0));  // При команде rand генерировать разные числа (для фигур)

ClearStock();  // Очистить игровое поле

}

 

/////////////////////////////////////////////////////////////////

// Класс приложения. Должен существовать только один  экземпляр этого класса.

// Функция InitInstance() вызывается при запуске приложения.

class MyApp : public CWinApp

{public:

virtual BOOL InitInstance()

{

// Создание главного  окна приложения и его отображение.

// Член MyApp::m_pMainWnd - это  указатель на объект главного  окна.

m_pMainWnd = new TetrisWnd;

m_pMainWnd->ShowWindow(SW_SHOWNORMAL);

return TRUE;

}

};

// Создание одного  и только одного экземпляра  приложения

MyApp app;

 

//////////////////////////////////////////////////////////////////

// Задание характеристик  главного окна

BOOL TetrisWnd::PreCreateWindow(CREATESTRUCT& cs)

{

cs.style &= ~WS_MAXIMIZEBOX; // Создает окно, которое НЕ имеет кнопку развертывания окна

cs.style &= ~WS_THICKFRAME;  // Создает окно, которое НЕ имеет рамку изменения размеров

cs.cx = 210;     // Размер окна по X

cs.cy = 450;     // Размер окна по Y

return CFrameWnd::PreCreateWindow(cs); // Применяем характеристики окна перед его созданием

}

 

// Перерисовка окна 

void TetrisWnd::OnPaint()

{

CPaintDC dc(this);  

int i,j;

 

CBrush brush;

brush.CreateSolidBrush(ShapeColor(color));

dc.SelectObject(&brush);

// Заполнение матрицы  форм (фигур) цветом

for(i=0; i<4; i++)

{

for(j=0; j<4; j++)

{

if(shape.cells[i][j]==true)

dc.Rectangle(curX+j*size, curY+i*size, curX+(j+1)*size, curY+(i+1)*size);

}

}

// Заполнение игрового  поля фигурами

for(i=0; i<20; i++)

{

for(j=0; j<10; j++)

{

if(stock[i][j]!=-1)

{

brush.DeleteObject();

brush.CreateSolidBrush(ShapeColor(stock[i][j]));

dc.SelectObject(&brush);

dc.Rectangle(j*size,i*size,(j+1)*size,(i+1)*size);

}

}

}

}

 

// Получить прямоугольник  размещения формы

CRect TetrisWnd::GetRect()

{

CRect r;

r.left=curX-size;

r.right=curX+size*5;

r.top=curY-size;

r.bottom=curY+size*5;

return r;

}

 

// Таймер

void TetrisWnd::OnTimer(UINT nIDEvent)

{ // Перемещение фигуры вниз

if(nIDEvent==1 && bGame==true)

{

if(IsAtBottom()) // Проверка возможности движения вниз

{

for(int i=0; i<4; i++)

for(int j=0; j<4; j++)

{

if(shape.cells[i][j]==true)

stock[curY/20+i][curX/20+j]=color;

}

NewShape();

if(IsAtBottom())

{

bGame=false; 

}

InvalidateRect(GetRect());  // Включает указанный прямоугольник в область обновления окна

CheckForLine(); // Проверка на завершенную (заполненную) линию

return;

}

curY+=20;

InvalidateRect(GetRect());  // Включает указанный прямоугольник в область обновления окна

else if(bGame==false)

{

KillTimer(1);

CString s;

s.Format("%d",score);  // Вывести результат

MessageBox("Вы проиграли !\rВаш результат : "+s);

}

 

CFrameWnd::OnTimer(nIDEvent);

}

 

// Обработчик нажатия  клавиши

void TetrisWnd::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)

{

switch(nChar)

{

case VK_UP:   shape.Rotate(); break; // Повернуть фигуру

case VK_LEFT:  if(CheckLeft()) return; curX-=20; break; // Переместить влево

case VK_RIGHT:  if(CheckRight()) return; curX+=20; break; // Переместить вправо

case VK_DOWN:  KillTimer(1); SetTimer(1,20,NULL); break; // Опустить вниз

}

InvalidateRect(GetRect());  // Включает указанный прямоугольник в область обновления окна

 

CFrameWnd::OnKeyDown(nChar, nRepCnt, nFlags);

}

 

// Новая игра

void TetrisWnd::OnGameNew()

{

ClearStock(); // Очистить игровое поле 

score=0;  // Подсчет счета

bGame=true;  // True - есть место для падение фигур. False - нет

NewShape();  // Создание новой формы наверху

Invalidate(); // Отрисовать компонент

}

 

// Очистить игровое  поле 

void TetrisWnd::ClearStock()

{

for(int i=0; i<20; i++)

for(int j=0; j<10; j++)

stock[i][j]=-1;

}

 

// Проверка возможности  двигать фигуру вниз

bool TetrisWnd::IsAtBottom()

{

for(int i=0; i<4; i++)

{

for(int j=0; j<4; j++)

{

if(shape.cells[i][j]==true)

{

if((curY/20+i)>=19 || stock[curY/20+i+1][curX/20+j]!=-1)

return true;

}

}

}

return false;

}

 

// Проверка на  завершенную (заполненную) линию

void TetrisWnd::CheckForLine()

{

int i,j,k,c=0;

bool flag;

 

for(i=0; i<20; i++)

{

flag=true;

 

for(j=0; j<10; j++)

flag &= (stock[i][j]!=-1);

 

if(flag==true && j!=0)

{

c++;

score++;

for(k=i; k>0; k--)

{

for(j=0; j<10; j++)

{

stock[k][j]=stock[k-1][j];

}

}

}

}

for(k=0; k<c; k++)

for(j=0; j<10; j++)

stock[k][j]=-1;

 

Invalidate(); // Отрисовать компонент

}

 

// Проверка возможности  переместиться вправо

bool TetrisWnd::CheckRight()

{

for(int i=0; i<4; i++)

{

for(int j=0; j<4; j++)

{

if(shape.cells[i][j]==true)

{

if((curX/20+j)>=9 || stock[curY/20+i][curX/20+j+1]!=-1)

return true;

}

}

}

return false;

}

 

// Проверка возможности  переместиться влево

bool TetrisWnd::CheckLeft()

{

for(int i=0; i<4; i++)

{

for(int j=0; j<4; j++)

{

if(shape.cells[i][j]==true)

{

if((curX/20+j)<=0 || stock[curY/20+i][curX/20+j-1]!=-1)

return true;

}

}

}

return false;

}

 

////////////////////////////////////////////////////////////////////////////////

// Команды главного меню

////////////////////////////////////////////////////////////////////////////////

void TetrisWnd::OnGameExit()

{

OnClose();  // Выход из игры

}

 

void TetrisWnd::OnClose()

{

KillTimer(1); // Уничтожаем событие таймеpа

CFrameWnd::OnClose(); // Завершение процесса

}

 

void TetrisWnd::OnHelpAbout()

{

CDialog dlg(IDD_ABOUT);

dlg.DoModal(); // Вызов справки

}

/////////////////////////////////////////////////////////////////////////////////

// Зарезервированные цвета фигур

COLORREF TetrisWnd::ShapeColor(int color)

{

switch(color)

{

case 0: return RGB(255,0,0); // Красный

case 1: return RGB(0,255,0); // Зеленый

case 2: return RGB(0,0,255); // Синий

case 3: return RGB(255,0,255); // Пурпурный

case 4: return RGB(0,255,255); // Голубой

case 5: return RGB(255,255,0); // Желтый

}

return RGB(255,255,255);

}

 

// Создание новой формы вверху окна

void TetrisWnd::NewShape()

{

KillTimer(1);

SetTimer(1,speed,NULL);

curY=0;  // Координаты положения новой

curX=60; // созданной фигуры

ShapeType t=ShapeType(rand()%7); // Случайная генерация формы фигуры

shape.NewShape(t);

color=rand()%6;  // Выбор цвета фигуры

}