在开始写代码时,我将代码分为三个部分,
test.c
此文件我用于游戏的测试,主函数也在其中
game.c
此文件中,我将把各类函数的定义放入其中
game.h
此文件中,我将放入各类函数的声明,并且把头文件包含在此头文件中,在game.c和test.c中只要包含game.h头文件时就可以避免多次包含头文件
在写代码量比较多的程序时,我建议给代码类型划分区域,如:头文件包含在同一个项中、函数的定义写在同一个项中,这样一来,增加了我们代码的可读性。
在test.c中我们写如如下代码
int main()
{
int input = 0;
do {
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
printf("三子棋\n");
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,重新选择\n");
break;
}
} while (input);
return 0;
}
do while的循环语句的使用方式有些不同,它是先执行一次循环内容,再判断条件是否满足继续进行循环,这样方便打印游戏的菜单,然后再选择是否继续游戏,我们用一个input变量来供玩家进行选择是否继续游戏,
菜单代码如下:
void menu()
{
printf("****************************\n");
printf("******** 1.play ********\n");
printf("******** 0.exit ********\n");
printf("****************************\n");
}
很简易的匹配了do while循环中的条件,非0为真,0为假。
在编写游戏执行逻辑时我们要思考,在game函数中要怎么去写:三子棋要怎么去下、玩家下棋,和电脑下棋要怎么实现、怎样去判断输赢。当然,最基础的就是棋盘怎么实现,一个游戏能被玩起来最起码要有一个能玩的载体,不然任何操作都是空谈!
换个角度思考,三子棋,有三行三列,和c语言中的数组十分相似,所以我们就定义一个三行三列的数组,来存放棋子,难道我们只是简单的定义一个数组char board[3][3] = { 0 },像这样我觉得不是很灵活,用#define定义起来应该更加方便,
#define ROW 3
#define COL 3
在创建棋盘之前,我们先在game.h中用#define定义行ROW为3,列COL为3,再在其他项中包含game.h头文件,之后只要用到行和列我们都用ROW和COL来代替,这样一来,增加了我们后期代码的可续性,比如我们某一天不想玩三子棋了,想玩四子棋,五子棋,只要在所包含的头文件当中修改ROW和COL的值,就可以做到统一修改的操作,十分的灵活。修改完后我们创建一个三行三列的多维数组:char board[ROW][COL] = { 0 },再对它进行初始化,我用 InitBoard(board,ROW,COL)函数来初始化我的棋盘;
InitBoard(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; i < col; j++)
// {
// board[i][j] = ' ';
// }
//}
memset(&board[0][0], ' ', row * col * sizeof(board[0][0]));
}//初始化棋盘
在此函数中我们对数组进行初始化,将 ‘ ’(空格)作为数组的元素,我们可以用循环遍历的方式将空格放到数组中,或者用memset函数将数组初始化为空格,当然,在引用此函数时我们要引入相应的头文件string.h.
之前我们提到过,玩游戏得有一个载体,数组创建了,怎么去下呢?
玩家从何而知我的棋子落在了 哪里?
我们得让它显现出来,让玩家清楚的看到,棋子的落点。
相当于,将一幅画,从一个样板变成一幅画。也就是美化棋盘
我们在game.c中创建一个函数:DisplayBoard(board, ROW, COL),用来展示我们的棋盘;
DisplayBoard(board, ROW, COL)
void DisplayBoard(char board[ROW][COL], int row, int col)
{
int j = 0;
for (j = 0; j < row; j++)
{
int i = 0;
for (i = 0; i < col; i++)
{
printf(" %c ", board[j][i]);
if(i < col - 1)
printf("|");
}
printf("\n");
if (j < row - 1)
{
for (i = 0; i < col; i++)
{
printf("---");
if (i < col - 1)
printf("|");
}
}
printf("\n");
}
}//展示棋盘;
展示的效果如下:
这样一来,棋盘有了,现在就差怎么下的问题了,
在游戏中,我用*代替玩家的棋子,用#代替电脑的棋子
先从玩家的角度开始,作为一个玩家,看到棋盘,看到第一行就是第一行,看到第三行就是第三行,要在第三行第三列下棋,就输入3 3 坐标,但作为一个程序员,我们知道,数组的下标都是从0开始的,因此我们在让玩家下棋的时候要在程序内部处理好这一细节,避免数组越界,游戏崩溃,玩家下棋我用PlayerMove(board, ROW, COL)这一函数来执行玩家下棋的程序;
PlayerMove(board, ROW, COL);
//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col)
{
printf("玩家下棋\n");
printf("请输入要落子的坐标:>");
int x = 0;
int y = 0;
while (1)
{
scanf("%d%d", &x, &y);
if (x > 0 && x <= row && y > 0 && y <= col)
{
if (board[x-1][y-1] == ' ')
{
board[x - 1][y - 1] = '*';
break;
}
else
{
printf("坐标被占用,请重新输入\n");
}
}
else
{
printf("坐标违法,请重新输入\n");
}
}
}
在此函数中,我们还要考虑到此坐标下的棋是否被占用,或者坐标是否在合法范围内,用一个无限循环来搭建这一内容,满足目标棋盘为空,并且在合法范围之内的条件,就break跳出循环,代表玩家下棋成功
玩家的棋子下完了,接下来就是电脑下棋了,
那么怎么让电脑下棋呢?
在此之前,我们得了解函数rand和srand,这两个函数搭配使用可以生成一个随机数,这样一来就达到电脑下棋这一程序了,虽然不能做到人机那样聪明,但至少能与玩家初步的完成一场棋已经很不错了,电脑下棋我用 :CmoputerMove(board, ROW, COL)这一函数实现;
CmoputerMove(board, ROW, COL)
//电脑下棋
void CmoputerMove(char board[ROW][COL], int row, int col)
{
printf("电脑下棋\n");
while (1)
{
int x = rand() % col;
int y = rand() % row;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
在此程序中,我们只要对产生的随机数模上一个行或者列就能生成0~2之间的数字
现在程序初步的做到了玩家下棋和电脑下棋这两个步骤,完成以上代码运行程序如下;
当我们把所有棋盘都占满之后,程序就无法进行下去了,当到电脑的回合时,电脑生成的随机数对应到棋盘的坐标,发现没有可以落子的地方了,程序就无法运行下去了,所以我们还要将代码继续完善,到了最终一步,要判断玩家与电脑的输赢了。
我们知道,三子棋的获胜条件是某一行或者某一列棋子相同获胜,或棋盘的对角线棋子相同视为胜利,问题又来了,当我们知道某一行或者某一列棋子相同,或棋盘的对角线棋子相同时,又怎么知道获胜的一方是电脑还是玩家呢?很简单,只要将满足以上四种条件之一的其中的某一个棋子作为函数的返回值,返回到主函数中,再让主函数判断,它返回的是#还是*,是#就是电脑胜利,是*就是玩家胜利,
返回#就是电脑胜利
返回*就是玩家胜利
返回C继续
返回Q就是平局
我用IsWin(board, ROW, COL)作为判断胜利的函数体,用在game函数中创建的变量ret来接受此函数的返回值
IsWin(board, ROW, COL)
char IsWin(char board[ROW][COL], int row, int col)
{
//玩家获胜返回'*'
//电脑获胜返回'#'
//平局返回 'Q'
//继续返回 'C'
int i = 0;
for (i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
{
return board[1][i];
}
if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' ')
{
return board[i][1];
}
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 (IsFull(board, row, col))
{
return 'Q';
}
}
return 'C';
}
在此函数中,我又 嵌套了一个判断平局的函数,在循环判断中,判断平局函数也将会执行,判断平局函数的思路如下:当以上获胜的四种条件不满足时,程序就会进入判断平局函数,在此函数中,我们只做遍历数组这一简单的事情,只要每一行中的每一列都没有出现空格的话,同时也没有在某一方获胜的情况下,我们就返回Q,Q就作为判断是否平局的条件,
IsFull(board, row, col)
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; i < col; j++)
{
if (board[i][j] == ' ')
{
return 0;
}
}
}
return 1;
}
game函数体内的代码
void game()
{
srand((unsigned int)time(NULL));
char board[ROW][COL] = { 0 };
//先初始化一个3*3的棋盘
InitBoard(board,ROW,COL);
//打印棋盘
DisplayBoard(board, ROW, COL);
char ret = 0;
while (1)
{
//玩家下棋
PlayerMove(board, ROW, COL);
//打印棋盘
DisplayBoard(board, ROW, COL);
//判断输赢
ret = IsWin(board, ROW, COL);
if (ret != 'C')
{
break;
}
//电脑下棋
CmoputerMove(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 if ('Q' == ret)
{
printf("平局\n");
}
}
test.c中的代码
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void menu()
{
printf("****************************\n");
printf("******** 1.play ********\n");
printf("******** 0.exit ********\n");
printf("****************************\n");
}
void game()
{
srand((unsigned int)time(NULL));
char board[ROW][COL] = { 0 };
//先初始化一个3*3的棋盘
InitBoard(board,ROW,COL);
//打印棋盘
DisplayBoard(board, ROW, COL);
char ret = 0;
while (1)
{
//玩家下棋
PlayerMove(board, ROW, COL);
//打印棋盘
DisplayBoard(board, ROW, COL);
//判断输赢
ret = IsWin(board, ROW, COL);
if (ret != 'C')
{
break;
}
//电脑下棋
CmoputerMove(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 if ('Q' == ret)
{
printf("平局\n");
}
}
int main()
{
int input = 0;
do {
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
printf("三子棋\n");
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,重新选择\n");
break;
}
} while (input);
return 0;
}
game.c中的代码
#define _CRT_SECURE_NO_WARNINGS 1
//函数的实现
#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; i < col; j++)
// {
// board[i][j] = ' ';
// }
//}
memset(&board[0][0], ' ', row * col * sizeof(board[0][0]));
}//初始化棋盘
void DisplayBoard(char board[ROW][COL], int row, int col)
{
int j = 0;
for (j = 0; j < row; j++)
{
int i = 0;
for (i = 0; i < col; i++)
{
printf(" %c ", board[j][i]);
if(i < col - 1)
printf("|");
}
printf("\n");
if (j < row - 1)
{
for (i = 0; i < col; i++)
{
printf("---");
if (i < col - 1)
printf("|");
}
}
printf("\n");
}
}//展示棋盘;
//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col)
{
printf("玩家下棋\n");
printf("请输入要落子的坐标:>");
int x = 0;
int y = 0;
while (1)
{
scanf("%d%d", &x, &y);
if (x > 0 && x <= row && y > 0 && y <= col)
{
if (board[x-1][y-1] == ' ')
{
board[x - 1][y - 1] = '*';
break;
}
else
{
printf("坐标被占用,请重新输入\n");
}
}
else
{
printf("坐标违法,请重新输入\n");
}
}
}
//电脑下棋
void CmoputerMove(char board[ROW][COL], int row, int col)
{
printf("电脑下棋\n");
while (1)
{
int x = rand() % col;
int y = rand() % row;
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; i < col; j++)
{
if (board[i][j] == ' ')
{
return 0;
}
}
}
return 1;
}
//判断输赢
char IsWin(char board[ROW][COL], int row, int col)
{
//玩家获胜返回'*'
//电脑获胜返回'#'
//平局返回 'Q'
//继续返回 'C'
int i = 0;
for (i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
{
return board[1][i];
}
if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' ')
{
return board[i][1];
}
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 (IsFull(board, row, col))
{
return 'Q';
}
}
return 'C';
}
game.h中的代码
#pragma once
//头文件的包含
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#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 CmoputerMove(char board[ROW][COL], int row, int col);
//判断输赢
char IsWin(char board[ROW][COL], int row, int col);
//判断是否平局
int IsFull(char board[ROW][COL], int row, int col);