C语言实现简单的三子棋
Posted fy_闷油瓶
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言实现简单的三子棋相关的知识,希望对你有一定的参考价值。
目录
前言
井字棋,英文名叫Tic-Tac-Toe,是一种在3*3格子上进行的连珠游戏,和五子棋类似,由于棋盘一般不画边框,格线排成井字故得名。游戏需要的工具仅为纸和笔,然后由分别代表O和X的两个游戏者轮流在格子里留下标记(一般来说先手者为X),任意三个标记形成一条直线,则为获胜。本文我们使用C语言来实现这个小游戏。
一、设计思路
菜单
在生活中,我们所玩的每一款游戏在开始界面,都会有一个菜单供我们选择,开始游戏或者结束游戏,因此我们也要先设计一个简单的菜单,让玩家进行选择。棋盘的实现
当玩家选择开始游戏之后,我们要实现一个棋盘,对于3* 3的棋盘我们可以用一个3* 3的数组来实现,同时我们要将棋盘初始化为接近真实棋盘的模样。如下所示
游戏开始
当棋盘初始化之后,我们就可以正式开始游戏,玩家根据棋盘当前的状态,可以输入他想要落子位置的坐标,如果这个坐标没有被占用,那么我们就在这个位置打印’X’代表此处已经被玩家落子。当玩家落子之后,轮到电脑落子,我们让电脑随机产生一个符合棋盘大小的坐标,并且如果坐标没有被占用,就在这个坐标打印一个’O’代表此处已被电脑落子。
判断输赢
输赢的判断有两种方式:
第一种,玩家或者电脑每走完一步,我们就判断一次是否产生赢家
第二种,当玩家或者电脑走了三步时,我们才开始判断是否产生赢家
二、游戏代码
头文件
首先我们创建一个头文件,用来声明我们所使用到的函数以及定义棋盘的大小。由于我们要让电脑随机产生一个坐标,因此我们需要用到srand,rand以及time函数,同时在游戏过程中还需要用到输入输出函数,因此我们在头文件中,将我们需要用到的标准库全都包含在内,这样在.c文件就只需要包含这一个头文件即可
//game.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 3 //棋盘行数
#define COL 3 //棋盘列数
void InitBoard(char arr[ROW][COL], int row, int col); //初始化棋盘
void PrintBoard(char arr[ROW][COL], int row, int col); //打印棋盘
void PlayerMove(char arr[ROW][COL], int row, int col); //玩家下棋
void CopMove(char arr[ROW][COL], int row, int col); //电脑下棋
char IsWin(char arr[ROW][COL], int row, int col); //判断结果
游戏菜单
在main函数中,我们打印出游戏菜单,并让玩家进行选择,当玩家选择了开始游戏时,我们调用game函数。由于后面我们需要电脑产生随机的坐标,因此我们在main函数中使用srand初始化种子。对于srand和rand函数的使用方法在我的另一篇博客中进行了详细的介绍,有兴趣的读者可以作为参考。rand和srand函数用法
#define _CRT_SECURE_NO_WARNINGS
#include"game.h"
//打印菜单
void menu()
printf("**********************************************\\n");
printf("********请选择-> 1:开始游戏 0:结束游戏\\n");
printf("**********************************************\\n");
int main()
srand((unsigned int)time(NULL));
int input = 0;
do
menu();
printf("请选择:\\n");
scanf("%d", &input);
switch (input)
case 1:
game();
break;
case 0:
printf("游戏结束\\n");
break;
default:
printf("输入错误,请重新输入\\n");
break;
while (input);
return 0;
游戏开始
当玩家选择了开始游戏之后,我们调用game函数进行游戏, 首先我们初始化棋盘,并将棋盘打印出来,之后让玩家先走,玩家或者电脑每走完一步都打印一次棋盘并判断是否产生赢家。
当游戏结束时有三种可能:
1、玩家胜利
2、电脑胜利
3、平局
因此我们用Winer函数来判断胜者是谁。X代表玩家赢,O代表电脑赢。
//判断胜者是谁
void Winer(char ret)
if (ret == 'X')
printf("恭喜你赢了\\n");
else if (ret == 'O')
printf("很遗憾,你输了\\n");
else
printf("平局\\n");
// 游戏开始
void game()
char arr[ROW][COL] = 0 ;
InitBoard(arr, ROW, COL); //初始化棋盘
PrintBoard(arr, ROW, COL); //打印棋盘
while (1)
char ret = 0;
PlayerMove(arr, ROW, COL); //玩家下棋
PrintBoard(arr, ROW, COL); //打印棋盘
ret = IsWin(arr, ROW, COL); //判断输赢
if (ret != 'C') //当ret!='C'时,游戏结束(C代表continue)
Winer(ret); //判断胜者是谁
break;
CopMove(arr, ROW, COL); //电脑下棋
PrintBoard(arr, ROW, COL);
ret = IsWin(arr, ROW, COL);
if (ret != 'C')
Winer(ret);
break;
初始化和打印棋盘:
在玩家落子前,我们先将棋盘初始化空
//初始化棋盘
void InitBoard(char arr[ROW][COL], int row, int col)
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
for (j = 0; j < col; j++)
arr[i][j] = ' ';
由于我们所希望的棋盘是这样的:
为了实现类似的棋盘,我们需要使用|以及— 来作为分隔符,并且注意到,我们所希望的棋盘最后一行和最后一列都没有分隔符,因此我们在代码中要进行一个if语句的判断
//打印棋盘
void PrintBoard(char arr[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 ", arr[i][j]);
if (j < col - 1)
printf("|"); //不是最后一列,打印 | 作为分隔符
printf("\\n");
if (i < row - 1)
for (j = 0; j < col; j++) //不是最后一行,打印--- --- --- 作为分隔符
printf("--- ");
printf("\\n");
玩家下棋
我们令棋盘上的坐标从1开始,因此对应到数组的下标就要减1,并且需要判断玩家当前输入的坐标是否已经被占用,如果没被占用则赋值为X。
//玩家下棋
void PlayerMove(char arr[ROW][COL], int row, int col)
int i = 0;
int j = 0;
printf("玩家下棋,请输入落子的坐标:\\n");
while (1)
scanf("%d%d", &i, &j);
if (i <= 0 || j <= 0 || i > ROW || j > COL) //超出棋盘范围
printf("坐标输入错误,请重新输入\\n");
else
if (arr[i - 1][j - 1] == ' ') //坐标未被占用
arr[i - 1][j - 1] = 'X'; //当前坐标赋值为X
break;
else printf("此处已经被占用,请重新输入\\n");
电脑下棋
电脑下棋时,我们需要电脑随机生成一个[0,row]的行坐标和一个[0,col]的列坐标,用srand来实现随机的坐标。只要产生的坐标没被占用,就将其赋值为O,不需要打印提示信息。
//电脑下棋
void CopMove(char arr[ROW][COL], int row, int col)
int i = 0;
int j = 0;
printf("电脑下棋:\\n");
while (1)
i = rand() % row;
j = rand() % col;
if (arr[i][j] == ' ')
arr[i][j] = 'O';
break;
判断输赢
想要判断输赢,我们必须要检查每一行,每一列以及两个对角线是否有连续三个相同的符号,并且这个符号不为空(因为初始化的时候我们将其置为了空)。当三个位置符号相同时,我们只需要将这个符号返回,然后在Winer函数中判断胜者到底是谁。当没有胜者产生时,我们还要判断棋盘是否落满,如果落满则表明和棋。当没有胜者并且棋盘也没有落满时,游戏继续。
//判断棋盘是否满
int IsFull(char arr[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 (arr[i][j] == ' ') return 0; //如果有一个位置为空,表明棋盘还没落满,返回0
return 1; //棋盘全部落满,返回1
//3*3 输赢判断
char IsWin(char arr[ROW][COL], int row, int col)
int i = 0;
int j = 0;
for (i = 0; i < row; i++) //判断行是否相等
if (arr[i][0] == arr[i][1] && arr[i][0] == arr[i][2] && arr[i][0] != ' ')
return arr[i][0]; //玩家胜利则arr[i][0]是X,电脑胜利则是O
for (i = 0; i < col; i++) //判断列是否相等
if (arr[0][i] == arr[1][i] && arr[0][i] == arr[2][i] && arr[0][i] != ' ')
return arr[0][i];
//判断主对角线是否相等
if (arr[0][0] == arr[1][1] && arr[0][0] == arr[2][2] && arr[0][0] != ' ')
return arr[0][0];
//判断副对角线是否相等
if (arr[0][2] == arr[1][1] && arr[1][1] == arr[2][0] && arr[1][1] != ' ')
return arr[1][1];
//判断棋盘是否落满
int ret = IsFull(arr, row, col);
if (ret) return 'F'; //如果ret为1,则棋盘落满,返回'F'
else return 'C'; //程序运行到这,表明没有胜者并且棋盘不为空
//(如果不满足这几个条件,在前面函数就已经返回,不会运行到这一步)
//返回C则继续游戏
三、代码改进
上面的代码只能在3*3的棋盘上判断输赢,无法对更大的棋盘进行判断,对此作出如下改进:
行判断:第 i 行连续三个位置是否相同,同时保证不超过棋盘的范围
列判断:第 j 列连续三个位置是否相同,同时保证不超过棋盘的范围
主对角线判断:连续三个主对角线位置是否相同,即arr[i][j],arr[i+1][j+1],arr[i+2][j+2]是否相同,同时保证不超过棋盘范围
副对角线判断:连续三个副对角线位置是否相同,即arr[i][j],arr[i+1][j-1],arr[i+2][j-2]是否相同,同时保证不超过棋盘范围
因为主对角线判断时,行号和列号不断增大,因此i和j从0开始,而副对角线判断时,行号不断增大,列号不断减小,因此i从0开始,j从col-1开始。
//判断任意大小棋盘输赢
char IsWin(char arr[ROW][COL], int row, int col)
int i = 0;
int j = 0;
//判断行
for (i = 0; i < row; i++) //判断第i行连续3个位置是否相等
for (j = 0; j < col - 2; j++) //不能超出棋盘范围,因此j < col - 2
if (arr[i][j] == arr[i][j + 1] && arr[i][j] == arr[i][j + 2] && arr[i][j] != ' ')
return arr[i][j];
//判断列
for (j = 0; j < col; j++) //判断第j列连续3个位置是否相等
for (i = 0; i < row - 2; i++) //不能超出棋盘范围,因此i < row - 2
if (arr[i][j] == arr[i + 1][j] && arr[i][j] == arr[i + 2][j] && arr[i][j] != ' ')
return arr[i][j];
//判断对角线
for (i = 0; i < row - 2; i++) //主对角线判断
for (j = 0; j < col - 2; j++)
if (arr[i][j] == arr[i + 1][j + 1] && arr[i][j] == arr[i + 2][j + 2] && arr[i][j] != ' ')
return arr[i][j];
for (i = 0; i < row - 2; i++) //副对角线判断
for (j = col - 1; j >= 2; j--)
if (arr[i][j] == arr[i + 1][j - 1] && arr[i][j] == arr[i + 2][j - 2] && arr[i][j] != ' ')
return arr[i][j];
int ret = IsFull(arr, row, col);
if (ret) return 'F';
return 'C';
后记
本文虽然实现了一个简单的三子棋,但是由于电脑落子是随机的,因此电脑很难获胜,当棋盘比较小时,甚至想让它赢都很困难。也就是游戏难度太低,毫无游戏体验。如果想让电脑更加智能化,需要使用更复杂的算法,同时在判断输赢时,使用的是暴力枚举法,对所有的可能都进行了判断,时间复杂度为O(n^2),效率比较低。鉴于目前水平有限,只能使用最简单的算法,随着后续知识的扩展,再对算法进行改进。以上是关于C语言实现简单的三子棋的主要内容,如果未能解决你的问题,请参考以下文章