首页 > 其他分享 >五子棋人机对战

五子棋人机对战

时间:2023-07-16 22:56:39浏览次数:35  
标签:分数 int 五子棋 SCORE break 对战 score 人机 SIZE

#include <windows.h>
#include <windowsx.h>
#include <ShObjIdl.h>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <vector>
#include <algorithm>
#include <iostream>
#include <cstdio>
#define CELL_SIZE 50
#define BOARD_SIZE 15
using namespace std;

// 定义一些常量
const int MARGIN = 5; // 棋盘边缘空隙
const int LINE_WIDTH = 5;

const int SCORE_WIN = 30000000; // 赢得分数

const int SCORE_LIVE_FOUR = 700000; // 活四分数
const int SCORE_HALF_SLEEP_FOUR = 450000; // 半眠四分数
const int SCORE_SLEEP_FOUR = -1; // 眠四分数

const int SCORE_LIVE_THREE = 40000; // 活三分数
const int SCORE_HALF_SLEEP_THREE = 1; // 半眠三分数
const int SCORE_SLEEP_THREE = -5; // 眠三分数

const int SCORE_LIVE_TWO = 9000; // 活二分数
const int SCORE_HALF_SLEEP_TWO = 0; // 半眠二分数
const int SCORE_SLEEP_TWO = -5; // 眠二分数

const int SCORE_LIVE_ONE = 2000; // 活一分数
const int SCORE_HALF_SLEEP_ONE = -10; // 半眠一分数
const int SCORE_SLEEP_ONE = -10; // 眠一分数

// 定义一些枚举类型
enum Player {NONE, HUMAN, COMPUTER}; // 玩家类型,无、人类、机器
enum Direction {HORIZONTAL, VERTICAL, LEFT_DIAGONAL, RIGHT_DIAGONAL}; // 方向类型,水平、垂直、左斜、右斜

// 定义一个结构体,表示棋盘上的一个位置
struct Position {
    int x; // 横坐标
    int y; // 纵坐标
    Position(int x = -2, int y = -2) : x(x), y(y) {} // 构造函数,初始化为-1表示无效位置
};
// 定义一个全局变量,表示上一次落子的位置,初始为无效位置
Position lastPos(-2, -2);

// 定义一个结构体,表示一个评分项,包括位置和分数
struct ScoreItem {
    Position pos; // 位置
    int score; // 分数
    ScoreItem(Position pos, int score) : pos(pos), score(score) {} // 构造函数,初始化位置和分数
};
HINSTANCE hInstance;
Player board[BOARD_SIZE][BOARD_SIZE];
int currentPlayer = 1;
//1 human
//2 robot
bool gameOver = false;
Player starter[2] = {NONE,NONE}, winner = NONE;
bool ended = false;
int steps = 0;
int ManhattanDistance(int x_1,int y_1,int x_2,int y_2){
    return abs(x_1-x_2)+abs(y_1-y_2);
} 
void ComputerMove();
int Evaluate(Position pos, Player player);
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    hInstance = hInst;
    WNDCLASS wc = {0};
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInst;
    wc.lpszClassName = "Gobang";
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    RegisterClass(&wc);
    HWND hwnd = CreateWindow("Gobang", "Gobang", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CELL_SIZE * BOARD_SIZE + 70, CELL_SIZE * BOARD_SIZE + 70, NULL, NULL, hInst, NULL);
    SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_SIZEBOX & ~WS_MAXIMIZEBOX & ~WS_MINIMIZEBOX);
    DeleteMenu(GetSystemMenu(hwnd, FALSE), SC_SIZE, MF_BYCOMMAND);
    DeleteMenu(GetSystemMenu(hwnd, FALSE), SC_MAXIMIZE, MF_BYCOMMAND);
    DeleteMenu(GetSystemMenu(hwnd, FALSE), SC_MINIMIZE, MF_BYCOMMAND);
    DeleteMenu(GetSystemMenu(hwnd, FALSE), SC_RESTORE, MF_BYCOMMAND);
    SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); 
    
    // 从任务管理器获取图标
    HICON hIcon;
    ExtractIconEx("C:\\Windows\\System32\\taskmgr.exe", 0, NULL, &hIcon, 1);
    // 设置程序图标
    
    SendMessage(GetActiveWindow(), WM_SETICON, ICON_BIG, (LPARAM)hIcon);
    SendMessage(GetActiveWindow(), WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
    
    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);
    MSG msg;
    while(GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch(uMsg)
    {
        case WM_CREATE:
            break;
        case WM_PAINT:
        {
            steps++;
            if(steps==1){
            //    if(rand()%2) 
                    //board[7][7]=COMPUTER,
            //starter[0]=COMPUTER,starter[1]=HUMAN;
                //else 
                starter[0]=HUMAN,starter[1]=COMPUTER;
            } 
                
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);
            
              // 创建一个黄色的画刷
        HBRUSH hBrush = CreateSolidBrush(RGB(255, 255, 0));

        // 将画刷选入设备上下文
        HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc, hBrush);

        // 绘制一个正方形
        Rectangle(hdc, (lastPos.x)*CELL_SIZE+5, (lastPos.y)*CELL_SIZE+5, (lastPos.x+1)*CELL_SIZE+5, (lastPos.y+1)*CELL_SIZE+5);

        // 恢复原来的画刷
        SelectObject(hdc, hOldBrush);

        // 删除创建的画刷
        DeleteObject(hBrush);
            for(int i = 1; i <= BOARD_SIZE + 1; i++)
            {
                MoveToEx(hdc, CELL_SIZE * (i - 1) + 5, 5, NULL);
                LineTo(hdc, CELL_SIZE * (i - 1) + 5, CELL_SIZE * BOARD_SIZE + 5);
                MoveToEx(hdc, 5, CELL_SIZE * (i - 1) + 5, NULL);
                LineTo(hdc, CELL_SIZE * BOARD_SIZE + 5, CELL_SIZE * (i - 1) + 5);
            }
            
            for(int i = 0; i < BOARD_SIZE; i++)
            {
                for(int j = 0; j < BOARD_SIZE; j++)
                {
                    RECT cellRect = {i * CELL_SIZE, j * CELL_SIZE, (i + 1) * CELL_SIZE, (j + 1) * CELL_SIZE};
                    if(board[i][j] == 1)
                    {
                        Ellipse(hdc, cellRect.left + 15, cellRect.top + 15, cellRect.right - 5, cellRect.bottom - 5);
                    }
                    else if(board[i][j] == 2)
                    {
                        MoveToEx(hdc, cellRect.left + 15, cellRect.top + 15, NULL);
                        LineTo(hdc, cellRect.right - 5, cellRect.bottom - 5);
                        MoveToEx(hdc, cellRect.right - 5, cellRect.top + 15, NULL);
                        LineTo(hdc, cellRect.left + 15, cellRect.bottom - 5);
                    }
                }
            }
           
            EndPaint(hwnd, &ps);

            break;
        }
        case WM_LBUTTONDOWN:
        {
            if(gameOver){
                ended = true;
            }
                
            if(ended)
                break;
            int x = GET_X_LPARAM(lParam) / CELL_SIZE;
            int y = GET_Y_LPARAM(lParam) / CELL_SIZE;
            if(board[x][y] == 0)
            {
                board[x][y] = HUMAN;
                lastPos=Position(x,y);
                 InvalidateRect(hwnd, NULL, TRUE);
                gameOver = false;
                for(int i = 0; i < BOARD_SIZE; i++)
                    for(int j = 0; j < BOARD_SIZE - 4; j++)
                        if(board[i][j] == currentPlayer && board[i][j+1] == currentPlayer
                        && board[i][j+2] == currentPlayer && board[i][j+3] == currentPlayer
                        && board[i][j+4] == currentPlayer)
                            gameOver = true; 
                for(int i = 0; i < BOARD_SIZE - 4; i++)
                    for(int j = 0; j < BOARD_SIZE; j++)
                        if(board[i][j] == currentPlayer && board[i+1][j] == currentPlayer
                        && board[i+2][j] == currentPlayer && board[i+3][j] == currentPlayer
                        && board[i+4][j] == currentPlayer)
                            gameOver = true; 
                for(int i = 0; i < BOARD_SIZE - 4; i++)
                    for(int j = 0; j < BOARD_SIZE - 4; j++)
                        if(board[i][j] == currentPlayer && board[i+1][j+1] == currentPlayer
                        && board[i+2][j+2] == currentPlayer && board[i+3][j+3] == currentPlayer
                        && board[i+4][j+4] == currentPlayer)
                            gameOver = true; 
                for(int i = 0; i < BOARD_SIZE - 4; i++)
                    for(int j = 4; j < BOARD_SIZE; j++)
                        if(board[i][j] == currentPlayer && board[i+1][j-1] == currentPlayer
                        && board[i+2][j-2] == currentPlayer && board[i+3][j-3] == currentPlayer
                        && board[i+4][j-4] == currentPlayer)
                            gameOver = true; 
                if(gameOver == true){
                    MessageBox(hwnd, "Game over!", "Gobang", MB_OK);
                    break;
                }
                
                    currentPlayer = 2;
                    ComputerMove();
                    
                InvalidateRect(hwnd, NULL, TRUE); 
                gameOver = false;
                for(int i = 0; i < BOARD_SIZE; i++)
                    for(int j = 0; j < BOARD_SIZE - 4; j++)
                        if(board[i][j] == currentPlayer && board[i][j+1] == currentPlayer
                        && board[i][j+2] == currentPlayer && board[i][j+3] == currentPlayer
                        && board[i][j+4] == currentPlayer)
                            gameOver = true; 
                for(int i = 0; i < BOARD_SIZE - 4; i++)
                    for(int j = 0; j < BOARD_SIZE; j++)
                        if(board[i][j] == currentPlayer && board[i+1][j] == currentPlayer
                        && board[i+2][j] == currentPlayer && board[i+3][j] == currentPlayer
                        && board[i+4][j] == currentPlayer)
                            gameOver = true; 
                for(int i = 0; i < BOARD_SIZE - 4; i++)
                    for(int j = 0; j < BOARD_SIZE - 4; j++)
                        if(board[i][j] == currentPlayer && board[i+1][j+1] == currentPlayer
                        && board[i+2][j+2] == currentPlayer && board[i+3][j+3] == currentPlayer
                        && board[i+4][j+4] == currentPlayer)
                            gameOver = true; 
                for(int i = 0; i < BOARD_SIZE - 4; i++)
                    for(int j = 4; j < BOARD_SIZE; j++)
                        if(board[i][j] == currentPlayer && board[i+1][j-1] == currentPlayer
                        && board[i+2][j-2] == currentPlayer && board[i+3][j-3] == currentPlayer
                        && board[i+4][j-4] == currentPlayer)
                            gameOver = true; 
                if(gameOver == true){
                    MessageBox(hwnd, "Game over!", "Gobang", MB_OK);
                    break;
                }
                currentPlayer = 1;
            }
            break;
        }
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    return 0;
}
// 机器落子函数
void ComputerMove() {
    // 定义一个评分项的向量,存储每个位置的评分
    vector<ScoreItem> scores;
    // 遍历棋盘上的每个位置
    for (int i = 0; i < BOARD_SIZE; i++) {
        for (int j = 0; j < BOARD_SIZE; j++) {
            // 如果当前位置没有棋子,则评估该位置的分数
            if (board[i][j] == NONE) {
                // 定义一个位置结构体,存储当前位置
                Position pos(i, j);

                // 定义两个变量,分别存储机器玩家和人类玩家在该位置的分数
                int computerScore = Evaluate(pos, COMPUTER);
                int humanScore = Evaluate(pos, HUMAN);
                // 定义一个变量,存储该位置的综合分数,取机器玩家和人类玩家分数中的较大者
                int score = computerScore + humanScore;
                // 在评分向量中压入该位置和分数的评分项
                scores.push_back(ScoreItem(pos, score));
            }
        }
    }
    
    // 如果评分向量为空,则表示棋盘已满,直接返回
    if (scores.empty()) {
        return;
    }

    // 对评分向量进行排序,按照分数从大到小的顺序
    sort(scores.begin(), scores.end(), [](const ScoreItem& a, const ScoreItem& b) {
        return a.score > b.score;
    });

    // 定义一个变量,存储最佳位置,初始为评分向量中第一个位置(分数最高)
    Position bestPos = scores[0].pos;
    
        // 定义一个变量,存储最高分数
        int maxScore = scores[0].score;
        // 定义一个整数向量,存储所有最高分数的位置的下标
        vector<int> maxIndices;

        // 遍历评分向量,找出所有最高分数的位置的下标,并压入整数向量中
        for (int i = 0; i < scores.size(); i++) {
            if (scores[i].score == maxScore) {
                maxIndices.push_back(i);
            }
            else {
                break;
            }
        }

        // 如果整数向量中有多个下标,则从中随机选择一个作为最佳位置的下标
        if (maxIndices.size() > 1) {
            int index = rand() % maxIndices.size();
            bestPos = scores[maxIndices[index]].pos;
        }

    // 在棋盘数组中记录机器玩家的棋子
    board[bestPos.x][bestPos.y] = COMPUTER;
    lastPos=Position(bestPos.x,bestPos.y);    

}

// 评估一个位置的分数函数,参数为位置和玩家类型,返回分数
int Evaluate(Position pos, Player player) {
    int totalScore = 0;
    // 定义一个变量,存储总分数,初始为0

    // 定义一个数组,存储四个方向上的偏移量
    int offset[4][2] = {{-1, 0}, {0, -1}, {-1, -1}, {-1, 1}};

    // 遍历四个方向
    for (int k = 0; k < 4; k++) {
        // 定义两个变量,表示当前方向上的偏移量
        int dx = offset[k][0];
        int dy = offset[k][1];

        // 定义两个变量,表示当前位置的坐标
        int x = pos.x;
        int y = pos.y;

        // 定义两个变量,分别表示该方向上的活/眠状态和连子数,初始为活和1(包括自己)
        int live = 0;
        int count = 1;
        int empty = 0;
        // 沿着当前方向前进,直到遇到边界或不同颜色的棋子,累加连子数,判断活/眠状态
        while (true) {
            x += dx;
            y += dy;
            if (x < 0 || x >= BOARD_SIZE || y < 0 || y >= BOARD_SIZE || (board[x][y] != player)) {
                if (board[x][y] != NONE) {
                    live ++; // 如果遇到边界或对手的棋子,则为眠
                }
                break;
            }
            count++;
        }

        // 沿着当前方向后退,直到遇到边界或不同颜色的棋子,累加连子数,判断活/眠状态
        x = pos.x;
        y = pos.y;
        while (true) {
            x -= dx;
            y -= dy;
            if (x < 0 || x >= BOARD_SIZE || y < 0 || y >= BOARD_SIZE || board[x][y] != player) {
                if (board[x][y] != NONE) {
                    live ++; // 如果遇到边界或对手的棋子,则为眠
                }
                break;
            }
            count++;
        }
        if(count>=5) live = 0; 
        // 根据活/眠状态和连子数,给该方向上的分数赋值
        int score = 0;
        if (count >= 5) { // 如果连子数大于等于5,则为必胜分数
            score = SCORE_WIN;
            if(player == COMPUTER) score*=4;
        }
        else if (live==0) { // 如果是活的
            switch (count) { // 根据连子数分别赋值
                case 4:
                    score = SCORE_LIVE_FOUR;
                    if(player == COMPUTER) score*=2;
                    break;
                case 3:
                    score = SCORE_LIVE_THREE;
                    break;
                case 2:
                    score = SCORE_LIVE_TWO;
                    break;
                case 1:
                    score = SCORE_LIVE_ONE;
                    break;
                default:
                    break;
            }
        }
        else if(live == 1){ // 如果是眠的
            switch (count) { // 根据连子数分别赋值
                case 4:
                    score = SCORE_HALF_SLEEP_FOUR;
                    if(player == COMPUTER) score*=3/2;
                    break;
                case 3:
                    score = SCORE_HALF_SLEEP_THREE;
                    break;
                case 2:
                    score = SCORE_HALF_SLEEP_TWO;
                    break;
                case 1:
                    score = SCORE_HALF_SLEEP_ONE;
                    break;
                default:
                    break;
            }
        }
        else { // 如果是眠的
            switch (count) { // 根据连子数分别赋值
                case 4:
                    score = SCORE_SLEEP_FOUR;
                    break;
                case 3:
                    score = SCORE_SLEEP_THREE;
                    break;
                case 2:
                    score = SCORE_SLEEP_TWO;
                    break;
                case 1:
                    score = SCORE_SLEEP_ONE;
                    break;
                default:
                    break;
            }
        }

        // 累加该方向上的分数到总分数
        totalScore += score;
    }

    // 返回总分数
    return totalScore;
}

标签:分数,int,五子棋,SCORE,break,对战,score,人机,SIZE
From: https://www.cnblogs.com/cytxzgbp/p/17512294.html

相关文章

  • 强化学习:基于蒙特卡洛树和策略价值网络的深度强化学习五子棋(含码源)
    强化学习:基于蒙特卡洛树和策略价值网络的深度强化学习五子棋(含码源)特点自我对弈详细注释流程简单代码结构net:策略价值网络实现mcts:蒙特卡洛树实现server:前端界面代码legacy:废弃代码docs:其他文件utils:工具代码network.py:移植过来的网络结构代码model_5400.p......
  • 多人在线对战开发指南 · C#
    多人在线对战是一款基于C#编写的游戏SDK,它为有强联网需求的网络游戏提供了一整套的客户端SDK解决方案,因此开发团队不再需要自建服务端,从而节省大部分开发和运维成本。多人在线对战提供的主要功能如下:+获取房间列表创建房间加入房间随机加入(符合条件的)房间获取房间玩家获取、......
  • 【Python&RS】基于GDAL给无人机图片定义坐标系
    ​    前段时间有过一个想法,就是如果可以给无人机拍摄的图片定义坐标系,再使用GADL库里的镶嵌拼接函数,是不是就可以实现快速拼接影像。虽然结果不是正射影像,但效率比无人机厂家的软件提高了很多很多,主要还是看用途。    有了这个想法后就要行动起来,定义一个坐标......
  • 【无人机控制】基于几何自适应控制算法解耦姿态动力学的四旋翼无人机附matlab代码
    ✅作者简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,matlab项目合作可私信。......
  • 【无人机编队】基于一阶一致性实现无领导多无人机协同编队控制附matlab仿真
    ✅作者简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,matlab项目合作可私信。......
  • 【任务分配】基于matlab实现多无人机动态任务分配附matlab代码
    ✅作者简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,matlab项目合作可私信。......
  • 2023智能系统与人机交互国际会议(ISHCI2023)
    2023智能系统与人机交互国际会议(ISHCI2023)由中国湖北众科自然科学研究院主办,将于2023年10月20-21日在中国武汉召开。会议每年举办一届,成为人们在智能系统和人机交互及相关领域交流观点和经验的理想平台。我们热烈邀请您为ISHCI2023做出贡献和参与。★重要信息会议时间:2023年10......
  • Ardupilot: 开启多个无人机SITL仿真实例Shell脚本
    将该脚本文件放入Ardupilot目录下,文件名称为swarm.sh例如:开启三台Copter无人机仿真示例,可运行脚本:bashswarm.shArduCopter3#!/bin/bash#runexample:startrunthreeArduCoptersitl#bashswarm.shArduCopter3#Vehiclestartlocationlocs=('22.71......
  • 迅为2K0500开发板龙芯全国产平台工业控制人机界面解决方案
       触摸屏控制器:龙芯2K0500可以作为触摸屏控制器的核心处理器,用于接收触摸输入信号,并进行实时的触摸事件处理和解析。它可以支持多点触控、手势识别等高级触摸功能,为用户提供直观、交互式的操作体验。图形界面显示:龙芯2K0500具备强大的图形处理能力,可以用于实时渲染和显示......
  • 行业分析| 无人机电力巡检的应用
    随着现代生活水平的不断提升,人们对各行各业的发展都提出了更高的品质要求,对于电力的需求不断上涨,因此也加速了电力行业的转型升级。基于这一发展状况,我国电力行业逐渐开始选择应用无人机电力巡检等现代高科技技术。无人机电力巡检是指利用无人机进行巡检,利用人工智能技术和高清晰......