大一課程設計——不會五子棋
在撰寫此次課程設計后,我了解到其實C語言不但可以在黑白色的命令提示符界面進行運行,還可以有很美觀的視窗界面,課程設計中我完成了自學EasyX這一圖形庫內各種函式的使用,不僅掌握了如何學習課本之外的知識,更獲得了通過交流論壇和查找資料解決實際應用中的問題的能力,對于這個初級的五子棋小游戲仍有許多可以改進的地方,比如寫一個智能化程度更高的AI,亦或是寫個電腦對戰電腦的DLC,作為計算機專業的學生的樂趣也在于這種理解和創造的程序,
文章目錄
- 大一課程設計——不會五子棋
- 一.簡單雙人五子棋
- 1.judge函式
- 2.make函式
- 二.人機五子棋
- 1.內容提要
- 2.設計內容
- 3.功能模塊詳細設計
- 3.1 游戲初始化
- 3.2 雙人游戲進行
- 3.3 人機游戲進行
- 3.4 勝負判定函式——judge
- 3.5 顯示落子順序函式——arraychess、rr_arraychess、rj_arraychess
- 3.6 人機判斷函式——rj_judge
- 3.7 人機判斷函式——scorejudge
- 3.8 成品展示
一.簡單雙人五子棋
簡單的雙人五子棋只需要克服繪制(插入)棋盤、放置棋子和判斷勝負幾個難點,可以用來熟悉EasyX的基本操作,
可以使用EasyX實作棋盤圖片的插入(附上棋盤圖片),

代碼展示:
#include <graphics.h>
#include <stdio.h>
#include <windows.h>
int chess[35][35];
int judge(int x, int y, int type) //左右上下斜判斷
{
// x = (x + 15) / 25;
// y = (y + 15) / 25;
int i = 0, number = 0;
for (i = 0; i < 4; i++) //上
{
if (chess[x][y - i - 1] == type && y - i - 1 >= 0)
{
number++;
}
}
if (number == 4)
return 1;
else
number = 0;
for (i = 0; i < 4; i++) //下
{
if (chess[x][y + i + 1] == type)
{
number++;
}
}
if (number == 4)
return 1;
else
number = 0;
for (i = 0; i < 4; i++) //左
{
if (chess[x - i - 1][y] == type && x - i - 1 >= 0)
{
number++;
}
}
if (number == 4)
return 1;
else
number = 0;
for (i = 0; i < 4; i++) //右
{
if (chess[x + i + 1][y] == type)
{
number++;
}
}
if (number == 4)
return 1;
else
number = 0;
for (i = 0; i < 4; i++) //左上
{
if (chess[x - i - 1][y - i - 1] == type && x - i - 1 >= 0 && y - i - 1 >= 0)
{
number++;
}
}
if (number == 4)
return 1;
else
number = 0;
for (i = 0; i < 4; i++) //右上
{
if (chess[x + i + 1][y - i - 1] == type && y - i - 1 >= 0)
{
number++;
}
}
if (number == 4)
return 1;
else
number = 0;
for (i = 0; i < 4; i++) //左下
{
if (chess[x - i - 1][y + i + 1] == type && x - i - 1 >= 0)
{
number++;
}
}
if (number == 4)
return 1;
else
number = 0;
for (i = 0; i < 4; i++) //右下
{
if (chess[x + i + 1][y + i + 1] == type)
{
number++;
}
}
if (number == 4)
return 1;
else
number = 0;
return 0;
}
int make(int x, int y, int type)
{
int x1, y1, n = 5;
if (x < 15 || y < 15 || y >= 385 || x >= 385)
return 0;
else
{
x = (x + 15) / 25;
y = (y + 15) / 25;
if (chess[x][y] != 0)
return 0;
else
{
x1 = x * 25;
y1 = y * 25;
if (type == 1)
{
setfillcolor(BLACK);
chess[x][y] = 1;
}
else
{
setfillcolor(WHITE);
chess[x][y] = 2;
}
fillcircle(x1, y1, 5);
n = judge(x, y, type);
if (n == 1) //五子連珠
return 2;
else
return 1;
}
}
}
int main()
{
HWND my_consle = GetForegroundWindow();
ShowWindow(my_consle, SW_SHOW);
int choice = 0, n = 5;
IMAGE img;
MOUSEMSG m;
loadimage(&img, L"五子棋棋盤.gif");
int w, h;
w = img.getwidth();
h = img.getheight();
initgraph(w, h);
putimage(0, 0, &img);
while (1)
{
out:
Sleep(5);
m = GetMouseMsg();
if (m.uMsg == WM_LBUTTONDOWN && choice == 0) //
{
n = make(m.x, m.y, 1);
if (n == 1)
choice = 1;
else if (n == 2)
{
settextstyle(20, 0, L"楷體");
setbkmode(TRANSPARENT);
settextcolor(RED);
outtextxy(120, 150, L"黑方勝利");
break;
}
else
goto out;
}
else if (m.uMsg == WM_LBUTTONDOWN && choice == 1)
{
n = make(m.x, m.y, 2);
if (n == 1)
{
choice = 0;
}
else if (n == 2)
{
settextstyle(20, 0, L"楷體");
setbkmode(TRANSPARENT);
settextcolor(RED);
outtextxy(120, 150, L"白方勝利");
break;
}
else
goto out;
}
else
continue;
}
getchar();
}
1.judge函式
int judge(int x, int y, int type) //左右上下斜判斷
{
// x = (x + 15) / 25;
// y = (y + 15) / 25;
int i = 0, number = 0;
for (i = 0; i < 4; i++) //上
{
if (chess[x][y - i - 1] == type && y - i - 1 >= 0)
{
number++;
}
}
if (number == 4)
return 1;
else
number = 0;
for (i = 0; i < 4; i++) //下
{
if (chess[x][y + i + 1] == type)
{
number++;
}
}
if (number == 4)
return 1;
else
number = 0;
for (i = 0; i < 4; i++) //左
{
if (chess[x - i - 1][y] == type && x - i - 1 >= 0)
{
number++;
}
}
if (number == 4)
return 1;
else
number = 0;
for (i = 0; i < 4; i++) //右
{
if (chess[x + i + 1][y] == type)
{
number++;
}
}
if (number == 4)
return 1;
else
number = 0;
for (i = 0; i < 4; i++) //左上
{
if (chess[x - i - 1][y - i - 1] == type && x - i - 1 >= 0 && y - i - 1 >= 0)
{
number++;
}
}
if (number == 4)
return 1;
else
number = 0;
for (i = 0; i < 4; i++) //右上
{
if (chess[x + i + 1][y - i - 1] == type && y - i - 1 >= 0)
{
number++;
}
}
if (number == 4)
return 1;
else
number = 0;
for (i = 0; i < 4; i++) //左下
{
if (chess[x - i - 1][y + i + 1] == type && x - i - 1 >= 0)
{
number++;
}
}
if (number == 4)
return 1;
else
number = 0;
for (i = 0; i < 4; i++) //右下
{
if (chess[x + i + 1][y + i + 1] == type)
{
number++;
}
}
if (number == 4)
return 1;
else
number = 0;
return 0;
}
judge函式判斷先向上檢索,若該方向上檢索,若連續四個棋子顏色一致則number=4且回傳回傳值1,否則number依然為0并進行另一方向的檢索直至八個方向結束或出現五子連珠,
2.make函式
int make(int x, int y, int type)
{
int x1, y1, n = 5;
if (x < 15 || y < 15 || y >= 385 || x >= 385)
return 0;
else
{
x = (x + 15) / 25;
y = (y + 15) / 25;
if (chess[x][y] != 0)
return 0;
else
{
x1 = x * 25;
y1 = y * 25;
if (type == 1)
{
setfillcolor(BLACK);
chess[x][y] = 1;
}
else
{
setfillcolor(WHITE);
chess[x][y] = 2;
}
fillcircle(x1, y1, 5);
n = judge(x, y, type);
if (n == 1) //五子連珠
return 2;
else
return 1;
}
}
}
make函式用于繪制棋子,并為judge函式提供坐標以判斷勝負
二.人機五子棋
1.內容提要
在初步還原了簡單五子棋的實作方法后就可以嘗試更高級的界面、更高級的AI,因此我選擇五子棋作為課程設計,
本五子棋學習了https://blog.csdn.net/qq_45669199/article/details/101145656和https://blog.csdn.net/ChinaJane163/article/details/52599787兩篇文章作為參考,
人機對戰參考了用評估函式求評估值的方法實作電腦落子,評估函式的物件為(假設電腦為白子):
【1】多少白子連成線(因為越多白子練成線,對電腦越有利)
【2】多少將要連成線的黑子,被這個白子打斷了(還要考慮填了這個子后,電腦對人所落的子的破壞),
評估函式得到的值越大,電腦越傾向在那個位置落子,簡單的說,就是此AI回傳的是第一個評估值最高的位置,
2.設計內容

3.功能模塊詳細設計
3.1 游戲初始化
設定視窗大小與背景圖片一致,讓視窗名稱變為“不會五子棋”并使用API函式修改視窗名稱,加載多張圖片以實作顏色的動態效果,通過滑鼠移動和點擊的位置實作背景的變化和互動,
void initgame()
{
//設定視窗大小
initgraph(500, 750);
//讓視窗名稱變成不會五子棋
// 獲得視窗句柄
HWND hWnd = GetHWnd();
// 使用 API 函式修改視窗名稱
SetWindowText(hWnd, "不會五子棋");
B:;
//加載圖片
IMAGE background;
loadimage(&background, "back.jpg", 500, 750);
putimage(0, 0, &background);
//設定背景顏色
setbkcolor(RGB(195, 195, 195)); //目的是為了讓顯示的文字看起來沒有邊框
//設定五張圖片來顯示出顏色動態效果
IMAGE ch1, ch2, ch3, ch4, ch5, ch6;
loadimage(&ch1, "1.jpg", 500, 750);
loadimage(&ch2, "2.jpg", 500, 750);
loadimage(&ch3, "3.jpg", 500, 750);
loadimage(&ch4, "4.jpg", 500, 750);
//游戲界面選擇
MOUSEMSG m; // 定義滑鼠訊息
while (true)
{
// 獲取滑鼠訊息
m = GetMouseMsg();
//清除滑鼠快取
FlushMouseMsgBuffer();
//放置背景避免黑屏
putimage(0, 0, &background);
switch (m.uMsg)
{
case WM_MOUSEMOVE:
if (m.x >= 0 && m.x <= 220 && m.y >= 495 && m.y <= 545) //(0,495)(220,545) 開始游戲
{
putimage(0, 0, &ch1);
}
if (m.x >= 275 && m.x <= 500 && m.y >= 495 && m.y <= 545) // (275,495)(500,545) 人機對戰
{
putimage(0, 0, &ch2);
}
if (m.x >= 0 && m.x <= 220 && m.y >= 615 && m.y <= 665) // (0,615)(220,665) 操作提示
{
putimage(0, 0, &ch3);
}
if (m.x >= 275 && m.x <= 500 && m.y >= 615 && m.y <= 665) // (275,615)(500,665) 退出游戲
{
putimage(0, 0, &ch4);
}
break;
case WM_LBUTTONDOWN:
if (m.x >= 0 && m.x <= 220 && m.y >= 495 && m.y <= 545) //(0,495)(220,545) 開始游戲
{
goto A;
}
if (m.x >= 275 && m.x <= 500 && m.y >= 495 && m.y <= 545) // (275,495)(500,545) 人機對戰
{
rj_begingame();
}
if (m.x >= 0 && m.x <= 220 && m.y >= 615 && m.y <= 665) // (0,615)(220,665) 操作提示
{
IMAGE explain;
loadimage(&explain, "操作提示.jpg", 500, 750);
putimage(0, 0, &explain);
system("pause");
goto B;
}
if (m.x >= 275 && m.x <= 500 && m.y >= 615 && m.y <= 665) // (275,615)(500,665) 退出游戲
{
//先釋放資源
closegraph();
exit(0);
}
}
}
A:;
}
3.2 雙人游戲進行
以(25,325)(425,725)畫棋盤,從(50,350)開始劃線,游標最初設定于(225,525)(即(25+425)/2,(325,725)/2,即正中間),定義一個有225個元素的結構體陣列來代表游標的所有分布的可能性并以index作為結構體陣列下標進行填充,定義并獲取滑鼠資訊和鍵盤資訊,在下棋程序中通過array保存下棋順序,通過getpixel()(getpixel(int x, int y)用于獲取點的顏色)防止重復下棋,使用judge判斷勝負并輸出結果,悔棋時保證保證是悔的上一顆的棋,
void begingame()
{
//加載背景音樂
mciSendString("open BGM1.mp3 alias bg", NULL, 0, NULL);
mciSendString("play bg repeat", NULL, 0, NULL);
C:
//顯示背景音樂開關
IMAGE BGM;
loadimage(&BGM, "BGM.jpg", 500, 750);
putimage(0, 0, &BGM);
//棋盤加載
setlinecolor(BLACK);
setlinestyle(PS_SOLID);
setfillcolor(RGB(223, 200, 158));
fillrectangle(25, 325, 425, 725); //計算而來的
for (int i = 1; i < 15; i++)
{
for (int j = 1; j < 15; j++)
{
rectangle(25 + j * 25, 325 + i * 25, 50 + j * 25, 350 + i * 25); //棋盤左上是50 350
}
}
//黑子先手 游戲開始游標默認在正中間
//繪制游標
xy.x = 225;
xy.y = 525; // 這個坐標是推出來的 就是最中心的坐標
setlinecolor(RED);
setlinestyle(PS_DOT);
rectangle(xy.x - 15, xy.y - 15, xy.x + 15, xy.y + 15);
//定義一個有225個元素的結構體陣列來代表游標的所有分布的可能性
post master[225];
//填充電腦位置 //棋盤左上是50 350
int index = 0; //定義結構體陣列的下標
for (int i = 1; i <= 15; i++)
{
for (int j = 1; j <= 15; j++)
{
// 25 + j * 25, 325 + i * 25, //棋盤左上是50 350
master[index].x = 25 + j * 25;
master[index].y = 325 + i * 25;
index++;
}
}
//清除上一下滑鼠快取
FlushMouseMsgBuffer();
MOUSEMSG m; // 定義滑鼠訊息
while (true)
{
//按下esc回傳游戲界面
if (_kbhit())
{
char key;
key = _getch();
switch (key)
{
case 27:
{
//重置color
color = 0;
//音樂暫停
mciSendString("close bg", NULL, 0, NULL); //竟然神奇的把pause改成close就可以消除按下esc會頁面在點擊開始游戲直接出來棋子的bug
main();
}
}
}
//清除上一下滑鼠快取讓游標更加靈活
FlushMouseMsgBuffer();
//獲取一潭訓鼠訊息
m = GetMouseMsg();
//是否暫停音樂
if (bgm % 2 == 0)
{
settextcolor(RGB(0, 0, 0));
settextstyle(20, 0, "楷體");
outtextxy(25, 730, "背景音樂:OFF");
mciSendString("pause bg", NULL, 0, NULL);
}
else
{
settextcolor(RGB(0, 0, 0));
settextstyle(20, 0, "楷體");
outtextxy(25, 730, "背景音樂:O N");
mciSendString("resume bg", NULL, 0, NULL);
}
//顯示提示黑子先手或者顯示提示當前將要下的棋子顏色
if (color == 0)
{
settextcolor(RGB(0, 0, 0));
settextstyle(20, 0, "楷體");
outtextxy(30, 300, "黑子先手"); //
}
else if (color % 2 != 0)
{
settextcolor(RGB(0, 0, 0));
settextstyle(20, 0, "楷體");
outtextxy(30, 300, "輪到棋子:");
//提示落子顏色
setfillcolor(WHITE);
setlinecolor(WHITE);
fillcircle(130, 310, 10);
}
else
{
settextcolor(RGB(0, 0, 0));
settextstyle(20, 0, "楷體");
outtextxy(30, 300, "輪到棋子:");
//提示落子顏色
setfillcolor(BLACK);
setlinecolor(BLACK);
fillcircle(130, 310, 10);
}
switch (m.uMsg)
{
case WM_MOUSEMOVE:
for (int i = 0; i < 225; i++)
{
if ((m.x >= master[i].x - 10 && m.x <= master[i].x + 10) && (m.y >= master[i].y - 10 && m.y <= master[i].y + 10)) // m.x == master[i].x && m.y == master[i].y
{
//(m.x >= master[i].x - 5 && m.x <= master[i].x + 5) && (m.y >= master[i].y - 5 && m.y <= master[i].y + 5)
setlinecolor(RGB(223, 200, 158));
rectangle(xy.x - 15, xy.y - 15, xy.x + 15, xy.y + 15);
xy.x = master[i].x;
xy.y = master[i].y;
setlinecolor(RED);
setlinestyle(PS_DOT);
rectangle(xy.x - 15, xy.y - 15, xy.x + 15, xy.y + 15);
}
}
break;
case WM_MBUTTONDOWN:
if (m.mkShift)
{
bgm++; // 如果按下中鍵同時按下shift就決定音樂是否暫停(先按住shift在按住滑鼠中鍵)
Sleep(100);
break;
}
if (m.mkCtrl)
{
//如果按下中鍵同時按下ctrl就重新開始游戲
color = 0; //重置顏色
IMAGE background;
loadimage(&background, "back.jpg", 500, 750);
putimage(0, 0, &background); //重置畫面
goto C;
}
exit(0); //設定強制退出游戲功能
case WM_LBUTTONUP:
//不可以重復下棋 通過getpixel這個函式來實作
if (getpixel(xy.x - 3, xy.y - 3) == RGB(0, 0, 0) || getpixel(xy.x - 3, xy.y - 3) == RGB(255, 255, 255))
{
break;
}
if (color % 2 == 0)
{
//把下的棋的順序保存在array陣列里面
array[color] = xy;
setfillcolor(BLACK);
setlinecolor(BLACK);
fillcircle(xy.x, xy.y, 10);
PlaySound("104.wav", NULL, SND_FILENAME | SND_ASYNC);
COLORREF hei = getpixel(xy.x - 3, xy.y - 3);
//勝負判斷
int a = judge(hei, xy.x, xy.y);
if (a == 1)
{
settextcolor(RGB(0, 0, 0));
settextstyle(30, 0, "楷體");
outtextxy(120, 200, "恭喜黑棋獲勝");
outtextxy(80, 170, "(請按任意鍵繼續游戲)");
char str[30];
sprintf_s(str, "戰了%d回合", color / 2 + 1);
outtextxy(120, 230, str);
PlaySound("ying.wav", NULL, SND_FILENAME | SND_ASYNC);
//顯示落子順序
arraychess();
//重置color
color = 0;
system("pause");
goto C;
}
//加入平局的判斷
if (a == 0 && color == 224) // color最大值是224
{
settextcolor(RGB(0, 0, 0));
settextstyle(30, 0, "楷體");
outtextxy(250, 200, "平局");
outtextxy(65, 170, "(請按任意鍵繼續游戲)");
PlaySound("ying.wav", NULL, SND_FILENAME | SND_ASYNC);
//顯示棋子順序
arraychess();
//重置color
color = 0;
system("pause");
goto C;
}
}
else
{
//把下的棋的順序保存在array陣列里面
array[color] = xy;
setfillcolor(WHITE);
setlinecolor(WHITE);
fillcircle(xy.x, xy.y, 10);
PlaySound("104.wav", NULL, SND_FILENAME | SND_ASYNC);
COLORREF bai = getpixel(xy.x - 3, xy.y - 3);
//勝負判斷
int b = judge(bai, xy.x, xy.y);
if (b == 1)
{
settextcolor(RGB(0, 0, 0));
settextstyle(30, 0, "楷體");
outtextxy(120, 200, "恭喜白棋獲勝!");
outtextxy(80, 170, "(請按任意鍵繼續游戲)");
char str[30];
sprintf_s(str, "大戰了%d回合!", color / 2 + 1);
outtextxy(120, 230, str);
PlaySound("ying.wav", NULL, SND_FILENAME | SND_ASYNC);
//顯示棋子順序
arraychess();
//重置color
color = 0;
system("pause");
goto C;
}
//加入平局的判斷
if (b == 0 && color == 224)
{
settextcolor(RGB(0, 0, 0));
settextstyle(30, 0, "楷體");
outtextxy(105, 200, "平局!");
outtextxy(65, 170, "(請按任意鍵繼續游戲)");
PlaySound("ying.wav", NULL, SND_FILENAME | SND_ASYNC);
//顯示棋子順序
arraychess();
//重置color
color = 0;
system("pause");
goto C;
}
}
//把當前下的棋保存在huixy中
huixy.x = xy.x;
huixy.y = xy.y;
color++;
Sleep(100);
break;
case WM_RBUTTONDOWN: //設定悔棋功能
//保證是悔的上一顆的棋
if (huixy.x == xy.x && huixy.y == xy.y && getpixel(xy.x - 3, xy.y - 3) != RGB(223, 200, 158))
{
setfillcolor(RGB(223, 200, 158));
setlinecolor(RGB(223, 200, 158));
fillcircle(xy.x, xy.y, 10);
setlinecolor(BLACK);
for (int i = 1; i < 15; i++)
{
for (int j = 1; j < 15; j++)
{
rectangle(25 + j * 25, 325 + i * 25, 50 + j * 25, 350 + i * 25);
}
}
color--;
break;
}
}
//來一個重新檢查 主要是讓白棋不存在中心有黑線的情況
for (int i = 0; i < 255; i++)
{
if (getpixel(master[i].x - 3, master[i].y - 3) == RGB(0, 0, 0))
{
setfillcolor(RGB(0, 0, 0));
setlinecolor(RGB(0, 0, 0));
fillcircle(master[i].x, master[i].y, 10);
}
if (getpixel(master[i].x - 3, master[i].y - 3) == RGB(255, 255, 255))
{
setfillcolor(RGB(255, 255, 255));
setlinecolor(RGB(255, 255, 255));
fillcircle(master[i].x, master[i].y, 10);
}
}
}
}
3.3 人機游戲進行
思路和畫棋盤等操作與雙人游戲一致,但是電腦使用評估函式判斷落子方位
核心代碼:
case 1:
{
for (int i = 0; i < 225; i++)
{
if (RGB(0, 0, 0) != getpixel(alphcat[i].x - 3, alphcat[i].y - 3) && RGB(255, 255, 255) != getpixel(alphcat[i].x - 3, alphcat[i].y - 3)
/*當前落棋點沒有落棋 (這個點的顏色是背景色)*/)
{
//RGB(255, 255, 255) != getpixel(alphcat[i].x - 3, alphcat[i].y - 3)
alphcat[i].score = scorejudge(alphcat[i].x, alphcat[i].y);
}
}
//再來選出一個得分最大的,如果得分一樣就隨機從最大的一個選一個
alph alphmax;
alphmax.score = alphcat[0].score;
for (int i = 1; i < 225; i++)
{
if (alphcat[i].score >= alphmax.score)
{
alphmax = alphcat[i];
}
}
// 把下的棋的順序保存在array陣列里面
rjarray[color] = alphmax;
//根據這個點畫白棋
setfillcolor(WHITE);
setlinecolor(WHITE);
fillcircle(alphmax.x, alphmax.y, 10);
PlaySound("104.wav", NULL, SND_FILENAME | SND_ASYNC);
for (int i = 0; i < 225; i++)
{
alphcat[i].score = 0;
}
COLORREF bai = getpixel(alphmax.x - 3, alphmax.y - 3);
3.4 勝負判定函式——judge
回傳1表示勝負已定,回傳0表示勝負未分,
檢查一個方向的5個格子,通過顏色判斷,若該方向未定勝負則還原落子位置繼續判斷,(與簡單五子棋思路相似)
int judge(COLORREF cl, int x, int y)
{
int a, b;
//定義上下總個數
int i = 0;
a = x;
b = y;
/*x = xy.x;
y = xy.y;*/
//先檢查上面的
while (cl == getpixel(a - 3, b - 3 - 25))
{
i++;
b -= 25;
}
//還原落子位置
a = x;
b = y;
//再檢查下面的
while (cl == getpixel(a - 3, b - 3 + 25))
{
i++;
b += 25;
}
//還原落子位置
a = x;
b = y;
if (i >= 4)
{
return 1;
}
//定義左右總個數
int j = 0;
a = x;
b = y;
//先檢查左面的
while (cl == getpixel(a - 3 - 25, b - 3))
{
j++;
a -= 25;
}
//還原落子位置
a = x;
b = y;
//再檢查下面的
while (cl == getpixel(a - 3 + 25, b - 3))
{
j++;
a += 25;
}
//還原落子位置
a = x;
b = y;
if (j >= 4)
{
return 1;
}
//定義'\'總個數
int k = 0;
a = x;
b = y;
//先檢查上面的
while (cl == getpixel(a - 3 - 25, b - 3 - 25))
{
k++;
a -= 25;
b -= 25;
}
//還原落子位置
a = x;
b = y;
//再檢查下面的
while (cl == getpixel(a - 3 + 25, b - 3 + 25))
{
k++;
a += 25;
b += 25;
}
//還原落子位置
a = x;
b = y;
if (k >= 4)
{
return 1;
}
//定義'/'總個數
int l = 0;
a = x;
b = y;
//先檢查上面的
while (cl == getpixel(a - 3 + 25, b - 3 - 25))
{
l++;
a += 25;
b -= 25;
}
//還原落子位置
a = x;
b = y;
//再檢查下面的
while (cl == getpixel(a - 3 - 25, b - 3 + 25))
{
l++;
a -= 25;
b += 25;
}
//還原落子位置
a = x;
b = y;
if (l >= 4)
{
return 1;
}
return 0;
}
3.5 顯示落子順序函式——arraychess、rr_arraychess、rj_arraychess
arraychess用于顯示雙人游戲落子順序
rr_arraychess用于顯示人機游戲玩家落子順序
rj_arraychess用于顯示電腦游戲玩家落子順序
主要實作的功能是在黑色的棋上顯示白色順序,在白色棋子上顯示黑色的順序,在結束后恢復背景顏色,
void arraychess()
{
//主要實作的功能是在黑色的棋上顯示白色順序 在白色棋子上顯示黑色的順序
for (int i = 0; i <= color; i++)
{
if (i % 2 == 0)
{
setbkcolor(RGB(0, 0, 0));
char arr[10];
sprintf_s(arr, "%-2d", i + 1);
settextcolor(RGB(255, 255, 255));
settextstyle(15, 0, "楷體");
outtextxy(array[i].x - 7, array[i].y - 7, arr);
}
else
{
setbkcolor(RGB(255, 255, 255));
char arr[10];
sprintf_s(arr, "%-2d", i + 1);
settextcolor(RGB(0, 0, 0));
settextstyle(15, 0, "楷體");
outtextxy(array[i].x - 7, array[i].y - 7, arr);
}
}
//恢復了背景顏色
setbkcolor(RGB(195, 195, 195));
}
void rj_arraychess()
{
// 在白色棋子上顯示黑色的順序
for (int i = 0; i <= color; i++)
{
if (i % 2 != 0)
{
setbkcolor(RGB(255, 255, 255));
char arr[10];
sprintf_s(arr, "%-2d", i + 1);
settextcolor(RGB(0, 0, 0));
settextstyle(15, 0, "楷體");
outtextxy(rjarray[i].x - 7, rjarray[i].y - 7, arr);
}
}
//恢復了背景顏色
setbkcolor(RGB(195, 195, 195));
}
void rr_arraychess()
{
// 在黑色棋子上顯示白色的順序
for (int i = 0; i <= color; i++)
{
if (i % 2 == 0)
{
setbkcolor(RGB(0, 0, 0));
char arr[10];
sprintf_s(arr, "%-2d", i + 1);
settextcolor(RGB(255, 255, 255));
settextstyle(15, 0, "楷體");
outtextxy(array[i].x - 7, array[i].y - 7, arr);
}
}
//恢復了背景顏色
setbkcolor(RGB(195, 195, 195));
}
3.6 人機判斷函式——rj_judge
創建一個人機判斷 死幾活幾的通解 引數分別是顏色,x坐標,y坐標,死活判斷(1代表死,2代表活),死幾或者活幾,回傳1代表成立,回傳0代表不成立,具體見代碼部分注釋,(https://blog.csdn.net/ChinaJane163/article/details/52599787)
int rj_judge(COLORREF cl, int x, int y, int q, int p)
{
//用一個K來體現某一個方向的下一個是背景而不是有另外顏色的(活) K = 1 表示下一個為背景色
int K = 0;
int a, b;
//定義上下總個數
int i = 0;
a = x;
b = y;
//先檢查上面的
while (cl == getpixel(a - 3, b - 3 - 25))
{
i++;
b -= 25;
}
//檢查是否下一個是背景色
if (getpixel(a - 3, b - 3 - 25) == RGB(223, 200, 158) && b - 25 > 350)
{
K++;
}
//還原落子位置
a = x;
b = y;
//再檢查下面的
while (cl == getpixel(a - 3, b - 3 + 25))
{
i++;
b += 25;
}
//檢查是否下一個是背景色
if (getpixel(a - 3, b - 3 + 25) == RGB(223, 200, 158) && b + 35 < 700)
{
K++;
}
//還原落子位置
a = x;
b = y;
if (i == p - 1 && K == q)
{
return 1;
}
//重置K
K = 0;
//定義左右總個數
int j = 0;
a = x;
b = y;
//先檢查左面的
while (cl == getpixel(a - 3 - 25, b - 3))
{
j++;
a -= 25;
}
//檢查是否下一個是背景色
if (getpixel(a - 3 - 25, b - 3) == RGB(223, 200, 158) && a - 35 > 50)
{
K++;
}
//還原落子位置
a = x;
b = y;
//再檢查下面的
while (cl == getpixel(a - 3 + 25, b - 3))
{
j++;
a += 25;
}
//檢查是否下一個是背景色
if (getpixel(a - 3 + 25, b - 3) == RGB(223, 200, 158) && a + 35 < 700)
{
K++;
}
//還原落子位置
a = x;
b = y;
if (j == p - 1 && K == q)
{
return 1;
}
//重置K
K = 0;
//定義'\'總個數
int k = 0;
a = x;
b = y;
//先檢查上面的
while (cl == getpixel(a - 3 - 25, b - 3 - 25))
{
k++;
a -= 25;
b -= 25;
}
//檢查是否下一個是背景色
if (getpixel(a - 3 - 25, b - 3 - 25) == RGB(223, 200, 158) && a - 25 > 50 && b - 25 > 350)
{
K++;
}
//還原落子位置
a = x;
b = y;
//再檢查下面的
while (cl == getpixel(a - 3 + 25, b - 3 + 25))
{
k++;
a += 25;
b += 25;
}
//檢查是否下一個是背景色
if (getpixel(a - 3 + 25, b - 3 + 25) == RGB(223, 200, 158) && a + 25 < 400 && b + 25 < 700)
{
K++;
}
//還原落子位置
a = x;
b = y;
if (k == p - 1 && K == q)
{
return 1;
}
//重置K
K = 0;
//定義'/'總個數
int l = 0;
a = x;
b = y;
//先檢查上面的
while (cl == getpixel(a - 3 + 25, b - 3 - 25))
{
l++;
a += 25;
b -= 25;
}
//檢查是否下一個是背景色
if (getpixel(a - 3 + 25, b - 3 - 25) == RGB(223, 200, 158) && a + 25 < 400 && b - 25 > 350)
{
K++;
}
//還原落子位置
a = x;
b = y;
//再檢查下面的
while (cl == getpixel(a - 3 - 25, b - 3 + 25))
{
l++;
a -= 25;
b += 25;
}
//檢查是否下一個是背景色
if (getpixel(a - 3 - 25, b - 3 + 25) == RGB(223, 200, 158) && a - 25 > 50 && b + 25 < 700)
{
K++;
}
//還原落子位置
a = x;
b = y;
if (l == p - 1 && K == q)
{
return 1;
}
return 0;
}
3.7 人機判斷函式——scorejudge
參考了CSDN上的粗暴版AI,通過評估函式回傳值的大小確定落子位置,
計分表如下:
| 白子為電腦 | 黑子為玩家 |
|---|---|
| 白子連成五子 +10000 | 阻礙黑子連成五子 +1000 |
| 白子連成活四 +200 | 阻礙黑子連成活四 +100 |
| 白子連成死四 +50 | 阻礙黑子連成死四 +20 |
| 白子連成活三 +30 | 阻礙黑子連成活三 +10 |
| 白子連成死三 +8 | 阻礙黑子連成死三 +5 |
| 白子成成活二 +2 | 阻礙黑子連成活二 +1 |
| 白子連成死二 +2 | 阻礙黑子連成死二 +1 |
| 白子連成活一 +1 | 阻礙黑子連成活一 +0 |
| 白子連成活二 +1 | 阻礙黑子連成死一 +0 |
————————————————
原文鏈接:https://blog.csdn.net/ChinaJane163/article/details/52599787
int scorejudge(int x, int y)
{
/*主要思想(來自https://blog.csdn.net/ChinaJane163/article/details/52599787)
電腦白子 自己黑子
白子連成五子 +10000 阻礙黑子連成五子 +1000
白子連成活四 +200 阻礙黑子連成活四 +100
白子連成死四 +50 阻礙黑子連成死四 +20
白子連成活三 +30 阻礙黑子連成活三 +10
白子連成死三 +8 阻礙黑子連成死三 +5
白子連成活二 +2 阻礙黑子連成活二 +1
白子連成死二 +2 阻礙黑子連成死二 +1
白子連成活一 +1 阻礙黑子連成活一 +0
白子連成死一 +1 阻礙黑子連成死一 +0
*/
int a = x;
int b = y;
//定義一個得分
int score = 0;
//白子連成五子 +10000
if (judge(RGB(255, 255, 255), a, b)) //這個函式只是i的值與k的值不一樣 所以可以加兩個變數在函式里面
{
score += 10000;
}
//阻礙黑子連成五子 +1000
if (judge(RGB(0, 0, 0), a, b))
{
score += 1000;
}
//白子連成活四 +200
if (rj_judge(RGB(255, 255, 255), a, b, 2, 4))
{
score += 200;
}
//阻礙黑子連成活四 +100
if (rj_judge(RGB(0, 0, 0), a, b, 2, 4))
{
score += 100;
}
//白子連成死四 +50
if (rj_judge(RGB(255, 255, 255), a, b, 1, 4))
{
score += 50;
}
//阻礙黑子連成死四 +20
if (rj_judge(RGB(0, 0, 0), a, b, 1, 4))
{
score += 20;
}
//白子連成活三 +30
if (rj_judge(RGB(255, 255, 255), a, b, 2, 3))
{
score += 30;
}
//阻礙黑子連成活三 +10
if (rj_judge(RGB(0, 0, 0), a, b, 2, 3))
{
score += 10;
}
//白子連成死三 +8
if (rj_judge(RGB(255, 255, 255), a, b, 1, 3))
{
score += 8;
}
//阻礙黑子連成死三 +5
if (rj_judge(RGB(0, 0, 0), a, b, 1, 3))
{
score += 5;
}
//白子連成活二 +2
if (rj_judge(RGB(255, 255, 255), a, b, 2, 2))
{
score += 2;
}
//阻礙黑子連成活二 +1
if (rj_judge(RGB(0, 0, 0), a, b, 2, 2))
{
score += 1;
}
//白子連成死二 +2
if (rj_judge(RGB(255, 255, 255), a, b, 1, 2))
{
score += 2;
}
//阻礙黑子連成死二 +1
if (rj_judge(RGB(0, 0, 0), a, b, 1, 2))
{
score += 1;
}
//白子連成活一 +1
if (rj_judge(RGB(255, 255, 255), a, b, 2, 1))
{
score += 1;
}
//白子連成死一 +1
if (rj_judge(RGB(255, 255, 255), a, b, 1, 1))
{
score += 1;
}
return score;
}
3.8 成品展示
#include <stdio.h>
#include <stdlib.h>
#include <graphics.h>
#include <conio.h>
#pragma comment(lib,"winmm.lib") //背景音樂
//游戲初始化
void initgame();
//人人游戲進行
void begingame();
//人機游戲進行
void rj_begingame();
//游戲結束釋放資源
void gameover();
//游戲勝負的判定 回傳1表示勝負已定 回傳0表示勝負未分
int judge(COLORREF, int, int);
//人人模式游戲結束顯示下棋順序
void arraychess();
//人機模式游戲結束顯示電腦下棋順序
void rj_arraychess();
//人機模式游戲結束顯示人下棋順序
void rr_arraychess();
//創建一個人機判斷 死幾活幾的通解 引數分別是 顏色, x坐標, y坐標, 死活判斷(1代表死,2代表活), 死幾或者活幾 回傳1代表成立,回傳0代表不成立
int rj_judge(COLORREF , int , int , int , int );
//人機每一步分析可用棋子上的分數值,回傳分數最高的就是落子點
int scorejudge(int, int);
//設定游標結構體型別
typedef struct
{
int x;
int y;
}post;
//設定人機結構體型別
typedef struct
{
int x;
int y;
int score;
}alph;
//設定游標
post xy;
//用一個數的奇偶性來判斷產生棋子的黑白 偶數表示黑棋 當color == 225的時候表明平局
int color = 0;
//用一個開關來控制游戲的背景音樂; 奇數表示開
int bgm = 1;
//為了防止出現隨意悔棋的情況 所以規定每個人只能悔當前下的棋 所以定義一個游標記錄將要悔棋的地點
post huixy;
//人悔了一步棋,機器也要退回上一個的位置
alph rjhuixy;
//定義一個稍微大一點的結構體陣列來存放每一步棋子順序(人人)
post array[1000] = { 0 };
//定義一個稍微大一點的結構體陣列來存放每一步棋子順序(人機)
alph rjarray[1000] = { 0 };
int main()
{
initgame();
begingame();
gameover();
system("pause");
return 0;
}
void initgame()
{
//設定視窗大小
initgraph(500, 750);
//讓視窗名稱變成不會五子棋
// 獲得視窗句柄
HWND hWnd = GetHWnd();
// 使用 API 函式修改視窗名稱
SetWindowText(hWnd, "不會五子棋");
B:;
//加載圖片
IMAGE background;
loadimage(&background, "back.jpg", 500, 750);
putimage(0, 0, &background);
//設定背景顏色
setbkcolor(RGB(195,195,195)); //目的是為了讓顯示的文字看起來沒有邊框
//設定五張圖片來顯示出顏色動態效果
IMAGE ch1, ch2, ch3, ch4, ch5 ,ch6;
loadimage(&ch1, "1.jpg", 500, 750);
loadimage(&ch2, "2.jpg", 500, 750);
loadimage(&ch3, "3.jpg", 500, 750);
loadimage(&ch4, "4.jpg", 500, 750);
//游戲界面選擇
MOUSEMSG m; // 定義滑鼠訊息
while (true)
{
// 獲取滑鼠訊息
m = GetMouseMsg();
//清除滑鼠快取
FlushMouseMsgBuffer();
//放置背景避免黑屏
putimage(0, 0, &background);
switch (m.uMsg)
{
case WM_MOUSEMOVE:
if (m.x >=0 && m.x <= 220 && m.y >= 495 && m.y <= 545) //(0,495)(220,545) 開始游戲
{
putimage(0, 0, &ch1);
}
if (m.x >= 275 && m.x <= 500 && m.y >= 495 && m.y <= 545) // (275,495)(500,545) 人機對戰
{
putimage(0, 0, &ch2);
}
if (m.x >= 0 && m.x <= 220 && m.y >= 615 && m.y <= 665) // (0,615)(220,665) 操作提示
{
putimage(0, 0, &ch3);
}
if (m.x >= 275 && m.x <= 500 && m.y >= 615 && m.y <= 665) // (275,615)(500,665) 退出游戲
{
putimage(0, 0, &ch4);
}
break;
case WM_LBUTTONDOWN:
if (m.x >= 0 && m.x <= 220 && m.y >= 495 && m.y <= 545) //(0,495)(220,545) 開始游戲
{
goto A;
}
if (m.x >= 275 && m.x <= 500 && m.y >= 495 && m.y <= 545) // (275,495)(500,545) 人機對戰
{
rj_begingame();
}
if (m.x >= 0 && m.x <= 220 && m.y >= 615 && m.y <= 665) // (0,615)(220,665) 操作提示
{
IMAGE explain;
loadimage(&explain, "操作提示.jpg", 500, 750);
putimage(0, 0, &explain);
system("pause");
goto B;
}
if (m.x >= 275 && m.x <= 500 && m.y >= 615 && m.y <= 665) // (275,615)(500,665) 退出游戲
{
//先釋放資源
closegraph();
exit(0);
}
}
}
A:;
}
void begingame()
{
//加載背景音樂
mciSendString("open BGM1.mp3 alias bg", NULL, 0, NULL);
mciSendString("play bg repeat", NULL, 0, NULL);
C:
//顯示背景音樂開關
IMAGE BGM;
loadimage(&BGM, "BGM.jpg", 500, 750);
putimage(0, 0, &BGM);
//棋盤加載
setlinecolor(BLACK);
setlinestyle(PS_SOLID);
setfillcolor(RGB(223,200,158));
fillrectangle(25, 325, 425, 725); //計算而來的
for (int i = 1; i < 15; i++)
{
for (int j = 1; j < 15; j++)
{
rectangle(25 + j * 25, 325 + i * 25, 50 + j * 25, 350 + i * 25); //棋盤左上是50 350
}
}
//黑子先手 游戲開始游標默認在正中間
//繪制游標
xy.x = 225;
xy.y = 525; // 這個坐標是推出來的 就是最中心的坐標
setlinecolor(RED);
setlinestyle(PS_DOT);
rectangle(xy.x - 15, xy.y - 15, xy.x + 15, xy.y + 15);
//定義一個有225個元素的結構體陣列來代表游標的所有分布的可能性
post master[225];
//填充電腦位置 //棋盤左上是50 350
int index = 0; //定義結構體陣列的下標
for (int i = 1; i <= 15; i++)
{
for (int j = 1; j <= 15; j++)
{
//25 + j * 25, 325 + i * 25, //棋盤左上是50 350
master[index].x = 25 + j * 25;
master[index].y = 325 + i * 25;
index++;
}
}
//清除上一下滑鼠快取
FlushMouseMsgBuffer();
MOUSEMSG m; // 定義滑鼠訊息
while (true)
{
//按下esc回傳游戲界面
if (_kbhit())
{
char key;
key = _getch();
switch (key)
{
case 27:
{
//重置color
color = 0;
//音樂暫停
mciSendString("close bg", NULL, 0, NULL); //竟然神奇的把pause改成close就可以消除按下esc會頁面在點擊開始游戲直接出來棋子的bug
main();
}
}
}
//清除上一下滑鼠快取讓游標更加靈活
FlushMouseMsgBuffer();
//獲取一潭訓鼠訊息
m = GetMouseMsg();
//是否暫停音樂
if (bgm % 2 == 0)
{
settextcolor(RGB(0, 0, 0));
settextstyle(20, 0, "楷體");
outtextxy(25, 730, "背景音樂:OFF");
mciSendString("pause bg", NULL, 0, NULL);
}
else
{
settextcolor(RGB(0, 0, 0));
settextstyle(20, 0, "楷體");
outtextxy(25, 730, "背景音樂:O N");
mciSendString("resume bg", NULL, 0, NULL);
}
//顯示提示黑子先手或者顯示提示當前將要下的棋子顏色
if (color == 0)
{
settextcolor(RGB(0, 0, 0));
settextstyle(20, 0, "楷體");
outtextxy(30, 300, "黑子先手");//
}
else if(color % 2 != 0)
{
settextcolor(RGB(0, 0, 0));
settextstyle(20, 0, "楷體");
outtextxy(30, 300, "輪到棋子:");
//提示落子顏色
setfillcolor(WHITE);
setlinecolor(WHITE);
fillcircle(130, 310, 10);
}
else
{
settextcolor(RGB(0, 0, 0));
settextstyle(20, 0, "楷體");
outtextxy(30, 300, "輪到棋子:");
//提示落子顏色
setfillcolor(BLACK);
setlinecolor(BLACK);
fillcircle(130, 310, 10);
}
switch (m.uMsg)
{
case WM_MOUSEMOVE:
for (int i = 0; i < 225; i++)
{
if ( (m.x >= master[i].x - 10 && m.x <= master[i].x + 10) && (m.y >= master[i].y - 10 && m.y <= master[i].y + 10) ) // m.x == master[i].x && m.y == master[i].y
{
//(m.x >= master[i].x - 5 && m.x <= master[i].x + 5) && (m.y >= master[i].y - 5 && m.y <= master[i].y + 5)
setlinecolor(RGB(223,200,158));
rectangle(xy.x - 15, xy.y - 15, xy.x + 15, xy.y + 15);
xy.x = master[i].x;
xy.y = master[i].y;
setlinecolor(RED);
setlinestyle(PS_DOT);
rectangle(xy.x - 15, xy.y - 15, xy.x + 15, xy.y + 15);
}
}
break;
case WM_MBUTTONDOWN:
if (m.mkShift)
{
bgm++; // 如果按下中鍵同時按下shift就決定音樂是否暫停(先按住shift在按住滑鼠中鍵)
Sleep(100);
break;
}
if (m.mkCtrl)
{
//如果按下中鍵同時按下ctrl就重新開始游戲
color = 0; //重置顏色
IMAGE background;
loadimage(&background, "back.jpg", 500, 750);
putimage(0, 0, &background); //重置畫面
goto C;
}
exit(0); //設定強制退出游戲功能
case WM_LBUTTONUP:
//通過getpixel實作不可重復下棋
if (getpixel(xy.x - 3, xy.y - 3) == RGB(0, 0, 0) || getpixel(xy.x - 3, xy.y - 3) == RGB(255, 255, 255))
{
break;
}
if (color % 2 == 0)
{
//把落子順序保存在array陣列里
array[color] = xy;
setfillcolor(BLACK);
setlinecolor(BLACK);
fillcircle(xy.x, xy.y, 10);
PlaySound("104.wav", NULL, SND_FILENAME | SND_ASYNC);
COLORREF hei = getpixel(xy.x - 3, xy.y - 3);
//勝負判斷
int a = judge(hei, xy.x, xy.y);
if (a == 1)
{
settextcolor(RGB(0, 0, 0));
settextstyle(30, 0, "楷體");
outtextxy(120, 200, "恭喜黑棋獲勝");
outtextxy(80, 170, "(請按任意鍵繼續游戲)");
char str[30];
sprintf_s(str, "戰了%d回合", color / 2 + 1);
outtextxy(120, 230, str);
PlaySound("ying.wav", NULL, SND_FILENAME | SND_ASYNC);
//顯示落子順序
arraychess();
//重置color
color = 0;
system("pause");
goto C;
}
//加入平局的判斷
if (a == 0 && color == 224) //color最大值是224
{
settextcolor(RGB(0, 0, 0));
settextstyle(30, 0, "楷體");
outtextxy(250, 200, "平局");
outtextxy(65, 170, "(請按任意鍵繼續游戲)");
PlaySound("ying.wav", NULL, SND_FILENAME | SND_ASYNC);
//顯示棋子順序
arraychess();
//重置color
color = 0;
system("pause");
goto C;
}
}
else
{
//把落子保存在array陣列里
array[color] = xy;
setfillcolor(WHITE);
setlinecolor(WHITE);
fillcircle(xy.x, xy.y, 10);
PlaySound("104.wav", NULL, SND_FILENAME | SND_ASYNC);
COLORREF bai = getpixel(xy.x - 3, xy.y - 3);
//勝負判斷
int b = judge(bai, xy.x, xy.y);
if (b == 1)
{
settextcolor(RGB(0, 0, 0));
settextstyle(30, 0, "楷體");
outtextxy(120, 200, "恭喜白棋獲勝");
outtextxy(80, 170, "(請按任意鍵繼續游戲)");
char str[30];
sprintf_s(str, "大戰了%d回合!", color / 2 + 1);
outtextxy(120, 230, str);
PlaySound("ying.wav", NULL, SND_FILENAME | SND_ASYNC);
//顯示棋子順序
arraychess();
//重置color
color = 0;
system("pause");
goto C;
}
//加入平局的判斷
if (b == 0 && color == 224)
{
settextcolor(RGB(0, 0, 0));
settextstyle(30, 0, "楷體");
outtextxy(105, 200, "平局");
outtextxy(65, 170, "(請按任意鍵繼續游戲)");
PlaySound("ying.wav", NULL, SND_FILENAME | SND_ASYNC);
//顯示棋子順序
arraychess();
//重置color
color = 0;
system("pause");
goto C;
}
}
//把當前下的棋保存在huixy中
huixy.x = xy.x;
huixy.y = xy.y;
color++;
Sleep(100);
break;
case WM_RBUTTONDOWN:
//設定悔棋功能
//保證是悔的上一顆的棋
if (huixy.x == xy.x && huixy.y == xy.y && getpixel(xy.x - 3, xy.y - 3) != RGB(223,200,158))
{
setfillcolor(RGB(223,200,158));
setlinecolor(RGB(223,200,158));
fillcircle(xy.x, xy.y, 10);
setlinecolor(BLACK);
for (int i = 1; i < 15; i++)
{
for (int j = 1; j < 15; j++)
{
rectangle(25 + j * 25, 325 + i * 25, 50 + j * 25, 350 + i * 25);
}
}
color--;
break;
}
}
//重新檢查防止主要是讓白棋中心出現黑線
for (int i = 0; i < 255; i++)
{
if (getpixel(master[i].x - 3, master[i].y - 3) == RGB(0, 0, 0))
{
setfillcolor(RGB(0, 0, 0));
setlinecolor(RGB(0, 0, 0));
fillcircle(master[i].x, master[i].y, 10);
}
if (getpixel(master[i].x - 3, master[i].y - 3) == RGB(255, 255, 255))
{
setfillcolor(RGB(255, 255, 255));
setlinecolor(RGB(255, 255, 255));
fillcircle(master[i].x, master[i].y, 10);
}
}
}
}
void rj_begingame()
{
//加載背景音樂
mciSendString("open BGM.mp3 alias bg", NULL, 0, NULL);
mciSendString("play bg repeat", NULL, 0, NULL);
C:
//顯示背景音樂開關
IMAGE BGM;
loadimage(&BGM, "BGM.jpg", 500, 750);
putimage(0, 0, &BGM);
// 棋盤加載
setlinecolor(BLACK);
setlinestyle(PS_SOLID);
setfillcolor(RGB(223,200,158));
fillrectangle(25, 325, 425, 725);
for (int i = 1; i < 15; i++)
{
for (int j = 1; j < 15; j++)
{
rectangle(25 + j * 25, 325 + i * 25, 50 + j * 25, 350 + i * 25); //棋盤左上是50 350
}
}
//黑子先手 游戲開始游標默認在正中間
//繪制游標
xy.x = 225;
xy.y = 525;
setlinecolor(RED);
setlinestyle(PS_DOT);
rectangle(xy.x - 15, xy.y - 15, xy.x + 15, xy.y + 15);
//初始化人機阿爾法貓
//加載每個點并且把分數設為0
alph alphcat[225];
//填充電腦位置
//棋盤左上是50 350
int inde = 0; //定義結構體陣列的下標
for (int i = 1; i <= 15; i++)
{
for (int j = 1; j <= 15; j++)
{
//25 + j * 25, 325 + i * 25,
alphcat[inde].x = 25 + j * 25;
alphcat[inde].y = 325 + i * 25;
alphcat[inde].score = 0;
inde++;
}
}
//定義一個有225個元素的結構體陣列來代表游標的所有分布的可能性
post master[225];
//填充電腦位置
int index = 0; //定義結構體陣列的下標
for (int i = 1; i <= 15; i++)
{
for (int j = 1; j <= 15; j++)
{
//25 + j * 25, 325 + i * 25,
master[index].x = 25 + j * 25;
master[index].y = 325 + i * 25;
index++;
}
}
//清除上一下滑鼠快取
FlushMouseMsgBuffer();
MOUSEMSG m; // 定義滑鼠訊息
while (true)
{
//按下esc回傳游戲界面
if (_kbhit())
{
char key;
key = _getch();
switch (key)
{
case 27:
{
//重置color
color = 0;
//音樂暫停
mciSendString("close bg", NULL, 0, NULL);
main();
}
}
}
//清除上一下滑鼠快取,讓游標更加靈活
FlushMouseMsgBuffer();
//獲取一潭訓鼠訊息
m = GetMouseMsg();
//是否暫停音樂
if (bgm % 2 == 0)
{
settextcolor(RGB(0, 0, 0));
settextstyle(20, 0, "楷體");
outtextxy(25, 730, "背景音樂:OFF");
mciSendString("pause bg", NULL, 0, NULL);
}
else
{
settextcolor(RGB(0, 0, 0));
settextstyle(20, 0, "楷體");
outtextxy(25, 730, "背景音樂:O N");
mciSendString("resume bg", NULL, 0, NULL);
}
//顯示提示黑子先手或者顯示提示當前將要下的棋子顏色
if (color == 0)
{
settextcolor(RGB(0, 0, 0));
settextstyle(20, 0, "楷體");
outtextxy(30, 300, "黑子先手");
}
else if (color % 2 != 0)
{
settextcolor(RGB(0, 0, 0));
settextstyle(20, 0, "楷體");
outtextxy(30, 300, "輪到棋子:");
//提示落子顏色
setfillcolor(WHITE);
setlinecolor(WHITE);
fillcircle(130, 310, 10);
}
else
{
settextcolor(RGB(0, 0, 0));
settextstyle(20, 0, "楷體");
outtextxy(30, 300, "輪到棋子:");
//提示落子顏色
setfillcolor(BLACK);
setlinecolor(BLACK);
fillcircle(130, 310, 10);
}
switch (m.uMsg)
{
case WM_MOUSEMOVE:
for (int i = 0; i < 225; i++)
{
if ((m.x >= master[i].x - 10 && m.x <= master[i].x + 10) && (m.y >= master[i].y - 10 && m.y <= master[i].y + 10))
{
setlinecolor(RGB(223,200,158));
rectangle(xy.x - 15, xy.y - 15, xy.x + 15, xy.y + 15);
xy.x = master[i].x;
xy.y = master[i].y;
setlinecolor(RED);
setlinestyle(PS_DOT);
rectangle(xy.x - 15, xy.y - 15, xy.x + 15, xy.y + 15);
}
}
break;
case WM_MBUTTONDOWN:
if (m.mkShift)
{
bgm++; //按下中鍵同時按下shift就決定音樂是否暫停(先按住shift在按住滑鼠中鍵)
Sleep(100);
break;
}
if (m.mkCtrl)
{
//如果按下中鍵同時按下ctrl就重新開始游戲
color = 0; //重置顏色
IMAGE background;
loadimage(&background, "back.jpg", 500, 750);
putimage(0, 0, &background); //重置畫面
goto C;
}
exit(0); //設定強制退出游戲功能
case WM_LBUTTONUP:
{
//通過getpixel函式防止重復下棋
if (getpixel(xy.x - 3, xy.y - 3) == RGB(0, 0, 0) || getpixel(xy.x - 3, xy.y - 3) == RGB(255, 255, 255))
{
break;
}
//把落子順序保存在array陣列里
array[color] = xy;
setfillcolor(BLACK);
setlinecolor(BLACK);
fillcircle(xy.x, xy.y, 10);
PlaySound("104.wav", NULL, SND_FILENAME | SND_ASYNC);
COLORREF hei = getpixel(xy.x - 3, xy.y - 3);
//勝負判斷
int a = judge(hei, xy.x, xy.y);
if (a == 1)
{
settextcolor(RGB(0, 0, 0));
settextstyle(30, 0, "楷體");
outtextxy(120, 200, "恭喜你打敗電腦");
outtextxy(80, 170, "(請按任意鍵繼續游戲)");
char str[30];
sprintf_s(str, "戰了%d回合", color / 2 + 1);
outtextxy(120, 230, str);
PlaySound("ying.wav", NULL, SND_FILENAME | SND_ASYNC);
//顯示棋子順序
rr_arraychess();//人
rj_arraychess();//電腦
//重置color
color = 0;
system("pause");
goto C;
}
//加入平局的判斷
if (a == 0 && color == 224) //因為color最大值是224
{
settextcolor(RGB(0, 0, 0));
settextstyle(30, 0, "楷體");
outtextxy(80, 200, "平局");
outtextxy(65, 170, "(請按任意鍵繼續游戲)");
PlaySound("ying.wav", NULL, SND_FILENAME | SND_ASYNC);
//顯示棋子順序
rr_arraychess();
rj_arraychess();
//重置color
color = 0;
system("pause");
goto C;
}
//把當前下的棋保存在huixy中
huixy.x = xy.x;
huixy.y = xy.y;
color++;
Sleep(100);
}
case 1:
{
for (int i = 0; i < 225; i++)
{
if (RGB(0, 0, 0) != getpixel(alphcat[i].x - 3, alphcat[i].y - 3) && RGB(255, 255, 255) != getpixel(alphcat[i].x - 3, alphcat[i].y - 3)
/*當前落棋點沒有落棋 (這個點的顏色是背景色)*/)
{
//RGB(255, 255, 255) != getpixel(alphcat[i].x - 3, alphcat[i].y - 3)
alphcat[i].score = scorejudge(alphcat[i].x, alphcat[i].y);
}
}
//再來選出一個得分最大的,如果得分一樣就隨機從最大的一個選一個
alph alphmax;
alphmax.score = alphcat[0].score;
for (int i = 1; i < 225; i++)
{
if (alphcat[i].score >= alphmax.score)
{
alphmax = alphcat[i];
}
}
// 把落子順序保存在array陣列里
rjarray[color] = alphmax;
//根據這個點畫白棋
setfillcolor(WHITE);
setlinecolor(WHITE);
fillcircle(alphmax.x, alphmax.y, 10);
PlaySound("104.wav", NULL, SND_FILENAME | SND_ASYNC);
for (int i = 0; i < 225; i++)
{
alphcat[i].score = 0;
}
COLORREF bai = getpixel(alphmax.x - 3, alphmax.y - 3);
//勝負判斷
int b = judge(bai, alphmax.x, alphmax.y);
if (b == 1)
{
settextcolor(RGB(0, 0, 0));
settextstyle(30, 0, "楷體");
outtextxy(120, 200, "恭喜電腦獲勝");
outtextxy(80, 170, "(請按任意鍵繼續游戲)");
char str[30];
sprintf_s(str, "戰了%d回合", color / 2 + 1);
outtextxy(120, 230, str);
PlaySound("ying.wav", NULL, SND_FILENAME | SND_ASYNC);
//顯示棋子順序
rr_arraychess();
rj_arraychess();
//重置color
color = 0;
system("pause");
goto C;
}
//加入平局的判斷
if (b == 0 && color == 224) //因為color最大值是224
{
settextcolor(RGB(0, 0, 0));
settextstyle(30, 0, "楷體");
outtextxy(250, 200, "平局");
outtextxy(65, 170, "(請按任意鍵繼續游戲)");
PlaySound("ying.wav", NULL, SND_FILENAME | SND_ASYNC);
//顯示棋子順序
rr_arraychess();
rj_arraychess();
//重置color
color = 0;
system("pause");
goto C;
}
//把當前下的棋保存在rjhuixy中
rjhuixy.x = alphmax.x;
rjhuixy.y = alphmax.y;
color++;
Sleep(100);
break;
}
case WM_RBUTTONDOWN: //設定悔棋功能
//保證是悔的上一顆的棋
if (huixy.x == xy.x && huixy.y == xy.y && getpixel(xy.x - 3, xy.y - 3) != RGB(223,200,158))
{
//悔自己的黑棋
setfillcolor(RGB(223,200,158));
setlinecolor(RGB(223,200,158));
fillcircle(xy.x, xy.y, 10);
//機器也要悔一步棋
setfillcolor(RGB(223,200,158));
setlinecolor(RGB(223,200,158));
fillcircle(rjhuixy.x, rjhuixy.y, 10);
//重新畫棋盤
setlinecolor(BLACK);
for (int i = 1; i < 15; i++)
{
for (int j = 1; j < 15; j++)
{
rectangle(25 + j * 25, 325 + i * 25, 50 + j * 25, 350 + i * 25);
}
}
color -= 2;
break;
}
}
//來一個重新檢查 主要是讓白棋不存在中心有黑線的情況
for (int i = 0; i < 255; i++)
{
if (getpixel(master[i].x - 3, master[i].y - 3) == RGB(0, 0, 0))
{
setfillcolor(RGB(0, 0, 0));
setlinecolor(RGB(0, 0, 0));
fillcircle(master[i].x, master[i].y, 10);
}
if (getpixel(master[i].x - 3, master[i].y - 3) == RGB(255, 255, 255))
{
setfillcolor(RGB(255, 255, 255));
setlinecolor(RGB(255, 255, 255));
fillcircle(master[i].x, master[i].y, 10);
}
}
}
}
void gameover()
{
//游戲結束釋放資源
_getch();
closegraph();
}
int judge(COLORREF cl, int x, int y)
{
int a, b;
//定義上下總個數
int i = 0;
a = x;
b = y;
/*x = xy.x;
y = xy.y;*/
//先檢查上面的
while (cl == getpixel(a - 3, b - 3 - 25))
{
i++;
b -= 25;
}
//還原落子位置
a = x;
b = y;
//再檢查下面的
while (cl == getpixel(a - 3, b - 3 + 25))
{
i++;
b += 25;
}
//還原落子位置
a = x;
b = y;
if (i >= 4)
{
return 1;
}
//定義左右總個數
int j = 0;
a = x;
b = y;
//先檢查左面的
while (cl == getpixel(a - 3 - 25, b - 3))
{
j++;
a -= 25;
}
//還原落子位置
a = x;
b = y;
//再檢查下面的
while (cl == getpixel(a - 3 + 25, b - 3))
{
j++;
a += 25;
}
//還原落子位置
a = x;
b = y;
if (j >= 4)
{
return 1;
}
//定義'\'總個數
int k = 0;
a = x;
b = y;
//先檢查上面的
while (cl == getpixel(a - 3 - 25, b - 3 - 25))
{
k++;
a -= 25;
b -= 25;
}
//還原落子位置
a = x;
b = y;
//再檢查下面的
while (cl == getpixel(a - 3 + 25, b - 3 + 25))
{
k++;
a += 25;
b += 25;
}
//還原落子位置
a = x;
b = y;
if (k >= 4)
{
return 1;
}
//定義'/'總個數
int l = 0;
a = x;
b = y;
//先檢查上面的
while (cl == getpixel(a - 3 + 25, b - 3 - 25))
{
l++;
a += 25;
b -= 25;
}
//還原落子位置
a = x;
b = y;
//再檢查下面的
while (cl == getpixel(a - 3 - 25, b - 3 + 25))
{
l++;
a -= 25;
b += 25;
}
//還原落子位置
a = x;
b = y;
if (l >= 4)
{
return 1;
}
return 0;
}
void arraychess()
{
//主要實作的功能是在黑色的棋上顯示白色順序 在白色棋子上顯示黑色的順序
for (int i = 0; i <= color; i++)
{
if (i % 2 == 0)
{
setbkcolor(RGB(0, 0, 0));
char arr[10];
sprintf_s(arr, "%-2d", i + 1);
settextcolor(RGB(255, 255, 255));
settextstyle(15, 0, "楷體");
outtextxy(array[i].x - 7, array[i].y - 7, arr);
}
else
{
setbkcolor(RGB(255, 255, 255));
char arr[10];
sprintf_s(arr, "%-2d", i + 1);
settextcolor(RGB(0, 0, 0));
settextstyle(15, 0, "楷體");
outtextxy(array[i].x - 7, array[i].y - 7, arr);
}
}
//恢復了背景顏色
setbkcolor(RGB(195,195,195));
}
void rj_arraychess()
{
// 在白色棋子上顯示黑色的順序
for (int i = 0; i <= color; i++)
{
if (i % 2 != 0)
{
setbkcolor(RGB(255, 255, 255));
char arr[10];
sprintf_s(arr, "%d", i + 1);
settextcolor(RGB(0, 0, 0));
settextstyle(15, 0, "楷體");
outtextxy(rjarray[i].x - 7, rjarray[i].y - 7, arr);
}
}
//恢復背景顏色
setbkcolor(RGB(195,195,195));
}
void rr_arraychess()
{
// 在黑色棋子上顯示白色的順序
for (int i = 0; i <= color; i++)
{
if (i % 2 == 0)
{
setbkcolor(RGB(0, 0, 0));
char arr[10];
sprintf_s(arr, "%2d", i + 1);
settextcolor(RGB(255, 255, 255));
settextstyle(15, 0, "楷體");
outtextxy(array[i].x - 7, array[i].y - 7, arr);
}
}
//恢復了背景顏色
setbkcolor(RGB(195,195,195));
}
int rj_judge(COLORREF cl, int x, int y, int q, int p)
{
//用一個K來體現某一個方向的下一個是背景而不是有另外顏色的(活) K = 1 表示下一個為背景色
int K = 0;
int a, b;
//定義上下總個數
int i = 0;
a = x;
b = y;
//先檢查上面的
while (cl == getpixel(a - 3, b - 3 - 25))
{
i++;
b -= 25;
}
//檢查是否下一個是背景色
if (getpixel(a - 3, b - 3 - 25) == RGB(223,200,158) && b - 25 > 350)
{
K++;
}
//還原落子位置
a = x;
b = y;
//再檢查下面的
while (cl == getpixel(a - 3, b - 3 + 25))
{
i++;
b += 25;
}
//檢查是否下一個是背景色
if (getpixel(a - 3, b - 3 + 25) == RGB(223,200,158) && b + 35 < 700)
{
K++;
}
//還原落子位置
a = x;
b = y;
if (i == p - 1 && K == q)
{
return 1;
}
//重置K
K = 0;
//定義左右總個數
int j = 0;
a = x;
b = y;
//先檢查左面的
while (cl == getpixel(a - 3 - 25, b - 3))
{
j++;
a -= 25;
}
//檢查是否下一個是背景色
if (getpixel(a - 3 - 25, b - 3) == RGB(223,200,158) && a - 35 > 50)
{
K++;
}
//還原落子位置
a = x;
b = y;
//再檢查下面的
while (cl == getpixel(a - 3 + 25, b - 3))
{
j++;
a += 25;
}
//檢查是否下一個是背景色
if (getpixel(a - 3 + 25, b - 3) == RGB(223,200,158) && a + 35 < 700)
{
K++;
}
//還原落子位置
a = x;
b = y;
if (j == p - 1 && K == q)
{
return 1;
}
//重置K
K = 0;
//定義'\'總個數
int k = 0;
a = x;
b = y;
//先檢查上面的
while (cl == getpixel(a - 3 - 25, b - 3 - 25))
{
k++;
a -= 25;
b -= 25;
}
//檢查是否下一個是背景色
if (getpixel(a - 3 - 25, b - 3 - 25) == RGB(223,200,158) && a - 25 > 50 && b - 25 > 350)
{
K++;
}
//還原落子位置
a = x;
b = y;
//再檢查下面的
while (cl == getpixel(a - 3 + 25, b - 3 + 25))
{
k++;
a += 25;
b += 25;
}
//檢查是否下一個是背景色
if (getpixel(a - 3 + 25, b - 3 + 25) == RGB(223,200,158) && a + 25 < 400 && b + 25 < 700)
{
K++;
}
//還原落子位置
a = x;
b = y;
if (k == p - 1 && K == q)
{
return 1;
}
//重置K
K = 0;
//定義'/'總個數
int l = 0;
a = x;
b = y;
//先檢查上面的
while (cl == getpixel(a - 3 + 25, b - 3 - 25))
{
l++;
a += 25;
b -= 25;
}
//檢查是否下一個是背景色
if (getpixel(a - 3 + 25, b - 3 - 25) == RGB(223,200,158) && a + 25 < 400 && b - 25 > 350)
{
K++;
}
//還原落子位置
a = x;
b = y;
//再檢查下面的
while (cl == getpixel(a - 3 - 25, b - 3 + 25))
{
l++;
a -= 25;
b += 25;
}
//檢查是否下一個是背景色
if (getpixel(a - 3 - 25, b - 3 + 25) == RGB(223,200,158) && a - 25 > 50 && b + 25 < 700)
{
K++;
}
//還原落子位置
a = x;
b = y;
if (l == p - 1 && K == q)
{
return 1;
}
return 0;
}
int scorejudge(int x, int y)
{
/*主要思想(來自https://blog.csdn.net/ChinaJane163/article/details/52599787)
電腦白子 自己黑子
白子連成五子 +10000 阻礙黑子連成五子 +1000
白子連成活四 +200 阻礙黑子連成活四 +100
白子連成死四 +50 阻礙黑子連成死四 +20
白子連成活三 +30 阻礙黑子連成活三 +10
白子連成死三 +8 阻礙黑子連成死三 +5
白子連成活二 +2 阻礙黑子連成活二 +1
白子連成死二 +2 阻礙黑子連成死二 +1
白子連成活一 +1 阻礙黑子連成活一 +0
白子連成死一 +1 阻礙黑子連成死一 +0
*/
int a = x;
int b = y;
//定義一個得分
int score = 0;
//白子連成五子 +10000
if (judge(RGB(255, 255, 255), a, b)) //這個函式只是i的值與k的值不一樣 所以可以加兩個變數在函式里面
{
score += 10000;
}
//阻礙黑子連成五子 +1000
if (judge(RGB(0, 0, 0), a, b))
{
score += 1000;
}
//白子連成活四 +200
if (rj_judge(RGB(255, 255, 255), a, b, 2, 4))
{
score += 200;
}
//阻礙黑子連成活四 +100
if (rj_judge(RGB(0, 0, 0), a, b, 2, 4))
{
score += 100;
}
//白子連成死四 +50
if (rj_judge(RGB(255, 255, 255), a, b, 1, 4))
{
score += 50;
}
//阻礙黑子連成死四 +20
if (rj_judge(RGB(0, 0, 0), a, b, 1, 4))
{
score += 20;
}
//白子連成活三 +30
if (rj_judge(RGB(255, 255, 255), a, b, 2, 3))
{
score += 30;
}
//阻礙黑子連成活三 +10
if (rj_judge(RGB(0, 0, 0), a, b, 2, 3))
{
score += 10;
}
//白子連成死三 +8
if (rj_judge(RGB(255, 255, 255), a, b, 1, 3))
{
score += 8;
}
//阻礙黑子連成死三 +5
if (rj_judge(RGB(0, 0, 0), a, b, 1, 3))
{
score += 5;
}
//白子連成活二 +2
if (rj_judge(RGB(255, 255, 255), a, b, 2, 2))
{
score += 2;
}
//阻礙黑子連成活二 +1
if (rj_judge(RGB(0, 0, 0), a, b, 2, 2))
{
score += 1;
}
//白子連成死二 +2
if (rj_judge(RGB(255, 255, 255), a, b, 1, 2))
{
score += 2;
}
//阻礙黑子連成死二 +1
if (rj_judge(RGB(0, 0, 0), a, b, 1, 2))
{
score += 1;
}
//白子連成活一 +1
if (rj_judge(RGB(255, 255, 255), a, b, 2, 1))
{
score += 1;
}
//白子連成死一 +1
if (rj_judge(RGB(255, 255, 255), a, b, 1, 1))
{
score += 1;
}
return score;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/397499.html
標籤:其他
