Смекни!
smekni.com

Программа "Крестики-нолики 5 в ряд на неограниченном игровом поле" (стр. 3 из 4)

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;

}

///////////Расчет по диагонали с левого нижнего/////////