专栏文章
棋类人机对战
休闲·娱乐参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @min0iwzf
- 此快照首次捕获于
- 2025/12/01 18:34 3 个月前
- 此快照最后确认于
- 2025/12/01 18:34 3 个月前
黑白棋
普通版
C普通可悔棋版
C#include <windows.h>
#include <stdio.h>
#define SCREENWIDTH 640
#define SCREENHALFWIDTH 320
#define SCREENHEIGHT 640
#define SCREENHALFHEIGHT 320
#define WIDTH 70
#define HEIGHT 70
#define HALFSIZE 30
#define MAPLEFT (SCREENHALFWIDTH - (WIDTH << 2))
#define MAPRIGHT (SCREENHALFWIDTH + (WIDTH << 2))
#define MAPTOP (SCREENHALFHEIGHT - (HEIGHT << 2))
#define MAPBOTTOM (SCREENHALFHEIGHT + (HEIGHT << 2))
// 8个方向的增量
const int dir[8][2] = {{-1, -1}, {0, -1}, {1, -1}, {-1, 0}, {1, 0}, {-1, 1}, {0, 1}, {1, 1}};
// 位置权重表
const int weights[8][8] = {
{ 500, -25, 10, 5, 5, 10, -25, 500},
{ -25, -45, 1, 1, 1, 1, -45, -25},
{ 10, 1, 3, 2, 2, 3, 1, 10},
{ 5, 1, 2, 1, 1, 2, 1, 5},
{ 5, 1, 2, 1, 1, 2, 1, 5},
{ 10, 1, 3, 2, 2, 3, 1, 10},
{ -25, -45, 1, 1, 1, 1, -45, -25},
{ 500, -25, 10, 5, 5, 10, -25, 500}
};
const RECT screen = {0, 0, SCREENWIDTH, SCREENHEIGHT};
// --- 全局变量 ---
int board[8][8];
// 悔棋堆栈:黑白棋最多60步,64足够
int history[64][8][8];
int step_count = 0;
int clickx, clicky;
int turn; // 1=黑(玩家), 2=白(电脑)
int winner; // 0=无, 1=黑, 2=白, 3=平局
// 函数声明
void computerMove(HWND hwnd);
BOOL hasValidMove(int player);
void checkGameOver();
ATOM registerWindow(HINSTANCE hInstance, WNDPROC WndProc, LPCSTR className){
WNDCLASSEXA wc;
memset(&wc, 0, sizeof(wc));
wc.cbSize = sizeof(WNDCLASSEXA);
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursorA(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszClassName = className;
wc.hIcon = LoadIconA(NULL, IDI_APPLICATION);
wc.hIconSm = LoadIconA(NULL, IDI_APPLICATION);
return RegisterClassExA(&wc);
}
BOOL createWindow(HINSTANCE hInstance, LPCSTR className, LPCSTR title, DWORD style, int x, int y, int width, int height){
return CreateWindowExA(WS_EX_CLIENTEDGE, className, title, style, x, y, width, height, NULL, NULL, hInstance, NULL) == NULL;
}
WPARAM mainloop(){
MSG msg;
while (GetMessageA(&msg, NULL, 0, 0)){
TranslateMessage(&msg);
DispatchMessageA(&msg);
}
return msg.wParam;
}
void maptoscreen(int *const x, int *const y, int clx, int cly){
*x = MAPLEFT + clx * WIDTH + (WIDTH >> 1);
*y = MAPTOP + cly * HEIGHT + (HEIGHT >> 1);
}
void paintmap(HDC hdc){
int i;
HBRUSH bgBrush = CreateSolidBrush(0x008000); // 深绿色背景
RECT mapRect = {MAPLEFT, MAPTOP, MAPRIGHT, MAPBOTTOM};
FillRect(hdc, &mapRect, bgBrush);
DeleteObject(bgBrush);
for (i = MAPLEFT; i <= MAPRIGHT; i += WIDTH){
MoveToEx(hdc, i, MAPTOP, NULL);
LineTo(hdc, i, MAPBOTTOM);
}
for (i = MAPTOP; i <= MAPBOTTOM; i += HEIGHT){
MoveToEx(hdc, MAPLEFT, i, NULL);
LineTo(hdc, MAPRIGHT, i);
}
}
void paintpieces(HDC hdc){
HBRUSH oldbrush, blackBrush, whiteBrush;
HPEN oldpen, nullPen;
int i, j, x, y;
blackBrush = CreateSolidBrush(0x000000);
whiteBrush = CreateSolidBrush(0xFFFFFF);
nullPen = CreatePen(PS_NULL, 0, 0);
oldpen = (HPEN)SelectObject(hdc, nullPen);
for (i = 0; i < 8; ++i){
for (j = 0; j < 8; ++j){
if (board[i][j] != 0) {
maptoscreen(&x, &y, i, j);
oldbrush = (HBRUSH)SelectObject(hdc, (board[i][j] == 1) ? blackBrush : whiteBrush);
Ellipse(hdc, x - HALFSIZE, y - HALFSIZE, x + HALFSIZE, y + HALFSIZE);
SelectObject(hdc, oldbrush);
}
}
}
SelectObject(hdc, oldpen);
DeleteObject(blackBrush);
DeleteObject(whiteBrush);
DeleteObject(nullPen);
}
void paintinfo(HDC hdc){
SetTextAlign(hdc, TA_CENTER);
SetBkMode(hdc, TRANSPARENT);
char buf[128];
if (winner) {
if (winner == 1) lstrcpyA(buf, "Black Wins! (Press 'Z' to Undo)");
else if (winner == 2) lstrcpyA(buf, "White Wins! (Press 'Z' to Undo)");
else lstrcpyA(buf, "Draw! (Press 'Z' to Undo)");
TextOutA(hdc, SCREENHALFWIDTH, 10, buf, lstrlenA(buf));
} else {
if (turn == 1) lstrcpyA(buf, "Your Turn (Black) - Press 'Z' to Undo");
else lstrcpyA(buf, "AI Thinking...");
TextOutA(hdc, SCREENHALFWIDTH, 10, buf, lstrlenA(buf));
}
}
void mainpaint(HDC hdc){
paintmap(hdc);
paintpieces(hdc);
paintinfo(hdc);
}
// 悔棋相关函数
void saveState() {
if (step_count < 64) {
memcpy(history[step_count], board, sizeof(board));
step_count++;
}
}
void undo() {
if (step_count > 0) {
step_count--;
memcpy(board, history[step_count], sizeof(board));
turn = 1; // 悔棋后强制回到玩家回合
winner = 0; // 重置胜负状态
}
}
BOOL isInside(int x, int y) {
return x >= 0 && x < 8 && y >= 0 && y < 8;
}
int executeMoveLogic(int tempBoard[8][8], int x, int y, int player, BOOL dryRun) {
if (tempBoard[x][y] != 0) return 0;
int opponent = 3 - player;
int totalFlipped = 0;
for (int k = 0; k < 8; k++) {
int dx = dir[k][0];
int dy = dir[k][1];
int nx = x + dx;
int ny = y + dy;
int flipped = 0;
while (isInside(nx, ny) && tempBoard[nx][ny] == opponent) {
nx += dx;
ny += dy;
flipped++;
}
if (flipped > 0 && isInside(nx, ny) && tempBoard[nx][ny] == player) {
totalFlipped += flipped;
if (!dryRun) {
int cx = x + dx;
int cy = y + dy;
while (cx != nx || cy != ny) {
tempBoard[cx][cy] = player;
cx += dx;
cy += dy;
}
}
}
}
if (!dryRun && totalFlipped > 0) {
tempBoard[x][y] = player;
}
return totalFlipped;
}
BOOL cango(int x, int y) {
return executeMoveLogic(board, x, y, turn, TRUE) > 0;
}
void makeMove(int x, int y, int player) {
executeMoveLogic(board, x, y, player, FALSE);
}
int getmouse(HWND hwnd, int *const x, int *const y){
*x = (*x - MAPLEFT) / WIDTH - (*x < MAPLEFT ? 1 : 0);
*y = (*y - MAPTOP) / HEIGHT - (*y < MAPTOP ? 1 : 0);
if (*x < 0 || *x >= 8 || *y < 0 || *y >= 8){
return 256;
}
if (!cango(*x, *y)){
return 16;
}
return 0;
}
// --- AI 部分 ---
int evaluateBoard(int b[8][8], int player) {
int score = 0;
int opponent = 3 - player;
for(int i=0; i<8; i++) {
for(int j=0; j<8; j++) {
if(b[i][j] == player) score += weights[i][j];
else if(b[i][j] == opponent) score -= weights[i][j];
}
}
return score;
}
int minimax(int b[8][8], int depth, int alpha, int beta, int currentPlayer) {
if (depth == 0) return evaluateBoard(b, 2);
int validMoves[64][2];
int moveCount = 0;
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
if (executeMoveLogic(b, i, j, currentPlayer, TRUE) > 0) {
validMoves[moveCount][0] = i;
validMoves[moveCount][1] = j;
moveCount++;
}
}
}
if (moveCount == 0) {
int opponent = 3 - currentPlayer;
BOOL opponentCanMove = FALSE;
for (int i = 0; i < 8; i++) for (int j = 0; j < 8; j++)
if (executeMoveLogic(b, i, j, opponent, TRUE) > 0) opponentCanMove = TRUE;
if (!opponentCanMove) {
int score = 0;
for(int i=0; i<8; i++) for(int j=0; j<8; j++) {
if(b[i][j] == 2) score++; else if(b[i][j] == 1) score--;
}
return score > 0 ? 10000 : (score < 0 ? -10000 : 0);
}
return minimax(b, depth, alpha, beta, 3 - currentPlayer);
}
int bestVal, tempBoard[8][8];
if (currentPlayer == 2) {
bestVal = -99999;
for (int k = 0; k < moveCount; k++) {
memcpy(tempBoard, b, sizeof(int)*64);
executeMoveLogic(tempBoard, validMoves[k][0], validMoves[k][1], currentPlayer, FALSE);
int val = minimax(tempBoard, depth - 1, alpha, beta, 1);
if (val > bestVal) bestVal = val;
if (val > alpha) alpha = val;
if (beta <= alpha) break;
}
} else {
bestVal = 99999;
for (int k = 0; k < moveCount; k++) {
memcpy(tempBoard, b, sizeof(int)*64);
executeMoveLogic(tempBoard, validMoves[k][0], validMoves[k][1], currentPlayer, FALSE);
int val = minimax(tempBoard, depth - 1, alpha, beta, 2);
if (val < bestVal) bestVal = val;
if (val < beta) beta = val;
if (beta <= alpha) break;
}
}
return bestVal;
}
void computerMove(HWND hwnd) {
int bestScore = -99999;
int bestMove[2] = {-1, -1};
int alpha = -99999;
int beta = 99999;
int depth = 6;
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
if (executeMoveLogic(board, i, j, 2, TRUE) > 0) {
int tempBoard[8][8];
memcpy(tempBoard, board, sizeof(board));
executeMoveLogic(tempBoard, i, j, 2, FALSE);
int score = minimax(tempBoard, depth - 1, alpha, beta, 1);
if (score > bestScore) {
bestScore = score;
bestMove[0] = i;
bestMove[1] = j;
}
if (score > alpha) alpha = score;
}
}
}
if (bestMove[0] != -1) {
makeMove(bestMove[0], bestMove[1], 2);
RECT xrect = {0,0,SCREENWIDTH, SCREENHEIGHT};
InvalidateRect(hwnd, &xrect, FALSE);
UpdateWindow(hwnd);
}
}
BOOL hasValidMove(int player) {
for (int i = 0; i < 8; i++) for (int j = 0; j < 8; j++)
if (executeMoveLogic(board, i, j, player, TRUE) > 0) return TRUE;
return FALSE;
}
void checkGameOver() {
int blackCount = 0, whiteCount = 0;
BOOL blackCanMove = hasValidMove(1);
BOOL whiteCanMove = hasValidMove(2);
if (!blackCanMove && !whiteCanMove) {
for(int i=0; i<8; i++) for(int j=0; j<8; j++) {
if(board[i][j] == 1) blackCount++;
else if(board[i][j] == 2) whiteCount++;
}
if (blackCount > whiteCount) winner = 1;
else if (whiteCount > blackCount) winner = 2;
else winner = 3;
}
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam){
switch (Message){
case WM_PAINT:{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hbmMem = CreateCompatibleBitmap(hdc, SCREENWIDTH, SCREENHEIGHT);
HBITMAP hbm = (HBITMAP)SelectObject(hdcMem, hbmMem);
HBRUSH background = CreateSolidBrush(0xbfbfbf);
FillRect(hdcMem, &screen, background);
mainpaint(hdcMem);
BitBlt(hdc, 0, 0, SCREENWIDTH, SCREENHEIGHT, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, hbm);
DeleteObject(hbmMem);
DeleteDC(hdcMem);
DeleteObject(background);
EndPaint(hwnd, &ps);
break;
}
case WM_CREATE:
turn = 1;
winner = 0;
step_count = 0; // 重置步数
memset(board, 0, sizeof(board));
board[3][3] = board[4][4] = 2;
board[3][4] = board[4][3] = 1;
break;
case WM_KEYDOWN: // 处理按键消息
if (wParam == 'Z') { // 按下 'Z' 键
// 只有在已走过步数且当前不是电脑思考中(虽然单线程卡住时也按不动)时撤回
if (step_count > 0 && turn == 1 || winner) {
undo();
RECT r = {0,0,SCREENWIDTH, SCREENHEIGHT};
InvalidateRect(hwnd, &r, FALSE);
}
}
break;
case WM_LBUTTONDOWN:
if (turn != 1 || winner) break;
clickx = LOWORD(lParam);
clicky = HIWORD(lParam);
if (getmouse(hwnd, &clickx, &clicky) == 0){
// 1. 在落子前保存当前状态
saveState();
makeMove(clickx, clicky, 1);
RECT updrect = {0, 0, SCREENWIDTH, SCREENHEIGHT};
InvalidateRect(hwnd, &updrect, FALSE);
UpdateWindow(hwnd);
turn = 2;
if (hasValidMove(2)) {
Sleep(100);
computerMove(hwnd);
if (!hasValidMove(1)) {
if (!hasValidMove(2)) {
checkGameOver();
} else {
while (hasValidMove(2) && !hasValidMove(1)) {
Sleep(500);
computerMove(hwnd);
checkGameOver();
if (winner) break;
}
if (!winner && hasValidMove(1)) turn = 1;
}
} else {
turn = 1;
}
} else {
MessageBoxA(hwnd, "AI Pass! (No valid moves)", "Info", MB_OK);
if (!hasValidMove(1)) checkGameOver();
else turn = 1;
}
checkGameOver();
InvalidateRect(hwnd, &updrect, FALSE);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, Message, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE _0, HINSTANCE _1, LPSTR _2, int _3){
RECT rcClient = {0, 0, SCREENWIDTH, SCREENHEIGHT};
const DWORD style = WS_OVERLAPPED | WS_CAPTION | WS_VISIBLE | WS_SYSMENU | WS_MINIMIZEBOX;
WPARAM wParam;
int realwidth, realheight;
if (!registerWindow(_0, WndProc, "Reversi")){
MessageBoxA(NULL, "Window Registration Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK);
return 0;
}
AdjustWindowRect(&rcClient, style, FALSE);
realwidth = rcClient.right - rcClient.left;
realheight = rcClient.bottom - rcClient.top;
if (createWindow(_0, "Reversi", "Reversi (Press Z to Undo)", style,
GetSystemMetrics(SM_CXSCREEN) - realwidth >> 1,
GetSystemMetrics(SM_CYSCREEN) - realheight >> 1,
realwidth, realheight)){
MessageBoxA(NULL, "Window Creation Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK);
return 0;
}
wParam = mainloop();
return wParam;
}
加强可悔棋版
C#include <windows.h>
#include <stdio.h>
#define SCREENWIDTH 640
#define SCREENHALFWIDTH 320
#define SCREENHEIGHT 640
#define SCREENHALFHEIGHT 320
#define WIDTH 70
#define HEIGHT 70
#define HALFSIZE 30
#define MAPLEFT (SCREENHALFWIDTH - (WIDTH << 2))
#define MAPRIGHT (SCREENHALFWIDTH + (WIDTH << 2))
#define MAPTOP (SCREENHALFHEIGHT - (HEIGHT << 2))
#define MAPBOTTOM (SCREENHALFHEIGHT + (HEIGHT << 2))
// 8个方向的增量
const int dir[8][2] = {{-1, -1}, {0, -1}, {1, -1}, {-1, 0}, {1, 0}, {-1, 1}, {0, 1}, {1, 1}};
// 位置权重表
const int weights[8][8] = {
{ 500, -25, 10, 5, 5, 10, -25, 500},
{ -25, -45, 1, 1, 1, 1, -45, -25},
{ 10, 1, 3, 2, 2, 3, 1, 10},
{ 5, 1, 2, 1, 1, 2, 1, 5},
{ 5, 1, 2, 1, 1, 2, 1, 5},
{ 10, 1, 3, 2, 2, 3, 1, 10},
{ -25, -45, 1, 1, 1, 1, -45, -25},
{ 500, -25, 10, 5, 5, 10, -25, 500}
};
const RECT screen = {0, 0, SCREENWIDTH, SCREENHEIGHT};
// --- 全局变量 ---
int board[8][8];
// 悔棋堆栈:黑白棋最多60步,64足够
int history[64][8][8];
int step_count = 0;
int clickx, clicky;
int turn; // 1=黑(玩家), 2=白(电脑)
int winner; // 0=无, 1=黑, 2=白, 3=平局
// 函数声明
void computerMove(HWND hwnd);
BOOL hasValidMove(int player);
void checkGameOver();
ATOM registerWindow(HINSTANCE hInstance, WNDPROC WndProc, LPCSTR className){
WNDCLASSEXA wc;
memset(&wc, 0, sizeof(wc));
wc.cbSize = sizeof(WNDCLASSEXA);
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursorA(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszClassName = className;
wc.hIcon = LoadIconA(NULL, IDI_APPLICATION);
wc.hIconSm = LoadIconA(NULL, IDI_APPLICATION);
return RegisterClassExA(&wc);
}
BOOL createWindow(HINSTANCE hInstance, LPCSTR className, LPCSTR title, DWORD style, int x, int y, int width, int height){
return CreateWindowExA(WS_EX_CLIENTEDGE, className, title, style, x, y, width, height, NULL, NULL, hInstance, NULL) == NULL;
}
WPARAM mainloop(){
MSG msg;
while (GetMessageA(&msg, NULL, 0, 0)){
TranslateMessage(&msg);
DispatchMessageA(&msg);
}
return msg.wParam;
}
void maptoscreen(int *const x, int *const y, int clx, int cly){
*x = MAPLEFT + clx * WIDTH + (WIDTH >> 1);
*y = MAPTOP + cly * HEIGHT + (HEIGHT >> 1);
}
void paintmap(HDC hdc){
int i;
HBRUSH bgBrush = CreateSolidBrush(0x008000); // 深绿色背景
RECT mapRect = {MAPLEFT, MAPTOP, MAPRIGHT, MAPBOTTOM};
FillRect(hdc, &mapRect, bgBrush);
DeleteObject(bgBrush);
for (i = MAPLEFT; i <= MAPRIGHT; i += WIDTH){
MoveToEx(hdc, i, MAPTOP, NULL);
LineTo(hdc, i, MAPBOTTOM);
}
for (i = MAPTOP; i <= MAPBOTTOM; i += HEIGHT){
MoveToEx(hdc, MAPLEFT, i, NULL);
LineTo(hdc, MAPRIGHT, i);
}
}
void paintpieces(HDC hdc){
HBRUSH oldbrush, blackBrush, whiteBrush;
HPEN oldpen, nullPen;
int i, j, x, y;
blackBrush = CreateSolidBrush(0x000000);
whiteBrush = CreateSolidBrush(0xFFFFFF);
nullPen = CreatePen(PS_NULL, 0, 0);
oldpen = (HPEN)SelectObject(hdc, nullPen);
for (i = 0; i < 8; ++i){
for (j = 0; j < 8; ++j){
if (board[i][j] != 0) {
maptoscreen(&x, &y, i, j);
oldbrush = (HBRUSH)SelectObject(hdc, (board[i][j] == 1) ? blackBrush : whiteBrush);
Ellipse(hdc, x - HALFSIZE, y - HALFSIZE, x + HALFSIZE, y + HALFSIZE);
SelectObject(hdc, oldbrush);
}
}
}
SelectObject(hdc, oldpen);
DeleteObject(blackBrush);
DeleteObject(whiteBrush);
DeleteObject(nullPen);
}
void paintinfo(HDC hdc){
SetTextAlign(hdc, TA_CENTER);
SetBkMode(hdc, TRANSPARENT);
char buf[128];
if (winner) {
if (winner == 1) lstrcpyA(buf, "Black Wins! (Press 'Z' to Undo)");
else if (winner == 2) lstrcpyA(buf, "White Wins! (Press 'Z' to Undo)");
else lstrcpyA(buf, "Draw! (Press 'Z' to Undo)");
TextOutA(hdc, SCREENHALFWIDTH, 10, buf, lstrlenA(buf));
} else {
if (turn == 1) lstrcpyA(buf, "Your Turn (Black) - Press 'Z' to Undo");
else lstrcpyA(buf, "AI Thinking...");
TextOutA(hdc, SCREENHALFWIDTH, 10, buf, lstrlenA(buf));
}
}
void mainpaint(HDC hdc){
paintmap(hdc);
paintpieces(hdc);
paintinfo(hdc);
}
// 悔棋相关函数
void saveState() {
if (step_count < 64) {
memcpy(history[step_count], board, sizeof(board));
step_count++;
}
}
void undo() {
if (step_count > 0) {
step_count--;
memcpy(board, history[step_count], sizeof(board));
turn = 1; // 悔棋后强制回到玩家回合
winner = 0; // 重置胜负状态
}
}
BOOL isInside(int x, int y) {
return x >= 0 && x < 8 && y >= 0 && y < 8;
}
int executeMoveLogic(int tempBoard[8][8], int x, int y, int player, BOOL dryRun) {
if (tempBoard[x][y] != 0) return 0;
int opponent = 3 - player;
int totalFlipped = 0;
for (int k = 0; k < 8; k++) {
int dx = dir[k][0];
int dy = dir[k][1];
int nx = x + dx;
int ny = y + dy;
int flipped = 0;
while (isInside(nx, ny) && tempBoard[nx][ny] == opponent) {
nx += dx;
ny += dy;
flipped++;
}
if (flipped > 0 && isInside(nx, ny) && tempBoard[nx][ny] == player) {
totalFlipped += flipped;
if (!dryRun) {
int cx = x + dx;
int cy = y + dy;
while (cx != nx || cy != ny) {
tempBoard[cx][cy] = player;
cx += dx;
cy += dy;
}
}
}
}
if (!dryRun && totalFlipped > 0) {
tempBoard[x][y] = player;
}
return totalFlipped;
}
BOOL cango(int x, int y) {
return executeMoveLogic(board, x, y, turn, TRUE) > 0;
}
void makeMove(int x, int y, int player) {
executeMoveLogic(board, x, y, player, FALSE);
}
int getmouse(HWND hwnd, int *const x, int *const y){
*x = (*x - MAPLEFT) / WIDTH - (*x < MAPLEFT ? 1 : 0);
*y = (*y - MAPTOP) / HEIGHT - (*y < MAPTOP ? 1 : 0);
if (*x < 0 || *x >= 8 || *y < 0 || *y >= 8){
return 256;
}
if (!cango(*x, *y)){
return 16;
}
return 0;
}
// --- 强化版 AI 部分 ---
// 辅助函数:计算某一方当前的合法走步数量(行动力)
int countValidMoves(int b[8][8], int player) {
int count = 0;
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
if (executeMoveLogic(b, i, j, player, TRUE) > 0) {
count++;
}
}
}
return count;
}
// 辅助函数:计算棋子总数
int countDiscs(int b[8][8]) {
int count = 0;
for(int i=0; i<8; i++)
for(int j=0; j<8; j++)
if(b[i][j] != 0) count++;
return count;
}
// 核心评估函数:比原版更聪明
// 策略:开局中局注重行动力和角,终局注重棋子数
int evaluateBoardStrong(int b[8][8], int player) {
int opponent = 3 - player;
int myTiles = 0, oppTiles = 0;
int myFrontier = 0, oppFrontier = 0;
int p, q, x, y, k;
int score = 0;
// 1. 基础权值分(沿用原来的位置权重,但降低比重)
for(int i=0; i<8; i++) {
for(int j=0; j<8; j++) {
if(b[i][j] == player) {
score += weights[i][j];
myTiles++;
} else if(b[i][j] == opponent) {
score -= weights[i][j];
oppTiles++;
}
// 计算边界子(Frontier Discs):如果棋子周围有空位,它是不稳定的
if (b[i][j] != 0) {
int isFrontier = 0;
for (k = 0; k < 8; k++) {
int nx = i + dir[k][0];
int ny = j + dir[k][1];
if (nx >= 0 && nx < 8 && ny >= 0 && ny < 8 && b[nx][ny] == 0) {
isFrontier = 1;
break;
}
}
if (isFrontier) {
if (b[i][j] == player) myFrontier++;
else oppFrontier++;
}
}
}
}
// 2. 阶段判断
int totalDiscs = myTiles + oppTiles;
// --- 终局逻辑 (64格占满前10步) ---
// 如果快结束了,直接算谁的棋子多,这就是“完美杀手”
if (totalDiscs > 54) {
return (myTiles - oppTiles) * 1000;
}
// --- 中盘逻辑 ---
// A. 行动力 (Mobility) - 最关键的改进点
// 你的对手如果只有很少的走法,他就很容易被迫走到坏位置(如C位或X位)
int myMoves = countValidMoves(b, player);
int oppMoves = countValidMoves(b, opponent);
// 权重大大增加,压制对手的行动力
score += (myMoves - oppMoves) * 300;
// B. 边界子惩罚
// 边界子越少越好,意味着你的棋子被包裹在内部,很难被翻转
score -= (myFrontier - oppFrontier) * 50;
// C. 角的安全性 (Corner Stability)
// 如果占了角,分数极高;如果没占角但占了角旁边的点(星位/X位),惩罚极高
// 原有的 weights 数组已经包含了一部分,这里额外加强对角的控制逻辑
int corners[4][2] = {{0,0}, {0,7}, {7,0}, {7,7}};
for(int i=0; i<4; i++) {
x = corners[i][0];
y = corners[i][1];
if (b[x][y] == player) {
score += 600; // 只要占了角,就给予巨额奖励
} else if (b[x][y] == opponent) {
score -= 600;
}
}
return score;
}
// 增强版 Minimax:带 Alpha-Beta 剪枝
int minimaxStrong(int b[8][8], int depth, int alpha, int beta, int currentPlayer, int maximizingPlayer) {
// 如果深度为0 或 游戏结束
if (depth == 0) {
return evaluateBoardStrong(b, maximizingPlayer);
}
// 1. 获取所有合法走步
int validMoves[64][2];
int moveCount = 0;
// 简单的移动排序优化:优先搜索角,这样能更快触发剪枝
// 检查角
int corners[4][2] = {{0,0}, {0,7}, {7,0}, {7,7}};
for (int k=0; k<4; k++) {
if (executeMoveLogic(b, corners[k][0], corners[k][1], currentPlayer, TRUE) > 0) {
validMoves[moveCount][0] = corners[k][0];
validMoves[moveCount][1] = corners[k][1];
moveCount++;
}
}
// 检查其他位置
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
// 避免重复添加角
if ((i==0||i==7) && (j==0||j==7)) continue;
if (executeMoveLogic(b, i, j, currentPlayer, TRUE) > 0) {
validMoves[moveCount][0] = i;
validMoves[moveCount][1] = j;
moveCount++;
}
}
}
// 2. 如果没有合法走步 (Pass)
if (moveCount == 0) {
int opponent = 3 - currentPlayer;
// 检查对手是否能走
if (countValidMoves(b, opponent) == 0) {
// 双方都不能走,游戏结束,计算最终分数
int finalScore = 0;
for(int i=0; i<8; i++) for(int j=0; j<8; j++) {
if(b[i][j] == maximizingPlayer) finalScore++;
else if(b[i][j] != 0) finalScore--;
}
if (finalScore > 0) return 100000 + finalScore; // 赢
if (finalScore < 0) return -100000 + finalScore; // 输
return 0; // 平
}
// 对手走,深度不减(为了防止深度耗尽看不到结局)
return minimaxStrong(b, depth, alpha, beta, opponent, maximizingPlayer);
}
int tempBoard[8][8];
int bestVal;
// 3. 递归搜索
if (currentPlayer == maximizingPlayer) { // 我方回合(找最大值)
bestVal = -999999;
for (int k = 0; k < moveCount; k++) {
memcpy(tempBoard, b, sizeof(int)*64);
executeMoveLogic(tempBoard, validMoves[k][0], validMoves[k][1], currentPlayer, FALSE);
int val = minimaxStrong(tempBoard, depth - 1, alpha, beta, 3 - currentPlayer, maximizingPlayer);
if (val > bestVal) bestVal = val;
if (val > alpha) alpha = val;
if (beta <= alpha) break; // 剪枝
}
} else { // 敌方回合(找最小值)
bestVal = 999999;
for (int k = 0; k < moveCount; k++) {
memcpy(tempBoard, b, sizeof(int)*64);
executeMoveLogic(tempBoard, validMoves[k][0], validMoves[k][1], currentPlayer, FALSE);
int val = minimaxStrong(tempBoard, depth - 1, alpha, beta, 3 - currentPlayer, maximizingPlayer);
if (val < bestVal) bestVal = val;
if (val < beta) beta = val;
if (beta <= alpha) break; // 剪枝
}
}
return bestVal;
}
void computerMove(HWND hwnd) {
int bestScore = -999999;
int bestMove[2] = {-1, -1};
int alpha = -999999;
int beta = 999999;
// 动态深度:开局简单点,中局深一点,终局更深
int discs = countDiscs(board);
int depth = 6;
if (discs > 48) depth = 10; // 终局搜索加深
// 为了提高效率,这里也可以做一次简单的Move Ordering
int validMoves[64][2];
int moveCount = 0;
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
if (executeMoveLogic(board, i, j, 2, TRUE) > 0) {
validMoves[moveCount][0] = i;
validMoves[moveCount][1] = j;
moveCount++;
}
}
}
for (int k = 0; k < moveCount; k++) {
int i = validMoves[k][0];
int j = validMoves[k][1];
int tempBoard[8][8];
memcpy(tempBoard, board, sizeof(board));
executeMoveLogic(tempBoard, i, j, 2, FALSE);
// 调用增强版 minimax,当前是白棋(2),maximizingPlayer也是2
int score = minimaxStrong(tempBoard, depth - 1, alpha, beta, 1, 2);
// 增加一点随机性打破平局(仅在分数相同时),让AI走棋稍微多样化一点
if (score > bestScore) {
bestScore = score;
bestMove[0] = i;
bestMove[1] = j;
}
if (score > alpha) alpha = score;
}
if (bestMove[0] != -1) {
makeMove(bestMove[0], bestMove[1], 2);
RECT xrect = {0,0,SCREENWIDTH, SCREENHEIGHT};
InvalidateRect(hwnd, &xrect, FALSE);
UpdateWindow(hwnd);
}
}
BOOL hasValidMove(int player) {
for (int i = 0; i < 8; i++) for (int j = 0; j < 8; j++)
if (executeMoveLogic(board, i, j, player, TRUE) > 0) return TRUE;
return FALSE;
}
void checkGameOver() {
int blackCount = 0, whiteCount = 0;
BOOL blackCanMove = hasValidMove(1);
BOOL whiteCanMove = hasValidMove(2);
if (!blackCanMove && !whiteCanMove) {
for(int i=0; i<8; i++) for(int j=0; j<8; j++) {
if(board[i][j] == 1) blackCount++;
else if(board[i][j] == 2) whiteCount++;
}
if (blackCount > whiteCount) winner = 1;
else if (whiteCount > blackCount) winner = 2;
else winner = 3;
}
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam){
switch (Message){
case WM_PAINT:{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hbmMem = CreateCompatibleBitmap(hdc, SCREENWIDTH, SCREENHEIGHT);
HBITMAP hbm = (HBITMAP)SelectObject(hdcMem, hbmMem);
HBRUSH background = CreateSolidBrush(0xbfbfbf);
FillRect(hdcMem, &screen, background);
mainpaint(hdcMem);
BitBlt(hdc, 0, 0, SCREENWIDTH, SCREENHEIGHT, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, hbm);
DeleteObject(hbmMem);
DeleteDC(hdcMem);
DeleteObject(background);
EndPaint(hwnd, &ps);
break;
}
case WM_CREATE:
turn = 1;
winner = 0;
step_count = 0; // 重置步数
memset(board, 0, sizeof(board));
board[3][3] = board[4][4] = 2;
board[3][4] = board[4][3] = 1;
break;
case WM_KEYDOWN: // 处理按键消息
if (wParam == 'Z') { // 按下 'Z' 键
// 只有在已走过步数且当前不是电脑思考中(虽然单线程卡住时也按不动)时撤回
if (step_count > 0 && turn == 1 || winner) {
undo();
RECT r = {0,0,SCREENWIDTH, SCREENHEIGHT};
InvalidateRect(hwnd, &r, FALSE);
}
}
break;
case WM_LBUTTONDOWN:
if (turn != 1 || winner) break;
clickx = LOWORD(lParam);
clicky = HIWORD(lParam);
if (getmouse(hwnd, &clickx, &clicky) == 0){
// 1. 在落子前保存当前状态
saveState();
makeMove(clickx, clicky, 1);
RECT updrect = {0, 0, SCREENWIDTH, SCREENHEIGHT};
InvalidateRect(hwnd, &updrect, FALSE);
UpdateWindow(hwnd);
turn = 2;
if (hasValidMove(2)) {
Sleep(100);
computerMove(hwnd);
if (!hasValidMove(1)) {
if (!hasValidMove(2)) {
checkGameOver();
} else {
while (hasValidMove(2) && !hasValidMove(1)) {
Sleep(500);
computerMove(hwnd);
checkGameOver();
if (winner) break;
}
if (!winner && hasValidMove(1)) turn = 1;
}
} else {
turn = 1;
}
} else {
MessageBoxA(hwnd, "AI Pass! (No valid moves)", "Info", MB_OK);
if (!hasValidMove(1)) checkGameOver();
else turn = 1;
}
checkGameOver();
InvalidateRect(hwnd, &updrect, FALSE);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, Message, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE _0, HINSTANCE _1, LPSTR _2, int _3){
RECT rcClient = {0, 0, SCREENWIDTH, SCREENHEIGHT};
const DWORD style = WS_OVERLAPPED | WS_CAPTION | WS_VISIBLE | WS_SYSMENU | WS_MINIMIZEBOX;
WPARAM wParam;
int realwidth, realheight;
if (!registerWindow(_0, WndProc, "Reversi")){
MessageBoxA(NULL, "Window Registration Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK);
return 0;
}
AdjustWindowRect(&rcClient, style, FALSE);
realwidth = rcClient.right - rcClient.left;
realheight = rcClient.bottom - rcClient.top;
if (createWindow(_0, "Reversi", "Reversi (Press Z to Undo)", style,
GetSystemMetrics(SM_CXSCREEN) - realwidth >> 1,
GetSystemMetrics(SM_CYSCREEN) - realheight >> 1,
realwidth, realheight)){
MessageBoxA(NULL, "Window Creation Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK);
return 0;
}
wParam = mainloop();
return wParam;
}
加强加强版与加强版对战演示
C#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#define SCREENWIDTH 640
#define SCREENHALFWIDTH 320
#define SCREENHEIGHT 640
#define SCREENHALFHEIGHT 320
#define WIDTH 70
#define HEIGHT 70
#define HALFSIZE 30
#define MAPLEFT (SCREENHALFWIDTH - (WIDTH << 2))
#define MAPRIGHT (SCREENHALFWIDTH + (WIDTH << 2))
#define MAPTOP (SCREENHALFHEIGHT - (HEIGHT << 2))
#define MAPBOTTOM (SCREENHALFHEIGHT + (HEIGHT << 2))
// 8个方向的增量
const int dir[8][2] = {{-1, -1}, {0, -1}, {1, -1}, {-1, 0}, {1, 0}, {-1, 1}, {0, 1}, {1, 1}};
// 基础位置权重表 (White AI uses this)
const int weights[8][8] = {
{ 500, -25, 10, 5, 5, 10, -25, 500},
{ -25, -45, 1, 1, 1, 1, -45, -25},
{ 10, 1, 3, 2, 2, 3, 1, 10},
{ 5, 1, 2, 1, 1, 2, 1, 5},
{ 5, 1, 2, 1, 1, 2, 1, 5},
{ 10, 1, 3, 2, 2, 3, 1, 10},
{ -25, -45, 1, 1, 1, 1, -45, -25},
{ 500, -25, 10, 5, 5, 10, -25, 500}
};
const RECT screen = {0, 0, SCREENWIDTH, SCREENHEIGHT};
// --- 全局变量 ---
int board[8][8];
int history[64][8][8];
int step_count = 0;
int turn; // 1=黑(超级AI), 2=白(原版AI)
int winner; // 0=无, 1=黑, 2=白, 3=平局
// 函数声明
void computerMove_White(HWND hwnd); // 原版后手算法
void computerMove_Black(HWND hwnd); // 新版先手算法
BOOL hasValidMove(int player);
void checkGameOver();
ATOM registerWindow(HINSTANCE hInstance, WNDPROC WndProc, LPCSTR className){
WNDCLASSEXA wc;
memset(&wc, 0, sizeof(wc));
wc.cbSize = sizeof(WNDCLASSEXA);
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursorA(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszClassName = className;
wc.hIcon = LoadIconA(NULL, IDI_APPLICATION);
wc.hIconSm = LoadIconA(NULL, IDI_APPLICATION);
return RegisterClassExA(&wc);
}
BOOL createWindow(HINSTANCE hInstance, LPCSTR className, LPCSTR title, DWORD style, int x, int y, int width, int height){
return CreateWindowExA(WS_EX_CLIENTEDGE, className, title, style, x, y, width, height, NULL, NULL, hInstance, NULL) == NULL;
}
WPARAM mainloop(){
MSG msg;
while (GetMessageA(&msg, NULL, 0, 0)){
TranslateMessage(&msg);
DispatchMessageA(&msg);
}
return msg.wParam;
}
void maptoscreen(int *const x, int *const y, int clx, int cly){
*x = MAPLEFT + clx * WIDTH + (WIDTH >> 1);
*y = MAPTOP + cly * HEIGHT + (HEIGHT >> 1);
}
void paintmap(HDC hdc){
int i;
HBRUSH bgBrush = CreateSolidBrush(0x008000);
RECT mapRect = {MAPLEFT, MAPTOP, MAPRIGHT, MAPBOTTOM};
FillRect(hdc, &mapRect, bgBrush);
DeleteObject(bgBrush);
for (i = MAPLEFT; i <= MAPRIGHT; i += WIDTH){
MoveToEx(hdc, i, MAPTOP, NULL);
LineTo(hdc, i, MAPBOTTOM);
}
for (i = MAPTOP; i <= MAPBOTTOM; i += HEIGHT){
MoveToEx(hdc, MAPLEFT, i, NULL);
LineTo(hdc, MAPRIGHT, i);
}
}
void paintpieces(HDC hdc){
HBRUSH oldbrush, blackBrush, whiteBrush;
HPEN oldpen, nullPen;
int i, j, x, y;
blackBrush = CreateSolidBrush(0x000000);
whiteBrush = CreateSolidBrush(0xFFFFFF);
nullPen = CreatePen(PS_NULL, 0, 0);
oldpen = (HPEN)SelectObject(hdc, nullPen);
for (i = 0; i < 8; ++i){
for (j = 0; j < 8; ++j){
if (board[i][j] != 0) {
maptoscreen(&x, &y, i, j);
oldbrush = (HBRUSH)SelectObject(hdc, (board[i][j] == 1) ? blackBrush : whiteBrush);
Ellipse(hdc, x - HALFSIZE, y - HALFSIZE, x + HALFSIZE, y + HALFSIZE);
SelectObject(hdc, oldbrush);
}
}
}
SelectObject(hdc, oldpen);
DeleteObject(blackBrush);
DeleteObject(whiteBrush);
DeleteObject(nullPen);
}
void paintinfo(HDC hdc){
SetTextAlign(hdc, TA_CENTER);
SetBkMode(hdc, TRANSPARENT);
char buf[128];
if (winner) {
if (winner == 1) lstrcpyA(buf, "Black (Super AI) Wins!");
else if (winner == 2) lstrcpyA(buf, "White (Original AI) Wins!");
else lstrcpyA(buf, "Draw!");
TextOutA(hdc, SCREENHALFWIDTH, 10, buf, lstrlenA(buf));
} else {
if (turn == 1) lstrcpyA(buf, "Black (Super AI) Thinking...");
else lstrcpyA(buf, "White (Original AI) Thinking...");
TextOutA(hdc, SCREENHALFWIDTH, 10, buf, lstrlenA(buf));
}
}
void mainpaint(HDC hdc){
paintmap(hdc);
paintpieces(hdc);
paintinfo(hdc);
}
BOOL isInside(int x, int y) {
return x >= 0 && x < 8 && y >= 0 && y < 8;
}
int executeMoveLogic(int tempBoard[8][8], int x, int y, int player, BOOL dryRun) {
if (tempBoard[x][y] != 0) return 0;
int opponent = 3 - player;
int totalFlipped = 0;
for (int k = 0; k < 8; k++) {
int dx = dir[k][0];
int dy = dir[k][1];
int nx = x + dx;
int ny = y + dy;
int flipped = 0;
while (isInside(nx, ny) && tempBoard[nx][ny] == opponent) {
nx += dx;
ny += dy;
flipped++;
}
if (flipped > 0 && isInside(nx, ny) && tempBoard[nx][ny] == player) {
totalFlipped += flipped;
if (!dryRun) {
int cx = x + dx;
int cy = y + dy;
while (cx != nx || cy != ny) {
tempBoard[cx][cy] = player;
cx += dx;
cy += dy;
}
}
}
}
if (!dryRun && totalFlipped > 0) {
tempBoard[x][y] = player;
}
return totalFlipped;
}
void makeMove(int x, int y, int player) {
executeMoveLogic(board, x, y, player, FALSE);
}
int countValidMoves(int b[8][8], int player) {
int count = 0;
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++)
if (executeMoveLogic(b, i, j, player, TRUE) > 0) count++;
return count;
}
int countDiscs(int b[8][8]) {
int count = 0;
for(int i=0; i<8; i++)
for(int j=0; j<8; j++)
if(b[i][j] != 0) count++;
return count;
}
// -------------------------------------------------------------
// 原版白棋 AI (保持不变)
// -------------------------------------------------------------
int evaluateBoardOriginal(int b[8][8], int player) {
int opponent = 3 - player;
int myTiles = 0, oppTiles = 0, myFrontier = 0, oppFrontier = 0, score = 0;
for(int i=0; i<8; i++) {
for(int j=0; j<8; j++) {
if(b[i][j] == player) { score += weights[i][j]; myTiles++; }
else if(b[i][j] == opponent) { score -= weights[i][j]; oppTiles++; }
if (b[i][j] != 0) {
int isFrontier = 0;
for (int k = 0; k < 8; k++) {
int nx = i + dir[k][0];
int ny = j + dir[k][1];
if (nx >= 0 && nx < 8 && ny >= 0 && ny < 8 && b[nx][ny] == 0) {
isFrontier = 1; break;
}
}
if (isFrontier) { if (b[i][j] == player) myFrontier++; else oppFrontier++; }
}
}
}
int totalDiscs = myTiles + oppTiles;
if (totalDiscs > 54) return (myTiles - oppTiles) * 1000;
int myMoves = countValidMoves(b, player);
int oppMoves = countValidMoves(b, opponent);
score += (myMoves - oppMoves) * 300;
score -= (myFrontier - oppFrontier) * 50;
int corners[4][2] = {{0,0}, {0,7}, {7,0}, {7,7}};
for(int i=0; i<4; i++) {
if (b[corners[i][0]][corners[i][1]] == player) score += 600;
else if (b[corners[i][0]][corners[i][1]] == opponent) score -= 600;
}
return score;
}
int minimaxOriginal(int b[8][8], int depth, int alpha, int beta, int currentPlayer, int maximizingPlayer) {
if (depth == 0) return evaluateBoardOriginal(b, maximizingPlayer);
int validMoves[64][2];
int moveCount = 0;
int corners[4][2] = {{0,0}, {0,7}, {7,0}, {7,7}};
for (int k=0; k<4; k++) {
if (executeMoveLogic(b, corners[k][0], corners[k][1], currentPlayer, TRUE) > 0) {
validMoves[moveCount][0] = corners[k][0]; validMoves[moveCount][1] = corners[k][1]; moveCount++;
}
}
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
if ((i==0||i==7) && (j==0||j==7)) continue;
if (executeMoveLogic(b, i, j, currentPlayer, TRUE) > 0) {
validMoves[moveCount][0] = i; validMoves[moveCount][1] = j; moveCount++;
}
}
}
if (moveCount == 0) {
if (countValidMoves(b, 3 - currentPlayer) == 0) {
int finalScore = 0;
for(int i=0; i<8; i++) for(int j=0; j<8; j++) {
if(b[i][j] == maximizingPlayer) finalScore++; else if(b[i][j] != 0) finalScore--;
}
if (finalScore > 0) return 100000 + finalScore;
if (finalScore < 0) return -100000 + finalScore;
return 0;
}
return minimaxOriginal(b, depth, alpha, beta, 3 - currentPlayer, maximizingPlayer);
}
int tempBoard[8][8];
int bestVal = (currentPlayer == maximizingPlayer) ? -999999 : 999999;
for (int k = 0; k < moveCount; k++) {
memcpy(tempBoard, b, sizeof(int)*64);
executeMoveLogic(tempBoard, validMoves[k][0], validMoves[k][1], currentPlayer, FALSE);
int val = minimaxOriginal(tempBoard, depth - 1, alpha, beta, 3 - currentPlayer, maximizingPlayer);
if (currentPlayer == maximizingPlayer) {
if (val > bestVal) bestVal = val;
if (val > alpha) alpha = val;
} else {
if (val < bestVal) bestVal = val;
if (val < beta) beta = val;
}
if (beta <= alpha) break;
}
return bestVal;
}
void computerMove_White(HWND hwnd) {
int bestScore = -999999;
int bestMove[2] = {-1, -1};
int alpha = -999999;
int beta = 999999;
int discs = countDiscs(board);
int depth = 6;
if (discs > 48) depth = 10;
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
if (executeMoveLogic(board, i, j, 2, TRUE) > 0) {
int tempBoard[8][8];
memcpy(tempBoard, board, sizeof(board));
executeMoveLogic(tempBoard, i, j, 2, FALSE);
int score = minimaxOriginal(tempBoard, depth - 1, alpha, beta, 1, 2);
if (score > bestScore) {
bestScore = score; bestMove[0] = i; bestMove[1] = j;
}
if (score > alpha) alpha = score;
}
}
}
if (bestMove[0] != -1) {
makeMove(bestMove[0], bestMove[1], 2);
RECT xrect = {0,0,SCREENWIDTH, SCREENHEIGHT};
InvalidateRect(hwnd, &xrect, FALSE);
UpdateWindow(hwnd);
}
}
// -------------------------------------------------------------
// 升级版黑棋 AI (先手) - 设计用于击败白棋
// -------------------------------------------------------------
// 1. 动态位置估值:如果角被占据,周围的坏点就会变成好点
int getDynamicWeight(int b[8][8], int x, int y) {
int w = weights[x][y];
// 左上角逻辑
if (x <= 1 && y <= 1) {
if (b[0][0] != 0) return 50; // 角被占,临近点不再是-25/-45,而是正分
}
// 右上角
else if (x >= 6 && y <= 1) {
if (b[7][0] != 0) return 50;
}
// 左下角
else if (x <= 1 && y >= 6) {
if (b[0][7] != 0) return 50;
}
// 右下角
else if (x >= 6 && y >= 6) {
if (b[7][7] != 0) return 50;
}
return w;
}
// 2. 超级估值函数
int evaluateBoardBlack(int b[8][8], int player) {
int opponent = 3 - player;
int myTiles = 0, oppTiles = 0;
int myFrontier = 0, oppFrontier = 0;
int score = 0;
// 基础计数
for(int i=0; i<8; i++) {
for(int j=0; j<8; j++) {
if (b[i][j] == 0) continue;
// 动态位置分
int w = getDynamicWeight(b, i, j);
if(b[i][j] == player) { score += w; myTiles++; }
else { score -= w; oppTiles++; }
// 边界子判定
int isFrontier = 0;
for (int k = 0; k < 8; k++) {
int nx = i + dir[k][0]; int ny = j + dir[k][1];
if (nx >= 0 && nx < 8 && ny >= 0 && ny < 8 && b[nx][ny] == 0) {
isFrontier = 1; break;
}
}
if (isFrontier) { if (b[i][j] == player) myFrontier++; else oppFrontier++; }
}
}
int totalDiscs = myTiles + oppTiles;
// --- 终局:完美算子 ---
if (totalDiscs > 58) return (myTiles - oppTiles) * 10000;
// --- 行动力压制 ---
int myMoves = countValidMoves(b, player);
int oppMoves = countValidMoves(b, opponent);
// 关键策略:Evaporation(蒸发)。前期尽量让对方子多,自己子少,但要是安全子
// 黑棋 AI 极度看重“相对行动力”
// 在中局,让对手无路可走比自己得分更重要
score += (myMoves - oppMoves) * 500;
// 边界惩罚加强,迫使黑棋下在内部
score -= (myFrontier - oppFrontier) * 100;
// --- 奇偶性 Parity ---
// 剩余空位为偶数时,作为当前行动方(黑棋),如果能抢占最后一个区域的先手是劣势,
// 但通常我们希望控制Parity。这里简化为:如果在最后几个空位能保持主动
if (totalDiscs > 45) {
score += (myTiles - oppTiles) * 10; // 后期逐渐转为重视子数
} else {
// 前期不要太多子
score -= (myTiles - oppTiles) * 5;
}
// --- 角的绝对控制 ---
int corners[4][2] = {{0,0}, {0,7}, {7,0}, {7,7}};
for(int i=0; i<4; i++) {
if (b[corners[i][0]][corners[i][1]] == player) score += 1000; // 比白棋更重视角
else if (b[corners[i][0]][corners[i][1]] == opponent) score -= 1000;
}
return score;
}
// 结构体用于移动排序
typedef struct {
int x, y;
int score;
} MoveScore;
int compareMoves(const void *a, const void *b) {
return ((MoveScore*)b)->score - ((MoveScore*)a)->score; // 降序
}
// 升级版 Minimax:带移动排序和更深搜索
int minimaxBlack(int b[8][8], int depth, int alpha, int beta, int currentPlayer, int maximizingPlayer) {
if (depth == 0) return evaluateBoardBlack(b, maximizingPlayer);
int opponent = 3 - currentPlayer;
MoveScore moves[64];
int moveCount = 0;
// 1. 生成走法并进行启发式评分(为了排序)
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
if (executeMoveLogic(b, i, j, currentPlayer, TRUE) > 0) {
moves[moveCount].x = i;
moves[moveCount].y = j;
// 启发式评分:角最高,C/X位最低,减少对方行动力最高
int score = getDynamicWeight(b, i, j);
// 简单的静态预判:如果这步棋让对方无路可走,加分
// (为了性能,这里不进行完整的递归模拟,只用静态表辅助)
moves[moveCount].score = score;
moveCount++;
}
}
}
if (moveCount == 0) {
if (countValidMoves(b, opponent) == 0) {
int finalScore = 0;
for(int i=0; i<8; i++) for(int j=0; j<8; j++) {
if(b[i][j] == maximizingPlayer) finalScore++; else if(b[i][j] != 0) finalScore--;
}
if (finalScore > 0) return 200000 + finalScore;
if (finalScore < 0) return -200000 + finalScore;
return 0;
}
return minimaxBlack(b, depth, alpha, beta, opponent, maximizingPlayer);
}
// 2. 对走法排序:好的走法先搜,极大提高剪枝效率
qsort(moves, moveCount, sizeof(MoveScore), compareMoves);
int tempBoard[8][8];
int bestVal = (currentPlayer == maximizingPlayer) ? -9999999 : 9999999;
for (int k = 0; k < moveCount; k++) {
memcpy(tempBoard, b, sizeof(int)*64);
executeMoveLogic(tempBoard, moves[k].x, moves[k].y, currentPlayer, FALSE);
int val = minimaxBlack(tempBoard, depth - 1, alpha, beta, opponent, maximizingPlayer);
if (currentPlayer == maximizingPlayer) {
if (val > bestVal) bestVal = val;
if (val > alpha) alpha = val;
} else {
if (val < bestVal) bestVal = val;
if (val < beta) beta = val;
}
if (beta <= alpha) break;
}
return bestVal;
}
void computerMove_Black(HWND hwnd) {
int bestScore = -9999999;
int bestMove[2] = {-1, -1};
int alpha = -9999999;
int beta = 9999999;
int discs = countDiscs(board);
// 黑棋搜索深度更深:中局8层,终局12层 (原版只有6和10)
// 得益于移动排序,这通常是可行的
int depth = 8;
if (discs > 50) depth = 12;
// 同样应用移动排序到第一层
MoveScore moves[64];
int moveCount = 0;
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
if (executeMoveLogic(board, i, j, 1, TRUE) > 0) {
moves[moveCount].x = i;
moves[moveCount].y = j;
moves[moveCount].score = getDynamicWeight(board, i, j);
moveCount++;
}
}
}
qsort(moves, moveCount, sizeof(MoveScore), compareMoves);
for (int k = 0; k < moveCount; k++) {
int i = moves[k].x;
int j = moves[k].y;
int tempBoard[8][8];
memcpy(tempBoard, board, sizeof(board));
executeMoveLogic(tempBoard, i, j, 1, FALSE);
// 黑棋是1,maximizingPlayer也是1
int score = minimaxBlack(tempBoard, depth - 1, alpha, beta, 2, 1);
if (score > bestScore) {
bestScore = score;
bestMove[0] = i;
bestMove[1] = j;
}
if (score > alpha) alpha = score;
// 稍微更新界面以免卡死感
if (k==0) {
RECT r = {0,0,10,10}; InvalidateRect(hwnd, &r, FALSE); UpdateWindow(hwnd);
}
}
if (bestMove[0] != -1) {
makeMove(bestMove[0], bestMove[1], 1);
RECT xrect = {0,0,SCREENWIDTH, SCREENHEIGHT};
InvalidateRect(hwnd, &xrect, FALSE);
UpdateWindow(hwnd);
}
}
// -------------------------------------------------------------
// 游戏主逻辑
// -------------------------------------------------------------
BOOL hasValidMove(int player) {
for (int i = 0; i < 8; i++) for (int j = 0; j < 8; j++)
if (executeMoveLogic(board, i, j, player, TRUE) > 0) return TRUE;
return FALSE;
}
void checkGameOver() {
if (!hasValidMove(1) && !hasValidMove(2)) {
int blackCount = 0, whiteCount = 0;
for(int i=0; i<8; i++) for(int j=0; j<8; j++) {
if(board[i][j] == 1) blackCount++;
else if(board[i][j] == 2) whiteCount++;
}
if (blackCount > whiteCount) winner = 1;
else if (whiteCount > blackCount) winner = 2;
else winner = 3;
}
}
// 处理回合逻辑
void processTurns(HWND hwnd) {
if (winner) return;
// 黑棋(AI)回合
if (turn == 1) {
if (hasValidMove(1)) {
Sleep(200); // 稍微停顿一下
computerMove_Black(hwnd);
turn = 2;
} else {
// 黑棋无棋可走
if (hasValidMove(2)) turn = 2;
else checkGameOver();
}
}
// 检查游戏是否结束,如果没结束则继续白棋
checkGameOver();
if (winner) { InvalidateRect(hwnd, NULL, FALSE); return; }
// 白棋(原版AI)回合
if (turn == 2) {
if (hasValidMove(2)) {
Sleep(200);
computerMove_White(hwnd);
turn = 1;
} else {
// 白棋无棋可走
if (hasValidMove(1)) turn = 1;
else checkGameOver();
}
}
checkGameOver();
InvalidateRect(hwnd, NULL, FALSE);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam){
switch (Message){
case WM_PAINT:{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hbmMem = CreateCompatibleBitmap(hdc, SCREENWIDTH, SCREENHEIGHT);
HBITMAP hbm = (HBITMAP)SelectObject(hdcMem, hbmMem);
HBRUSH background = CreateSolidBrush(0xbfbfbf);
FillRect(hdcMem, &screen, background);
mainpaint(hdcMem);
BitBlt(hdc, 0, 0, SCREENWIDTH, SCREENHEIGHT, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, hbm);
DeleteObject(hbmMem);
DeleteDC(hdcMem);
DeleteObject(background);
EndPaint(hwnd, &ps);
// 触发自动对弈
if (!winner) {
// 通过发送自定义消息或Timer来驱动AI,避免阻塞UI太久
// 这里简单处理:如果不是重绘导致的循环,可以延迟执行
// 但为了简单,我们在 Timer 里处理
}
break;
}
case WM_CREATE:
turn = 1; // 黑棋先走
winner = 0;
step_count = 0;
memset(board, 0, sizeof(board));
board[3][3] = board[4][4] = 2;
board[3][4] = board[4][3] = 1;
SetTimer(hwnd, 1, 500, NULL); // 设置定时器驱动AI
break;
case WM_TIMER:
if (!winner) {
processTurns(hwnd);
}
break;
case WM_KEYDOWN:
if (wParam == 'R') { // Press R to reset
turn = 1; winner = 0; step_count = 0;
memset(board, 0, sizeof(board));
board[3][3] = board[4][4] = 2;
board[3][4] = board[4][3] = 1;
InvalidateRect(hwnd, NULL, FALSE);
}
break;
case WM_DESTROY:
KillTimer(hwnd, 1);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, Message, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE _0, HINSTANCE _1, LPSTR _2, int _3){
RECT rcClient = {0, 0, SCREENWIDTH, SCREENHEIGHT};
const DWORD style = WS_OVERLAPPED | WS_CAPTION | WS_VISIBLE | WS_SYSMENU | WS_MINIMIZEBOX;
if (!registerWindow(_0, WndProc, "ReversiAI")) return 0;
AdjustWindowRect(&rcClient, style, FALSE);
int w = rcClient.right - rcClient.left;
int h = rcClient.bottom - rcClient.top;
if (createWindow(_0, "ReversiAI", "Black (Super) vs White (Old)", style,
GetSystemMetrics(SM_CXSCREEN)/2 - w/2, GetSystemMetrics(SM_CYSCREEN)/2 - h/2, w, h)) return 0;
return mainloop();
}
加强加强可悔棋重开版
C#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#define SCREENWIDTH 640
#define SCREENHALFWIDTH 320
#define SCREENHEIGHT 640
#define SCREENHALFHEIGHT 320
#define WIDTH 70
#define HEIGHT 70
#define HALFSIZE 30
#define MAPLEFT (SCREENHALFWIDTH - (WIDTH << 2))
#define MAPRIGHT (SCREENHALFWIDTH + (WIDTH << 2))
#define MAPTOP (SCREENHALFHEIGHT - (HEIGHT << 2))
#define MAPBOTTOM (SCREENHALFHEIGHT + (HEIGHT << 2))
// 8个方向
const int dir[8][2] = {{-1, -1}, {0, -1}, {1, -1}, {-1, 0}, {1, 0}, {-1, 1}, {0, 1}, {1, 1}};
// 基础权重表
const int weights[8][8] = {
{ 500, -25, 10, 5, 5, 10, -25, 500},
{ -25, -45, 1, 1, 1, 1, -45, -25},
{ 10, 1, 3, 2, 2, 3, 1, 10},
{ 5, 1, 2, 1, 1, 2, 1, 5},
{ 5, 1, 2, 1, 1, 2, 1, 5},
{ 10, 1, 3, 2, 2, 3, 1, 10},
{ -25, -45, 1, 1, 1, 1, -45, -25},
{ 500, -25, 10, 5, 5, 10, -25, 500}
};
const RECT screen = {0, 0, SCREENWIDTH, SCREENHEIGHT};
// --- 全局变量 ---
int board[8][8];
// 悔棋堆栈
int history[64][8][8];
int step_count = 0;
int clickx, clicky;
int turn; // 1=黑(人类), 2=白(Super AI)
int winner; // 0=无, 1=黑, 2=白, 3=平局
// 函数声明
void computerMove_Super(HWND hwnd);
BOOL hasValidMove(int player);
void checkGameOver();
ATOM registerWindow(HINSTANCE hInstance, WNDPROC WndProc, LPCSTR className){
WNDCLASSEXA wc;
memset(&wc, 0, sizeof(wc));
wc.cbSize = sizeof(WNDCLASSEXA);
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursorA(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszClassName = className;
wc.hIcon = LoadIconA(NULL, IDI_APPLICATION);
wc.hIconSm = LoadIconA(NULL, IDI_APPLICATION);
return RegisterClassExA(&wc);
}
BOOL createWindow(HINSTANCE hInstance, LPCSTR className, LPCSTR title, DWORD style, int x, int y, int width, int height){
return CreateWindowExA(WS_EX_CLIENTEDGE, className, title, style, x, y, width, height, NULL, NULL, hInstance, NULL) == NULL;
}
WPARAM mainloop(){
MSG msg;
while (GetMessageA(&msg, NULL, 0, 0)){
TranslateMessage(&msg);
DispatchMessageA(&msg);
}
return msg.wParam;
}
void maptoscreen(int *const x, int *const y, int clx, int cly){
*x = MAPLEFT + clx * WIDTH + (WIDTH >> 1);
*y = MAPTOP + cly * HEIGHT + (HEIGHT >> 1);
}
void paintmap(HDC hdc){
int i;
HBRUSH bgBrush = CreateSolidBrush(0x008000);
RECT mapRect = {MAPLEFT, MAPTOP, MAPRIGHT, MAPBOTTOM};
FillRect(hdc, &mapRect, bgBrush);
DeleteObject(bgBrush);
for (i = MAPLEFT; i <= MAPRIGHT; i += WIDTH){
MoveToEx(hdc, i, MAPTOP, NULL);
LineTo(hdc, i, MAPBOTTOM);
}
for (i = MAPTOP; i <= MAPBOTTOM; i += HEIGHT){
MoveToEx(hdc, MAPLEFT, i, NULL);
LineTo(hdc, MAPRIGHT, i);
}
}
void paintpieces(HDC hdc){
HBRUSH oldbrush, blackBrush, whiteBrush;
HPEN oldpen, nullPen;
int i, j, x, y;
blackBrush = CreateSolidBrush(0x000000);
whiteBrush = CreateSolidBrush(0xFFFFFF);
nullPen = CreatePen(PS_NULL, 0, 0);
oldpen = (HPEN)SelectObject(hdc, nullPen);
for (i = 0; i < 8; ++i){
for (j = 0; j < 8; ++j){
if (board[i][j] != 0) {
maptoscreen(&x, &y, i, j);
oldbrush = (HBRUSH)SelectObject(hdc, (board[i][j] == 1) ? blackBrush : whiteBrush);
Ellipse(hdc, x - HALFSIZE, y - HALFSIZE, x + HALFSIZE, y + HALFSIZE);
SelectObject(hdc, oldbrush);
}
}
}
SelectObject(hdc, oldpen);
DeleteObject(blackBrush);
DeleteObject(whiteBrush);
DeleteObject(nullPen);
}
void paintinfo(HDC hdc){
SetTextAlign(hdc, TA_CENTER);
SetBkMode(hdc, TRANSPARENT);
char buf[128];
if (winner) {
if (winner == 1) lstrcpyA(buf, "Black (Human) Wins! (Press 'R' to Reset)");
else if (winner == 2) lstrcpyA(buf, "White (Super AI) Wins! (Press 'R' to Reset)");
else lstrcpyA(buf, "Draw! (Press 'R' to Reset)");
TextOutA(hdc, SCREENHALFWIDTH, 10, buf, lstrlenA(buf));
} else {
if (turn == 1) lstrcpyA(buf, "Your Turn (Black) - Press 'Z' to Undo");
else lstrcpyA(buf, "Super AI (White) Thinking...");
TextOutA(hdc, SCREENHALFWIDTH, 10, buf, lstrlenA(buf));
}
}
void mainpaint(HDC hdc){
paintmap(hdc);
paintpieces(hdc);
paintinfo(hdc);
}
// --- 游戏逻辑 ---
BOOL isInside(int x, int y) {
return x >= 0 && x < 8 && y >= 0 && y < 8;
}
// 悔棋保存
void saveState() {
if (step_count < 64) {
memcpy(history[step_count], board, sizeof(board));
step_count++;
}
}
// 悔棋执行
void undo() {
// 因为是人机对战,悔棋通常意味着悔两步(回到玩家上一步),或者一步(如果轮到电脑下但还没下)
// 简单起见,我们假设必须回到玩家的回合。
// 如果现在是玩家回合(turn=1),我们要撤销 电脑的一步 + 玩家的一步 = 2步
if (step_count >= 2 && turn == 1 && winner == 0) {
step_count -= 2;
memcpy(board, history[step_count], sizeof(board));
turn = 1;
}
// 如果游戏结束了,可能也只要撤销1步或2步
else if (winner != 0 && step_count > 0) {
step_count--; // 简单回退一步看看
memcpy(board, history[step_count], sizeof(board));
winner = 0;
turn = 1; // 强制回玩家
}
}
int executeMoveLogic(int tempBoard[8][8], int x, int y, int player, BOOL dryRun) {
if (tempBoard[x][y] != 0) return 0;
int opponent = 3 - player;
int totalFlipped = 0;
for (int k = 0; k < 8; k++) {
int dx = dir[k][0];
int dy = dir[k][1];
int nx = x + dx;
int ny = y + dy;
int flipped = 0;
while (isInside(nx, ny) && tempBoard[nx][ny] == opponent) {
nx += dx;
ny += dy;
flipped++;
}
if (flipped > 0 && isInside(nx, ny) && tempBoard[nx][ny] == player) {
totalFlipped += flipped;
if (!dryRun) {
int cx = x + dx;
int cy = y + dy;
while (cx != nx || cy != ny) {
tempBoard[cx][cy] = player;
cx += dx;
cy += dy;
}
}
}
}
if (!dryRun && totalFlipped > 0) {
tempBoard[x][y] = player;
}
return totalFlipped;
}
BOOL cango(int x, int y) {
return executeMoveLogic(board, x, y, turn, TRUE) > 0;
}
void makeMove(int x, int y, int player) {
executeMoveLogic(board, x, y, player, FALSE);
}
int getmouse(HWND hwnd, int *const x, int *const y){
*x = (*x - MAPLEFT) / WIDTH - (*x < MAPLEFT ? 1 : 0);
*y = (*y - MAPTOP) / HEIGHT - (*y < MAPTOP ? 1 : 0);
if (*x < 0 || *x >= 8 || *y < 0 || *y >= 8){
return 256;
}
if (!cango(*x, *y)){
return 16;
}
return 0;
}
int countValidMoves(int b[8][8], int player) {
int count = 0;
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++)
if (executeMoveLogic(b, i, j, player, TRUE) > 0) count++;
return count;
}
int countDiscs(int b[8][8]) {
int count = 0;
for(int i=0; i<8; i++)
for(int j=0; j<8; j++)
if(b[i][j] != 0) count++;
return count;
}
// -------------------------------------------------------------
// Super AI (White / Player 2) Implementation
// -------------------------------------------------------------
// 动态权重:如果角被占,旁边就变安全
int getDynamicWeight(int b[8][8], int x, int y) {
int w = weights[x][y];
if (x <= 1 && y <= 1 && b[0][0] != 0) return 50;
else if (x >= 6 && y <= 1 && b[7][0] != 0) return 50;
else if (x <= 1 && y >= 6 && b[0][7] != 0) return 50;
else if (x >= 6 && y >= 6 && b[7][7] != 0) return 50;
return w;
}
int evaluateBoardSuper(int b[8][8], int player) {
int opponent = 3 - player;
int myTiles = 0, oppTiles = 0;
int myFrontier = 0, oppFrontier = 0;
int score = 0;
for(int i=0; i<8; i++) {
for(int j=0; j<8; j++) {
if (b[i][j] == 0) continue;
int w = getDynamicWeight(b, i, j);
if(b[i][j] == player) { score += w; myTiles++; }
else { score -= w; oppTiles++; }
// Frontier Discs
int isFrontier = 0;
for (int k = 0; k < 8; k++) {
int nx = i + dir[k][0]; int ny = j + dir[k][1];
if (nx >= 0 && nx < 8 && ny >= 0 && ny < 8 && b[nx][ny] == 0) {
isFrontier = 1; break;
}
}
if (isFrontier) { if (b[i][j] == player) myFrontier++; else oppFrontier++; }
}
}
int totalDiscs = myTiles + oppTiles;
// 终局:完全计算棋子数
if (totalDiscs > 58) return (myTiles - oppTiles) * 10000;
// 行动力压制
int myMoves = countValidMoves(b, player);
int oppMoves = countValidMoves(b, opponent);
score += (myMoves - oppMoves) * 500;
// 边界子惩罚
score -= (myFrontier - oppFrontier) * 100;
// 蒸发策略 (Evaporation): 前期棋子越少越好
if (totalDiscs <= 45) {
score -= (myTiles - oppTiles) * 5;
} else {
score += (myTiles - oppTiles) * 10;
}
// 角的控制
int corners[4][2] = {{0,0}, {0,7}, {7,0}, {7,7}};
for(int i=0; i<4; i++) {
if (b[corners[i][0]][corners[i][1]] == player) score += 1000;
else if (b[corners[i][0]][corners[i][1]] == opponent) score -= 1000;
}
return score;
}
typedef struct { int x, y, score; } MoveScore;
int compareMoves(const void *a, const void *b) {
return ((MoveScore*)b)->score - ((MoveScore*)a)->score;
}
// 增强版 Minimax
int minimaxSuper(int b[8][8], int depth, int alpha, int beta, int currentPlayer, int maximizingPlayer) {
if (depth == 0) return evaluateBoardSuper(b, maximizingPlayer);
int opponent = 3 - currentPlayer;
MoveScore moves[64];
int moveCount = 0;
// 生成走法并预估分
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
if (executeMoveLogic(b, i, j, currentPlayer, TRUE) > 0) {
moves[moveCount].x = i;
moves[moveCount].y = j;
moves[moveCount].score = getDynamicWeight(b, i, j);
moveCount++;
}
}
}
if (moveCount == 0) {
if (countValidMoves(b, opponent) == 0) {
int finalScore = 0;
for(int i=0; i<8; i++) for(int j=0; j<8; j++) {
if(b[i][j] == maximizingPlayer) finalScore++; else if(b[i][j] != 0) finalScore--;
}
if (finalScore > 0) return 200000 + finalScore;
if (finalScore < 0) return -200000 + finalScore;
return 0;
}
return minimaxSuper(b, depth, alpha, beta, opponent, maximizingPlayer);
}
// 移动排序
qsort(moves, moveCount, sizeof(MoveScore), compareMoves);
int tempBoard[8][8];
int bestVal = (currentPlayer == maximizingPlayer) ? -9999999 : 9999999;
for (int k = 0; k < moveCount; k++) {
memcpy(tempBoard, b, sizeof(int)*64);
executeMoveLogic(tempBoard, moves[k].x, moves[k].y, currentPlayer, FALSE);
int val = minimaxSuper(tempBoard, depth - 1, alpha, beta, opponent, maximizingPlayer);
if (currentPlayer == maximizingPlayer) {
if (val > bestVal) bestVal = val;
if (val > alpha) alpha = val;
} else {
if (val < bestVal) bestVal = val;
if (val < beta) beta = val;
}
if (beta <= alpha) break;
}
return bestVal;
}
void computerMove_Super(HWND hwnd) {
int bestScore = -9999999;
int bestMove[2] = {-1, -1};
int alpha = -9999999;
int beta = 9999999;
int discs = countDiscs(board);
// 动态深度:中局 8,终局 12
int depth = 8;
if (discs > 50) depth = 12;
int aiPlayer = 2; // 白棋
MoveScore moves[64];
int moveCount = 0;
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
if (executeMoveLogic(board, i, j, aiPlayer, TRUE) > 0) {
moves[moveCount].x = i;
moves[moveCount].y = j;
moves[moveCount].score = getDynamicWeight(board, i, j);
moveCount++;
}
}
}
qsort(moves, moveCount, sizeof(MoveScore), compareMoves);
for (int k = 0; k < moveCount; k++) {
int i = moves[k].x;
int j = moves[k].y;
int tempBoard[8][8];
memcpy(tempBoard, board, sizeof(board));
executeMoveLogic(tempBoard, i, j, aiPlayer, FALSE);
// AI 是白棋(2),maximizingPlayer = 2
int score = minimaxSuper(tempBoard, depth - 1, alpha, beta, 1, aiPlayer);
if (score > bestScore) {
bestScore = score;
bestMove[0] = i;
bestMove[1] = j;
}
if (score > alpha) alpha = score;
}
if (bestMove[0] != -1) {
makeMove(bestMove[0], bestMove[1], aiPlayer);
RECT xrect = {0,0,SCREENWIDTH, SCREENHEIGHT};
InvalidateRect(hwnd, &xrect, FALSE);
UpdateWindow(hwnd);
}
}
// -------------------------------------------------------------
// 主流程控制
// -------------------------------------------------------------
BOOL hasValidMove(int player) {
for (int i = 0; i < 8; i++) for (int j = 0; j < 8; j++)
if (executeMoveLogic(board, i, j, player, TRUE) > 0) return TRUE;
return FALSE;
}
void checkGameOver() {
if (!hasValidMove(1) && !hasValidMove(2)) {
int blackCount = 0, whiteCount = 0;
for(int i=0; i<8; i++) for(int j=0; j<8; j++) {
if(board[i][j] == 1) blackCount++;
else if(board[i][j] == 2) whiteCount++;
}
if (blackCount > whiteCount) winner = 1;
else if (whiteCount > blackCount) winner = 2;
else winner = 3;
}
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam){
switch (Message){
case WM_PAINT:{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hbmMem = CreateCompatibleBitmap(hdc, SCREENWIDTH, SCREENHEIGHT);
HBITMAP hbm = (HBITMAP)SelectObject(hdcMem, hbmMem);
HBRUSH background = CreateSolidBrush(0xbfbfbf);
FillRect(hdcMem, &screen, background);
mainpaint(hdcMem);
BitBlt(hdc, 0, 0, SCREENWIDTH, SCREENHEIGHT, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, hbm);
DeleteObject(hbmMem);
DeleteDC(hdcMem);
DeleteObject(background);
EndPaint(hwnd, &ps);
break;
}
case WM_CREATE:
turn = 1;
winner = 0;
step_count = 0;
memset(board, 0, sizeof(board));
board[3][3] = board[4][4] = 2;
board[3][4] = board[4][3] = 1;
break;
case WM_KEYDOWN:
if (wParam == 'Z') { // Undo
if (step_count > 0 && turn == 1 && !winner) {
undo();
InvalidateRect(hwnd, NULL, FALSE);
}
} else if (wParam == 'R') { // Reset
turn = 1; winner = 0; step_count = 0;
memset(board, 0, sizeof(board));
board[3][3] = board[4][4] = 2;
board[3][4] = board[4][3] = 1;
InvalidateRect(hwnd, NULL, FALSE);
}
break;
case WM_LBUTTONDOWN:
if (turn != 1 || winner) break; // 只能在玩家回合(黑)点击
clickx = LOWORD(lParam);
clicky = HIWORD(lParam);
if (getmouse(hwnd, &clickx, &clicky) == 0){
// 1. 玩家下棋
saveState(); // 存一步玩家
makeMove(clickx, clicky, 1);
RECT updrect = {0, 0, SCREENWIDTH, SCREENHEIGHT};
InvalidateRect(hwnd, &updrect, FALSE);
UpdateWindow(hwnd);
checkGameOver();
if (winner) break;
// 2. 轮到 AI
turn = 2;
if (hasValidMove(2)) {
// 存一步电脑前的状态(为了undo能回退2步回到这里)
saveState();
// 稍微延迟一下,体验更好
Sleep(100);
computerMove_Super(hwnd); // 执行白棋 Super AI
checkGameOver();
if (winner) break;
// AI 走完,交还给玩家
if (hasValidMove(1)) {
turn = 1;
} else {
// 如果玩家无路可走,AI 继续
while (hasValidMove(2) && !hasValidMove(1)) {
MessageBoxA(hwnd, "Black Passes! (No valid moves)", "Info", MB_OK);
saveState();
Sleep(500);
computerMove_Super(hwnd);
checkGameOver();
if (winner) break;
}
if (!winner) turn = 1;
}
} else {
// AI 无路可走
MessageBoxA(hwnd, "White Passes! (No valid moves)", "Info", MB_OK);
if (hasValidMove(1)) turn = 1;
else checkGameOver();
}
InvalidateRect(hwnd, &updrect, FALSE);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, Message, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE _0, HINSTANCE _1, LPSTR _2, int _3){
RECT rcClient = {0, 0, SCREENWIDTH, SCREENHEIGHT};
const DWORD style = WS_OVERLAPPED | WS_CAPTION | WS_VISIBLE | WS_SYSMENU | WS_MINIMIZEBOX;
if (!registerWindow(_0, WndProc, "ReversiHumanVsSuperAI")) return 0;
AdjustWindowRect(&rcClient, style, FALSE);
int w = rcClient.right - rcClient.left;
int h = rcClient.bottom - rcClient.top;
if (createWindow(_0, "ReversiHumanVsSuperAI", "Human (Black) vs Super AI (White)", style,
GetSystemMetrics(SM_CXSCREEN)/2 - w/2, GetSystemMetrics(SM_CYSCREEN)/2 - h/2, w, h)) return 0;
return mainloop();
}
五子棋
普通版
C#include <windows.h>
#include <stdlib.h>
#include <time.h>
#ifdef UNICODE
#define TSTR(quote) L##quote
#else
#define TSTR(quote) quote
#endif
#define SCREENWIDTH 640
#define SCREENHALFWIDTH 320
#define SCREENHEIGHT 640
#define SCREENHALFHEIGHT 320
#define WIDTH 36
#define HEIGHT 38
#define HALFSIZE 15
#define LINELEFT (SCREENHALFWIDTH - WIDTH * 7)
#define LINERIGHT (SCREENHALFWIDTH + WIDTH * 7)
#define LINETOP (SCREENHALFHEIGHT - HEIGHT * 7)
#define LINEBOTTOM (SCREENHALFHEIGHT + HEIGHT * 7)
#define MAPLEFT (LINELEFT - (WIDTH >> 1))
#define MAPTOP (LINETOP - (HEIGHT >> 1))
const RECT screen = {0, 0, SCREENWIDTH, SCREENHEIGHT};
int mapcolor[15][15]; // 0:空, 1:黑(玩家), 2:白(电脑)
int clickx, clicky;
int turn, winner; // turn=1(玩家回合), turn=2(电脑回合)
// 注册窗口类
ATOM registerWindow(HINSTANCE hInstance, WNDPROC WndProc, LPSTR className){
WNDCLASSEX wc;
memset(&wc, 0, sizeof(wc));
wc.cbSize = sizeof(WNDCLASSEX);
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszClassName = className;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
return RegisterClassEx(&wc);
}
// 创建窗口
BOOL createWindow(HINSTANCE hInstance, LPSTR className, LPSTR title, DWORD style, int x, int y, int width, int height){
return CreateWindowEx(WS_EX_CLIENTEDGE, className, title, style,
x, y, width, height,
NULL, NULL, hInstance, NULL) == NULL;
}
// 消息循环
WPARAM mainloop(){
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
// 坐标转换:棋盘格 -> 屏幕像素
void maptoscreen(int *const x, int *const y, int clx, int cly){
*x = LINELEFT + clx * WIDTH;
*y = LINETOP + cly * HEIGHT;
}
// 绘制棋盘
void paintmap(HDC hdc){
int i, x, y;
HBRUSH oldbrush, dotbrush, background = CreateSolidBrush(0x268cd8);
const RECT back = {MAPLEFT, MAPTOP,
LINERIGHT + WIDTH - (WIDTH >> 1),
LINEBOTTOM + HEIGHT - (HEIGHT >> 1)};
FillRect(hdc, &back, background);
DeleteObject(background);
for (i = LINELEFT; i <= LINERIGHT; i += WIDTH){
MoveToEx(hdc, i, LINETOP, NULL);
LineTo(hdc, i, LINEBOTTOM);
}
for (i = LINETOP; i <= LINEBOTTOM; i += HEIGHT){
MoveToEx(hdc, LINELEFT, i, NULL);
LineTo(hdc, LINERIGHT, i);
}
// 绘制五个星位
dotbrush = CreateSolidBrush(0);
oldbrush = (HBRUSH)SelectObject(hdc, dotbrush);
int stars[5][2] = {{3,3}, {3,11}, {7,7}, {11,3}, {11,11}};
for(int k=0; k<5; k++){
maptoscreen(&x, &y, stars[k][0], stars[k][1]);
Ellipse(hdc, x - 4, y - 4, x + 4, y + 4);
}
SelectObject(hdc, oldbrush);
DeleteObject(dotbrush);
}
// 绘制棋子
void paintdot(HDC hdc){
HBRUSH oldbrush, dotbrush;
int i, j, x, y;
// 画黑棋
dotbrush = CreateSolidBrush(0);
oldbrush = (HBRUSH)SelectObject(hdc, dotbrush);
for (i = 0; i <= 14; ++i){
for (j = 0; j <= 14; ++j){
if (mapcolor[i][j] == 1){
maptoscreen(&x, &y, i, j);
Ellipse(hdc, x - HALFSIZE, y - HALFSIZE, x + HALFSIZE, y + HALFSIZE);
}
}
}
SelectObject(hdc, oldbrush);
DeleteObject(dotbrush);
// 画白棋
dotbrush = CreateSolidBrush(0xffffff);
oldbrush = (HBRUSH)SelectObject(hdc, dotbrush);
for (i = 0; i <= 14; ++i){
for (j = 0; j <= 14; ++j){
if (mapcolor[i][j] == 2){
maptoscreen(&x, &y, i, j);
Ellipse(hdc, x - HALFSIZE, y - HALFSIZE, x + HALFSIZE, y + HALFSIZE);
}
}
}
SelectObject(hdc, oldbrush);
DeleteObject(dotbrush);
}
// 绘制胜负信息
void paintinfo(HDC hdc){
SetTextAlign(hdc, TA_CENTER);
SetBkMode(hdc, TRANSPARENT);
switch (winner){
case 1:
TextOutA(hdc, SCREENHALFWIDTH, 10, "Black (Player) Wins!", 20);
break;
case 2:
TextOutA(hdc, SCREENHALFWIDTH, 10, "White (Computer) Wins!", 22);
break;
}
}
void mainpaint(HDC hdc){
paintmap(hdc);
paintdot(hdc);
paintinfo(hdc);
}
// 获取鼠标点击的棋盘坐标
int getmouse(HWND hwnd, int *const x, int *const y){
*x = (*x - MAPLEFT) / WIDTH - (*x < MAPLEFT ? 1 : 0);
*y = (*y - MAPTOP) / HEIGHT - (*y < MAPTOP ? 1 : 0);
if (*x < 0 || *x >= 15 || *y < 0 || *y >= 15){
return 256;
}
if (mapcolor[*x][*y]){
return 16;
}
return 0;
}
// 检查特定方向是否有5连
int checklinemode(int x, int y, int mode){
if (x < 0 || y < 0 || x > 18 || y > 18) return 0;
// 原有的胜负判定逻辑保留
switch (mode){
case 1: // 纵向
return y <= 14 ? mapcolor[x][y] & mapcolor[x][y + 1] & mapcolor[x][y + 2] & mapcolor[x][y + 3] & mapcolor[x][y + 4] : 0;
case 2: // 横向
return x <= 14 ? mapcolor[x][y] & mapcolor[x + 1][y] & mapcolor[x + 2][y] & mapcolor[x + 3][y] & mapcolor[x + 4][y] : 0;
case 3: // 左上到右下
return x <= 14 && y <= 14 ? mapcolor[x][y] & mapcolor[x + 1][y + 1] & mapcolor[x + 2][y + 2] & mapcolor[x + 3][y + 3] & mapcolor[x + 4][y + 4] : 0;
case 4: // 右上到左下
return x >= 4 && y <= 14 ? mapcolor[x][y] & mapcolor[x - 1][y + 1] & mapcolor[x - 2][y + 2] & mapcolor[x - 3][y + 3] & mapcolor[x - 4][y + 4] : 0;
}
return 0;
}
// 检查胜负
void checkline(int lx, int ly){
int x, y;
for (y = ly - 4; y <= ly; ++y) if (winner = checklinemode(lx, y, 1)) return;
for (x = lx - 4; x <= lx; ++x) if (winner = checklinemode(x, ly, 2)) return;
for (x = lx - 4, y = ly - 4; x <= lx; ++x, ++y) if (winner = checklinemode(x, y, 3)) return;
for (x = lx + 4, y = ly - 4; y <= ly; --x, ++y) if (winner = checklinemode(x, y, 4)) return;
}
// ==========================================
// AI 核心逻辑开始
// ==========================================
// 评估某个方向上的连子情况
// return: 该方向的得分
int evaluateLine(int x, int y, int dx, int dy, int color) {
int count = 1; // 包含当前位置
int block = 0; // 被封堵的端点数
int empty = -1; // 空位位置(暂未完全利用,可用于更高级AI)
// 正向搜索
int i, tempX, tempY;
for (i = 1; i <= 4; i++) {
tempX = x + dx * i;
tempY = y + dy * i;
if (tempX < 0 || tempX >= 15 || tempY < 0 || tempY >= 15) {
block++;
break;
}
if (mapcolor[tempX][tempY] == color) {
count++;
} else if (mapcolor[tempX][tempY] == 0) {
empty = i; // 记录第一个空位
break;
} else {
block++;
break;
}
}
// 反向搜索
for (i = 1; i <= 4; i++) {
tempX = x - dx * i;
tempY = y - dy * i;
if (tempX < 0 || tempX >= 15 || tempY < 0 || tempY >= 15) {
block++;
break;
}
if (mapcolor[tempX][tempY] == color) {
count++;
} else if (mapcolor[tempX][tempY] == 0) {
break;
} else {
block++;
break;
}
}
// 评分规则 (权重可调整)
if (count >= 5) return 200000; // 成5 (必胜)
if (block == 0) {
if (count == 4) return 50000; // 活4
if (count == 3) return 3000; // 活3
if (count == 2) return 400; // 活2
if (count == 1) return 10;
} else if (block == 1) {
if (count == 4) return 2500; // 冲4 (死4)
if (count == 3) return 50; // 死3
if (count == 2) return 5; // 死2
}
return 0; // 两头堵死或无子
}
// 获取某点对于某颜色的总评分
int getPointScore(int x, int y, int color) {
int score = 0;
// 四个方向:横、纵、左斜、右斜
score += evaluateLine(x, y, 1, 0, color);
score += evaluateLine(x, y, 0, 1, color);
score += evaluateLine(x, y, 1, 1, color);
score += evaluateLine(x, y, 1, -1, color);
return score;
}
// 电脑执行落子
void computerMove(HWND hwnd) {
int x, y;
int maxScore = -1;
int bestX = 7, bestY = 7; // 默认中心
int myScore, enemyScore, totalScore;
// 如果棋盘全满(平局处理,简单判断)
int full = 1;
for (x = 0; x < 15; x++) {
for (y = 0; y < 15; y++) {
if (mapcolor[x][y] == 0) {
full = 0;
// 1. 进攻分:如果电脑下这,对电脑多有利
myScore = getPointScore(x, y, 2);
// 2. 防守分:如果黑棋下这,对黑棋多有利(电脑必须拦截)
enemyScore = getPointScore(x, y, 1);
// 综合评分:进攻 + 防守
// 可以在这里调整攻防权重。通常防守稍微重要一点,但如果自己能赢(myScore极高)则优先
if (myScore >= 200000) {
// 如果自己能赢,直接下,不用管防守
totalScore = 2000000;
} else if (enemyScore >= 200000) {
// 对手要赢了,必须堵,且优先级仅次于自己赢
totalScore = 1000000;
} else {
// 普通情况,权衡攻守。略微偏重防守以稳健为主
totalScore = myScore + enemyScore;
}
// 增加一点随机性避免走法完全固定(可选)
// totalScore += (rand() % 10);
if (totalScore > maxScore) {
maxScore = totalScore;
bestX = x;
bestY = y;
}
}
}
}
if (full) return; // 棋盘满了
// 执行落子
mapcolor[bestX][bestY] = 2; // 电脑是2(白)
// 检查胜负
checkline(bestX, bestY);
// 刷新界面
RECT updrect;
int sx, sy;
maptoscreen(&sx, &sy, bestX, bestY);
SetRect(&updrect, sx - HALFSIZE, sy - HALFSIZE, sx + HALFSIZE, sy + HALFSIZE);
InvalidateRect(hwnd, &updrect, FALSE); // 局部刷新
if (winner) {
RECT allrect = {0, 0, SCREENWIDTH, 100};
InvalidateRect(hwnd, &allrect, FALSE); // 刷新顶部文字
}
turn = 1; // 轮到玩家
}
// ==========================================
// AI 逻辑结束
// ==========================================
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam){
switch (Message){
case WM_PAINT:{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hbmMem = CreateCompatibleBitmap(hdc, SCREENWIDTH, SCREENHEIGHT);
HBITMAP hbm = (HBITMAP)SelectObject(hdcMem, hbmMem);
HBRUSH background = CreateSolidBrush(0xffffff);
FillRect(hdcMem, &screen, background);
mainpaint(hdcMem);
BitBlt(hdc, 0, 0, SCREENWIDTH, SCREENHEIGHT, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, hbm);
DeleteObject(hbmMem);
DeleteDC(hdcMem);
DeleteObject(background);
EndPaint(hwnd, &ps);
break;
}
case WM_CREATE:
srand((unsigned)time(NULL)); // 初始化随机种子
turn = 1;
winner = 0;
break;
case WM_LBUTTONDOWN:
// 如果已分胜负或不是玩家回合,忽略点击
if (winner || turn != 1) break;
clickx = LOWORD(lParam);
clicky = HIWORD(lParam);
// 玩家落子验证
if (getmouse(hwnd, &clickx, &clicky)){
break;
}
// 玩家(黑)落子
mapcolor[clickx][clicky] = 1;
checkline(clickx, clicky);
// 刷新玩家落子区域
{
int x, y;
maptoscreen(&x, &y, clickx, clicky);
RECT updrect = {x - HALFSIZE, y - HALFSIZE, x + HALFSIZE, y + HALFSIZE};
InvalidateRect(hwnd, &updrect, FALSE);
}
if (winner){
RECT updrect = {0, 0, SCREENWIDTH, 100};
InvalidateRect(hwnd, &updrect, FALSE);
} else {
// 玩家没赢,且棋盘未满,轮到电脑下
turn = 2;
// 为了让玩家看到落子过程,强制立即重绘一次玩家的棋子
UpdateWindow(hwnd);
// 电脑思考并落子
computerMove(hwnd);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, Message, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE _0, HINSTANCE _1, LPSTR _2, int _3){
RECT rcClient = {0, 0, SCREENWIDTH, SCREENHEIGHT};
const DWORD style = WS_OVERLAPPED | WS_CAPTION | WS_VISIBLE | WS_SYSMENU | WS_MINIMIZEBOX;
WPARAM wParam;
int realwidth, realheight;
if (!registerWindow(_0, WndProc, TSTR("Gomoku"))){
MessageBox(NULL, TSTR("Window Registration Failed!"), TSTR("Error!"), MB_ICONEXCLAMATION | MB_OK);
return 0;
}
AdjustWindowRect(&rcClient, style, FALSE);
realwidth = rcClient.right - rcClient.left;
realheight = rcClient.bottom - rcClient.top;
if (createWindow(_0, TSTR("Gomoku"), TSTR("五子棋 (人机对战)"), style,
GetSystemMetrics(SM_CXSCREEN) - realwidth >> 1,
GetSystemMetrics(SM_CYSCREEN) - realheight >> 1,
realwidth, realheight)){
MessageBox(NULL, TSTR("Window Creation Failed!"), TSTR("Error!"), MB_ICONEXCLAMATION | MB_OK);
return 0;
}
wParam = mainloop();
return wParam;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...