![]() |
|
|
Курсовая работа: Программа "Крестики-нолики 5 в ряд на неограниченном игровом поле"#include "XO.h" #include "ChildView.h" #ifdef _DEBUG #define new DEBUG_NEW #endif // CChildView CChildView::CChildView() { } CChildView::~CChildView() { } BEGIN_MESSAGE_MAP(CChildView, CWnd) ON_WM_PAINT() ON_WM_LBUTTONDOWN() ON_COMMAND(ID_NEW_GAME, &CChildView::OnNewGame) ON_COMMAND(ID_X1010, &CChildView::OnX1010) ON_COMMAND(ID_X100100, &CChildView::OnX100100) ON_COMMAND(ID_X1919, &CChildView::OnX1919) ON_COMMAND(ID_X3030, &CChildView::OnX3030) ON_COMMAND(ID_X5050, &CChildView::OnX5050) ON_COMMAND(ID_STEP_H, &CChildView::OnStepH) ON_COMMAND(ID_STEP_C, &CChildView::OnStepC) ON_COMMAND(ID_LEVEL_BEG, &CChildView::OnLevelBeg) ON_COMMAND(ID_LEVEL_AMAT, &CChildView::OnLevelAmat) ON_COMMAND(ID_LEVEL_PROF, &CChildView::OnLevelProf) END_MESSAGE_MAP() BOOL CChildView::PreCreateWindow(CREATESTRUCT& cs) NULL )); //Глобальные переменные unsigned char** fields;//Игровое поле ( = 0 - ничего нет, = 1 - нолик, //= 2 - крестик, = 3 - выигравший нолик, = 4 - выигравший крестик //= 5 - последний поставленный нолик, = 6 - последний поставленный крестик) float** calc_fields;//Рассчитанное значение оценочной функции int size_x = 19;//Размер поля по x (19 - по умолчанию) int size_y = 19;//Размер поля по y (19 - по умолчанию) int old_size_x = 0;//Старый размер поля по x int old_size_y = 0;//Старый размер поля по y int attack_factor = 1; //Коэффициент агрессивности ИИ (1 - по умолчанию) int valuation_factor = 3;//Оценочный коэффициент (4 - по умолчанию) bool end_game = false;//Наступил конец игры? int last_x = 0;//Координата x последнего хода int last_y = 0;//Координата y последнего хода bool player_first_step = true;//Приоритетность хода (true - человек) int comp_level = 0;//Уровень игры компьютера (0 - эксперт) //Перерисовка окна void CChildView::OnPaint() { CPaintDC dc(this); //Выведем поле CBrush brushBgnd(RGB(0xF4, 0xA4, 0x60));//Кисть для заднего фона CPen penO(PS_SOLID,2,RGB(0x00, 0x00, 0xFF));//Перо для нолика CPen penX(PS_SOLID,2,RGB(0x00, 0x80, 0x00));//Перо для крестика CPen penWin(PS_SOLID,2,RGB(0xFF, 0x00, 0x00));//Перо для выигрышных крестика или нолика CPen penLast(PS_SOLID,2,RGB(0xFF, 0xFF, 0x00));//Перо для вывода последнего крестика или нолика CPen penBlack(PS_SOLID,1,RGB(0x00, 0x00, 0x00)); //Перо для вывода сетки dc.SelectObject(&brushBgnd); //Выбираем кисть с задним фоном for (int y=0;y<size_y;y++) { for(int x=0;x<size_x;x++) { //Выводим сетку dc.SelectObject(&penBlack); if ((x == 0) && (y == 0)) { dc.Rectangle(0,0,y*15+15,x*15+15); } else if (x == 0) { dc.Rectangle(y*15-1,0,y*15+15,x*15+15); } else if (y == 0) { dc.Rectangle(0,x*15-1,y*15+15,x*15+15); } else { dc.Rectangle(y*15-1,x*15-1,y*15+15,x*15+15); } //Устанавливаем перо для содержимого клетки switch (fields[x][y]) { case 0: continue; break; case 1: dc.SelectObject(&penO); break; case 2: dc.SelectObject(&penX); break; case 3: case 4: dc.SelectObject(&penWin); break; } //Проверяем, не текущий ли ход if ((last_x == x) && (last_y == y)) { dc.SelectObject(&penLast); } //Выводим содержимое клетки switch (fields[x][y]) { case 0: continue; break; //Выводим нолик case 1: case 3: case 5: dc.Ellipse(y*15+2,x*15+2,y*15+13,x*15+13); break; //Выодим крестик case 2: case 4: case 6: dc.MoveTo(y*15+2,x*15+2); dc.LineTo(y*15+12,x*15+12); dc.MoveTo(y*15+2,x*15+12); dc.LineTo(y*15+12,x*15+2); break; } //Возвращаем значения для последних выведенных элементов if ((fields[x][y] == 5) || (fields[x][y] == 6)) { fields[x][y] -= 2; } } } } //Нажатие на левую кнопку мыши, здесь производится основной расчет void CChildView::OnLButtonDown(UINT, CPoint xy) { //Проверка, не закончена ли игра if (!end_game) { //Ставим в массив игрового поля нолик в зависимости от координат мыши if (fields[xy.y/15][xy.x/15] == 0) { fields[xy.y/15][xy.x/15] = 1; last_x = xy.y; last_y = xy.x; } else return; //Проводим анализ, не закончена ли игра if (!end_analyze()) { //Игра не закончена //Ход компьютера ii(); //Проверяем, не закончена ли игра if (end_analyze()) { //Игра закончена, выводим сообщение и отрисовываем на экран this->OnPaint(); this->Invalidate(); this->MessageBox(CA2T("Вы проиграли"),0,0); end_game = true;//Ставим признак конца игры return; } } else { //Игра закончена, выводим сообщение и отрисовываем на экран this->OnPaint(); this->Invalidate(); this->MessageBox(CA2T("Вы выиграли"),0,0); end_game = true;//Ставим признак конца игры return; } //Перерисовка окна this->OnPaint(); this->Invalidate(); } return; } //Функция вычисления, не закончена ли игра int CChildView::end_analyze() { //Проход по всему полю (расчет ведется для каждой клетки) for (int i=0;i<size_x;i++) { for (int j=0;j<size_y;j++) { //Пропускаем пустую клетку if (fields[i][j]==0) continue; int tek = fields[i][j];//Значение текущей клетки int end;//Текущая длина ряда int u;//Доп. счетчик ///////////////////////////////////////// //Смотрим вправо от текущей клетки end = 0; for (int k = j;k<j+5;k++) { if ((k == size_y) || (fields[i][k] != tek)) { //Нет ряда из 5 break; } end++; } if (end == 5) { //Есть ряд из 5 - конец игры for (int k = j;k<j+5;k++) { fields[i][k]=tek+2; //Заполняем все клетки в ряду значением + 2 для отсветки красным цветом } return 1; } ///////////////////////////////////////// //Смотрим вниз и вправо от текущей клетки end = 0; u=i; for (int k = j;k<j+5;k++) { if ((k == size_y) || (u==size_x) || (fields[u][k] != tek)) { //Нет ряда из 5 break; } end++; u++; } if (end == 5) { //Есть ряд из 5 - конец игры u=i; for (int k = j;k<j+5;k++) { fields[u][k]=tek+2; //Заполняем все клетки в ряду значением + 2 для отсветки красным цветом u++; } return 1; } ///////////////////////////////////////// //Смотрим вниз и влево от текущей клетки end = 0; u=i; for (int k = j;k>j-5;k--) { if ((k == -1) || (u==size_x) || (fields[u][k] != tek)) { //Нет ряда из 5 break; } end++; u++; } if (end == 5) { //Есть ряд из 5 - конец игры u=i; for (int k = j;k>j-5;k--) { fields[u][k]=tek+2; //Заполняем все клетки в ряду значением + 2 для отсветки красным цветом u++; } return 1; } ///////////////////////////////////////// //Смотрим вниз от текущей клетки end = 0; for (int k = i;k<i+5;k++) { if ((k == size_x) || (fields[k][j] != tek)) { //Нет ряда из 5 break; } end++; } if (end == 5) { //Есть ряд из 5 - конец игры for (int k = i;k<i+5;k++) { fields[k][j]=tek+2; //Заполняем все клетки в ряду значением + 2 для отсветки красным цветом } return 1; } } } //Игра не окончена return 0; } //Функция расчета действий компьютера void CChildView::ii() { float max = -1;//Максимальное значение оценочной функции int cur_x = 0,cur_y = 0;//Текущие x и у int povtor_num = 0;//Количество повторов одинаковых значений оценочной функции int cur_povtor = 0;//Номер текущего повтора //Расчитываем оценочную функцию для всех клеток for (int i=0;i<size_x;i++) { for (int j=0;j<size_y;j++) { if (fields[i][j] == 0) { //Расчет оценочной функции calc_fields[i][j] = calculate(2,i,j) + calculate(1,i,j)*(float)attack_factor; //Берем в расчет уровень (для профессионала случайности нет) if (comp_level == 1)//Для любителя (небольшая случайность) { calc_fields[i][j] *= (1 + ((float)rand() / 32767)) / 2; } if (comp_level == 2)//Для новичка (максимальная случайность) { calc_fields[i][j] *= ((float)rand() / 32767); } if (calc_fields[i][j] == max) { //Еще одна клетка с максимальным значением оценочной функции povtor_num++; } if (calc_fields[i][j] > max) { //Клетка с максимальным значением оценочной функции max = calc_fields[i][j]; povtor_num = 0; cur_x = i; cur_y = j; } } } } //Проверяем, есть ли вообще свободные клетки на поле if (max == -1) { return; } //Выбираем куда сделать ход if (povtor_num > 0) { //Выбираем куда ходить случайным образом из клеток с одинаковыми значениями оценочной функции cur_povtor = rand() / (32767 / povtor_num);//Номер элемента, куда надо ходить //Ищем его по полю int buf_povtor = -1; for (int i=0;i<size_x;i++) { for (int j=0;j<size_y;j++) { if (calc_fields[i][j] == max) { buf_povtor++; if (buf_povtor == cur_povtor) //Клетка найдена { fields[i][j] = 2;//Ставим крестик last_x = i;//Запоминаем координаты последнего хода last_y = j; return; } } } } } else { //Одна клетка с максимальным знаечением fields[cur_x][cur_y] = 2;//Ставим крестик last_x = cur_x;//Запоминаем координаты последнего хода last_y = cur_y; } } //Функция расчета оценочной функции unsigned long CChildView::calculate(int id,int x,int y) { //Подсчет оценочной функции //Ставим в массиве временно значение == id fields[x][y] = id; int series_length = 0;//Текущая длина ряда unsigned long sum = 0;//Общее значение оценочной функции ///////////Расчет сверху вниз///////// //Проход по каждой клетки, которая может входить в ряд for (int i = 0;i<5;i++) { //Проверка, не вышли ли за границы поля if ((x-4+i) < 0) continue; if ((x+i) > (size_x - 1)) break; //Проход по всем возможным рядам, отстоящим от клетки не более чем на 5 for (int j=0;j<5;j++) { if ((fields[x-4+i+j][y] != id) && (fields[x-4+i+j][y] != 0)) { //Конец ряда series_length = 0; break; } if (fields[x-4+i+j][y] != 0) series_length++; //Ряд увеличивается } if (series_length == 1) series_length = 0;//Ряд из самой клетки не учитываем if (series_length == 5) series_length = 100; //Выигрышная ситуация, ставим большое значение //Плюсуем серию к общей сумме unsigned long pow_st = valuation_factor; if (series_length == 100) { if (id == 2) pow_st = 10000;//Большое значение при своем выигрыше else pow_st = 1000; //Большое значение при выигрыше соперника, но меньшее, чем при своем } else { for (int i=0;i<series_length;i++)//Возводим оценочный коэффициент в степень длины серии { pow_st*=valuation_factor; } } sum += pow_st; series_length = 0; } ///////////Расчет слева направо///////// //Проход по каждой клетки, которая может входить в ряд for (int i = 0;i<5;i++) { //Проверка, не вышли ли за границы поля if ((y-4+i) < 0) continue; if ((y+i) > (size_y - 1)) break; //Проход по всем возможным рядам, отстоящим от клетки не более чем на 5 for (int j=0;j<5;j++) { if ((fields[x][y-4+i+j] != id) && (fields[x][y-4+i+j] != 0)) { //Конец ряда series_length = 0; break; } if (fields[x][y-4+i+j] != 0) series_length++; //Ряд увеличивается } if (series_length == 1) series_length = 0; //Ряд из самой клетки не учитываем if (series_length == 5) series_length = 100; //Выигрышная ситуация, ставим большое значение //Плюсуем серию к общей сумме unsigned long pow_st = valuation_factor; if (series_length == 100) { if (id == 2) pow_st = 10000;//Большое значение при своем выигрыше else pow_st = 1000; //Большое значение при выигрыше соперника, но меньшее, чем при своем } else { for (int i=0;i<series_length;i++)//Возводим оценочный коэффициент в степень длины серии { pow_st*=valuation_factor; } } sum += pow_st; series_length = 0; } ///////////Расчет по диагонали с левого верхнего///////// //Проход по каждой клетки, которая может входить в ряд for (int i = 0;i<5;i++) { //Проверка, не вышли ли за границы поля if ((y-4+i) < 0) continue; if ((x-4+i) < 0) continue; if ((x+i) > (size_x - 1)) break; if ((y+i) > (size_y - 1)) break; //Проход по всем возможным рядам, отстоящим от клетки не более чем на 5 for (int j=0;j<5;j++) { if ((fields[x-4+i+j][y-4+i+j] != id) && (fields[x-4+i+j][y-4+i+j] != 0)) { //Конец ряда series_length = 0; break; } if (fields[x-4+i+j][y-4+i+j] != 0) series_length++; //Ряд увеличивается } if (series_length == 1) series_length = 0; //Ряд из самой клетки не учитываем if (series_length == 5) series_length = 100; //Выигрышная ситуация, ставим большое значение //Плюсуем серию к общей сумме unsigned long pow_st = valuation_factor; if (series_length == 100) { if (id == 2) pow_st = 10000;//Большое значение при своем выигрыше else pow_st = 1000; //Большое значение при выигрыше соперника, но меньшее, чем при своем } else { for (int i=0;i<series_length;i++)//Возводим оценочный коэффициент в степень длины серии { pow_st*=valuation_factor; } } sum += pow_st; series_length = 0; } ///////////Расчет по диагонали с левого нижнего///////// //Проход по каждой клетки, которая может входить в ряд for (int i = 0;i<5;i++) { //Проверка, не вышли ли за границы поля if ((y-4+i) < 0) continue; if ((x+4-i) > (size_x - 1)) continue; if ((x-i) < 0) break; if ((y+i) > (size_y - 1)) break; //Проход по всем возможным рядам, отстоящим от клетки не более чем на 5 for (int j=0;j<5;j++) { if ((fields[x+4-i-j][y-4+i+j] != id) && (fields[x+4-i-j][y-4+i+j] != 0)) { //Конец ряда series_length = 0; break; } if (fields[x+4-i-j][y-4+i+j] != 0) series_length++; //Ряд увеличивается } if (series_length == 1) series_length = 0; //Ряд из самой клетки не учитываем if (series_length == 5) series_length = 100; //Выигрышная ситуация, ставим большое значение //Плюсуем серию к общей сумме unsigned long pow_st = valuation_factor; if (series_length == 100) { if (id == 2) pow_st = 10000;//Большое значение при своем выигрыше else pow_st = 1000; //Большое значение при выигрыше соперника, но меньшее, чем при своем } else { for (int i=0;i<series_length;i++)//Возводим оценочный коэффициент в степень длины серии { pow_st*=valuation_factor; } } sum += pow_st; series_length = 0; } //Возвращаем исходное значение fields[x][y] = 0; return sum; } //В меню нажата кнопка Новая игра void CChildView::OnNewGame() { new_game(); //Функция начала новой игры this->OnPaint(); this->Invalidate(); } //Выбрано поле 10x10 void CChildView::OnX1010() { //Ставим галочку в меню switch (size_x) { case 19: set_chеcked_menu(ID_X1919,ID_X1010); break; case 30: set_chеcked_menu(ID_X3030,ID_X1010); break; case 50: set_chеcked_menu(ID_X5050,ID_X1010); break; case 100: set_chеcked_menu(ID_X100100,ID_X1010); break; } size_x = 10; size_y = 10; //Стартуем новую игру new_game(); //Устанавливаем новые размеры окна resize_window(); } //Выбрано поле 19x19 void CChildView::OnX1919() { //Ставим галочку в меню switch (size_x) { case 10: set_chеcked_menu(ID_X1010,ID_X1919); break; case 30: set_chеcked_menu(ID_X3030,ID_X1919); break; case 50: set_chеcked_menu(ID_X5050,ID_X1919); break; case 100: set_chеcked_menu(ID_X100100,ID_X1919); break; } size_x = 19; size_y = 19; //Стартуем новую игру new_game(); //Устанавливаем новые размеры окна resize_window(); } //Выбрано поле 19x19 void CChildView::OnX3030() { //Ставим галочку в меню switch (size_x) { case 10: set_chеcked_menu(ID_X1010,ID_X3030); break; case 19: set_chеcked_menu(ID_X1919,ID_X3030); break; case 50: set_chеcked_menu(ID_X5050,ID_X3030); break; case 100: set_chеcked_menu(ID_X100100,ID_X3030); break; } size_x = 30; size_y = 30; //Стартуем новую игру new_game(); //Устанавливаем новые размеры окна resize_window(); } //Выбрано поле 50x50 void CChildView::OnX5050() { //Ставим галочку в меню switch (size_x) { case 10: set_chеcked_menu(ID_X1010,ID_X5050); break; case 19: set_chеcked_menu(ID_X1919,ID_X5050); break; case 30: set_chеcked_menu(ID_X3030,ID_X5050); break; case 100: set_chеcked_menu(ID_X100100,ID_X5050); break; } size_x = 50; size_y = 50; //Стартуем новую игру new_game(); //Устанавливаем новые размеры окна resize_window(); } //Выбрано поле 100x100 void CChildView::OnX100100() { //Ставим галочку в меню switch (size_x) { case 10: set_chеcked_menu(ID_X1010,ID_X100100); break; case 19: set_chеcked_menu(ID_X1919,ID_X100100); break; case 30: set_chеcked_menu(ID_X3030,ID_X100100); break; case 50: set_chеcked_menu(ID_X5050,ID_X100100); break; } size_x = 100; size_y = 100; //Стартуем новую игру new_game(); //Устанавливаем новые размеры окна resize_window(); } //Функция начала новой игры void CChildView::new_game() { if ((old_size_x != 0) || (old_size_y != 0)) { //Освобождаем память динамических массивов try { for (int i=0;i<old_size_x;i++) { delete fields[i]; delete calc_fields[i]; } delete fields; delete calc_fields; } catch(...) { this->MessageBox(CA2T("Ошибка работы с памятью"),0,0); this->CloseWindow(); } } //Присваиваем старым значениям новые old_size_x = size_x; old_size_y = size_y; //Выделяем память под массивы try { fields = new unsigned char*[size_x]; calc_fields = new float*[size_x]; for(int i=0;i<size_x;i++) { fields[i] = new unsigned char[size_y]; calc_fields[i] = new float[size_y]; } } catch(...) { this->MessageBox(CA2T("Ошибка выделения памяти"),0,0); this->CloseWindow(); } //Обнуляем массивы for (int i=0;i<size_x;i++) for (int j=0;j<size_y;j++) { fields[i][j] = 0; } //Сбрасываем флаг начала игры end_game = false; //Проверяем, не должен ли компьютер ходить первым if (!player_first_step) ii(); } //Установка новых размеров окна void CChildView::resize_window() { //Устанавливаем размеры окна в соответствии с размером поля RECT Rect;//Размеры внутренней области RECT WindowRect;//Размеры окна this->GetWindowRect(&Rect); this->GetParent()->GetWindowRect(&WindowRect); this->GetParent()->MoveWindow(WindowRect.left,WindowRect.top, size_y*15 + 4 + (WindowRect.right - Rect.right) + (Rect.left - WindowRect.left), size_x*15 + 4 + (WindowRect.bottom - Rect.bottom) + (Rect.top - WindowRect.top),1); } //Установка галочки в меню void CChildView::set_chеcked_menu(unsigned int old_id,unsigned int new_id) { //Создаем структуру элемента меню MENUITEMINFO menuItemInfo; menuItemInfo.cbSize = sizeof(MENUITEMINFO); menuItemInfo.fMask = MIIM_STATE;//Работа с состоянием элемента //Получаем указатель на меню CMenu *pMenu=this->GetParent()->GetMenu(); //Ставим галочку в новом элементе menuItemInfo.fState = MFS_CHECKED; pMenu->SetMenuItemInfoW(new_id,&menuItemInfo,0); //Снимаем галочку в старом элементе menuItemInfo.fState = MFS_UNCHECKED; pMenu->SetMenuItemInfoW(old_id,&menuItemInfo,0); return; } //Выбран первый ход человека void CChildView::OnStepH() { //Ставим галочку в меню if (player_first_step == false) set_chеcked_menu(ID_STEP_C,ID_STEP_H); player_first_step = true; //Старт новой игры new_game(); //Перерисовка окна this->OnPaint(); this->Invalidate(); } //Выбран первый ход компьютера void CChildView::OnStepC() { //Ставим галочку в меню if (player_first_step == true) set_chеcked_menu(ID_STEP_H,ID_STEP_C); player_first_step = false; //Старт новой игры new_game(); //Перерисовка окна this->OnPaint(); this->Invalidate(); } //Выбран уровень компьютера - новичок void CChildView::OnLevelBeg() { //Ставим галочку в меню if (comp_level == 0) { set_chеcked_menu(ID_LEVEL_PROF,ID_LEVEL_BEG); } else if (comp_level == 1) { set_chеcked_menu(ID_LEVEL_AMAT,ID_LEVEL_BEG); } comp_level = 2; attack_factor = 1; //Старт новой игры new_game(); //Перерисовка окна this->OnPaint(); this->Invalidate(); } //Выбран уровень компьютера - любитель void CChildView::OnLevelAmat() { //Ставим галочку в меню if (comp_level == 2) { set_chеcked_menu(ID_LEVEL_BEG,ID_LEVEL_AMAT); } else if (comp_level == 0) { set_chеcked_menu(ID_LEVEL_PROF,ID_LEVEL_AMAT); } comp_level = 1; attack_factor = 10; //Старт новой игры new_game(); //Перерисовка окна this->OnPaint(); this->Invalidate(); } //Выбран уровень компьютера - профессионал void CChildView::OnLevelProf() { //Ставим галочку в меню if (comp_level == 2) { set_chеcked_menu(ID_LEVEL_BEG,ID_LEVEL_PROF); } else if (comp_level == 1) { set_chеcked_menu(ID_LEVEL_AMAT,ID_LEVEL_PROF); } comp_level = 0; attack_factor = 1; //Старт новой игры new_game(); //Перерисовка окна this->OnPaint(); this->Invalidate(); 7. Примеры выполнения программы Примеры выполнения программы приведены на рисунках 2, 3, 4, 5. Рисунок 2 – Вид окна программе при запуске. Рисунок 3 – Игровая ситуация при игре на поле 30x30 на сложности любитель. Рисунок 4 – Выигрыш компьютера при игре на сложности профессионал. Рисунок 5 – Победа игрока при игре на сложности любитель на поле 30x30. В ходе выполнения курсовой работы был разработан алгоритм действий компьютерного игрока при игре в крестики-нолики пять в ряд на бесконечном поле. В ходе выполнения работы удалось добиться достаточно сильного уровня игры на сложности профессионал. Разработанный алгоритм был реализован на языке C++ на платформе Microsoft Visual Studio 2008. Список литературы 1. Арчер Т., Уайтчепел Э. Visual C++ .NET. Библия пользователя — Вильямс, 2007. 2. Либерти Д., Джонс Б. Освой самостоятельно C++ за 21 день. — Вильямс, 2007. 3. Материалы сайта http://www.firststeps.ru/mfc/steps. 4. Подбельский В.В. Язык C++. — М.:"Финансы и статистика", 2003. |
Страницы: 1, 2
![]() |
||
НОВОСТИ | ![]() |
![]() |
||
ВХОД | ![]() |
|
Рефераты бесплатно, реферат бесплатно, курсовые работы, реферат, доклады, рефераты, рефераты скачать, рефераты на тему, сочинения, курсовые, дипломы, научные работы и многое другое. |
||
При использовании материалов - ссылка на сайт обязательна. |