[windowsAPI] 11주차(1), Tic-Tac-Toe (with MIN-MAX Algorithm)

교내 전공과목인 윈도우즈 API수업을 정리합니다

 

 

 

 

1. Tic-Tac-Toe 프로그램을 수읽기(재귀함수)를 통해 사용해보자

인공지능의 min-max-Game Tree, 즉 수 읽기 알고리즘을 적용시켜 보자

 

(1) 소스코드, 구현사항

// init 함수
void init(HWND hWnd) {  //파라미터를 가지도록 변경
	int i, j;
	turn = 1; 
	iCount = 0;
	for (i = 0; i < 3; i++)
		for (j = 0; j < 3; j++) {
			pan[i][j] = 0;
		}
	InvalidateRect(hWndMain, NULL, TRUE);
}


//evaluate함수
int evaluate(int depth = 0) {
	if (winpoint(2)) // 컴퓨터가 이기는 결과에 점수 증가
		return 10 + depth;
	else if (winpoint(1)) // 컴퓨터가 지는 결과에 점수 감수
		return -10 - depth;
	else
		return 0;
}

 

 

//minmax함수
int minmax(int* pos, int depth, int turn) {
	int score;
	int best;
	int position;

	if (turn == 1)
		best = 99;
	else
		best = -99;

	if (depth == 0 || winpoint(1) || winpoint(2))
		return evaluate(depth);

	for (int i = 0; i < 9; i++) {
		if (pan[i % 3][i / 3] == 0) {// 선하향 후우향,  빈 칸이라면
			pan[i % 3][i / 3] = turn; // 탐색을 위해 현재 차례에 컴퓨터의 돌을 놓기 (예상)
			if (turn == 1)
				/*재귀함수*/
				score = minmax(pos, depth - 1, 2); // 컴퓨터 차례로 넘겨서 이전 칸의 minmax값 찾기
			else
				score = minmax(pos, depth - 1, 1); // 플레이어 차례로 넘겨서 이전 칸의 minmax값 찾기

			pan[i % 3][i / 3] = 0; // 계산을 끝낸 후, 현 자리를 초기화 한다
			if (turn == 1) { // 플레이어 조사시, 최적값이 '최소'가 되는 것을 찾아 기록한다
				if (score < best) {  
					best = score;
					position = i; // i기준의 최적값(최소) 위치 기록
				}
			}
			else { // 컴퓨터 조사시, 최적값이 '최대'가 되는 것을 찾아 기록한다
				if (score > best) { 
					best = score;
					position = i; // i기준의 최적값(최대) 위치 기록
				}
			}
		}
	}
	*pos = position; // 최종 위치를 선택(주소값이니까 전역st)
	return best; //최적값 반환
}

필요한 설명은 주석에 달았으나, 이 함수가 제일 중요하다.

i를 통해 각 칸에 대하여, 경로의 최적값을 판단하기 위한 점수를 부여한다

이 함수가 끝날 때 최적위치와 최적값을 저장한다

 

// computer함수, 실제로 computer가 바둑을 두게 됨
int computer() {
	int depth = 0;
	int pos = 0;
	for (int i = 0; i < 9; i++) 
		if(pan[i%3][i/3] == 0)
			depth++; //depth는 현재 비어있는 칸의 수로 결정
	
	minmax(&pos, depth, 2); // 컴퓨터의 최적을 찾으러 감
	pan[pos % 3][pos / 3] = 2; // 여기는 컴퓨터 꺼
	if (winpoint(2)) // 이겼다면 1이 들어옴(true)
		return 1;
	else // 졌다면
		return 0;
}

minmax를 쓰게되는 함수.

 

 

//WndProc > WM_LBUTTONDOWN의 일부

		if (pan[x][y] == 0) {
			pan[x][y] = turn;
			iCount++;
			// turn = (turn == 1 ? 2 : 1);  // 컴퓨터와 승부할 것이니 이제 필요없음
			InvalidateRect(hWnd, NULL, TRUE);
			UpdateWindow(hWnd);

			//메세지박스 응답선택에 따른 반응
			if (winpoint(1)) { // 1이 이겼다면
				wsprintf(buf, TEXT("사용자 win. \n 새로 시작하겠습니까?"));
				if (MessageBox(hWnd, buf, TEXT("TicTacToe"), MB_YESNO | MB_ICONEXCLAMATION) == IDYES)
					init(hWnd);
				else
					SendMessage(hWnd, WM_CLOSE, 0L, 0L);
				return 0;
			}
			else if (iCount == 9) { // 더 이상 둘 곳이 없어 비기거나 플레이어가 졌다면
				if (MessageBox(hWnd, TEXT("Game Over. \n 새로 시작하겠습니까?"), TEXT("TicTacToe"),
					MB_YESNO | MB_ICONEXCLAMATION) == IDYES) {
					init(hWnd);
					//turn =1; //얜 없어도 될 것 같아서 가렸음
				}
				else
					SendMessage(hWnd, WM_CLOSE, 0L, 0L);
				return 0;
			}
			else { //게임이 진행중이라면, 
				int ret = computer(); // 컴퓨터의 승(1)패(0)값을 받아옴
				iCount++;
				InvalidateRect(hWnd, NULL, TRUE);
				UpdateWindow(hWnd);
				if (ret) { //컴퓨터가 이겼다면
					wsprintf(buf, TEXT(" 컴퓨터 win. \n 새로 시작하겠습니까?"));
					if (MessageBox(hWnd, buf, TEXT("MinMax"), MB_YESNO | MB_ICONEXCLAMATION) == IDYES)
						init(hWnd);
					else
						SendMessage(hWnd, WM_CLOSE, 0L, 0L);
					return 0;
				}
				return 0;
			}
		}

조건문이 minmax를 활용하도록 바꼈다.

 

 

 

(2) 실행결과

컴퓨터를 절대 이길 수 없는, 지거나 비기기만 하는 게임이 진행됨