1.三子棋怎么玩
三子棋在生活中是很常见的游戏,其游戏规则也很简单,即双方在棋盘上轮流下棋,当棋盘上某一方的棋子横向或竖向或对角线方向连成三个棋子连成一条线,就判定哪一方获胜,形如:
上图就判定为黑色棋子方获胜。
2.三子棋代码实现的思路
2.1棋盘怎么实现
通过上述分析,我们得知要想下三子棋,我们得有棋盘,而关于棋盘我们可以简化其为一个二维数组,然后将其连同分隔线打印出来,这样棋盘就有了。
2.2玩家以及电脑怎么落子
关于玩家下棋,我们将玩家所要下的棋子坐标输入,通过更改二维数组元素的信息,然后打印出来,即可完成下棋;关于电脑落子,通过生成大小在行列范围内的随机数,找到二维数组对应的位置改变其元素,然后打印出来完成下棋。
2.3怎么判断输赢
关于赢就是三种情况,横、竖、对角线某方棋子连成一条线,那么代码怎么写呢?我是这样思考的:
(1)横向,从横向的第一个和第二个数组元素开始,如果相等再判断第二个和第三个数组元素是否相等,以此类推,当发现某处相邻元素不相等时,即判断该行不存在赢的情况;当不存在某处相邻元素不相等,且此时数组元素不为初始化时数组的元素,就判定存在某一方赢了;
(2)纵向,同横向逻辑相似,判断的是纵向相邻的两个元素,如果第一个和第二个数组元素相等,再判断第二个和第三个数组元素是否相等,以此类推,当发现某处相邻元素不相等时,即判断该列不存在赢的情况;反之,当不存在某处相邻元素不相等,且此时数组元素不为初始化时数组的元素,就判定存在某一方赢了;
(3)对角线,同样的思考逻辑,判断对角线方向相邻的两个元素,如果第一个和第二个数组元素相等,再判断第二个和第三个数组元素是否相等,以此类推,当发现某处相邻元素不相等时,即判断该对角线方向不存在赢的情况;反之,当不存在某处相邻元素不相等,且此时数组元素不为初始化时数组的元素,就判定存在某一方赢了;
当不存在哪一方赢时,剩下就是平局或者继续了。平局我们只需判断棋盘是否满了,当棋盘不满时,游戏即为继续,满了就是平局。
2.4三子棋游戏逻辑
综上所述,整个三子棋游戏的逻辑如下:
1.有存储棋子的二维数组;
2.打印棋盘;
3.玩家下棋;
4.判断输赢;
5.电脑下棋;
6.判断输赢;
7.某方赢了或者棋盘满了,停止下棋,结束游戏。
3.三子棋代码实现
首先贴上我的代码中关于函数的功能介绍:
#pragma once
//库函数头文件
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
//标识符常量
#define ROW 3//定义行
#define COL 3//定义列
#define NUM 3//定义游戏规则,几子连成线为赢
//自定义函数声明
//游戏菜单
void menu();
//初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col);
//打印棋盘
void Print(char board[ROW][COL], int row, int col);
//玩家下棋
void person(char board[ROW][COL], int row, int col);
//电脑下棋
void computer(char board[ROW][COL], int row, int col);
//判断输赢
char is_win(char board[ROW][COL], int row, int col);
//判断行相等
char equ_row(char board[ROW][COL], int i, int j, int *pc);
//判断列相等
char equ_col(char board[ROW][COL], int i, int j, int* pc);
//判断对角线相等
char diag(char board[ROW][COL], int i, int j, int* pc);
char dia(char board[ROW][COL], int i, int j, int* pc);
接着就是整个游戏的功能实现函数:
#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
//游戏菜单
void menu()
{
printf("**********************\n");
printf("****** 1.paly ********\n");
printf("****** 0.exit ********\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 Print(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++)
{
printf(" %c ", board[i][j]);
if (j < col - 1)
printf("|");
}
if (i < row - 1)
{
printf("\n");
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
printf("|");
}
}
printf("\n");
}
}
//玩家下棋,这里规定玩家下棋,棋子为'*'
void person(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
while (1)
{
printf("请玩家下棋-> ");
scanf("%d %d", &i, &j);
if (i >= 1 && i <= row && j >= 1 && j <= col)
{
if (' ' == board[i-1][j-1])
{
board[i-1][j-1] = '*';
break;
}
else
{
printf("位置已有棋子,请重下\n");
}
}
else
{
printf("输入超出范围,请重下\n");
}
}
}
//电脑下棋,这里规定,电脑下棋,棋子为'#'
void computer(char board[ROW][COL], int row, int col)
{
printf("电脑下棋\n");
int i = 0;
int j = 0;
while (1)
{
i = rand() % row;
j = rand() % col;
if (' ' == board[i][j])
{
board[i][j] = '#';
break;
}
}
}
//判断行相等
char equ_row(char board[ROW][COL], int i, int j, int* pc)
{
while (board[i][j] != board[i][j + 1] && j < COL - 2 || board[i][j] == ' ' && j < COL - 2)//跳过行中相邻元素不相等或者相等但为空格的情况
j++;
if (board[i][j] == board[i][j + 1] && j < COL - 1 && (*pc) < NUM - 1 && board[i][j] != ' ')//如果相邻元素相等且不为空格就继续判断之后的元素;当然元素与其后面的元素比较,列就不能到最后一列,只能到最后一列的前一列。
{
*pc = *pc + 1;//这里用*pc来计算递归次数
return equ_row(board, i, j + 1, pc);
}
else if (NUM - 1 == *pc)//这里通过*pc来计算递归次数,进而统计有几个相邻棋子都相等,递归一次判断两个元素相等,所以当*pc等于NUM-1时,就存在赢的一方
return board[i][j];
else//通过比较后,不存在NUM个相邻元素都相等且不为空格就返回0
return '\0';
}
//判断列相等
char equ_col(char board[ROW][COL], int i, int j, int * pc)
{
while (board[i][j] != board[i + 1][j] && i < ROW - 2 || board[i][j] == ' ' && i < ROW - 2)//跳过列中相邻元素不相等或者相等但为空格的情况
i++;
if (board[i][j] == board[i + 1][j] && i < ROW - 1 && (*pc) < NUM - 1 && board[i][j] != ' ')//如果相邻行同列的元素相等且不为空格就继续判断之后的元素;当然元素与其下方的元素比较,行就不能到最后一列,只能到最后一行的前一行。
{
*pc = *pc + 1;//这里用*pc来计算递归次数
return equ_col(board, i + 1, j, pc);
}
else if (NUM - 1 == *pc)//通过*pc来计算递归次数,进而统计有几个相邻棋子都相等,递归一次判断两个元素相等,所以当*pc等于NUM-1时,就存在赢的一方
return board[i][j];
else
return '\0';
}
//判断对角线相等
char diag(char board[ROW][COL], int i, int j, int* pc)
{
while (board[i][j] != board[i + 1][j + 1] && i < ROW - 2 || board[i][j] == ' ' && i < ROW - 2)//跳过对角线中相邻元素不相等或者相等但为空格的情况
{
i++;
j++;
}
if (board[i][j] == board[i + 1][j + 1] && i < ROW - 1 && (*pc) < NUM - 1 && board[i][j] != ' ')//在对角线上,如果相邻的元素相等且不为空格就继续判断之后的元素;
{
*pc = *pc + 1;
return diag(board, i + 1, j + 1, pc);
}
else if (NUM - 1 == *pc)
return board[i][j];
else
return '\0';
}
//判断另一对角线相等
char dia(char board[ROW][COL], int i, int j, int* pc)
{
while (board[i][j] != board[i + 1][j - 1] && i < ROW - 2 || board[i][j] == ' ' && i < ROW - 2)
{
i++;
j--;
}
if (board[i][j] == board[i + 1][j - 1] && i < ROW - 1 && (*pc) < NUM - 1 && board[i][j] != ' ')//在另一条对角线上,如果相邻的元素相等且不为空格就继续判断之后的元素;
{
*pc = *pc + 1;
return dia(board, i + 1, j - 1, pc);
}
else if (NUM - 1 == *pc)
return board[i][j];
else
return '\0';
}
//判断是否平局
char is_full(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 'c';
}
}
return 'q';
}
//判断输赢
//这里由于玩家棋子为'*',电脑棋子为'#',所以我规定电脑赢返回'#',玩家赢返回'*',游戏继续返回'c',平局返回'q'
char is_win(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
int count = 0;
for (i = 0; i < row; i++)
{
count = 0;
if (equ_row(board, i, j, &count))
return equ_row(board, i, j, &count);
}
i = 0;
for (j = 0; j < col; j++)
{
count = 0;
if (equ_col(board, i, j, &count))
return equ_col(board, i, j, &count);
}
for (i = 0, j = COL - NUM; j >= 0; j--)
{
count = 0;
if (diag(board, i, j, &count))
return diag(board, i, j, &count);
}
for (i = 1, j = 0; i <= ROW - NUM; i++)
{
count = 0;
if (diag(board, i, j, &count))
return diag(board, i, j, &count);
}
for (i = 0, j = NUM - 1; j < COL; j++)
{
count = 0;
if (dia(board, i, j, &count))
return dia(board, i, j, &count);
}
for(i = 1, j = COL-1; i <= ROW - NUM; i++)
{
count = 0;
if (dia(board, i, j, &count))
return dia(board, i, j, &count);
}
return is_full(board, col, row);
}
最后就是游戏的逻辑函数了:
#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
void game()
{
char board[ROW][COL] = { 0 };
InitBoard(board,ROW,COL);
Print(board, ROW, COL);
char ret = 0;
while (1)
{
person(board, ROW, COL);
ret = is_win(board, ROW, COL);
if (ret != 'c')
break;
Print(board, ROW, COL);
computer(board, ROW, COL);
ret = is_win(board, ROW, COL);
if (ret != 'c')
break;
Print(board, ROW, COL);
}
if (ret == '*')
{
printf("玩家赢\n");
Print(board, ROW, COL);
}
else if(ret == '#')
{
printf("电脑赢\n");
Print(board, ROW, COL);
}
else
{
printf("平局\n");
Print(board, ROW, COL);
}
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
return 0;
}
上述代码就是实现三子棋的代码。对于判断输赢,即判断行,列,对角线元素是否相等,我采用的是递归的方式,即函数可以用来检查相邻两元素是否相等,在判断前跳过不相等的相邻元素以及相等的空格元素,然后再来判断相邻且相等的元素有几个是相等的,当数量满足NUM(即N子棋)后,判定哪一方赢。在程序里,由于玩家棋子为’*‘,电脑棋子为’#‘,所以我规定电脑赢返回’#‘,玩家赢返回’ *‘,游戏继续返回’c’,平局返回’q’。至于递归如何思考,我有写一篇关于文章,专门介绍了我写递归的思路,链接: 关于函数递归和函数迭代,我的理解.
我对三子棋代码如何判断输赢进行了优化,通过优化,这个函数就具备了N子棋的玩法,即可改变ROW,COL,NUM的大小,就可以自定义棋盘大小以及游戏规则。