基于极大极小算法和alpha-beta剪枝实现AI井字棋

2024-03-18 10:38

本文主要是介绍基于极大极小算法和alpha-beta剪枝实现AI井字棋,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

关于极大极小算法和alpha-beta剪枝可以参考文章的参考资料,这里仅对其进行代码实现。

其实这个算法单纯的理解并不容易,下面用代码进行实现。

说一下实现这个AI井字棋的思路:

简单的来说就是计算机希望估值函数值最大,而下棋人希望这个估值最小,因此在计算机决策是就用递归的向前看,这里的递归其实蛮不好理解的,但是可以宏观的去理解。

下面是代码的构成:

  1. 一个ChessBoard的类用来处理一些关于棋盘的事件。
  2. 一个TicTacToe的类用来实现游戏开始和计算机,玩家决策的事件

ChessBoard.h

//Author : yqtao
//Date   :2016.10.30
#ifndef CHESS_BOARD_H
#define CHESS_BOARD_H
#include<vector>
#include<iostream>
#include<string>
using namespace std;const int ChessBoard_Row = 13;
const int ChessBoard_Col = 26;
const int GridNuber = 9;
const char Comp_Char = 'X';
const char Human_Char = 'O';
const char Blank_Char = ' ';class ChessBoard {
public:ChessBoard();bool isEmpty(int pos);bool isFull();bool canWin(char c);bool immediateComWin(int& bestMove);bool immediateHumanWin(int& bestMove);void placeComp(int pos);void placeHuman(int pos);void unPlace(int pos);void print();
private:vector<char> boardInOneDimens;vector<string> board;};
#endif // !CHESS_BOARD_H

ChessBoard.cpp

#include"ChessBoard.h"
ChessBoard::ChessBoard() {             //默认构造函数boardInOneDimens.resize(GridNuber);for (int i = 0; i < GridNuber; i++)boardInOneDimens[i] = ' ';vector<string>tmp = {"- - - - - - - - - - - - -","|       |       |       |","|       |       |       |","|       |       |       |","- - - - - - - - - - - - -","|       |       |       |","|       |       |       |","|       |       |       |","- - - - - - - - - - - - -","|       |       |       |","|       |       |       |","|       |       |       |","- - - - - - - - - - - - -"};board = tmp;
}
//check the posion is empty or not 
bool ChessBoard::isEmpty(int pos) {return boardInOneDimens[pos] == ' ';
}
//
bool ChessBoard::isFull() {for (int i = 0; i < GridNuber; i++) {if (boardInOneDimens[i] == ' ')return false;}return true;
}
//
bool ChessBoard::canWin(char c) {//check every rowfor (int i = 0; i <= 6; i+=3) {if (boardInOneDimens[i] == c&&boardInOneDimens[i] == boardInOneDimens[i + 1] &&boardInOneDimens[i] == boardInOneDimens[i + 2])return true;}//check every colfor (int i = 0; i < 3; i++) {if (boardInOneDimens[i] == c&&boardInOneDimens[i] == boardInOneDimens[i + 3] &&boardInOneDimens[i] == boardInOneDimens[i + 6])return true;}//check every diagonalsif (boardInOneDimens[0] == c&&boardInOneDimens[4] == c&&boardInOneDimens[8] == c)return true;if (boardInOneDimens[2] == c&&boardInOneDimens[4] == c&&boardInOneDimens[6] == c)return true;return false;
}
//
bool ChessBoard::immediateComWin(int& bestMove) {for (int i = 0; i < GridNuber; i++) {if (isEmpty(i)) {boardInOneDimens[i] =Comp_Char;bool Win = canWin(Comp_Char); boardInOneDimens[i] = Blank_Char;   //backtraceingif (Win) {bestMove = i;return true;}}}return false;
}
//
bool ChessBoard::immediateHumanWin(int& bestMove) {for (int i = 0; i < GridNuber; i++) {if (isEmpty(i)) {boardInOneDimens[i] = Human_Char;bool Win = canWin(Human_Char);boardInOneDimens[i] = Blank_Char;   //backtraceingif (Win) {bestMove = i;return true;}}}return false;
}
//
void ChessBoard::placeComp(int pos) {boardInOneDimens[pos] = 'X';
}
//
void ChessBoard::placeHuman(int pos) {boardInOneDimens[pos] = 'O';
}
//
void ChessBoard::unPlace(int pos) {boardInOneDimens[pos] = ' ';
}
//
void ChessBoard::print() {int cnt = 0;for (int i = 2; i <= 10; i += 4) {for (int j = 4; j <= 20; j += 8) {board[i][j] = boardInOneDimens[cnt++];}}for (int i = 0; i < ChessBoard_Row; ++i) {cout << board[i] << endl;}
}

TicTacToe.h

#ifndef TICTACTOE_H
#define TICTACTOE_H
#include "ChessBoard.h"
enum Role { Human, Comp };
static const int CompWin = 2;
static const int Draw = 1;
static const int HumanWin = 0;
class TicTacToe {
public:void start();bool handleGameOver();bool gameIsOver(bool &draw, Role &win);Role chooseFirstPlace();void compPlace();int getBestMove();void humanPlace();int getPlacePosition();void findCompMove(int& bestMove,int& value, int alpha, int beta);void findHumanMove(int& bestMove,int& value, int alpha, int beta);
private:ChessBoard board;
};
#endif // !TicTacToe

TicTacToe.cpp

#include "TicTacToe.h"
void TicTacToe::start() {Role firstPlace = chooseFirstPlace();if (firstPlace == Comp) {  // Choose computer to be the firstcompPlace();}while (1) {board.print();if (handleGameOver()) break;humanPlace();board.print();if (handleGameOver()) break;compPlace();}
}
Role TicTacToe::chooseFirstPlace() {int choose;while (1) {cout << "who will place first" << endl;cout << "0 : human first " << endl;cout << "1 : computer first" << endl;cin >> choose;if (choose == 0 || choose == 1) {cout << endl;break;}elsecout << "error,please enter again" << endl;}return (Role)choose;
}
//
void TicTacToe::humanPlace() {int pos = getPlacePosition();board.placeHuman(pos);cout << "Your choice:" << endl;
}
//
int TicTacToe::getPlacePosition() {int m, n,pos;while (1) {cout << "It is your turn, please input where you want :" << endl;cout << "for example: 2 2 mean you want to add position 2 row,2 col:" << endl;cin >> m >> n;if (m < 0 || m>3 || n < 0 || n>3)cout << "error,please input again:" << endl;else {pos = (m - 1) * 3 + n - 1;if (board.isEmpty(pos))break;elsecout << "error,please input again:" << endl;}}return pos;
}
//
void TicTacToe::compPlace() {int bestMove = getBestMove();board.placeComp(bestMove);cout << "the computer choice is: " << endl;
}
int TicTacToe::getBestMove() {int bestMove = 0, value = 0;findCompMove(bestMove,value,HumanWin, CompWin);return bestMove;
}
void TicTacToe::findCompMove(int& bestMove,int &value, int alpha, int beta) {if (board.isFull())value = Draw;else if (board.immediateComWin(bestMove))value = CompWin;else {value = alpha;for (int i = 0; i < GridNuber&&value < beta; i++) {if (board.isEmpty(i)) {board.placeComp(i);int tmp = -1, response = -1;  // Tmp is uselessfindHumanMove(tmp, response, value, beta);board.unPlace(i);if (response > value) {value = response;bestMove = i;}}}}
}void TicTacToe::findHumanMove(int& bestMove,int & value, int alpha, int beta) {if (board.isFull())value=Draw;else if (board.immediateHumanWin(bestMove)){value=HumanWin;}else {value = beta; for (int i = 0; i < GridNuber&&value>alpha; i++) {if (board.isEmpty(i)) {board.placeHuman(i);int tmp = -1, response = -1;  // Tmp is uselessfindCompMove(tmp, response, alpha, value);board.unPlace(i);if (response <value) {value = response;bestMove = i;}}}}
}
//
bool TicTacToe::gameIsOver(bool &draw, Role &win) {if (board.canWin(Comp_Char)) {win =Comp ;draw = false;return true;}else if (board.canWin(Human_Char)) {win = Human;draw = false;return true;}else if (board.isFull()) {draw = true;return true;}else {return false;}
}bool TicTacToe::handleGameOver()  {bool draw = false;Role whoWin = Human;if (gameIsOver(draw, whoWin)) {if (draw) {cout << "Draw!" << endl;}else {if (whoWin == Comp) {cout << "You lose!" << endl;}else if (whoWin == Human) {cout << "Congratulations! You defeat the computer." << endl;}}return true;}else {return false;}
}

实现的结果如下图所示:

这里写图片描述

完成的代码和运行:

在Linux的环境下:

git clone https://github.com/yqtaowhu/DataStructureAndAlgorithm
cd DataStructureAndAlgorithm/Algorithm/TicTacToe/
make run

在window的环境下可用VS进行编译。
完整的代码在我的Github:
https://github.com/yqtaowhu/DataStructureAndAlgorithm/tree/master/Algorithm/TicTacToe

参考资料:
1. 数据结构与算法分析——C++语言描述
2. http://blog.csdn.net/tianranhe/article/details/8301756
3. http://blog.csdn.net/qq_22885773/article/details/50967021

这篇关于基于极大极小算法和alpha-beta剪枝实现AI井字棋的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/822077

相关文章

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

AI绘图怎么变现?想做点副业的小白必看!

在科技飞速发展的今天,AI绘图作为一种新兴技术,不仅改变了艺术创作的方式,也为创作者提供了多种变现途径。本文将详细探讨几种常见的AI绘图变现方式,帮助创作者更好地利用这一技术实现经济收益。 更多实操教程和AI绘画工具,可以扫描下方,免费获取 定制服务:个性化的创意商机 个性化定制 AI绘图技术能够根据用户需求生成个性化的头像、壁纸、插画等作品。例如,姓氏头像在电商平台上非常受欢迎,

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

从去中心化到智能化:Web3如何与AI共同塑造数字生态

在数字时代的演进中,Web3和人工智能(AI)正成为塑造未来互联网的两大核心力量。Web3的去中心化理念与AI的智能化技术,正相互交织,共同推动数字生态的变革。本文将探讨Web3与AI的融合如何改变数字世界,并展望这一新兴组合如何重塑我们的在线体验。 Web3的去中心化愿景 Web3代表了互联网的第三代发展,它基于去中心化的区块链技术,旨在创建一个开放、透明且用户主导的数字生态。不同于传统

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第

AI一键生成 PPT

AI一键生成 PPT 操作步骤 作为一名打工人,是不是经常需要制作各种PPT来分享我的生活和想法。但是,你们知道,有时候灵感来了,时间却不够用了!😩直到我发现了Kimi AI——一个能够自动生成PPT的神奇助手!🌟 什么是Kimi? 一款月之暗面科技有限公司开发的AI办公工具,帮助用户快速生成高质量的演示文稿。 无论你是职场人士、学生还是教师,Kimi都能够为你的办公文

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

Andrej Karpathy最新采访:认知核心模型10亿参数就够了,AI会打破教育不公的僵局

夕小瑶科技说 原创  作者 | 海野 AI圈子的红人,AI大神Andrej Karpathy,曾是OpenAI联合创始人之一,特斯拉AI总监。上一次的动态是官宣创办一家名为 Eureka Labs 的人工智能+教育公司 ,宣布将长期致力于AI原生教育。 近日,Andrej Karpathy接受了No Priors(投资博客)的采访,与硅谷知名投资人 Sara Guo 和 Elad G

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖