设计目标:通过C语言实现三子棋小游戏,能够判断胜负以及简单的人机交互。
目前的人机下棋是通过rand函数随机产生坐标,不具有对抗性,后续会推出更强大的人机。
1.头文件:包含初始化棋盘,打印棋盘,玩家下棋,电脑下棋,判断胜负等函数的声明,以及宏定义行列数,方便以后扩展。
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<time.h>
#define ROW 3 //行
#define COL 3 //列
void initialize_board(char board[ROW][COL],int row ,int col); //初始化棋盘
void Print_board(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 Win_lose(char board[ROW][COL], int row, int col); //判断胜负
2.主函数:打开菜单,玩家输入,判断玩家输入,进入游戏或退出游戏或重新输入。
其中srand((unsigned)time(NULL))中的srand()函数用来设置算法的种子,time(NULL)返回当前的时间,先调用srand(time(NULL))是把当前的时间作为种子,是程序每次运行产生不同的随机数序列。
用二维数组保存棋盘数据。
int main()
{
int input;
srand((unsigned)time(NULL)); //srand()函数用来设置算法的种子,time(NULL)返回当前的时间,先调用srand(time(NULL))是把当前的时间作为种子,是程序每次运行产生不同的随机数序列。
do
{
menu(); //每次开始,重新输出菜单
printf("请选择模式:-->");
scanf("%d", &input); //录入
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("无效选择,请重新选择\n");
break;
}
} while (input); //有输入则继续循环
return 0;
}
先要进行一次循环内的输入和判断,所以采用do-while循环。
3.game游戏函数:进入游戏时要先初始化棋盘,之后每一步操作后都要打印棋盘。玩家和电脑每次下棋的过程都是,下棋,打印棋盘,判断胜负,如果触发胜利或者平局将结束游戏。
void game()
{
char label = 0; //胜负标签;胜利返回W,未胜利返回0,平局返回D
char board[ROW][COL];
initialize_board(board,ROW,COL); //初始化棋盘
Print_board(board,ROW,COL); //打印棋盘
while (1)
{
Playermove(board, ROW, COL); //玩家下棋
Print_board(board, ROW, COL);
label = Win_lose(board, ROW, COL);
if (label == 'W')
{
printf("*****************玩家胜利***************\n\n");
Print_board(board, ROW, COL);
break;
}
else if (label == 'D')
{
printf("******************平局******************\n\n");
Print_board(board, ROW, COL);
break;
}
ComputerMove(board, ROW, COL); //电脑下棋
Print_board(board, ROW, COL);
label = Win_lose(board, ROW, COL);
if (label == 'W')
{
printf("****************电脑胜利***************\n\n");
Print_board(board, ROW, COL);
break;
}
else if (label == 'D')
{
printf("******************平局******************\n\n");
Print_board(board, ROW, COL);
break;
};
}
}
4.menu菜单函数:用于交互。
void menu()
{
printf("*******************************\n");
printf("********** 1. play ************\n");
printf("********** 0. exit ************\n");
printf("*******************************\n");
}
5.initialize_board初始化函数:将棋盘数组每个元素遍历,初始化为空格。使用两个循环对二维数组进行遍历。
void initialize_board(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++)
{
board[i][j] = ' ';
}
}
}
6. Print_board打印棋盘函数:三个行数据行,二个行分隔行。数据行格式为: 0 | 0 | 0 分隔行格式为:---|---|--- 共2个。
void Print_board(char board[ROW][COL], int row, int col) //打印棋盘:一行数据,一行分割。数据行: 0 | 0 | 0 分格行:---|---|--- 共2个。
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
printf(" %c ", board[i][j]);
if (j < col - 1) //每次打印数据后打印分割,最后一列后不打印
printf("|");
}
printf("\n");
if (i < row - 1) //每打印一行数据后打印分割,最后一行后不打印,这里不直接打印---|---|----是为了以后方便扩展
{
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
printf("|");
}
printf("\n");
}
}
}
7.Playermove玩家下棋:输入玩家规定的坐标,先判断坐标是否合法,再判断是否为空,都满足则赋“*”。
void Playermove(char board[ROW][COL], int row, int col) //玩家下棋
{
int x = 0;
int y = 0;
printf("玩家下棋\n");
while (1)
{
printf("请输入要下的坐标(用空格隔开):");
scanf("%d %d", &x, &y);
if (x >= 1 && y >= 1 && x <= row && y <= col) //先判断坐标是否合法
{
if (board[x - 1][y - 1] != ' ') //判断是否为空
{
printf("坐标已有棋子,请重新输入\n");
}
else
{
board[x - 1][y - 1] = '*';
break;
}
}
else
{
printf("非法坐标,请重新输入\n");
}
}
}
8.ComputerMove玩家下棋:rand随机机获取一个坐标,判断为空就落子#号,rand() % row; 简单来说就是获取 0 - (row-1)中的一个数。
void ComputerMove(char board[ROW][COL], int row, int col) //电脑下棋
{
int x, y;
printf("电脑下棋\n");
while (1) //随机下一个位置
{
x = rand() % row; //获取 0 - (row-1)中的一个数
y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
9.判断胜负函数:胜利返回W,未胜利返回L,平局返回D,否则返回0 。对三行,三列,对角线分别判断是否三个相同,相同则胜利;
不相同则判断是否为平局,没有空位为和局,遍历判断。Judge_draw只在本源文件使用,所以加static,避免命名重复。
若都不满足,则继续游戏。
static int Judge_draw(char board[ROW][COL], int row, int col) //Judge_draw只在本源文件使用,所以加static,避免命名重复。
//判断是否和局,是就返回1
{
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 Win_lose(char board[ROW][COL], int row, int col) //判断胜负;胜利返回W,未胜利返回L,平局返回D,否则返回0
{
int i = 0;
for (i = 0; i < row; i++) //判断三行,且都不等于空格
{
if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][2] != ' ') //两两比较,不能连等
return 'W';
}
for (i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[2][i] != ' ') //判断三列,且都不等于空格
return 'W';
}
if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[2][2] != ' ') //判断主对角,且都不等于空格
return 'W';
if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[2][0] != ' ') //判断主对角,且都不等于空格
return 'W';
if (Judge_draw(board,row,col) == 1) //判断是否和局
return 'D';
return 0;
}
目前可以简单的游戏,后续会推出更厉害的人机。
示例:
完整代码如下:
fun.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<time.h>
#define ROW 3 //行
#define COL 3 //列
void initialize_board(char board[ROW][COL],int row ,int col); //初始化棋盘
void Print_board(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 Win_lose(char board[ROW][COL], int row, int col); //判断胜负
fun.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"fun.h"
void initialize_board(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++)
{
board[i][j] = ' ';
}
}
}
void Print_board(char board[ROW][COL], int row, int col) //打印棋盘:一行数据,一行分割。数据行: 0 | 0 | 0 分格行:---|---|--- 共2个。
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
printf(" %c ", board[i][j]);
if (j < col - 1) //每次打印数据后打印分割,最后一列后不打印
printf("|");
}
printf("\n");
if (i < row - 1) //每打印一行数据后打印分割,最后一行后不打印,这里不直接打印---|---|----是为了以后方便扩展
{
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("玩家下棋\n");
while (1)
{
printf("请输入要下的坐标(用空格隔开):");
scanf("%d %d", &x, &y);
if (x >= 1 && y >= 1 && x <= row && y <= col) //先判断坐标是否合法
{
if (board[x - 1][y - 1] != ' ') //判断是否为空
{
printf("坐标已有棋子,请重新输入\n");
}
else
{
board[x - 1][y - 1] = '*';
break;
}
}
else
{
printf("非法坐标,请重新输入\n");
}
}
}
void ComputerMove(char board[ROW][COL], int row, int col) //电脑下棋
{
int x, y;
printf("电脑下棋\n");
while (1) //随机下一个位置
{
x = rand() % row; //获取 0 - (row-1)中的一个数
y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
static int Judge_draw(char board[ROW][COL], int row, int col) //Judge_draw只在本源文件使用,所以加static,避免命名重复。
//判断是否和局,是就返回1
{
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 Win_lose(char board[ROW][COL], int row, int col) //判断胜负;胜利返回W,未胜利返回L,平局返回D,否则返回0
{
int i = 0;
for (i = 0; i < row; i++) //判断三行,且都不等于空格
{
if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][2] != ' ') //两两比较,不能连等
return 'W';
}
for (i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[2][i] != ' ') //判断三列,且都不等于空格
return 'W';
}
if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[2][2] != ' ') //判断主对角,且都不等于空格
return 'W';
if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[2][0] != ' ') //判断主对角,且都不等于空格
return 'W';
if (Judge_draw(board,row,col) == 1) //判断是否和局
return 'D';
return 0;
}
game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"fun.h"
void menu()
{
printf("*******************************\n");
printf("********** 1. play ************\n");
printf("********** 0. exit ************\n");
printf("*******************************\n");
}
void game()
{
char label = 0; //胜负标签;胜利返回W,未胜利返回0,平局返回D
char board[ROW][COL];
initialize_board(board,ROW,COL); //初始化棋盘
Print_board(board,ROW,COL); //打印棋盘
while (1)
{
Playermove(board, ROW, COL); //玩家下棋
Print_board(board, ROW, COL);
label = Win_lose(board, ROW, COL);
if (label == 'W')
{
printf("*****************玩家胜利***************\n\n");
Print_board(board, ROW, COL);
break;
}
else if (label == 'D')
{
printf("******************平局******************\n\n");
Print_board(board, ROW, COL);
break;
}
ComputerMove(board, ROW, COL); //电脑下棋
Print_board(board, ROW, COL);
label = Win_lose(board, ROW, COL);
if (label == 'W')
{
printf("****************电脑胜利***************\n\n");
Print_board(board, ROW, COL);
break;
}
else if (label == 'D')
{
printf("******************平局******************\n\n");
Print_board(board, ROW, COL);
break;
};
}
}
int main()
{
int input;
srand((unsigned)time(NULL)); //srand()函数用来设置算法的种子,time(NULL)返回当前的时间,先调用srand(time(NULL))是把当前的时间作为种子,是程序每次运行产生不同的随机数序列。
do
{
menu(); //每次开始,重新输出菜单
printf("请选择模式:-->");
scanf("%d", &input); //录入
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("无效选择,请重新选择\n");
break;
}
} while (input); //有输入则继续循环
return 0;
}