这是我学习C语言来制作的第一个小游戏--三子棋,也叫井字棋。三子棋是我们小时候经常玩的一个游戏,上课的时候经常和同桌不听讲就撕张纸玩玩五子棋,到后来能撕的纸就没那么多了,就开始玩玩三子棋,两横两竖就把棋盘给画好了。
那么我现在就来说说三子棋的游戏规则,首先两个人猜拳决定谁先下棋,你下一次,然后我下一次,当谁的水平方向、竖直方向或者斜线方向连成了3个棋子,谁就赢了,如果棋盘中棋子已经放满了,但是还没有决出胜负,那么就是平局。
接着我们来捋一捋思路:
1.自定义模块
制作一个游戏,我们需要对游戏进行一个自定义模块,那么自然创建一个头文件(game.h)包含各种游戏步骤的命名,一个源文件(game.c)的游戏步骤定义,以及一个测试源文件(test.c);
解决方案资源管理器
2.游戏菜单
首先得有一个游戏菜单(menu)让玩家进行是否游戏选择,玩一次当然不过瘾,所以我们在main函数中用do while循环进行一个游戏界面循环,游戏结束后再次选择是否进行游戏;
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
scanf("%d", &input);
switch (input)
{
case 1:
//选择1,游戏开始
game();
break;
case 0:
//退出
printf("exit\n");
break;
default:
//重新选择
printf("please choose again!\n");
break;
}
} while (input != 0);
return 0;
}
test.c
3. 游戏的思路
接下来就是得把main函数中的(menu())和(game())给解决了,在屏幕上输出一个简易菜单不难吧;我们再来把游戏的内容进行一个编辑;这个游戏的思路:(1)先在屏幕上输出一个33的棋盘,那么棋盘里面要放入玩家和电脑下的棋,所以我们创建了一个二维字符数组board[ROW][COL]并初始化,这里的ROW和COL应在game.h中define,以便于以后需要修改不同的游戏类型;(2)接下来就是游戏的一个过程,这里我把玩家下的棋记录为‘’,电脑下的棋记录为‘#’,首先是玩家下棋,然后是电脑下棋;
4. 判断游戏的状态
并且每一次下完棋都要判断游戏的一个状态,那么我将游戏状态(ret)分为了4种返回值:(1)玩家胜‘*’;(2)电脑胜‘#’;(3)平局‘-’;(4)游戏继续‘C’;
//test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu()
{
printf("______________________________________\n");
printf(" %d子棋 \n",price);
printf(" 1.play \n");
printf(" 0.exit \n");
printf("______________________________________\n");
printf("plaese choose:>");
}
void game()
{
//创建棋盘(二维数组)
char board[ROWS][COLS]; //转定义
//初始化棋盘
InitBoard(board, ROWS, COLS,' ');
//打印棋盘
DisplayBoard(board, ROWS, COLS);
//开始游戏并判断游戏进程
char ret = 0;
while (1)
{
//玩家下棋
PlayerMove(board, ROWS, COLS, '*');
DisplayBoard(board, ROWS, COLS);
//判断输赢
ret = Iswin(board, ROWS, COLS,'*','#');
if (ret != 'c')
{
break;
}
//电脑下棋
ComputerMove(board, ROWS, COLS, '#');
DisplayBoard(board, ROWS, COLS);
//判断输赢
ret = Iswin(board, ROWS, COLS,'*','#');
if (ret != 'c')
{
break;
}
}
if (ret == '*')
{
printf("you win!\n");
}
else if (ret == '#')
{
printf("you lost!\n");
}
else if (ret == '-')
{
printf("---draw---\n");
}
DisplayBoard(board, ROWS, COLS);
}
游戏进行的每一步都尽量模块化 - test.c
步骤进行定义和算法
现在大致的游戏步骤已经讲完了,接下来是给各种步骤进行定义和算法;在test.c里我初始化棋盘(InitBoard)、打印棋盘(DisplayBoard)、玩家下棋(PlayerMove)、电脑下棋(ComputerMove)、判断输赢(IsWin)都需要我们来进行定义及解释,所以我们要在test.c里包含“game.h”头文件,并在“game.h”里放入刚刚命名的步骤及类型;
test.c
//game.h
#pragma once
#include<stdio.h>
#include<string.h>
#include<time.h>
#define ROWS 10
#define COLS 10
#define price 5
//初始化棋盘
char InitBoard(char board[ROWS][COLS], int rows, int cols,char set);
//打印棋盘
void DisplayBoard(char board[ROWS][COLS],int rows,int cols);
//玩家下棋
void PlayerMove(char board[ROWS][COLS], int rows, int cols,char set1);
//电脑下棋
void ComputerMove(char board[ROWS][COLS], int rows, int cols, char set2);
//判断输赢
char Iswin(char board[ROWS][COLS], int rows, int cols,char set1,char set2);
game.h
"game.h"头文件及自定义函数
- 在“game.h”文件中包含所有要用到的头文件及自定义函数;接下来就是对自定义函数的自定义;
"game.c"函数实现
- 对"game.h"的定义在“game.c”中实现,所以“game.c”中同样要包含头文件“game.h”
初始化
- 初始化棋盘(IninBoard) - 这个就不多说了,把二维数组的每个元素都用' '代替;
//初始化棋盘
char InitBoard(char board[ROWS][COLS], int rows, int cols,char set)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
game.c
打印棋盘
- 打印棋盘(DisplayBoard) - 这里值得注意的是我们可以将棋盘分成两个大部分:打印一行数组,再打印一行分隔,再将这两个大部分细分一下就好做了;
//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int rows, int cols)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
printf(" %c ", board[i][j]);
if (j < cols - 1)
{
printf("|");
}
}
printf("\n");
if (i < rows - 1)
{
for (j = 0; j < cols; j++)
{
printf("---");
if (j < cols - 1)
{
printf("|");
}
}
printf("\n");
}
}
}
game.c
玩家下棋
- 玩家下棋(PlayerMove) - 提示玩家下棋,玩家输入的是行和列的数字,首先要判断玩家输入的数字是否合法(在board[ROW][COL]范围内),如果合法 - 判断该位置是否被占用,没有占用就执行下棋然后该步骤结束,如果被占用了则进行提示和重新输入下棋位置并对该过程进行循环;如果不合法 - 提示和重新输入下棋位置并对该过程进行循环;代码实现如下:
//玩家下棋 -> '*'
void PlayerMove(char board[ROWS][COLS], int rows, int cols,char set1)
{
//玩家输入需要下棋坐标
int x, y;
printf("请输入你要下的棋:>");
while (1)
{
scanf("%d %d", &x, &y);
//判断该坐标是否合法
if (x >= 1 && x <= rows && y >= 1 && y <= cols && board[x-1][y-1] == ' ')
{
board[x-1][y-1] = set1;
break;
}
else
{
printf("该坐标不合法或已被占用,请重新输入:>");
}
}
}
game.c
电脑下棋
- 电脑下棋(ComputerMove) - 电脑下棋就没有那么复杂了,我们每次随机生成两个0 - 2的整数,main函数中设定随机值:srand((unsigned int)rand(time(NULL)));生成的数x,y放入board[x][y]判断该位置是否被占用,若是被占用则循环该过程,没被占用则下棋后跳出步骤;
//电脑下棋
void ComputerMove(char board[ROWS][COLS], int rows, int cols, char set2)
{
printf("电脑下棋:\n");
while (1)
{
int x = rand() % rows;
int y = rand() % cols;
if (board[x][y] == ' ')
{
board[x][y] = set2;
break;
}
}
}
game.c
判定游戏输赢
最后就是判断游戏过程(IsWin) - 有横向,纵向和斜向三种判断,分别写出判断算法,值得注意的是玩家和电脑下棋后都要进行判定输赢或继续。
//判断输赢
char Iswin(char board[ROWS][COLS], int rows, int cols,char set1,char set2)
{
//win条件:一排、一列、斜着连成一串
int x = 0;
int y = 0;
int count1 = 0; //玩家计数
int count2 = 0; //电脑计数
//横向判断
for (x = 0; x < rows; x++)
{
count1 = 0; //玩家计数
count2 = 0; //电脑计数
for (y = 0; y < cols; y++)
{
if (board[x][y] == set1)
{
count1++;
if (count1 == price)
{
return set1;
}
}
else if (board[x][y] == set2)
{
count2++;
if (count2 == price)
{
return set2;
}
}
}
}
//纵向判断
for (y = 0; y < cols; y++)
{
count1 = 0; //玩家计数
count2 = 0; //电脑计数
for (x = 0; x < rows; x++)
{
if (board[x][y] == set1)
{
count1++;
if (count1 == price)
{
return set1;
}
}
else if (board[x][y] == set2)
{
count2++;
if (count2 == price)
{
return set2;
}
}
}
}
count1 = 0; //玩家计数
count2 = 0; //电脑计数
//斜向1判断
for (x = 0, y = 0;x < rows && y < cols; x++, y++)
{
if (board[x][y] == set1)
{
count1++;
if (count1 == price)
{
return set1;
}
}
else if (board[x][y] == set2)
{
count2++;
if (count2 == price)
{
return set2;
}
}
else
{
count1 = 0;
count2 = 0;
}
}
count1 = 0; //玩家计数
count2 = 0; //电脑计数
//斜向2判断
for (x = 0, y = cols - 1; y >= 0; x++, y--)
{
if (board[x][y] == set1)
{
count1++;
if (count1 == price)
{
return set1;
}
}
else if (board[x][y] == set2)
{
count2++;
if (count2 == price)
{
return set2;
}
}
else
{
count1 = 0;
count2 = 0;
}
}
for (x = 0; x < rows; x++)
{
for (y = 0; y < cols; y++)
{
if (board[x][y] == ' ')
{
return 'c';
}
}
}
return '-';
}
那么在最后,我们是否还能让电脑下棋的时候能有一个很智能的下棋方法呢,因为每个随机下棋并不能给玩家带来好的游戏体验。能力有限,希望有大神指教
以上就是我制作三子棋的一个思路和操作过程,如有不正,请多指教!