zoukankan      html  css  js  c++  java
  • 简单的井字棋 AI DEMO | Minimax 算法

    在“类与对象”实训课上,有一道附加题让我们用 OOP 做一个的井字棋模拟程序,要求中电脑是随机落子的,这样显然不是很优雅。回忆起以前学的对抗搜索(这里叫 MaxMin 算法),我继续给游戏中的电脑一方写了个 AI。由于井字棋游戏运算规模很小,大部分的剪枝手段变得比较鸡肋,但以此为引搜索了一些资料,了解一些有趣的计算机博弈论知识,有机会再继续探究一下。

    极大极小(Minimax)算法

    Minimax算法 又名极小化极大算法,是一种找出失败的最大可能性中的最小值的算法(即最小化对手的最大得益)。通常以递归形式来实现。

    Minimax算法常用于棋类等由两方较量的游戏和程序。该算法是一个零总和算法,即一方要在可选的选项中选择将其优势最大化的选择,另一方则选择令对手优势最小化的一个,其输赢的总和为0(有点像能量守恒,就像本身两个玩家都有1点,最后输家要将他的1点给赢家,但整体上还是总共有2点)。很多棋类游戏可以采取此算法,例如tic-tac-toe。

    Alpha-beta 剪枝

    Alpha-beta剪枝是一种搜索算法,用以减少极小化极大算法(Minimax算法)搜索树的节点数。这是一种对抗性搜索算法,主要应用于机器游玩的二人游戏(如井字棋、象棋、围棋)。当算法评估出某策略的后续走法比之前策略的还差时,就会停止计算该策略的后续发展。该算法和极小化极大算法所得结论相同,但剪去了不影响最终决定的分枝。

    待改进的点:

    • 判定胜利的方法比较蠢,可以用循环代替

    • 没有对搜索树的预计深度进行评估,使 AI 做出最 “快” 的策略

    • 代码可读性不强,不符合工程代码规范

    #include <bits/stdc++.h>
    using namespace std;
    class MyChess {
    	private:
    		char A[3][3];
      		int step;	
    	public:
    		MyChess();
    		void DispChessboard();
    		void  PlayerMove();
    		void  ComputerMove();
    		bool isFull();
    		int MaxSearch();
    		int MinSearch();
    		int checkWin();              
    };
    MyChess::MyChess() {
    	memset(A,0,sizeof(A));
    	step=0;
    }
    void MyChess::DispChessboard() {
    	cout<<"-------------------
    ";
    	for (int i=0;i<3;i++) {
    		cout<<"|  "<<A[i][0]<<"  |  "<<A[i][1]<<"  |  "<<A[i][2]<<"  |
    ";
    		cout<<"-------------------
    ";
    	}
    }
    void MyChess::PlayerMove() {
    	int x,y;
    	cout<<"Step "<<++step<<" piece(x,y): ";
    	cin>>x>>y;
    	while (A[x][y] || x>2 || x<0 || y>2 || y<0) {
    		cout<<"ERROR!! piece(x,y): ";
    		cin>>x>>y;
    	}
    	A[x][y]='O';
    }
    void MyChess::ComputerMove() { // 模拟 AI 的选择过程 
    	int x,y,score=1;
    	for (int i=0;i<3;i++) {
    		for (int j=0;j<3;j++) {
    			if (!A[i][j]) {
    				A[i][j]='X';
    				int temp=MaxSearch();
    				if (score>temp) x=i,y=j,score=temp;
    				A[i][j]=0;
    			}
    		}
    	}
    	A[x][y]='X';
    }
    int MyChess::MaxSearch() { // 人类执子时,希望找到权值最大的子节点 
    	int ret=-1,sta=checkWin();
    	if (sta) return sta;
    	if (isFull()) return 0;
    	for (int i=0;i<3;i++) for (int j=0;j<3;j++)
    		if (!A[i][j]) A[i][j]='O',ret=max(ret,MinSearch()),A[i][j]=0;
    	return ret;
    }
    int MyChess::MinSearch() { // 电脑执子时,希望找找到权值最小的子节点 
    	int ret=1,sta=checkWin();
    	if (sta) return sta;
    	if (isFull()) return 0;
    	for (int i=0;i<3;i++) for (int j=0;j<3;j++)
    		if (!A[i][j]) A[i][j]='X',ret=min(ret,MaxSearch()),A[i][j]=0;
    	return ret;	
    }
    bool MyChess::isFull() { // 平局判定 
    	for (int i=0;i<3;i++)
    		for (int j=0;j<3;j++)
    			if (!A[i][j]) return false;
    	return true;
    }
    int MyChess::checkWin() {
    	for (int i=0;i<3;i++) if (A[i][0]=='O' && A[i][1]=='O' && A[i][2]=='O') return 1;	
    	for (int i=0;i<3;i++) if (A[0][i]=='O' && A[1][i]=='O' && A[2][i]=='O') return 1;		
    	for (int i=0;i<3;i++)  if (A[i][0]=='X' && A[i][1]=='X' && A[i][2]=='X') return -1;
    	for (int i=0;i<3;i++) if (A[0][i]=='X' && A[1][i]=='X' && A[2][i]=='X') return -1;
    	if (A[0][0]=='O' && A[1][1]=='O' && A[2][2]=='O') return 1;
    	if (A[2][0]=='O' && A[1][1]=='O' && A[0][2]=='O') return 1;
    	if (A[0][0]=='X' && A[1][1]=='X' && A[2][2]=='X') return -1;
    	if (A[2][0]=='X' && A[1][1]=='X' && A[0][2]=='X') return -1;	
    	return 0;
    }
    int main() {
    	MyChess Ch; int now=1;
    	Ch.DispChessboard();
    	while (!Ch.isFull()) {
    		if (now&1) {
    			Ch.PlayerMove();
    			if (Ch.checkWin()==1) { Ch.DispChessboard(),cout<<"You win!
    "; break; }
    			else if (Ch.isFull()) { Ch.DispChessboard(),cout<<"Gme tie!
    "; break; }	
    		}
    		else {
    			Ch.ComputerMove();
    			Ch.DispChessboard();
    			if (Ch.checkWin()==-1) { cout<<"Computer win!
    "; break; }
    		}
    		now^=1;	
    	}
    	
    	return 0;
    }
    
  • 相关阅读:
    [置顶] android ListView包含Checkbox滑动时状态改变
    Xamarin Android Gestures详解
    尝试在条件“$(_DeviceSdkVersion) >= 21”中对计算结果为“”而不是数字的“$(_DeviceSdkVersion)
    Xamarin Android自定义文本框
    C#四种深拷贝方法(转载)
    设置pictureBox的边框颜色(转载)
    C# 在运行中拖拽,改变控件大小位置类(转载)
    Ocelot + Consul的demo(二)集群部署
    Objective-C 简介
    计算机网络—概述
  • 原文地址:https://www.cnblogs.com/zhwer/p/14094339.html
Copyright © 2011-2022 走看看