目录
前言
正文
主函数部分(test.c)
头文件部分(game.h)
功能实现函数部分(game.c)
1.初始化函数
2.打印棋盘函数
3.玩家移动函数
4.电脑移动函数
5.判断输赢函数
6.重回game函数
效果展示
总结
前言
经过一段时间的学习,分支与循环、数组与函数的相关知识已经足够支撑我们完成一个小游戏了,这个小游戏不需要太多的功能,也不需要太复杂的逻辑,只需要懂点循环与分支,懂点数组的使用以及函数如何传参和返值即可。作为草稿纸上常出现的小游戏,三子棋的逻辑可谓是非常简单了,只需要玩家走一步、电脑走一步,并在八种可能获胜结果中比对就行了,下面让我们一起来看看三子棋的实现。
注意:本文为模拟游戏实现,代码量两百左右,共有两个源文件+一个头文件。
正文
本程序一共就下面三个文件
主函数部分(test.c)
我们直接从主函数开始
下面看看game(游戏)函数的设计
//三子棋 #include"game.h" void menu() { printf("**************************\n"); printf("******** 1.Play ********\n"); printf("******** 0.Exit ********\n"); printf("**************************\n"); } void game() { char board[ROW][COL] = { 0 };//定义 init_board(board, ROW, COL);//初始化 display_board(board, ROW, COL);//打印 //下棋 char tmp = 0; while (1) { player_move(board, ROW, COL);//玩家走 display_board(board, ROW, COL);//打印 tmp=who_win(board, ROW, COL);//判断 if (tmp != 'C') break; computer_move(board, ROW, COL);//电脑走 display_board(board, ROW, COL);//打印 tmp=who_win(board, ROW, COL);//判断 if (tmp != 'C') break; } if (tmp == '*') printf("玩家获胜\n"); else if (tmp == '#') printf("电脑获胜\n"); else printf("和局\n"); } int main() { int input = 0; srand((unsigned int)time(NULL)); do { menu(); printf("请输入你的选择:>"); scanf("%d", &input); switch (input) { case 1: game(); break; case 0: printf("退出游戏!\n"); break; default : printf("选择错误,重新选择!\n"); break; } } while (input); return 0; }
主函数文件(test.c就介绍完了),下面介绍game.h这个头文件
头文件部分(game.h)
这个头文件是自己建立的,目的就是在主函数源文件(test.c)和功能实现函数(game.c)中搭起一座沟通的桥梁。
#pragma once #include<stdio.h> #include<stdlib.h> #define ROW 3 #define COL 3 void init_board(char board[][COL],int row,int col);//初始化 void display_board(char board[][COL], int row, int col);//打印 void player_move(char board[][COL], int row, int col);//玩家走 void computer_move(char board[][COL], int row, int col);//电脑走 char who_win(char board[][COL], int row, int col);//判断
自定义头文件部分代码量最少,功能也比较简单,如果说test.c是大脑、控制中心,那么game.h就相当于神经系统的一部分,是函数间沟通的桥梁,能对函数起主导作用。
功能实现函数部分(game.c)
此部分是程序运行必不可少的部分,没有它们,程序能跑,但什么都干不了,类似于植人。
本部分函数细节较多,因此我会分开逐个讲解。
1.初始化函数
初始函数主要就是把整个数组遍历一遍,然后将字符 空格 (' ')赋给每个数组即可
void init_board(char board[][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] = ' ';//初始化数组为空格' ' } } }
2.打印棋盘函数
打印棋盘函数需要思考下:什么情况打印 | 和 什么情况打印 --- ,不想清楚这些问题就无法打印出优美的棋盘。
void display_board(char board[][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"); } } }
3.玩家移动函数
玩家移动就是让玩家输入坐标,输入坐标有三种情况:非法、占用、成功,依次判断即可,其次要注意不是人人都知道坐标从0开始,因此在使用x、y时要减1,确保坐标不会越界。
void player_move(char board[][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] == ' ') { board[x - 1][y - 1] = '*';//落子成功 break;//跳出循环 } else printf("此坐标已被占用,重新输入\n"); } else printf("坐标非法,重新输入\n"); } }
4.电脑移动函数
电脑移动不要太多判断,只需要给电脑一个随机值,一个判断条件。
直到电脑落子成功再跳出即可
void computer_move(char board[][COL], int row, int col)//电脑走 { printf("轮到电脑落子:>"); while (1) { int x = rand() % row; int y = rand() % col; if (board[x][y] == ' ') { board[x][y] = '#'; break; } } printf("电脑已落子\n"); }
5.判断输赢函数
这个函数出现于玩家或电脑移动之后,目的就是判断是否已达成胜利条件(共八种),如果达成了,就能跳出game函数中的死循环,结束游戏。
注意:本程序中函数就判断输赢函数和判断和局函数有返回值,一个是char,一个是int
int place(char board[][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 who_win(char board[][COL], int row, int col)//判断 { int i = 0; int j = 0; for (i = 0; i < row; i++) { if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][2] != ' ') return board[i][0]; }//判断行 for (j = 0; j < row; j++) { if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[2][j] != ' ') return board[0][j]; }//判断列 if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[2][2] != ' ') return board[0][0];//判断正斜 if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[2][0] != ' ') return board[0][2];//判断反斜 if (place(board, ROW, COL) == 1) return 'Q';//判断和局 return 'C';//默认选项 }
—
6.重回game函数
经过game.c中各函数的一顿操作,最终由判断输赢函数携带返回值回到game函数进行判断
效果展示
让我们直接来看看效果如何:
可以看到三种情况都能实现,现在只需要将代码进行优化,主要是优化输赢判断部分,使其兼容性更强,以后在玩五子棋时能沿用这套方案。
下面我将展示功能实现函数(game.c)的全部代码:
#define _CRT_SECURE_NO_WARNINGS 1 #include"game.h" void init_board(char board[][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 display_board(char board[][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"); } } } void player_move(char board[][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] == ' ') { board[x - 1][y - 1] = '*';//落子成功 break;//跳出循环 } else printf("此坐标已被占用,重新输入\n"); } else printf("坐标非法,重新输入\n"); } } void computer_move(char board[][COL], int row, int col)//电脑走 { printf("轮到电脑落子:>"); while (1) { int x = rand() % row; int y = rand() % col; if (board[x][y] == ' ') { board[x][y] = '#'; break; } } printf("电脑已落子\n"); } int place(char board[][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 who_win(char board[][COL], int row, int col)//判断 { int i = 0; int j = 0; for (i = 0; i < row; i++) { if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][2] != ' ') return board[i][0]; }//判断行 for (j = 0; j < row; j++) { if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[2][j] != ' ') return board[0][j]; }//判断列 if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[2][2] != ' ') return board[0][0];//判断正斜 if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[2][0] != ' ') return board[0][2];//判断反斜 if (place(board, ROW, COL) == 1) return 'Q';//判断和局 return 'C';//默认选项 }
总结
我们本次三子棋之旅到这就要结束了,说实在的,三子棋这段代码蕴含逻辑比较简单,唯一需要注意的就是棋盘打印和电脑随机值,只有理解透彻了,代码敲起来才能得心应手!