[알고리즘]#10_MiniMax, 최적의 의사 결정

2025. 4. 23. 15:58· 알고리즘
목차
  1. #1. 소개
  2. #2. 구성
  3. 1. 게임 트리
  4. 2. 기저 조건(종료 조건)
  5. 3. DFS(백트래킹)
  6. 4. alpha-beta 가지 치기, 최적화
  7. #3. 코드
  8. 1. 3x3 틱택토
  9. 2. 프로그래머스, 사라지는 발판

#1. 소개

1. 최대화 플레이어: 이익을 최대화하려는 플레이어
2. 최소화 플레이어: 최대화 플레이어의 이익을 최소화하려는 플레이어

최적의 의사 결정을 위한 재귀 알고리즘. 

 


 

#2. 구성

 

1. 게임 트리

게임 상태를 표현하는 트리

 

2. 기저 조건(종료 조건)

1. 게임 종료 함수: 어느 한 쪽이 이겼는지 판단하고, 그 결과를 반환하는 함수
2. 평가 함수: 게임 종료 상태의 수치화, 가장 간단한 형태는 승리/패배/무승부를 각각 +10/-10/0으로 표현

 

3. DFS(백트래킹)

최대? 최소? 누구의 턴인지 체크하고, 현재 상태에 대한 처리 이후 백트래킹 수행.
물론, 현재 최대화 플레이어의 턴이라면 재귀 호출 시 '최소화 플레이어'의 턴으로 설정하고 재귀 호출 진행.

 

4. alpha-beta 가지 치기, 최적화

alpha-beta 가지치기는 Minmax 알고리즘의 최적화 전략, 더 이상 살펴볼 필요가 없는 노드를 미리 제거함으로써 탐색 경로의 수를 줄인다. 최대화 플레이어는 이미 알려진 최소 보장 값(alpha) 보다 최소화 플레이어가 낮은 점수로 제한 가능하다면, 최대화 플레이어는 해당 경로를 절대 선택 하지 않는다. 또, 최소화 플레이어는 이미 알려진 최대 허용 값(beta) 보다 최대화 플레이어가 높은 점수를 얻을 수 있다면, 최소화 플레이 이는 이 경로를 절대 선택 안 한다.

1. alpha : 최대화 플레이어가 확보한 최소 점수 값, "적어도 이 점수는 얻을 수 있다"는 보장
2. beta : 최소화 플레이어가 확보 가능한 최대 점수 값, "이 점수보다 크게 허용하지 않겠다"는 상한

alpha는 매우 작은 값, beta는 매우 큰 값으로 초기화.

최대화 플레이어는 모든 자식 노드를 평가하고, 최대 점수 갱신 이후 alpha = max(alpha, 최대 점수) 업데이트 진행, 그리고 만약 beta <= alpha 라면, 현재 노드의 나머지 자식 노드는 검사할 필요가 없음.

반대로 최소화 플레이어는 모든 자식 노드를 평가하고, 최소 점수 갱신 이후 beta = min(beta, 최소 점수) 업데이트 진행, 그리고 만약 beta <= alpha 라면, 현재 노드의 나머지 자식 노드는 검사할 필요가 없음

 


 

#3. 코드

 

1. 3x3 틱택토

#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <climits>
#include <algorithm>
using namespace std;

typedef pair<int,int> p;

//3x3 2차원 배열
int n = 3;
int m = 3;

//게임 트리
vector<vector<int>> board(n, vector<int>(m, 0));

//게임 종료 함수
int checkWinner()
{
    //가로
    for(int i=0; i<m; i++)
    {
        if(board[i][0] != 0 && board[i][0] == board[i][1] && board[i][1] == board[i][2])
            return board[i][0];
    }
    //세로
    for (int j = 0; j < 3; j++) {
        if (board[0][j] != 0 && board[0][j] == board[1][j] && board[1][j] == board[2][j]) {
            return board[0][j];
        }
    }
    //대각
    if (board[0][0] != 0 && board[0][0] == board[1][1] && board[1][1] == board[2][2]) {
        return board[0][0];
    }
    if (board[0][2] != 0 && board[0][2] == board[1][1] && board[1][1] == board[2][0]) {
        return board[0][2];
    }
    //무승부
    bool isFull = true;
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            if (board[i][j] == 0) {
                isFull = false;
                break;
            }
        }
        if (!isFull) break;
    }

    return isFull ? 2 : 0; // 무승부면 2, 진행 중이면 0
}

vector<p> getAvailableMoves() {
    vector<pair<int, int>> moves;
    
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            if (board[i][j] == 0) {
                moves.push_back({i, j});
            }
        }
    }
    
    return moves;
}

//minimax
int miniMax(int depth, bool IsMaximizing, int alpha, int beta)
{
    //종료 함수
    int result = checkWinner();

    //기저 조건
    if(result == 1) return 10 - depth;
    if(result == -1) return depth - 10;
    if(result == 2) return 0;

    //백트래킹 
    //최대화 플레이어 턴
    if(IsMaximizing)
    {
        int bestScore = INT_MIN;

        //모든 가능한 이동 탐색
        vector<p> moves = getAvailableMoves();
        for(auto move : moves)
        {
            int ny = move.first;
            int nx = move.second;
            
            board[ny][nx] = 1;

            //DFS
            int score = miniMax(depth+1, false, alpha, beta);

            //백트래킹
            board[ny][nx] = 0;

            //최대값 업데이트
            bestScore = max(bestScore, score);

            //알파 값 업데이트
            alpha = max(alpha, bestScore);

            //알파-베타 가지치기, 최적화
            if(beta <= alpha)
                break;
        }
        return bestScore;
    }
    //최소화 플레이어 턴
    else
    {
        int bestScore = INT_MAX;

        //모든 가능한 이동 탐색
        vector<p> moves = getAvailableMoves();
        for(auto move : moves)
        {
            int ny = move.first;
            int nx = move.second;
            
            board[ny][nx] = -1;

            //DFS
            int score = miniMax(depth+1, true, alpha, beta);

            //백트래킹
            board[ny][nx] = 0;

            //최대값 업데이트
            bestScore = min(bestScore, score);

            //beta 값 업데이트
            beta = min(beta, bestScore);

            //알파-베타 가지치기, 최적화
            if(beta <= alpha)
                break;
        }
        return bestScore;
    }
}

 

2. 프로그래머스, 사라지는 발판

#include <string>
#include <vector>
#include <climits>
#include <algorithm>
using namespace std;

typedef pair<int,int> p;

int n, m;
int dy[] = {1,-1,0,0};
int dx[] = {0,0,1,-1};

struct Result
{
    bool win;
    int moves;
    
    Result(bool _win, int _moves) : win(_win), moves(_moves) {}
};

vector<p> FindNextMoves(p cur, vector<vector<int>>& board)
{
    int cy = cur.first;
    int cx = cur.second;
    
    vector<p> moves;
    
    for(int i = 0; i < 4; ++i)
    {
        int ny = cy + dy[i];
        int nx = cx + dx[i];
        
        if(ny < 0 || ny >= n || nx < 0 || nx >= m || board[ny][nx] == 0) continue;
        
        moves.push_back({ny, nx});
    }
    
    return moves;
}

Result minimax(vector<vector<int>>& board, p cur, p opo)
{
    // 현재 위치가 발판이 없으면 패배
    if(board[cur.first][cur.second] == 0) return Result(false, 0);
    
    // 이동할 곳이 없으면 패배
    vector<p> moves = FindNextMoves(cur, board);
    if(moves.empty()) return Result(false, 0); 
    
    bool canWin = false;
    int minWinMoves = INT_MAX;
    int maxLoseMoves = 0;
    
    for(auto move : moves)
    {
        int cy = cur.first, cx = cur.second;
        
        // 두 플레이어가 같은 위치에 있고, 현재 플레이어가 이동하면 상대방은 바로 패배
        if(opo.first == cy && opo.second == cx)
        {
            canWin = true;
            minWinMoves = min(minWinMoves, 1);
            continue;
        }
        
        // 현재 발판 제거
        board[cy][cx] = 0;
        
        // 상대방 차례 계산
        auto opoResult = minimax(board, opo, move);
        
        // 백트래킹
        board[cy][cx] = 1;
        
        if(!opoResult.win)
        {
            canWin = true;
            minWinMoves = min(minWinMoves, opoResult.moves + 1);
        }
        else
        {
            maxLoseMoves = max(maxLoseMoves, opoResult.moves + 1);
        }
    }
    
    if(canWin)
        return Result(true, minWinMoves);
    else
        return Result(false, maxLoseMoves);
}

int solution(vector<vector<int>> board, vector<int> aloc, vector<int> bloc) {
    n = board.size();
    m = board[0].size();
    
    p a = {aloc[0], aloc[1]};
    p b = {bloc[0], bloc[1]};
    
    auto res = minimax(board, a, b);
    
    return res.moves;
}

 


 

 

 

 

저작자표시 (새창열림)

'알고리즘' 카테고리의 다른 글

[알고리즘]#12_차분 배열, 2차원 배열의 O(k+n*m)의 범위 업데이트  (1) 2025.07.22
[알고리즘]#카탈란 수열, DP  (0) 2025.04.23
[알고리즘]#9_Quick Select  (0) 2024.04.30
[알고리즘]#7_LCA(Least Common Ancestor)  (0) 2024.03.12
[알고리즘]#7_투 포인터  (0) 2023.12.13
  1. #1. 소개
  2. #2. 구성
  3. 1. 게임 트리
  4. 2. 기저 조건(종료 조건)
  5. 3. DFS(백트래킹)
  6. 4. alpha-beta 가지 치기, 최적화
  7. #3. 코드
  8. 1. 3x3 틱택토
  9. 2. 프로그래머스, 사라지는 발판
'알고리즘' 카테고리의 다른 글
  • [알고리즘]#12_차분 배열, 2차원 배열의 O(k+n*m)의 범위 업데이트
  • [알고리즘]#카탈란 수열, DP
  • [알고리즘]#9_Quick Select
  • [알고리즘]#7_LCA(Least Common Ancestor)
Hardii2
Hardii2
개발 블로그Hardii2 님의 블로그입니다.
Hardii2
개발 블로그
Hardii2
전체
오늘
어제
  • 분류 전체보기
    • 알고리즘
    • 웹 개발
      • Node.js
      • React
    • 게임개발
      • DirectX12
      • 관련 지식
      • Unreal C++
      • Unreal 블루프린트
    • 언어
      • Effective C++
      • Basic C++
      • 디자인 패턴
      • 자료구조
      • 기술 질문
    • 문제 풀이
      • BOJ 문제 풀이
      • Programmers 문제 풀이
      • geeksForgeeks 문제 풀이
    • 수학
      • 확률과 통계
      • 게임수학
    • 개인프로젝트
    • 그룹프로젝트
      • PM
      • Dev
    • Github

블로그 메뉴

  • 홈
  • 글쓰기

공지사항

인기 글

태그

  • 최단 경로
  • set
  • dfs
  • 알고리즘
  • BFS
  • 개발
  • BOJ
  • 우선순위 큐
  • 기술 질문
  • Unreal Blueprint
  • 디자인 패턴
  • DP
  • 트리
  • unreal
  • 정렬
  • stl
  • 그래프
  • C++
  • programmers
  • Effective C++

최근 댓글

최근 글

hELLO · Designed By 정상우.v4.2.2
Hardii2
[알고리즘]#10_MiniMax, 최적의 의사 결정
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.