使用C語(yǔ)言實(shí)現(xiàn)五子棋游戲
本文實(shí)例為大家分享了C語(yǔ)言實(shí)現(xiàn)五子棋游戲的具體代碼,供大家參考,具體內(nèi)容如下
一、實(shí)現(xiàn)的目的和意義
1、鞏固和加深對(duì)c語(yǔ)言知識(shí)的理解
2、學(xué)會(huì)使用編譯器的各種調(diào)試
3、提高解決實(shí)際問題的能力
二、實(shí)現(xiàn)內(nèi)容描述
實(shí)現(xiàn)簡(jiǎn)單的人人對(duì)戰(zhàn)五子棋,此設(shè)計(jì)用的是C語(yǔ)言去實(shí)現(xiàn)
三、實(shí)現(xiàn)原理
采用二位數(shù)組保存棋盤信息,棋盤上面的任何一個(gè)位置,里頭可以放三個(gè)信息:
1、空
2、用戶1的落子
3、用戶2的落子
下棋就是在二維數(shù)組種找對(duì)應(yīng)的空位置,進(jìn)行落子,落完之后立即就要判斷落子位置是否有五字連珠,從而判斷誰(shuí)數(shù)輸誰(shuí)贏。每走一次棋會(huì)有四種情況:
*用戶1贏
*用戶2贏
*平局(這里說的平局是棋盤被占滿的情況)
*沒有出結(jié)果 意思就是時(shí)此用戶沒贏 下個(gè)用繼續(xù)去下棋
四、實(shí)現(xiàn)模塊五子棋實(shí)現(xiàn)分為三大模塊
文件名 作用
five_chress.h 五子棋的函數(shù)聲明,頭文件聲明等
five_chress.c 五子棋函數(shù)接口的實(shí)現(xiàn)
main.c 五子棋函數(shù)測(cè)試功能
五、模塊代碼分析
1、five_chress.h
#pragma once #include <stdio.h> #include <string.h> #include<Windows.h> #define ROW 10//數(shù)組號(hào)行號(hào) 按需求調(diào)整 #define COL 10//數(shù)組列數(shù) 按需求調(diào)整 #define PLAYER1 1//玩家編號(hào),默認(rèn)棋盤數(shù)據(jù)是0,玩家1落子,該位置被改成1 #define PLAYER2 2//玩家編號(hào),默認(rèn)棋盤數(shù)據(jù)是0,玩家2落子,該位置被改成2 #define NEXT ? ? ? ?0//游戲繼續(xù) #define PLAYER1_WIN 1//玩家1贏 #define PLAYER2_WIN 2//玩家2贏 #define DRAW ? ? ? ?3//平局 ? ?enum Dir{ ?? ?LEFT, ?? ?RIGHT, ?? ?UP, ?? ?DOWN, ?? ?LEFT_UP, ?? ?LEFT_DOWN, ?? ?RIGHT_UP, ?? ?RIGHT_DOWN ? ? };//用枚舉去表示4個(gè)方向,上下,左右,左上右下,右上左下,統(tǒng)計(jì)用戶當(dāng)前棋子四個(gè)方向的棋數(shù)是否為大于等于5 ? ? void Menu();//實(shí)現(xiàn)菜單 ? ? void Game();//構(gòu)建游戲入口Game()函數(shù) ? ? ?
2、five_chress.c
#include "five_chress.h" int x = 0; int y = 0; void Menu() { ?? ?printf("############################\n"); ?? ?printf("## 1. Play ? ? ? ?0. Exit ##\n"); ?? ?printf("############################\n"); ?? ?printf("Please Select# "); } //按照x,y作為起點(diǎn),按照特定的方向,求連續(xù)相對(duì)的最大格式 int ChessCount(int board[][COL], int row, int col, enum Dir d) { ?? ?int _x = x - 1; //從1 ?? ?int _y = y - 1; //從1 ?? ?int count = 0; ?? ?while (1){ ?? ??? ?switch (d){ ?? ??? ?case LEFT://從當(dāng)前位置向左移動(dòng)統(tǒng)計(jì) 橫坐標(biāo)不變 縱坐標(biāo)變了 所以是y-- ?? ??? ??? ?_y--; ?? ??? ??? ?break; ?? ??? ?case RIGHT://從當(dāng)前位置向右移動(dòng)統(tǒng)計(jì) 橫坐標(biāo)不變 縱坐標(biāo)變了 所以是_y++ ?? ??? ??? ?_y++; ?? ??? ??? ?break; ?? ??? ?case UP://從當(dāng)前位置向上移動(dòng)統(tǒng)計(jì) 縱坐標(biāo)不變 橫坐標(biāo)變了 所以是_x-- ?? ??? ??? ?_x--; ?? ??? ??? ?break; ?? ??? ?case DOWN://從當(dāng)前位置向下移動(dòng)統(tǒng)計(jì) 縱坐標(biāo)不變 橫坐標(biāo)變了 所以是_x++ ?? ??? ??? ?_x++; ?? ??? ??? ?break; ?? ??? ?case LEFT_UP://從當(dāng)前位置向左上移動(dòng)統(tǒng)計(jì) 縱,橫變都變了 所以是_x--,_y-- ?? ??? ??? ?_x--, _y--; ?? ??? ??? ?break; ?? ??? ?case LEFT_DOWN://從當(dāng)前位置左下移動(dòng)統(tǒng)計(jì) 縱,橫坐標(biāo)都變了 所以是_x++,_y-- ?? ??? ??? ?_x++, _y--; ?? ??? ??? ?break; ?? ??? ?case RIGHT_UP://從當(dāng)前位置右上移動(dòng)統(tǒng)計(jì) 縱,橫坐標(biāo)都變了 所以是_x--,_y++- ?? ??? ??? ?_x--, _y++; ?? ??? ??? ?break; ?? ??? ?case RIGHT_DOWN://從當(dāng)前位置右下移動(dòng)統(tǒng)計(jì) 縱,橫坐標(biāo)都變了 所以是_x++,_y++ ?? ??? ??? ?_x++, _y++; ?? ??? ??? ?break; ?? ??? ?default: ?? ??? ??? ?//Do Nothing ?? ??? ??? ?break; ?? ??? ?} ?? ??? ?if (_x < 0 || _x > row - 1 || _y < 0 || _y > col - 1){ ?? ??? ??? ?break;/表示越界了就停止 ?? ??? ?} ?? ??? ?//合法 ?? ??? ?if (board[x - 1][y - 1] == board[_x][_y]){ ?? ??? ??? ?count++;//統(tǒng)計(jì)從舊位置出發(fā)統(tǒng)計(jì)和他一樣的個(gè)數(shù) ?? ??? ?} ?? ??? ?else{ ?? ??? ??? ?break;//如果碰到不一樣的就終止循環(huán) ?? ??? ?} ?? ?} ?? ?return count;//返回統(tǒng)計(jì)個(gè)數(shù) } //4種情況 //NEXT:表明要繼續(xù) //PLAYER1_WIN: 用戶1贏了 //PLAYER2_WIN:用戶2贏了 //DRAW: 平局 int IsOver(int board[][COL], int row, int col) { ?? ? ?? ?int count1 = ChessCount(board, row, col, LEFT) + ChessCount(board, row, col, RIGHT) + 1; //? ?? ?int count2 = ChessCount(board, row, col, UP) + ChessCount(board, row, col, DOWN) + 1; //? ?? ?int count3 = ChessCount(board, row, col, LEFT_UP) + ChessCount(board, row, col, RIGHT_DOWN) + 1; //? ?? ?int count4 = ChessCount(board, row, col, LEFT_DOWN) + ChessCount(board, row, col, RIGHT_UP) + 1; //? ?? ?if (count1 >= 5 || count2 >= 5 || count3 >= 5 || count4 >= 5){ ?? ??? ?//有五子連珠 ?? ??? ?//一定有人贏 ?? ??? ?//x, y ?? ??? ?if (board[x - 1][y - 1] == PLAYER1){ ?? ??? ??? ?return PLAYER1_WIN; ?? ??? ?} ?? ??? ?else{ ?? ??? ??? ?return PLAYER2_WIN; ?? ??? ?} ?? ?} ?? ?int i = 0; ?? ?for (; i < row; i++){ ?? ??? ?int j = 0; ?? ??? ?for (; j < col; j++){ ?? ??? ??? ?if (board[i][j] == 0){ ?? ??? ??? ??? ?return NEXT;//當(dāng)前位置還沒有被填寫棋盤不滿 當(dāng)前用戶沒有贏返回next ?讓下個(gè)用戶繼續(xù)下 ?? ??? ??? ?} ?? ??? ?} ?? ?} ?? ?return DRAW;//最后一種情況棋盤滿了 為平局 } void ShowBoard(int board[][COL], int row, int col) { ?? ?//printf("\e[1;1H\e[2J");這是linux環(huán)境下用的清屏 ?? ?//講數(shù)組內(nèi)容,進(jìn)行可視化 ?? ?system("cls");//vs環(huán)境下用的清屏 ?? ? ?? ?int i = 0; ?? ?for (i=1; i <= col; i++){ ?? ??? ?printf("%3d", i); ?? ?} ?? ?printf("\n"); ?? ?for (i = 0; i < row; i++){ ?? ??? ?int j = 0; ?? ??? ?printf("%2d",i + 1 ); ?? ??? ?for (; j < col; j++){ ?? ??? ??? ?if (board[i][j] == 0){ ?? ??? ??? ??? ?printf(" . "); ?? ??? ??? ?} ?? ??? ??? ?else if (board[i][j] == PLAYER1){ ?? ??? ??? ??? ?printf(" x "); ?? ??? ??? ?} ?? ??? ??? ?else{ ?? ??? ??? ??? ?printf (" y "); ?? ??? ??? ?} ?? ??? ?} ?? ??? ?printf("\n"); ?? ?} } void PlayerMove(int board[][COL], int row, int col, int who) { ?? ?while (1){ ?? ??? ?printf("Player[%d] Please Enter Your Pos# ", who); ?? ??? ?scanf("%d %d", &x, &y); ?? ??? ?if (x < 1 || x > row || y < 1 || y > col){ ?? ??? ??? ?printf("Pos Is Not Right!\n"); ?? ??? ??? ?continue; ?? ??? ?} ?? ??? ?else if (board[x - 1][y - 1] != 0){ ?? ??? ??? ?printf("Pos Is Occupied!\n"); ?? ??? ??? ?continue; ?? ??? ?} ?? ??? ?else{ ?? ??? ??? ?//合法性,去重 ?? ??? ??? ?board[x - 1][y - 1] = who; ?? ??? ??? ?break; ?? ??? ?} ?? ?} } void Game() { ?? ?int board[ROW][COL]; ?? ?memset(board, 0, sizeof(board)); ?? ?int result = NEXT; ?? ?do{ ?? ??? ?ShowBoard(board, ROW, COL);//顯示棋盤 ?? ??? ?PlayerMove(board, ROW, COL, PLAYER1);//Player1先走 ?? ??? ?result = IsOver(board, ROW, COL);//判斷游戲是否結(jié)束 ?? ??? ?if (NEXT != result){ ?? ??? ??? ?break;//如果返回值不等于NEXT就跳出循環(huán) 說明當(dāng)前用戶贏了或者平局 如果=NEXT 下個(gè)用戶繼續(xù)下 ?? ??? ?} ?? ??? ?ShowBoard(board, ROW, COL);//同上 ?? ??? ?PlayerMove(board, ROW, COL, PLAYER2); ?? ??? ?result = IsOver(board, ROW, COL); ?? ??? ?if (NEXT != result){ ?? ??? ??? ?break; ?? ??? ?} ?? ?} while (1); ?? ?//p1 win, p2 win, draw ?? ?switch (result){ ?? ?case PLAYER1_WIN: ?? ??? ?ShowBoard(board, ROW, COL); ?? ??? ?printf("恭喜用戶1,你已經(jīng)贏了!\n"); ?? ??? ?break; ?? ?case PLAYER2_WIN: ?? ??? ?printf("恭喜用戶2,你已經(jīng)贏了!\n"); ?? ??? ?break; ?? ?case DRAW: ?? ??? ?printf("平局\n"); ?? ??? ?break; ?? ?default: ?? ??? ?//do nothing! ?? ??? ?break; ?? ?} }
分析:
我們要先構(gòu)建游戲入口void game()函數(shù)在里面構(gòu)建上層本調(diào)用框架 例如一開始初始化數(shù)組把每個(gè)位置都置為0,然后構(gòu)建調(diào)用框架:
1、我們要保存二位數(shù)組并可視化 就要構(gòu)建ShowBoard函數(shù);
2、之后 我們要讓用戶下棋 就要構(gòu)建PlayerMove()函數(shù)
3、第一個(gè)用戶下完之后就要立馬判斷當(dāng)前用戶是否贏,因?yàn)槁渥雍团卸ㄊ菑?qiáng)相關(guān)的 這時(shí)候要構(gòu)建 IsOver()函數(shù)
如果此用戶沒贏 那二個(gè)用戶繼續(xù)下。
當(dāng)我們這些函數(shù)邏輯都明確好的時(shí)候 就用分而治之思想 ,實(shí)現(xiàn)他們的功能。
* 1、ShowBoard()函數(shù)里面我用到了一個(gè)c語(yǔ)言里面的清屏函數(shù)printf(“cls”);引上頭文件window.h,這個(gè)的作用是在固定地方刷新視圖,把上次的視圖清理掉 ,顯示當(dāng)前位置視圖。
后面就打印視圖的內(nèi)容。用戶一和用戶二可以用字符表示,也可以用圓圈圖案表示這個(gè)圖案可以在網(wǎng)上找復(fù)制一下就可
*2、 Playermove()函數(shù)讓用戶下棋,里面先考慮它的合法性 ,不能越界和重復(fù),再然后用我們?cè)趂ive_chrsss.h文件定義宏來(lái)賦值 ,賦完之后再終止循環(huán)。
*3、 Isover()函數(shù)里面是讓我們判斷誰(shuí)輸誰(shuí)贏 換句話游戲是否結(jié)束。游戲結(jié)束標(biāo)志是分為四種情況。用戶一贏,用戶二贏,平局,繼續(xù)。前兩種情況則需要判斷從當(dāng)前位置出發(fā) 沿四個(gè)方向當(dāng)前用戶棋的個(gè)數(shù)是否為大于等于5,在判斷一下是哪個(gè)用戶的棋 就返回哪個(gè)用戶。 而這判斷需要在構(gòu)建并調(diào)用ChressCount()函數(shù)去統(tǒng)計(jì)當(dāng)前用戶的個(gè)數(shù)是否大于等于5,有4個(gè)方向,例如上和下為一個(gè)方向,左和右為一個(gè)方向,左上和右下為一個(gè)方向,右上和左下為一個(gè)方向,分別各自相加之后在加上1就是當(dāng)前的棋,如果滿足這個(gè)4個(gè)方向大于等于5的一個(gè)條件,我們就能知道一定有人贏,否則在繼續(xù)判斷周圍是否有空位置,有則返回NEXT讓下個(gè)用戶繼續(xù)下,沒有則返回平局*ChressCoun()函數(shù)具體分析請(qǐng)看代碼塊
這些函數(shù)實(shí)現(xiàn)完之后再在Game()函數(shù)里面來(lái)個(gè)Switch循環(huán),來(lái)結(jié)收宏 去打印誰(shuí)贏 還是平局。
3、main.c
#include "five_chress.h" int main() { ?? ?int quit = 0; ?? ?int select = 0; ?? ?while (!quit){ ?? ??? ?Menu(); ?? ??? ?scanf("%d", &select); ?? ??? ?switch (select){ ?? ??? ?case 1: ?? ??? ??? ?Game(); ?? ??? ??? ?break; ?? ??? ?case 0: ?? ??? ??? ?quit = 1; ?? ??? ??? ?printf("ByeBye!\n"); ?? ??? ??? ?break; ?? ??? ?default: ?? ??? ??? ?printf("Enter Error, Try Again!\n"); ?? ??? ??? ?break; ?? ??? ?} ?? ?} ?? ?return 0; }
main函數(shù)作為測(cè)試入口,相比較里面的邏輯是很簡(jiǎn)單的,創(chuàng)建一個(gè)菜單函數(shù)和調(diào)用一個(gè)Game函數(shù),再增添點(diǎn)其他的內(nèi)容。
六、實(shí)現(xiàn)結(jié)果
七、總結(jié)
實(shí)現(xiàn)五子棋給我最大的體會(huì)是應(yīng)用比理論學(xué)習(xí)難得多,首先要邏輯清楚,也會(huì)涉及到各種實(shí)際問題,但是加深了我對(duì)知識(shí)的理解和運(yùn)用,也知道了只有多練習(xí) 多寫代碼才能更好的提升編程技術(shù)。同時(shí)我也學(xué)到了實(shí)現(xiàn)一個(gè)復(fù)雜的項(xiàng)目時(shí),先去構(gòu)建一個(gè)框架,一個(gè)清晰的邏輯。再去逐個(gè)擊破每個(gè)問題,要學(xué)會(huì)用分而治之的思想去解決問題。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
C語(yǔ)言實(shí)現(xiàn)自動(dòng)存取款機(jī)模擬系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)自動(dòng)存取款機(jī)模擬系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05適合初學(xué)者的C語(yǔ)言轉(zhuǎn)義字符講解
轉(zhuǎn)義字符是很多程序語(yǔ)言、數(shù)據(jù)格式和通信協(xié)議的形式文法的一部分。對(duì)于一個(gè)給定的字母表,一個(gè)轉(zhuǎn)義字符的目的是開始一個(gè)字符序列,使得轉(zhuǎn)義字符開頭的該字符序列具有不同于該字符序列單獨(dú)出現(xiàn)(沒有轉(zhuǎn)義字符開頭)時(shí)的語(yǔ)義。因此轉(zhuǎn)義字符開頭的字符序列被叫做轉(zhuǎn)義序列2022-04-04基于malloc與free函數(shù)的實(shí)現(xiàn)代碼及分析
本篇文章介紹了malloc與free函數(shù)的實(shí)現(xiàn)代碼及分析。需要的朋友參考下2013-05-05c++動(dòng)態(tài)內(nèi)存管理與智能指針的相關(guān)知識(shí)點(diǎn)
為了更容易同時(shí)也更安全地使用動(dòng)態(tài)內(nèi)存,新的標(biāo)準(zhǔn)庫(kù)提供了兩種智能指針(smart pointer)類型來(lái)管理對(duì)象,下面這篇文章主要給大家介紹了關(guān)于c++動(dòng)態(tài)內(nèi)存管理與智能指針的相關(guān)知識(shí)點(diǎn),需要的朋友可以參考下2022-03-03簡(jiǎn)要解讀C++的動(dòng)態(tài)和靜態(tài)關(guān)聯(lián)以及虛析構(gòu)函數(shù)
這篇文章主要介紹了簡(jiǎn)要解讀C++的動(dòng)態(tài)和靜態(tài)關(guān)聯(lián)以及虛析構(gòu)函數(shù),析構(gòu)函數(shù)在C++編程中平時(shí)并不是太常用,需要的朋友可以參考下2015-09-09C++任意線程通過hwnd實(shí)現(xiàn)將操作發(fā)送到UI線程執(zhí)行
做Windows界面開發(fā)時(shí),經(jīng)常需要在多線程環(huán)境中將操作拋到主線程執(zhí)行,下面我們就來(lái)學(xué)習(xí)一下如何在不需要重新定義消息以及接收消息的情況下實(shí)現(xiàn)這一要求,感興趣的可以了解下2024-03-03一篇文章帶你了解C語(yǔ)言文件操作中的幾個(gè)函數(shù)
這篇文章主要介紹了使用C語(yǔ)言操作文件的基本函數(shù)整理,包括創(chuàng)建和打開以及關(guān)閉文件的操作方法,需要的朋友可以參考下,希望能夠給你帶來(lái)幫助2021-09-09