三子棋 -- 数组的运用

实现效果

1、菜单及棋盘展示
在这里插入图片描述
2、玩家与电脑下棋
在这里插入图片描述
3、判断输赢
在这里插入图片描述

模块的分装

1、test.c / 测试模块

目标:建立游戏的“入口”,同时希望这个入口便于测试
功能:显示菜单 → 玩家选择 → play / exit

2、game.c / 游戏模块

功能:打印棋盘 → 玩家下棋 → 判断输赢 → 电脑下棋 → 判断

3、game.h / 头文件

附上标准库与创建的函数,另外创建一个头文件使得引用更简练。

代码实现

1、头文件模块

#define _CRT_SECURE_NO_WARNINGS 1

#define ROW 3
#define COL 3

#include<stdio.h>
#include<time.h>

//初始化棋盘
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);

(1)由#define定义行列标识符

下文在测试模块中将提到具体思路。

(2)#include “game.h”

可以看到这边包含了标准库的引用,此后在各个函数中将在代码块前段附上该引用。

2、测试模块

#define _CRT_SECURE_NO_WARNINGS 1

#include "game.h"

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

void test()//测试模块
{
	int input = 0;
	
	do
	{
		menu();
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("三子棋\n");
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误,请重新输入。\n");
			break;
		}

	} while (input);
}

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

在这里插入图片描述

(1) 主函数与test()模块分开

便于前期测试:即代码未完全时,依旧可以时刻检查正常与否。

(2)do while循环

游戏至少进行一次。

(3)switch(input)

由input判断下一步执行操作。

3、游戏模块

(1)三子棋 → 多字棋?

当我们只是想单一地实现“三”字棋的功能时,打印当然很容易实现,仅需运用一个最基本的嵌套循环打印即可。

//初始化棋盘
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]);
		if(i<row-1)
			printf("---|---|---\n");
	}
}


(注意最后一行无需打印分隔线,故运用if语句去掉最后一行)

效果也尽如人意。
但问题来了:有三字棋,是不是有四字、五字甚至很多很多字呢?这时显然刻板的一个根据行打印没办法满足要求。
在这里插入图片描述
回到棋盘的显示,既然行的循环行不通,我又想让棋盘的显示根据我想要多少字就显示多少个元素,往后退一格,我是不是可以根据元素组合进行打印呀。

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

在这里插入图片描述
在这里插入图片描述

(2)下棋操作

根据三子棋规则,每下一步,需要观察对局是否产生输赢结果。因而代码每一步应该包含功能:输入坐标 - 显示下子 - 判断输赢。
这里用*代表玩家下子,#代表电脑下子。请注意:下子与后边的输赢判断有关。

void player_move(char board[ROW][COL], int row, int col)
{
	//输入位置 → 是否合法 → 显示下子 
	printf("玩家下棋\n");
	while (1)
	{
		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] = '*';
				break;
			}
			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;
		}
	}
}

显示下子的步骤只需在test.c模块的game函数包含上相关函数即可,这边都是一一照应。

void game()
{
	char board[ROW][COL];
	char ret = 0;
	init_board(board, ROW, COL);
	print_board(board, ROW, COL);

	while (1)
	{
		player_move(board, ROW, COL);
		print_board(board, ROW, COL);
		
		computer_move(board, ROW, COL);
		print_board(board, ROW, COL);
}

(3)判断输赢

1)无外乎:你赢,电脑赢,平局三种情况。
2)赢的情况需要根据是否落子连续的情况,分别可能是:横排、竖排、对角线。因此需要有三个判断条件。
3)借由返回的坐标来指示输赢,平局使判断结束时返回C。

//is_full函数仅是为了支持is_win函数功能实现,只在函数内部使用,无需声明
static 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][0] == board[i][2] && board[i][0] != ' ')
		{
			return board[i][0];
		}
	}
	//判断列
	for (i = 0;i < row;i++)
	{
		if (board[0][i] == board[1][i] && board[0][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 (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
	{
		return board[1][1];
	}

	//平局
	if (is_full(board, row, col) == 1)
	{
		return 'Q';
	}
	return 'C';
}

同样的test.c中的对应:

while (1)
	{
		player_move(board, ROW, COL);
		print_board(board, ROW, COL);
		//判断输赢
		ret = is_win(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
		computer_move(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 (ret == 'Q')
		printf("平局\n");
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值