目录
提要
前几天做了一下三子棋的项目,很有趣。不过美中不足就是电脑移动采用随机算法太愚钝了,导致我想输都输不了(哈哈)。然而我还没有开始学关于算法的内容,关于三子棋“智能化”的实现只能靠我自己思考。经过断断续续的思考,也有了一丝头绪。下面先把普通三子棋的代码搬过来。
game.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include"game.h"
#include<stdlib.h>
#include<time.h>
void inti_board(char b[ROW][COL])
{
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL; j++)
{
b[i][j] = ' ';
}
}
}
void print_board(char b[ROW][COL])
{
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL; j++)
{
printf(" %c ", *(*(b + i) + j));
if (j < ROW - 1)
{
printf("|");
}
}
printf("\n");
if (i < COL - 1)
{
for (int k = 0; k < ROW; k++)
{
printf("---");
if (k < ROW - 1)
{
printf("|");
}
}
printf("\n");
}
}
}
void player_move(char b[ROW][COL])
{
printf("请选择移动坐标");
int x = 0;
int y = 0;
int flag = 1;
while (flag)
{
scanf("%d %d", &x, &y);
if (b[x - 1][y - 1] == ' ' && x - 1 < ROW && y - 1 < COL)
{
b[x - 1][y - 1] = '*';
flag = 0;
}
else if (x - 1 >= ROW || y - 1 >= COL)
{
printf("没有这个位置哦,重新选择吧\n");
}
else if (b[x - 1][y - 1] != ' ')
{
printf("这里已经有棋了,重新选择吧\n");
}
}
}
static int is_win(char b[ROW][COL])
{
for (int i = 0; i < ROW; i++)
{
if (b[i][0] == b[i][1] && b[i][1] == b[i][2] && b[i][0] != ' ')
{
return b[i][0];
}
}
for (int i = 0; i < ROW; i++)
{
if (b[0][i] == b[1][i] && b[1][i] == b[2][i] && b[0][i] != ' ')
{
return b[0][i];
}
}
if (b[0][0] == b[1][1] && b[2][2] == b[1][1] && b[0][0] != ' ')
{
return b[0][0];
}
else if (b[0][2] == b[1][1] && b[2][0] == b[1][1] && b[1][1] != ' ')
{
return b[1][1];
}
else
{
return ' ';
}
}
void winer(ret)
{
if (ret == '#')
{
printf("电脑获胜\n");
}
else if (ret == '*')
{
printf("玩家获胜\n");
}
}
void pc_move(char board[ROW][COL])
{
while (1)
{
int x = rand() % ROW;
int y = rand() % COL;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
int is_full(char b[ROW][COL])
{
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL; j++)
{
if (b[i][j] == ' ')
{
return 0;
}
}
}
return 1;
}
void game()
{
char board[3][3];
inti_board(board);
while (1)
{
print_board(board);
player_move(board);
char ret = is_win(board);
int ret1 = is_full(board);
if (ret != ' ')
{
print_board(board);
winer(ret);
break;
}
else if (ret1)
{
printf("平局了\n");
break;
}
pc_move(board);
ret = is_win(board);
ret1 = is_full(board);
if (ret != ' ')
{
print_board(board);
winer(ret);
break;
}
else if (ret1)
{
printf("平局了\n");
print_board(board);
break;
}
}
}
game.h
#define _CRT_SECURE_NO_WARNINGS
#pragma once
#define ROW 3
#define COL 3
#include<stdlib.h>
#include<time.h>
void inti_board(char b[ROW][COL]);
void print_board(char b[ROW][COL]);
void game();
static int is_win(char b[ROW][COL]);
void winer(ret);
void pc_move(char board[ROW][COL]);
main.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include"game.h"
int main()
{
srand((unsigned int)time(NULL));
int n = 0;
do
{
printf("*****************************\n");
printf("*************1:GAME**********\n");
printf("*************0:EXIT**********\n");
printf("*****************************\n");
printf("请输入0或1\n");
scanf("%d", &n);
switch (n)
{
case 1:
{
game();
break;
}
case 0:
{
printf("再见\n");
return 1;
break;
}
default:
{
printf("输入有误!!\n");
break;
}
}
} while (n);
return 0;
}
方案一
思路
1.首先要明白下一步可以胜利并且完成堵对方“活二”
2.1.可以先创建一个文本,用来记录历史对局。每次下完一盘,记录本局的双方移动坐标、胜利情况。然后在新的一局中,检索有没有与历史对局相似的走法:若这么走输过,就换一下坐标;若赢过,那就按之前的走法。这就是大体思路,具体的小细节边写边想吧。
2.2.或许可以进行对对手下一步进行推演,感觉复杂一点
2.3.或与也可以对局面进行积分分析
实现
优先获胜
首先,不管再智能,轮到电脑的落子时,一定先选择自己可以胜利的地方落子;

此时,电脑一定要下在(3,2)位置,而不是去堵二或者其他位置。
void smart_pc_move(char board[ROW][COL])
{
int sign = 1;
int score = 0;
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL; j++)
{
if (board[i][j] == '#')
{
score++;
}
else if (board[i][j] == '*')
{
score--;
}
}
if (score == 2)
{
for (int j = 0; j < COL; j++)
{
if (board[i][j] == ' ')
{
board[i][j] = '#';
sign = 0;
score = 0;
break;
}
}
}
score = 0;
}
while (sign)
{
int x = rand() % ROW;
int y = rand() % COL;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
这先实现了横行的判断先胜
实现过程:
使用积分变量score记录分数,若当前是‘#’(电脑),就++;是’*‘,就--。
这就解决了路径阻挡检测问题。当score==2,就有”活二“的适合,就检测位置并填入;
同时使用标记变量sign来避免下了又下的问题。
样例:
1.

2.

3.

虽然现在在没有”活二“的情况下,电脑仍然是随机下棋状态。但检测多次,可以正式电脑似乎”聪明“点了,成功! /历程:一开始想再建一个自定义函数来实现功能,但发现自定义函数里边的自定义函数不能两者共用一个形参;并且忘了每一行重置score的数值,导致score是好几行叠加的结果。虽然很简单的功能,实现起来也很坎坷()
同理,用在斜方向和纵方向上:
void smart_pc_move(char board[ROW][COL])
{
int sign = 1;
int score = 0;
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL; j++)
{
if (board[i][j] == '#')
{
score++;
}
else if (board[i][j] == '*')
{
score--;
}
}
if (score == 2)
{
for (int j = 0; j < COL; j++)
{
if (board[i][j] == ' ')
{
board[i][j] = '#';
sign = 0;
score = 0;
break;
}
}
}
score = 0;
}
score = 0;
if (sign != 0)
{
for (int i = 0; i < COL; i++)
{
for (int j = 0; j < ROW; j++)
{
if (board[j][i] == '#')
{
score++;
}
else if (board[j][i] == '*')
{
score--;
}
}
if (score == 2)
{
for (int j = 0; j < ROW; j++)
{
if (board[j][i] == ' ')
{
board[j][i] = '#';
sign = 0;
score = 0;
break;
}
}
}
score = 0;
}
}
score = 0;
if (sign != 0)
{
for (int i = 0; i < COL; i++)
{
if (board[i][i] == '#')
{
score++;
}
else if (board[i][i] == '*')
{
score--;
}
}
if (score == 2)
{
for (int j = 0; j < COL; j++)
{
if (board[j][j] == ' ')
{
board[j][j] = '#';
sign = 0;
score = 0;
break;
}
}
}
}
score = 0;
if (sign != 0)
{
int col = 2;
int row = 0;
for (int i = 0; i < 3; i++)
{
if (board[row][col] == '#')
{
score++;
}
else if (board[row][col] == '*')
{
score--;
}
col--;
row++;
}
if (score == 2)
{
for (int i = 0; i < 3; i++)
{
if (board[row][col] == ' ')
{
board[row][col] = '#';
sign = 0;
score = 0;
break;
}
}
}
}
score = 0;
while (sign)
{
int x = rand() % ROW;
int y = rand() % COL;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
/很好,一次调试成功
后文追加:右斜线是不可以的,可以看看哪里错了
样例:


/让电脑下成自己想要测试的样子好费劲
然后堵二
在没有自己(电脑)的”活二“时,也不能乱下。要先堵住对手的“活二”;
不过实现起来还有比较简单,和上一部分有所重叠。
void smart_pc_move(char board[ROW][COL])
{
int sign = 1;
int score = 0;
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL; j++)
{
if (board[i][j] == '#')
{
score++;
}
else if (board[i][j] == '*')
{
score--;
}
}
if (score == 2)
{
for (int j = 0; j < COL; j++)
{
if (board[i][j] == ' ')
{
board[i][j] = '#';
sign = 0;
score = 0;
break;
}
}
}
else if (score == -2)
{
for (int j = 0; j < COL; j++)
{
if (board[i][j] == ' ')
{
board[i][j] = '#';
sign = 0;
score = 0;
break;
}
}
}
score = 0;
}
score = 0;
if (sign != 0)
{
for (int i = 0; i < COL; i++)
{
for (int j = 0; j < ROW; j++)
{
if (board[j][i] == '#')
{
score++;
}
else if (board[j][i] == '*')
{
score--;
}
}
if (score == 2)
{
for (int j = 0; j < ROW; j++)
{
if (board[j][i] == ' ')
{
board[j][i] = '#';
sign = 0;
score = 0;
break;
}
}
}
else if (score == -2)
{
for (int j = 0; j < ROW; j++)
{
if (board[j][i] == ' ')
{
board[j][i] = '#';
sign = 0;
score = 0;
break;
}
}
}
score = 0;
}
}
score = 0;
if (sign != 0)
{
for (int i = 0; i < COL; i++)
{
if (board[i][i] == '#')
{
score++;
}
else if (board[i][i] == '*')
{
score--;
}
}
if (score == 2)
{
for (int j = 0; j < COL; j++)
{
if (board[j][j] == ' ')
{
board[j][j] = '#';
sign = 0;
score = 0;
break;
}
}
}
else if (score == -2)
{
for (int j = 0; j < COL; j++)
{
if (board[j][j] == ' ')
{
board[j][j] = '#';
sign = 0;
score = 0;
break;
}
}
}
}
score = 0;
if (sign != 0)
{
int col = 3;
int row = 1;
for (int i = 0; i < 3; i++)
{
if (board[row-1][col-1] == '#')
{
score++;
}
else if (board[row-1][col-1] == '*')
{
score--;
}
col--;
row++;
}
col = 3;
row = 1;
if (score == 2)
{
for (int i = 0; i < 3; i++)
{
if (board[row-1][col-1] == ' ')
{
board[row-1][col-1] = '#';
sign = 0;
score = 0;
break;
}
}
}
else if (score == -2)
{
for (int i = 0; i < 3; i++)
{
if (board[row-1][col-1] == ' ')
{
board[row-1][col-1] = '#';
sign = 0;
score = 0;
break;
}
}
}
}
score = 0;
while (sign)
{
int x = rand() % ROW;
int y = rand() % COL;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
样例:




我收回刚才说的“比较简单”的话
插曲
一开始我是想把上一步的代码复制并修改一下的,但是看着太长了,像“史山”一样。于是想起用我的积分函数:-2不就代表要堵的“活二”?,于是就简单修改了一下:
else if (score == -2)
{
for (int j = 0; j < COL; j++)
{
if (board[i][j] == ' ')
{
board[i][j] = '#';
sign = 0;
score = 0;
break;
}
}
}
类似这样的代码,只需要添加一个“-”就行
但当我感觉如此快捷并测试时,发现右斜线始终不可以
由于右斜线我使用的是row = 0 col = 3 然后row++ col--实现的右斜线遍历
一开始我查找与输出都用的row与col并且没重置,导致这俩的值不知道早飞哪去了,,,
调试了一下发现是这里的问题
if (sign != 0)
{
int col = 2;
int row = 0;
for (int i = 0; i < 3; i++)
{
if (board[row][col] == '#')
{
score++;
}
else if (board[row][col] == '*')
{
score--;
}
col--;
row++;
}
然后很好发现,col可以被减为负数,原因是循环次数不对(吗?)
我就改了一下循环次数,但是还是不可以,,,
进一步调试发现:循环次数一定是三,因为要判断三个格子。
最后终于修正成功为
if (sign != 0)
{
int col = 3;
int row = 1;
for (int i = 0; i < 3; i++)
{
if (board[row-1][col-1] == '#')
{
score++;
}
else if (board[row-1][col-1] == '*')
{
score--;
}
col--;
row++;
}
col = 3;
row = 1;
if (score == 2)
{
for (int i = 0; i < 3; i++)
{
if (board[row-1][col-1] == ' ')
{
board[row-1][col-1] = '#';
sign = 0;
score = 0;
break;
}
}
}
else if (score == -2)
{
for (int i = 0; i < 3; i++)
{
if (board[row-1][col-1] == ' ')
{
board[row-1][col-1] = '#';
sign = 0;
score = 0;
break;
}
}
}
}
真是饶了好大一圈啊(哭)
总结
代码:
game.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include"game.h"
#include<stdlib.h>
#include<time.h>
void inti_board(char b[ROW][COL])
{
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL; j++)
{
b[i][j] = ' ';
}
}
}
void print_board(char b[ROW][COL])
{
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL; j++)
{
printf(" %c ", *(*(b + i) + j));
if (j < ROW - 1)
{
printf("|");
}
}
printf("\n");
if (i < COL - 1)
{
for (int k = 0; k < ROW; k++)
{
printf("---");
if (k < ROW - 1)
{
printf("|");
}
}
printf("\n");
}
}
}
void player_move(char b[ROW][COL])
{
printf("请选择移动坐标");
int x = 0;
int y = 0;
int flag = 1;
while (flag)
{
scanf("%d %d", &x, &y);
if (b[x - 1][y - 1] == ' ' && x - 1 < ROW && y - 1 < COL)
{
b[x - 1][y - 1] = '*';
flag = 0;
}
else if (x - 1 >= ROW || y - 1 >= COL)
{
printf("没有这个位置哦,重新选择吧\n");
}
else if (b[x - 1][y - 1] != ' ')
{
printf("这里已经有棋了,重新选择吧\n");
}
}
}
static int is_win(char b[ROW][COL])
{
for (int i = 0; i < ROW; i++)
{
if (b[i][0] == b[i][1] && b[i][1] == b[i][2] && b[i][0] != ' ')
{
return b[i][0];
}
}
for (int i = 0; i < ROW; i++)
{
if (b[0][i] == b[1][i] && b[1][i] == b[2][i] && b[0][i] != ' ')
{
return b[0][i];
}
}
if (b[0][0] == b[1][1] && b[2][2] == b[1][1] && b[0][0] != ' ')
{
return b[0][0];
}
else if (b[0][2] == b[1][1] && b[2][0] == b[1][1] && b[1][1] != ' ')
{
return b[1][1];
}
else
{
return ' ';
}
}
void winer(ret)
{
if (ret == '#')
{
printf("电脑获胜\n");
}
else if (ret == '*')
{
printf("玩家获胜\n");
}
}
void smart_pc_move(char board[ROW][COL])
{
int sign = 1;
int score = 0;
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL; j++)
{
if (board[i][j] == '#')
{
score++;
}
else if (board[i][j] == '*')
{
score--;
}
}
if (score == 2)
{
for (int j = 0; j < COL; j++)
{
if (board[i][j] == ' ')
{
board[i][j] = '#';
sign = 0;
score = 0;
break;
}
}
}
else if (score == -2)
{
for (int j = 0; j < COL; j++)
{
if (board[i][j] == ' ')
{
board[i][j] = '#';
sign = 0;
score = 0;
break;
}
}
}
score = 0;
}
score = 0;
if (sign != 0)
{
for (int i = 0; i < COL; i++)
{
for (int j = 0; j < ROW; j++)
{
if (board[j][i] == '#')
{
score++;
}
else if (board[j][i] == '*')
{
score--;
}
}
if (score == 2)
{
for (int j = 0; j < ROW; j++)
{
if (board[j][i] == ' ')
{
board[j][i] = '#';
sign = 0;
score = 0;
break;
}
}
}
else if (score == -2)
{
for (int j = 0; j < ROW; j++)
{
if (board[j][i] == ' ')
{
board[j][i] = '#';
sign = 0;
score = 0;
break;
}
}
}
score = 0;
}
}
score = 0;
if (sign != 0)
{
for (int i = 0; i < COL; i++)
{
if (board[i][i] == '#')
{
score++;
}
else if (board[i][i] == '*')
{
score--;
}
}
if (score == 2)
{
for (int j = 0; j < COL; j++)
{
if (board[j][j] == ' ')
{
board[j][j] = '#';
sign = 0;
score = 0;
break;
}
}
}
else if (score == -2)
{
for (int j = 0; j < COL; j++)
{
if (board[j][j] == ' ')
{
board[j][j] = '#';
sign = 0;
score = 0;
break;
}
}
}
}
score = 0;
if (sign != 0)
{
int col = 3;
int row = 1;
for (int i = 0; i < 3; i++)
{
if (board[row-1][col-1] == '#')
{
score++;
}
else if (board[row-1][col-1] == '*')
{
score--;
}
col--;
row++;
}
col = 3;
row = 1;
if (score == 2)
{
for (int i = 0; i < 3; i++)
{
if (board[row-1][col-1] == ' ')
{
board[row-1][col-1] = '#';
sign = 0;
score = 0;
break;
}
}
}
else if (score == -2)
{
for (int i = 0; i < 3; i++)
{
if (board[row-1][col-1] == ' ')
{
board[row-1][col-1] = '#';
sign = 0;
score = 0;
break;
}
}
}
}
score = 0;
while (sign)
{
int x = rand() % ROW;
int y = rand() % COL;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
int is_full(char b[ROW][COL])
{
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL; j++)
{
if (b[i][j] == ' ')
{
return 0;
}
}
}
return 1;
}
void game()
{
char board[3][3];
inti_board(board);
while (1)
{
print_board(board);
player_move(board);
char ret = is_win(board);
int ret1 = is_full(board);
if (ret != ' ')
{
print_board(board);
winer(ret);
break;
}
else if (ret1)
{
printf("平局了\n");
break;
}
smart_pc_move(board);
ret = is_win(board);
ret1 = is_full(board);
if (ret != ' ')
{
print_board(board);
winer(ret);
break;
}
else if (ret1)
{
printf("平局了\n");
print_board(board);
break;
}
}
}
game.h
#define _CRT_SECURE_NO_WARNINGS
#pragma once
#define ROW 3
#define COL 3
#include<stdlib.h>
#include<time.h>
void inti_board(char b[ROW][COL]);
void print_board(char b[ROW][COL]);
void game();
static int is_win(char b[ROW][COL]);
void winer(ret);
void smart_pc_move(char board[ROW][COL]);
main.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include"game.h"
int main()
{
FILE* p = fopen("E:\\gitee库\\study.c\\三子棋plus\\data.txt","a+");
srand((unsigned int)time(NULL));
int n = 0;
do
{
printf("*****************************\n");
printf("*************1:GAME**********\n");
printf("*************0:EXIT**********\n");
printf("*****************************\n");
printf("请输入0或1\n");
scanf("%d", &n);
switch (n)
{
case 1:
{
game();
break;
}
case 0:
{
printf("再见\n");
return 1;
break;
}
default:
{
printf("输入有误!!\n");
break;
}
}
} while (n);
return 0;
}
感慨
从下午开始只是弄了第一步就已经天黑了,本以为可以完成其中一个方案的,看来只能明后天再弄了。
谢谢观看,觉得不错点个赞吧!
&spm=1001.2101.3001.5002&articleId=163827788&d=1&t=3&u=cb51d5d97ea04870a6b774832b19e32b)
287

被折叠的 条评论
为什么被折叠?



