C语言写五子棋,使用多文件形式,使用代码看起来更好看;在这里我实现的功能是双人博弈,如果要实现人机对战,那么代码就会很复杂;
一.main.c
在主调函数中首先要提供一个给用户选择的界面,在这里我们假定选择1为开始游戏,2为退出游戏,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
#include "gobang.h" void Mean(){ printf ( "-----------------------\n" ); printf ( " 1.play 2.drop up\n" ); printf ( "-----------------------\n" ); } int main(){ int seclet = 0; int c = 0; while (!c){ Mean(); printf ( "Please choose number:\n" ); scanf ( "%d" , &seclet); switch (seclet){ case 1: Game(); break ; case 2: c = 1; break ; default : printf ( "Please Enter Once:\n" ); break ; } } printf ( "Byebye~\n" ); system ( "pause" ); return 0; } |
函数执行开始,会在显示框中提示用户输入数字,1为进入游戏,此时会调用Game()函数;2为退出游戏。其中while循环的作用是当用户进入界面输入错误(非0或1)或者完成一次游戏后继续弹出选项,只有当输入0才将num置为0,退出循环。
二.gobang.h
函数的头文件,其中包含宏定义和函数的声明,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
#pragma once #define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include <windows.h> #define ROW 10//控制棋盘大小 #define COL 10//控制棋盘大小 #define PLAYER1 1//玩家1的棋为 1 #define PLAYER2 2//玩家2的棋为 2 #define NEXT 3//NEXT代表继续 #define DRAW 4//DRAW代表平局 #define U 10//上 #define RU 11//右上 #define R 12//右 #define RD 13//右下 #define D 14//下 #define LD 15//左下 #define L 16//左 #define LU 17//左上 extern void Game(); //函数的声明 |
三.gobang.c
五子棋的主要逻辑就是:先打印出棋盘,然后玩家一走一步,判断是否连成五子(若成功则跳出),在打印出走之后的棋盘,玩家二走一步,再次判断是否连成五子,再打印出走之后的棋盘;
所以除了Game()函数外还需要实现以下几个接口:
1
2
3
|
Print() //打印棋盘 Player() //玩家下棋 Judge //判断是否连成五子 |
1.Game()
五子棋的主要代码都会写在这个文件里,test.c当中必须包含头文件test.h。Game()函数调用其他函数,实现整个下棋过程。因为两个玩家下棋是同样的操作,所以调用同一个函数,只是传入的玩家参数不同,定义变量who,使得每次进入while循环,who的值都会改变一次,详细过程见如下代码和注释。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
void Game(){ int checkerboard[ROW][COL] = { 0 }; //定义一个二维数组 int result = 0; //定义变量 int who = PLAYER1; //定义变量who初始值为PLAYER1的值 while (1){ //一直做循环 Print(checkerboard); //打印出初始面板 Player(checkerboard, ROW, COL, who); //玩家开始下棋 result = Judge(checkerboard); if (result != NEXT){ //判断result的值是否等于NEXT,不等于则跳出循环 break ; } who = (who == PLAYER1 ? PLAYER2 : PLAYER1); //每进入一次循环who的值都会改变一次 } Print(checkerboard); //打印出最终的面板 switch (result){ case PLAYER1: //返回值为PLAYLER1,玩家一胜利 printf ( "PLALYER1 win\n" ); break ; case PLAYER2: //返回值为PLAYER2,玩家二胜利 printf ( "PLAYER2 win\n" ); break ; case DRAW: //返回值为DRAW,平局 printf ( "IS DRAW" ); break ; } } |
2.Print()
打印棋盘的函数并不难实现,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
void Print( int board[][COL]){ //打印当前棋盘 //system("cls"); printf ( " " ); for ( int i = 0; i < ROW; i++){ //打印出横着1到10 printf ( " %d " , i); } printf ( "\n" ); for ( int i = 0; i < ROW; i++){ printf ( "%d" , i); for ( int j = 0; j < COL; j++){ if (board[i][j] == 0){ printf ( " . " ); //打印一个点 } else { printf ( " %d " , board[i][j]); //打印出当前位置的值 } } printf ( "\n" ); } } |
3.Player()
此函数无非就是给board[x][y]按照x,y坐标赋值,赋值为PLAYER1或者PLAYER2。要注意将x,y定义为全局变量,延长其生命周期,作用是记录每次落子位置,便于计算是否连成五子。Player()函数代码如下:
1
2
|
int x = 0; //全局变量x int y = 0; //全局变量y |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
void Player( int board[][COL], int row, int col, int c){ while (1){ printf ( "Please Enter x y:\n" ); scanf ( "%d%d" ,&x,&y); if (x<0 || x>row - 1 || y<0 || y>col - 1){ //x,y坐标不满足条件则返回到while printf ( "Eorr\n" ); continue ; } if (board[x][y] == 0){ //此处为初始值,可以在此处下棋 board[x][y] = c; //给board[][]赋值为PLAYER1或者PLAYER2 break ; //跳出循环 } else { printf ( "此处不为空,重新输入\n:" ); continue ; } } } |
4.Judge()
判断是否连成五子,这是最难得一步,在这里之前定义得八个方向就用的上了。连成五子无非就四种情况,横着,竖着,斜着(两种情况),则只需要统计则四个方向棋子的数量。在这里说明为什么if()判断中的条件是>=4。在Calculation()函数中统计某一个方向的棋子数量(那八个方向)时,当前棋子的位置已知,假如它的上方有四颗棋子,则五子已经连成,但因为计数器的初始值为0,所以此时count的值为4,函数的返回值也为4,所以在Judge()函数中,if()的条件为>=4(此时>4的情况一般不会发生)。其余详细代码实现如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
int Judge( int board[][COL]){ if (Calculation(board, U) + Calculation(board, D)>=4 || \ //统计上和下棋子数量,此时结果为竖直方向上的相同棋子数量 Calculation(board, RU) + Calculation(board, LD) >= 4 || \ //统计右上和左下棋子数量,此时结果为斜着向上的相同棋子数量 Calculation(board, R) + Calculation(board, L) >= 4 || \ //统计右和左棋子数量,此时结果为横向上的相同棋子数量 Calculation(board, RD) + Calculation(board, LU) >= 4){ //统计右下和左上棋子数量,此时结果为斜着方向上的相同棋子数量 return board[x][y]; } for ( int i = 0; i < ROW; i++){ //如果还有一个坐标为初始值,游戏继续 for ( int j = 0; j < COL; j++){ if (board[i][j] == 0){ return NEXT; } } } return DRAW; //每个坐标都不为初始值且没人胜利,平局 } int Calculation( int board[][COL], int direction){ //传入了方向参数 int _x = x; //局部变量使其等于当前坐标 int _y = y; //局部变量使其等于当前坐标 int count = 0; //计数器 while (1){ //一直做循环直到统计完某个方向 switch (direction){ case U: //往上则y坐标不变,x坐标减一,以下情况类似 _x--; break ; case D: _x++; break ; case L: _y--; break ; case R: _y++; break ; case RU: _x--; _y++; break ; case RD: _x++; _y++; break ; case LD: _x++; _y--; break ; case LU: _x--; _y--; break ; default : break ; } if (_x<0 || _x>ROW - 1 || _y<0 || _y>COL - 1){ //统计的某个方向已经到了边界,无需统计跳出循环 break ; } else { if (board[x][y] == board[_x][_y]){ //棋子和当前下的棋子相同 count++; //计数器加一 } else { break ; //棋子和当前下的棋子不同,跳出循环 } } } return count; } |
我们还可以在Print()函数中加上system("cls"),此函数为清屏操作,加上后就是在一张棋盘下棋了,还可以改进输出棋子的内容,如将
这样就可以用不同的符号代表棋子了,最终的运行结果如下图:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:https://blog.csdn.net/Enthusiastic_boy/article/details/117520651