自用五子棋2.0
2026-10-25 15:38:38
发布于:上海
#include <windows.h>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cstdio>
#define SIZE 15
#define SEARCH_DEPTH 3 // 深度设为3以保证在Dev-C++环境下流畅运行,可尝试改为4
// 棋盘状态:0空, 1黑(AI), 2白(玩家)
int board[SIZE][SIZE];
HWND hWnd;
int winPlayer = 0;
bool isThinking = false; // 标记AI是否正在思考
// 方向数组:横、竖、左斜、右斜
const int dir[4][2] = {{1,0}, {0,1}, {1,1}, {1,-1}};
// 评分常量
const int SCORE_WIN = 10000000;
const int SCORE_LIVE4 = 900000;
const int SCORE_DEAD4 = 500000;
const int SCORE_LIVE3 = 100000;
const int SCORE_DEAD3 = 30000;
const int SCORE_LIVE2 = 5000;
// 候选点结构体,用于排序
struct Move {
int x, y;
int score;
bool operator<(const Move& other) const {
return score > other.score; // 降序排列,高分优先
}
};
bool CheckWin(int x, int y, int color) {
for (int d = 0; d < 4; d++) {
int dx = dir[d][0], dy = dir[d][1];
int cnt = 1;
// 正向检查
for (int k = 1; k < 5; k++) {
int nx = x + dx * k, ny = y + dy * k;
if (nx >= 0 && nx < SIZE && ny >= 0 && ny < SIZE && board[ny][nx] == color) cnt++;
else break;
}
// 反向检查
for (int k = 1; k < 5; k++) {
int nx = x - dx * k, ny = y - dy * k;
if (nx >= 0 && nx < SIZE && ny >= 0 && ny < SIZE && board[ny][nx] == color) cnt++;
else break;
}
if (cnt >= 5) return true;
}
return false;
}
// 计算单个方向上的棋型分数
int CalcLineScore(int x, int y, int dx, int dy, int color) {
int s = 0;
// 滑动窗口检测长度为5的区间
for (int st = -4; st <= 0; st++) {
int c = 0, leftBlock = 0, rightBlock = 0;
int sx = x + st * dx;
int sy = y + st * dy;
for (int i = 0; i < 5; i++) {
int nx = sx + dx * i;
int ny = sy + dy * i;
if (nx < 0 || nx >= SIZE || ny < 0 || ny >= SIZE) {
if (i == 0) leftBlock = 1;
if (i == 4) rightBlock = 1;
continue;
}
if (board[ny][nx] == color) {
c++;
} else if (board[ny][nx] != 0) {
// 被对方棋子阻挡
if (i == 0) leftBlock = 1;
if (i == 4) rightBlock = 1;
}
}
// 如果两端都被堵死,且中间没有形成连五(c<5),则无分
if (leftBlock && rightBlock) {
if(c < 5) continue;
}
if (c == 5) s += SCORE_WIN;
else if (c == 4) {
if (!leftBlock && !rightBlock) s += SCORE_LIVE4;
else if (leftBlock ^ rightBlock) s += SCORE_DEAD4;
} else if (c == 3) {
if (!leftBlock && !rightBlock) s += SCORE_LIVE3;
else if (leftBlock ^ rightBlock) s += SCORE_DEAD3;
} else if (c == 2) {
if (!leftBlock && !rightBlock) s += SCORE_LIVE2;
}
}
return s;
}
// 评估某一点对于某颜色的价值
int EvalPoint(int x, int y, int color) {
if (board[y][x] != 0) return -SCORE_WIN; // 非空点不可下
int total = 0;
for (int d = 0; d < 4; d++) {
total += CalcLineScore(x, y, dir[d][0], dir[d][1], color);
}
// 中心权重,鼓励占中
int dist = (x - 7) * (x - 7) + (y - 7) * (y - 7);
total += 200 / (1 + dist);
return total;
}
// 全局局面评估:AI得分 - 对手得分
int EvalBoard(int ai) {
int op = 3 - ai;
int s = 0;
// 只评估空位,避免重复计算和逻辑错误
for (int y = 0; y < SIZE; y++) {
for (int x = 0; x < SIZE; x++) {
if (board[y][x] == 0) {
s += EvalPoint(x, y, ai);
s -= EvalPoint(x, y, op) * 1.2; // 防守权重略高
}
}
}
return s;
}
// 获取候选落子点:只考虑已有棋子周围2格内的空位
void GetCandidate(std::vector<Move>& moves, int ai) {
moves.clear();
bool hasChess = false;
bool visited[SIZE][SIZE];
memset(visited, 0, sizeof(visited)); // 正确初始化
for (int y = 0; y < SIZE; y++) {
for (int x = 0; x < SIZE; x++) {
if (board[y][x] != 0) {
hasChess = true;
for (int dy = -2; dy <= 2; dy++) {
for (int dx = -2; dx <= 2; dx++) {
int nx = x + dx, ny = y + dy;
if (nx >= 0 && nx < SIZE && ny >= 0 && ny < SIZE && board[ny][nx] == 0 && !visited[ny][nx]) {
visited[ny][nx] = true;
// 预计算启发式分数,用于排序
int score = EvalPoint(nx, ny, ai) + EvalPoint(nx, ny, 3-ai);
moves.push_back({nx, ny, score});
}
}
}
}
}
}
if (!hasChess) {
moves.push_back({7, 7, 0});
} else {
// 按启发式分数降序排序,优化Alpha-Beta剪枝效率
std::sort(moves.begin(), moves.end());
}
}
int AlphaBeta(int depth, int alpha, int beta, int isMax, int ai) {
if (depth == 0) return EvalBoard(ai);
int op = 3 - ai;
std::vector<Move> candidates;
GetCandidate(candidates, isMax ? ai : op); // 根据当前层角色生成候选点
if (candidates.empty()) return EvalBoard(ai);
if (isMax) {
int best = -SCORE_WIN;
for (const auto& m : candidates) {
board[m.y][m.x] = ai;
// 立即胜利检测
if (CheckWin(m.x, m.y, ai)) {
board[m.y][m.x] = 0;
return SCORE_WIN;
}
int val = AlphaBeta(depth - 1, alpha, beta, 0, ai);
board[m.y][m.x] = 0;
if (val > best) best = val;
if (best > alpha) alpha = best;
if (alpha >= beta) return alpha; // 剪枝
}
return best;
} else {
int best = SCORE_WIN;
for (const auto& m : candidates) {
board[m.y][m.x] = op;
// 立即失败检测(对手赢)
if (CheckWin(m.x, m.y, op)) {
board[m.y][m.x] = 0;
return -SCORE_WIN;
}
int val = AlphaBeta(depth - 1, alpha, beta, 1, ai);
board[m.y][m.x] = 0;
if (val < best) best = val;
if (best < beta) beta = best;
if (alpha >= beta) return beta; // 剪枝
}
return best;
}
}
void AiThink(int &outX, int &outY, int ai) {
int bestVal = -SCORE_WIN;
outX = 7; outY = 7;
std::vector<Move> candidates;
GetCandidate(candidates, ai);
// 第一层搜索:寻找直接获胜或最优解
for (const auto& m : candidates) {
board[m.y][m.x] = ai;
// 1. 检查是否直接获胜
if (CheckWin(m.x, m.y, ai)) {
board[m.y][m.x] = 0;
outX = m.x; outY = m.y;
return;
}
// 2. 递归搜索
int cur = AlphaBeta(SEARCH_DEPTH - 1, -SCORE_WIN, SCORE_WIN, 0, ai);
board[m.y][m.x] = 0;
if (cur > bestVal) {
bestVal = cur;
outX = m.x;
outY = m.y;
}
}
}
void ResetGame() {
memset(board, 0, sizeof(board));
winPlayer = 0;
isThinking = false;
int ax, ay;
AiThink(ax, ay, 1);
board[ay][ax] = 1;
InvalidateRect(hWnd, NULL, TRUE);
}
LRESULT CALLBACK WndProc(HWND hw, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_KEYDOWN:
if (wParam == 0x52) { // 'R' key
ResetGame();
}
break;
case WM_LBUTTONDOWN: {
if (winPlayer != 0 || isThinking) break;
int mx = LOWORD(lParam);
int my = HIWORD(lParam);
int cx = mx / 40;
int cy = my / 40;
if (cx >= 0 && cx < SIZE && cy >= 0 && cy < SIZE && board[cy][cx] == 0) {
// 玩家落子
board[cy][cx] = 2;
if (CheckWin(cx, cy, 2)) {
winPlayer = 2;
InvalidateRect(hw, NULL, FALSE);
return 0;
}
// 显示思考中
isThinking = true;
HDC hdc = GetDC(hw);
SetBkMode(hdc, TRANSPARENT);
SetTextColor(hdc, RGB(255, 0, 0));
TextOutA(hdc, 200, 300, "AI Thinking...", 14);
ReleaseDC(hw, hdc);
// AI 思考并落子
int ax, ay;
AiThink(ax, ay, 1);
board[ay][ax] = 1;
isThinking = false;
if (CheckWin(ax, ay, 1)) {
winPlayer = 1;
}
InvalidateRect(hw, NULL, FALSE);
}
break;
}
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hw, &ps);
// 背景
HBRUSH bgBrush = CreateSolidBrush(RGB(220, 179, 92));
Rectangle(hdc, 0, 0, 600, 600);
FillRect(hdc, &ps.rcPaint, bgBrush);
DeleteObject(bgBrush);
// 网格线
HPEN pen = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
SelectObject(hdc, pen);
for (int i = 0; i < SIZE; i++) {
MoveToEx(hdc, i * 40 + 20, 20, NULL);
LineTo(hdc, i * 40 + 20, 580);
MoveToEx(hdc, 20, i * 40 + 20, NULL);
LineTo(hdc, 580, i * 40 + 20);
}
DeleteObject(pen);
// 棋子
for (int y = 0; y < SIZE; y++) {
for (int x = 0; x < SIZE; x++) {
if (board[y][x] == 0) continue;
COLORREF col = board[y][x] == 1 ? RGB(0, 0, 0) : RGB(255, 255, 255);
HBRUSH br = CreateSolidBrush(col);
SelectObject(hdc, br);
Ellipse(hdc, x * 40 + 4, y * 40 + 4, x * 40 + 36, y * 40 + 36);
DeleteObject(br);
}
}
// 胜利提示
if (winPlayer != 0) {
HFONT bigFont = CreateFontA(48, 0, 0, 0, FW_BOLD, 0, 0, 0, GB2312_CHARSET, 0, 0, 0, 0, "SimHei");
HFONT oldFont = (HFONT)SelectObject(hdc, bigFont);
char buf[64];
if (winPlayer == 1) strcpy(buf, "AI 获胜!");
else strcpy(buf, "你 获胜!");
SetBkMode(hdc, TRANSPARENT);
SetTextColor(hdc, RGB(255, 0, 0));
TextOutA(hdc, 180, 280, buf, strlen(buf));
SelectObject(hdc, oldFont);
DeleteObject(bigFont);
}
EndPaint(hw, &ps);
break;
}
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hw, msg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR, int) {
WNDCLASSEX wc = {0};
wc.cbSize = sizeof(wc);
wc.lpfnWndProc = WndProc;
wc.hInstance = hInst;
wc.lpszClassName = "GobangAI_Optimized";
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
RegisterClassEx(&wc);
hWnd = CreateWindowEx(0, "GobangAI_Optimized", "五子棋 AI (R重置)", WS_OVERLAPPEDWINDOW, 100, 100, 620, 660, NULL, NULL, hInst, NULL);
ShowWindow(hWnd, SW_SHOW);
ResetGame(); // 初始化并让AI先手
MSG msg = {0};
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
这里空空如也



















有帮助,赞一个