【C语言初阶】三子棋优化,让电脑下棋更聪明

文章介绍了如何优化三子棋游戏中电脑的下棋策略,使其能主动堵截玩家并进攻。通过斜向、横向和纵向的判断,当玩家有连续两个棋子时,电脑会选择合适的位置阻止玩家或尝试自己获胜。同时,文章提到了使用`goto`语句的原因和替代`break`可能产生的问题,以确保电脑每次只下一颗棋子。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一.优化:

1.斜向下判断:

2.斜向上判断:

3.行判断:

4.列判断:

二.优化电脑下棋的全部代码:

三.完整代码:

game.h:

test.c:

game.c:


前言:在之前我们就写了简易版的三子棋,在电脑下棋的时候,电脑像一个傻子一样,随机下棋。

在玩家马上要赢得时候,也不会去堵玩家的棋。

今天我们要优化的就是:

1.电脑会去堵截玩家

2.电脑会主动进攻。

一.优化:

1.斜向下判断:

玩家快赢的时候,也就是两个格子都是玩家棋的时候,电脑会主动拦截

前两次的都下的玩家的棋,如果这一次也是玩家下棋,那么电脑会主动赢下比赛

void ComputerMove(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("电脑下棋:>\n");
	while (1)
	{
		x = rand() % row;
		x = rand() % col;
		int i = 0;
		int j = 0;
		int a = 0;
		int b = 0;
		int count1 = 0;
		int count2 = 0;
		
		if (board[1][1] == ' ')
		{
		//如果中间格子为空格,电脑就下棋把它占下
		//占下中间的空格,赢得概率比较大
			board[1][1] = '#';
			break;
		}
		//判断斜向下
		for (i = 0, j = 0, count1 = 0, count2 = 0; i < ROW, j < COL; i++, j++)
		{
			if (board[i][j] == '#')
				count1++;
			else if (board[i][j] == '*')
				count2++;
			else
			{
            //如果board[i][j]=' '时,a就等于i,b就等于j
            //下面的board[a][j]='#',就会在这个空格上面下棋
				a = i;
				b = j;
			}
		}

		if ((count1 == ROW - 1 || count2 == ROW - 1) && (count1 + count2) != ROW)
           {
            //如果count1是2的话,那么就是之前有两个格子是#,下面电脑就会下#,从而赢下比赛
            //如果count2是2的话,那么就是之前有两个格子是*,下面电脑就会下#,来拦截玩家
            //这里count1+count2不能等于3。如果等于3了,那这斜向下的三个位置都已经下了棋了,
            //后面也就没法下棋了
			board[a][b] = '#';
			goto end;//这里我们最后说
		}

2.斜向上判断:

这里和上面的代码是差不多的,基本上一样,我们知道,斜向上的三个格子的数组坐标分别为

[2][0],[1][1]和[0][2],所以下面for循环里面的内容和上面有一点点差别。其他代码和上面的代码是一样的。

for (i = 0, j = COL - 1, count1 = 0, count2 = 0; i < ROW, j >= 0; i++, j--)
		{
			if (board[i][j] == '#')
				count1++;
			else if (board[i][j] == '*')
				count2++;
			else
			{
				a = i;
				b = j;
			}
		}
		if ((count1 == ROW - 1 || count2 == ROW - 1) && (count1 + count2) != ROW)
		{
			board[a][b] = '#';
			goto end;
		}

3.行判断:

for (i = 0; i < ROW; i++)
		{
			count1 = 0;
			count2 = 0;
			for (j = 0; j < COL; j++)
			{
				if (board[i][j] == '#')
					count1++;
				else if (board[i][j] == '*')
					count2++;
				else
				{
					a = i;
					b = j;
				}
			}
			if ((count1 == ROW - 1 || count2 == ROW - 1) && (count1 + count2) != ROW)
			{
				board[a][b] = '#';
				goto end;
			}
		}

4.列判断:

for (j = 0; j < COL; j++)
		{
			count1 = 0;
			count2 = 0;
			for (i = 0; i < ROW; i++)
			{
				if (board[i][j] == '#')
					count1++;
				else if (board[i][j] == '*')
					count2++;
				else
				{
					a = i;
					b = j;
				}
			}
			if ((count1 == ROW - 1 || count2 == ROW - 1) && (count1 + count2) != ROW)
			{
				board[a][b] = '#';
				goto end;
			}
		}

二.优化电脑下棋的全部代码:

void ComputerMove(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("电脑下棋:>\n");
	while (1)
	{
		x = rand() % row;
		x = rand() % col;
		int i = 0;
		int j = 0;
		int a = 0;
		int b = 0;
		int count1 = 0;
		int count2 = 0;
		
		if (board[1][1] == ' ')
		{
		//如果中间格子为空格,电脑就下棋把它占下
		//占下中间的空格,赢得概率比较大
			board[1][1] = '#';
			break;
		}
		//判断斜向下
		for (i = 0, j = 0, count1 = 0, count2 = 0; i < ROW, j < COL; i++, j++)
		{
			if (board[i][j] == '#')
				count1++;
			else if (board[i][j] == '*')
				count2++;
			else
			{
				a = i;
				b = j;
			}
		}

		if ((count1 == ROW - 1 || count2 == ROW - 1) && (count1 + count2) != ROW)
		{
			board[a][b] = '#';
			goto end;
		}


		for (i = 0, j = COL - 1, count1 = 0, count2 = 0; i < ROW, j >= 0; i++, j--)
		{
			if (board[i][j] == '#')
				count1++;
			else if (board[i][j] == '*')
				count2++;
			else
			{
				a = i;
				b = j;
			}
		}
		if ((count1 == ROW - 1 || count2 == ROW - 1) && (count1 + count2) != ROW)
		{
			board[a][b] = '#';
			goto end;
		}

		//行判断
		for (i = 0; i < ROW; i++)
		{
			count1 = 0;
			count2 = 0;
			for (j = 0; j < COL; j++)
			{
				if (board[i][j] == '#')
					count1++;
				else if (board[i][j] == '*')
					count2++;
				else
				{
					a = i;
					b = j;
				}
			}
			if ((count1 == ROW - 1 || count2 == ROW - 1) && (count1 + count2) != ROW)
			{
				board[a][b] = '#';
				goto end;
			}
		}

		//列判断
		for (j = 0; j < COL; j++)
		{
			count1 = 0;
			count2 = 0;
			for (i = 0; i < ROW; i++)
			{
				if (board[i][j] == '#')
					count1++;
				else if (board[i][j] == '*')
					count2++;
				else
				{
					a = i;
					b = j;
				}
			}
			if ((count1 == ROW - 1 || count2 == ROW - 1) && (count1 + count2) != ROW)
			{
				board[a][b] = '#';
				goto end;
			}
		}

		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
		end:
			break;
		}
	}
}

上述我们还有一个问题,为什么这里会用goto语句呢?这里我们就要先学习一下goto语句的作用。

C 语言中的 goto 语句允许把控制无条件转移到同一函数内的被标记的语句

上面代码我们写的goto end,当电脑下棋了#之后,就会执行goto语句到下一个end,可知end在最后的代码处,然后break跳出整个while循环。非常完美。

那如果我们把每一个地方的goto end换成break发生什么呢?

这里我们可以看出电脑第二次下棋的时候一次性下了2格子,这是为什么呢?那为什么第一次电脑下棋只下了1次呢?

这里就要看break的作用了,在玩家下棋的时候,没有下最中间的位置,那么电脑第一次下棋就会下在最中间的位置,又因为这里有一个break。

if (board[1][1] == ' ')
		{
		//如果中间格子为空格,电脑就下棋把它占下
		//占下中间的空格,赢得概率比较大
			board[1][1] = '#';
			break;
		}

 所以break跳出整个循环。第二次玩家又下棋,然后就是电脑第二次下棋,这里玩家已经两个连起了,所以电脑就会在行这里下棋,来堵截玩家

继续这里又有一个break,但是break在while循环里面的for循环里面break只跳出for循环(在上述代码行判断可以查看,if语句在for循环里面)。

所以代码还在while循环里面运行,代码继续走到最后一个位置。

if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}

if语句判断棋盘还有空格没有,很明显棋盘还有很多空格,所以电脑又会随机下一个格子,之后break再跳出while循环。所以这就是为什么电脑第二次下了两个棋子。

我们的目的是无论是玩家还是电脑都是一次下一个棋,所以这里我们用goto语句,跳到最后一个break语句,然后break语句跳出整个while循环,所以电脑就只能一次下一个棋了

试玩一下:

 可以看出电脑已经变聪明了,哈哈。

三.完整代码:

game.h:

#pragma once
//函数的定义
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#define ROW 3
#define COL 3
//初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col);
//打印棋盘
void Print_Board(char board[ROW][COL], int row, int col);
//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col);
//电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col);
//判断输赢
char Is_Win(char board[ROW][COL], int row, int col);

test.c:

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu()
{
	printf("********************\n");
	printf("****** 三子棋 ******\n");
	printf("****** 1.play ******\n");
	printf("****** 0.exit ******\n");
	printf("********************\n");
}
	void game()
	{
		char board[ROW][COL] = { 0 };
		InitBoard(board, ROW, COL);
		Print_Board(board, ROW, COL);
		char ret = 0;
		while (1)
		{
			PlayerMove(board, ROW, COL);
			Print_Board(board, ROW, COL);
			ret = Is_Win(board, ROW, COL);
			if (ret != 'C')
			{
				break;
			}
			ComputerMove(board, ROW, COL);
			Print_Board(board, ROW, COL);
			ret = Is_Win(board, ROW, COL);
			if (ret != 'C')
			{
				break;
			}
		}
		if ('#' == ret)
			printf("电脑赢\n");
		else if ('*' == ret)
			printf("恭喜你,你赢了\n");
		else if ('Q' == ret)
			printf("平局\n");
	}
int main()
{
	srand((unsigned int)time(NULL));
	int input = 0;
	do 
	{
		menu();
		printf("请输入:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);

	return 0;
}

game.c:

#define _CRT_SECURE_NO_WARNINGS 1
//函数的实现
#include"game.h"
void InitBoard(char board[ROW][COL], int row, int col)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			board[i][j] = ' ';
		}
	}
}
void Print_Board(char board[ROW][COL], int row, int col)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++) {
			printf(" %c ",board[i][j]);
			if (j < col - 1)
				printf("|");
		}
		printf("\n");
		if(i<row-1)
			for (j= 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)
					printf("|");
			}
		printf("\n");
	}
}
void PlayerMove(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	while (1)
	{
		printf("请输入你要下棋的坐标:>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col )
		{
			if (board[x - 1][y - 1] == ' ') {
				board[x - 1][y - 1] = '*';
				break;
			}
			else
				printf("坐标被占用,请重新输入\n");
		}
		else
			printf("输入的坐标不合法,请重新输入\n");
	}
}
void ComputerMove(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("电脑下棋:>\n");
	while (1)
	{
		x = rand() % row;
		x = rand() % col;
		int i = 0;
		int j = 0;
		int a = 0;
		int b = 0;
		int count1 = 0;
		int count2 = 0;
		
		if (board[1][1] == ' ')
		{
		//如果中间格子为空格,电脑就下棋把它占下
		//占下中间的空格,赢得概率比较大
			board[1][1] = '#';
			break;
		}
		//判断斜向下
		for (i = 0, j = 0, count1 = 0, count2 = 0; i < ROW, j < COL; i++, j++)
		{
			if (board[i][j] == '#')
				count1++;
			else if (board[i][j] == '*')
				count2++;
			else
			{
				a = i;
				b = j;
			}
		}

		if ((count1 == ROW - 1 || count2 == ROW - 1) && (count1 + count2) != ROW)
		{
			board[a][b] = '#';
			goto end;
		}


		for (i = 0, j = COL - 1, count1 = 0, count2 = 0; i < ROW, j >= 0; i++, j--)
		{
			if (board[i][j] == '#')
				count1++;
			else if (board[i][j] == '*')
				count2++;
			else
			{
				a = i;
				b = j;
			}
		}
		if ((count1 == ROW - 1 || count2 == ROW - 1) && (count1 + count2) != ROW)
		{
			board[a][b] = '#';
			goto end;
		}

		//行判断
		for (i = 0; i < ROW; i++)
		{
			count1 = 0;
			count2 = 0;
			for (j = 0; j < COL; j++)
			{
				if (board[i][j] == '#')
					count1++;
				else if (board[i][j] == '*')
					count2++;
				else
				{
					a = i;
					b = j;
				}
			}
			if ((count1 == ROW - 1 || count2 == ROW - 1) && (count1 + count2) != ROW)
			{
				board[a][b] = '#';
				goto end;
			}
		}

		//列判断
		for (j = 0; j < COL; j++)
		{
			count1 = 0;
			count2 = 0;
			for (i = 0; i < ROW; i++)
			{
				if (board[i][j] == '#')
					count1++;
				else if (board[i][j] == '*')
					count2++;
				else
				{
					a = i;
					b = j;
				}
			}
			if ((count1 == ROW - 1 || count2 == ROW - 1) && (count1 + count2) != ROW)
			{
				board[a][b] = '#';
				goto end;
			}
		}

		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
		end:
			break;
		}
	}
}
int Is_Full(char board[ROW][COL], int row, int col)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			if (board[i][j] == ' ')
				return 0;
		}
	}
	return 1;
}
char Is_Win(char board[ROW][COL], int row, int col)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ')
			return board[i][0];
		}
	for (j = 0; j < col; j++)
	{
		if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[0][j] != ' ')
			return board[0][j];
	}
	if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
		return board[1][1];
	if (board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[1][1] != ' ')
		return board[1][1];
	if (Is_Full(board, row, col))
	{
		return 'Q';
	}
	return 'C';
}

参考:【C语言】 三子棋小游戏 (超详解+游戏优化+原代码) 

优化代码原作者:程序羊羊的笔记本

最后感谢大家的支持。

评论 26
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值