9.数组的应用实例1:三子棋(综合以前学习的知识)
三子棋的实现:(重点都在注释中)
1. 游戏不退出,继续玩下一把(循环)
2. 应用多文件的形式写代码
game.h -- 游戏函数的声明
#pragma once //游戏相关的自定义函数 声明 写到game.h中 #include <stdio.h> #include <stdlib.h> //rand函数需要的头文件 #include <time.h> //time函数需要的头文件 //直接把头文件包含在自定义头文件中,当别的文件包含本头文件时, //这个头文件里的头文件也会被包含 //定义宏常量:行数ROW 列数COL //,之后想修改,直接修改头文件里的这两个宏常量就可以 #define ROW 3 #define COL 3 // 1. InitBoard(); 用于初始化数组,将空格全部初始化为空格 void InitBoard(char board[ROW][COL], int row, int col); // 2. DisplayBoard():打印数组(棋盘) void DisplayBoard(char board[ROW][COL], int row, int col); // 3. 玩家下棋: void PlayerMove(char board[ROW][COL], int row, int col); // 4. 电脑下棋 void ComputerMove(char board[ROW][COL], int row, int col); // 5. 判断输赢(返回对应的符号): //玩家赢 -- ‘*’ //电脑赢 -- ‘#’ //平局 -- ‘Q’ //继续 -- ‘C’ char IsWin(char board[ROW][COL], int row, int col);
game.c -- 游戏函数的实现
#define _CRT_SECURE_NO_WARNINGS 1 //游戏相关的自定义函数 代码 写到game.c中 #include "game.h" // 1. InitBoard(); 用于初始化数组,将空格全部初始化为空格 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] = ' '; // 将空格全部初始化为空格 } } } // 2. DisplayBoard():打印数组(棋盘) //版本1:都是空格,什么都看不见 //void DisplayBoard(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++) // 遍历列 // { // printf("%c", board[i][j]); // } // // 打印完一行后换行 // printf("\n"); // } //} //版本2:只能打印对应的行,不能打印对应的列(列被”写死“了) //void DisplayBoard(char board[ROW][COL], int row, int col) //{ // int i = 0; // for (i = 0; i < row; i++) // 遍历行 // { // //1. 打印数据 // printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]); // // //2. 打印分割线 // if (i < row - 1) //只有中间才需要打印分割线,最后一行不打印分割线 // { // printf("---|---|---\n"); // } // // } //} //版本3: 实现多行多列,边写边想边实现 void DisplayBoard(char board[ROW][COL], int row, int col) { int i = 0; for (i = 0; i < row; i++) // 遍历行 { //1. 打印数据 int j = 0; for (j = 0; j < col; j++) { printf(" %c ", board[i][j]); // 先打印数据 if (j < col-1) // 最后一行不打印竖杠 { printf("|"); // 再打印竖杠 } } // 打印完一行后换行 printf("\n"); //2. 打印分割线 if (i < row - 1)//只有中间才需要打印分割线,最后一行不打印分割线 { int j = 0; for (j = 0; j < col; j++) { printf("---"); if (j < col - 1) // 最后一行不打印竖杠 { printf("|"); // 再打印竖杠 } } printf("\n"); } } } // 3. 玩家下棋: void PlayerMove(char board[ROW][COL], int row, int col) { // 让玩家通过坐标选择下棋位置 int x = 0; int y = 0; printf("玩家下棋>: \n"); while (1) //输入成功了再break跳出循环 { printf("请输入下棋的坐标,中间使用空格>:"); scanf("%d %d", &x, &y); //判断输入是否正确(二维数组行和列都是从0开始的,但玩家不一定知道) if (x >= 1 && x <= row && y >= 1 && y <= col) //坐标合法 { //判断是否可以落子 if (board[x-1][y-1] == ' ') //可以落子 //board[x-1][y-1]:输入是1行1列,其实是0行0列 { //落子了就把空格换成 * 号 board[x - 1][y - 1] = '*'; break; } else //不能落子 { printf("坐标被占用,不能落子,重新输入坐标\n"); } } else //坐标非法 { printf("坐标非法,重新输入\n"); } } } // 4. 电脑下棋 void ComputerMove(char board[ROW][COL], int row, int col) { int x = 0; // 让x随机生成 0 -- (row-1) 的值 int y = 0; // 让y随机生成 0 -- (col-1) 的值 printf("电脑下棋:>\n"); //数组的下标已经约定了,不会越界,但还有可能被占用 while (1) { x = rand() % row; //结果为 0 -- (row-1) 的值 y = rand() % col; //结果为 0 -- (col-1) 的值 if (board[x][y] == ' ') //如果随机坐标为空格,则落子 { board[x][y] = '#'; break; //落子成功则跳出循环 } //若不是空格,则继续循环出一个随机坐标,直至落子成功 } } // 5. 判断输赢(返回对应的符号): //玩家赢 -- ‘*’ //电脑赢 -- ‘#’ //平局 -- ‘Q’ //继续 -- ‘C’ // 5(补充):判断棋盘是否已满 int IsFull(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; //整个数组中有找到空格,则返回0 } } } //整个数组中没有找到空格(棋盘已满),则返回1 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][0] != ' ') //同行第一个元素等于第二个元素,第二个元素等于第三个元素,判断其中一个元素是否为空格 { return board[i][0]; //因为玩家赢时,同行上都是 ‘*’,所以直接返回行上的其中一个元素就行 } } // 判断 三列 是否有连成一线的 for (i = 0; i < col; i++) { if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ') { return board[0][i]; //因为电脑赢时,同行上都是 ‘#’,所以直接返回行上的其中一个元素就行 } } // 判断 对角线 是否有连成一线的 //其中一条对角线 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]; } //平局 (上面四个return都没执行的话,则执行下面的代码) //自定义函数IsFull()函数用于判断棋盘是否已满 if (IsFull(board, row, col) == 1) //如果返回值为1,说明棋盘已满,平局 { return 'Q'; } //继续 (没有赢,也没平局,继续下棋) return 'C'; }
1. InitBoard(); 用于初始化数组,将空格全部初始化为空格
2. DisplayBoard():打印数组(棋盘)
3. PlayerMove():玩家下棋
4. ComputerMove():电脑下棋
5. IsFull():判断输赢(返回对应的符号):
玩家赢 -- ‘*’
电脑赢 -- ‘#’
平局 -- ‘Q’
继续 -- ‘C’
test.c -- 测试游戏
#define _CRT_SECURE_NO_WARNINGS 1 //1. 游戏不退出,继续玩下一把(循环) //2. 应用多文件的形式写代码 #include "game.h" //包含自己定义的头文件 void menu() { printf("***********************************\n"); printf("********** 1.play ***********\n"); printf("********** 0.exit ***********\n"); printf("***********************************\n"); } void game() { /* 1. 游戏在走的过程中要进行数据的存储,三子棋可以使用3*3的二维数组 即 char board[3][3]; 2. 没下棋之前数组中存放空格,下棋后将空格替换成字符 */ char board[ROW][COL] = { 0 }; InitBoard(board, ROW, COL); //传递 数组名,行数,列数 // 用于初始化数组,将空格全部初始化为空格 // 将游戏相关的自定义函数代码写到game.c中 // 打印棋盘 DisplayBoard(board, ROW, COL); //下棋: char ret = 0; //接受判断输赢的符号 //让玩家一直下: while (1) { //玩家下棋: PlayerMove(board, ROW, COL); //落子后再打印棋盘看棋盘情况 DisplayBoard(board, ROW, COL); //判断输赢(三子相连) ret = IsWin(board, ROW, COL); if (ret != 'C') { break; } //电脑下棋: ComputerMove(board, ROW, COL); //落子后再打印棋盘看棋盘情况 DisplayBoard(board, ROW, COL); //判断输赢 ret = IsWin(board, ROW, COL); if (ret != 'C') { break; } } //写在while循环外面的话,只用写一次 if (ret == '*') { printf("玩家赢\n"); } else if (ret == '#') { printf("电脑赢\n"); } else { printf("平局"); } } int main() { int input = 0; //rand函数调用之前得调用srand, //整个程序只用调一次,所以放在主函数中 srand((unsigned int)time(NULL)); //1. 游戏不退出,继续玩下一把(循环) do //不管三七二十一,先打印菜单 { menu(); //打印游戏菜单 printf("请选择:>"); scanf("%d", &input); //1 0 其它 switch (input) { case 1: game(); break; case 0: printf("退出游戏\n"); break; default: printf("选择错误,重新选择:\n"); break; } } while (input); //当input为0时停止循环退出游戏 //当input非0时继续循环 return 0; }
void game()
主程序: