六子棋
一,这是我大二一次程序设计比赛的成果,相对而言感觉还可以,感觉对当时的自己挺有成就感,所以发出来分享一下,对于是菜鸟的自己,用了大概一个月时间
二,代码部分
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<time.h>
#include <array>
#include <algorithm>
#include <functional>
#include<iostream>
using namespace std;
#pragma warning(disable:4996);
#define BLACK 0
#define WHITE 1
#define EMPTY 2
#define zero 0
#define one 1
#define two 30
#define three 32
#define four 600
#define five 800
#define six 500000
#define Azero 0
#define Aone -1
#define Atwo -50
#define Athree -52
#define Afour -800
#define Afive -1000
#define Asix -1000000
#define DT 2 //搜索广度
#define CG 400 //可行域大小
#define GG 20000 //树层上线
int KK;
struct Point { //点结构
int x, y;
};
struct Step { //步结构
Point first, second;
int value;
};
typedef struct Tree { //博弈树
Point first, second; //当前的两个决策点
Point ffirst, fsecond; //根的选择
int value; //当前棋盘
}Tree;
typedef struct cango {
int x;
int y;
int va; //用于启发式搜索排序
}cango;
struct {
bool operator()(cango a, cango b)const {
return a.va > b.va;
}
}customLessA;
struct {
bool operator()(cango a, cango b)const {
return a.va < b.va;
}
}customLessB;
int Board[19][19];//存储棋盘信息,其元素值为 BLACK, WHITE, EMPTY 之一
///
///
/
/
//
///
/复制棋盘 展示棋盘
void copy(int Board[19][19], int board[19][19]) //棋盘复刻为动态数组
{
for (int i = 0; i < 19; i++) {
for (int j = 0; j < 19; j++) {
board[i][j] = Board[i][j];
}
}
}
/评分模块
int power(int a[6], int tt)
{
int j;
int count1 = 0; //我的棋子的个数
int count2 = 0; //对面的棋子的个数
for (j = 0; j < 6; j++) //数当前路中自己的棋子的个数 对面的棋子的个数 空白的个数
{
if (a[j] == tt) count1++;
if (a[j] == 1 - tt)count2++;
}
if (count2 == 0) {
if (count1 == 0) { return zero; }
if (count1 == 1) { return one; }
if (count1 == 2) { return two; }
if (count1 == 3) { return three; }
if (count1 == 4) { return four; }
if (count1 == 5) { return five; }
if (count1 == 6) { return six; }
}
if (count1 == 0) {
if (count2 == 0) { return Azero; }
if (count2 == 1) { return Aone; }
if (count2 == 2) { return Atwo; }
if (count2 == 3) { return Athree; }
if (count2 == 4) { return Afour; }
if (count2 == 5) { return Afive; }
if (count2 == 6) { return Asix; }
}
return 0;
}
void givepower(int tt, int chestmoble[3][3][3][3][3][3]) //tt是自己执棋的颜色
{
int i[6];
int a = 0;
for (i[0] = 0; i[0] < 3; i[0]++) {
for (i[1] = 0; i[1] < 3; i[1]++) {
for (i[2] = 0; i[2] < 3; i[2]++) {
for (i[3] = 0; i[3] < 3; i[3]++) {
for (i[4] = 0; i[4] < 3; i[4]++) {
for (i[5] = 0; i[5] < 3; i[5]++) {
chestmoble[i[0]][i[1]][i[2]][i[3]][i[4]][i[5]] = power(i, tt);
}
}
}
}
}
}
}
int givevalue(int board[19][19], int tt, int chestmoble[3][3][3][3][3][3]) //计算如果下在当前点的话当前棋盘的得分
{
int i, j;
int value = 0;
int value1 = 0; //横向路价值
int value2 = 0; //竖向路价值
int value3 = 0; //左上右下斜向路价值
int value4 = 0; //右上左下斜向路价值
int bb[19][19];
for (i = 0; i < 19; i++) {
for (j = 0; j < 19; j++) {
bb[i][j] = board[i][j];
}
}
横向路搜索价值
for (i = 0; i < 19; i++) {
for (j = 0; j < 14; j++) {
value1 += chestmoble[bb[j][i]][bb[j + 1][i]][bb[j + 2][i]][bb[j + 3][i]][bb[j + 4][i]][bb[j + 5][i]];
}
}
竖向路搜索价值
for (i = 0; i < 19; i++) {
for (j = 0; j < 14; j++) {
value2 += chestmoble[bb[i][j]][bb[i][j + 1]][bb[i][j + 2]][bb[i][j + 3]][bb[i][j + 4]][bb[i][j + 5]];
}
}
int p, q; //斜向路搜索价值辅助变量
右上左下 / 斜向路搜索价值
//上半部分
for (i = 5; i < 19; i++) {
for (j = 0; j < i - 4; j++) {
p = i - j;
value3 += chestmoble[bb[p][j]][bb[p - 1][j + 1]][bb[p - 2][j + 2]][bb[p - 3][j + 3]][bb[p - 4][j + 4]][bb[p - 5][j + 5]];
}
}
//下半部分
for (i = 13; i > 0; i--) {
for (j = 0; j < 14 - i; j++) {
q = i + j;
value3 += chestmoble[bb[q][18 - j]][bb[q + 1][17 - j]][bb[q + 2][16 - j]][bb[q + 3][15 - j]][bb[q + 4][14 - j]][bb[q + 5][13 - j]];
}
}
///左上右下 \ 斜向路搜索价值
//上半部分
for (i = 13; i > -1; i--) {
for (j = 0; j < 14 - i; j++) {
p = i + j;
value4 += chestmoble[bb[p][j]][bb[p + 1][j + 1]][bb[p + 2][j + 2]][bb[p + 3][j + 3]][bb[p + 4][j + 4]][bb[p + 5][j + 5]];
}
}
//下半部分
for (i = 5; i < 18; i++) {
for (j = 0; j < i - 4; j++) {
q = i - j;
value4 += chestmoble[bb[q][18 - j]][bb[q - 1][17 - j]][bb[q - 2][16 - j]][bb[q - 3][15 - j]][bb[q - 4][14 - j]][bb[q - 5][13 - j]];
}
}
value = value1 + value2 + value3 + value4;
return value;
}
局部优先模块
void judgecango(int bigboard[25][25], int yet[25][25], int x, int y, int& cc) {
int a = x - 3;
int b = y - 3;
int i, j;
for (i = x - DT; i < x + DT + 1; i++) {
for (j = y - DT; j < y + DT + 1; j++) {
if (bigboard[i][j] != -1 && bigboard[i][j] != 2 && yet[i][j] != 1) { //当这个点不越界,不是棋子的时候,未被考虑过的时候
bigboard[i][j] = 1;
cc++;
yet[i][j] = 1;
}
}
}
}
void savecanpoint(int board[19][19], cango can[200], int& cc)
{
cc = 0;
int i, j;
int bigboard[25][25];
int yet[25][25];
for (i = 0; i < 25; i++) { //初始化
for (j = 0; j < 25; j++) {
yet[i][j] = 0; //未被考虑 记未0 考虑后带入函数 记未1
if (i < 3 || j < 3 || i>21 || j>21) {
bigboard[i][j] = -1; //棋局外的点 -1
}
else {
bigboard[i][j] = 0; //棋局内的点存为0
if (board[i - 3][j - 3] != 2) { //棋局内的点不是空白时
bigboard[i][j] = 2; //棋局内是棋子的点 保存为 2
}
}
}
}
for (i = 0; i < 25; i++) { //将棋子附近的点标记为可行
for (j = 0; j < 25; j++) {
if (bigboard[i][j] == 2) {
judgecango(bigboard, yet, i, j, cc);
}
}
}
int k = 0; //保存适合行走数组的辅助变量
for (i = 0; i < 25; i++) {
for (j = 0; j < 25; j++) {
if (bigboard[i][j] == 1) {
can[k].x = i - 3;
can[k].y = j - 3;
k++;
}
}
}
}
/启发式搜索模块
void sortA(cango can[CG], int cc, int chestmoble[3][3][3][3][3][3], int board[19][19],int tt,int& HH) {
int SS;
SS = givevalue(board, tt, chestmoble);
for (int i = 0; i < cc; i++) {
board[can[i].x][can[i].y] = tt;
can[i].va = givevalue(board, tt, chestmoble);
if (SS - can[i].va == 0) {
HH++;
}
board[can[i].x][can[i].y] = EMPTY;
}
std::sort(can, can + cc, customLessA);
}
void sortB(cango can[CG], int cc, int chestmoble[3][3][3][3][3][3], int board[19][19], int tt,int& HH) {
int SS;
SS= givevalue(board, tt, chestmoble);
for (int i = 0; i < cc; i++) {
board[can[i].x][can[i].y] = 1-tt;
can[i].va = givevalue(board, tt, chestmoble);
if (SS - can[i].va == 0) {
HH++;
}
board[can[i].x][can[i].y] = EMPTY;
}
std::sort(can, can + cc, customLessB);
}
/和局判断模块
bool peace(int board[19][19], int tt,int chestmoble[3][3][3][3][3][3]) {
int VV;
if (KK > 55) {
VV= givevalue(board, tt, chestmoble);
if (VV == 0) {
return true;
}
}
return false;
}
///博弈树模块
void searchtwo(int board[19][19], int tt, int chestmoble[3][3][3][3][3][3], Tree& FF, int CT) {
int cc;
cango can[CG];
savecanpoint(board, can, cc);
int VV;
int ZZ = 100000;
int i, j, k;
int HH=0;
sortB(can, cc, chestmoble, board,tt,HH); //对可行步进行影响力排序
if (peace(board, tt, chestmoble) != true) {
cc = cc - HH;
}
//if (CT > 1 && cc > 100) {
//cc = 100;
//}
//cout << cc << endl;
for (i = 0; i < cc; i++) {
for (j = i + 1; j < cc; j++) {
board[can[i].x][can[i].y] = 1 - tt;
board[can[j].x][can[j].y] = 1 - tt;
//show(board);
VV = givevalue(board, tt, chestmoble);
//cout << "当前选择点为" << can[i].x <<"|"<< can[i].y << "|" << can[j].x << "|" << can[j].y << endl;
// << "棋盘价值为" << VV << endl;
if (CT == 1 && VV < FF.value) {
FF.value = VV;
//showFF(FF);
FF.ffirst.x = FF.first.x;
FF.ffirst.y = FF.first.y;
FF.fsecond.x = FF.second.x;
FF.fsecond.y = FF.second.y;
}
if (CT > 1) {
//cout << "in";
if (VV < FF.value) {
board[can[i].x][can[i].y] = EMPTY;
board[can[j].x][can[j].y] = EMPTY;
return;
}
if (ZZ > VV) {
ZZ = VV;
}
}
board[can[i].x][can[i].y] = EMPTY;
board[can[j].x][can[j].y] = EMPTY;
}
}
if (ZZ > FF.value && CT > 1) {
FF.value = ZZ;
FF.ffirst.x = FF.first.x;
FF.ffirst.y = FF.first.y;
FF.fsecond.x = FF.second.x;
FF.fsecond.y = FF.second.y;
}
return;
}
Step searchone(int board[19][19], int tt, int chestmoble[3][3][3][3][3][3])
{
Step step; //返回
int cc; //可行点计数
cango can[CG];
savecanpoint(board, can, cc); //保存可行数组和可行点数
Tree FF; //辅助用的树结构
FF.first.x = 0;
FF.first.y = 0;
FF.ffirst.x = 0;
FF.ffirst.y = 0;
FF.second.x = 0;
FF.second.y = 0;
FF.fsecond.x = 0;
FF.fsecond.y = 0;
FF.value = 100000; //树节点价值初始化
int i, j, k;
int CT = 1; //计数器 记录第N个枝杈
int NN; //第一步能赢的判断
int HH=0;
sortA(can, cc, chestmoble, board,tt,HH); //对可行步进行影响力排序
//cout << HH << endl;
//cc = cc - HH;
//if (cc > 50) {
//cc = 50;
//}
for (i = 0; i < cc; i++) {
for (j = i + 1; j < cc; j++) {
FF.first.x = can[i].x;
FF.first.y = can[i].y;
FF.second.x = can[j].x;
FF.second.y = can[j].y;
board[can[i].x][can[i].y] = tt;
board[can[j].x][can[j].y] = tt;
NN = givevalue(board, tt, chestmoble);
if (NN > 400000) {
step.first.x = can[i].x;
step.first.y = can[i].y;
step.second.x = can[j].x;
step.second.y = can[j].y;
step.value = NN;
return step;
}
searchtwo(board, tt, chestmoble, FF, CT);
board[can[i].x][can[i].y] = EMPTY;
board[can[j].x][can[j].y] = EMPTY;
CT++;
}
}
step.first.x = FF.ffirst.x;
step.first.y = FF.ffirst.y;
step.second.x = FF.fsecond.x;
step.second.y = FF.fsecond.y;
step.value = FF.value;
return step;
}
//
//
int main()
{
Step step;//临时步结构
char message[256];//通信消息缓冲
int computerSide;//己方执棋颜色
int start = 0;//对局开始标记
srand(int(time(0)));
//此处放置初始化代码
//...
while (1) //程序主循环
{
fflush(stdout);//不要删除此语句,否则程序会出问题
scanf("%s", message);//获取平台命令消息
//分析命令
if (strcmp(message, "name?") == 0)//向对战平台发送队名
{
fflush(stdin);
/***********将"令狐冲"改为你的队名,不超过6个汉字或12个英文字母,否则无成绩************/
/*******/ printf("name 发际线在作队\n"); /**只修改令狐冲,不要删除name空格****/
/***********将"令狐冲"改为你的队名,不超过6个汉字或12个英文字母,否则无成绩************/
}
else if (strcmp(message, "new") == 0)//建立新棋局
{
int i, j;
scanf("%s", message);//获取己方执棋颜色
fflush(stdin);
if (strcmp(message, "black") == 0) computerSide = BLACK; //执黑
else computerSide = WHITE; //执白
for (i = 0; i < 19; ++i) //初始化棋局
for (j = 0; j < 19; ++j)
Board[i][j] = EMPTY;
start = 1;
KK = 0;
if (computerSide == BLACK)
{
/**********生成第一手着法,并保存在step结构中,落子坐标为(step.first.x,step.first.y)**********/
/****************************在下方填充代码,并替换我的示例代码******************************/
step.first.x = 9;
step.first.y = 9;
/******************************在上面填充第一步行棋代码*******************************************/
Board[step.first.x][step.first.y] = computerSide;//处理己方行棋
printf("move %c%c@@\n", step.first.x + 'A', step.first.y + 'A');//输出着法
}
}
else if (strcmp(message, "move") == 0)//行棋,本程序核心
{
scanf("%s", message);//获取对手行棋着法
fflush(stdin);
step.first.x = message[0] - 'A'; step.first.y = message[1] - 'A';
step.second.x = message[2] - 'A'; step.second.y = message[3] - 'A';
//处理对手行棋
Board[step.first.x][step.first.y] = 1 - computerSide;
if (!(step.second.x == -1 && step.second.y == -1)) Board[step.second.x][step.second.y] = 1 - computerSide;
/**********************************************************************************************************/
/***生成落子的坐标,保存在step结构中,第1子下在(step.first.x,step.first.y),第2子下在(step.first.x,step.first.y)*****/
/**************************************在下方填充代码,并替换我的示例代码*****************************************/
//生成第1子落子位置step.first.x和step.first.y
//
///
///
/
/
int i, j, k;
int chestmoble[3][3][3][3][3][3]; //生成六元组 保存相应权重
int tt = computerSide; //执子颜色赋值给tt
memset(chestmoble, 0, sizeof(chestmoble)); //初始值全部赋值为0
givepower(tt, chestmoble); //给予权重
int board[19][19];
copy(Board, board);
if (peace(board, tt,chestmoble) == true) {
int cc; //可行点计数
cango can[CG];
savecanpoint(board, can, cc); //保存可行数组和可行点数
step.first.x = can[0].x;
step.first.y = can[0].y;
step.second.x = can[1].x;
step.second.y = can[1].y;
Board[step.first.x][step.first.y] = computerSide;
Board[step.second.x][step.second.y] = computerSide;
//Board[can[0].x][can[0].y] = computerSide;
//Board[can[1].x][can[1].y] = computerSide;
}
else {
step = searchone(board, tt, chestmoble);
Board[step.first.x][step.first.y] = computerSide;
Board[step.second.x][step.second.y] = computerSide;
}
KK++;
/
/
//
///
/
/*
/*****************************************在上面填充代码******************************************************/
/**********************************************************************************************************/
printf("move %c%c%c%c\n", step.first.x + 'A', step.first.y + 'A', step.second.x + 'A', step.second.y + 'A');//输出着法
}
else if (strcmp(message, "error") == 0)//着法错误
{
fflush(stdin);
}
else if (strcmp(message, "end") == 0)//对局结束
{
fflush(stdin);
start = 0;
}
else if (strcmp(message, "quit") == 0)//退出引擎
{
fflush(stdin);
printf("Quit!\n");
break;
}
}
return 0;
}
三、 核心算法的设计思路和实现要点
1,博弈树
2,最大最小值搜索
3,a,b剪枝
4,基于路的评分系统
5,局部优先
6,启发式搜索
7,和局判断
四、 算法时间、空间复杂度的估算(可选)
1,运算次数大概是10的6到7次方之间,导致有一些慢,进行了很多优化,如果电脑计算力好的话进行到四层会更强
2,运行内存900k
五、 完成过程中遇到的问题,排除问题的主要过程、使用的方法和技巧
1,博弈树的建立 递归
2,搜索次数太大,时间太慢,a,b剪枝,局部优先,启发式搜索
六、 所用方法的特别、新颖或创新之处(可选)
因为用到的是博弈树 情况很多 计算很慢 只能到第二层 为了优化时间采取了a,b,剪枝,局部优先,启发式搜索,虽然还是很慢,但比较之前快了不少了
七、 参考文献
闫文杰 六子棋计算机博弈关键技术研究,硕士学位论文 2010 06 01
明确说明所参考的论文、书籍、网络资料、开源代码或其他研究者的程序或成果等详细信息。
能帮到你们的话点个赞吧