.h包含头文件模块
// 头文件中一般放的是函数的声明
#pragma once
#define _CRT_SECURE_NO_WARNINGS
// 枚举
#ifndef __GAME_H__
#define __GAME_H__
#include<stdio.h>
#include<Windows.h>
#include<time.h>
#include<stdlib.h>
// define 定义的常量标识符
#define ROW 3
#define COL 3
// 定义一个 选项 类型
// 枚举默认从 0 开始向下依次增长
enum OPTION{
EXIT,
PLAY
};
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 CheckWin(char board[ROW][COL], int row, int col);
#endif //__GAME_H__
这是测试模块
// 测试模块,测试游戏模块是否可以正常运行
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include "game.h" // 引自己的头文件使用 "game.h"
// 打印菜单,完全是在测试的时候才需要,并不是游戏必须要的东西
// 游戏模块可能不需要它,所以把他放在测试函数中
void menu() {
printf("********************************\n");
printf("************ 1.play **********\n");
printf("************ 0.exit **********\n");
printf("********************************\n");
}
void game() {
char win = 0;
/*printf("hehe\n");*/
// 1. 打印棋盘
char board[ROW][COL] = { 0 };
InitBoard(board, ROW, COL); // 初始化棋盘函数 InitBoard
DisplayBoard(board, ROW, COL); // 打印棋盘
// 开始游戏
while (1) {
// 1. 玩家走
PlayerMove(board, ROW, COL);
// Sleep(1000); // 用于测试,目的是看出来效果
DisplayBoard(board, ROW, COL); // 打印棋盘
// 检测输赢,由于需要判断输赢,所以函数必须返回一个值供我们进行判断
// 可能出现的情况:
// 1. 玩家赢 -> 'X'
// 2. 电脑赢 -> 'O'
// 3. 平局 -> 'Q'
// 4. 继续 -> ' '
win = CheckWin(board, ROW, COL);
if (win != ' ') {
break;
}
// 2 .电脑走
ComputerMove(board, ROW, COL);
// Sleep(1000); // 用于测试,目的是看出来效果
DisplayBoard(board, ROW, COL); // 打印棋盘
win = CheckWin(board, ROW, COL);
if (win != ' ') {
break;
}
}
if (win == 'X') {
printf("玩家赢!\n");
}
else if (win == 'O') {
printf("电脑赢!\n");
}
else {
printf("平局!\n");
}
}
// test 函数是专门用来测试的
void test() {
int input = 0;
// 调用 Srand 时先给一个 time 函数,time 函数的参数为 NULL
// 返回类型需要强制类型转换为 unsigned int 型
srand((unsigned int)time(NULL));
// 保证起码可以进行一次选择是否玩游戏,当然可以玩很多次
do {
menu();
printf("请选择:");
scanf("%d", &input);
// 用户进行选择的时候有可能选择选择任何数字
// 所以使用 switch case 语句进行主意的处理
switch (input) {
// 这里的 PLAY 和 EXIT 是在 game.h 中定义的
// 所以在使用时需要引用头文件
case PLAY:
// 选择成功,玩游戏
game();
break;
case EXIT:
// 选择成功,退出游戏
printf("退出游戏!\n");
break;
// 默认选项,用户可能选择错误,此时就提示用户并让用户重新选择
default:
printf("选择错误,请重新选择!\n");
break;
}
} while (input);
}
int main()
{
test();
system("pause");
return 0;
}
真正的中心内容,游戏实现模块
// 游戏实现模块
// 养成良好的习惯,当把代码的主逻辑写好后,先测试一下,看代码写的是否存在问题
// 不要着急把里边的实现一次性写完
#include "game.h"
// 棋盘初始化
void InitBoard(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 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");
// printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
if (i < row - 1) {
for (j = 0; j < col; j++) {
printf("---", board[i][j]);
if (j < col - 1) {
printf("|");
}
}
printf("\n");
}
}
}
// 玩家走
void PlayerMove(char board[ROW][COL], int row, int col) {
// printf("玩家走!\n"); // 用于测试
int x = 0;
int y = 0;
printf("玩家走!");
while (1) {
printf("请输入坐标:");
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] = 'X';
break;
}
else {
printf("坐标被占用!请重新输入坐标:\n");
}
}
else {
printf("坐标非法!请重新输入坐标:\n");
}
}
}
// 电脑走
void ComputerMove(char board[ROW][COL], int row, int col) {
// printf("电脑走!\n"); // 用于测试
int x = 0;
int y = 0;
printf("电脑走!\n");
// 坐标范围 x[0-2], y[0-2]
// 使用 rand 函数生成随机数
// rand 函数使用之前需要调用 Srand
// 它不能在这里边调用,在整个工程里边,只要去生成随机数
// 那么这个随机数的生成只需要生成一次就够了,不需要频繁的设置随机数。
// 频繁的设置随机数会导致你所设置的 随机数不够随机,永远都是相邻的那些数字
// 所以在 test 函数中调用,因为 test 函数在整个 main 函数中只会调用一次
while (1) {
x = rand() % row;
y = rand() % col;
if (board[x][y] == ' ') {
board[x][y] = 'O';
break;
}
}
}
// 注意:一般情况下,我们不把 IsFull 这个函数放到头文件中
// 而是就放在这里,要注意这种设计。
// IsFull 这个函数判断棋局是否平局,判断游戏是否满了
// 这个函数只是为了在判断输赢的时候用一下
// 所以这个函数就没必要暴漏出去,我们只是内部需要,
// 所以不需要把它声明到头文件中去,直接在这里定义
// 这个 IsFull 函数不需要暴漏在外边去,
// 只需要在这个源文件下面的 CheckWin 这个函数中使用
// 为了避免和别人冲突,定义这个函数的时候在它前面加上一个 static
// static 修饰函数,这个函数只能在当前这个源文件中使用
// 不能再其他源文件内部使用,这个时候外部链接属性变成了内部链接属性
// 这样的话就保护了 ISFull 这个函数,使别人"看不到"这个函数,
// 此时别人也就可以再自己的代码中定义一个 IsFull 这个函数
// 同时也保证了命名不被冲突,是一个不错的设计
static int IsFull(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 CheckWin(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 < col; 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[0][0] == board[2][2]
&& board[0][0] != ' ') {
return board[0][0];
}
if (board[2][0] == board[1][1]
&& board[2][0] == board[0][2]
&& board[2][0] != ' ') {
return board[2][0];
}
// 判断棋盘是否满了,满了返回 'Q',没满返回 ' '
if (IsFull(board, row, col) ) {
return 'Q'; // 平局
}
return ' '; // 继续
}