写了一个三子棋小游戏检验最近的学习成果,还有自己在写的过程中遇到的一些错误和警告,以及解决办法。
1:函数传参时,要确保数据类型相同,否则会出现数据类型不兼容的警告
2:要返回函数值时,声明函数时的类型,就是返回的数据类型,尽量使各个代码块内的数据类型保持一致。
3:写循环语句时,注意循环能否顺利结束而不影响使用,注意每次循环完成跳出后执行的动作,否则会形成死循环。以这次代码为例,
```c
while (1)
{
player(arr);
board(arr);
computer(arr);
board(arr);
result = victory(arr);
if (result != NEXT)//每一次下完棋子后,都要判断是否有人胜利或平局。不是NEXT代表有结果,结束比赛。
{
break;
}
}
这样写时会发生什么呢?
玩家先下棋字,如果前8颗棋子都没有分出胜负,当第九颗由人落下后,棋盘已经满了,但是电脑不知道,循环也没有结束,就在电脑落子的函数里为了找到一个能下子的位置死循环了,你问为什么死循环?小笨蛋,当然是因为找不到落子的位置,又没有判断出结果。
4:函数有返回值时,考虑到每一种情况,对应的分支语句要加return。否则会出现warning C4715: “victory”: 不是所有的控件路径都返回值。
代码头文件game.h:
#ifndef _GAME_H
#define _GAME_H
#include <stdio.h>
#include <windows.h>
#include <time.h>
#pragma warning(disable:4996)
#define ROW 3
#define COL 3
#define PLAYER 'X'//玩家用X
#define COMP 'O'//电脑用O
#define DRAW 'D'//平局
#define NEXT 'N'//
void game();
void menu();
#endif
2. List item
代码main.c文件:
#include "game.h"
void game();
void menu();
int main()
{
int quit = 0;
int select = 0;
while (quit == 0)
{
menu();
scanf("%d", &select);
switch (select)
{
case 1:
game();
printf("再来?\n");
break;
case 2:
quit = 1;
printf("byebye!\n");
break;
default:
printf("输入有误!请重新输入!\n");
continue;
}
}
system("pause");
}
3. List item
代码game.c文件:
#include "game.h"
void menu()
{
printf("*****************************\n");
printf("****
1:play 2:exit ****\n");
printf("*****************************\n");
}
void board(char arr[][COL])//数组传参发生降维,二维数组可以省略第一个[]的数字。
{
int i = 0, j = 0;
printf(" | 1
| 2 | 3 | \n");
for (i = 1; i <= COL; i++)//玩家使用棋盘时候,确定位置从1开始 ,COL结束。
{
printf("---------------\n");
printf(" %d |", i);
for (j = 1; j <= COL; j++)
{
printf(" %c |",arr[i-1][j-1]);//由于数组名下标(0-2)和玩家(1-3)使用的差别,i和j分别减一。
}
printf("\n");
}
printf("\n");
}
void player(char arr[][COL])
{
int a = 0, b = 0;
while (1)
{
printf("请输入你要下的位置!\n");
scanf("%d %d", &a, &b);
if (a<1 || a>3 || b<1 || b>3)
{
printf("输入有误,请重新输入!");
continue;
}
else
{
if (arr[a - 1][b - 1] != ' ')
{
printf("该位置已有棋子,请重新选择位置!\n");
continue;
}
else
{
arr[a - 1][b - 1] = PLAYER;
break;
}
}
}
}
void computer(char arr[][COL])
{
while (1)
{
int r = rand() % ROW;
int q = rand() % COL;
if (arr[r][q] == ' ')
{
arr[r][q] = COMP;
break;
}
}
}
char victory(char arr[][COL])
{
int i = 0, j = 0;
for (i = 0; i < 3; i++)//判断行是否三连
{
if (arr[i][0] == arr[i][1] &&\
arr[i][0] == arr[i][2] &&\
arr[i][0] != ' ')//另外判断三个是否同样时,不可以写成 arr[i][0] ==
arr[i][1] == arr[i][2]==arr[i][0] != ' '。
// 因为'=='是个逻辑运算符,运算后结果为1或0,第一个“==”完成后变成了“1或者0”==arr[i][2],明显失真。
{
return arr[i][0];
}
}
for (i = 0; i < 3; i++)//判断列是否三连
{
if (arr[0][i] == arr[1][i]&&\
arr[0][i] == arr[2][i]&&\
arr[0][i] != ' ')
{
return arr[0][i];
}
}
if (arr[0][0] == arr[1][1]&&\
arr[1][1] == arr[2][2]&&\
arr[1][1] != ' ')//↘方向的三连判断
{
return arr[1][1];
}
if ((arr[0][2] == arr[1][1] &&\
arr[1][1] == arr[2][0])&&\
arr[1][1] != ' ')//↙方向的三连判断,上面的“\”起到承接下一行代码的作用提高可读性。
{
return arr[1][1];
}
for (i = 0; i < 3; i++)//用来判断尚未分出胜负时,棋盘是否下满,没满则下一步NEXT,已满则平局。
{
for (j = 0; j < 3; j++)
{
if (arr[i][j] == ' ')
{
return NEXT;
}
}
}
return DRAW;//函数有返回值时,考虑到每一种情况,对应的分支语句要加return。否则会出现warning C4715: “victory”: 不是所有的控件路径都返回值。
}
void game ()
{
char arr[ROW][COL];
//InitBoard(board, ROW, COL)
memset(arr,' ', sizeof(arr));
srand((unsigned long)time(NULL));
board(arr);
while (1)
{
player(arr);
board(arr);
char result = victory(arr);//用一个外部变量取代函数的返回值,提高可读性,确保数据类型相同。
if (result != NEXT)//每一次下完棋子后,都要判断是否有人胜利或平局。不是NEXT代表有结果,结束比赛。
{
break;
}
computer(arr);
board(arr);
result = victory(arr);
if (result != NEXT)//每一次下完棋子后,都要判断是否有人胜利或平局。不是NEXT代表有结果,结束比赛。
{
break;
}
}
char result = victory(arr);
if (result == PLAYER)
{
printf("你赢了!\n");
}
else
{
if (result == COMP)
{
printf("你输了!\n");
}
else
{
printf("平局!\n");
}
}
}