前言
我們通過5個簡單易懂的小游戲來加強我們對C語言的認識,這五個小游戲不僅有單人,還有人機對戰(zhàn)和人人對戰(zhàn)。讓我們在學習之余來上一局緊張刺激的小游戲吧!
一、準備工作
我們要做5個小游戲,我們要分別為5個小游戲創(chuàng)建一個頭文件和一個源文件。分別為game1.h/game1.c,game2.h/game2.c,game3.h/game3.c,game4.h/game4.c,game5.h/game5.c。這樣做的目的是把每個游戲所分開構建,方便日后對我們小游戲的重構等操作。我們還須要一個main.c,用來包含這5個小游戲的頭文件。main函數(shù)只負責調(diào)用,我們把所有小游戲代碼函數(shù)在其他文件中實現(xiàn)并用static加以修飾,使用戶使用game函數(shù)間接的調(diào)用我們小游戲的代碼。
二、游戲菜單
我們先構建main.c:
1.它需要包含五個小游戲的頭文件
2.構建我們的main函數(shù)
#include"game1.h"//包含小游戲1的頭文件
#include"game2.h"//包含小游戲2的頭文件
#include"game3.h"//包含小游戲3的頭文件
#include"game4.h"//包含小游戲4的頭文件
#include"game5.h"//包含小游戲5的頭文件
#include<time.h>
int main()
{
srand((unsigned int)time(NULL));//用來生成隨機數(shù)
do
{
int optional = 0;//創(chuàng)建一個接收選擇的變量
menu();
printf("請輸入你的選項:\n");
scanf("%d", &optional);
switch (optional)
{
case 1:
game1();
break;
case 2:
game2();
break;
case 3:
game3();
break;
case 4:
game4();
break;
case 5:
game5();
break;
case 0:
printf("感謝你的游玩,歡迎下次使用。\n");
exit(0);
default:
printf("你的選項輸入有誤,請重新輸入:\n");
break;
}
} while (1);
}
我們在main函數(shù)中用了do-while循環(huán),目的是讓用戶進行選擇,直到用戶選擇退出或者搗亂才進行退出。
scanf函數(shù)有風險,當用戶搗亂輸入字母時,會造成緩沖異常。所以我們把要接收的選擇變量放在循環(huán)中,并且賦初始值為0,當用戶輸入字母時使程序退出。
srand函數(shù)用來產(chǎn)生隨機數(shù),當我們只使用rand函數(shù)時所產(chǎn)生的隨機數(shù)會不變,會使我們重復游玩的體驗感變差。
我們在為這幾個游戲的合集創(chuàng)造一個菜單,讓用戶選擇具體游玩那個游戲。
void menu()
{
printf("*****************************\n");
printf("*****1.猜數(shù)字 2.三子棋*****\n");
printf("*****3.掃 雷 4.五子棋*****\n");
printf("*****5.飛行棋 0.退 出*****\n");
printf("*****************************\n");
}
基本菜單已經(jīng)做好了,我們進入每個小游戲的菜單。
我們先來創(chuàng)建小游戲的頭文件:
#pragma once
#include<stdio.h>
#include<stdlib.h>
void game1();
小游戲幾我們在小游戲的頭文件中就對應game幾,我以小游戲1為例,其他四個小游戲和這個差不多。
我們再為每個創(chuàng)建源文件:
#include"game1.h"
static void menu1()
{
printf("*****************************\n");
printf("***** 猜數(shù)字 *****\n");
printf("***** 1.Start the game ******\n");
printf("***** 0.Exit the game ******\n");
printf("*****************************\n");
}
void game1()
{
int optional = 0;//創(chuàng)建一個接收選擇的變量
do
{
optional = 0;
menu1();
printf("請輸入你的選項:\n");
scanf("%d", &optional);
switch (optional)
{
case 1:
//game();
break;
case 0:
printf("猜數(shù)字小游戲以退出。\n");
break;
default:
printf("你的選項輸入有誤,請重新輸入:\n");
break;
}
} while (optional);
}
還是以game1為例,其他的小游戲中的選項和game1相同,只把其中的菜單函數(shù)中的小游戲換一下名字。循環(huán)中的game函數(shù)用來調(diào)用這個小游戲中的組成函數(shù)。
我們現(xiàn)在來運行一下,來測試一下代碼是否是我們所期望的那樣
經(jīng)測試可以按照我們的思路正常進行,現(xiàn)在就讓我們正式寫出每個小游戲吧。
三、游戲內(nèi)容
1.猜數(shù)字
思路:系統(tǒng)隨機生成一個隨機數(shù),通過我們輸入的數(shù)和生成數(shù)進行比較,給予我們提示猜大了還是猜小了。
static void game()
{
int num = rand() % 100 + 1;//創(chuàng)建一個變量,用來存放隨機數(shù)。
int guess = 0;//創(chuàng)建一個變量,用來存放所猜的數(shù)字。
//rand函數(shù)是用來生成隨機數(shù)的,我們用rand函數(shù)生成的隨機數(shù)對100取模并加1,可以產(chǎn)生1~100的隨機數(shù)
while(1)
{
printf("請輸入你猜測的數(shù)字:");
scanf("%d", &guess);
if (guess == num)
{
printf("恭喜你,猜對了!\n");
break;
}
else if (guess > num)
{
printf("你猜的數(shù)字過大,請重新猜測吧!\n");
}
else if (guess < num)
{
printf("你猜的數(shù)字過小,請重新猜測吧!\n");
}
}
return;
}
我們簡單的運行一下吧!
可以正常運行,且我們可以重復的游玩。
2.三子棋
思路:用數(shù)組存儲我們的棋子,并判斷是否獲勝。
1.打印棋盤
2.玩家進行下棋
3.判斷玩家是否獲勝
4.電腦進行下棋
5.判斷電腦是否獲勝
我們先定義兩個宏:用來決定我們可以設置的棋盤大小。
#define ROW 3 //行
#define COL 3 //列
我們開始構建game函數(shù),看看里面需要什么模塊來支持游戲思路:
static void game()
{
char win;//創(chuàng)建一個變量,存放判段獲勝條件的字符。
//我們把C代表繼續(xù),D代表平局,*代表玩家獲勝,#代表電腦獲勝
char checkerboard[ROW][COL] = { 0 };//創(chuàng)建一個數(shù)組,用來存放棋盤信息
initialization(checkerboard,ROW,COL);//初始化數(shù)組,把數(shù)組中的每個元素初始化為空格
ptintinitialization(checkerboard, ROW, COL);//打印棋盤
while(1)
{
Player(checkerboard, ROW, COL, '*');//玩家下棋,玩家的標志為 *
win = Iswin(checkerboard, ROW, COL);//判斷是否獲勝
if (win != 'C')
{
break;
}
ptintinitialization(checkerboard, ROW, COL);//打印棋盤
Computer(checkerboard, ROW, COL, '#');//電腦下棋,電腦的標志為 #
win = Iswin(checkerboard, ROW, COL);
if (win != 'C')
{
break;
}
ptintinitialization(checkerboard, ROW, COL);//打印棋盤
}
if (win == 'D')
{
printf("平局\n");
ptintinitialization(checkerboard, ROW, COL);
}
else if (win == '*')
{
printf("恭喜你獲得勝利\n");
ptintinitialization(checkerboard, ROW, COL);
}
else
{
printf("很遺憾,你輸?shù)袅吮荣怽n");
ptintinitialization(checkerboard, ROW, COL);
}
}
我們先創(chuàng)建我們所用到的數(shù)組和一個可以判斷輸贏的變量,我們把數(shù)組進行初始化,并且進行打印,讓玩家知道可以下棋的位置,通過玩家下棋函數(shù)和電腦下棋函數(shù)來實現(xiàn)人機對戰(zhàn)。
初始化數(shù)組函數(shù):
static void initialization(char arr[ROW][COL],int row,int col)//初始化數(shù)組
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
arr[i][j] = ' ';//把數(shù)組中的每個元素賦值為空格
}
}
}
打印棋盤函數(shù):
static void ptintinitialization(char arr[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 (j < col - 1)
{
printf(" %c |", arr[i][j]);//打印棋盤,如果為列中的最后一個元素,則不打印|
}
else
{
printf(" %c ", arr[i][j]);
}
}
printf("\n");
if(i < row-1)//打印棋盤,如果為行中的最后一個元素,則不打印---
{
for (j = 0; j < col; j++)
{
if (j < col)
{
printf("--- ");
}
}
}
printf("\n");
}
}
打印的效果如圖所示。
當我們把宏改為:
#define ROW 9 //行
#define COL 9 //列
我們可以看到打印的棋盤擴大了。這是宏的好處,不需要我們在每個函數(shù)的改數(shù)值。只需要改宏定義的數(shù)值,就可以改變我們棋盤的大小。
玩家下棋函數(shù):
static void Player(char arr[ROW][COL], int row, int col, char ch)//玩家下棋函數(shù)
{
int x = 0;
int y = 0;
while(1)
{
printf("請輸入你要下棋的坐標:");
scanf("%d %d", &x, &y);
if (x<1 || x>row || y<1 || y>col)//判斷坐標是否合法
{
printf("你輸入的坐標不合法,請重新輸入。\n");
}
else if (arr[x - 1][y - 1] != ' ')//判斷輸入坐標是否被占用
{
printf("你輸入的坐標已被占用,請重新輸入。\n");
}
else
{
arr[x - 1][y - 1] = ch;//數(shù)組下標從0開始,所以玩家的真實坐標要進行減1
break;
}
}
}
電腦下棋函數(shù):
static void Computer(char arr[ROW][COL], int row, int col, char ch)
{
while (1)
{
int x = rand() % 3;//產(chǎn)生0~2的數(shù)字
int y = rand() % 3;
if (arr[x][y] == ' ')//如果坐標未被占用,則電腦下棋,否則產(chǎn)生新的坐標進行判斷
{
arr[x][y] = ch;//電腦的坐標的下標和數(shù)組下標對應,不需要進行減1操作
break;
}
}
}
判斷是否獲勝函數(shù):
static char Iswin(char arr[ROW][COL], int row, int col)//判斷是否獲勝
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)//判斷每一排中是否有人獲勝
{
if (arr[i][0] == arr[i][1] && arr[i][1] == arr[i][2] && arr[i][1] != ' ')
{
return arr[i][1];
}
}
for (i = 0; i < col; i++)//判斷每一列中是否有人獲勝
{
if (arr[0][i] == arr[1][i] && arr[1][i] == arr[2][i] && arr[1][i] != ' ')
{
return arr[1][i];
}
}
if ((arr[0][0] == arr[1][1] && arr[1][1] == arr[2][2] && arr[1][1] != ' ') ||
(arr[0][2] == arr[1][1] && arr[1][1] == arr[2][0] && arr[1][1] != ' '))//判斷對角線上是否有人獲勝
{
return arr[1][1];
}
for (i = 0; i < row; i++)
{
for (j = 0; i < col; i++)
{
if (arr[i][j] == ' ');//判斷是否還有空位
{
return 'C';
}
}
}
return 'D';
}
讓我們運行一下看看結果吧!
注意:
1.我們所使用的scanf是不安全的,大家可以查找一下對應的解決辦法,了解一下為什么不安全。
2.對所用的數(shù)組進行初始話是要注意間隔,數(shù)組傳參是注意形參和實參的類型是否一致,我們使用的是char類型數(shù)組,如果形參用int接受會造成什么情況呢?大家可以下去試一試。
3.大家可以對這個代碼進行重構,可以實現(xiàn)人人對戰(zhàn)。在這里只顯示了人機對戰(zhàn),重構一下加入人人對戰(zhàn)吧。
如果形參用int接受會造成數(shù)組越界,造成棧錯誤。
3.掃雷
思路:用數(shù)組存儲雷的信息,通過排查,找到?jīng)]有雷的地方。
1.打印地圖
2.玩家進行掃雷
3.判斷玩家是否踩雷
4.更新地圖
分析:
在這里插入圖片描述
上圖是一個9*9的掃雷地圖,當我們要判斷紅色的地方,對周邊的判斷勢必會造成越界訪問。如何解決這個問題呢?
我們把這個數(shù)組上下左右個擴充一行,這樣進行判斷就不會產(chǎn)生數(shù)組越界了。打印數(shù)組的問題我們通過代碼來分析吧。
我們設置兩個數(shù)組,一個用來存儲地圖信息,一個用來向玩家展示,向玩家展示的數(shù)組需要通過存儲地圖信息的數(shù)組來判斷地圖上的安全地方。
我們先定義五個宏:分別決定我們可以設置的地圖大小和我們打印地圖的大小,最后一個宏來存放我們地雷的個數(shù),后期也方便我們進行測試。
#define ROW 9 //顯示的行
#define COL 9 //顯示的列
#define ROWS ROW+2 //真實數(shù)組的行
#define COLS COL+2 //真實數(shù)組的列
#define MINENUM 10 //地雷的個數(shù)
然后對我們的game函數(shù)進行補充,來看看具體的思路吧:
static void game()
{
int winnum = ROW * COL - MINENUM;//設置一個變量,用來存放還有多少個安全的地方
char minemap[ROWS][COLS] = { 0 };//雷分布的地圖
char showmap[ROWS][COLS] = { 0 };//向玩家展示的地圖
Initialization(minemap, showmap, ROWS, COLS);//初始化數(shù)組,把雷分布隨機分布在地圖中,并且把向玩家展示的地圖全部替換為 *
//Ptintinitialization(minemap, ROW, COL);//測試代碼,用來打印地圖
while (winnum)
{
int x = 0;
int y = 0;
Ptintinitialization(showmap, ROW, COL);//打印展示地圖
printf("請輸入排雷坐標:");
scanf("%d %d", &x, &y);
if (x<1 || x>ROW || y<1 || y>COL)//判斷坐標是否合法
{
printf("你輸入的坐標不合法,請重新輸入。\n");
}
else if (showmap[x][y] != '*')//判斷坐標是否已被排查過
{
printf("你輸入的坐標已被排過雷了,請重新輸入。\n");
}
else
{
if (!Determine(minemap, x, y))//!Determine(minemap, x, y)用來判斷是否踩到雷
{
Ptintinitialization(minemap, ROW, COL);//打印地雷地圖,讓玩家知道地雷在哪里。
break;
}
Renewmap(minemap, showmap, x, y);//用來更新向玩家展示的地圖
winnum--;//對安全的地方進行減1
}
}
if(winnum == 0)//當安全地方為0時,證明排雷成功
{
printf("恭喜你排雷成功!\n");
Ptintinitialization(showmap, ROW, COL);//打印展示地圖,讓玩家知道自己獲勝地圖
}
}
初始化數(shù)組函數(shù):
static void Initialization(char minemap[ROWS][COLS], char showmap[ROWS][COLS], int rows, int cols)
{
int count = MINENUM;//創(chuàng)建一個變量,用來計算放置的雷的數(shù)量
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)//把地雷地圖全部賦值為字符0
{
for (j = 0; j < cols; j++)
{
minemap[i][j] = '0';
}
}
while (count)//把雷分布隨機分布在地雷地圖中
//我們把0代表安全,1代表雷區(qū)
{
int x = rand() % ROW + 1;//創(chuàng)建隨機變量范圍在1~9
int y = rand() % COL + 1;//創(chuàng)建隨機變量范圍在1~9
if (minemap[x][y] == '0')//判斷該位置是否已有雷
{
minemap[x][y] = '1';//放置地雷
count--;
}
}
for (i = 0; i < rows; i++)//把展示地圖全部賦值為字符*
{
for (j = 0; j < cols; j++)
{
showmap[i][j] = '*';
}
}
}
我們所展示的地圖行為1 ~ ROW,列為1 ~ COL,所以把生成的雷的坐標控制在這個范圍之間。
打印地圖函數(shù):
static void Ptintinitialization(char showmap[ROWS][COLS], int row, int col)//打印地圖
{
int i = 1;
int j = 1;
for (i = 1; i <= row; i++)
{
if (i == 1)//當i為1時,需要多一個空格,目的為了和地圖對齊
{
printf(" ");
}
printf(" %d", i);
}
printf("\n");
for (i = 1; i <= row; i++)//打印我們所需要的地圖
{
printf("%d", i);
for (j = 1; j <= col; j++)
{
printf(" %c", showmap[i][j]);
}
printf("\n");
}
}
我們從1開始打印,打印到ROW處,只打印我們所需要的地方。
判斷是否踩到雷:
static bool Determine(char minemap[ROWS][COLS],int x,int y)
{
if (minemap[x][y] == '1')
{
printf("排雷失敗,你已死亡!\n");
return false;
}
else
{
return true;
}
}
更新展示的地圖:
static void Renewmap(char minemap[ROWS][COLS], char showmap[ROWS][COLS], int x, int y)
{
showmap[x][y] = (minemap[x - 1][y - 1] + minemap[x - 1][y] + minemap[x - 1][y + 1]
+ minemap[x][y - 1] + minemap[x][y + 1]
+ minemap[x + 1][y - 1] + minemap[x + 1][y] + minemap[x + 1][y + 1]) - 8*'0' + '0';
}
更新向玩家展示的地圖,把周圍的地雷數(shù)相加,并用相應的數(shù)字代替。我們用的是字符數(shù)組,里面存放的是字符1和字符0,所以要減去0的ASCLL碼值可以得到整形數(shù)字,把他們相加代表這個方格周圍8個地方的地雷數(shù),最后加上0的ASCLL碼值得到對應的字符數(shù)字。
讓我們測試一下:我們把測試代碼解除注釋,并把我們宏定義的地雷個數(shù)設置的大一點,我先把它設置為79,真正游玩時可以根據(jù)想要玩的難度進行更改
我們看看排雷失敗會不會正常顯示:
排雷失敗也會正常顯示。
注意:
1.scanf是不安全的
2.對所用的數(shù)組進行初始話是要注意間隔,對數(shù)組進行訪問時要注意是否越界。
3.重構思路:可以加入展開地圖(周圍8個地方?jīng)]有地雷時可以展開,知道一個格子周圍有地雷為止),加入小旗(把認為是地雷的地方用小旗替換,只有取消小旗改位置才可以進行掃雷),加入時間(當時間終止時未排完所有雷則游戲結束)等。
4.五子棋
思路:判斷那個玩家先進行了五子連珠(一行或一列或者對角線有連續(xù)的5顆一樣的棋子)
1.打印棋盤
2.玩家1進行下棋
3.判斷玩家1是否獲勝
4.玩家2進行下棋
5.判斷玩家2是否獲勝
我們還是先用宏定義棋盤大?。?/p>
#define ROW 9 //棋盤的行
#define COL 9 //棋盤的列
再來看game思路:
static void game()
{
char win;//創(chuàng)建一個變量,存放判段獲勝條件的字符。
//我們把C代表繼續(xù),D代表平局,白棋(@)代表玩家1獲勝,黑棋(O)代表玩家2獲勝
char checkerboard[ROW][COL] = { 0 };//棋盤數(shù)組
Initialization(checkerboard, ROW, COL);//初始化數(shù)組,把棋盤的所有位置都賦值為 *
Ptintinitialization(checkerboard, ROW, COL);//打印棋盤
while (1)
{
Player(checkerboard, ROW, COL, '@');//玩家1下棋,玩家1的標志為 @
win = Iswin(checkerboard, ROW, COL);//判斷是否獲勝
if (win != 'C')
{
break;
}
Ptintinitialization(checkerboard, ROW, COL);//打印棋盤
Player(checkerboard, ROW, COL, 'O');//玩家2下棋,玩家2的標志為 O
win = Iswin(checkerboard, ROW, COL);//判斷是否獲勝
if (win != 'C')
{
break;
}
Ptintinitialization(checkerboard, ROW, COL);//打印棋盤
}
if (win == 'D')
{
printf("平局\n");
Ptintinitialization(checkerboard, ROW, COL);
}
else if (win == '@')
{
printf("玩家1獲勝\n");
Ptintinitialization(checkerboard, ROW, COL);
}
else
{
printf("玩家2獲勝\n");
Ptintinitialization(checkerboard, ROW, COL);
}
}
五子棋的思路和三子棋的思路一模一樣,只是把電腦換成了另一個玩家。所以game函數(shù)代碼基本沒有太大變化。而玩家下棋思路一致,所以我們只需要一個函數(shù)就可以解決兩個玩家下棋的思路。
初始化數(shù)組函數(shù):
static void Initialization(char arr[ROW][COL], int row, int col)//初始化數(shù)組,把棋盤的所有位置都賦值為 *
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)//把展示地圖全部賦值為字符*
{
for (j = 0; j < col; j++)
{
arr[i][j] = '*';
}
}
}
對數(shù)組進行初始化和掃雷中向玩家展示的地圖的思路一模一樣。
打印地圖函數(shù):
static void Ptintinitialization(char arr[ROW][COL], int row, int col)//打印棋盤
{
int i = 0;
int j = 0;
for (i = 1; i <= row; i++)
{
if (i == 1)//當i為1時,需要多兩個空格,目的為了和地圖對齊
{
printf(" ");
}
if (i < 9)//當i小于9時,需要多一個個空格,目的為了和地圖對齊
{
printf(" %d ", i);
}
else//當i大于于9時,不需要多一個個空格
{
printf(" %d", i);
}
}
printf("\n");
for (i = 0; i < row; i++)//打印我們所需要的地圖
{
printf("%2d", i + 1);
for (j = 0; j < col; j++)
{
printf(" %c ", arr[i][j]);//需要多一個個空格,目的為了和地圖對齊
}
printf("\n");
}
}
五子棋的地圖可以隨便放大縮小,一定要注意地圖是否和上面的行和列進行了對齊。
玩家下棋函數(shù):
int x = 0;//用來接收玩家下棋的坐標
int y = 0;//用來接收玩家下棋的坐標
static void Player(char arr[ROW][COL], int row, int col, char ch)//玩家下棋函數(shù),通過ch來判斷是玩家1還是玩家2
{
int player = 0;//創(chuàng)建一個變量來確定是玩家?guī)?/span>
if (ch == '@')//如果ch為白棋(@),則代表玩家1
{
player = 1;
}
else//反之則為玩家2
{
player = 2;
}
while (1)
{
printf("請玩家 %d 輸入要下棋的坐標:",player);
scanf("%d %d", &x, &y);
if (x<1 || x>row || y<1 || y>col)//判斷坐標是否合法
{
printf("你輸入的坐標不合法,請重新輸入。\n");
}
else if (arr[x - 1][y - 1] != '*')//判斷輸入坐標是否被占用
{
printf("你輸入的坐標已被占用,請重新輸入。\n");
}
else
{
arr[x - 1][y - 1] = ch;//數(shù)組下標從0開始,所以玩家的真實坐標要進行減1
break;
}
}
}
我們把玩家下棋的坐標設置為全局變量,方便我們判斷是否是五子連珠的情況。
判斷是否獲勝函數(shù):
enum Direction
{
LEFT,
RIGHT,
UP,
DOWN,
LEFTUP,
RIGHTDOWN,
RIGHTUP,
LEFTDOWN
};
static char Iswin(char arr[ROW][COL], int row, int col)//判斷是否獲勝
{
static chessnum = 0;//創(chuàng)建一個靜態(tài)變量,用來存放棋盤中使用的格數(shù)
chessnum++;//每進行一次判斷調(diào)用就對棋盤使用格數(shù)進行+1
int winnum1 = 1 + Wincount(arr, ROW, COL, LEFT) + Wincount(arr, ROW, COL, RIGHT);
//確定左和右共計多少相同個棋子,需要加上自身的棋子
int winnum2 = 1 + Wincount(arr, ROW, COL, UP) + Wincount(arr, ROW, COL, DOWN);
//確定上和下共計多少相同個棋子,需要加上自身的棋子
int winnum3 = 1 + Wincount(arr, ROW, COL, LEFTUP) + Wincount(arr, ROW, COL, RIGHTDOWN);
//確定左上和右下共計多少相同個棋子,需要加上自身的棋子
int winnum4 = 1 + Wincount(arr, ROW, COL, RIGHTUP) + Wincount(arr, ROW, COL, LEFTDOWN);
//確定右上和左下共計多少相同個棋子,需要加上自身的棋子
if (winnum1 >= 5 || winnum2 >= 5 || winnum3 >= 5 || winnum4 >= 5)//判斷是否獲勝
{
return arr[x - 1][y - 1];//返回獲勝棋子的字符
}
else
{
if (chessnum == ROW * COL)//如果棋盤使用格數(shù)等于棋盤的格數(shù),證明平局
{
return 'D';
}
else//如果棋盤使用格數(shù)不等于棋盤的格數(shù),證明還有空位,繼續(xù)進行比賽
{
return 'C';
}
}
}
我們需要統(tǒng)計這8個方向的棋子相同個數(shù)。不會產(chǎn)生5個連著 * 的情況,我們保存的地址永遠是上一個玩家落子的地址。我們把8個方向用menu類型進行表示。
static int Wincount(char arr[ROW][COL], int row, int col, enum Direction dir)
{
int count = 0;//記錄相同棋子個數(shù)
int _x = x - 1;//要對坐標進行減1,因為我們的棋盤從1開始,數(shù)組下標從0開始
int _y = y - 1;//要對坐標進行減1,因為我們的棋盤從1開始,數(shù)組下標從0開始
//用局部變量保存,避免在這里修改全局變量的值
while (1)
{
switch (dir)
{
case LEFT:
_y--;//對中心坐標的列坐標進行減1
break;
case RIGHT:
_y++;//對中心坐標的列坐標進行加1
break;
case UP:
_x++;
break;
case DOWN:
_x--;
break;
case LEFTUP:
_y--;
_x++;
break;
case RIGHTDOWN:
_y++;
_x--;
break;
case RIGHTUP:
_y++;
_x++;
break;
case LEFTDOWN:
_y--;
_x--;
break;
default:
break;
}
if (_x < 0 || _x > ROW || _y < 0 || _y>COL)//判斷位置是否合法
{
return count;
}
else if(arr[_x][_y] != arr[x-1][y-1])//判斷是否和上一個玩家的棋子相同
{
return count;
}
else
{
count++;
}
}
}
我們設置為死循環(huán),用來判斷相同方向中具體棋子個數(shù)。menu類型更具我們傳的參數(shù)不同,在switch分支中走不同的路線。
注意:
數(shù)組的越界。
對五子連珠的判斷
重構思路:可以加入網(wǎng)絡,實現(xiàn)網(wǎng)路對戰(zhàn),可以加入悔棋功能等
5.飛行棋
玩法:1.除了起始位置是一樣的,其余時間當一名玩家位置和另一名玩家位置相同時發(fā)生踩踏。
2.當玩家踩到道具時,進行相應的道具操作
3.那個玩家先到終點即為獲勝
思路:
1.用數(shù)組來存儲地圖信息
2.用一個結構體來構造玩家信息
3.用一個結構體數(shù)組來存儲玩家信息
4.用隨機函數(shù)模擬篩子操作
我們先對game思路進行構造:
int map[100];//創(chuàng)建一個地圖大小為100的地圖。并且把地圖元素全部初始化為0
//為了對數(shù)組進行方便的操作,我們把數(shù)組設置為全局變量,使每個函數(shù)可以直接訪問數(shù)組
static void game()
{
struct Player play[2];//創(chuàng)建一個玩家數(shù)組,里面存放玩家信息
Initializationmap();//初始化地圖數(shù)組,把地圖相應位置填上道具元素,
//我們把地圖數(shù)組設置為了全局函數(shù),所以不要需要傳參
Initializationplay(play);//初始化玩家數(shù)組,加入玩家信息,并把玩家位置置于地圖開始位置
Ptintinitialization(play);//打印棋盤,地圖數(shù)組為全局函數(shù),所以不要對地圖傳參。
//需要玩家信息來判斷地圖要填的字符,所以要對玩家進行傳參
while (1)//使玩家交替進行游玩,直到產(chǎn)生勝利者
{
if (play[0].flags == false)//判斷玩家1是否處于暫停回合。
{
Play(&play[0], &play[1],play);//玩家1進行游戲
Ptintinitialization(play);//打印棋盤
}
else//處于暫?;睾?,把暫停回合改為繼續(xù)
{
play[0].flags = false;
}
if (play[0].position >= 99)//判斷玩家1是否獲勝
{
printf("%s僥幸贏了%s\n", play[0].name, play[1].name);
break;
}
if (play[1].flags == false)//判斷玩家2是否處于暫?;睾?。
{
Play(&play[1], &play[0],play);//玩家2進行游戲
Ptintinitialization(play);//打印棋盤
}
else
{
play[1].flags = false;//更改暫停選項
}
if (play[1].position >= 99)//判斷玩家2是否獲勝
{
printf("%s僥幸贏了%s\n", play[1].name, play[0].name);
break;
}
}
}
我們?yōu)榱朔奖惆训貓D數(shù)組設置位全局數(shù)組,這樣減少傳參。并且數(shù)組在初始化后是不需要改變的。我們另外設置了一個結構體數(shù)組用來存放玩家信息(位置,是否處于暫停位)。
初始化地圖函數(shù):
static void Initializationmap()//初始化地圖數(shù)組
//地圖數(shù)組中
//0代表什么都沒有
//1代表道具1(幸運輪盤),2代表道具2(地雷),3代表道具3(暫停),4代表道具4(時空隧道)
{
int luckyturn[] = {1, 20, 45, 60, 75, 90};//在相應的位置放入 幸運輪盤
for (int i = 0; i < sizeof(luckyturn) / sizeof(luckyturn[0]); i++)
//sizeof(luckyturn) / sizeof(luckyturn[0])用來求出數(shù)組元素的個數(shù)
{
map[luckyturn[i]] = 1;
}
int Landmine[] = { 3, 6, 19, 25, 36, 49, 69, 70, 80 };//在相應的位置放入 地雷
for (int i = 0; i < sizeof(Landmine) / sizeof(Landmine[0]); i++)
{
map[Landmine[i]] = 2;
}
int puse[] = { 2, 11, 26, 35, 44, 59, 71, 88 };//在相應的位置放入 暫停
for (int i = 0; i < sizeof(puse) / sizeof(puse[0]); i++)
{
map[puse[i]] = 3;
}
int timetunnel[] = { 5, 15, 30, 50, 77 };//在相應的位置放入 時空隧道
for (int i = 0; i < sizeof(timetunnel) / sizeof(timetunnel[0]); i++)
{
map[timetunnel[i]] = 4;
}
}
初始化地圖操作使我們在想要的位置放置合適的道具,道具的位置可以根據(jù)自己的喜歡而改變。
初始化玩家函數(shù):
static void Initializationplay(struct Player *play)
{
printf("請輸入玩家A的姓名\n");
scanf("%s", &(play[0].name));
while (!strcmp(play[0].name,""))//判斷玩家A的姓名是否為空
{
printf("玩家姓名不能為空,請重新輸入玩家A的姓名\n");
scanf("%s", &(play[0].name));
}
printf("請輸入玩家B的姓名\n");
scanf("%s", &(play[1].name));
while (1)
{
if (!strcmp(play[1].name, ""))//判斷玩家B的姓名是否為空
{
printf("玩家姓名不能為空,請重新輸入玩家B的姓名\n");
scanf("%s", &(play[1].name));
}
else if(!strcmp(play[1].name, play[0].name))//判斷玩家B的姓名和玩家A是否相同
{
printf("玩家姓名不能一致,請重新輸入玩家B的姓名\n");
scanf("%s", &(play[1].name));
}
else
{
break;
}
}
play[0].position = 0;//把玩家A的位置置于0位置(地圖開始位置)
play[0].flags = false;//把玩家A的暫停條件置為假。
play[1].position = 0;//把玩家B的位置置于0位置(地圖開始位置)
play[1].flags = false;//把玩家B的暫停條件置為假。
}
在這個函數(shù)中我們進行了字符串比較,字符串比較不可以直接用 ==,必須要用strcmp函數(shù)
展示地圖函數(shù):
extern char Drawstrmap(const struct Player* play, int i);//對Drawstrmap函數(shù)進行聲明
static void Ptintinitialization(const struct Player* play) //打印棋盤
//由于不會對玩家信息進行更改,我們把參數(shù)設置為const,防止在函數(shù)中誤改
{
printf("圖例:幸運輪盤:# 地雷:@ 暫停:I 時空隧道:> \n");//向玩家展示道具信息
//第一段
for (int i = 0; i < 30; i++)
{
printf("%c ", Drawstrmap(play, i));
}
printf("\n");
//第一豎列
for (int i = 30; i < 35; i++)
{
for (int j = 0; j <= 28; j++)
{
printf(" ");
}
printf("%c ", Drawstrmap(play, i));
printf("\n");
}
//第二段
for (int i = 64; i >= 35; i--)//地圖為從前向后打打印
{
printf("%c ", Drawstrmap(play, i));
}
printf("\n");
//第二數(shù)列
for (int i = 65; i <= 69; i++)
{
printf("%c ", Drawstrmap(play, i));
printf("\n");
}
//第三行豎列
for (int i = 70; i <= 99; i++)
{
printf("%c ", Drawstrmap(play, i));
}
printf("\n");//畫完地圖換行
}
static char Drawstrmap(const struct Player* play, int i)//打印地圖元素
{
char ch;
if (play[0].position == play[1].position && play[0].position == i)
//當玩家1和玩家2的位置處于起始位置時
{
ch = 'M';
}
else if (play[0].position == i)//當玩家1位于當前位置時
{
ch = 'A';
}
else if (play[1].position == i)//當玩家2位于當前位置時
{
ch = 'B';
}
else
{
switch (map[i])
{
case 0://地圖數(shù)組元素為0時
ch = 'O';
break;
case 1:
ch = '#';
break;
case 2:
ch = '@';
break;
case 3:
ch = 'I';
break;
case 4:
ch = '>';
break;
}
}
return ch;
}
這里我們構造了一個Drawstrmap函數(shù),目的是把地圖數(shù)組中的整形元素構造為我們所需要的字符元素,我們傳入地圖的位置,來判斷具體打印什么字符,且不對我們地圖數(shù)組進行更改。
游玩函數(shù):
static void Play(struct Player *play1, struct Player* play2, struct Player* play)
//play1為當前玩家,play2為另一名玩家,play為玩家數(shù)組,傳玩家數(shù)組方便進行位置判斷
{
int points = rand() % 6 + 1;//設置隨機變量,范圍為1~6
printf("%s請按任意鍵開始致篩子\n", play1->name);
system("pause");//暫停屏幕
printf("%s篩到了%d,前進%d格,", play1->name, points, points);
system("pause");
play1->position += points;//對玩家位置進行更新
Weizhi(play);//判斷玩家位置是否在合法的范圍內(nèi)
if (play1->position == play2->position)//現(xiàn)在玩家的位置和另一名玩家位置相同時
{
printf("%s踩到了%s,%s退6格,", play1->name, play2->name, play2->name);
system("pause");
play2->position -= 6;//對另一名玩家位置進行更新
Weizhi(play);//判斷玩家位置是否在合法的范圍內(nèi)
printf("%s已退6格,", play2->name);
system("pause");
}
else
{
switch (map[play1->position])//檢查本位玩家是否進行到道具位置
{
case 0:
printf("%s踩到了方塊,安全,", play1->name);
system("pause");
break;
case 1:
{
printf("%s踩到了幸運轉(zhuǎn)盤,1————交換位置,2————轟炸對方,請選擇按鍵\n", play1->name);
int a = 0;//用來接收用戶的選擇
scanf("%d", &a);
while (1)
{
if (a == 1)
{
printf("%s選擇了交換位置,", play1->name);
system("pause");
int b = play1->position;
play1->position = play2->position;
play2->position = b;
Weizhi(play);//判斷玩家位置是否在合法的范圍內(nèi)
printf("交換完成,");
system("pause");
break;
}
else if (a == 2)
{
printf("%s選擇了轟炸對方,%s向后退6格,", play1->name, play2->name);
system("pause");
play2->position -= 6;
Weizhi(play);
printf("執(zhí)行成功,\n");
system("pause");
break;
}
else
{
printf("輸入不正確,請重新輸入");
int ch;
while ((ch = fgetc(stdin)) != EOF && ch != '\n');
//清除緩沖區(qū),防止緩沖區(qū)問題使程序不能正常進行
scanf("%d", &a);
}
}
}
break;
case 2:
printf("%s踩到了地雷,退6格,", play1->name);
system("pause");
play1->position -= 6;
Weizhi(play);
break;
case 3:
printf("%s踩到了暫停,下一回合禁止行動", play1->name);
system("pause");
play1->flags = true;
break;
case 4:
printf("%s踩到了時空隧道,前進十格", play1->name);
system("pause");
play1->position += 10;
Weizhi(play);
break;
}
}
Weizhi(play);
system("cls");//清除屏幕
}
我們在游玩函數(shù)中來進行篩子的模擬和道具的使用。我們設置清除緩沖區(qū)是為了解決scanf函數(shù)緩沖區(qū)問題,避免出現(xiàn)死循環(huán)。
注意:
數(shù)組的越界。我們要確保每個玩家的位置處于地圖中。
字符串的比較
重構思路:1.可以加入網(wǎng)絡,實現(xiàn)網(wǎng)路對戰(zhàn),2.把幸運輪盤單獨封裝為一個函數(shù)。3.加入更多道具。4.加入其他玩家。5可以加入積分,用積分換取道具。文章來源:http://www.zghlxwxcb.cn/news/detail-520686.html
總結
我們的五個小游戲已經(jīng)全部做完。下面是做小游戲要注意的點:
scanf函數(shù)是不安全,我們可以尋找一個安全的函數(shù)進行替代,或者解決scanf的安全問題
謹防數(shù)組越界,我們好幾個小游戲都用了數(shù)組進行存儲,有字符型數(shù)組,還有整形數(shù)組,要謹防數(shù)組越界,注意數(shù)組傳參。
把經(jīng)常使用的代碼構造為函數(shù),減少代碼冗余。文章來源地址http://www.zghlxwxcb.cn/news/detail-520686.html
到了這里,關于詳解5個C語言簡單易懂小游戲的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!