mirror of
https://github.com/LHY0125/Gobang-Game.git
synced 2026-05-10 02:19:46 +08:00
325 lines
10 KiB
C
325 lines
10 KiB
C
#include "ai.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
|
|
extern int BOARD_SIZE;
|
|
extern int board[MAX_BOARD_SIZE][MAX_BOARD_SIZE];
|
|
extern int step_count;
|
|
extern const int direction[4][2];
|
|
extern Step steps[MAX_STEPS];
|
|
|
|
/**
|
|
* @brief ?????????????????????????
|
|
* ??????????????????????????????????????????????????
|
|
* ????????????????????????????????
|
|
* @param x ????????????? (0-based)??
|
|
* @param y ????????????? (0-based)??
|
|
* @param player ????? (PLAYER ?? AI)??????????????????????
|
|
* @return int ???????????????????????
|
|
* @note ??????????
|
|
* - ??????????????????????M???????????????????????????
|
|
* - ?????????????????\????????????????????\????????????????????\?????????
|
|
* ?????????????????????
|
|
* - ?????? (??????????????????AI???):
|
|
* - ????: 1,000,000 (???)
|
|
* - ????: 100,000 (????????)
|
|
* - ????: 10,000 (????????????)
|
|
* - ????: 5,000 (??????)
|
|
* - ????: 1,000
|
|
* - ???: 500
|
|
* - ???: 100
|
|
* - ????: ????????
|
|
* - ?????????????????????????????????????????????????????????AI????????
|
|
*/
|
|
int evaluate_pos(int x, int y, int player)
|
|
{
|
|
// ?????????????
|
|
int original = board[x][y];
|
|
// ??????????????
|
|
board[x][y] = player;
|
|
|
|
int total_score = 0; // ???
|
|
int line_scores[4] = {0}; // ???????????
|
|
|
|
// ??????????????????
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
int dx = direction[i][0], dy = direction[i][1];
|
|
// ????????????????????
|
|
DirInfo info = count_specific_direction(x, y, dx, dy, player);
|
|
|
|
// ?????????????????
|
|
if (info.continuous_chess >= 5)
|
|
{
|
|
board[x][y] = original; // ???????
|
|
return 1000000; // ????????
|
|
}
|
|
|
|
// ??????????????????
|
|
switch (info.continuous_chess)
|
|
{
|
|
case 4: // ??????
|
|
if (info.check_start && info.check_end) // ????(???????)
|
|
line_scores[i] = 100000;
|
|
else if (info.check_start || info.check_end) // ????(??????)
|
|
line_scores[i] = 10000;
|
|
else // ????(??????)
|
|
line_scores[i] = 500;
|
|
break;
|
|
|
|
case 3: // ??????
|
|
if (info.check_start && info.check_end) // ????
|
|
line_scores[i] = 5000;
|
|
else if (info.check_start || info.check_end) // ????
|
|
line_scores[i] = 1000;
|
|
else // ????
|
|
line_scores[i] = 50;
|
|
break;
|
|
|
|
case 2: // ??????
|
|
if (info.check_start && info.check_end) // ???
|
|
line_scores[i] = 500;
|
|
else if (info.check_start || info.check_end) // ???
|
|
line_scores[i] = 100;
|
|
else // ????
|
|
line_scores[i] = 10;
|
|
break;
|
|
|
|
case 1: // ????
|
|
if (info.check_start && info.check_end) // ????????
|
|
line_scores[i] = 50;
|
|
else if (info.check_start || info.check_end) // ????????
|
|
line_scores[i] = 10;
|
|
else // ???????
|
|
line_scores[i] = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// ???????????????+?????????????
|
|
int max_score = 0;
|
|
int sum_score = 0;
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
if (line_scores[i] > max_score)
|
|
max_score = line_scores[i];
|
|
sum_score += line_scores[i];
|
|
}
|
|
total_score = max_score * 10 + sum_score; // ????????????
|
|
|
|
// ????????????????????????
|
|
int center_x = BOARD_SIZE / 2;
|
|
int center_y = BOARD_SIZE / 2;
|
|
int distance = abs(x - center_x) + abs(y - center_y); // ?????????
|
|
int position_bonus = 50 * (BOARD_SIZE - distance); // ??????????????????
|
|
|
|
board[x][y] = original; // ?????????
|
|
return total_score + position_bonus; // ????????????
|
|
}
|
|
|
|
/**
|
|
* @brief ??????-???????????????????Minimax?????????????????
|
|
* ??????????????????????????????????????????????????????
|
|
* @param x ????????????????
|
|
* @param y ????????????????
|
|
* @param player ??????????? (PLAYER ?? AI)??
|
|
* @param depth ??????????????????AI????????????????????
|
|
* @param alpha ?????????????AI?????????????????????????
|
|
* @param beta ???????????????????????????????????????????
|
|
* @param is_maximizing ???????true???????????????AI???????false????????????????????????
|
|
* @return int ???????????????????????????
|
|
* @note ?????????:
|
|
* 1. **??????????**: ?????????????????????????????????????????????????????????????????
|
|
* 2. **??????? (AI)**: ??????????????????????????????????????????????????????????alpha???
|
|
* 3. **????????? (????)**: ????????????????????????????AI???????????????????????????????????beta???
|
|
* 4. **??-????**: ?????Minimax???????????
|
|
* - **?????**: ????????????????????????????????????????alpha?????????????????????????
|
|
* ??????????????????????????????????????????(if beta <= alpha)
|
|
* - **????**: ??????????ah?????????????????????????????beta??????????????????????????
|
|
* ???????????????????????????????????????????????????????(if beta <= alpha)
|
|
* ????????????????????????????????????????????AI??????????
|
|
*/
|
|
int dfs(int x, int y, int player, int depth, int alpha, int beta, bool is_maximizing)
|
|
{
|
|
// ???n??????????
|
|
if (check_win(x, y, player))
|
|
{
|
|
return (player == AI) ? 1000000 + depth : -1000000 - depth;
|
|
}
|
|
|
|
// ?????????????
|
|
if (depth == 0 || step_count >= BOARD_SIZE * BOARD_SIZE)
|
|
{
|
|
return evaluate_pos(x, y, AI) - evaluate_pos(x, y, PLAYER);
|
|
}
|
|
|
|
int best_score = is_maximizing ? -1000000 : 1000000;
|
|
|
|
// ????????????????????
|
|
for (int i = 0; i < BOARD_SIZE; i++)
|
|
{
|
|
for (int j = 0; j < BOARD_SIZE; j++)
|
|
{
|
|
if (board[i][j] != EMPTY)
|
|
continue;
|
|
|
|
// ???????????
|
|
board[i][j] = player;
|
|
step_count++;
|
|
|
|
// ???????(???????????????)
|
|
int current_score = dfs(i, j, (player == AI) ? PLAYER : AI, depth - 1, alpha, beta, !is_maximizing);
|
|
|
|
// ????????
|
|
board[i][j] = EMPTY;
|
|
step_count--;
|
|
|
|
// ????????(AI)???
|
|
if (is_maximizing)
|
|
{
|
|
best_score = (current_score > best_score) ? current_score : best_score;
|
|
alpha = (best_score > alpha) ? best_score : alpha;
|
|
// ?????
|
|
if (beta <= alpha)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
// ????????(????)???
|
|
else
|
|
{
|
|
best_score = (current_score < best_score) ? current_score : best_score;
|
|
beta = (best_score < beta) ? best_score : beta;
|
|
// ????
|
|
if (beta <= alpha)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if ((is_maximizing && best_score >= beta) || (!is_maximizing && best_score <= alpha))
|
|
{
|
|
break; // ????????????
|
|
}
|
|
}
|
|
|
|
return best_score;
|
|
}
|
|
|
|
/**
|
|
* @brief AI?????????????????????????????????????????????
|
|
* @note ??????????????????
|
|
* 1. ????????????????????????????????????????????????
|
|
* 2. ???????????????????????????DFS?????????????????
|
|
* @note ???????
|
|
* - ??????????????????????????
|
|
* - ????>10??????????????????????????2??
|
|
* - ??????????????????
|
|
*/
|
|
void ai_move(int depth)
|
|
{
|
|
// 1. ???????????????????????????????????
|
|
for (int i = 0; i < BOARD_SIZE; i++)
|
|
{
|
|
for (int j = 0; j < BOARD_SIZE; j++)
|
|
{
|
|
if (board[i][j] != EMPTY)
|
|
continue;
|
|
|
|
// ?????????????????
|
|
board[i][j] = PLAYER;
|
|
bool need_block = false;
|
|
|
|
// ??????????
|
|
for (int k = 0; k < 4; k++)
|
|
{
|
|
DirInfo info = count_specific_direction(i, j, direction[k][0], direction[k][1], PLAYER);
|
|
|
|
// ????????????????????????????????
|
|
if (info.continuous_chess >= 4 && (info.check_start || info.check_end))
|
|
{
|
|
need_block = true;
|
|
break;
|
|
}
|
|
|
|
// ????????????????????????????
|
|
if (info.continuous_chess == 3 && info.check_start && info.check_end)
|
|
{
|
|
need_block = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
board[i][j] = EMPTY; // ???????
|
|
|
|
if (need_block)
|
|
{
|
|
// ??????????????????
|
|
board[i][j] = AI;
|
|
steps[step_count++] = (Step){AI, i, j};
|
|
printf("AI????(%d, %d)\n", i + 1, j + 1);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 2. ?????????????????????????????????
|
|
int best_score = -1000000;
|
|
int best_x = -1, best_y = -1;
|
|
|
|
// ????????????????
|
|
for (int i = 0; i < BOARD_SIZE; i++)
|
|
{
|
|
for (int j = 0; j < BOARD_SIZE; j++)
|
|
{
|
|
if (board[i][j] != EMPTY)
|
|
continue;
|
|
|
|
// ????????????????(2??????)
|
|
bool has_nearby_stone = false;
|
|
for (int di = -2; di <= 2; di++)
|
|
{
|
|
for (int dj = -2; dj <= 2; dj++)
|
|
{
|
|
int ni = i + di;
|
|
int nj = j + dj;
|
|
if (ni >= 0 && ni < BOARD_SIZE &&
|
|
nj >= 0 && nj < BOARD_SIZE)
|
|
{
|
|
if (board[ni][nj] != EMPTY)
|
|
{
|
|
has_nearby_stone = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (has_nearby_stone)
|
|
break;
|
|
}
|
|
if (!has_nearby_stone && step_count > 10)
|
|
continue;
|
|
|
|
// ???AI????
|
|
board[i][j] = AI;
|
|
int current_score = dfs(i, j, PLAYER, depth, -1000000, 1000000, false);
|
|
board[i][j] = EMPTY;
|
|
|
|
// ???????????
|
|
if (current_score > best_score)
|
|
{
|
|
best_score = current_score;
|
|
best_x = i;
|
|
best_y = j;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ??????????
|
|
if (best_x != -1 && best_y != -1)
|
|
{
|
|
board[best_x][best_y] = AI;
|
|
steps[step_count++] = (Step){AI, best_x, best_y};
|
|
printf("AI????(%d, %d)\n", best_x + 1, best_y + 1);
|
|
}
|
|
} |