一、 n字棋游戏规则
在一个m*m的正方形棋盘格上先由玩家落子,然后电脑落子,当行,或者列,或者斜着连着的数量达到开局设置的n个子,则玩家或者电脑获胜,当棋盘格下满了以后平局。
二、游戏实现
(1)游戏整体流程:
首先打印菜单,然后让玩家选择是否进入游戏还是退出游戏,如果进入游戏,创建棋盘格,玩家选择棋盘格大小,玩家选择玩几子棋,然后游戏开始,玩家落子,电脑落子判定胜负平,游戏结束,最后让玩家选择继续游戏还是退出游戏。
本文使用玩家落子为 * ,电脑落子为 #
(2)如何实现n子棋:
在游戏开始之后创建整形int = 0,保存玩家想玩的n子棋。创建int row 和int col,保存玩家想玩的棋盘格
(3)如何实现玩家自定义游戏棋盘:
我们可以定义一个很大的二维数组(本文定义了50*50的二维数组),通过宏定义ROW=50,COL=50,在一轮游戏开始的时候创建二维数组char board[ROW][COL] , 这样我们就有了ROW*COL大小的棋盘格。
玩家输入棋盘格大小之后,只需要显示出玩家设定的大小范围即可。
#define ROW 50
#define COL 50
自定义棋盘格大小代码:
void BoardSize(int* row, int* col)
{
while (1)
{
int i = 0;
printf("\n输入正方形棋盘格的大小,上限为%d\n",ROW);
printf("至少输入3,请输入->");
scanf("%d", &i);
*row = i;
*col = *row;
if (*row > 2 && *row <= ROW)
{
break;
}
else
{
printf("\n输入非法请重新输入!\n");
}
}
}
(4)初始化玩家设定的棋盘
显示棋盘时,未落子的地方应当时空白的所以我们将board[ROW][COL]中前row-1行和前col-1列初始化为空格即可。
//初始化棋盘
void InitBoard(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++)
{
board[i][j] = ' ';
}
}
}
(5) 显示棋盘
显示board[ROW][COL]中的前row-1行和前col-1列时,因为初始化都是空格,我们是看不到的,所以我们需要打印棋盘格线,将其分 开。并提示这是第几行第几列,如图:
这是如何实现的呢?
空白部分是就是棋盘格初始化的空格,为了方便观看,我们显示的时候可以在打印board[i][j]的前后都打印一个空格。每打印一个空格后面就好打印一个 | 所以打印到col-1列时,竖线只需要打印到col-2即可,下一行是先打印 --- 后打印 | ,同样也是 --- 打印到col-1, | 打印到col-2。可以察觉到每两行就是一个循环。最后一次循环的时候就不需要打印第二行也就是---|---|---。
提示第几行第几列时如何实现的呢?
显然提示列的部分是在再开始打印的,从0一直打印到col即可,为了对齐可以加入空格。
提示行的部分是在每次开始打印board[i][0]的第一个元素之前打印是行数和 | 。
此部分重点是如何对齐,一定要分清楚影响打印的是行数还是列数!!!
//打印棋盘
void DispalyBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i <= col; i++)
{
if (i < 9)
{
printf(" %d ", i );
}
else
{
printf(" %d ", i );
}
}
printf("\n");
for (i = 0; i < row ; i++)
{
if (i < 9)
{
printf(" %d |", i + 1);
}
else
{
printf(" %d|", i + 1);
}
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++)
{
if (j <= col)
{
printf("---");
if (j < col)
{
printf("|");
}
}
}
}
printf("\n");
}
}
(6)玩家落子
只需要判定玩家输入落子的位置是不是可以落子的,如果落子位置是空格就可以落子,不是就需要重新输入,一直到落子成功。
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 (' ' == board[x-1][y-1])
{
board[x-1][y-1] = '*';
break;
}
else
{
printf("\n输入坐标非法,请重新输入\n");
}
}
}
(7)电脑落子
电脑落子需要生成随机数,我们可以通过rand()函数来实现,由于在使用rand()之前要使用srand()对其进行初始化。
srand((unsigned int)time(NULL));
这样就可完成对rand()的初始化time()函数是获取时间戳。rand()和srand()需要引用库函数 #include<stdlib.h>,time()需要引用库函数#include<time.h>。
rand()%row可以得到0~(row-1),rand()%col可以得到0~(col-1)。
后面只要判断随机值的位置是不是空格,判定能否落子即可。
//电脑走
void ComputerMove(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;
}
}
}
(8)判断玩家是否获胜
获胜有三种情况:行符合,列符合,斜着符合,其中斜着符合,可分为左对角线方向和右对角线方向。
判断行:
只需要判断0~(row-1)行,列需要判断0~(col-1-(n-1))即 0~(col-n),对数组这些部分进行遍历。对board[i][j]遍历时,判断这一行从这个元素开始是不是都是 * ,因为 * 有对应的ASCII码值,所以创建一个sum存储这n个位置的ASCII码值之和,如果sum -减去‘*’(ASCII码值)乘以n等于0,就证明这些位置都是 *
判断列:
与行判断相似,只需要判断0~(col-1)列,行需要判断0~(row-1-(n-1))即 0~(row-n),对数组这些部分进行遍历。
左对角线判断:
当我们进行左对角线判断时因为获胜时棋子是左斜着的,所以只需要判断0~(row-n)行,0~(col-n)列,相当于判断棋盘部分比玩家设置的棋盘部分少了后n行,后n列。对数组这些部分进行遍历。以遍历的位置为原点,计算这点向右下方的这些元素的ASCII码值之和是否等于‘*’(ASCII码值)乘以n。
右对角线判断:
当我们进行右对角线判断时因为获胜时棋子是右斜着的,所以只需要判断0~(row-n)行,(n-1)~(col-1)列。对数组这些部分进行遍历。以遍历的位置为原点,计算这点向左下方的这些元素的ASCII码值之和是否等于‘*’(ASCII码值)乘以n。
判断电脑赢和判定玩家赢同理只不过判定的ASCII码值从*变成#。
char PlayerWin(char board[ROW][COL], int row, int col, int n)
{
int i = 0;
for (i = 0; i < row ; i++)
{
int j = 0;
for (j = 0; j < col ; j++)
{
int k = 0;
//行
int sum1 = 0;
if (j <= col - n)
{
for (k = 0; k < n; k++)
{
sum1 = sum1 + board[i][j + k];
}
}
//列
int sum2 = 0;
if (i <= row - n)
{
for (k = 0; k < n; k++)
{
sum2 = sum2 + board[i + k][j];
}
}
//左对角线
int sum3 = 0;
if (i <= row - n && j <= col - n)
{
for (k = 0; k < n; k++)
{
sum3 = sum3 + board[i+k][j+k];
}
}
//右对角线
int sum4 = 0;
if (i <= row - n && j >= n - 1)
{
for (k = 0; k < n; k++)
{
sum4 = sum4 + board[i + k][j - k];
}
}
if ( (0 == sum1 - '*' * n)|| (0 == sum2 - '*' * n) || (0 == sum3 - '*' * n) || (0 == sum4 - '*' * n))
{
return '*';
}
}
}
return 'C';
}
(9)平局
当棋盘内落满子时为平局,所以对board[row][col]的每个元素进行遍历,如果没有空格就就平局。
int IsDraw(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;
}
}
}
return 1;
}
(10)游戏主体实现
游戏开始玩家输入棋盘格大小,选择几子棋,无论玩家落子还是电脑落子都要进行判断输赢,根据输赢平局函数的返回值判断。
//游戏开始
void game()
{
srand((unsigned int)time(NULL));
char board[ROW][COL];
int n = 0;
//玩家设置的棋盘行列
int row = 0;
int col = 0;
BoardSize( &row, &col);
//选择下几子棋
while (1)
{
printf("\n可以玩三子棋以上的游戏\n");
printf("您想玩几子棋?请输入数字->:");
scanf("%d", &n);
if ( n > 2 && n <= row )
{
break;
}
else
{
printf("\n输入非法,请重新输入!\n");
}
}
//初始化棋盘
InitBoard(board, row, col);
//打印棋盘
DispalyBoard(board, row, col);
//玩家 * 42 电脑 # 35 空格32
while (1)
{
//玩家走
PlayerMove(board, row, col);
DispalyBoard(board, row, col);
//判断玩家获胜
if (PlayerWin(board, row, col, n) == '*')
{
printf("恭喜玩家获胜\n");
break;
}
//奇数棋盘玩家最后落子
//判断是否平局
if (IsDraw(board, row, col) == 1)
{
printf("棋逢对手,平局!\n");
break;
}
//电脑走
printf("电脑玩家落子\n");
ComputerMove(board, row, col);
DispalyBoard(board, row, col);
//判断电脑是否获胜
if (ComputerWin(board, row, col, n) == '#')
{
printf("恭喜电脑获胜\n");
break;
}
//偶数棋盘电脑最后落子
//判断是否平局
if (IsDraw(board, row, col) == 1)
{
printf("棋逢对手,平局!\n");
break;
}
}
}
(11)游戏逻辑实现
input初始设置不能为0,不然直接不进入循环。
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
int main()
{
int input = 1;
while (input)
{
menu();
printf("\n请输入->:");
scanf("%d", &input);
switch (input)
{
case 0:
{
printf("\n已经退出游戏\n");
break;
}
case 1:
{
game();
break;
}
default:
{
printf("\n输入错误,请重新输入\n");
break;
}
}
}
return 0;
}
三、完整代码
(1)game.h
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 50
#define COL 50
//打印菜单
void menu();
//游戏主体
void game();
//玩家选择棋盘格大小
void BoardSize(int* row, int* col);
//初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col);
//打印棋盘
void DispalyBoard(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);
//玩家赢
char PlayerWin(char board[ROW][COL], int row, int col, int n);
//电脑赢
char ComputerWin(char board[ROW][COL], int row, int col, int n);
//平局
int IsDraw(char board[ROW][COL], int row, int col);
(2)test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
int main()
{
int input = 1;
while (input)
{
menu();
printf("\n请输入->:");
scanf("%d", &input);
switch (input)
{
case 0:
{
printf("\n已经退出游戏\n");
break;
}
case 1:
{
game();
break;
}
default:
{
printf("\n输入错误,请重新输入\n");
break;
}
}
}
return 0;
}
(3)game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu()
{
printf("\n****************\n");
printf("*****1.play*****\n");
printf("*****0.exit*****\n");
printf("****************\n");
}
void BoardSize(int* row, int* col)
{
while (1)
{
int i = 0;
printf("\n输入正方形棋盘格的大小,上限为%d\n",ROW);
printf("至少输入3,请输入->");
scanf("%d", &i);
*row = i;
*col = *row;
if (*row > 2 && *row <= ROW)
{
break;
}
else
{
printf("\n输入非法请重新输入!\n");
}
}
}
//初始化棋盘
void InitBoard(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++)
{
board[i][j] = ' ';
}
}
}
//打印棋盘
void DispalyBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i <= col; i++)
{
if (i < 9)
{
printf(" %d ", i );
}
else
{
printf(" %d ", i );
}
}
printf("\n");
for (i = 0; i < row ; i++)
{
if (i < 9)
{
printf(" %d |", i + 1);
}
else
{
printf(" %d|", i + 1);
}
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++)
{
if (j <= col)
{
printf("---");
if (j < col)
{
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 (' ' == board[x-1][y-1])
{
board[x-1][y-1] = '*';
break;
}
else
{
printf("\n输入坐标非法,请重新输入\n");
}
}
}
//电脑走
void ComputerMove(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;
}
}
}
//玩家赢
char PlayerWin(char board[ROW][COL], int row, int col, int n)
{
int i = 0;
for (i = 0; i < row ; i++)
{
int j = 0;
for (j = 0; j < col ; j++)
{
int k = 0;
//行
int sum1 = 0;
if (j <= col - n)
{
for (k = 0; k < n; k++)
{
sum1 = sum1 + board[i][j + k];
}
}
//列
int sum2 = 0;
if (i <= row - n)
{
for (k = 0; k < n; k++)
{
sum2 = sum2 + board[i + k][j];
}
}
//左对角线
int sum3 = 0;
if (i <= row - n && j <= col - n)
{
for (k = 0; k < n; k++)
{
sum3 = sum3 + board[i+k][j+k];
}
}
//右对角线
int sum4 = 0;
if (i <= row - n && j >= n - 1)
{
for (k = 0; k < n; k++)
{
sum4 = sum4 + board[i * k][j - k];
}
}
if ( (0 == sum1 - '*' * n)|| (0 == sum2 - '*' * n) || (0 == sum3 - '*' * n) || (0 == sum4 - '*' * n))
{
return '*';
}
}
}
return 'C';
}
//电脑赢
char ComputerWin(char board[ROW][COL], int row, int col, int n)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
int k = 0;
//行
int sum1 = 0;
if (j <= col - n)
{
for (k = 0; k < n; k++)
{
sum1 = sum1 + board[i][j + k];
}
}
//列
int sum2 = 0;
if (i <= row - n)
{
for (k = 0; k < n; k++)
{
sum2 = sum2 + board[i + k][j];
}
}
//左对角线
int sum3 = 0;
if (i <= row - n && j <= col - n)
{
for (k = 0; k < n; k++)
{
sum3 = sum3 + board[i + k][j + k];
}
}
//右对角线
int sum4 = 0;
if (i <= row - n && j >= n - 1)
{
for (k = 0; k < n; k++)
{
sum4 = sum4 + board[i + k][j - k];
}
}
if ((0 == sum1 - '#' * n) || (0 == sum2 - '#' * n) || (0 == sum3 - '#' * n) || (0 == sum4 - '#' * n))
{
return '#';
}
}
}
return 'C';
}
//平局
int IsDraw(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;
}
}
}
return 1;
}
//游戏开始
void game()
{
srand((unsigned int)time(NULL));
char board[ROW][COL];
int n = 0;
//玩家设置的棋盘行列
int row = 0;
int col = 0;
BoardSize( &row, &col);
//选择下几子棋
while (1)
{
printf("\n可以玩三子棋以上的游戏\n");
printf("您想玩几子棋?请输入数字->:");
scanf("%d", &n);
if ( n > 2 && n <= row )
{
break;
}
else
{
printf("\n输入非法,请重新输入!\n");
}
}
//初始化棋盘
InitBoard(board, row, col);
//打印棋盘
DispalyBoard(board, row, col);
//玩家 * 42 电脑 # 35 空格32
while (1)
{
//玩家走
PlayerMove(board, row, col);
DispalyBoard(board, row, col);
//判断玩家获胜
if (PlayerWin(board, row, col, n) == '*')
{
printf("恭喜玩家获胜\n");
break;
}
//奇数棋盘玩家最后落子
//判断是否平局
if (IsDraw(board, row, col) == 1)
{
printf("棋逢对手,平局!\n");
break;
}
//电脑走
printf("电脑玩家落子\n");
ComputerMove(board, row, col);
DispalyBoard(board, row, col);
//判断电脑是否获胜
if (ComputerWin(board, row, col, n) == '#')
{
printf("恭喜电脑获胜\n");
break;
}
//偶数棋盘电脑最后落子
//判断是否平局
if (IsDraw(board, row, col) == 1)
{
printf("棋逢对手,平局!\n");
break;
}
}
}