新手与关于三子棋的“智能化”思考(上)

目录

提要

game.c

game.h

main.c

方案一

思路

实现

优先获胜

然后堵二

插曲

总结

代码:

感慨


提要

前几天做了一下三子棋的项目,很有趣。不过美中不足就是电脑移动采用随机算法太愚钝了,导致我想输都输不了(哈哈)。然而我还没有开始学关于算法的内容,关于三子棋“智能化”的实现只能靠我自己思考。经过断断续续的思考,也有了一丝头绪。下面先把普通三子棋的代码搬过来。

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;
}

感慨

从下午开始只是弄了第一步就已经天黑了,本以为可以完成其中一个方案的,看来只能明后天再弄了。

谢谢观看,觉得不错点个赞吧!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值