c語言期中實戰專案—三子棋,思路分析+代碼詳細注釋>
- 專案簡述
- 思路分析
- 代碼及注釋
- 總結及注意事項
專案簡述
- 游戲簡介
此游戲很簡單,就是列印一個三行三列的空格,外加一些字符,構成如下的棋盤,游戲玩家和電腦在棋盤里面加元素,當誰填充元素使行或者列,或者對角線元素三個相同時,誰就獲勝,但當棋盤滿時也結束游戲,(其實就是井字棋,高中無聊上課玩的),棋盤如下,注意這里的 | 是兩根短的 | 拼在一起的,

此專案綜合應用了c語言中的變數的定義,輸入,輸出函式,回圈,選擇的嵌套使用,指標,二維陣列的初始化和使用等知識,希望通過閱讀這篇博文與練習代碼,提高你的代碼書寫能力及對知識的理解應用,
思路分析
-
多檔案編程拆分代碼
**隨著編程學習的深入,程式越編越龐大,可讀性降低,這使得我們撰寫程式復雜,檢查除錯程式成為一項很困難的事情,分模塊可以使初學者更好地理解程式,理解程式的思維,多檔案(模塊化)開發C程式的方法,就是把不同功能的函式封裝到不同的檔案中,多個.c檔案和一個.h檔案,c語言中主函式呼叫其他檔案中的函式實作相應的功能,**此次專案也將程式分為三個模塊,各模塊具體如下圖:

-
代碼整體思路流程圖
c語言中默認以main函式作為程式的入口,test.c檔案中有main函式,所以整個專案從test.c開始,首先列印選單(玩不玩都有的),之后呼叫game.c里面的函式,(下面圖片的7個函式列出了)之后回傳test.c列印結果

在這里代碼用到了許多的回傳值,其實這里是程式需要,下面是我寫程式,理解代碼時查閱資料對函式呼叫return 帶回傳值的一些講解
這里是參考return 表示把程式流程從被調函式轉向主調函式并把運算式的值帶回主調函式,實作函式值的回傳,回傳時可附帶一個回傳值,由return后面的引數指定, return通常是必要的,因為函式呼叫的時候計算結果通常是通過回傳值帶出的, 如果函式執行不需要回傳計算結果,也經常需要回傳一個狀態碼來表示函式執行的順利與否(-1和0就是最常用的狀態碼),主調函式可以通過回傳值判斷被調函式的執行情況,
在此次代碼中我認為game是主調函式,它呼叫的函式是被調函式,
代碼及注釋
代碼是簡單的三子棋,電腦不會智能的下棋,是隨機的,定義了#define定義的宏常量行和列可以列印n*n棋盤,但功能沒完善,待改善,代碼后面就有詳細的注釋,所以程序就不詳細的講了,難的我會單獨提出來說一下,建議復制到編譯器細細研究,
test.c檔案代碼
#define _CRT_SECURE_NO_WARNINGS
#include "game.h"//包含game.h里面的頭檔案
void menu()//列印選單
{
printf("******************************\n");
printf("****** 1. play *****\n");
printf("****** 0. exit *****\n");
printf("******************************\n");
}
void game()
{
//存盤資料 - 二維陣列
char board[ROW][COL];
//初始化棋盤 - 初始化空格
InitBoard(board, ROW, COL);
//列印一下棋盤 - 本質是列印陣列的內容
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')//不等于‘C'就是為#或者*或者Q,因為被調函式里return會回傳其中一個字符,就是說通過該字符可以判斷贏輸,平局,之后就要跳出回圈,在下面判斷游戲結果
break;//跳出回圈的關鍵
//電腦下棋
ComputerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
//判斷電腦是否贏得游戲,下一次判斷一次
ret = IsWin(board, ROW, COL);
if (ret != 'C')
break;
}
if (ret == '*')
{
printf("玩家贏了\n");
}
else if (ret == '#')
{
printf("電腦贏了\n");
}
else
{
printf("平局\n");//回傳值非*非#,為Q
}
DisplayBoard(board, ROW, COL);
}
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;
}
game.c里面的代碼
在這里插入代碼片#define _CRT_SECURE_NO_WARNINGS
#include "game.h"//包含game.h里面的頭檔案
void InitBoard(char board[ROW][COL], int row, int col)//自定義函式,前面的字母含義是:void回傳值型別,InitBoard函式名,括號里面的是形式引數
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)//ROW,COL是#define定義的識別符號常量,值為 3,3,回圈列印的9個空格元素,二維陣列的下標也是從0開始
{
board[i][j] = ' ';
}
}
}
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]);//列印棋盤空的位置,%c那用陣列初始化的空格代替,兩邊的空格是為了美觀,列印棋盤時參照圖片思考
if (j < col - 1)//為什么要減一,是因為陣列下標從0開始,最后一列不列印
printf("|");//控制最后一列的 | 不列印
}
printf("\n");
if (i < row - 1)
{
int j = 0;
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
printf("|");//控制最后一列的 | 不列印
}
printf("\n");
}
}
}
void PlayerMove(char board[][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("玩家走:>\n");
while (1)
{
printf("請輸入下棋的坐標:>");
scanf("%d %d", &x, &y);
//判斷坐標合法性,棋盤上人輸入的坐標
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
//下棋,只有坐標合法才下,不合法就提示,重新輸入,第一個if與最后一個else配對
//坐標是否被占用
if (board[x - 1][y - 1] == ' ')//二維陣列的兩個下標都是從0開始,所以要減一
{
board[x - 1][y - 1] = '*';
break;//while那里為真1一直回圈,當你在坐標上放上一個符號就跳出回圈,用戶只能下一次,不能多次,一定要有break
}
else
{
printf("坐標被占用, 請重新輸入\n");
}
}
else
{
printf("坐標非法, 請重新輸入\n");
}
}
}
void ComputerMove(char board[ROW][COL], int row, int col)
{
printf("電腦走:>\n");
while (1)
{
int x = rand() % row;//限制電腦下棋位置,取余產生0—2的數字,,呼叫rand 函式,該函式有講解外面
int y = rand() % col;
//判斷占用
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
//回圈的嵌套,遍歷陣列
int IsFull(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 0;//棋盤沒滿
}
}
}
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][1] != ' ')//判斷不為陣列初始化的空格,判斷兩兩是否相等,相等輸出其中任意一個字符
{
return board[i][1];//
}
}
//判斷三列
for (i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
{
return board[1][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];
}
//之前先判斷是否贏,沒贏再在這里判斷平局
int ret = IsFull(board, row, col);
if (ret == 1)
{
return 'Q';//通過回傳值,回傳給主調主調函式里的ret,列印游戲結果,也就是test.c所在的game函式
//一個函式可以有多個retuen 但只可以有一個回傳值,回傳了上面的*或者#就不會回傳這里的Q或者C,這里很重要
}
//繼續
return 'C';
}
v
game.h里面的代碼
#pragma once
//頭檔案的包含,test.c game.c里面用到的函式宣告
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//符號的定義,#define定義的識別符號常量,這樣可以輕松的整體改變陣列大小,可以自定義幾行幾列的棋盤,免去了之后多次改變陣列行和列
#define ROW 3
#define COL 3
//函式的宣告
//初始化棋盤的
void InitBoard(char board[ROW][COL], int row, int col);
//列印棋盤的函式
void DisplayBoard(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);
//
//1. 玩家贏了 - *
//2. 電腦贏了 - #
//3. 平局 - Q
//4. 游戲繼續 - C
//判斷游戲是否有輸贏
char IsWin(char board[ROW][COL], int row, int col);
知識補充;
在這里我們用到一個rand 函式,他的作用是產生一個0-32767的亂數,但在用它之前要條用另一個函式srand,
函式里面用time 函式作為種子,如果種子不變產生的數字也就不隨機了,函式回傳值為unsignedint,變數為空指標,因此該函式寫為scrand((unsignedint)time(NULL));
總結及注意事項
其實上面的分析說的已經夠了,其余的問題自己都可以決解的,沒有太大的注意事項,只是寫代碼時大家可以嘗試大化小,小化大,先把一個大的問題拆分成幾個小問題,分模塊,分函式,再組合再實作整體的功能,這一思想我們以后作業中可能接觸的會更多,
代碼有待改善不是很完美,文章哪里寫的有問題歡迎評論指正,有不懂得代碼評論決議,關注博主不迷路,后期更新掃雷實戰專案,記得點贊鼓勵哦!!!!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/280569.html
標籤:其他
上一篇:可以雙人玩的三子棋!
下一篇:用c語言實作和平精英!
