切換
舊版
前往
大廳
主題

【Code】C++ 19x19 五子棋 & 刊物幫推

| 2018-08-07 03:00:17 | 巴幣 10 | 人氣 2916

  哈囉各位好,這裡是用C++實作五子棋程式,而文末有幫推筆友的公會刊物。
  這是筆友第一次實體書制作參與,我也就來幫幫推,在打包我的程式碼好交作業之餘,也給點面子看一下吧。

  不含除錯實作,如果不是從井字遊戲移植的話也真的差不多是兩小時呢,跟預測的一模一樣真不愧是我自己哈哈。
  但如果含除錯,呃因為19*19沒寫測資所以是手動除,所以就花了滿長的時間來著,而且因為是單一人稱手動除錯,所以有可能還留有沒剔乾淨的BUG,如果有出現的話請跟我說一聲,寫信與留言都好,不要用私訊我沒有裝哈哈姆特。

  總之跟井字遊戲與五子棋唯一的不同,除了輸入法以外就是勝利判定。
  輸入法是以XY座標做輸入,也就是(17,4)、(5,5)、(10,10)等等輸入。
  第一個輸入數字是橫軸(X),第二個是縱軸(Y)。

  勝利判定則是用步林判斷。
  勝利函式下還分成直、橫、左斜、右斜四個子函式。
  這四個子函式只要有一個是正確的就會回傳勝利,而子函式的判斷則是透過:
  • 捕捉你所下的棋子座標。
  • 判斷該座標隔壁是否連著一樣的棋子。
  • 是的話就連子數+1,否的話就跳出函數。
  • 跳出函式後,判斷連子數是否大等於設置數(5子棋就是5顆)。
  • 如果大等於設置數,輸出勝利提示而結束遊戲迴圈。







以下是程式碼,語言C++,沒有偷藏直接複製貼上可用

#include<iostream>
using namespace std;
/*直五子判斷,地圖為map[y][x]*/
bool RowWin(int Long,char Player,int x,int y,char map[19][19]){//連棋長度(『五子』棋設為5),目前的旗子款式,最後一子的X座標,最後一子的Y座標,地圖
    int Connect=1;//初始(因為初始本身就是一顆棋子,所以為1)
    for(int i=1;;i++){//1為起始(因為+0就是基底做標本身,所以從1開始)的加法無限迴圈
        if(map[y+i][x]==Player) Connect++;//如果Y座標+1(直線向上)是相同的旗子,初始+1
        else break;//若不是,則結束迴圈
    }
    for(int i=1;;i++){
        if(map[y-i][x]==Player) Connect++;//如果Y座標-1(直線向下)是相同的旗子,初始+1
        else break;
    }
    if(Connect>=Long) return true;//如果最終結果大於等於連棋長度,則獲勝
    else return false;
}
/*橫五子判斷,地圖為map[y][x]*/
bool ColWin(int Long,char Player,int x,int y,char map[19][19]){
    int Connect=1;
    for(int i=1;;i++){
        if(map[y][x+i]==Player) Connect++;//如果X座標+1(橫線向上)是相同的旗子,初始+1
        else break;
    }
    for(int i=1;;i++){
        if(map[y][x-i]==Player) Connect++;//如果X座標-1(橫線向下)是相同的旗子,初始+1
        else break;
    }
    if(Connect>=Long) return true;
    else return false;
}
/*右斜五子判斷,地圖為map[y][x]*/
bool ObliWin1(int Long,char Player,int x,int y,char map[19][19]){
    int Connect=1;
    for(int i=1;;i++){
        if(map[y+i][x+i]==Player) Connect++;//如果XY座標+1(右斜向上)是相同的旗子,初始+1
        else break;
    }
    for(int i=1;;i++){
        if(map[y-i][x-i]==Player) Connect++;//如果XY座標-1(右斜向下)是相同的旗子,初始+1
        else break;
    }
    if(Connect>=Long) return true;
    else return false;
}
/*左斜五子判斷,地圖為map[y][x]*/
bool ObliWin2(int Long,char Player,int x,int y,char map[19][19]){
    int Connect=1;
    for(int i=1;;i++){
        if(map[y+i][x-i]==Player) Connect++;//如果XY座標各-1+1(左斜向上)是相同的旗子,初始+1
        else break;
    }
    for(int i=1;;i++){
        if(map[y-i][x+i]==Player) Connect++;//如果XY座標各+1-1(左斜向下)是相同的旗子,初始+1
        else break;
    }
    if(Connect>=Long) return true;
    else return false;
}
/*勝負判斷*/
bool winner(int Long,char Player,int x,int y,char map[19][19]){//連棋長度(『五子』棋設為5),目前的旗子款式,最後一子的X座標,最後一子的Y座標,地圖
    /*如果上述四個判斷有一個成立,則獲勝*/
    if(ColWin(Long,Player,x,y,map)==true||RowWin(Long,Player,x,y,map)==true||ObliWin1(Long,Player,x,y,map)==true||ObliWin2(Long,Player,x,y,map)==true) return true;
    else return false;
}
/*判斷是否能在這個位置放置棋子*/
bool TFset(char Player,int XSwitch,int YSwitch,char map[19][19]){//目前的旗子款式,欲輸入X座標,欲輸入Y座標,地圖
    /*輸入範圍判斷,所在位置是否存在棋子判斷,地圖為map[YSwitch][XSwitch]*/
    if(XSwitch>=0 && XSwitch<19 && YSwitch>=0 && YSwitch<19 && map[YSwitch][XSwitch]==' '){ map[YSwitch][XSwitch]=Player; return true; }
    else{ cout<<"按錯了啦 蛤啊你是目洨膩?"<<endl; return false; }/*錯誤提示*/
}
/*顯示輸出*/
void show(char map[19][19]){//地圖
    cout<<"   01  02  03  04  05  06  07  08  09  10  11  12  13  14  15  16  17  18  19"<<endl;//X座標提示
    for(int i=18;i>=0;i--){//地圖要倒過來顯示,Y座標才會接近一般數學使用
        cout<<"   --------------------------------------"<<endl;

        if(i+1<10) cout<<"0"<<i+1;//Y座標提示
        else cout<<i+1;
        
        for(int j=0;j<19;j++) { cout<<"| "<<map[i][j]<<" "; } cout<<"|";

        if(i+1<10) cout<<"0"<<i+1<<endl;//Y座標提示
        else cout<<i+1<<endl;

    }cout<<"   --------------------------------------"<<endl;
    cout<<"   01  02  03  04  05  06  07  08  09  10  11  12  13  14  15  16  17  18  19"<<endl;//X座標提示
}

int main(){
    char map[19][19];//注意:顯示端雖然是以map[x][y]顯示,但實際輸入時寫在判斷函式內的可是map[y][x]
    for(int i=0;i<19;i++)//搜尋全地圖並空地圖初始化
        for(int j=0;j<19;j++) map[i][j]=' ';
    
    char Player='O';//第一顆棋子為O  
    int XSwitch,YSwitch;//按下的X、Y座標
    
    show(map);//地圖顯示
    while(1){
        cout<<"(Y軸:↑X軸:→     X、Y都按0時,程式退出)"<<endl;
        cout<<"X:"; cin>>XSwitch;//輸入X座標
        cout<<"Y:"; cin>>YSwitch;//輸入Y座標
    /*終止遊戲*/    
        if(XSwitch==0&&YSwitch==0) break;//如果X、Y都輸入0,結束迴圈
        else{ XSwitch=XSwitch-1; YSwitch=YSwitch-1; }//因為陣列起始為0,所以輸入的值需要-1

        system("CLS");//清除畫面的系統字
        bool TFSet=TFset(Player,XSwitch,YSwitch,map);//判斷是否能夠放置棋子,並放進棋子
        show(map);//更新地圖顯示
    /*判斷是否有玩家獲勝*/
        if(winner(5,Player,XSwitch,YSwitch,map)==true) {//剛才所下的棋子為基準(最後一子),作八方勝利掃查判斷
            cout<<Player<<" 獲勝。\n";//若有玩家獲勝,輸出提示後結束第三個全遊戲迴圈
            break;                   
        }
    /*判斷是否滿圖*/
        bool mapover=true;//初始為滿
        for(int i=0;i<19;i++)//搜尋全地圖
            for(int j=0;j<19;j++)
                if(map[i][j]==' ') mapover=false;//只要有一個是空的,地圖就不為滿  
                
        if(mapover==true){//如果滿圖,輸出滿圖提示後結束迴圈
            cout<<"滿圖,不分勝負。";
            break;
        }
    /*迴圈繼續,更替棋子*/
        if(TFSet==true){//如果方才放入棋子的選項有正常執行
            if(Player=='O') Player='@';//更替棋子
            else Player='O';            
        }
    }
}








註一:為什麼輸出成.exe檔,檔案會秒開之後秒關呢?
因為這是測試模組的啟動,如果是輸出成.exe檔檔案要長開不關,需要在最後一個括號前加入:

int main(){
   …
    system("pause");
}



註二:為什麼我的執行結果畫面會是黑底白字?梗的結果是白底藍字?
因為我有用系統字去更改,白底藍字的系統字如下,請加在『int main(){……}』內的某處:
int main(){
    system("color f9");
}



註三:那為什麼你不要直接把那些程式附在主程式內?傲嬌?
因為就算沒有美觀也沒有長開,那顆程式一樣還能動我就盡量壓短嘛,你才傲嬌。


註四:原本說不寫的五子棋C++的,那所以為什麼忽然又要寫了呢?
有一天我躺在床上,然後我忽然想到好像行的通?
所以就寫了,結束。


註五:為什麼棋子不是在線上而是在格子裡?是辦不到嗎?
因為C++是半形ASCII,單純是如果硬用在線上視覺效果很差而已。



執行結果畫面:










好哩,接著讓我幫忙推推書吧?
這次幫推的刊物是十六夜文藝創作公會的刊物《真實》。


Q1:為什麼梗會在這裡打廣告?
A1:呃基本上零資本的話(雖然我確實沒有資本,不過一旦扯到資本整件事就會複雜很多,所以不要遊說我丟錢喔),我很願意協助相識一場的人(呃請至少跟我相識一場,我又沒有仙人模式)任何逐夢的過程,編寫散文、轉學休學、開設商行、築夢輔導,當然出版刊物的宣傳也絕對在這個資格內,這件事我從開始創帳號前就在做了,三不五時就在說肚爛現實主義的人,如果不偶爾幹點浪漫的贊助就只是單純的糞青呢。

Q2:梗,你這樣寫,你一狗票來看笑話的讀者們是不會接受的。
A2:呃……幹啊不就有一次我在有感而發我第一個訂戶跟第一個網路搭訕的筆友,Hsin就不知道怎麼回事亂入鬧彆扭,雖然是個綜藝咖但是是冷靜自持文雅寧靜(個人認為,啦)的Hsin耶?你敢信嗎?




你知道沒有行動的主張就只是一場嘴砲,所以當然要行動資助資助啊,小的們!掏出你們的分享力啊銃三小!芯芯妳最重要了!






Q3:那為什麼貼在程式後面?幹啊梗你瞧不起人嗎?出來談判!
A3:我也不知道她怎麼會選擇讓我貼在程式後面……明明不管貼哪裡我都會打一定的字數做宣傳啊……Hsin妳傲嬌嗎!出來面對!





  好勒看笑話是一回事,總之《真實》這部作品是一部一款短篇集,內部蒐藏八位寫手的作品,每部是約一萬五千字的中短品。


(嗯對圖我沒有告知,若當事人抗議我看見訊息後立刻下架)



  而這個是Hsin自己的宣傳,作為作者群之一當然比較懂產品,所以就直接上連結囉,絕對不是我偷懶啊哈哈。

  呃好啦我補充一下,Hsin的宣傳裏頭包含她的書內作品《盜命之眼》部分試閱,我想她是太緊張嗎?怎麼就忘記寫在標題了呢?

  這是預購連結,單品訂價是折合美金大概七塊的新台幣200元整。
  為什麼用這麼莫名其妙的寫法?呃沒有啦就一時想到不好意思……


  最後聽說如果沒有預購,8/18、8/19的CWTK 27現場也不見的可以入手,呃,哇喔。
  總之就是這樣,代為宣傳到此告一段落也感謝讀到這裡,敬請期待刊物《真實》以及Hsin於書內收錄短品《盜命之眼》!
送禮物贊助創作者 !
0
留言

創作回應

地上爬的星星
原本只是好奇梗大會怎麼寫,結果卻看到在跟學姊打情罵俏,怪不得...
2018-08-07 09:16:50
不是,怎麼看都是我被調戲啊哪來的打情罵俏……,而且怪不得什麼啊……
2018-08-08 14:22:44
章魚茶
X座標提示……好髒XDDD
明明用iomanip這個library和一個迴圈就可以做到的事情
for( int i = 1; i <= 19; ++i )
{ cout << setw( 4 ) << i; }
(礙於手機排版我就不照我的code style來了XD)

還有五子棋連線的判定明明就可以省一半的時間,往上往下選一個找就夠了啦O3O

(我還是沒有抓來Debug,不過至少這幾個點可以優化XD)
2018-08-07 10:06:09
喔其實我一開始也是這樣寫哈哈,可是後來看了一下覺得視覺效果不太好,就是我盡量讓它光用看的也可以想像出那是棋盤,包含裡面放置棋子的框框寫得這麼奇怪也是希望讓它看起來更不接近程式,而是畫面本身
話說這個也不用用setw啦,反正等寬就輸入空白字符就好了呀,需要用到這個取代的反而是y座標提示裡面的if else,不過因為這算是寫給文組看的程式所以函式庫能少用我就盡量少用啦哈哈

然後我實際上我一開始也是只寫一個,因為是從右上往左下,所以我也只保留靠右或靠上的那一個這樣效率比較好

但是假設後來有人要把它改成圖形介面,就要從『最後一顆』點擊棋子開始看效率才會高,如果這個樣子的話那麼這個邏輯就會直接變成一個bug,然後我要發揮我的綜藝咖精神了各位觀眾

對耶!就算不是圖形介面我也可以這麼辦啊!幹幹幹等等就這樣改!
2018-08-08 14:31:04
章魚茶
梗大,你突破盲腸了OuOb
2018-08-08 14:32:35
對啊那我沖三小用圖搜啦媽的,肯定是最近自動掃算寫四五千行腦子寫抽了吧哈哈
2018-08-08 14:35:51
變色泰迪
雖然沒每一行認真看(反正需要再回來就好
但是我懂惹...吧
那要買書但是不能匯款又不能跑高雄該怎麼辦
2018-08-08 23:58:20
喔好啊好啊,實際上也不難啦哈哈
不過我跟章魚討論後突破盲點,所以有小小修正寫法哈哈,您看看吧

喔我剛剛問Hsin,她說可以試試看用這個解決喔http://u750072.pixnet.net/blog/post/9009396-%E7%B6%B2%E8%B3%BC%E5%BF%85%E7%9C%8B%EF%BC%81%E9%83%B5%E5%B1%80%E5%8C%AF%E6%AC%BE-%E8%B7%A8%E8%A1%8C%E5%8C%AF%E6%AC%BE-%E9%83%B5%E5%B1%80%E7%84%A1%E6%91%BA%E5%8C%AF%E6%AC%BE-
2018-08-10 00:47:21

更多創作