总体思想
将下三子棋的过程分类为不同的模块,再逐一用函数实现相应的操作,采用化繁为简,化整为零的思想。因此采用多文件,将整个程序细分,使其变得清晰简明,一旦出现问题也方便调试和维护。比如这次就分为头文件,函数文件,以及主函数文件(可视情况而定)
首先将头文件命名为chess.h,函数文件命名为chess.c,主函数文件命名为main.c
代码讲解
- chess.h
#ifndef _CHESS_H_
#define _CHESS_H_
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#include<windows.h>
#pragma warning(disable:4996)
#define ROW 3
#define COL 3
#define RedPiece 'X'
#define BlackPiece 'O'
void Menu();
void Game();
void IntBoard(char board[][COL], int row, int col);
void ShowBoard(char board[][COL], int row, int col);
int PlayMove(char board[][COL], int row, int col);
void ComptMove(char board[][COL], int row, int col);
char JudgeResult(char board[][COL], int row, int col);
#endif
此时要注意头文件的格式,定义的全局变量和所要调用的函数都要在头文件中进行声明。三子棋所要用到三行三列的二维数组,IntBoard为定义三子棋棋盘;ShowBoard为展示三子棋棋盘;PlayMove为玩家落子;ComptMove为电脑落子;JudgeReslt为判断当前棋局。为了区分玩家与电脑的棋子,定义红棋RedPiece为玩家,显示为字符“X”,定义黑棋BlackPiece为电脑,显示为字符“O”。
- main.c
main.c要完成整个游戏的框架。首先调用定义的Menu显示主页,引导玩家进入游戏,其次采用switch让玩家选择是否进入游戏。若选择1,则调用main.c中的Game函数,实现游戏功能。要想多次游戏可将switch包含到while循环中,并定义一个局部变量永不成立作为进入循环的条件,若想要退出,则将其赋值1(表示成立)即可退出循环退出游戏。
#include "chess.h"//注意格式,""用于自定义头文件名
void Menu() {
printf("###########################\n");
printf("##########请选择###########\n");
printf("##1.play##########2.exit###\n");
printf("###########################\n");
printf("Please Enter Selsct:");
}
int main() {
int quit = 0;
while (!quit) {
int select = 0;
Menu();
scanf("%d", &select);
switch (select) {
case 1:
Game();
break;
case 2:
quit = 1;
break;
default:
printf("输入错误,请重新输入\n");
break;
}
}
return 0;
system("pause");
}
- chess.c
函数文件内包含所有定义的函数,让我们分别看一下。
1.IntBoard
void IntBoard(char board[][COL], int row, int col){
int i, j;
for (i = 0; i < row; i++) {
for (j = 0; j < col; j++) {
board[i][j] = ' ';
}
}
}
棋盘可以看成一个二维数组,运用定义的全局变量ROW,COL分别作为数组的行列,采用for循环嵌套将数组每个元素初始化为空格。
2.Showboard
void ShowBoard(char board[][COL], int row, int col) {
printf("| 1 | 2 | 3 |\n");
int i;
for (i = 0; i < row; i++) {
printf("--------------------\n");
printf(" %d | %c | %c | %c |\n", i+1, board[i][0], board[i][1], board[i][2]);//i+1为了显示出的序号符合习惯
}
printf("-------------------\n");
}
此为展示棋盘,注意最好将显示出的行列转换为习惯标号,有别于数组。
3.PlayMove
int PlayMove(char board[][COL], int row, int col) {
int x = 0;
int y = 0;
printf("Please Enter Your Pos<x,y>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= 3 && y >= 1 && y <= 3) {
if (board[x - 1][y - 1] != ' ') {
return 2;
}
board[x - 1][y - 1] = RedPiece;
return 0;
}
return 1;
}
定义整型函数,首先玩家需要输入要落子的坐标,再对坐标进行判断是否已经落子,若之前无落子则返回0,若有返回2,输入错误则返回1.
4.JudgeResult
char JudgeResult(char board[][COL], int row, int col) {
int i = 0;
for (i = 0; i < col; i++) {
if (board[0][i] != ' ' && board[0][i] == board[1][i] && board[1][i] == board[2][i]) {
return board[0][i];
}
}
for (i = 0; i < row; i++) {
if (board[i][0] != ' ' && board[i][0] == board[i][1] && board[i][1] == board[i][2]) {
return board[i][0];
}
}
if (board[0][0] != ' ' && board[0][0] == board[1][1] && board[1][1] == board[2][2]) {
return board[0][0];
}
if (board[0][2] != ' ' && board[0][2] == board[1][1] && board[1][1] == board[0][2]) {
return board[0][2];
}
for (i = 0; i < row; i++) {
int j;
for (j = 0; j < col; j++) {
if (board[i][j] == ' ') {
return 'N';
}
}
}
return 'P';
}
定义字符型函数,判断行,列,两个对角线三个相邻棋格是否相等且不为空,若符合则返回对应符号代表对应方获胜,若棋格仍有空位则返回N表示继续落子,若无任何行列对角线有三个相同符号且棋格无空,则说明此局平局返回P。
5.ComptMove
void ComptMove(char board[][COL], int row, int col) {
while (1) {
int i = rand() % row;
int j = rand() % col;
if (board[i][j] == ' ') {
board[i][j] = BlackPiece;
break;
}
}
printf("Computer...Done");
Sleep(1000);
}
采用while循环,用rand生成随机数作为坐标位置,若该格为空,则电脑落子,输出指示,到下一步。
6.Game
void Game() {
char board[ROW][COL];
IntBoard(board, ROW, COL);
char result = 'N';
srand((unsigned long)time(NULL));
while (1) {
ShowBoard(board, ROW, COL);
int type = PlayMove(board, ROW, COL);
if (1 == type) {
printf("您输入的坐标有误,请重新输入:\n");
continue;
}
else if (2 == type) {
printf("您输入的坐标已被占用,请重新输入:\n");
continue;
}
else {
printf("Player......Done\n");
}
result = JudgeResult(board, ROW, COL);
if (result != 'N') {
break;
}
ComptMove(board, ROW, COL);
result = JudgeResult(board, ROW, COL);
if (result != 'N') {
break;
}
}
ShowBoard(board, ROW, COL);
switch (result) {
case'P':
printf("恭喜你,和电脑打成平手\n");
break;
case RedPiece:
printf("恭喜你,你赢了\n");
case BlackPiece:
printf("对不起,电脑赢了\n");
break;
default:
printf("出错\n");
break;
}
}
此为重要的一步,Game要做的就是将其他函数整合起来,将初始化棋盘(IntBoard),展示棋盘(ShowBoard),用while循环将玩家落子(PalyMove),判断(JudgeResult),电脑落子(ComptMove),判断这一系列过程串起来,完成游戏步骤。
拓展
Game函数中有srand((unsigned long)time(NULL));,这个函数常与rand函数一起使用,包含在<stdlib.h>头文件中。
其原型为void srand(unsigned int seed)或void rand(void)seed相当于一个种子,srand函数根据seed设置随机起点,rand函数就是根据这个起点返回一个随机数【seed RANG_MAX】。为了让每一次程序调用的值都不同,所以使用系统时间用来初始化(time(NULL))
总结
虽然三子棋实现思路及过程并不难,学会其中思想非常关键。将大问题划分为小模块,用函数实现其功能可以让代码有条理,易维护,采用多文件也可以使思路更为清晰。养成良好的编写习惯非常重要,边写就要边试着运行,可以缩小出错的范围。这篇博客还有很多不足的地方,日后还得多加练习,才可以写出更漂亮的代码。