三子棋模拟器 for linux
中考前一周开摆写的
采用的是minimax算法和 $\alpha - \beta$ 剪枝
目前是基于规则的估价函数(因为q779不会AI)
目前在含C++环境的 ubuntu20.04 和 macOS Monterey 12.4下都是可以编译的
代码:
//######
//
// 三子棋 for linux
// Dev By q779
// First Update 2021.6.6
// Latest Update 2022.7.23
// AI LEVEL 1.0
//
//######
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iomanip>
#include <random>
using namespace std;
#define int long long
#define INF (int)0x3f3f3f3f
#define maxDEP 5
struct Node
{
int x=-1,y=-1,val=-INF;
};
//棋子:AI为1,玩家为2
int eval(int b[5][5],int k)
{
int mark=k%2==0?2:1;//对手的
int count=0;
for(int i=1; i<=3; i++)
{
if((b[i][1]==mark||!b[i][1]) && (b[i][2]==mark||!b[i][2]) && (b[i][3]==mark||!b[i][3]))count++;
if((b[1][i]==mark||!b[1][i]) && (b[2][i]==mark||!b[2][i]) && (b[3][i]==mark||!b[3][i]))count++;
if(b[i][1]==1 && b[i][2]==1 && b[i][3]==1)return 50;
if(b[1][i]==1 && b[2][i]==1 && b[3][i]==1)return 50;
if(b[i][1]==2 && b[i][2]==2 && b[i][3]==2)return -50;
if(b[1][i]==2 && b[2][i]==2 && b[3][i]==2)return -50;
}
if((b[1][1]==1 && b[2][2]==1 && b[3][3]==1) || (b[1][3]==1 && b[2][2]==1 && b[3][1]==1))return 50;
if((b[1][1]==2 && b[2][2]==2 && b[3][3]==2) || (b[1][3]==2 && b[2][2]==2 && b[3][1]==2))return -50;
if((b[1][1]==mark||b[1][1]==0) && (b[2][2]==mark||b[2][2]==0) && (b[3][3]==mark||b[3][3]==0))count++;
if((b[1][3]==mark||b[1][3]==0) && (b[2][2]==mark||b[2][2]==0) && (b[3][1]==mark||b[3][1]==0))count++;
return mark==1?count:-count;
}
//棋盘
struct board
{
int body[5][5]={0};//棋盘
int count=0;
char AI='O',player='X';
char kong=' ';
//清空棋盘
void clear(){memset(body,0,sizeof(body));}
//输出棋盘
void print()
{
puts(" 1 2 3 ");
for(int i=1; i<=3;i++)
{
puts(" -------------");
printf("%c | ",char(i+48));
for(int j=1; j<=3; j++)
{
if(body[i][j]==0)printf(" | ");
if(body[i][j]==1)printf("%c | ",AI);
if(body[i][j]==2)printf("%c | ",player);
if(j==3)puts("");
}
}
puts(" -------------");
}
Node dfs(int k, int alpha, int beta)
{
if(k==maxDEP || count==9)
{
return Node({-1,-1,eval(body,k)});
}
int x=-1,y=-1;
for(int i=1; i<=3; i++)
for(int j=1; j<=3; j++)
if(body[i][j]==0)
{
body[i][j]=k%2==0?1:2;
count++;
Node node=dfs(k+1,alpha,beta);
if(k%2==0)//max
{
if(node.val > alpha)
{
alpha=node.val;
x=i,y=j;
}
if(node.val >= beta)
{
body[i][j]=0;count--;
return Node({-1,-1,node.val});
}
}else//min
{
if(node.val < beta)
{
beta=node.val;
x=i,y=j;
}
if(node.val <= alpha)
{
body[i][j]=0;count--;
return Node({-1,-1,node.val});
}
}
body[i][j]=0;
count--;
}
return Node({x,y,k%2==0?alpha:beta});
}
//循环游戏的主体
void Loop()
{
print();
bool flag=1;
while(count<9)
{
int tx,ty;
SCAN:;
printf("input 坐标 列+[空格]+行 >");
scanf("%lld%lld",&ty,&tx);
if(tx<1||ty<1||tx>3||ty>3)
{
puts("非法输入!");
goto SCAN;
}
if(body[tx][ty])
{
puts("那里已经有棋子了!");
goto SCAN;
}
//放棋
body[tx][ty]=2;count++;
puts("");
if(!flag)system("clear");flag=0;
if(eval(body,0)>=50)
{
system("clear");
puts("你被人工智能打败了!");
puts("");
print();
return ;
}else if(eval(body,0)<=-50)
{
system("clear");
puts("你战胜了人工智能!");
puts("");
print();
return;
}else if(count==9)
{
system("clear");
puts("平局,显然你打不过人工智能了!");
puts("");
print();
return;
}
//AI下棋
Node node = dfs(0,-INF,INF);
if(body[node.x][node.y]==0)
{
body[node.x][node.y]=1;
puts("");puts("");
printf("AI 在 第%lld列 第%lld行 放了一颗棋子\n",node.y,node.x);
puts("");puts("");system("clear");
count++;
if(eval(body,0)>=50)
{
system("clear");
puts("你被人工智能打败了!");
puts("");
print();
return ;
}
}else
{
if(eval(body,0)<=-50)
{
system("clear");
puts("你战胜了人工智能!");
puts("");
print();
return;
}else
{
system("clear");
puts("平局,显然你打不过人工智能了!");
puts("");
print();
return;
}
}
print();
}
}
}main_body;
void start_game()
{
int choose;
printf("欢迎来到三子棋游戏!\n\n");
printf("请选择你的棋子样式\n");
puts("");
puts("X ********* [1]");
puts("$ ********* [2]");
puts("");
printf("默认样式为 X\n");
printf("> ");
scanf("%lld",&choose);
if(choose==1) main_body.player='X';
if(choose==2) main_body.player='$';
system("clear");
printf("\n祝你好运\n\n\n\nPause any key to continue.\n");
getchar();getchar();
system("clear");
main_body.Loop();
}
signed main()
{
start_game();
return 0;
}