手把手教学三子棋
经过先前学习的 数组+ 函数的知识,我们可以完成三子棋小游戏了,再写三子棋之前,我们首先简单了解C语言中的一个 关键字 —— define。
一.define关键字
#include<stdio.h>
#define a 10
int main()
{
printf("a=%d", a);
return 0;
}
可以看到输出 a=3 那么define对于写三子棋有什么好处呢!通过define可以将三子棋扩展到五子棋,甚至更多。
二.设计三子棋的思路
- 1.创建菜单【0:退出游戏,1:开始游戏】
- 2.初始化棋盘中的棋子
- 3.打印棋盘+棋子
- 4.玩家下棋(字符*),并打印棋盘
- 5.判断玩家是否赢(字符*代表玩家赢)
- 6.电脑下棋(字符#),并打印棋盘
- 7.判断电脑是否赢(字符#代表电脑赢)
- 8.若一轮没有赢家,游戏继续(字符C代表游戏继续)
- 9.当棋盘下满时,游戏平局(字符Q代表游戏平局)
- 10.循环以上步骤,直到游戏结束为止
三.三子棋代码的函数实现
1.创建菜单【0:退出游戏,1:开始游戏】
void PrintMenu()//打印菜单函数
{
printf("*********************************\n");
printf("**********0.exit 1.play**********\n");
printf("*********************************\n");
}
运行效果:
2.初始化棋盘中的棋子
- 游戏开始时,棋盘中并无棋子,且棋子用字符代替,所以初始化棋子为空格。
void InitBoard(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] = ' ';//先初始化为空格
}
}
}
3.打印棋盘+棋子
- 由于棋子初始化为空格,看不出开,但不代表棋子不存在。
#define ROW 3
#define COL 3
void PrintBoard(char board[ROW][COL], int row, int col)//定义打印棋盘+棋子的函数
{
int i = 0;
int j = 0;
for (i = 0;i < row;i++)//循环row次
{
for (j = 0;j < col;j++)//先打印第一行
{
printf(" %c ", board[i][j]);//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");
}
}
运行效果:
若将最上面的两行代码改为 #define ROW 10 #define COL 10 会发生什么呢?
没错,变成了10*10的棋盘,有兴趣的话可以将其改为五子棋。
4.玩家下棋(字符*),并打印棋盘
- 站在玩家的角度,输入1表示第一行或第一列,但是数组下标是从0开始的,所以打印棋子时要将玩家输入的值减1。
- 玩家落子要在棋盘的范围之内,并且若该处有棋子,则不能下到该处。
- 玩家下完棋后,要判断游戏局势——输,赢,平局,继续。
void PlayerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
while (1)
{
printf("玩家下棋\n");
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");
}
}
}
PrintBoard(board, ROW, COL);//打印棋盘
运行效果:
5.电脑下棋(字符#),并打印棋盘
- 电脑下棋有优先级
- 优先级1:若每行3个坐标,任意2个坐标相同且不是空格,则下第3个坐标。若每列3个坐标,任意2个坐标相同且不是空格,则下第3个坐标。若每个对角线3个坐标,任意2个坐标相同且不是空格,则下第3个坐标。一方面可以堵住玩家,另一方面电脑可以获得游戏胜利。
- 优先级2:若中心没有棋,则电脑毫不犹豫的下中心,电脑可以占据优势。
- 优先级3:若玩家先下棋(且下中心),则电脑必须下4个角落,否则电脑必输。
- 优先级4:利用【rand()%3+1】随便下,但要设置随机种子srand((unsigned int)time(NULL))。
- 电脑下完棋后,要判断游戏局势——输,赢,平局,继续。
void ComputerMove(char board[ROW][COL], int row, int col)
{
printf("电脑下棋\n");
while (1)
{
//优先级1:
//若每行3个坐标,任意2个坐标相同且不是空格,则下第3个坐标
//一方面可以堵住玩家,另一方面电脑可以获得游戏胜利
int i = 0;
int count = 0;//若count==1,则跳出while循环
for (i = 0;i < row;i++)
{
if (board[i][0] == board[i][1] && board[i][0] != ' ')
{
if (board[i][2] == ' ')//满足该坐标为空
{
board[i][2] = '#';
count++;
break;//只能跳出for循环
}
}
if (board[i][1] == board[i][2] && board[i][1] != ' ')
{
if (board[i][0] == ' ')
{
board[i][0] = '#';
count++;
break;
}
}
if (board[i][0] == board[i][2] && board[i][0] != ' ')
{
if (board[i][1] == ' ')
{
board[i][1] = '#';
count++;
break;
}
}
}
if (count == 1)
{
break;//若count==1,则跳出while循环
}
//若每列3个坐标,任意2个坐标相同且不是空格,则下第3个坐标
//一方面可以堵住玩家,另一方面电脑可以获得游戏胜利
int j = 0;
for (j = 0;j < col;j++)
{
if (board[0][j] == board[1][j] && board[0][j] != ' ')
{
if (board[2][j] == ' ')//满足该坐标为空
{
board[2][j] = '#';
count++;
break;//只能跳出for循环
}
}
if (board[1][j] == board[2][j] && board[1][j] != ' ')
{
if (board[0][j] == ' ')
{
board[0][j] = '#';
count++;
break;
}
}
if (board[0][j] == board[2][j] && board[0][j] != ' ')
{
if (board[1][j] == ' ')
{
board[1][j] = '#';
count++;
break;
}
}
}
if (count == 1)
{
break;//若count==1,则跳出while循环
}
//若每个对角线3个坐标,任意2个坐标相同且不是空格,则下第3个坐标
//一方面可以堵住玩家,另一方面电脑可以获得游戏胜利
//1.反斜杠 \
if (board[0][0] == board[1][1] && board[0][0] != ' ')
{
if (board[2][2] == ' ')//满足该坐标为空
{
board[2][2] = '#';
break;
}
}
if (board[1][1] == board[2][2] && board[1][1] != ' ')
{
if (board[0][0] == ' ')
{
board[0][0] = '#';
break;
}
}
if (board[0][0] == board[2][2] && board[0][0] != ' ')
{
if (board[1][1] == ' ')
{
board[1][1] = '#';
break;
}
}
//2.正斜杆 /
if (board[0][2] == board[1][1] && board[0][2] != ' ')
{
if (board[2][0] == ' ')//满足该坐标为空
{
board[2][0] = '#';
break;
}
}
if (board[1][1] == board[2][0] && board[1][1] != ' ')
{
if (board[0][2] == ' ')
{
board[0][2] = '#';
break;
}
}
if (board[0][2] == board[2][0] && board[0][2] != ' ')
{
if (board[1][1] == ' ')
{
board[1][1] = '#';
break;
}
}
//优先级2:
//若中心没有棋,则电脑毫不犹豫的下中心,电脑可以占据优势
if (board[1][1] == ' ')
{
board[1][1] = '#';
break;
}
//优先级3:
//若玩家先下棋(且聪明),则电脑必须下4个角落,否则电脑必输
if (board[0][0] == ' ')
{
board[0][0] = '#';
break;
}
if (board[0][2] == ' ')
{
board[0][2] = '#';
break;
}
if (board[2][0] == ' ')
{
board[2][0] = '#';
break;
}
if (board[2][2] == ' ')
{
board[2][2] = '#';
break;
}
//优先级4:
//随便下
int x = rand() % 3 + 1;
int y = rand() % 3 + 1;
if (board[x - 1][y - 1] == ' ')
{
board[x - 1][y - 1] = '#';
break;
}
}
}
PrintBoard(board, ROW, COL);//打印棋盘
6.判断棋盘是否已满
- 判断棋盘是否满了的函数,棋盘满了 返回1 - 平局,没满 返回0 - 继续。
//定义判断棋盘是否满了的函数
//满了 返回1 - 平局
//没满 返回0 - 继续
int IsFull(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++)
{
if (board[i][j] == ' ')
{
return 0;
}
}
}
return 1;
}
7.判断游戏结果
- 判断输赢的函数
- 玩家赢返回 —— *
- 电脑赢返回 —— #
- 平局返回 —— Q
- 继续返回 —— C
char IsWin(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][1] != ' ')
{
return board[i][1];//玩家赢返回* 电脑赢返回#
}
}
//判断列
int j = 0;
for (j = 0;j < col;j++)
{
if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[1][j] != ' ')
{
return board[1][j];//玩家赢返回* 电脑赢返回#
}
}
//判断对角线
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 (IsFull(board, row, col) == 1)
{
return 'Q';
}
//没有人赢,且没有平局,则游戏继续
return 'C';
}
四.三子棋总代码
1.头文件game.h
- 头文件game.h用于存放函数的声明。
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 3
#define COL 3
//声明打印菜单的函数
void PrintMenu();
//声明初始化棋盘元素的函数
void InitBoard(char board[ROW][COL], int row, int col);
//声明打印棋盘的函数
void PrintBoard(char board[ROW][COL], int row, int col);
//声明玩家下棋的函数
void PlayerMove(char board[ROW][COL], int row, int col);
//声明电脑下棋的函数
//找没有下棋的地方随机下棋
void ComputerMove(char board[ROW][COL], int row, int col);
//声明判断棋盘是否满了的函数
//满了返回1 - 平局
//没满返回0
int IsFull(char board[ROW][COL], int row, int col);
//声明判断输赢的函数
//玩家赢 - '*'
//电脑赢 - '#'
//平局 - 'Q'
//继续 - 'C'
char IsWin(char board[ROW][COL], int row, int col);
2.源文件game.c
- 源文件game.c用于存放函数的定义。
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
//定义菜单函数
void PrintMenu()
{
printf("*********************************\n");
printf("**********0.exit 1.play**********\n");
printf("*********************************\n");
}
//定义初始化棋盘元素函数
void InitBoard(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 PrintBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
//循环row次
for (i = 0;i < row;i++)
{
//先打印第一行
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 PlayerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
while (1)
{
printf("玩家下棋\n");
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 ComputerMove(char board[ROW][COL], int row, int col)
{
printf("电脑下棋\n");
while (1)
{
//优先级1:
//若每行3个坐标,任意2个坐标相同,则下第3个坐标
//一方面可以堵住玩家,另一方面电脑可以获得游戏胜利
int i = 0;
int count = 0;//若count==1,则跳出while循环
for (i = 0;i < row;i++)
{
if (board[i][0] == board[i][1] && board[i][0] != ' ')
{
if (board[i][2] == ' ')//满足该坐标为空
{
board[i][2] = '#';
count++;
break;//只能跳出for循环
}
}
if(board[i][1] == board[i][2] && board[i][1] != ' ')
{
if (board[i][0] == ' ')
{
board[i][0] = '#';
count++;
break;
}
}
if(board[i][0] == board[i][2] && board[i][0] != ' ')
{
if (board[i][1] == ' ')
{
board[i][1] = '#';
count++;
break;
}
}
}
if (count == 1)
{
break;//若count==1,则跳出while循环
}
//若每列3个坐标,任意2个坐标相同,则下第3个坐标
//一方面可以堵住玩家,另一方面电脑可以获得游戏胜利
int j = 0;
for (j = 0;j < col;j++)
{
if (board[0][j] == board[1][j] && board[0][j] != ' ')
{
if (board[2][j] == ' ')//满足该坐标为空
{
board[2][j] = '#';
count++;
break;//只能跳出for循环
}
}
if (board[1][j] == board[2][j] && board[1][j] != ' ')
{
if (board[0][j] == ' ')
{
board[0][j] = '#';
count++;
break;
}
}
if (board[0][j] == board[2][j] && board[0][j] != ' ')
{
if (board[1][j] == ' ')
{
board[1][j] = '#';
count++;
break;
}
}
}
if (count == 1)
{
break;//若count==1,则跳出while循环
}
//若每个对角线3个坐标,任意2个坐标相同,则下第3个坐标
//一方面可以堵住玩家,另一方面电脑可以获得游戏胜利
//1.反斜杠 \
if (board[0][0] == board[1][1] && board[0][0] != ' ')
{
if (board[2][2] == ' ')//满足该坐标为空
{
board[2][2] = '#';
break;
}
}
if (board[1][1] == board[2][2] && board[1][1] != ' ')
{
if (board[0][0] == ' ')
{
board[0][0] = '#';
break;
}
}
if (board[0][0] == board[2][2] && board[0][0] != ' ')
{
if (board[1][1] == ' ')
{
board[1][1] = '#';
break;
}
}
//2.正斜杆 /
if (board[0][2] == board[1][1] && board[0][2] != ' ')
{
if (board[2][0] == ' ')//满足该坐标为空
{
board[2][0] = '#';
break;
}
}
if (board[1][1] == board[2][0] && board[1][1] != ' ')
{
if (board[0][2] == ' ')
{
board[0][2] = '#';
break;
}
}
if (board[0][2] == board[2][0] && board[0][2] != ' ')
{
if (board[1][1] == ' ')
{
board[1][1] = '#';
break;
}
}
//优先级2:
//若中心没有棋,则电脑毫不犹豫的下中心,电脑可以占据优势
if (board[1][1] == ' ')
{
board[1][1] = '#';
break;
}
//优先级3:
//若玩家先下棋(且聪明),则电脑必须下4个角落,否则电脑必输
if (board[0][0] == ' ')
{
board[0][0] = '#';
break;
}
if (board[0][2] == ' ')
{
board[0][2] = '#';
break;
}
if (board[2][0] == ' ')
{
board[2][0] = '#';
break;
}
if (board[2][2] == ' ')
{
board[2][2] = '#';
break;
}
//优先级4:
//随便下
int x = rand() % 3 + 1;
int y = rand() % 3 + 1;
if (board[x - 1][y - 1] == ' ')
{
board[x - 1][y - 1] = '#';
break;
}
}
}
//定义判断棋盘是否满了的函数
//满了 返回1 - 平局
//没满 返回0
int IsFull(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++)
{
if (board[i][j] == ' ')
{
return 0;
}
}
}
return 1;
}
//定义判断输赢的函数
char IsWin(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][1] != ' ')
{
return board[i][1];//玩家赢返回* 电脑赢返回#
}
}
//判断列
int j = 0;
for (j = 0;j < col;j++)
{
if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[1][j] != ' ')
{
return board[1][j];//玩家赢返回* 电脑赢返回#
}
}
//判断对角线
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 (IsFull(board, row, col) == 1)
{
return 'Q';
}
//没有人赢,且没有平局,则游戏继续
return 'C';
}
3.源文件test.c
- 源文件test.c用于存放进行三子棋游戏的函数和主函数。
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void game()
{
char ret = 0;//用于接收 IsWin函数 的返回值
char board[ROW][COL] = { 0 };
InitBoard(board, ROW, COL);//初始化棋盘元素
PrintBoard(board, ROW, COL);//打印棋盘
while (1)
{
PlayerMove(board, ROW, COL);//玩家下棋(*)
PrintBoard(board, ROW, COL);//打印棋盘
//判断输赢
ret = IsWin(board, ROW, COL);
if (ret != 'C')
{
break;
}
ComputerMove(board, ROW, COL);//电脑下棋(#)
PrintBoard(board, ROW, COL);//打印棋盘
//判断输赢
ret = IsWin(board, ROW, COL);
if (ret != 'C')
{
break;
}
}
if (ret == '*')
{
printf("玩家赢!\n");
}
else if (ret == '#')
{
printf("电脑赢!\n");
}
else
{
printf("平局\n");
}
}
int main()
{
srand((unsigned int)time(NULL));
int input = 0;
do
{
PrintMenu();
printf("请输入(0/1):");
scanf("%d", &input);
switch (input)
{
case 0:
printf("退出游戏!\n");
break;
case 1:
printf("游戏开始!\n");
game();
break;
default:
printf("输入错误,请重新输入(0/1):\n");
break;
}
} while (input);
return 0;
}
运行之后你会发现,不尝试几次,很难赢的了电脑,当然是可以赢的。
有了三子棋的基础,实现五子棋是不是瞬间有了方向呢!相信你一定可以做到的。