目录
三字棋介绍
三字棋,也被称为井字棋(Tic Tac Toe),是一种非常简单的两人对战策略游戏。在这个游戏中,两位玩家分别代表X和O,在3x3的棋盘上轮流落子。游戏的目标是在棋盘的任何一行、一列或对角线上首先连成三个相同的符号(X或O),以赢得比赛。
C语言实现三字棋的预期功能
-
初始化棋盘:游戏开始时,应创建一个3x3的棋盘,并用空格表示每个位置尚未落子。
-
玩家轮流下棋:两位玩家分别使用X和O符号轮流在棋盘上放置棋子。程序应确保每位玩家只能在下一步中选择一个空位置落子。本文程序玩家符号为 ‘*’ , 电脑符号为 ‘#’。
-
显示棋盘状态:在每一步之后,程序应更新并显示当前的棋盘状态,以便玩家了解游戏进展。
-
检查胜负条件:程序需要实现一个函数来检查当前棋盘状态,看是否有玩家已经连成三个相同的符号。如果有,则宣布该玩家获胜,并结束游戏。
-
处理平局情况:如果棋盘填满且没有玩家获胜,则判定为平局,程序应宣布游戏结束。
-
用户交互:程序应能够接收用户的输入,如选择下棋的位置,并在适当的时候给出提示或错误信息。
-
重新开始或退出游戏:游戏结束后,程序应提供选项供用户选择重新开始新的一局或退出游戏。
游戏规则
三字棋的基本规则
- 棋盘与棋子:游戏在一个3x3的棋盘上进行,每位玩家拥有一种棋子,通常分别用X和O表示。
- 开局与走棋:游戏开始时,两位玩家通过猜拳或其他方式决定谁先下第一步。之后,两位玩家轮流在棋盘的空格上放置自己的棋子,每次只能走一步。本文程序设定为玩家先下第一步
- 胜利条件:任何一位玩家先将自己的三个棋子连成一条直线(横线、竖线或对角线)时,即宣告胜利。游戏结束。
- 平局情况:如果棋盘上的所有空格都被填满,且没有玩家连成三个棋子,则游戏以平局结束。
游戏设计及代码实现
游戏开始界面
游戏开始界面这里,我们使用 printf("") 打印出一个简单的游戏开始界面
代码实现
void menu(){
printf("************************\n");
printf("******** 1.play ********\n");
printf("******** 0.exit ********\n");
printf("************************\n");
}
玩家输入输出
打印出游戏开始界面后,玩家需要可以选择开始游戏或者退出游戏,我们这里使用 switch case 语句,玩家输入 ’1‘ 开始游戏,输入 ’0‘ 退出游戏;输入其它数字则选择错误,让玩家重新选择。游戏结束后,当我们想再进行下一次游戏时,总不能关闭程序去重新打开它。所以这里使用了 do-while 语句,当我们想继续游戏的时候,输入 ‘1’ 即可,非零为真,循环继续;退出游戏时,输入 ‘0’ 就可以,0 为假,循环终止。
代码实现
//存放玩家输入的数字,‘1’开始游戏,‘0’退出游戏
int input = 0;
do
{
menu();
printf("请选择:》");
scanf("%d", &input);
//‘1’开始游戏;‘0’退出游戏;其它数字,重新输入
switch (input)
{
case 1:
game();
printf("请输入:》\n");
break;
case 0:
printf("退出游戏。\n");
break;
default:
printf("选择错误,请重新选择!\n");
break;
}
} while (input);//选择‘0’时,退出循环,结束游戏
棋盘
初始化棋盘
游戏开始时,程序应该创建一个3x3的棋盘,并用空格表示每个位置尚未落子。这里我们创建一个二维数组 board [ROW] [COL] (ROW为行数,COL为列数,头文件里可改变数量,本文为3);利用两个 for 循环将 board [ROW] [COL] = ’ ‘ 即可。
代码实现
//初始化棋盘,每个坐标位置应为空,还未被下棋
void InitBoard(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] = ' ';
}
}
}
打印棋盘
这时候如果我们直接使用 for 循环将board [ROW] [COL] 棋盘打印出来的时候,我们是什么都看不到的,如图
所以为了看到棋盘的样子,我们可以给棋盘加上一框,效果如图
这里用到了 ‘|’ ‘---’ 这两个符号,再用 printf 打印出来的,不过得需要循环来控制它,棋盘上棋子的位置目前为空格,观察上图,当我们在棋盘上每行打印出一个空格的时候,后面有一个 ‘|’ ,每行的最后一个没有;利用 for 循环,每打印一个 ’ ‘ 的时候,后面再打印一个 ’ | ‘,再用 if 语句设定条件,最后一个不打印。每行打印完成后,下面有一行 ’---|---|---‘,同上每打印一个 ’---‘ 的时候,后面再打印一个 ’ | ‘,再用 if 设定条件,最后一个不打印;这种有两行,同样我们可以使用 if 语句设定条件,最后一行不打印。
代码实现
//void DisplayBoard(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++)
// {
// printf("%c",board[i][j]);
// }
// printf("\n");
// }
//}
//打印棋盘
void DisplayBoard(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++)
{
printf(" %c ", board[i][j]);
//棋盘上的 ‘|’
if (j < col - 1)
{
printf("|");
}
}
printf("\n");
//棋盘上的‘---|’
if (i < row - 1)
{
int j = 0;
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
{
printf("|");
}
}
}
printf("\n");
}
}
玩家下棋
棋盘上落子的位置被我们初始化成了 ‘ ’ 空格,而玩家下棋就是把 ‘ ’ 空格替换成代表玩家的棋子符号 ‘ * ’即可。
定义 x 、y 存放玩家输入的坐标,就是玩家下棋的位置;使用while语句,因为玩家需要可以多次下棋;if 条件语句,因为x 和 y 不能超过棋盘行数和列数,不然程序在棋盘上就找不到这个位置;数组的下标是从 0 开始的,但是从生活中看,序号大多从 1 开始,所以当玩家输入完坐标后,把它减去 1 就是对应棋盘上的坐标。当程序读入符合规定的 x、y 值后,if 条件语句,因为需要检查棋盘此处位置是否已经存在棋子,是否为空,不为空此处位置便不可以被下棋。
代码实现
//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("玩家下棋(请输入要下棋的棋子坐标,中间使用空格):");
scanf("%d %d", &x, &y);
while (1)
{
//玩家输入的坐标要在 1 到行数(列数)之间
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");
}
}
}
电脑下棋
和玩家下棋一样,将棋盘上 ’ ‘ 空格的位置替换成电脑的符号 ’ # ‘ 即可。
电脑棋手的坐标,可以使用随机函数 srand 随机产生,这样每次的游戏对局才能不一样。
代码实现
//电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("电脑下棋:\n");
while (1)
{
//随机生成电脑下棋的棋子坐标
x = rand() % row;
y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
判断输赢
三字棋输赢的判定是三个棋子连成一条直线(横线、竖线或对角线),所以当玩家赢的时候,有三个 ’ * ‘ 符号连成一条直线,我们只需要在棋盘上检查出现这个情况后就可以判断输赢,当函数返回 ’ * ’ 玩家赢,‘ # ’ 电脑赢。棋盘被占满,平局的时候返回 ‘ q ’。当以上所有情况都没有的时,这时是继续游戏,函数返回值设为 ‘ c '。
代码实现
//判断胜负,
//返回字符‘*’玩家赢
//返回字符‘#’电脑赢
//返回字符‘q’棋盘被占满,平局
//返回字符‘c’游戏继续
char IsWin(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[1][2] && board[i][0] != ' ')
{
return board[i][0];
}
//每一列
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[0][0] != ' ')
{
return board[0][0];
}
//右上角左下角对角线
if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][2] != ' ')
{
return board[0][2];
}
//棋盘被占满,返回字符‘q'
if (IsFull(board,row,col)==1)
{
return 'q';
}
return 'c';
}
棋盘占满
当棋盘上的所有位置都有棋子没有空格的时候,棋盘上每一个位置都占满棋子,棋盘被占满。利用for 循环将二维数组的每一个元素和空格符号 ‘ ’ 比较,出现空格,棋盘上还有空格,可以接着下棋;没有空格,返回 1 ,判定程序便返回 ’ q ',本次游戏平局。
代码实现
//检查棋盘是否被棋子占满
int IsFull(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++)
{
if (board[i][j] == ' ')
{
return 0;
}
}
}
return 1;
}
完整的源代码及注释
整合全部代码,一个头文件(game.h),两个源文件(test.c)(game.c)。
头文件 game.h 的文件代码:
//头文件提供函数和类的声明//
#pragma once
#include<stdio.h>
//srand()随机函数的头文件
#include<stdlib.h>
//time()函数的头文件
#include<time.h>
//定义棋盘ROW行数、COL列数
#define ROW 3
#define COL 3
//初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col);
//打印棋盘
void DisplayBoard(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 IsWin(char board[ROW][COL], int row, int col);
源文件 test.c 的文件的代码:
//源文件提供函数和类的实现//
#define _CRT_SECURE_NO_WARNINGS
//引用头文件,头文件提供了函数和类的声明
#include"game.h"
//开始菜单的打印
void menu(){
printf("************************\n");
printf("******** 1.play ********\n");
printf("******** 0.exit ********\n");
printf("************************\n");
}
void game() {
//创建一个二维数组,三字棋棋盘
char board[ROW][COL] = { 0 };
//初始化棋盘
InitBoard(board, ROW, COL);
//打印出三字棋棋盘
DisplayBoard(board, ROW, COL);
//存放判断输赢函数 IsWin 的返回值
char ret = 0;
while(1)
{
//玩家下棋
PlayerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
ret = IsWin(board, ROW, COL);
if (ret != 'c')
{
break;
}
//电脑下棋
ComputerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
ret = IsWin(board, ROW, COL);
if (ret != 'c')
{
break;
}
}
if (ret == '*')
printf("玩家赢!\n");
else if (ret == '#')
printf("电脑赢!\n");
else
printf("平局\n");
}
int main() {
//存放玩家输入的数字,‘1’开始游戏,‘0’退出游戏
int input = 0;
//srand随机函数,生成随机数
srand((unsigned int)time(NULL));
do
{
menu();
printf("请选择:》");
scanf("%d", &input);
//‘1’开始游戏;‘0’退出游戏;其它数字,重新输入
switch (input)
{
case 1:
game();
printf("请输入:》\n");
break;
case 0:
printf("退出游戏。\n");
break;
default:
printf("选择错误,请重新选择!\n");
break;
}
} while (input);//选择‘0’时,退出循环,结束游戏
return 0;
}
源文件 game.c 的文件代码:
//源文件提供函数和类的实现//
#define _CRT_SECURE_NO_WARNINGS
//引用头文件,头文件提供了函数和类的声明
#include"game.h"
//初始化棋盘,每个坐标位置应为空,还未被下棋
void InitBoard(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 DisplayBoard(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++)
{
printf(" %c ", board[i][j]);
//棋盘上的的 ‘|’
if (j < col - 1)
{
printf("|");
}
}
printf("\n");
//棋盘上的‘---|’
if (i < row - 1)
{
int j = 0;
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;
printf("玩家下棋(请输入要下棋的棋子坐标,中间使用空格):");
scanf("%d %d", &x, &y);
while (1)
{
//玩家输入的坐标要在 1 到行数(列数)之间
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;
y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
//检查棋盘是否被棋子占满
int IsFull(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++)
{
if (board[i][j] == ' ')
{
return 0;
}
}
}
return 1;
}
//判断胜负,
//返回字符‘*’玩家赢
//返回字符‘#’电脑赢
//返回字符‘q’棋盘被占满,平局
//返回字符‘c’游戏继续
char IsWin(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[1][2] && board[i][0] != ' ')
{
return board[i][0];
}
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[0][0] != ' ')
{
return board[0][0];
}
if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][2] != ' ')
{
return board[0][2];
}
//棋盘被占满,返回字符‘q'
if (IsFull(board,row,col)==1)
{
return 'q';
}
return 'c';
}