系列文章目录
1.三子棋初级版
前言
本篇博客将带大家了解用C语言实现初级版的三子棋。
分别存放在:
test_sanziqi.c(大体的思路将在此文件中体现)
game_sanziqi.c(具体的步骤将在此文件中体现)
game_sanziqi.h(存放各种声明)
这三个文件中;
注意:
在下面的代码中,各个代码属于哪一个文件中小编都会在开头用注释标出;
因为是初级版,所以电脑输入不具有智能化,同时判断输赢的方式也不是那么地智能。
一、思路
要想实现一个如下图所示的三子棋小游戏,我们首先要学会肢解这个小游戏
通过分析,我们可以得出:
要实现这个三子棋小游戏,我们首先要对先将其的大体框架写出,然后再逐一突破:打印出它的棋盘(3*3形式的);对棋盘里的空格进行填充输入;判断输赢。
敲出大体框架--->打印棋盘---->键入”棋子“--->判断输赢
二、实现步骤
首先要打印一个菜单,让玩家知道各个数字分别代表何种意思
代码如下:
//test_sanziqi.c
//打印菜单
void menu()
{
printf("******************************\n");
printf("********** 1.play **********\n");
printf("********** 0.exit **********\n");
printf("******************************\n");
}
我们只需要在main函数对其引用即可;
再将大体的玩法敲出
代码如下:
//test_sanziqi.c
int main()
{
do
{
menu();//调用菜单,进行打印
printf("请选择:>\n");
scanf("%d", &input);
switch (input)
{
case 1:
printf("游戏开始\n");
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);//此处是while(input)的原因是:
//当input不为0时,可以继续进行循环,即继续游戏;
//而只有键入的input为0时才会跳出游戏,直接game over
return 0;
}
运行以后选择1便可以打印出一个菜单(如图):
选择进入游戏以后则可以进入游戏
所以需要一个函数来实现游戏(均在test_sanziqi.c文件中)
所以便可以在这个函数中引用各种可以满足需求的函数
2.打印棋盘
由开头的图片我们不难想到:通过二维数组的方式来实现棋盘的输出;但是,我们又该如何实现未“下棋”状态下的空格的打印呢?
于是乎,我们可以通过定义一个3*3的二维数组并将其初始化为空格用以实现空格的打印
代码如下:
//test_sanziqi.c
//游戏需要一个棋盘(3*3)
//定义一个棋盘3*3
char board[ROW][COL] = { 0 };//使用char类型是因为:电脑下棋打印为'#',而玩家下棋打印为'*'
//对棋盘初始化
init_board(board, ROW, COL);//将其初识化为空格,可以占一个位置,如果初始化为0,根本不打印
//game_sanziqi.c
#define ROW 3//这种更加方便修改
#define COL 3//这种写法在改变了ROW和COL的同时不用修改其他,棋盘也能随之改变,更加省心
//棋盘初始化
void init_board(char board[ROW][COL], int row, int col);
//打印棋盘
void display_board(char board[ROW][COL], int row, int col);
//game_sanziqi.c
//对棋盘初始化
void init_board(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] = ' ';
}
}
}
//game_sanziqi.c
//打印棋盘
void display_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");
}
}
打印棋盘原理:
经过分析以后,我们可以将棋盘分为红色部分和灰色部分;
打印红色部分:结合灰色部分我们可以推出一个落棋的空格应该是由三个小空格组成的而我们落子应该是落在中间那个小空格里的,所以我们可以通过下图所示办法打印:
将空格打印完了以后,我们便要思考如何将|打印出。分析上图可知|只在两个大空格之间,故可以写出如下代码:
因为红色框框和灰色框框所框部分是相连的
所以我们应当在同一个for循环里面接着打印红色框框的部分接着打印灰色部分
打印灰色部分:仿照打印红色部分的方法可以写出如下代码:
以上便是打印棋盘的全部过程;
3.键入“棋子”
棋盘准备就绪,我们就该开始玩游戏了:
代码如下:
//game_sanziqi.c
//玩游戏
//玩家玩
void player_move(char board[ROW][COL],int row,int col);
//电脑玩
#include <stdlib.h>
#include <time.h>
void computer_move(char board[ROW][COL],int row,int col);
//test_sanziqi.c
//玩游戏
while (1)
{
//玩家玩
player_move(board, ROW, COL);
display_board(board, ROW, COL);
//电脑玩
computer_move(board, ROW, COL);
display_board(board, ROW, COL);//调用棋盘,更加直观
}
//main函数中还要有如下操作来实现电脑随机下棋(通过时间轴)
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();//调用菜单,进行打印
//game_sanziqi.c
//玩游戏
//玩家下棋
void player_move(char board[ROW][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] == ' ')//判断坐标是否已经被占用;
//此处x、y都要减一
//因为按照我们的习惯,
//并不会将棋盘上最开始的那个落棋处输作(0,0),
//通常都是输作(1,1)
{
board[x - 1][y - 1] = '*';
break;
}
else//此处的这些else所打印的是起提示作用,提示犯了何种错误导致程序无法继续,
//并结合此提示做出修改(即,下次键入更加合适的坐标)
printf("该坐标已被占用,请重新输入\n");
}
else
printf("坐标非法,请重新输入\n");
}
}
//电脑下棋
void 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;
}//这里不像玩家下棋一般写else,是因为电脑是随机下棋的,
//如果这次得到的坐标不合适,直接进入循环,找下一个坐标即可,不用提示错在何处
}
}
键入“棋子”原理:
在本篇博客中,我们是按照玩家先落子的顺序来玩游戏的;
在game_sanziqi.c文件中,我们先实现玩家下棋,再实现电脑下棋
玩家下棋(*):将scanf放在while循环中可以确保多次输入;同时,落子的时候我们要确保落子的位置合法(即在棋盘上),以及落子的位置没有被占用(即之前没有落过子);当我们键入的坐标犯了上面提及的这两种错误,我们可以进行打印所犯的错误,用以提示玩家。
电脑下棋(#):因为是初级版,所以此处电脑下棋单纯就是随机的,毫无智能可言,故我们可以想到通过时间轴来实现随机落子。
输出如下:
4.判断输赢
经过分析,我们可以得出三子棋小游戏无非就四种结果:玩家赢、电脑赢、平局、继续
所以我们可以写一个带有返回值的函数来判断游戏的状态:
玩家赢--->返回’*‘
电脑赢--->返回’#‘
平局--->返回’Q‘
继续--->返回’C'
代码如下:
//test_sanziqi.c
//玩游戏
int ret = 0;
while (1)
{
//玩家玩
player_move(board, ROW, COL);
display_board(board, ROW, COL);
//判断游戏状态
ret = is_win(board, ROW, COL);
if (ret != 'C')
break;//break直接跳出循环,进行外面的if判断
//电脑玩
computer_move(board, ROW, COL);
display_board(board, ROW, COL);//调用棋盘,更加直观
//判断游戏状态
ret = is_win(board, ROW, COL);//直观看到以后再进行判断输出
if (ret != 'C')
break;//break直接跳出循环,进行外面的if判断
}
if (ret == '*')
printf("玩家赢\n");
if (ret == '#')
printf("电脑赢\n");
else if (ret == 'Q')//这里一定要写到if(ret=='Q'),不能省略
//假如省略了,这里的else就会和if(ret=='#')相匹配
//而当进入第一个if以后,如果第二个if不满足的时候,而恰好玩家赢了,这种情况输出玩家赢得同时还会输出平局
printf("平局\n");
display_board(board, ROW, COL);//最后打印棋盘,更加直观
}
//game_sanziqi.h
//判断游戏状态
//玩家赢--返回*
//电脑赢--返回#
//平局--返回Q
//游戏继续--返回C
char is_win(char board[ROW][COL], int row, int col);
//game_sanziqi.c
//如果是平局,返回1;否则返回0(此函数是用来确定棋盘是否已满)
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;//注意这里不能写成if(board[i][j]!=' ') return 1;然后其他的情况返回0;
//因为这样写的话,会出现:
//当有一个board[i][j]!=' '时,就返回1,
//然后就会在下方判断平局得地方返回'Q',
//即,电脑会判为平局(尽管你可能只输了一个坐标)
}
}
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 < row; 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];
//判断平局
if (is_full(board, ROW, COL) ==1)
return 'Q';
return 'C';
}
原理:
无论是玩家赢还是电脑赢,都要三点成线才可以,而三点成线的情况一共有三种(行成线、列成线、对角线成线)、所以对是否出现这三种情况进行判断即可;
倘若出现了上述的三种情况之一的任何一种,我们便可以将对应的成线的三个棋子的任何一个的坐标输出,即不是‘#’就是‘*’。
最后,将各个文件的代码汇总如下:
game_sanziqi.h
//game_sanziqi.h
#include <stdio.h>
#define ROW 3//这种形式更加方便修改
#define COL 3//这种写法在改变了ROW和COL的同时不用修改其他,棋盘也能随之改变,更加省心
//棋盘初始化
void init_board(char board[ROW][COL], int row, int col);
//打印棋盘
void display_board(char board[ROW][COL], int row, int col);
//玩游戏
//玩家玩
void player_move(char board[ROW][COL], int row, int col);
//电脑玩
#include <stdlib.h>
#include <time.h>
void computer_move(char board[ROW][COL], int row, int col);
//判断游戏状态
//玩家赢--返回*
//电脑赢--返回#
//平局--返回Q
//游戏继续--返回C
char is_win(char board[ROW][COL], int row, int col);
game_sanziqi.c
//game_sanziqi.c
#include "game.h"
//对棋盘初始化
void init_board(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 display_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]);//注意%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[ROW][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] == ' ')//判断坐标是否已经被占用;
//此处x、y都要减一
//是因为按照我们的习惯
//并不会将棋盘上最开始的那个落棋处输作(0,0)
//通常都是输作(1,1)
{
board[x - 1][y - 1] = '*';
break;
else//此处的这些else所打印的是起提示作用,提示犯了何种错误导致程序无法继续,
//并结合此提示做出修改(即,下次键入更加合适的坐标)
}
printf("该坐标已被占用,请重新输入\n");
}
else
printf("坐标非法,请重新输入\n");
}
}
//电脑下棋
void 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;//这里不像玩家下棋一般写else是因为电脑是随机下棋的,
//如果这次得到的坐标不合适,直接进入循环,找下一个坐标即可,不用提示错在何处
}
}
}
//如果是平局,返回1;否则返回0
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;//注意这里不能写成if(board[i][j]!=' ') return 1;
//然后其他的情况返回0;
//因为这样写的话,会出现:
//当有一个board[i][j]!=' '时,就返回1,
//然后就会在下方判断平局得地方返回'Q',
//即,电脑会判为平局(尽管你可能只输了一个坐标)
}
}
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 < row; 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];
//判断平局
if (is_full(board, ROW, COL) == 1)
return 'Q';
return 'C';
}
test_sanziqi.c
//test_sanziqi.c
#include "game.h"
//打印菜单
void menu()
{
printf("******************************\n");
printf("********** 1.play **********\n");
printf("********** 0.exit **********\n");
printf("******************************\n");
}
//开始游戏
void game()
{
//游戏需要一个棋盘(3*3)
//定义一个棋盘3*3
char board[ROW][COL] = { 0 };//使用char类型是因为:
//电脑下棋打印为'#',玩家下棋打印为'*'
//对棋盘初始化
init_board(board, ROW, COL);//将其初识化为空格,可以占一个位置,如果初始化为0,根本不打印
//打印棋盘
display_board(board, ROW, COL);
//玩游戏
int ret = 0;
while (1)
{
//玩家玩
player_move(board, ROW, COL);
display_board(board, ROW, COL);
//判断游戏状态
ret = is_win(board, ROW, COL);
if (ret != 'C')
break;
//电脑玩
computer_move(board, ROW, COL);
display_board(board, ROW, COL);//调用棋盘,更加直观
//判断游戏状态
ret = is_win(board, ROW, COL);//直观看到以后再进行判断输出
if (ret != 'C')
break;
}
if (ret == '*')
printf("玩家赢\n");
if (ret == '#')
printf("电脑赢\n");
else if (ret == 'Q')//这里一定要写到if(ret=='Q'),不能省略
//假如省略了,这里的else就会和if(ret=='#')相匹配
//而当进入第一个if以后,
//如果第二个if不满足的时候,而恰好玩家赢了,
//这种情况输出玩家赢得同时还会输出平局
printf("平局\n");
display_board(board, ROW, COL);//最后打印棋盘,更加直观
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu(); //调用菜单,进行打印
printf("请选择:>\n");
scanf("%d", &input);
switch (input)
{
case 1:
printf("游戏开始\n");
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);//此处是while(input)的原因是:
//当input不为0时,可以继续进行循环,即继续游戏;
//而只有键入的input为0时才会跳出游戏,直接game over
return 0;
}
总结
希望此篇博客对你有所帮助,感谢阅读。