前言
本篇仅为学习分支循环结构,不能实现高质量的人机博弈,小游戏随便看看就行。
一、程序框架
创建头文件game.h,用于定义棋盘行数列数,声明函数;
创建源文件game,c,用于游戏过程中具体函数的实现;
创建源文件test.c,用于游戏宏观流程的实现。
二、具体实现
1.游戏的开始和结束
采用do…while循环实现,这部分写在test.c的main函数中。
代码如下(示例):
int input;
do
{
menu();
printf("请输入:");
scanf("%d", &input);
switch(input)
{
case 1:
game();
break;
case 0:
printf("退出成功\n");
break;
default:
printf("选择错误,重新选择:\n");
break;
}
} while (input);
2.game()函数
主要是为了封装每一局游戏的具体实现过程,方便阅读,也写在test.c文件中。
代码如下(示例):
void game()
{
char board[ROW][COL];
InitBoard(board, ROW, COL);//初始化棋盘
Display(board, ROW, COL);//展示棋盘
while (1)
{
Player(board, ROW, COL);//玩家下棋
Display(board, ROW, COL);
if (IsWin(board, ROW, COL) == 1)
{
printf("恭喜你获胜!\n");
break;
}
else if(IsWin(board, ROW, COL) == 0)
{
printf("平局\n");
break;
}
Sleep(2000);//查看自己落子效果
Computer(board, ROW, COL);//电脑下棋
Display(board, ROW, COL);
if (IsWin(board, ROW, COL) == 1)
{
printf("桀桀桀,电脑获胜!\n");
break;
}
else if (IsWin(board, ROW, COL) == 0)
{
printf("平局--\n");
break;
}
}
}
3.初始化棋盘
从此处开始,具体每一局游戏逻辑的函数定义均写在game.c文件中。
每局游戏开始,先将棋盘中元素全部初始化为空格
void InitBoard(char board[][COL], int row, int col)
{
int i, j;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
board[i][j] = ' ';
}
}
}
4.展示棋盘
用于每次落子后的棋盘视觉化,为了美观而有所装饰
void Display(char board[][COL], int row, int col)
{
system("cls");//每次清屏更加美观
int i, j;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
if(j<col-1)
{
printf(" %c |", board[i][j]);
}
else
{
printf(" %c \n", board[i][j]);
}
}
for (j = 0; j < col; j++)
{
if (i < row - 1)
{
printf("----");
}
}
printf("\n");
}
}
具体效果为:
5.玩家落子
这部分判定输入坐标是否有效很重要,注意不能取代掉已经用过的棋盘位置
void Player(char board[][COL], int row, int col)
{
while(1)
{
printf("请输入落子坐标(中间用空格):");
int x, y;
scanf("%d %d", &x, &y);
//输入有效的判定条件
if ((x <= row) && (x >= 1) && (y <= col) && (y >= 1) && (board[x-1][y-1] == ' '))
{
board[x - 1][y - 1] = '*';
break;
}
else
{
printf("输入错误,请重新输入:");
}
}
}
6.电脑落子
C语言生成随机数需要在main函数中设置随机数生成器,且只需设置一次
srand((unsigned)time(NULL));
然后就可直接调用rand()函数生成随机数(范围在0~32767)
void Computer(char board[ROW][COL], int row, int col)
{
while(1)
{
int x = rand() % row;
int y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
7.输赢判断
三子棋的规则下,任意行任意列或两条对角线落子相同即分出输赢
(结合代码注释)
int IsWin(char board[ROW][COL], int row, int col)
{
int i, j;
//行连线:
int flag = 0;
for (i = 0; i < row; i++)
{
flag = 0;//每行单独判断,每次都要赋0
for (j = 0; j < col-1; j++)
{
if ((board[i][j] == board[i][j + 1]) && (board[i][j]!=' '))
{
flag++;
}
}
if (flag == col - 1)
{
return 1;
}
}
//列连线:
flag = 0;
for (j = 0; j < col; j++)
{
flag = 0;//每列单独判断,每次都要赋0
for (i = 0; i < row - 1; i++)
{
if ((board[i][j] == board[i+1][j]) && (board[i][j] != ' '))
{
flag++;
}
}
if (flag == row - 1)
{
return 1;
}
}
//对角线:
flag = 0;
for (i = 0; i < row-1; i++)
{
for (j = 0; j < col - 1; j++)//最后一列,最后一行除外
{
if ((i == j) && (board[i][j] == board[i+1][j+1]) && (board[i][j] != ' '))
{
flag++;
}
}
}
if (flag == col - 1)
{
return 1;
}
flag = 0;
for (i = 0; i < row-1; i++)
{
for (j = col; j > 0; j--)//第一列,最后一行除外
{
if ((i + j == row-1) && (board[i][j] == board[i+1][j-1]) && (board[i][j] != ' '))
{
flag++;
}
}
}
if (flag == col - 1)
{
return 1;
}
//棋盘落满,平局:
flag = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
if (board[i][j] != ' ')
{
flag++;
}
}
}
if (flag == row * col)
{
return 0;
}
//游戏继续:
return -1;
}
三、N子棋拓展
在头文件game.h中,直接修改标识符常量ROW和COL即可,代码均适用于N子棋。
#pragma once
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <windows.h>
#define ROW 3
#define COL 3
void InitBoard(char board[ROW][COL], int row, int col);//初始化
void Display(char board[ROW][COL], int row, int col);//展示棋盘
void Player(char board[ROW][COL], int row, int col);//玩家下棋
void Computer(char board[ROW][COL], int row, int col);//电脑下棋
int IsWin(char board[ROW][COL], int row, int col);//判断输赢
总结
三子棋小游戏的语法并不复杂,其重点在于对游戏流程的细分和分支循环逻辑的理解,共勉。