三子棋小游戏的设计与实现
一、前言
本章适合初学C语言的小伙伴,通过编写简单的C语言小游戏,可以增强我们写代码的乐趣,
有利于我们更好的掌握所学的知识,话不多说,让我们进入正题。
首先让我们看一下游戏的最终效果图:
二、游戏的设计思路
由于该游戏的代码相比于我们之前设计的猜数字游戏的代码量偏多,所以这次我们封装成三个模块,分别是:
test.c - 用于游戏的逻辑测试
game.h - 用于函数的声明
game.c - 用于函数的实现
首先,我们要设计游戏的逻辑,因为无论如何,我们游戏的逻辑代码都会运行至少一次,所以这里我们选择使用 do{}while()循环,
同时,精美的游戏菜单是必不可少的,他能让玩游戏的人一目了然的知道咱们游戏的玩法,漂亮的游戏菜单还能提升玩家的玩的兴趣。
接下来,很容易想到,三子棋,第一步肯定需要先打印一个棋盘,这里虽然我们的游戏是三子棋,只需要打印一个3*3的棋盘,
但是为了游戏能更好的维护和开发,我们可以设计一个只需要改动一点代码就能变化的棋盘。
三、游戏功能的具体实现
1.游戏逻辑的设计与实现
这一模块主要包含了main主函数的设计与menu菜单函数的设计。
代码实现:
void menu()
{
printf("***********************************\n");
printf("***** 1. play *****\n");
printf("***** 0. exit *****\n");
printf("***********************************\n");
}
int main()
{
int input = 0;
do
{
//游戏的菜单
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
printf("游戏开始...\n");
break;
case 0:
printf("退出游戏!\n");
break;
default:
printf("选择错误,请重新选择!\n");
break;
}
} while (input);
return 0;
}
代码效果:
2.game游戏函数的设计与实现
这个模块是整个游戏的核心部分,主要包含了五个接口的设计与实现,分别是init_board(棋盘的初始化函数),
display_board(棋盘的打印函数),player_move(玩家下棋函数),computer_move(电脑下棋函数),is_win(判断输赢的函数)。
(1)init_board 初始化函数
数组初始化只需要将数组中的元素全部初始化为空格就可以了,这样做有个好处,棋盘打印的时候不会出现错位的情况,
如果全部初始化为'\0'的话,棋盘会出现错位的现象。
代码实现:
void init_board(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0;i < row;i++)
{
int j = 0;
for (j = 0;j < col;j++)
{
board[i][j] = ' ';
}
}
}
(2)display_board 棋盘的打印函数
打印棋盘的时候,可以直接打印一个3*3的棋盘,但是这样的代码不具有可维护性,所以这里我建议不要把代码写死了,让棋盘可以随着行和列的变化而变化。
代码实现:
void dispaly_board(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0;i < row;i++)
{
int j = 0;
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");
}
}
(3)player_move 玩家下棋函数
棋盘实现完了,下面就是设计下棋的玩法了,由于我们不是写的图形化界面,所以这里我们使用坐标来实现玩家下棋的位置,
让玩家输入一个坐标(这里要注意,不是所有的玩家都是程序员,一般人对坐标的理解都是从1开始的),当玩家输入好坐标之后
需要让电脑判断坐标的合法性并且如果该坐标已经下过棋的话也不能使用。
代码实现:
player_move(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("玩家下棋:>\n");
while (1)
{
printf("请输入要下棋的坐标:>");
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");
}
}
}
(4)computer_move 电脑下棋函数
电脑下棋涉及到算法,我们这里先不设计的那么深入,让电脑随机的生成一个坐标,当该坐标没有落子或则非法就可以了。
代码实现:
computer_move(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;
}
}
}
(5)is_win 判断输赢的函数
判断游戏的输赢,我们分三种情况,如果该函数返回的是'*'就是玩家赢,'#'就是电脑赢,'Q'就是平局,'C'就是继续游戏谁也没赢。
此时我们需要写一个函数判断棋盘是否满了,如果没有满且没有任何一方赢的时候就返回'C',如果棋盘满了且没有任何一方赢的时候就返回'Q'。
代码实现:
static int is_full(char board[ROW][COL], int row, int col)
{
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 is_win(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[0][0] != ' ')
{
return board[1][1];
}
if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][2] != ' ')
{
return board[1][1];
}
if (is_full(board, row, col) == 1)
{
return 'Q';
}
//继续
return 'C';
}
(6)game 函数的封装
代码实现:
void game()
{
char ret = 0;
//数据的存储需要一个3*3的二维数组
char board[ROW][COL] = { 0 };
//初始化二维数组
init_board(board, ROW, COL);
//打印棋盘
dispaly_board(board, ROW, COL);
while (1)
{
//玩游戏
//玩家下棋
player_move(board, ROW, COL);
dispaly_board(board, ROW, COL);
ret = is_win(board, ROW, COL);
if (ret != 'C')
{
break;
}
//电脑下棋
computer_move(board, ROW, COL);
dispaly_board(board, ROW, COL);
ret = is_win(board, ROW, COL);
if (ret != 'C')
{
break;
}
}
if (ret == '*')
{
printf("玩家赢!\n");
}
else if (ret == '#')
{
printf("电脑赢!\n");
}
else if(ret == 'Q') {
printf("平局");
}
dispaly_board(board, ROW, COL);
}
四、原码
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()
{
char ret = 0;
//数据的存储需要一个3*3的二维数组
char board[ROW][COL] = { 0 };
//初始化二维数组
init_board(board, ROW, COL);
//打印棋盘
dispaly_board(board, ROW, COL);
while (1)
{
//玩游戏
//玩家下棋
player_move(board, ROW, COL);
dispaly_board(board, ROW, COL);
ret = is_win(board, ROW, COL);
if (ret != 'C')
{
break;
}
//电脑下棋
computer_move(board, ROW, COL);
dispaly_board(board, ROW, COL);
ret = is_win(board, ROW, COL);
if (ret != 'C')
{
break;
}
}
if (ret == '*')
{
printf("玩家赢!\n");
}
else if (ret == '#')
{
printf("电脑赢!\n");
}
else if(ret == 'Q') {
printf("平局");
}
dispaly_board(board, ROW, COL);
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
//游戏的菜单
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
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 <math.h>
#define ROW 3
#define COL 3
//初始化
void init_board(char board[ROW][COL], int row, int col);
//打印棋盘
void dispaly_board(char board[ROW][COL], int row, int col);
//玩家下棋
player_move(char board[ROW][COL], int row, int col);
//电脑下棋
computer_move(char board[ROW][COL], int row, int col);
//判断谁赢
char is_win(char board[ROW][COL], int row, int col);
game.c
//三子棋函数的实现
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void init_board(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0;i < row;i++)
{
int j = 0;
for (j = 0;j < col;j++)
{
board[i][j] = ' ';
}
}
}
void dispaly_board(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0;i < row;i++)
{
int j = 0;
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");
}
}
player_move(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("玩家下棋:>\n");
while (1)
{
printf("请输入要下棋的坐标:>");
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");
}
}
}
computer_move(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;
}
}
}
static int is_full(char board[ROW][COL], int row, int col)
{
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 is_win(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[0][0] != ' ')
{
return board[1][1];
}
if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][2] != ' ')
{
return board[1][1];
}
if (is_full(board, row, col) == 1)
{
return 'Q';
}
//继续
return 'C';
}