关于我重生到21世纪学C语言这件事——三子棋游戏!

与诸君共进步!!!

文章目录

  • 前言
  • [1. 游戏规则](#1. 游戏规则)
  • [2. 多个文件实现链式访问](#2. 多个文件实现链式访问)
  • [3. 打印菜单](#3. 打印菜单)
  • [4. 游戏核心内容的实现](#4. 游戏核心内容的实现)
    • [4.1 棋盘初始化](#4.1 棋盘初始化)
    • [4.2 棋盘展示](#4.2 棋盘展示)
    • [4.3 玩家下棋](#4.3 玩家下棋)
    • [4.4 电脑下棋](#4.4 电脑下棋)
    • [4.5 胜负的判断](#4.5 胜负的判断)
  • [5. 附源码的多个文件](#5. 附源码的多个文件)

前言

c语言三子棋 是对大家对数组,函数,循环与分支等内容的考察。通过本篇文章你将学会如何制作出三子棋,坚持一段时间的复习相信你肯定能够掌握三子棋!!!

1. 游戏规则

1、人机双方轮流在格子里落子棋子,先连成三棋一线者视为胜利

2、棋盘被摆满棋子仍未分出胜利,视为平局

2. 多个文件实现链式访问

三子棋并不是一项容易的代码,需要我们进行分文件编写。
什么是分文件编写?
就是把我们的程序代码划分成多个文件,这样就不会把所有的代码都放在main.c里面,采用分模块的编程思想,进行功能划分,把每个功能不一样的单独放在一个c文件里,然后写头文件把它封装成可调用的一个函数,在主函数调用这个封装好的函数,编译的时候一起编译即可。
这样的好处就是:

1.功能被划分到各个文件

2.便于调试

3.使主程序简洁
来让给我们看看具体操作如下:

  • 头文件 中创建game.h ----用来声明函数
  • 源文件 中创建game.c---函数具体实现 / test.c---主题流程

3. 打印菜单

先创建一个菜单以展示进入退出游戏功能:

c 复制代码
void menu()
{
	printf("****************************\n");
	printf("*******    1.play    *******\n");
	printf("*******    0.exit    *******\n");
	printf("****************************\n");
}

do...while 循环实现菜单使用,switch语句进行菜单选择

c 复制代码
int main()
{
	int input = 0;
	do
	{
		menu();
		printf("请输入0 或 1  ,别输错了 ");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("人机,玩不起别玩\n");
			break;
		default:
			printf("这都能输错,你是人机吗?\n");
			break;
		}
	} while (input);
}

4. 游戏核心内容的实现

在 game()函数中实现整个游戏

4.1 棋盘初始化

在game.h中定义行和列为3

c 复制代码
#define ROW 3
#define COL 3

game.h 中声明初始化函数Initboard

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

game.c 中实现初始化函数,创造三行三列的空格

c 复制代码
void init_board(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			board[i][j] = ' ';
		}
	}
}

这时候我们的棋盘初始化就完成了

4.2 棋盘展示

接下来就是我们的棋盘打印了

game.h 文件中声明打印棋盘

c 复制代码
void print_board(char board[ROW][COL], int row, int col);

game.c 文件中实现棋盘打印函数

c 复制代码
void print_board(char board[ROW][COL], int row, int col)
{

	int i = 0;
	for (i = 0; i < row; i++)
	{
		//printf("%c | %c | %c \n", board[i][0], board[i][1], board[i][2]);  使用循环的方法来解决这个问题

		int j = 0;
		for (j = 0; j < col; j++)
		{
			printf(" %c ", board[i][j]);
			if (j < col - 1)
				printf("|");
		}
		printf("\n");
		if (i < col - 1)
		{
			//printf("---|---|---\n");  使用循环的方法来解决这个问题

			for (j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)
					printf("|");
			}
			printf("\n");
		}
	}
}

棋盘打印完成

4.3 玩家下棋

game.h 声明玩家下棋函数

c 复制代码
void player_move(char board[ROW][COL], int row, int col);

game.h 中实现玩家下棋函数。玩家所在下棋子不能出棋盘否则坐标非法重新下,所下棋子必须在无棋子格子中否则坐标备占有重新下,while实现玩家下错棋子重新下直到下正确退出循环

c 复制代码
void player_move(char board[ROW][COL], int row, int col)
{
	printf("玩家下棋\n");
	printf("输入下棋坐标,记得中间有空格:>\n");
	int x = 0;
	int y = 0;
	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] = '*';

		}
		else
		{
			printf("这个位置已经被下了,天才,重新输一个\n");
		}
	}
	else
	{
		printf("坐标非法了,人机\n");
	}
}

这个时候我们玩家下棋的逻辑就完成了

4.4 电脑下棋

game.h 声明电脑下棋函数

c 复制代码
//电脑下棋
void computer_move(char board[ROW][COL], int row, int col);

在game.c文件中实现电脑下棋函数。电脑随机下棋需要随机坐标引头文件#include <stdlib.h>,随机数生成需要头文件#include <time.h>。将电脑所下棋控制在棋盘内然后判断是否下在空棋盘内否则重下,while实现玩家下错棋子重新下直到下正确退出循环

c 复制代码
oid computer_move(char board[ROW][COL], int row, int col)
{
	printf("电脑下棋\n");
	while (1)
	{
		int x = rand() % row;
		int y = rand() % col;


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

4.5 胜负的判断

game.h 中声明胜负判断函数。

c 复制代码
//判断游戏的胜负
char is_win(char board[ROW][COL], int row, int col);

game.c 中实现胜负判断函数。每次下棋结果共有四种1.玩家嬴 2.电脑嬴 3.平局 4.继续游戏

for循环实现行列三点成直线,if判断玩家或电脑三点成直线打印**' * '' # '**

c 复制代码
char is_win(char board[ROW][COL], int row, int col)
{
	int i = 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 (i = 0; i < col; i++)
	{
		if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
		{
			return board[0][i];
		}
	}
	//判断对角线
	if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
	{
		return board[1][1];
	}
	//平局
	if (is_full(board, row, col))
	{
		return 'Q';
	}

	//继续
	//没有玩家或者电脑赢,也没有平局,游戏继续
	return 'C';
}

5. 附源码的多个文件

1. game.c

c 复制代码
#define _CRT_SECURE_NO_WARNINGS 1

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



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

	int i = 0;
	for (i = 0; i < row; i++)
	{
		//printf("%c | %c | %c \n", board[i][0], board[i][1], board[i][2]);

		int j = 0;
		for (j = 0; j < col; j++)
		{
			printf(" %c ", board[i][j]);
			if (j < col - 1)
				printf("|");
		}
		printf("\n");
		if (i < col - 1)
		{
			//printf("---|---|---\n");
			for (j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)
					printf("|");
			}
			printf("\n");
		}
	}
}


//玩家下棋
void player_move(char board[ROW][COL], int row, int col)
{
	printf("玩家下棋\n");
	printf("输入下棋坐标,记得中间有空格:>\n");
	int x = 0;
	int y = 0;
	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] = '*';

		}
		else
		{
			printf("这个位置已经被下了,天才,重新输一个\n");
		}
	}
	else
	{
		printf("坐标非法了,人机\n");
	}
}



//人机下棋
void computer_move(char board[ROW][COL], int row, int col)
{
	printf("电脑下棋\n");
	while (1)
	{
		int x = rand() % row;
		int y = rand() % col;


		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			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;
	//判断三行
	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 (i = 0; i < col; i++)
	{
		if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
		{
			return board[0][i];
		}
	}
	//判断对角线
	if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
	{
		return board[1][1];
	}
	//平局
	if (is_full(board, row, col))
	{
		return 'Q';
	}

	//继续
	//没有玩家或者电脑赢,也没有平局,游戏继续
	return 'C';
}

2. game.h

c 复制代码
#pragma once

#include <stdio.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);

//人机下棋
void computer_move(char board[ROW][COL], int row, int col);

char is_win(char board[ROW][COL], int row, int col);

3. test.c

c 复制代码
#define _CRT_SECURE_NO_WARNINGS 1

#include "game.h"

void menu()
{
	printf("****************************\n");
	printf("*******    1.play    *******\n");
	printf("*******    0.exit    *******\n");
	printf("****************************\n");
}



void game()
{
	char board[ROW][COL];

	char ret = 0;//返回结果 # * Q C判断输赢

	//初始化棋盘
	init_board(board, ROW, COL);

	//打印棋盘
	print_board(board, ROW, COL);

	//开始玩游戏
	while (1)
	{
		//people 下棋
		player_move(board, ROW, COL);
		print_board(board, ROW, COL);
		//判断输赢
		ret = is_win(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}

		//computer 下棋
		computer_move(board, ROW, COL);
		print_board(board, ROW, COL);
		//判断输赢
		ret = is_win(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}

	}
	if (ret == '#')
		printf("人机电脑都下不赢,真的cai\n");
	else if (ret == '*')
		printf("YOU WIN\n");
	else if (ret == 'Q')
		printf("我勒个平局啊,能不能把电脑赢下来!\n");

}


void test()
{
	srand((unsigned int)time(NULL));
	int input = 0;
	do
	{
		menu();
		printf("请输入0 或 1  ,别输错了 ");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("人机,玩不起别玩\n");
			break;
		default:
			printf("这都能输错,你是人机吗?\n");
			break;
		}
	} while (input);
}



int main()
{
	test();
	return 0;
}

这个三子棋游戏还是有些许的 bug ,就比如如果下同一个位置,所以我的建议是直接整一个 player1 和 player2 的对战。

最后就是一句话:天道酬勤,地道酬善,人道酬诚,商道酬信,业道酬精

我一直都相信勤能补拙,加油吧!!!

相关推荐
染指11106 分钟前
50.第二阶段x86游戏实战2-lua获取本地寻路,跨地图寻路和获取当前地图id
c++·windows·lua·游戏安全·反游戏外挂·游戏逆向·luastudio
sjsjs1114 分钟前
【多维DP】力扣3122. 使矩阵满足条件的最少操作次数
算法·leetcode·矩阵
哲学之窗17 分钟前
齐次矩阵包含平移和旋转
线性代数·算法·矩阵
Code out the future24 分钟前
【C++——临时对象,const T&】
开发语言·c++
Stark、27 分钟前
【Linux】文件IO--fcntl/lseek/阻塞与非阻塞/文件偏移
linux·运维·服务器·c语言·后端
taoyong00128 分钟前
Java线程核心01-中断线程的理论原理
java·开发语言
一雨方知深秋29 分钟前
智慧商城:封装getters实现动态统计 + 全选反选功能
开发语言·javascript·vue2·foreach·find·every
海威的技术博客31 分钟前
关于JS中的this指向问题
开发语言·javascript·ecmascript
Sudo_Wang43 分钟前
力扣150题
算法·leetcode·职场和发展
sam-zy44 分钟前
MFC用List Control 和Picture控件实现界面切换效果
c++·mfc