首页 > 其他分享 >【三子棋】——玩家VS电脑(C语言实现)

【三子棋】——玩家VS电脑(C语言实现)

时间:2023-09-22 20:31:53浏览次数:48  
标签:int 三子 C语言 VS board && row COL ROW

(文章目录)

前言

三子棋是黑白棋的一种。三子棋是一种民间传统游戏,又叫九宫棋、圈圈叉叉、一条龙、井字棋等。将正方形对角线连起来,相对两边依次摆上三个双方棋子,只要将自己的三个棋子走成一条线,对方就算输了。但是,有很多时候会出现和棋的情况。

在这里插入图片描述 :pig:好了话不多说,现在我就带大家用C语言来实现一下这个小游戏

1.游戏框架

游戏主要分三个模块实现:game.h、game.c和test.c

1.1 game.h

<font color="red">game.h:</font>用来写相关头文件,游戏中各部分函数的声明和预处理指令的实现

:dog:game.h的具体实现:

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 3
#define COL 3

//初始化棋盘
void init_board(char board[ROW][COL], int row, int col);

//打印棋盘
void print_board(char board[ROW][COL], int row, int col);

//玩家下棋
void player_move(char board[ROW][COL], int row, int col,char x);

//电脑下棋
void computer_board(char board[ROW][COL], int row, int col, char x);

//判断输赢
char is_win(char board[ROW][COL], int row, int col,char ch);

1.2 test.c

<font color="red">test.c:</font>主函数及游戏逻辑函数的实现

1.首先我们需要实现一个main函数,并将游戏菜单menu函数放入主函数中,使用一个循环来控制我们是否开始游戏,这里我们需要用到rand、srand和time函数来生成随机数,并将它运用于电脑下棋的坐标 在这里插入图片描述 2.接下来是游戏逻辑实现函数play_game函数:

  • 写一个二维数组来实现棋盘并将其初始化,棋盘的具体实现见后面的详解 在这里插入图片描述
  • 接下来分别由玩家和电脑下棋,玩家下棋用字符’*‘,电脑下棋用字符’#‘,并使用一个循环来进行玩家和电脑间的对弈,这里我们需要注意的是每次玩家(或电脑)下棋后都需要一个判断游戏输赢的函数来判断一下游戏状态
  • 通过游戏输赢函数的返回值来决定游戏是否继续,若游戏继续,将此时棋盘上的状态打印出来并由对方(玩家或电脑)继续下棋 在这里插入图片描述
  • 当棋盘已满或者有一方取得胜利时则退出循环并公布游戏结果 在这里插入图片描述

:bird:test.c的具体实现:

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"

//游戏逻辑实现函数
void play_game()
{
	char result;
	char board[ROW][COL] = { 0 };
	init_board(board, ROW, COL);//初始化棋盘
	print_board(board, ROW, COL);//打印棋盘
	while (1)
	{
		player_move(board, ROW, COL, '*');//玩家下棋
		result = is_win(board, ROW, COL, '*');//判断输赢
		if (result == '*' || result == 'q')
			break;
		print_board(board, ROW, COL);//打印棋盘
		computer_board(board, ROW, COL, '#');//电脑下棋
		result = is_win(board, ROW, COL, '#');//判断输赢
		if (result == '#' || result == 'q')
			break;
		print_board(board, ROW, COL);//打印棋盘
	}
	//判断输赢:  玩家: *  电脑: #  平局: q 
	if (result == '*')
		printf("恭喜您获得了胜利\n");
	else if (result == '#')
		printf("很遗憾,电脑赢了\n");
	else if (result == 'q')
		printf("平局\n");
	print_board(board, ROW, COL);//打印棋盘
}

//菜单函数
void menu()
{
	printf("|^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^|\n");
	printf("|                         1.play                        |\n");
	printf("|                         0.exit                        |\n");
	printf("|_______________________________________________________|\n");
}

//主函数
int main()
{
	//随机数生成器
	srand((unsigned int)time(NULL));
	int input = 0;
	do
	{
		//游戏菜单的实现
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			play_game();
			break;
		case 0:
			printf("退出程序\n");
			break;
		default:
			printf("选择错误,请重新选择:>");
			break;
		}
	} while (input);
	return 0;
}

1.3 game.c

<font color="red">game.c:</font>游戏中各个接口函数的实现

:dog:由于这个模块是实现游戏过程中各个功能函数的实现,我们把它放到<font color="red">游戏实现</font>这个模块来一一讲述

2.游戏实现

2.1初始化棋盘

这里我们需要用到一个3*3的二维数组,并将其每个元素都置为空白字符,这里我们可以用字符 '空格' 来实现。

//初始化棋盘
void init_board(char board[ROW][COL], int row, int col) 
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			board[i][j] = ' ';
		}
	}
}

2.2打印棋盘

为了使棋盘的打印更为美观

  • 二维数组数组中的每一个元素都采用”空格 元素 空格“的方式打印
  • 采用符号” | “和”- - -“进行棋盘的部署
//打印棋盘
void print_board(char board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			printf(" %c ", board[i][j]);
			if (j < col - 1)//最后一列不需要打印
				printf("|");
		}
		printf("\n");
		if (i < row - 1)//最后一行不需要打印这两个字符
		{
			for (int j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)
					printf("|");
			}
		}
		printf("\n");
	}
}

2.3玩家下棋

  • 函数中的最后一个形参char x用来接收玩家下棋时的字符 #
  • 用一个循环来控制玩家的输入,若输入的坐标不符合游戏规则,则会提示玩家重新输入,如果坐标合法,则落子
  • <font color="red">这里需要注意的是:</font> 由于数组下标是从0开始的,而我们输入的是真实的坐标,所以在判断坐标是否合理和落子时都必须将横纵坐标减一
//玩家下棋
void player_move(char board[ROW][COL], int row, int col, char x)
{
	int a, b;
	printf("玩家下棋:\n");
	while (1)
	{
		printf("请输入坐标:>");
		scanf("%d%d", &a, &b);
		//判断坐标是否合法
		if ((a >= 1 && a <= row) && (b >= 1 && b <= col))
		{
			//判断坐标是否为空
			if (board[a - 1][b - 1] != ' ')
			{
				printf("位置已被占用,请重新输入\n");
			}
			else
			{
				board[a - 1][b - 1] = x;
				break;
			}
		}
		else
		{
			printf("您的输入不合法,请重新输入\n");
		}
	}
	if ((a >= 1 && a <= row)&& (b >= 1 && b <= col))
	{
		board[a - 1][b - 1] = x;
	}
}

2.3电脑下棋

这个就相对简单了,不过需要注意的是

  • 我们需要通过%ROW和%COL来控制生成的随机坐标在数组的大小范围内,若此坐标没被占用,则落子
//电脑下棋
void computer_board(char board[ROW][COL], int row, int col, char x)
{
	printf("电脑下棋\n");
	while(1)
	{
		//生成随机数
		int a = rand() % row;
		int b = rand() % col;
		//判断坐标是否已被占用
		if (board[a][b] == ' ')
		{
			board[a][b] = x;
			break;
		}
	}
}

2.4判断输赢

  • 为了能使我们的代码更加灵活,只需要修改二维数组的行和列就可以实现<font color="red">N子棋</font>,使用行数形参char ch接收到无论是电脑还是玩家所落子的“==字符==”,并使用<font color="blue">计数器</font>的方式按照<font color="blue">列——行——主对角线——副对角线</font>的顺序依次进行判断,若满足三行三列或者对角线的字符都相同,则返回该字符
  • 如果在判断过程中无论玩家还是电脑都不能取胜,还需要分装一个函数来判断棋盘是否已满,在<font color="blue">is_win</font>函数中调用该函数,若棋盘已满,则返回“==q==”
//判断输赢
char is_win(char board[ROW][COL], int row, int col, char ch)
{
	int count = 0;
	int i;
		//判断每一列
		int j = 0;
		for (int i = 0, j = 0; j < col; j++)
		{
			if (board[i][j] == ch)
			{
				count++;
				for (int k = i + 1; k < row; k++)
				{
					if (board[k][j] == ch)
						count++;
					else
						break;
				}
			}
			if (count == row)
				return ch;
			else
				count = 0;
		}
		count = 0;
		//判断每一行
		for (int j = 0,i = 0; i < row; i++)
		{
			if (board[i][j] == ch)
			{
				count++;
				for (int k = j + 1; k < col; k++)
				{
					if (board[i][k] == ch)
						count++;
					else
						break;
				}
			}
			if (count == row)
				return ch;
			else
				count = 0;
		}
		count = 0;
	//判断主对角线
	 i = 0;
	 j = 0;
	while (i < row)
	{
		if(board[i++][j++] == ch)
			count++;
	}
	if (count == row)
		return ch;
	count = 0;
	//判断副对角线
	i = 0;
	while (i < row)
	{
		for (j = 0; j < col; j++)
		{
			if (i + j == row - 1)
			{
				if (board[i][j] == ch)
					count++;
			}
		}
		i++;
	}
	if (count == row)
		return ch;
	int p = is_full(board, row, col);
	if (p == 0)
	{
		return 'q';
	}
}

:pig:判断棋盘是否已满

//判断棋盘是否已满
int is_full(char board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			if (board[i][j] == ' ')
				return 1;
		}
	}
	return 0;
}

2.5游戏结局

<font color="red">注:这是<font color="blue">test.c</font>文件中的一段代码</font>

  • 调用<font color="blue">is_win</font>函数时,若是玩家下棋后判断输赢,传参时需要将玩家所下的“==#==”传过去,否则则将电脑所下的字符“==*==”进行传参
  • <font color="blue">is_win</font>函数会根据玩家或电脑落子后传参的不同返回三种不同的字符:“==#==”——电脑获胜、“==*==”——玩家获胜,若返回“==q==”,则说明棋盘已满,最后当然是平局喽,当然无论最后是哪一种结局,都需要将棋盘上的结果打印一下
while (1)
	{
		player_move(board, ROW, COL, '*');//玩家下棋
		result = is_win(board, ROW, COL, '*');//判断输赢
		if (result == '*' || result == 'q')
			break;
		print_board(board, ROW, COL);//打印棋盘
		computer_board(board, ROW, COL, '#');//电脑下棋
		result = is_win(board, ROW, COL, '#');//判断输赢
		if (result == '#' || result == 'q')
			break;
		print_board(board, ROW, COL);//打印棋盘
	}
	//判断输赢:  玩家: *  电脑: #  平局: q 
	if (result == '*')
		printf("恭喜您获得了胜利\n");
	else if (result == '#')
		printf("很遗憾,电脑赢了\n");
	else if (result == 'q')
		printf("平局\n");
	print_board(board, ROW, COL);//打印棋盘

3.电脑下棋优化

我们知道在前面的代码中,因为电脑是随机落子的,所以电脑获胜的可能性实在太小了,为了能让电脑下棋能够变得更加灵活,博主特地改编了电脑落子的方式

  • 先判断下一步棋是否能让玩家或者电脑任意一方获胜
  • 若玩家将要赢,则去赌玩家 若电脑将要赢,则落子到对应位置让自己获胜
  • 倘若两总情况都不满足,则自己随机落子
//电脑下棋(智能下棋)
void computer_board(char board[ROW][COL], int row, int col, char x)
{
	printf("电脑下棋\n");
	//若玩家将要赢,则去赌玩家
	//主对角线
	int i =0, j = 0;
	if ((board[i][j] == '#' && board[i + 1][j + 1] == '#'&& board[i + 2][j + 2]==' ')
		||(board[i][j] == '*' && board[i + 1][j + 1] == '*' && board[i + 2][j + 2] == ' '))
	{
		board[i + 2][j + 2] = x;
		return;
	}
	else if ((board[i][j] == '#' && board[i + 2][j + 2] == '#'&& board[i + 1][j + 1]==' ')
		    || (board[i][j] == '*' && board[i + 2][j + 2] == '*' && board[i + 1][j + 1] == ' '))
	     {
		board[i + 1][j + 1] = x;
		return;
	     }
	else if ((board[i + 1][j + 1] == '#' && board[i + 2][j + 2] == '#'&& board[i][j]==' ')
		|| (board[i + 1][j + 1] == '*' && board[i + 2][j + 2] == '*' && board[i][j] == ' '))
	     {
		board[i][j] = x;
		return;
	     }
	//副对角线
	 i = 0, j = 2;
	if ((board[i][j] == '#' && board[j][i] == '#'&& board[i + 1][j - 1]==' ')
		|| (board[i][j] == '*' && board[j][i] == '*' && board[i + 1][j - 1] == ' '))
	{ 
		board[i + 1][j - 1] = x;
		return;
	}
	else if ((board[i][j] == '#' && board[i + 1][j - 1] == '#'&& board[j][i]==' ')
		|| (board[i][j] == '*' && board[i + 1][j - 1] == '*' && board[j][i] == ' '))
	     {
		board[j][i] = x;
		return;
	     }
	else if ((board[j][i] == '#' && board[i + 1][j - 1] == '#'&& board[i][j]==' ')
		|| (board[j][i] == '*' && board[i + 1][j - 1] == '*' && board[i][j] == ' '))
	     {
		board[i][j] = x;
		return;
	     }
	//列
	j = 0;
	for (int i = 0, j = 0; j < col; j++)
	{
		if ((board[i][j] == '#' && board[i + 1][j] == '#' && board[i + 2][j] == ' ')
			|| (board[i][j] == '*' && board[i + 1][j] == '*' && board[i + 2][j] == ' '))
		{
			board[i + 2][j] = x;
			return;
		}
		else if ((board[i][j] == '#' && board[i + 2][j] == '#' && board[i + 1][j] == ' ')
			|| (board[i][j] == '*' && board[i + 2][j] == '*' && board[i + 1][j] == ' '))
		     {
			board[i + 1][j] = x;
			return;
		     }
		else if ((board[i + 1][j] == '#' && board[i + 2][j] == '#' && board[i][j] == ' ')
			|| (board[i + 1][j] == '*' && board[i + 2][j] == '*' && board[i][j] == ' '))
		     {
			board[i][j] = x;
			return;
		     }
	}
	//行
		for (int j = 0, i = 0; i < row; i++)
		{
			if ((board[i][j] == '#' && board[i][j + 1] == '#' && board[i][j + 2] == ' ')
				|| (board[i][j] == '*' && board[i][j + 1] == '*' && board[i][j + 2] == ' '))
			{
				board[i][j + 2] = x;
				return;
			}
			else if ((board[i][j] == '#' && board[i][j + 2] == '#' && board[i][j + 1] == ' ')
				|| (board[i][j] == '*' && board[i][j + 2] == '*' && board[i][j + 1] == ' '))
			     {
				board[i][j + 1] = x;
				return;
			     }
			else if ((board[i][j + 1] == '#' && board[i][j + 2] == '#' && board[i][j] == ' ')
				|| (board[i][j + 1] == '*' && board[i][j + 2] == '*' && board[i][j] == ' '))
			     {
				board[i][j] = x;
				return;
			     }
		}
	//若下一步棋玩家赢不了,电脑也赢不了,则随机下棋
	while (1)
	{
		//生成随机数
		int a = rand() % row;
		int b = rand() % col;
		//判断坐标是否已被占用
		if (board[a][b] == ' ')
		{
			board[a][b] = x;
			break;
		}
	}
}

4.小结

好了,到这里这个简单的C语言小游戏就实现了,其实代码量还是挺多的,如果大家觉得写的还不错的话还望给博主个三连哦!:kissing::kissing:

标签:int,三子,C语言,VS,board,&&,row,COL,ROW
From: https://blog.51cto.com/u_16209813/7571237

相关文章

  • c语言双指针法--原地删除数组中的元素
     27.移除元素-力扣(LeetCode) intremoveElement(int*nums,intnumsSize,intval){intleft=0;intright=0;while(right<numsSize){if(nums[right]!=val){nums[left]=nums[right];left++;}......
  • c语言-关键字static
    局部变量:运行周期=函数的运行周期全局变量:运行周期=整个程序的运行周期(程序可以是多个.c文件组成)static可以修饰:1、局部变量(函数内定义的)2、全局变量(函数外定义的) 3、函数1.修饰局部变量->静态局部变量:开辟存储空间。在编译的过程中,会在数据区为该变量开辟空间,并对其进行......
  • vscode highlight-words 插件
    转载于:https://www.niftyadmin.cn/n/4938901.html?action=onClick设置步骤:按按Ctrl+Shift+P,输入HighlightToggleCurrent,点击右边齿轮图标,进入快捷键页面,点击编辑按钮,按F8,然后按Enter键完成设置其他不变,调整box  "highlightwords.box":{    "light":true, ......
  • VMware vSphere 8.0 Update 2 正式版发布 - 企业级工作负载平台
    VMwarevSphere8.0Update2正式版发布-企业级工作负载平台2023-09-21,北京时间22日凌晨vSphere8.0Update2正式发布。ESXi8.0U2&vCenterServer8.0U2请访问原文链接:https://sysin.org/blog/vmware-vsphere-8-u2/,查看最新版。原创作品,转载请保留出处。作者主页......
  • VMware vCenter Server 8.0U2 发布 - 集中式管理 vSphere 环境
    VMwarevCenterServer8.0U2发布-集中式管理vSphere环境2023-09-21,北京时间22日凌晨vSphere8.0Update2正式发布。请访问原文链接:https://sysin.org/blog/vmware-vcenter-8-u2/,查看最新版。原创作品,转载请保留出处。作者主页:sysin.orgvSphere8.0U2新增功能,请......
  • c语言 qsort函数的使用
    #include<iostream>#include<stdio.h>voidprinfArray(int*nums,intsize){for(inti=0;i<size;i++){printf("%d",nums[i]);}printf("\n");}intcompare(voidconst*a,voidconst*......
  • 七天学会C语言-第六天(指针)
    1.指针变量与普通变量指针变量与普通变量是C语言中的两种不同类型的变量,它们有一些重要的区别和联系。普通变量是一种存储数据的容器,可以直接存储和访问数据的值。:intnum=10;//定义一个整数型普通变量num,赋值为10在例子中,变量num是一个普通整数变量,它直接存储了值10。指针变......
  • 学习C语言的第八天
    今天没有学新内容,把前两天的代码又回味了下,发现有几个还是算不上理解,只能说是背住了,不知道有没有大佬会看到我写的东西,如果看到了,能不能告诉我一下,像一些不容易理解的代码,我背住它有用吗?写一个代码让电脑关机#include<windows.h>#include<stdio.h>#include<string.h>intmain()......
  • C语言中整形的大小端存储
    (C语言中整形的大小端存储)1.案例引入众所周知,在IDEVisualstudio中,调试后可以在内存窗口中看见程序中一些变量的地址以及值我们这里将一个16进制数字0x12345678存到内存中intmain(){ inta=0x12345678; return0;}按下F10后进入调试,打开内存窗口,找到变量a的地址,......
  • C语言-数据结构之顺序表
    #include<stdio.h>#defineN128typedefintdata_type;typedefstruct{ data_typedata[N]; intlast;}sqlist;sqlist*list_create();intlist_show(sqlist*L);intlist_clear(sqlist*L);intlist_destory(sqlist*L);intlist_empty(sqlist*L......