1. 程式人生 > >C語言 五子棋

C語言 五子棋

顯示 ++ while 右下角 c語言 顏色 stdlib.h 正常 避免

#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <malloc.h>

struct rcd;//聲明節點結構
typedef struct rcd* Record;//節點指針別名
typedef struct rcd record;//節點別名
#define MAXIMUS 15 //定義棋盤大小

int p[MAXIMUS][MAXIMUS];//存儲對局信息
char buff[MAXIMUS*2
+1][MAXIMUS*4+3];//輸出緩沖器 int Cx,Cy;//當前光標位置 int Now;//當前走子的玩家,1代表黑,2代表白 int wl,wp;//當前寫入緩沖器的列數和行數位置 char* showText;//在棋盤中央顯示的文字信息 int count;//回合數 int Putable;//指示當前是否可以走棋 int Exiting;//1為當場上無子並按ESC時詢問是否退出程序的狀態,2為非此狀態 int ExiRep;//1為當回放到最後一回合並按向後時詢問是否退出回放的狀態,2為非此狀態 Record RecBeg,RecNow;//記錄的開始節點和當前節點 struct rcd//
記錄節點結構,雙鏈表形式 { int X;//此記錄走棋的X坐標 int Y;//此記錄走棋的Y坐標 Record Next;//前一個記錄 Record Back;//後一個記錄 }; Record newRecord()//記錄節點構造函數 { Record r=(Record)malloc(sizeof(record));//申請一個節點對象 r->Next=NULL;//給予前後節點初值NULL r->Back=NULL; return r; } void Exit()//檢查退出程序 { int
input; if(Exiting)//如果是第二次按下ESC { exit(0); } else//如果是第一次按下ESC則詢問是否退出程序 { showText="是否退出?再次按下ESC退出,其他鍵返回"; Exiting=1;//指示已經按下過ESC } } void ExitRep()//檢查退出回放 { int input; if(ExiRep)//如果是第二次後移 { ExiRep=3; } else//如果是第一次後移則詢問是否退出回放 { showText="是否退出?再次後移退出回放,其他鍵返回"; ExiRep=1;//指示已經按下過後移 } } void AddRecord()//添加記錄 { RecNow->X=Cx;//記錄坐標 RecNow->Y=Cy; RecNow->Next=newRecord();//創建下一個記錄節點 RecNow->Next->Back=RecNow;//完成雙鏈表 RecNow=RecNow->Next;//當前記錄推至下一個記錄節點 } int DelRecord()//刪除當前記錄節點,1為刪除成功,0為刪除失敗 { Record b;//上一個節點 if(RecNow->Back!=NULL)//越界檢查 { b=RecNow->Back;//緩存上一個節點 free(RecNow);//釋放當前節點 RecNow=b;//當前記錄回至上一個記錄節點 return 1; } else { return 0;//沒有節點可刪除時 } } void CleanRecord()//清理所有記錄 { Record n;//下一個節點 while(RecBeg->Next!=NULL)//刪除所有記錄,直到越界前為止 { n=RecBeg->Next;//記下下一個節點 free(RecBeg);//釋放當前節點 RecBeg=n;//當前記錄推至下一個記錄節點 } } char* Copy(char* strDest,const char* strSrc)//修改過的字符串復制函數,會忽略末端的\0 { char* strDestCopy = strDest; while (*strSrc!=\0) { *strDest++=*strSrc++; } return strDestCopy; } void Initialize()//初始化一個對局函數 { int i,j;//循環變量 system("title 對局中(按方向鍵控制光標,空格走子),Esc撤銷"); showText="";//重置顯示信息 count=0;//回合數歸零 RecNow=RecBeg=newRecord(); Exiting=0; for(i=0;i<MAXIMUS;i++)//重置對局數據 { for(j=0;j<MAXIMUS;j++) { p[i][j]=0; } } Cx=Cy=MAXIMUS/2;//重置光標到中央 Now=1;//重置當前為黑方 } char* getStyle(int i,int j)//獲得棋盤中指定坐標交點位置的字符,通過制表符拼成棋盤 { if(p[i][j]==1)//1為黑子 return ""; else if(p[i][j]==2)//2為白子 return ""; else if(i==0&&j==0)//以下為邊緣棋盤樣式 return ""; else if(i==MAXIMUS-1&&j==0) return ""; else if(i==MAXIMUS-1&&j==MAXIMUS-1) return ""; else if(i==0&&j==MAXIMUS-1) return ""; else if(i==0) return ""; else if(i==MAXIMUS-1) return ""; else if(j==0) return ""; else if(j==MAXIMUS-1) return ""; return "";//中間的空位 } char* getCurse(int i,int j){//獲得指定坐標交點位置左上格的樣式,通過制表符來模擬光標的顯示 if(Putable)//可走棋時光標為粗線 { if(i==Cx){ if(j==Cy) return ""; else if (j==Cy+1) return ""; } else if(i==Cx+1) { if(j==Cy) return ""; else if (j==Cy+1) return ""; } } else//不可走棋時光標為虛線 { if(i==Cx){ if(j==Cy) return ""; else if (j==Cy+1) return ""; } else if(i==Cx+1) { if(j==Cy) return ""; else if (j==Cy+1) return ""; } } return " ";//如果不在光標附近則為空 } void write(char* c)//向緩沖器寫入字符串 { Copy(buff[wl]+wp,c); wp+=strlen(c); } void ln()//緩沖器寫入位置提行 { wl+=1; wp=0; } void Display()//將緩沖器內容輸出到屏幕 { int i,l=strlen(showText);//循環變量,中間文字信息的長度 int Offset=MAXIMUS*2+2-l/2;//算出中間文字信息居中顯示所在的橫坐標位置 if(Offset%2==1)//如果位置為奇數,則移動到偶數,避免混亂 { Offset--; } Copy(buff[MAXIMUS]+Offset,showText);//講中間文字信息復制到緩沖器 if(l%2==1)//如果中間文字長度為半角奇數,則補上空格,避免混亂 { *(buff[MAXIMUS]+Offset+l)=0x20; } system("cls");//清理屏幕,準備寫入 for(i=0;i<MAXIMUS*2+1;i++){//循環寫入每一行 printf("%s",buff[i]); if(i<MAXIMUS*2)//寫入完每一行需要換行 printf("\n"); } } void Print()//將整個棋盤算出並儲存到緩沖器,然後調用Display函數顯示出來 { int i,j;//循環變量 wl=0; wp=0; for(j=0;j<=MAXIMUS;j++)//寫入出交點左上角的字符,因為需要打印棋盤右下角,所以很以橫縱各多一次循環 { for(i=0;i<=MAXIMUS;i++) { write(getCurse(i,j));//寫入左上角字符 if(j==0||j==MAXIMUS)//如果是棋上下盤邊緣則沒有連接的豎線,用空格填充位置 { if(i!=MAXIMUS) write(" "); } else//如果在棋盤中間則用豎線承接上下 { if(i==0||i==MAXIMUS-1)//左右邊緣的豎線更粗 write(""); else if(i!=MAXIMUS)//中間的豎線 write(""); } } if(j==MAXIMUS)//如果是最後一次循環,則只需要處理邊側字符,交點要少一排 { break; } ln();//提行開始打印交點內容 write(" ");//用空位補齊位置 for(i=0;i<MAXIMUS;i++)//按橫坐標循環正常的次數 { write(getStyle(i,j));//寫入交點字符 if(i!=MAXIMUS-1)//如果不在最右側則補充一個橫線承接左右 { if(j==0||j==MAXIMUS-1) { write("");//上下邊緣的橫線更粗 } else { write("");//中間的橫線 } } } ln();//寫完一行後提行 } Display();//將緩沖器內容輸出到屏幕 } int Put(){//在當前光標位置走子,如果非空,則返回0表示失敗 if(Putable) { p[Cx][Cy]=Now;//改變該位置數據 AddRecord(); return 1;//返回1表示成功 } else { return 0; } } int Check()//勝負檢查,即判斷當前走子位置有沒有造成五連珠的情況 { int w=1,x=1,y=1,z=1,i;//累計橫豎正斜反邪四個方向的連續相同棋子數目 for(i=1;i<5;i++)if(Cy+i<MAXIMUS&&p[Cx][Cy+i]==Now)w++;else break;//向下檢查 for(i=1;i<5;i++)if(Cy-i>0&&p[Cx][Cy-i]==Now)w++;else break;//向上檢查 if(w>=5)return Now;//若果達到5個則判斷當前走子玩家為贏家 for(i=1;i<5;i++)if(Cx+i<MAXIMUS&&p[Cx+i][Cy]==Now)x++;else break;//向右檢查 for(i=1;i<5;i++)if(Cx-i>0&&p[Cx-i][Cy]==Now)x++;else break;//向左檢查 if(x>=5)return Now;//若果達到5個則判斷當前走子玩家為贏家 for(i=1;i<5;i++)if(Cx+i<MAXIMUS&&Cy+i<MAXIMUS&&p[Cx+i][Cy+i]==Now)y++;else break;//向右下檢查 for(i=1;i<5;i++)if(Cx-i>0&&Cy-i>0&&p[Cx-i][Cy-i]==Now)y++;else break;//向左上檢查 if(y>=5)return Now;//若果達到5個則判斷當前走子玩家為贏家 for(i=1;i<5;i++)if(Cx+i<MAXIMUS&&Cy-i>0&&p[Cx+i][Cy-i]==Now)z++;else break;//向右上檢查 for(i=1;i<5;i++)if(Cx-i>0&&Cy+i<MAXIMUS&&p[Cx-i][Cy+i]==Now)z++;else break;//向左下檢查 if(z>=5)return Now;//若果達到5個則判斷當前走子玩家為贏家 return 0;//若沒有檢查到五連珠,則返回0表示還沒有玩家達成勝利 } void ReplayMode(){ int i,j;//循環變量 system("title 回放中(按左鍵後退,右鍵或空格前進),Esc退出"); showText="";//重置顯示信息 count=0;//回合數歸零 Putable=0;//不可走棋狀態 RecBeg->Back=newRecord(); RecBeg->Back->Next=RecBeg; RecBeg=RecBeg->Back; for(i=0;i<MAXIMUS;i++)//重置對局數據 { for(j=0;j<MAXIMUS;j++) { p[i][j]=0; } } Now=1;//重置當前為黑方 } void RepForward()//回放模式前進 { if(RecNow->Next->Next!=NULL)//越界檢查 { RecNow=RecNow->Next;//當前節點推至下一個記錄節點 p[RecNow->X][RecNow->Y]=Now;//按照記錄還原一個回合 Cx=RecNow->X;//設置光標位置 Cy=RecNow->Y; Now=3-Now;//轉換當前的黑白方 } else//若已達到最後則詢問退出 { ExitRep(); } } void RepBackward()//回放模式後退 { if(RecNow->Back!=NULL)//越界檢查 { p[RecNow->X][RecNow->Y]=0;//按照記錄撤銷一個回合 if(RecNow->Back->Back==NULL)//在整個棋盤沒有棋子時隱藏光標 { Cx=-2; Cy=-2; } else if(RecNow->Back==NULL)//在只有一個棋子時移動光標到這個棋子的位置 { Cx=RecNow->X; Cy=RecNow->Y; } else//正常情況下移動光標到上一回合的位置 { Cx=RecNow->Back->X; Cy=RecNow->Back->Y; } RecNow=RecNow->Back;//當前節點後退至上一個記錄節點 Now=3-Now;//轉換當前的黑白方 } } void ShowReplay() { int input;//輸入變量 ReplayMode();//初始化回放模式 RecNow=RecBeg;//當前觀察從頭開始 RepForward();//顯示第一次走棋 while(1)//開始無限回合的死循環,直到Esc退出 { if(ExiRep==3) { ExiRep=0; break; } Print();//打印棋盤 input=getch();//等待鍵盤按下一個字符 if(input==27)//如果是ESC則退出回放 { return; } else if(input==0x20)//如果是空格則前進 { RepForward(); continue; } else if(input==0xE0)//如果按下的是方向鍵,會填充兩次輸入,第一次為0xE0表示按下的是控制鍵 { input=getch();//獲得第二次輸入信息 switch(input)//判斷方向鍵方向並移動光標位置 { case 0x4B: RepBackward();//向左後退 break; case 0x4D: RepForward();//向右前進 continue; } } ExiRep=0;//未再次按後移則不準備退出 showText=""; } } void Regret()//悔棋撤銷,如果棋盤上沒有子即為退出 { if(DelRecord()){//嘗試刪除當前節點,如果有節點可以刪除則 p[RecNow->X][RecNow->Y]=0;//撤除當前回合 if(RecNow->Back==NULL)//如果刪除的是第一顆子則將光標移動到第一顆子原來的位置上 { Cx=RecNow->X; Cy=RecNow->Y; } else//否則將光標移動到上一顆子上 { Cx=RecNow->Back->X; Cy=RecNow->Back->Y; } Now=3-Now;//反轉當前黑白方 } else { Exit();//如果沒有棋子可以撤銷,則詢問退出 } } int RunGame()//進行整個對局,返回贏家信息(雖然有用上) { int input;//輸入變量 int victor;//贏家信息 Initialize();//初始化對局 while(1){//開始無限回合的死循環,直到出現勝利跳出 Putable=p[Cx][Cy]==0; Print();//打印棋盤 input=getch();//等待鍵盤按下一個字符 if(input==27)//如果是ESC則悔棋或退出 { Regret(); Print(); continue; } else if(input==0x20)//如果是空格則開始走子 { if(Put())//如果走子成功則判斷勝負 { victor=Check(); Now=3-Now;//輪換當前走子玩家 count++; if(victor==1)//如果黑方達到勝利,顯示提示文字並等待一次按鍵,返回勝利信息 { showText="黑方勝利!按R查看回放,按其他鍵重新開局"; Print(); input=getch(); if(input==0xE0) { getch(); } else if(input==R||input==r) { ShowReplay(); } return Now; } else if(victor==2)//如果白方達到勝利,顯示提示文字並等待一次按鍵,返回勝利信息 { showText="白方勝利!按R查看回放,按其他鍵重新開局"; Print(); input=getch(); if(input==0xE0) { getch(); } else if(input==R||input==r) { ShowReplay(); } return Now; }else if(count==MAXIMUS*MAXIMUS)//如果回合數達到了棋盤總量,即棋盤充滿,即為平局 { showText="平局!按R查看回放,按其他鍵重新開局"; Print(); input=getch(); if(input==0xE0) { getch(); } else if(input==R||input==r) { ShowReplay(); } CleanRecord(); return 0; } } } else if(input==0xE0)//如果按下的是方向鍵,會填充兩次輸入,第一次為0xE0表示按下的是控制鍵 { input=getch();//獲得第二次輸入信息 switch(input)//判斷方向鍵方向並移動光標位置 { case 0x4B:// Cx--; break; case 0x48: Cy--; break; case 0x4D: Cx++; break; case 0x50: Cy++; break; } if(Cx<0)Cx=MAXIMUS-1;//如果光標位置越界則移動到對側 if(Cy<0)Cy=MAXIMUS-1; if(Cx>MAXIMUS-1)Cx=0; if(Cy>MAXIMUS-1)Cy=0; } Exiting=0;//未再次按下ESC則不準備退出 showText=""; } } int main()//主函數 { system("mode con cols=63 lines=32");//設置窗口大小 system("color E0");//設置顏色 while(1){//循環執行遊戲 RunGame(); } }

C語言 五子棋