两个吃奶一个添下面视频_人妻第一页香蕉网_欧美xxxx少妇_妺妺窝人体色www婷婷

一、項目簡介

這是一個可以單人游玩的黑白棋小游戲。

采用鼠標左鍵點擊的方式下子。下子之后,處于該點和原本同顏色棋子之間的棋子會轉變顏色。本游戲代碼設置了可以調整難度的AI(改變內部的difficult參數(shù)),可以隨自己的喜好調整。

編譯環(huán)境:visual c++ 6.0

第三方庫:Easyx2017

二、運行截圖

黑白棋游戲界面


黑白棋游戲結束界面



三、源碼解析

首先看游戲的主體部分,也就是其運行邏輯。

void play(void)                   // 游戲過程
{
       MOUSEMSG m;
       int x, y;
       // 初始化棋子
       for(x = 0; x < 8; x++)
              for(y = 0; y < 8; y++)
                     map[x][y] = 0;
       map[3][4] = map[4][3] = 'B';
       map[3][3] = map[4][4] = 'W';
       // 開始游戲
       print();
       mciSendString("play 音樂\\背景音樂.wma from 0 repeat", NULL, 0, NULL);
       do
       {
              if (Canput('B'))                                                                   // 如果玩家有下子位置        {
                     while(true)
                     {
                            while(true)
                            {
                                   m = GetMouseMsg();                             // 獲取鼠標消息
                                   if(m.uMsg == WM_LBUTTONDOWN && m.x - 26 < 37 * 8 && m.y - 26 < 37 * 8)
                                                                                                  // 如果左鍵點擊
                                          break;
                            }
                            x = (m.y - 26) / 37;
                            y = (m.x - 26) / 37;
                            if(judge(x, y, 'B'))                                           // 如果當前位置有效
                            {
                                   draw(x, y, 'B');                                               // 下子
                                   mciSendString("play 音樂\\下子.wma from 0", NULL, 0, NULL);
                                   print();
                                   putimage(37 * y, 37 * x, &img[3]);         // 標識下子點
                                   break;
                            }
                            else
                                   continue;
                     }
                     if (quit('W'))                                                         // 計算機是否失敗
                            break;
              }
              if (Canput('W'))                                                           // 如果計算機有下子位置
              {
                     clock_t start;
                     start = clock();
                     D('W', 1);                                                                     // 搜索解法
                     while (clock() - start < CLOCKS_PER_SEC);
                     draw(X, Y, 'W');
                     print();
                     mciSendString("play 音樂\\下子.wma from 0", NULL, 0, NULL);
                     putimage(37 * Y, 37 * X, &img[4]); // 標識下子點
                     if (quit('B'))                                                           // 玩家是否失敗
                            break;
              }
       }while (Canput('B') || Canput ('W'));

我們定義了運算用的變量x,y,以及鼠標變量m。MOUSEMSG是Easyx中的結構體,用于保存鼠標消息。

然后初始化棋盤,即在中間的四個位置放上黑白各兩顆棋子。map[x][y]是一個二維字符串組,用”B”和”W”分別表示黑棋和白棋。

之后進入Do-while循環(huán),循環(huán)條件為兩方至少有一方可以下子。

首先玩家(黑)先行動。我們想要達成的目的是,如果我們點擊棋盤的一個點,這里允許下子則下子,不能下子則繼續(xù)檢測。

這個結構采取一個雙層循環(huán)來完成,內層不斷調用GetMouseMsg獲取鼠標信息(GetMouseMsg是Easyx中的函數(shù),其返回值為之前提到過的MOUSEMSG結構,.x和.y分別表示鼠標點擊的橫縱坐標位置),如果點擊,則確定該點的位置,內層循環(huán)結束。判定此次落子是否有效,有效則下子并終止外層循環(huán),無效則返回外層循環(huán)的頭部。

然后白方下子。這里利用一個動態(tài)規(guī)劃函數(shù)算出一個較好的落子點(與難度相關),并在計算開始之前計時。計算結束判定是否經過一秒(CLOCKS_PER_SEC表示一秒鐘內CPU運行的時鐘周期數(shù)),如果不到一秒則延遲到一秒后落子,防止影響人類棋手心態(tài)。然后同樣執(zhí)行落子程序以及音樂播放程序。

在整個Play函數(shù)外部,前方應該還有一個初始化函數(shù)load(),之后還有勝利處理函數(shù)。

 

之后,我們看其中的每一個函數(shù)應當如何實現(xiàn)。

這是全局當中聲明的函數(shù)。

void load(void);                        // 加載素材
void print(void);                       // 畫棋盤
void draw(int, int, char);              // 下當前子
int judge(int, int, char);                // 判斷當前是否可以落下
bool Canput(char);                          // 判斷是否有棋可吃
bool quit(char);                        // 判斷是否有棋存活
bool ask(void);                                // 彈出對話框
int D(char, int);                         // 動態(tài)規(guī)劃
void play(void);                         // 游戲過程

下面的是全局變量。

const int difficult = 6; // 難度
const int move[8][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1},
                                          {-1, -1}, {1, -1}, {1, 1}, {-1, 1}};
                                                 // 八個方向擴展
char map[8][8];                        // 棋盤
IMAGE img[5];                         // 保存圖片
int black, white;                 // 雙方的棋子數(shù)
int X, Y;                              // 白棋的下子點

 

加載素材并且初始化變量:

void load(void)           // 加載素材
{
       // 加載圖片
       loadimage(&img[0], "圖片\\空位.bmp");
       loadimage(&img[1], "圖片\\黑子.bmp");
       loadimage(&img[2], "圖片\\白子.bmp");
       loadimage(&img[3], "圖片\\黑子1.bmp");
       loadimage(&img[4], "圖片\\白子1.bmp");
 
       // 加載音樂
       mciSendString("open 音樂\\背景音樂.wma", NULL, 0, NULL);
       mciSendString("open 音樂\\和局.wma", NULL, 0, NULL);
       mciSendString("open 音樂\\勝利.wma", NULL, 0, NULL);
       mciSendString("open 音樂\\失敗.wma", NULL, 0, NULL);
       mciSendString("open 音樂\\下子.wma", NULL, 0, NULL);
 
       // 初始化棋盤
       initgraph(340, 340);
       IMAGE qipan;
       loadimage(&qipan, "圖片\\棋盤.bmp");
       putimage(0, 0, &qipan);
       setorigin(26, 26);
       SetWindowText(GetHWnd(), "黑白棋AI版");
}

loadimage是Easyx庫中的函數(shù),用于加載圖像。本案例中第一個參數(shù)是保存圖像的 IMAGE 對象指針,第二個是圖像地址。這個函數(shù)還可以拉伸圖片,或者自動適應IMAGE的大小,具體用法參見Easyx的官方文檔。

mciSendString是<mmsystem.h>當中的函數(shù),用來播放多媒體文件的API指令。

initgraph這個函數(shù)用于初始化繪圖窗口。

putimage用于在當前設備上繪制指定圖像。本案例當中代表在(0,0)處繪制棋盤圖形。

setorigin也在Easyx當中,用于設置坐標原點。

SetWindowText是Windows API宏,聲明在WinUser.h當中,用于設定窗口文本

 

繪制棋盤:

void print(void)   // 畫棋盤
{
       int x, y;
       black = white = 0;
       for(x = 0; x < 8; x++)
              for(y = 0; y < 8; y++)
                     switch(map[x][y])
                     {
                            case 0:
                                          putimage(37 * y, 37 * x, &img[0]);
                                   break;
                            case 'B':
                                          putimage(37 * y, 37 * x, &img[1]);
                                   black++;
                                   break;
                            case 'W':
                                          putimage(37 * y, 37 * x, &img[2]);
                                   white++;
                                   break;
                     }
}

利用雙層循環(huán)遍歷棋盤,根據(jù)map[x][y]中的字符,在對應位置繪制棋子就可以了。

 

落子:

void draw(int x, int y, char a)   // 下當前子
{
       char b = T(a);                                                               // 敵方子
       int i, x1, y1, x2, y2;
       bool sign;                  
       for (i = 0; i < 8; i++)
       {
              sign = false;
              x1 = x + move[i][0];
              y1 = y + move[i][1];
              while (0 <= x1 && x1 < 8 && 0 <= y1 && y1 < 8 && map[x1][y1])
              {
                     if(map[x1][y1] == b)
                            sign = true;
                     else
                     {
                            if(sign)
                            {
                                   x1 -= move[i][0];
                                   y1 -= move[i][1];
                                   x2 = x + move[i][0];
                                   y2 = y + move[i][1];
                                   while (((x <= x2 && x2 <= x1) || (x1 <= x2 && x2 <= x)) && ((y <= y2 && y2 <= y1) || (y1 <= y2 && y2 <= y)))
                                   {
                                          map[x2][y2] = a;
                                          x2 += move[i][0];
                                          y2 += move[i][1];
                                   }
                            }
                            break;
                     }
                     x1 += move[i][0];
                     y1 += move[i][1];
              }
       }
       map[x][y] = a;
}

對落子點的八個方向進行檢測。如果在該方向上,遇到的第一個棋子與落子顏色不同,并且沿著這條線下去,最后能找到一個和落子顏色相同的棋子,則將這之間的所有棋子改變顏色。

 

判斷當前位置是否可以落子:

int judge(int x, int y, char a)     // 判斷當前是否可以落下,同draw函數(shù)
{
       if(map[x][y])                                    // 如果當前不是空的返回0值
              return 0;
       char b = T(a);
       int i, x1, y1;
       int n = 0, sign;
       for (i = 0; i < 8; i++)
       {
              sign = 0;
              x1 = x + move[i][0];
              y1 = y + move[i][1];
              while (0 <= x1 && x1 < 8 && 0 <= y1 && y1 < 8 && map[x1][y1])
              {
                     if(map[x1][y1] == b)
                            sign++;
                     else
                     {
                            n += sign;
                            break;
                     }
                     x1 += move[i][0];
                     y1 += move[i][1];
              }
       }
       return n;              // 返回可吃棋數(shù)
}

和上一個函數(shù)差不多的邏輯。

 

判斷是否有棋可吃:

bool Canput(char c)
{
       int x, y;
       for(x = 0; x < 8; x++)
              for(y = 0; y < 8; y++)
                     if(judge(x, y, c))
                            return true;
       return false;
}

遍歷棋盤,在每個地方都調用judge函數(shù)即可。

 

判斷是否有棋存活

bool quit(char c)
{
       int x, y;
       bool b = false, w = false;
       for(x = 0; x < 8; x++)
              for(y = 0; y < 8; y++)
              {
                     if(map[x][y] == c)
                            return false;
              }
       return true;
}

同樣是簡單的遍歷。判斷map當中的字符是否全與參數(shù)相同即可。

 

bool ask(void)     // 彈出對話框
{
       HWND wnd = GetHWnd();
       int key;
       char str[50];
       ostrstream strout(str, 50);
       strout <<"黑:" <<black <<"  白:" <<white <<endl;
       if (black == white)
              strout <<"世界和平";
       else if(black > white)
              strout <<"恭喜你贏了!";
       else
              strout <<"小樣,還想贏我。";
       strout <<"\n再來一局嗎?" <<ends;
       if(black == white)
              key = MessageBox(wnd, str, "和局", MB_YESNO | MB_ICONQUESTION);
       else if(black > white)
              key = MessageBox(wnd, str, "黑勝", MB_YESNO | MB_ICONQUESTION);
       else
              key = MessageBox(wnd, str, "白勝", MB_YESNO | MB_ICONQUESTION);
       if(key == IDYES)
              return true;
       else
              return false;
}

GetHWnd在Easyx中定義,用于返回繪圖窗口句柄。在 Windows 下,句柄是一個窗口的標識,得到句柄后,可以使用 Windows API 中的函數(shù)實現(xiàn)對窗口的控制。

ostrstream strout(str,50);,作用是建立輸出字符串流對象strout,并使strout與字符數(shù)組str關聯(lián)(通過字符串流將數(shù)據(jù)輸出到字符數(shù)組str),流緩沖區(qū)大小為50。

MessageBox()函數(shù)包含在頭文件 windows.h中,它的功能是彈出一個標準的Windows對話框。返回值是一個int型的整數(shù),用于判斷用戶點擊了對話框中的哪一個按鈕。

 

AI決定落子位置:

int D(char c, int step)
{
       // 判斷是否結束遞歸
       if (step > difficult)      // 約束步數(shù)之內
              return 0;
       if (!Canput(c))
       {
              if (Canput(T(c)))
                     return -D(T(c), step);
              else
                     return 0;
       }
 
       int i, j, max = 0, temp, x, y;
       bool ans = false;
 
       // 建立臨時數(shù)組
       char **t = new char *[8];
       for (i = 0; i < 8; i++)
              t[i] = new char [8];
       for (i = 0; i < 8; i++)
              for (j = 0; j < 8; j++)
                     t[i][j] = map[i][j];
 
       // 搜索解法
       for (i = 0; i < 8; i++)
              for (j = 0; j < 8; j++)
                     if (temp = judge(i, j, c))
                     {
                            draw(i, j, c);
                            temp -= D(T(c), step + 1);
                            if (temp > max || !ans)
                            {
                                   max = temp;
                                   x = i;
                                   y = j;
                                   ans = true;
                            }
                            for (int k = 0; k < 8; k++)
                                   for (int l = 0; l < 8; l++)
                                          map[k][l] = t[k][l];
                     }
 
       // 撤銷空間
       for (i = 0; i < 8; i++)
              delete [] t[i];
       delete [] t;
 
       // 如果是第一步則標識白棋下子點
       if (step == 1)
       {
              X = x;
              Y = y;
       }
 
       return max;  // 返回最優(yōu)解
}

先看函數(shù)的輸入。Char c代表落子方,step是當前函數(shù)遞歸了幾次。要保證這個遞歸次數(shù)不大于difficult的值,以此來決定AI的強度。

#define T(c) ((c == 'B') ? 'W' : 'B'),意思是c為黑或者白,T(c)為對立的白或者黑。這個函數(shù)的目的是,也讓AI考慮黑方應該如何下棋,以此來一步一步地推斷出最佳位置。

搜索解法采取遍歷的方法。如果某一點可下,則下該點(不是直接打印在棋盤上),將數(shù)據(jù)存儲到臨時變量當中。遞歸調用指定次數(shù),在這個過程中累計翻轉的棋子,如果值超過了之前的最大值,則將落子點更新為該位置。當遍歷完成之后,AI就能得出每一點在預計步數(shù)之內的最大收益,從而得出最佳落點。

四、完整源碼

黑白棋純C語言完整源碼

點贊(0)

C語言網提供由在職研發(fā)工程師或ACM藍橋杯競賽優(yōu)秀選手錄制的視頻教程,并配有習題和答疑,點擊了解:

一點編程也不會寫的:零基礎C語言學練課程

解決困擾你多年的C語言疑難雜癥特性的C語言進階課程

從零到寫出一個爬蟲的Python編程課程

只會語法寫不出代碼?手把手帶你寫100個編程真題的編程百練課程

信息學奧賽或C++選手的 必學C++課程

藍橋杯ACM、信息學奧賽的必學課程:算法競賽課入門課程

手把手講解近五年真題的藍橋杯輔導課程

Dotcpp在線編譯      (登錄可減少運行等待時間)