经过一段时间的学习,分支与循环、数组与函数的相关知识已经足够支撑我们完成一个小游戏了,这个小游戏不需要太多的功能,也不需要太复杂的逻辑,只需要懂点循环与分支,懂点数组的使用以及函数如何传参和返值即可。作为草稿纸上常出现的小游戏,三子棋的逻辑可谓是非常简单了,只需要玩家走一步、电脑走一步,并在八种可能获胜结果中比对就行了,下面让我们一起来看看三子棋的实现。
注意:本文为模拟游戏实现,代码量两百左右,共有两个源文件+一个头文件。
本程序一共就下面三个文件
我们直接从主函数开始!
下面看看game(游戏)函数的设计
//三子棋
#include"game.h"
void menu()
{
printf("**************************\n");
printf("******** 1.Play ********\n");
printf("******** 0.Exit ********\n");
printf("**************************\n");
}
void game()
{
char board[ROW][COL] = { 0 };//定义
init_board(board, ROW, COL);//初始化
display_board(board, ROW, COL);//打印
//下棋
char tmp = 0;
while (1)
{
player_move(board, ROW, COL);//玩家走
display_board(board, ROW, COL);//打印
tmp=who_win(board, ROW, COL);//判断
if (tmp != 'C')
break;
computer_move(board, ROW, COL);//电脑走
display_board(board, ROW, COL);//打印
tmp=who_win(board, ROW, COL);//判断
if (tmp != 'C')
break;
}
if (tmp == '*')
printf("玩家获胜\n");
else if (tmp == '#')
printf("电脑获胜\n");
else
printf("和局\n");
}
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;
}
主函数文件(test.c就介绍完了),下面介绍game.h这个头文件
这个头文件是自己建立的,目的就是在主函数源文件(test.c)和功能实现函数(game.c)中搭起一座沟通的桥梁。
#pragma once
#include<stdio.h>
#include<stdlib.h>
#define ROW 3
#define COL 3
void init_board(char board[][COL],int row,int col);//初始化
void display_board(char board[][COL], int row, int col);//打印
void player_move(char board[][COL], int row, int col);//玩家走
void computer_move(char board[][COL], int row, int col);//电脑走
char who_win(char board[][COL], int row, int col);//判断
自定义头文件部分代码量最少,功能也比较简单,如果说test.c是大脑、控制中心,那么game.h就相当于神经系统的一部分,是函数间沟通的桥梁,能对函数起主导作用。
此部分是程序运行必不可少的部分,没有它们,程序能跑,但什么都干不了,类似于植人。 本部分函数细节较多,因此我会分开逐个讲解。
初始函数主要就是把整个数组遍历一遍,然后将字符 空格 (' ')赋给每个数组即可。
void init_board(char board[][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 display_board(char board[][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");
}
}
}
玩家移动就是让玩家输入坐标,输入坐标有三种情况:非法、占用、成功,依次判断即可,其次要注意不是人人都知道坐标从0开始,因此在使用x、y时要减1,确保坐标不会越界。
void player_move(char board[][COL], int row, int col)//玩家走
{
int x = 0;
int y = 0;
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");
}
}
电脑移动不要太多判断,只需要给电脑一个随机值,一个判断条件。 直到电脑落子成功再跳出即可
void computer_move(char board[][COL], int row, int col)//电脑走
{
printf("轮到电脑落子:>");
while (1)
{
int x = rand() % row;
int y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
printf("电脑已落子\n");
}
这个函数出现于玩家或电脑移动之后,目的就是判断是否已达成胜利条件(共八种),如果达成了,就能跳出game函数中的死循环,结束游戏。
注意:本程序中函数就判断输赢函数和判断和局函数有返回值,一个是char,一个是int
int place(char board[][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 who_win(char board[][COL], int row, int col)//判断
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
if (board[i][0] == board[i][1] &&
board[i][1] == board[i][2] && board[i][2] != ' ')
return board[i][0];
}//判断行
for (j = 0; j < row; j++)
{
if (board[0][j] == board[1][j] &&
board[1][j] == board[2][j] && board[2][j] != ' ')
return board[0][j];
}//判断列
if (board[0][0] == board[1][1] &&
board[1][1] == board[2][2] && board[2][2] != ' ')
return board[0][0];//判断正斜
if (board[0][2] == board[1][1] &&
board[1][1] == board[2][0] && board[2][0] != ' ')
return board[0][2];//判断反斜
if (place(board, ROW, COL) == 1)
return 'Q';//判断和局
return 'C';//默认选项
}
经过game.c中各函数的一顿操作,最终由判断输赢函数携带返回值回到game函数进行判断
让我们直接来看看效果如何:
可以看到三种情况都能实现,现在只需要将代码进行优化,主要是优化输赢判断部分,使其兼容性更强,以后在玩五子棋时能沿用这套方案。
下面我将展示功能实现函数(game.c)的全部代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void init_board(char board[][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 display_board(char board[][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");
}
}
}
void player_move(char board[][COL], int row, int col)//玩家走
{
int x = 0;
int y = 0;
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");
}
}
void computer_move(char board[][COL], int row, int col)//电脑走
{
printf("轮到电脑落子:>");
while (1)
{
int x = rand() % row;
int y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
printf("电脑已落子\n");
}
int place(char board[][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 who_win(char board[][COL], int row, int col)//判断
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
if (board[i][0] == board[i][1] &&
board[i][1] == board[i][2] && board[i][2] != ' ')
return board[i][0];
}//判断行
for (j = 0; j < row; j++)
{
if (board[0][j] == board[1][j] &&
board[1][j] == board[2][j] && board[2][j] != ' ')
return board[0][j];
}//判断列
if (board[0][0] == board[1][1] &&
board[1][1] == board[2][2] && board[2][2] != ' ')
return board[0][0];//判断正斜
if (board[0][2] == board[1][1] &&
board[1][1] == board[2][0] && board[2][0] != ' ')
return board[0][2];//判断反斜
if (place(board, ROW, COL) == 1)
return 'Q';//判断和局
return 'C';//默认选项
}
我们本次三子棋之旅到这就要结束了,说实在的,三子棋这段代码蕴含逻辑比较简单,唯一需要注意的就是棋盘打印和电脑随机值,只有理解透彻了,代码敲起来才能得心应手!