Странные шахматы как тестовое задание

от автора

Добрый вечер хаброжители!

Сегодня я предоставлю вашему вниманию небольшую статью не связанную с моими предыдущими статьями. Где-то более года назад мне пришлось делать одно тестовое задание чтобы устроится на работу. Выполнил я его в срок, но конкуренция была большая и скорее всего взяли человека который сделал его с помощью рекомендованных технологий и красочнее.

Суть задачи, есть доска 8 на 8 клеток. У игрока есть 9 шашек они расположены в углу доски в квадрате 3 на 3, у противника тоже столько же шашек и они расположены симметрично по диагонали в другом углу в квадрате 3 на 3. Каждый игрок ходит по очереди, нужно дойти шашками на места изначального положения соперника через всю доску, кто первый дошел тот и победил. Ходить можно только на пустые клетки и только вверх, вниз, влево и вправо(по диагонали нельзя!).

Управление я добавил мышью, а играть против компьютерного алгоритма. Черные — человек, белые — ИИ.

Помимо всего прочего изначально необходимо отрисовать доску, шашки и делать манипуляции с полем.

Немного кода для наглядности:

Game::Game() { 	run = true;//флаг признак нажатия кнопки выхода F5 	Matrix = new int* [8];//Поле 64 ячейки - значения 0 - для пустой ячейки, для игрока каждая пешка-шашка от 1 до 9, для компьютера значения в матрице от 10 до 18 	for (int i = 0; i < 8; i++) 		Matrix[i] = new int[8]; 	//Квадраты координат нужны чтобы программа знала какие ячейки над указателем мыши, 64 квадрата 	QuadCoorXleft = new int* [8];//каждой ячейки матрицы Matrix соответстует квадрат координат для мыши xleft означает левую координату x 	QuadCoorXright = new int* [8];//xright - правая x 	QuadCoorYdown = new int* [8];//верхняя y координата 	QuadCoorYup = new int* [8];//нижняя y координата 	for (int i = 0; i < 8; i++) 	{ 		QuadCoorXleft[i] = new int[8]; 		QuadCoorXright[i] = new int[8]; 		QuadCoorYdown[i] = new int[8]; 		QuadCoorYup[i] = new int[8]; 	} 	//Координаты пешек для отрисовки 	ChessX = new double[18];//X 	ChessY = new double[18];//Y 	//Выделяемая пешка ее координаты и значения 	ActiveX = -1;//X 	ActiveY = -1;//Y 	Active = -1;//Value 	firstplayer = true;//флаг того что можете игрок 1й ходить 	secondplayer = false;//флаг того что можете игрок 2й ходить 	ai = new bool[18];//ячейки флаги того что пешка на финишной позиции 	chessai tmp; 	for (int i = 0; i < 18; i++) 	{ 		ai[i] = false; 		if (i > 8) 		{ 			tmp.ai = ai[i]; 			tmp.value = i+1; 			Ai.push_back(tmp);//Вектор с флагами финиша каждой пешки для искуственного интеллекта 		} 	} 	aicountfirstrow = 0;//счетчик кол-ва пешек ИИ(искуственного интеллекта) на верхней строчке(0-я) 	aicountsecondrow = 0;//счетчик кол-ва пешек ИИ на предверхней строчке(1-я) 	aicountthirdrow = 0;//счетчик кол-ва пешек ИИ на предпредверхней строчке(2-я) }

Для отрисовки и захвата мыши используется библиотеки OpenGL и SDL2.

void Draw_Circle() { 	//Отрисовка круга(пешек-шахмат) черного 	for (int i = 0; i <= 50; i++) { 		float a = (float)i / 50.0f * 3.1415f * 2.0f; 		glVertex2f(cos(a), sin(a)); 	} } void Draw_Circle_Fill() { 	//Отрисовка круга(пешек-шахмат) белого 	for (int i = 0; i <= 50; i++) { 		float a = (float)i / 50.0f * 3.1415f * 2.0f; 		glVertex2f(0.0, 0.0); 		glVertex2f(cos(a), sin(a)); 	} }

Самое удобное что можно использовать glTranslatef чтобы заполнить доску шашками с перемещениями

... for (int i = 0; i < 9; i++) 		{ 			glPushMatrix(); 			glTranslatef(ChessX[i], ChessY[i], 0); 			glScalef(0.05, 0.05, 1); 			glBegin(GL_LINE_LOOP); 			Draw_Circle(); 			glEnd(); 			glPopMatrix(); 		} 		//Рисуем белые пешки ИИ 		for (int i = 9; i < 18; i++) 		{ 			glPushMatrix(); 			glTranslatef(ChessX[i], ChessY[i], 0); 			glScalef(0.05, 0.05, 1); 			glBegin(GL_LINES); 			Draw_Circle_Fill(); 			glEnd(); 			glPopMatrix(); 		}  ...

Ходы игрока:

void Game::Move_Up() { 	//Ход игрока вверх 	if (Active > 0 && ActiveX != 0 && Matrix[ActiveX-1][ActiveY] == 0)//Если выделенная пешка и не самая верхняя строчка и ячейка выше пустая 	{ 		Matrix[ActiveX-1][ActiveY] = Matrix[ActiveX][ActiveY] ;//присваиваем ячейке выше текущюю(выделенную пешку) 		Matrix[ActiveX][ActiveY] = 0;//затираем старую ячейку на пустую  		ChessY[Active-1] += 0.2;//перемещаем координату У пешки вверх для отрисовки 		ActiveX = -1;//стираем координаты выделенной пешки 		ActiveY = -1;//стираем координаты выделенной пешки 		Active = -1;//делаем неактивной текущую выделенную фигуру 		std::cout << " Player MoveUp " << Active << std::endl; 		firstplayer = false; 		secondplayer = true;//меняем флаги хода от игрока к ИИ 	} } void Game::Move_Down() { 	//Ход игрока вниз 	if (Active > 0 && ActiveX != 7 && Matrix[ActiveX+1][ActiveY] == 0)//Если выделенная пешка и не самая нижняя строчка и ячейка ниже пустая 	{ 		Matrix[ActiveX+1][ActiveY] = Matrix[ActiveX][ActiveY] ;//присваиваем ячейке ниже текущюю(выделенную пешку) 		Matrix[ActiveX][ActiveY] = 0;//затираем старую ячейку на пустую  		ChessY[Active-1] -= 0.2;//перемещаем координату У пешки вниз для отрисовки 		ActiveX = -1;//стираем координаты выделенной пешки 		ActiveY = -1;//стираем координаты выделенной пешки 		Active = -1;//делаем неактивной текущую выделенную фигуру 		std::cout << "Player MoveDown " << Active << std::endl; 		firstplayer = false; 		secondplayer = true;//меняем флаги хода от игрока к ИИ 	} } void Game::Move_Right() { 	//Ход игрока вправо 	if (Active > 0 && ActiveY != 7 && Matrix[ActiveX][ActiveY+1] == 0)//Если выделенная пешка и не самая правая строчка и ячейка справа пустая 	{ 		Matrix[ActiveX][ActiveY+1] = Matrix[ActiveX][ActiveY] ;//присваиваем ячейке справа текущюю(выделенную пешку) 		Matrix[ActiveX][ActiveY] = 0;//затираем старую ячейку на пустую  		ChessX[Active-1] += 0.2;//перемещаем координату Х пешки вправо для отрисовки 		ActiveX = -1;//стираем координаты выделенной пешки 		ActiveY = -1;//стираем координаты выделенной пешки 		Active = -1;//делаем неактивной текущую выделенную фигуру 		std::cout << "MoveRight " << Active << std::endl; 		firstplayer = false; 		secondplayer = true;//меняем флаги хода от игрока к ИИ 	} } void Game::Move_Left() { 	//Ход игрока влево  	if (Active > 0 && ActiveY != 0 && Matrix[ActiveX][ActiveY-1] == 0)//Если выделенная пешка и не самая левая строчка и ячейка слева пустая 	{ 		Matrix[ActiveX][ActiveY-1] = Matrix[ActiveX][ActiveY] ;//присваиваем ячейке слева текущюю(выделенную пешку) 		Matrix[ActiveX][ActiveY] = 0;//затираем старую ячейку на пустую  		ChessX[Active-1] -= 0.2;//перемещаем координату Х пешки влево для отрисовки 		ActiveX = -1;//стираем координаты выделенной пешки 		ActiveY = -1;//стираем координаты выделенной пешки 		Active = -1;//делаем неактивной текущую выделенную фигуру 		std::cout << "MoveLeft " << Active << std::endl; 		firstplayer = false; 		secondplayer = true;//меняем флаги хода от игрока к ИИ 	} }

Ходы компьютера почти аналогичны ходам игрока, в конце будет ссылка на полный код игры, если кому интересно.

void Game::ReccurentWalk() { 	//Реккурентный ход ИИ 	current = -1, currentI = -1, currentJ = -1;//изначально выделенная пешка не определена 	for (int i = 0; i < Ai.size(); i++)//поиск по массиву 		if (!Ai[i].ai)//если не завершены ходы для конкретных пешек 		{ 			if (Check_MoveUp(Ai[i].value) || Check_MoveLeft(Ai[i].value))//Можно ли походить вверх или влево? 			{ 				current = Ai[i].value;//запоминаем текущую пешку 				break; 			} 			else 			{ 				//Если походить нельзя стираем из массива ходов пешку 				std::vector<chessai>::iterator position = std::find_if(Ai.begin(), Ai.end(), find_s(Ai[i].value)); 				if (position != Ai.end()) // == vector.end() means the element was not found 					Ai.erase(position); 			} 		} 	for (int i = 0; i < 8; i++) 		for (int j = 0; j < 8; j++) 			if (Matrix[i][j] == current)//ищем в матрице пешку и запоминаем индексы 			{ 				currentI = i; 				currentJ = j; 				break; 			} 	if (currentI != -1 && currentJ != -1)//если какая либо найдена ходим либо вверх либо влево 	{ 		if (!Move_UpAI(currentI, currentJ)) 			if (!Move_LeftAI(currentI, currentJ)) 			{ 				ReccurentWalk(); 			} 	} 	else 	{ 		//если не найдена заполняем массив ходов снова пешками 		chessai tmp; 		for (int i = 0; i < 18; i++) 		{ 			ai[i] = false; 			if (i > 8) 			{ 				tmp.ai = ai[i]; 				tmp.value = i + 1; 				Ai.push_back(tmp); 			} 		} 		//ищем ту которая может походить вправо или вниз 		for (int i = 0; i < Ai.size(); i++) 			if (!Ai[i].ai) 			{ 				if (Check_MoveRight(Ai[i].value) || Check_MoveDown(Ai[i].value)) 				{ 					current = Ai[i].value; 					break; 				} 				else 				{ 					//если не может то стираем из массива 					std::vector<chessai>::iterator position = std::find_if(Ai.begin(), Ai.end(), find_s(Ai[i].value)); 					if (position != Ai.end()) // == Vector.end() means the element was not found 						Ai.erase(position); 				} 			} 		//ищем ее индексы в матрице 		for (int i = 0; i < 8; i++) 			for (int j = 0; j < 8; j++) 				if (Matrix[i][j] == current) 				{ 					currentI = i; 					currentJ = j; 					break; 				} 		//ходим вправо или вниз 		if(!Move_RightAI(currentI, currentJ)) 			if (!Move_DownAI(currentI, currentJ)) 			{ 				std::cout <<"Artificial Intellegence asked: WTF?" << std::endl; 			} 	} 	chessai tmp; 	if(Ai.empty())//если список ходов пуст заполняем снова всеми 		for (int i = 0; i < 18; i++) 		{ 			ai[i] = false; 			if (i > 8) 			{ 				tmp.ai = ai[i]; 				tmp.value = i + 1; 				Ai.push_back(tmp); 			} 		} }

Ну и собственно опишу словами, что делает ИИ. Он собирает три пешки вверху на 0-ой строке и если ходить нельзя влево, то двигает вверх остальные 3 пешки на 1 строку, аналогично и на 2-ю строку. Если ходить влево и вверх нельзя, то он берет любую шашку и двигает либо вниз либо вправо если можно конечно, то есть стремится всегда двигать влево или вверх( при условии что выше меньше трёх шашек в строке).

Ну собственно и геймплей:

И ссылка на исходный код.

ссылка на оригинал статьи https://habr.com/ru/post/563398/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *