一、制作目的
综合运用基础知识以及数组来求解问题,巩固夯实基础,思维扩展提升能力。
二、制作思路
利用二维数组的二维性在平面内能产生坐标来实现棋子的落定。运用循环以及字符制作一个伪棋盘。
难点:二维数组的运用以及循环制作棋盘过程
三、制作步骤
1、菜单(menu)
代码:
void menu()
{
printf("******************************\n");
printf("**********1、开始游戏*********\n");
printf("**********0、退出*************\n");
printf("******************************\n");
}
分析:制作游戏菜单,用printf函数输出打印,能让游戏操作性提高,玩家更容易上手。
2、初始化数组(Initboard)
代码:
void Initboard(char board[][3], int row, int col)//初始化
{
int i;
int j;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
board[i][j] = ' ';
}
}
}
分析:利用双重循环遍历二维数组对其值赋字符空格,即实现棋盘空的操作。row控制行,col控制列。亦可以用memset库函数来实现上述操作,memset(board,' ',row*col*sizeof(char));参数设置分别是数组,打印字符,打印的数目。
3、打印棋盘(Showboard)
代码:
void Showboard(char board[][3], int row, int col)//打印棋盘
{
int i;
int j;
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");
}
}
}
分析:首先依然是双重循环来实现字符的“井”字型打印,分别利用“|”实现列的分隔,“---”来实现行的分隔。
注意:打印输出 board[i][j]的时候注意为%c腾出相应的空间(代码中用空格实现)以便与“---”对其更加美观。
而输出打印“|”的时候注意条件是列减一。使用if (i< row - 1)可防止输出三行“---”。最后需每打印一行换行。
4、玩家输入,电脑输入( PlayMove,ComputerMove)
代码:
void PlayMove(char board[][3], int row, int col)
{
int x = 0;
int y = 0;
while (1)
{
printf("请输入1~3的坐标:");
scanf("%d%d", &x, &y);
if (x >= 1 && x <= 3 && y >= 1 && y <= 3)
{
if (board[x - 1][y - 1] == ' ')
{
board[x - 1][y - 1] = 'X';
break;
}
else
{
printf("此位置已经被占用!\n");
}
}
else
{
printf("憨憨!坐标输错了!\n");
}
}
}
void ComputerMove(char board[][3], int row, int col)
{
printf("电脑移动\n");
while (1)
{
int x = rand() % row + 1;// [0-3) [1-4)
int y = rand() % col + 1;
if (board[x - 1][y - 1] == ' ')
{
board[x - 1][y - 1] ='O';
break;
}
}
}
分析:利用x,y二维坐标控制落子。利用条件判断输入是否合法。
注意:当条件合法时,落子后需用break跳出条件。用空格判断此处是否落子。使用是需结合打印函数实现,不然后面看不到你落子的结果。其次在主函数定义随机数种子让电脑随机刷数。 x = rand() % row 此时x的范围是0·3的左开右闭区间。要显示1·3需加一实现。
5、判断胜负(IsWin)
代码:
static int IsFull(char board[][3], int row, int col)//static修饰只能在game.c中使用。判断棋盘下满否。
{
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;
}
char IsWin(char board[][3], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
//行
if (board[i][0] == board[i][1]&& board[i][1] == board[i][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][0] != ' ')
{
return board[0][2];
}
//平局
if (IsFull(board, row, col) == 1)
{
return 'Q';//棋盘下满,平局
}
}
//棋盘未满,胜负未分
return ' ';
}
分析:胜负的八种情况,行一样,列一样,斜着一样两种。然后依次返回里面的值,判断棋子判别胜负。棋盘未满胜负未分时继续游戏。棋盘已满胜负未分时平局。
注意:static定义的函数只在此文件中能使用。理清胜利情况,不要搞混。
6、主函数
代码:
void game()
{
char r;
char board[3][3] = { 0 };
Initboard(board, 3, 3);
Showboard(board, 3, 3);
while (1)
{
PlayMove(board, 3, 3);
r=IsWin(board, 3, 3);
if (r != ' ')
{
break;
}
Showboard(board, 3, 3);
ComputerMove(board, 3, 3);
r = IsWin(board, 3, 3);
if (r != ' ')
{
break;
}
Showboard(board, 3, 3);
}
if (r == 'Q')
{
printf("平局!\n");
}
else if (r == 'X')
{
printf("哈哈!我赢了!!\n");
}
else if (r == 'O')
{
printf("尴尬,我输了!!\n");
}
}
int main()
{
int in = 0;
do{
menu();
srand((unsigned int)time(NULL));
printf("请输入操作:");
scanf("%d", &in);
switch (in)
{
case 1:
game();
break;
case 0:
printf("游戏结束:\n");
break;
default:
printf("憨憨!你输入错了!\n");
break;
}
} while (in);
return 0;
分析:主函数运用switch函数调用不同的输入操作,利用game函数实现其他子函数的功能,思路清晰。
7、头文件
代码:
#ifndef __GAME_H__
#define __GAME_H__
#include <math.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void Initboard(char board[][3], int row, int col);
void Showboard(char board[][3], int row, int col);
void PlayMove(char board[][3], int row, int col);
char IsWin(char board[][3], int row, int col);
void ComputerMove(char board[][3], int row, int col);
#endif
分析:利用头文件,对函数进行库存然后只需在主函数调用自己写的game.h即可。创建game.h的同时创建game.c文件用于存放函数,将所写的函数放入其中,思路清晰,尽量保持test.c测试文件存放主函数来测试程序执行,代码少更加易懂。
三、总结
从这次实践中,我掌握了数组,函数,循环的基本用法,也学到了如何解决一个实际问题,以及如何让逻辑更加清晰,代码更加简练易懂。代码量虽然很大,但是细分成小块慢慢消化,还是很容易没明白其逻辑的。尤其注意一些特殊的细节,让程序更加精细,更加准确。在编写的过程中,尽量逐步调试,写好一个函数就去验证其功能,这样改bug起来将会变的轻松,避免一股脑写完发现一堆bug,改起来太吃力。