Тетрис
Чувашский государственный университет им. И.Н.Ульянова
Кафедра вычислительной техники
Курсовая работа
по объектно-ориентированному программированию
на тему
«Тетрис»
Выполнил: студент группы
ИВТ – 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_
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.
Список используемой литературы
- Б.Страуструп. Язык программирования С++, 3-е изд./Пер. с англ.- СПб.; М.; «Невский Диалект» - «Издательство БИНОМ», 1999 г. – 991с., ил.
- Т.А. Павловская. C/C++. Программирование на языке высокого уровня, Москва, 2003г.
- 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,
//{{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_
AfxGetApp()->
(HBRUSH)GetStockObject(BLACK_
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_
return TRUE;
}
};
// Создание одного и только одного экземпляра приложения
MyApp app;
//////////////////////////////
// Задание характеристик главного окна
BOOL TetrisWnd::PreCreateWindow(
{
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(
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(
dc.SelectObject(&brush);
dc.Rectangle(j*size,i*size,(j+
}
}
}
}
// Получить прямоугольник размещения формы
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]=
}
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]!
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]!
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]!
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; // Выбор цвета фигуры
}