今天的内容分为分成三部分来写,分为test.c game.h game.c(学习一下如何拆分代码)
其中, test.c 测试游戏的逻辑; game.h 关于游戏相关的函数声明,符号声明,头文件的包含; game.c 游戏相关函数的实现
开始进入正题吧。
打印菜单以及棋盘
在一开始就打印简易菜单,使用do while来实现
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);
简易菜单
void menu()
{
printf("***********************************\n");
printf("*********** 1.play ************\n");
printf("*********** 0.exit ************\n");
printf("***********************************\n");
}
打印完菜单之后,进行选择,键入1则进入游戏,0则退出游戏,键入其它则重新键入数字进行选择。
棋盘初始化
使用二维数组来建立棋盘,这只一个row行col列的二维数组。在game.h头文件中定义数组的行和列#define ROW 3 #define COL 3 。
char board[ROW][COL];
InitBoard(board,ROW,COL);
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; int j = 0;
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)
{
int j = 0;
for (j = 0; j < col; j++)
{
printf("---");
if(j<col-1)
printf("|");
}
printf("\n");
}
}
}
测试显示结果:
***********************************
*********** 1.play ************
*********** 0.exit ************
***********************************
请选择:》1
三子棋
| |
---|---|---
| |
---|---|---
| |
假设没有初始化棋盘的话,将InitBoard(board,ROW,COL); 给注释掉,观察一下打印结果:
***********************************
*********** 1.play ************
*********** 0.exit ************
***********************************
请选择:》1
三子棋
?| ?| ?
---|---|---
?| ?| ?
---|---|---
?| ?| ?
可以看到,如果不初始化棋盘的话,打印出来的就是随机值。 有一点需要注意,当前的代码写得还是不够通用。如果将头文件game.h中的行和列均改为10,因为在打印棋盘的函数实现那里只写出来三列,所以之后即使在头文件里面更改了行和列的数字,也还是只有三列。 如何修改代码呢?将每一个棋盘看成是被分割线分开的,行元素之间的分隔线是|,列元素之间的分割线是—
void DisplayBoard(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++)
{
printf(" %c ", board[i][j]);
if (j < col - 1)
{
printf("|");
}
}
printf("\n");
if (i < row - 1)
{
int j = 0;
for (j = 0; j < col; j++)
{
printf("---");
if(j<col-1)
printf("|");
}
printf("\n");
}
}
}
测试一下效果:
***********************************
*********** 1.play ************
*********** 0.exit ************
***********************************
请选择:》1
三子棋
| | | | | | | | |
---|---|---|---|---|---|---|---|---|---
| | | | | | | | |
---|---|---|---|---|---|---|---|---|---
| | | | | | | | |
---|---|---|---|---|---|---|---|---|---
| | | | | | | | |
---|---|---|---|---|---|---|---|---|---
| | | | | | | | |
---|---|---|---|---|---|---|---|---|---
| | | | | | | | |
---|---|---|---|---|---|---|---|---|---
| | | | | | | | |
---|---|---|---|---|---|---|---|---|---
| | | | | | | | |
---|---|---|---|---|---|---|---|---|---
| | | | | | | | |
---|---|---|---|---|---|---|---|---|---
| | | | | | | | |
玩家下棋:
玩家下棋的功能要求玩家输入所要放入棋子的坐标,坐标分别为(1,1)(1,2)、(1,3)、(2,1)、(2,2)、(2,3)、(3,1)、(3,2)、(3,3)行和列的坐标都是从1开始。首先要判断玩家键入的坐标是否合法 ,我们知道二维数组的元素下标无论是行还是列都是从0开始的,也就是(0,0)/(0,1)/(0,2) (1,0)/(1,1)/(1,2) (2,0)/(2,1)/(2,2)。所以判断坐标的合法性就看坐标的值是否都小于等于行的值和列的值,且大于等于1,因此就有了判断条件:
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
}
else
{
printf("坐标非法,请重新输入\n");
}
其次要在进行游戏时,判断坐标处是否被占用
if (board[x - 1][y - 1] == ' ')
{
board[x - 1][y - 1] = '*';
break;
}
else
{
printf("坐标被占用,请重新输入\n");
}
编写好这一部分的代码时,验证一下功能吧。玩家下棋代码的运行结果的测试:
***********************************
*********** 1.play ************
*********** 0.exit ************
***********************************
请选择:》1
三子棋
| |
---|---|---
| |
---|---|---
| |
玩家走:》
请输入下棋的坐标:》
1 1
* | |
---|---|---
| |
---|---|---
| |
玩家走:》
请输入下棋的坐标:》
1 1
坐标被占用,请重新输入
请输入下棋的坐标:》
1 3
* | | *
---|---|---
| |
---|---|---
| |
玩家走:》
请输入下棋的坐标:》
1 4
坐标非法,请重新输入
请输入下棋的坐标:》
电脑下棋
电脑下棋与玩家落子的原理都是一样的,都需要给定一个坐标。这里用生成随机数的方法来生成电脑落子的坐标。生成随机数要用到rand函数,在用rand函数的时候会首先用到srand,这里我们将srand函数写到main函数里面。
srand((unsigned int)time(NULL));
电脑下棋函数的实现如下:
void ComputerMove(char board[ROW][COL], int row, int col)
{
printf("电脑走:>\n");
while (1)
{
int x = rand() % row;
int y = rand() % col;
if (board[x][y] == ' ')
{
board[x ][y ] = '#';
break;
}
}
}
编写好电脑下棋的代码之后,测试一下运行效果:
***********************************
*********** 1.play ************
*********** 0.exit ************
***********************************
请选择:》1
三子棋
| |
---|---|---
| |
---|---|---
| |
玩家走:》
请输入下棋的坐标:》
1 1
* | |
---|---|---
| |
---|---|---
| |
电脑走:>
* | | #
---|---|---
| |
---|---|---
| |
玩家走:》
请输入下棋的坐标:》
2 3
* | | #
---|---|---
| | *
---|---|---
| |
电脑走:>
* | | #
---|---|---
| | *
---|---|---
| # |
玩家走:》
请输入下棋的坐标:》
1 2
* | * | #
---|---|---
| | *
---|---|---
| # |
电脑走:>
* | * | #
---|---|---
| # | *
---|---|---
| # |
玩家走:》
请输入下棋的坐标:》
2 1
* | * | #
---|---|---
* | # | *
---|---|---
| # |
电脑走:>
* | * | #
---|---|---
* | # | *
---|---|---
| # | #
玩家走:》
请输入下棋的坐标:》
3 1
* | * | #
---|---|---
* | # | *
---|---|---
* | # | #
电脑走:>
可以看到,最后棋盘被占满,电脑没办法走了,编程死循环了,所以接下来还要做一些工作。在无论是玩家还是电脑下棋之后,每一次落子都要判断一下谁赢啦。
判断游戏状态
分析一下: 在游戏进行的过程中,分为四种状态:
- 玩家赢了—此时返回一个*
- 电脑赢了—此时返回一个#
- 谁也没赢,平局----返回Q
- 没有赢,也没有平局,游戏继续—返回C
那么IsWin函数返回的就是char类型。要判断有没有人赢得游戏的话,就是要判断数组里面是不是有3个相连的,三个横线相连,三个列相连或者对角线相连。所以还是需要传参过去,要遍历数组的行、列以及对角线。所以传过去的参数有board以及ROW, COL。将函数的返回值记录到ret里面去。 在前三种状态下,游戏应该结束,最后一种状态的情况下,游戏继续。
char IsWin(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][1] == board[i][2] && board[i][0] != ' ')
{
return board[i][0];
}
}
for (i = 0; i < col; i++)
{
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[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];
}
int ret=IsFull(board,row,col);
if (ret == 1)
{
return 'Q';
}
return 'C';
}
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;
}
演示一下代码的整体效果:
玩家赢了的情况:
***********************************
*********** 1.play ************
*********** 0.exit ************
***********************************
请选择:》1
三子棋
| |
---|---|---
| |
---|---|---
| |
玩家走:》
请输入下棋的坐标:》
1 1
* | |
---|---|---
| |
---|---|---
| |
电脑走:>
* | |
---|---|---
| |
---|---|---
| # |
玩家走:》
请输入下棋的坐标:》
2 2
* | |
---|---|---
| * |
---|---|---
| # |
电脑走:>
* | |
---|---|---
# | * |
---|---|---
| # |
玩家走:》
请输入下棋的坐标:》
3 3
* | |
---|---|---
# | * |
---|---|---
| # | *
玩家赢了
* | |
---|---|---
# | * |
---|---|---
| # | *
电脑赢了的情况:
***********************************
*********** 1.play ************
*********** 0.exit ************
***********************************
请选择:》1
三子棋
| |
---|---|---
| |
---|---|---
| |
玩家走:》
请输入下棋的坐标:》
3 3
| |
---|---|---
| |
---|---|---
| | *
电脑走:>
| |
---|---|---
| |
---|---|---
# | | *
玩家走:》
请输入下棋的坐标:》
3 2
| |
---|---|---
| |
---|---|---
# | * | *
电脑走:>
| |
---|---|---
| # |
---|---|---
# | * | *
玩家走:》
请输入下棋的坐标:》
1 1
* | |
---|---|---
| # |
---|---|---
# | * | *
电脑走:>
* | |
---|---|---
| # | #
---|---|---
# | * | *
玩家走:》
请输入下棋的坐标:》
2 1
* | |
---|---|---
* | # | #
---|---|---
# | * | *
电脑走:>
* | | #
---|---|---
* | # | #
---|---|---
# | * | *
电脑赢了
* | | #
---|---|---
* | # | #
---|---|---
# | * | *
平局的情况:
***********************************
*********** 1.play ************
*********** 0.exit ************
***********************************
请选择:》1
三子棋
| |
---|---|---
| |
---|---|---
| |
玩家走:》
请输入下棋的坐标:》
2 2
| |
---|---|---
| * |
---|---|---
| |
电脑走:>
| | #
---|---|---
| * |
---|---|---
| |
玩家走:》
请输入下棋的坐标:》
2 3
| | #
---|---|---
| * | *
---|---|---
| |
电脑走:>
| | #
---|---|---
# | * | *
---|---|---
| |
玩家走:》
请输入下棋的坐标:》
1 2
| * | #
---|---|---
# | * | *
---|---|---
| |
电脑走:>
| * | #
---|---|---
# | * | *
---|---|---
| # |
玩家走:》
请输入下棋的坐标:》
3 1
| * | #
---|---|---
# | * | *
---|---|---
* | # |
电脑走:>
| * | #
---|---|---
# | * | *
---|---|---
* | # | #
玩家走:》
请输入下棋的坐标:》
1 1
* | * | #
---|---|---
# | * | *
---|---|---
* | # | #
平局
* | * | #
---|---|---
# | * | *
---|---|---
* | # | #
该代码还是有不足的,就是在判断输赢的那一块,该代码只能判断三子棋的输赢,之后需要想一下怎么才能实现更好地判断输赢,使得代码具有普遍性呢。
整体代码如下:
test.c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include "game.h"
void menu()
{
printf("***********************************\n");
printf("*********** 1.play ************\n");
printf("*********** 0.exit ************\n");
printf("***********************************\n");
}
void game()
{
char board[ROW][COL];
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;
ComputerMove(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
{
printf("平局\n");
}
DisplayBoard(board, ROW, COL);
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
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.h
#pragma once
#include <stdio.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 ComputerMove(char board[ROW][COL], int row, int col);
char IsWin(char board[ROW][COL], int row, int col);
game.c
#define _CRT_SECURE_NO_WARNINGS
#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; int j = 0;
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)
{
int j = 0;
for (j = 0; j < col; j++)
{
printf("---");
if(j<col-1)
printf("|");
}
printf("\n");
}
}
}
void PlayerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("玩家走:》\n");
while (1)
{
printf("请输入下棋的坐标:》\n");
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] = '*';
break;
}
else
{
printf("坐标被占用,请重新输入\n");
}
}
else
{
printf("坐标非法,请重新输入\n");
}
}
}
void ComputerMove(char board[ROW][COL], int row, int col)
{
printf("电脑走:>\n");
while (1)
{
int x = rand() % row;
int y = rand() % col;
if (board[x][y] == ' ')
{
board[x ][y ] = '#';
break;
}
}
}
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 IsWin(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][1] == board[i][2] && board[i][0] != ' ')
{
return board[i][0];
}
}
for (i = 0; i < col; i++)
{
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[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];
}
int ret=IsFull(board,row,col);
if (ret == 1)
{
return 'Q';
}
return 'C';
}
补充:
数组作为函数传参的时候: 在函数调用部分,实参写的是数组名;函数定义时,传过去的形参可以写成2种形式:一种是数组形式;另一种是指针形式。但是这个传过去的形参本质上都是指针,因为在数组传参的时候,传过去的数组名其实是数组首元素的地址。 假如有一个一维数组、一个二维数组,分别为:
int arr[10];
char ch[3][5];
此时有一个test1的函数,要给这个函数传参。
test1(arr);
void test1(int arr[10])
{
}
void test1(int arr[])
{
}
void test1(int *arr)
{
}
假设现在还有一个函数是test2函数,它要调用二维数组ch
test2(ch);
void test2(char arr[3][5])
{
}
void test2(char arr[][5])
{
}
这个地方的形参也可以是指针。
|