日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

连连看外挂1.0

發(fā)布時間:2023/12/16 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 连连看外挂1.0 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

??????? 一時興起,決定寫個連連看的外掛玩玩...于是斷斷續(xù)續(xù)經(jīng)過幾個晚上的努力基本呈現(xiàn)雛形...之前也看過一些外掛技術(shù)的文章,確實(shí)這里面的技術(shù)深不可測,第一次寫就搞個簡單的吧,以后再慢慢改進(jìn)。

??????? 最簡單的外掛莫過于器械式的,也就是通過界面分析,然后去模擬一些鼠標(biāo)或者鍵盤動作。連連看外掛就可以通過這種方式去寫。整個外掛基本分為以下幾步完成:

1. 獲取界面信息,當(dāng)然最主要的是方格信息,有了這個其實(shí)足夠?qū)懸粋€連連看外掛了,但是為了功能更強(qiáng)大,我另外獲取了其他一些界面元素,比如其他玩家的速度(剩下的方格速),開始按鈕的位置等等。

2. 連連看算法的實(shí)現(xiàn),其實(shí)這個是比較簡單的。基本可以分為兩種:兩條X連線+一條Y連線、兩條Y連線+一條X連線。當(dāng)然也包括了各種特殊情況。比如一條X一條Y等等。

3. 鼠標(biāo)事件的模擬。

1. 獲取界面信息

??????? 這部分工作其實(shí)是最繁瑣的,只能依靠不斷的調(diào)式去完成,我在這個過程使用到了系統(tǒng)鉤子,在一定程度上簡化了這個過程。首先要獲得的是每個方格的信息,QQ連連看中一共有11×19個方格數(shù),一共有44(好像45)種不同的圖案,這個基本可以靠肉眼獲得。

??????? 接下來的事情就需要代碼去完成了,主要包括起始點(diǎn),每個方格的長和寬,可以唯一區(qū)分每種圖案的N個像素點(diǎn)。首先需要獲得一個大概的數(shù)值,我寫了一個系統(tǒng)的鼠標(biāo)鉤子,主要完成的功能是通過鼠標(biāo)在QQ連連看窗口的點(diǎn)擊,獲得該點(diǎn)的client端坐標(biāo)和顏色值。基本代碼如下:

  • //?MouseHook.cpp?:?Defines?the?exported?functions?for?the?DLL?application.
  • //
  • #include?"stdafx.h"
  • #pragma?data_seg?("shareddata")
  • HHOOK?????????g_MouseHook?=?NULL;
  • HINSTANCE?????g_Instance??=?NULL;
  • HWND??????????g_GameWnd???=?NULL;
  • #pragma?data_seg()
  • #pragma?comment(linker,?"/SECTION:shareddata,RWS")
  • int?APIENTRY?DllMain(HINSTANCE?hInstance,?DWORD?dwReason,?LPVOID?lpReserved)
  • {
  • ????UNREFERENCED_PARAMETER(lpReserved);
  • ????switch?(dwReason)
  • ????{
  • ????case?DLL_PROCESS_ATTACH:
  • ????case?DLL_THREAD_ATTACH:
  • ????????g_Instance?=?hInstance;
  • ????????break;
  • ????case?DLL_THREAD_DETACH:
  • ????case?DLL_PROCESS_DETACH:
  • ????????break;
  • ????}
  • ????return?TRUE;
  • }
  • LRESULT?WINAPI?MouseProc(int?nCode,WPARAM?wParam,LPARAM?lParam)
  • {
  • ????LPMOUSEHOOKSTRUCT?pMouseHook=(MOUSEHOOKSTRUCT?FAR?*)?lParam;
  • ????if(0?<?nCode)?{
  • ????????if(WM_RBUTTONDOWN?==?wParam)?{
  • ????????????if?(NULL?==?g_GameWnd)
  • ????????????{
  • ????????????????MessageBoxA(NULL,?"Game?window?not?found!",?"Error",?MB_OK);
  • ????????????????return?CallNextHookEx(?g_MouseHook,?nCode,?wParam,?lParam?);
  • ????????????}
  • ????????????int?xPos?=?pMouseHook->pt.x;
  • ????????????int?yPos?=?pMouseHook->pt.y;
  • ????????????CPoint?point(xPos,?yPos);
  • ????????????CWindowDC?dc(NULL);
  • ????????????COLORREF?cl?=?dc.GetPixel(point.x,?point.y);
  • ????????????::ScreenToClient(g_GameWnd,?&point);
  • ????????????CString?strMsg;
  • ????????????strMsg.Format("x?=?%d,?y?=?%d,?color?=?%d",?point.x,?point.y,?cl);
  • ????????????MessageBoxA(NULL,?strMsg,?"Point?Position?&?Color",?MB_OK);
  • ????????}
  • ????}
  • ????LRESULT?RetVal?=?CallNextHookEx(?g_MouseHook,?nCode,?wParam,?lParam?);?
  • ????return?RetVal;
  • }
  • BOOL?InstallHook(HWND?hWnd)
  • {
  • ????if?(NULL?==?hWnd)?{
  • ????????MessageBoxA(NULL,?"Game?window?not?found!",?"Error",?MB_OK);
  • ????????return?FALSE;
  • ????}
  • ????g_GameWnd?=?hWnd;
  • ????if(NULL?!=?g_MouseHook)?{
  • ????????MessageBoxA(NULL,?"Hook?already?set!",?"Error",?MB_OK);
  • ????????return?FALSE;
  • ????}
  • ????g_MouseHook?=?SetWindowsHookEx(WH_MOUSE,?(HOOKPROC)MouseProc,?g_Instance,?0);
  • ????if(NULL?==?g_MouseHook)?{
  • ????????MessageBoxA(NULL,?"Hook?can?not?be?set!",?"Error",?MB_OK);
  • ????????return?FALSE;
  • ????}
  • ????return?TRUE;
  • }
  • BOOL?UnInstallHook()
  • {
  • ????if(NULL?==?g_MouseHook)?{
  • ????????MessageBoxA(NULL,?"Hook?not?installed?correct!",?"Error",?MB_OK);
  • ????????return?FALSE;
  • ????}?else?{
  • ????????if(UnhookWindowsHookEx(g_MouseHook))
  • ????????????return?TRUE;
  • ????????else?{
  • ????????????MessageBoxA(NULL,?"Hook?can?not?be?removed!",?"Error",?MB_OK);
  • ????????????return?FALSE;
  • ????????}
  • ????}
  • }
  • ??????? 另外在def文件中需要將InstallHook跟UnInstallHook導(dǎo)出。另外注意到在InstallHook中需要一個參數(shù)HWND,這個參數(shù)需要在調(diào)用這個DLL的客戶端代碼中獲得。方法自然是用EnumWindows函數(shù)去枚舉桌面上的所有窗口然后找到連連看窗口。

    ??????? 通過這個方式,然后加上一些簡單的調(diào)式,或者如下四個數(shù)據(jù):

  • const?int??gc_CellStartX?????=??12;???//?方塊窗口起始點(diǎn)-X
  • const?int??gc_CellStartY?????=??180;??//?方塊窗口起始點(diǎn)-Y
  • const?int??gc_CellWidth??????=??31;???//?每個方塊的寬度
  • const?int??gc_CellLength?????=??35;???//?每個方塊的長度
  • ??????? 接下的需要做的也是最重要的就是在每個方格上取N個點(diǎn)來唯一的表示一種圖案。那么N應(yīng)該是多少呢?自然是越少越好,程序才能跑的快。之前我也是隨機(jī)的在方塊上取4-5個點(diǎn),但是仔細(xì)一算一共209個小方塊,那需要調(diào)用1000+次GetPixel,這大大影響了程序的運(yùn)行速度。如果能用一個唯一的點(diǎn)的顏色會表示一個圖案自然最好,但是這個似乎找起來有難度。不管三七二十一,我先取了中心點(diǎn)再說,看看究竟能區(qū)分多少方塊。為了這個我再次使用到了鉤子,這次的鉤子我需要完成這件事情:在QQ連連看窗口上用鼠標(biāo)右擊某個圖案,然后可以獲得這個圖案上特定點(diǎn)的位置,跟前一個鉤子不同的主要是MouseProc函數(shù):

  • LRESULT?WINAPI?MouseProc(int?nCode,WPARAM?wParam,LPARAM?lParam)
  • {
  • ????LPMOUSEHOOKSTRUCT?pMouseHook=(MOUSEHOOKSTRUCT?FAR?*)?lParam;
  • ????if(0?<?nCode)?{
  • ????????if(WM_RBUTTONDOWN?==?wParam)?{
  • ????????????if?(NULL?==?g_GameWnd)
  • ????????????{
  • ????????????????MessageBoxA(NULL,?"Game?window?not?found!",?"Error",?MB_OK);
  • ????????????????return?CallNextHookEx(?g_MouseHook,?nCode,?wParam,?lParam?);
  • ????????????}
  • ????????????int?xPos?=?pMouseHook->pt.x;
  • ????????????int?yPos?=?pMouseHook->pt.y;
  • ????????????CPoint?point(xPos,?yPos);
  • ????????????::ScreenToClient(g_GameWnd,?&point);
  • ????????????int?colIndex?=?(point.x?-?gc_CellStartX)/gc_CellWidth;
  • ????????????int?rowIndex?=?(point.y?-?gc_CellStartY)/gc_CellLength;
  • ????????????ASSERT(rowIndex?>=?0?&&?rowIndex?<?11);
  • ????????????ASSERT(colIndex?>=?0?&&?colIndex?<?19);
  • ????????????
  • ????????????int?startx?=?gc_CellStartX?+?colIndex?*?gc_CellWidth;
  • ????????????int?starty?=?gc_CellStartY?+?rowIndex?*?gc_CellLength;
  • ????????????CPoint?pointCT(startx?+?gc_CellWidth?/?2,?starty?+?gc_CellLength?/?2);???//?center?point?of?the?cell
  • ????????????::ClientToScreen(g_GameWnd,?&pointCT);
  • ????????????CPoint?pointUP(pointCT.x,?pointCT.y?-?10);
  • ????????????CWindowDC?dc(NULL);
  • ????????????COLORREF?colorUP?=?dc.GetPixel(pointUP);
  • ????????????COLORREF?colorCT?=?dc.GetPixel(pointCT);
  • ????????????CString?strMsg;
  • ????????????strMsg.Format("Row:?%d,?Column:?%d/ncolorUP:?%d/nncolorCT:?%d/n",?rowIndex,?colIndex,?colorUP,?colorCT);
  • ????????????MessageBoxA(NULL,?strMsg,?"Cell?Color",?MB_OK);
  • ????????}
  • ????}
  • ????LRESULT?RetVal?=?CallNextHookEx(?g_MouseHook,?nCode,?wParam,?lParam?);?
  • ????return?RetVal;
  • }
  • ??????? 當(dāng)然還要加上之前獲得的4個常量才能通過編譯。不難看出在這個MouseProc中我們使用了兩個點(diǎn)唯一的區(qū)分一個圖案,一個中心點(diǎn)一個中心點(diǎn)偏上10個像素。但是其實(shí)這并不是一開始就獲得的結(jié)果,起初我就取了一個中心點(diǎn),然后通過不斷的測試,找到QQ連連看中每一種圖案中心點(diǎn)的顏色值,結(jié)果發(fā)現(xiàn)中心點(diǎn)可以區(qū)分出44種不同圖案中的39種,另外5種圖案中共有兩種顏色值。如下圖所示,紅顏色的兩個圖案中心點(diǎn)像素值相同,藍(lán)顏色的三個圖案中心點(diǎn)像素也相同。

    ????????

    ??????? 有了這個結(jié)果基本可以斷定兩個點(diǎn)可以唯一的區(qū)分一個圖案,我隨意的取了中心點(diǎn)上方的10個像素的點(diǎn)作為第二個點(diǎn),經(jīng)過上述鉤子程序的測試該點(diǎn)可以區(qū)分該5個點(diǎn)。為了盡量的加快程序速度,我并不是簡單的每次選取所有圖案的兩個點(diǎn),而是當(dāng)必要的時候,也就是碰到中心點(diǎn)不能區(qū)分的時候再次去讀取第二個點(diǎn)的像素值。為此,我使用了一個CellColor類唯一代表一個圖案,提供了一點(diǎn)基本的操作:

  • //?class?CellColor
  • class?CellColor{
  • public:
  • ????//?Following?static?variables?are?used?to?make?our?application?quicker...only?for?QQGame...
  • ????static?const?COLORREF?cBackGroundColor?=?7359536;
  • ????static?const?COLORREF?cDuplicateColor1???=?16317688;
  • ????static?const?COLORREF?cDuplicateColor2? ?=?40184;
  • public:
  • ????CellColor(){?;}
  • ????CellColor(COLORREF?color1,?COLORREF?color2)
  • ????????:?m_color1(color1),?m_color2(color2)
  • ????{
  • ????????//?
  • ????}
  • ????CellColor::CellColor(const?CellColor?&color)
  • ????{
  • ????????m_color1?=?color.m_color1;
  • ????????m_color2?=?color.m_color2;
  • ????}
  • ????bool?isBackGround()
  • ????{
  • ????????return?cBackGroundColor?==?m_color1;
  • ????}
  • ????bool?equal(CellColor&?color)
  • ????{
  • ????????return?m_color1?==?color.m_color1?&&?m_color2?==?color.m_color2;
  • ????}
  • private:
  • ????COLORREF?m_color1;
  • ????COLORREF?m_color2;
  • };
  • ??????? 下面的函數(shù)用來從QQ連連看窗口中獲取一個圖案的CellColor:

  • //
  • //?Get?each?cell?color.?We?choose?two?points?for?each?
  • //?cell,?because?two?points?is?enough?to?distinguish?
  • //?all?these?patterns
  • //?
  • CellColor?GetEachCellColor(int?startx,?int?starty)
  • {
  • ????//?Two?points?is?enough?to?distinguish?all?these?patterns.
  • ????CPoint?point1(startx?+?gc_CellWidth?/?2,?starty?+?gc_CellLength?/?2);
  • ????CPoint?point2(point1.x,?point1.y?-?10);
  • ????COLORREF?color1?=?0,?color2?=?0;
  • ????color1?=::GetPixel(g_GameDC,?point1.x,?point1.y);
  • ????//?Use?following?if?statement?to?make?it?faster.?Because?GetPixel?takes?time.
  • ????if(CellColor::cDuplicateColor1?==?color1?||?CellColor::cDuplicateColor2?==?color1)
  • ?????????color2?=?::GetPixel(g_GameDC,?point2.x,?point2.y);
  • ????return?CellColor(color1,?color2);
  • }
  • ??????? 到目前為止,最需要的東西我們已經(jīng)得到。已經(jīng)足夠?qū)懗鲆粋€最基本功能的連連看外掛。但是在完成最基本功能的同時我又突發(fā)奇想希望能完成這么一個功能:我的外掛可以根據(jù)其他對手的速率動態(tài)的調(diào)整自己的速度。有了這個想法,我需要獲得更多的界面信息,當(dāng)然主要是其他玩家分?jǐn)?shù)(剩余方格數(shù)量),但是這可不是一個簡單的活,雖然游戲窗口上明明白白寫著一個100,但是我怎么才能獲得這個數(shù)值呢?首先,我還是通過系統(tǒng)鉤子的方法再加上嚴(yán)格的測試得到了每個玩家窗口中的包含分?jǐn)?shù)的小矩形,通過截屏的方法我獲得了玩家分?jǐn)?shù)中從0到9的所有數(shù)值的特征點(diǎn),放大后如下所示:

    ???????

    我所需要做的就是找到那么幾個點(diǎn)唯一的確定這些數(shù)字,理論上可能4,5個就可以做到,然后一個人找啊找,結(jié)果發(fā)現(xiàn)這還真不是一件容易的活,反正vs在手,這還不是小菜一碟...

  • #include?<iostream>
  • using?namespace?std;
  • bool?Numbers[11][8][5];
  • void?fill(int?Num,?int?RowIndex,?int?ColIndex,?int?HorizontalNum?=?1,?int?VerticalNum?=?1)
  • {
  • ????if(Num?>?10?||?RowIndex?+?VerticalNum?>?8?||?ColIndex?+?HorizontalNum?>?5)?{
  • ????????cout<<"Wrong?Number!"<<endl;
  • ????????return;
  • ????}
  • ????for(int?i?=?0;?i?<?HorizontalNum;?i++)?{
  • ????????Numbers[Num][RowIndex][ColIndex?+?i]?=?true;
  • ????}
  • ????for(int?i?=?0;?i?<?VerticalNum;?i++)?{
  • ????????Numbers[Num][RowIndex?+?i][ColIndex]?=?true;
  • ????}
  • }
  • int?main()
  • {
  • ????memset(Numbers,?0,?11?*?5?*?8);
  • ????//?0
  • ????fill(0,?0,?1,?3);
  • ????fill(0,?1,?0,?1,?6);
  • ????fill(0,?1,?4,?1,?6);
  • ????fill(0,?7,?1,?3);
  • ????//?1
  • ????fill(1,?1,?1);
  • ????fill(1,?0,?2,?1,?8);
  • ????fill(1,?7,?1,?3);
  • ????//?2
  • ????fill(2,?0,?1,?3);
  • ????fill(2,?1,?0,?1,?2);
  • ????fill(2,?1,?4,?1,?2);
  • ????fill(2,?3,?3);
  • ????fill(2,?4,?2);
  • ????fill(2,?5,?1);
  • ????fill(2,?6,?0);
  • ????fill(2,?7,?0,?5);
  • ????//?3
  • ????fill(3,?0,?1,?3);
  • ????fill(3,?1,?0);
  • ????fill(3,?1,?4,?1,?2);
  • ????fill(3,?3,?2,?2);
  • ????fill(3,?4,?4,?1,?3);
  • ????fill(3,?6,?0);
  • ????fill(3,?7,?1,?3);
  • ????//?4
  • ????fill(4,?0,?3,?1,?8);
  • ????fill(4,?1,?2,?2);
  • ????fill(4,?2,?1,?1,?2);
  • ????fill(4,?4,?0);
  • ????fill(4,?5,?1,?4);
  • ????fill(4,?7,?3,?2);
  • ????//?5
  • ????fill(5,?0,?0,?5,?4);
  • ????fill(5,?3,?0,?4);
  • ????fill(5,?4,?4,?1,?3);
  • ????fill(5,?6,?0);
  • ????fill(5,?7,?1,?3);
  • ????//?6
  • ????fill(6,?0,?1,3);
  • ????fill(6,?1,?0,?1,?6);
  • ????fill(6,?1,?3);
  • ????fill(6,?3,?0,?4);
  • ????fill(6,?4,?4,?1,?3);
  • ????fill(6,?7,?1,?3);
  • ????//?7
  • ????fill(7,?0,?0,?5,?2);
  • ????fill(7,?0,?3,?1,?3);
  • ????fill(7,?3,?2,?1,?5);
  • ????//?8
  • ????fill(8,?0,?1,?3);
  • ????fill(8,?1,?0,?1,?2);
  • ????fill(8,?1,?4,?1,?2);
  • ????fill(8,?3,?1,?3);
  • ????fill(8,?4,?0,?1,?3);
  • ????fill(8,?4,?4,?1,?3);
  • ????fill(8,?7,?1,?3);
  • ????//?9
  • ????fill(9,?0,?1,?3);
  • ????fill(9,?1,?0,?1,?3);
  • ????fill(9,?1,?4,?1,?6);
  • ????fill(9,?4,?1,?4);
  • ????fill(9,?6,?1);
  • ????fill(9,?7,?1,?3);
  • ????for(int?m?=?0;?m?<?11;?m++){
  • ????????for(int?i?=?0;?i?<?8;?i++)?{
  • ????????????for(int?j?=?0;?j?<?5;?j++)
  • ????????????{
  • ????????????????if(Numbers[m][i][j])
  • ????????????????????cout<<"O";
  • ????????????????else
  • ????????????????????cout<<"?";
  • ????????????}
  • ????????????cout<<endl;
  • ????????}
  • ????????cout<<endl<<endl;
  • ????}
  • ????for(int?i1?=?0;?i1?<?8;?i1++)
  • ????for(int?j1?=?0;?j1?<?5;?j1++)?{
  • ????????int?i2?=?(j1?==?4??i1?+?1?:?i1);
  • ????????int?j2?=?(j1?==?4??0?:?j1?+?1);
  • ????????for(;?i2?<?8;?i2++)
  • ????????for(;?j2?<?5;?j2++)?{
  • ????????????int?i3?=?(j2?==?4??i2?+?1?:?i2);
  • ????????????int?j3?=?(j2?==?4??0?:?j2?+?1);
  • ????????????for(;?i3?<?8;?i3++)
  • ????????????for(;?j3?<?5;?j3++)?{
  • ????????????????int?i4?=?(j3?==?4??i3?+?1?:?i3);
  • ????????????????int?j4?=?(j3?==?4??0?:?j3?+?1);
  • ????????????????for(;?i4?<?8;?i4++)
  • ????????????????for(;?j4?<?5;?j4++)?{
  • ????????????????????int?i5?=?(j4?==?4??i4?+?1?:?i4);
  • ????????????????????int?j5?=?(j4?==?4??0?:?j4?+?1);
  • ????????????????????for(;?i5?<?8;?i5++)
  • ????????????????????for(;?j5?<?5;?j5++)?{
  • ????????????????????????int?i6?=?(j5?==?4??i5?+?1?:?i5);
  • ????????????????????????int?j6?=?(j5?==?4??0?:?j5?+?1);
  • ????????????????????????for(;?i6?<?8;?i6++)
  • ????????????????????????for(;?j6?<?5;?j6++)?{
  • ????????????????????????????int?value[11]?=?{0};
  • ????????????????????????????for(int?index?=?0;?index?<?11;?index++)?{
  • ????????????????????????????????int?r1?=?Numbers[index][i1][j1]?<<?5;
  • ????????????????????????????????int?r2?=?Numbers[index][i2][j2]?<<?4;
  • ????????????????????????????????int?r3?=?Numbers[index][i3][j3]?<<?3;
  • ????????????????????????????????int?r4?=?Numbers[index][i4][j4]?<<?2;
  • ????????????????????????????????int?r5?=?Numbers[index][i5][j5]?<<?1;
  • ????????????????????????????????int?r6?=?Numbers[index][i6][j6]?<<?0;
  • ????????????????????????????????value[index]?=?r1?+?r2?+?r3?+?r4?+?r5?+?r6;
  • ????????????????????????????}
  • ????????????????????????????bool?bSucess?=?true;
  • ????????????????????????????for(int?i?=?0;?i?<?11;?i++)
  • ????????????????????????????for(int?j?=?i?+?1;?j?<?11;?j++)?{
  • ????????????????????????????????if(value[i]?==?value[j])?{
  • ????????????????????????????????????bSucess?=?false;
  • ????????????????????????????????????goto?next;
  • ????????????????????????????????}
  • ????????????????????????????}
  • next:?;
  • ????????????????????????????if(bSucess)?{
  • ????????????????????????????????cout?<<?"x1?=?"?<<?j1?<<?"?y1?=?"?<<?i1?<<"?x2?=?"<<j2?<<"?y2?=?"<<i2?<<"?x3?=?"<<j3?<<"?y3?=?"
  • ?????????????????????????????????????<<?i3?<<?"?x4?=?"<<?j4?<<?"?y4?=?"<<?i4?<<"?x5?=?"<<?j5<<"?y5?=?"<<i5<<"?x6?=?"<<j6<<"?y6=?"<<i6<<endl;
  • ????????????????????????????????for(int?i?=?0;?i?<?11;?i++)?{
  • ????????????????????????????????????printf("%d:?%d/n",?i,?value[i]);
  • ????????????????????????????????}
  • ????????????????????????????????return?0;
  • ????????????????????????????}
  • ????????????????????????}
  • ????????????????????}
  • ????????????????}
  • ????????????}
  • ????????}
  • ????}
  • ????cout<<"Can?not?find?six?points?to?distinguish?these?numbers!"<<endl;
  • ????return?0;
  • }
  • ??????? 寫了程序才知道至少需要六個點(diǎn)才能區(qū)分這些個數(shù)字,怪不得我找了半年沒找到,還好即使調(diào)轉(zhuǎn)方向。除了這些信息之外,我還獲得了開始按鈕坐標(biāo)等信息(實(shí)現(xiàn)自動開始,保障程序的連續(xù)運(yùn)行)。OK,萬事俱備了,來看看我們的收獲吧:

  • /
  • //?These?const?globe?variables?will?help?us?to?get?all?players'?information
  • //?during?a?game.?Of?cause?the?main?thing?we?care?about?is?the?score.
  • //?Here?score?means?the?number?of?left?cells.
  • const?COLORREF?gc_OtherPlayersBKGround??=?11034624;??//?是否有玩家
  • const?COLORREF?gc_ScoreColor????????????????????? ??=?63728;??????? //?分?jǐn)?shù)顏色
  • const?int??????gc_FirstScoreRectStartX?????????????????? =?101;??????????? //?第一個玩家分?jǐn)?shù)起始點(diǎn)?-?X坐標(biāo)
  • const?int??????gc_FirstScoreRectStartY?????????????????? =?44;???????????? ?//?Y坐標(biāo),同上
  • const?int??????gc_SpaceBetweenTwoScoreRects????=?119;?????????? ? //?兩個玩家的分?jǐn)?shù)間距
  • const?int??????gc_EachNumberRectWidth????????????? ?=?7;???????????? ?? //?每個數(shù)字的寬度
  • const?int??????gc_EachNumberRectHeight??????????????=?10;????????????? //?每個數(shù)字的高度
  • const?int??????gc_MyScoreRectStartX????????????????????=?505;????????????//?自己的分?jǐn)?shù)起始點(diǎn)?-?X坐標(biāo)
  • const?int??????gc_MyScoreRectStartY???????????? ???????=?576;?????????? ?//?Y坐標(biāo),同上
  • /
  • /
  • //?These?globe?variables?are?useful?during?the?game.?gc_StartFlagPoint?
  • //?and?gc_StartFlagColor?are?used?to?check?wether?the?game?has?been?started.
  • //?gc_StartButtonPoint?is?used?to?simulate?the?mouse?click?to?start?a?game.
  • //?gc_SignificantPointsForSocre?is?used?to?get?players'?score.
  • const?CPoint??????????????gc_StartFlagPoint(311,?576);???????? ?//?
  • const?COLORREF? ???? gc_StartFlagColor?=?10534136;????? //?結(jié)合上一個表示游戲是否已開始
  • const?CPoint????????????? gc_StartButtonPoint(670,?570);???? //?開始按鈕的坐標(biāo)點(diǎn)
  • const?CPoint????????????? gc_SignificantPointsForSocre[6]?=?{CPoint(0,1),CPoint(4,1),CPoint(4,2),CPoint(4,3),CPoint(0,4),CPoint(2,4)};???????????????????????????????????????????????????????????????????????????????????? //?區(qū)分0-9的六個特征點(diǎn)
  • /
  • ??????? 到此為止第一步基本完成。

    ?

    2. 連連看算法的實(shí)現(xiàn)

    ??????? 這個部分分為兩部分,第一部分是遍歷整個數(shù)組找到兩個相同的圖案,第二部分檢查這兩個圖案是否連通。在介紹這部分工作之前需要簡單介紹一下表示每個方塊的數(shù)據(jù)結(jié)構(gòu)及這些數(shù)據(jù)結(jié)構(gòu)的填充:

  • /
  • //?All?player?information?during?each?game.
  • struct?Player{
  • ????bool?exist;?????//?是否有玩家
  • ????int??leftcells;?//?玩家剩余方格數(shù)
  • }?g_AllPlayers[5]?=?{(0,?0),?(0,?0),?(0,?0),?(0,?0),?(0,?0)};
  • /
  • /
  • //?All?cell?information?during?each?game.
  • struct?Cell{
  • ????Cell(){?empty?=?true;?}
  • ????CellColor???color;????//?方塊顏色,唯一確定一種圖案
  • ????CPoint??????postion;??//?方塊位置,?模擬鼠標(biāo)點(diǎn)擊
  • ????bool????????empty;????//?是否存在一個未消掉的方塊
  • }?g_AllCells[gc_RowNum][gc_ColNum];
  • /
  • /
  • UINT????g_CellNum????=?0;??????//?My?lefted?cell?number?-?used?inside
  • .....
  • .....
  • .....
  • //?Get?all?the?cell?information?for?the?game.?This?must
  • //?be?done?after?the?game?starts.?We?get?all?necessary
  • //?information?here.?The?most?important?is?to?fill?the
  • //?g_AllCells?array.
  • //?
  • void?GetAllCellInfo()
  • {
  • ????//?set?new?values?for?them
  • ????for(int?i?=?0;?i?<?gc_RowNum;?i++)
  • ????for(int?j?=?0;?j?<?gc_ColNum;?j++)
  • ????{
  • ????????int?x?=?gc_CellStartX?+?j?*?gc_CellWidth;
  • ????????int?y?=?gc_CellStartY?+?i?*?gc_CellLength;
  • ????????g_AllCells[i][j].color???=?GetEachCellColor(x,?y);
  • ????????g_AllCells[i][j].postion?=?CPoint(x?+?gc_CellWidth?/?2,?y?+?gc_CellLength?/?2);
  • ????}
  • ????for(int?i?=?0;?i?<?gc_RowNum;?i++)
  • ????for(int?j?=?0;?j?<?gc_ColNum;?j++)?{
  • ????????if(!g_AllCells[i][j].color.isBackGround())?{
  • ????????????g_AllCells[i][j].empty?=?false;
  • ????????????g_CellNum++;
  • ????????}
  • ????}
  • }
  • ??????? 不難看出,g_AllCells保存了所有方格信息,而g_CellNum保存了自己的剩余方格數(shù)。接下來就可以遍歷g_AllCells這個數(shù)組找到圖案相同的方塊并檢查是否連通。

    第一部分代碼如下:

  • ????????while(g_CellNum)?{
  • ????????????for(int?i?=?0;?i?<?gc_RowNum;?i++)
  • ????????????for(int?j?=?0;?j?<?gc_ColNum;?j++)?{
  • ????????????????if?(!g_AllCells[i][j].empty)?{
  • ????????????????????for(int?m?=?0;?m?<?gc_RowNum;?m++)
  • ????????????????????for(int?n?=?0;?n?<?gc_ColNum;?n++)?{
  • ????????????????????????if(!g_AllCells[m][n].empty?&&?
  • ????????????????????????????g_AllCells[i][j].color.equal(g_AllCells[m][n].color))?{
  • ????????????????????????????if(!(i?==?m?&&?j?==?n)?&&?CanBeDeleted(i,?j,?m,?n))?{
  • ????????????????????????????????SimulateMouseClick(g_AllCells[i][j].postion,?g_AllCells[m][n].postion);
  • ????????????????????????????????g_AllCells[i][j].empty?=?true;
  • ????????????????????????????????g_AllCells[m][n].empty?=?true;
  • ????????????????????????????????g_CellNum?-=?2;
  • ????????????????????????????????//?這里需要做很多事情...調(diào)整速度、檢查輸贏等...
  • ????????????????????????????????goto?next;
  • ????????????????????????????}
  • ????????????????????????}
  • ????????????????????}
  • ????????????????????next:?;???//?do?nothing?here,?just?start?a?new?search.
  • ????????????????}
  • ????????????}
  • ????????}
  • ??????? 第二部分其實(shí)也就一個函數(shù)的實(shí)現(xiàn): CanbeDeleted

  • /
  • //?This?function?will?check?if?two?cells?have?any?connect?path
  • //?if?so,?true?will?be?returned?and?then?we?simulate?the?mouse
  • //?click,?else?we?return?false?and?do?nothing.
  • //?
  • bool?CanBeDeleted(int?row1,?int?col1,?int?row2,?int?col2)
  • {
  • ????//?First?we?check?this?kind?of?path:?two?x-lines?and?one?y-line.
  • ????//?one?x-line?and?one?y-line?or?only?one?x-line?are?all?special
  • ????//?cases,?will?also?be?handled?by?following?code.
  • ????int?left1?=?col1,?right1?=?col1;
  • ????int?left2?=?col2,?right2?=?col2;
  • ????if(col1?!=?0)
  • ????????while(left1?>?0?&&?g_AllCells[row1][left1?-?1].empty){?left1--?;}
  • ????if(col1?!=?gc_ColNum?-?1)
  • ????????while(right1?<?gc_ColNum?-?1?&&?g_AllCells[row1][right1?+?1].empty){?right1++?;}
  • ????if(col2?!=?0)
  • ????????while(left2?>?0?&&?g_AllCells[row2][left2?-?1].empty){?left2--?;}
  • ????if(col2?!=?gc_ColNum?-?1)
  • ????????while(right2?<?gc_ColNum?-?1?&&?g_AllCells[row2][right2?+?1].empty){?right2++?;}
  • ????ASSERT(left1?>=?0?&&?left2?>=0?&&?right1?<?gc_ColNum?&&?right2?<?gc_ColNum);
  • ????int?commonLeft??=?max(left1,?left2);
  • ????int?commonRight?=?min(right1,?right2);
  • ????if(commonLeft?<=?commonRight)?{
  • ????????int?upCellRowIndex???=?min(row1,?row2);
  • ????????int?downCellRowIndex?=?max(row1,?row2);
  • ????????for(int?i?=?commonLeft;?i?<=?commonRight;?i++)?{
  • ????????????int?upCellRowIndexTmp?=?upCellRowIndex;
  • ????????????int?downCellRowIndexTmp?=?downCellRowIndex;
  • ????????????if(i?==?col1)?{
  • ????????????????if(row1?==?upCellRowIndexTmp)
  • ????????????????????upCellRowIndexTmp++;
  • ????????????????else
  • ????????????????????downCellRowIndexTmp--;
  • ????????????}
  • ????????????if(i?==?col2)?{
  • ????????????????if(row2?==?upCellRowIndexTmp)
  • ????????????????????upCellRowIndexTmp++;
  • ????????????????else
  • ????????????????????downCellRowIndexTmp--;
  • ????????????}
  • ????????????if(downCellRowIndexTmp?<?upCellRowIndexTmp)
  • ????????????????return?true;
  • ????????????while(g_AllCells[upCellRowIndexTmp][i].empty)?{
  • ????????????????if(upCellRowIndexTmp++?==?downCellRowIndexTmp)?{
  • ????????????????????//?We?do?find?a?path?here,?return?true.
  • ????????????????????return?true;
  • ????????????????}
  • ????????????}
  • ????????}
  • ????}
  • ????//?First?we?check?this?kind?of?path:?two?y-lines?and?one?x-line.
  • ????//?one?x-line?and?one?y-line?or?only?one?y-line?are?all?special
  • ????//?cases,?will?also?be?handled?by?following?code.
  • ????int?up1?=?row1,?down1?=?row1;
  • ????int?up2?=?row2,?down2?=?row2;
  • ????if(row1?!=?0)
  • ????????while(up1?>?0??&&?g_AllCells[up1?-?1][col1].empty){?up1--?;}
  • ????if(row1?!=?gc_RowNum?-?1)
  • ????????while(down1?<?gc_RowNum?-?1?&&?g_AllCells[down1?+?1][col1].empty){?down1++?;}
  • ????if(row2?!=?0)
  • ????????while(up2?>?0??&&?g_AllCells[up2?-?1][col2].empty){?up2--?;}
  • ????if(row2?!=?gc_RowNum?-?1)
  • ????????while(down2?<?gc_RowNum?-?1?&&?g_AllCells[down2?+?1][col2].empty){?down2++?;}
  • ????ASSERT(up1?>=?0?&&?up2?>=0?&&?down1?<?gc_RowNum?&&?down2?<?gc_RowNum);
  • ????int?commonUp???=?max(up1,?up2);
  • ????int?commonDown?=?min(down1,?down2);
  • ????if(commonUp?<=?commonDown)?{
  • ????????int?leftCellColIndex??=?min(col1,?col2);
  • ????????int?rightCellColIndex?=?max(col1,?col2);
  • ????????for(int?i?=?commonUp;?i?<=?commonDown;?i++)?{
  • ????????????int?leftCellColIndexTmp??=?leftCellColIndex;
  • ????????????int?rightCellColIndexTmp?=?rightCellColIndex;
  • ????????????if(i?==?row1)?{
  • ????????????????if(col1?==?leftCellColIndexTmp)
  • ????????????????????leftCellColIndexTmp++;
  • ????????????????else
  • ????????????????????rightCellColIndexTmp--;
  • ????????????}
  • ????????????if(i?==?row2)?{
  • ????????????????if(col2?==?leftCellColIndexTmp)
  • ????????????????????leftCellColIndexTmp++;
  • ????????????????else
  • ????????????????????rightCellColIndexTmp--;
  • ????????????}
  • ????????????if(rightCellColIndexTmp?<?leftCellColIndexTmp)
  • ????????????????return?true;
  • ????????????while(g_AllCells[i][leftCellColIndexTmp].empty)?{
  • ????????????????//?We?do?find?a?path?here,?return?true.
  • ????????????????if(leftCellColIndexTmp++?==?rightCellColIndex)?{
  • ????????????????????return?true;
  • ????????????????}
  • ????????????}
  • ????????}
  • ????}
  • ????//?No?connect?path?find?if?we?get?here.?Return?false.
  • ????return?false;
  • }
  • ??????? 這個函數(shù)包含了兩部分:檢查X-Y-X連通及Y-X-Y連通。當(dāng)然包括X-Y等其他特殊情況。兩部分代碼基本完全一致,不難看懂。

    ??????? 到目前為止,大部分工作已經(jīng)完成了。接下來我們需要模擬鼠標(biāo)的點(diǎn)擊事件。

    3. 鼠標(biāo)事件的模擬

    ??????? 整個程序有兩個地方需要模擬鼠標(biāo)點(diǎn)擊,第一當(dāng)然是去消掉那些方塊,第二我們用于模擬點(diǎn)擊開始按鈕,這樣我們就可以保證程序連續(xù)運(yùn)行,實(shí)現(xiàn)真正的掛機(jī)。分別采用了如下的方法:

  • /
  • //?Simulate?the?mouse?click?every?time?we?find?two?cells?
  • //?that?can?be?deleted.
  • //?
  • void?SimulateMouseClick(CPoint?pt1,?CPoint?pt2)
  • {
  • ????::SendMessage(g_GameWnd,?WM_LBUTTONDOWN,?0,?(LPARAM)MAKELONG(pt1.x,?pt1.y));
  • ????::SendMessage(g_GameWnd,?WM_LBUTTONUP,?0,?(LPARAM)MAKELONG(pt1.x,?pt1.y));
  • ????::SendMessage(g_GameWnd,?WM_LBUTTONDOWN,?0,?(LPARAM)MAKELONG(pt2.x,?pt2.y));
  • ????::SendMessage(g_GameWnd,?WM_LBUTTONUP,?0,?(LPARAM)MAKELONG(pt2.x,?pt2.y));
  • }
  • ......
  • ......
  • ......
  • //?模擬鼠標(biāo)點(diǎn)擊開始按鈕的代碼
  • //?Click?the?start?button,?waiting?for?other?players?to?start.
  • if(gc_StartFlagColor?!=?::GetPixel(g_GameDC,?gc_StartFlagPoint.x,?gc_StartFlagPoint.y))?{
  • ????//?Simulate?to?click?the?start?button?on?the?game?window.
  • ????CPoint?pt(gc_StartButtonPoint);
  • ????::ClientToScreen(g_GameWnd,?&pt);
  • ????SetCursorPos(pt.x,?pt.y);
  • ????mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);
  • ????mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0);
  • ????//?Keep?waiting?if?the?game?window?remains?unchanged.
  • ????while(::GetPixel(g_GameDC,?gc_StartFlagPoint.x,?gc_StartFlagPoint.y)?!=?gc_StartFlagColor){;}
  • }
  • ??????? 好了,到此為止程序已經(jīng)可以跑起來了...并且可以實(shí)現(xiàn)秒殺了...但是在我的外掛中另外還實(shí)現(xiàn)了其他一些簡單的功能。從下面的截圖中我們可以看出來程序的其他一些功能。

    ???????

    ??????? 我們可以看到程序可以有手動和自動兩種方式運(yùn)行。我們可以在程序運(yùn)行的時候動態(tài)地拖動滾動條來更改程序運(yùn)行速度,當(dāng)然我們需要采用多線程編寫。手動調(diào)整速度的實(shí)現(xiàn)比較簡單,我們只需根據(jù)滾動條的位置計算出一個較為合理的等待時間,然后調(diào)用sleep函數(shù)就可以了。自動方式較為復(fù)雜,我需要根據(jù)其他玩家的速度動態(tài)調(diào)整我的速度,雖然功能是實(shí)現(xiàn)了,基本可以保證贏得看不出來,但是還是有很多地方需要改進(jìn)。

    ??????? 另外需要說明的是,雖然這個外掛是基于無道具版本寫的,但是具有一定的處理道具的能力。我的實(shí)現(xiàn)方式如下:每次找到兩個可以消掉的方塊后先進(jìn)行鼠標(biāo)模擬點(diǎn)擊,然后從界面上獲得自己的剩余方格數(shù)量與程序內(nèi)部的方格數(shù)量g_CellNum進(jìn)行比較,如果發(fā)現(xiàn)兩個不符則說明遇到障礙、禁手、或者鏡子等道具,等待一定時間后重新獲取界面各種信息繼續(xù)運(yùn)行,這樣在一定程度上保證了程序的持續(xù)運(yùn)行性。但是改版本暫時不能處理重列功能,當(dāng)遇到無解的時候需要手動重列。雖然要實(shí)現(xiàn)這個功能不難,但是考慮到該版本是使用了模擬鼠標(biāo)的方法實(shí)現(xiàn)的,很難做到完美,就在下一個版本中增加這個功能了。

    ??????? 另外再開發(fā)的時候還遇到了一個難點(diǎn),就是如何判斷輸贏。每次消掉兩個后程序會等待一個時間然后再繼續(xù)消掉下兩個,但是在這段時間中對手可能已經(jīng)贏了,游戲結(jié)束,但是我的程序確還在"消掉"。這顯然不是我們期望的,因?yàn)樵谡麄€程序中,我沒有使用sleep函數(shù)去等待。而是在每次等待的時間內(nèi)不斷的去查看其它玩家的剩余方塊數(shù),如果檢測到零則說明游戲結(jié)束,其他玩家已經(jīng)勝利。如下函數(shù)幫我實(shí)現(xiàn)了這個目的:

  • /
  • //?This?function?is?used?to?wait?for?a?provided?wait?time?and
  • //?check?the?game?state.?If?anyother?player?has?finished?this
  • //?game?we?return?flase.
  • //?
  • bool?WaitAndCheckGameState(int?waitingTime)
  • {
  • ????DWORD?startTime?=?::GetTickCount();
  • ????do?{
  • ????????//?這里的GetMostCompetitiveScore用來獲得最快玩家的剩余方塊數(shù)
  • ????????if(0?==?GetMostCompetitiveScore())
  • ????????????return?false;
  • ????}
  • ????while(::GetTickCount()?<=?startTime?+?waitingTime);
  • ????return?true;
  • }
  • ??????? 通過這種方式,我可以基本地判斷出游戲的勝負(fù)。但是還有一種情況暫時無法處理,就是當(dāng)遇到其他玩家也使用外掛并且都是用秒殺的時候,這時勝負(fù)很難判斷。程序會經(jīng)常判斷錯誤。在以后的版本中會進(jìn)行改進(jìn)。

    遇到的問題:

    1. 在HOOK中使用MessageBox似乎有問題,當(dāng)用鼠標(biāo)右擊的時候有偶然性。不是每次都能彈出。網(wǎng)上查了一下好像也說在HOOK中不易使用MessageBox。具體原因有待研究...

    2. 在多線程中使用InstallHook好像不能正常使用HOOK,沒找到什么原因,因?yàn)樵谶@個外掛中使用到了多線程來保證界面的可交互性,當(dāng)我在開始按鈕點(diǎn)擊的相應(yīng)函數(shù)中調(diào)用InstallHook可以正常使用,但是在AfxBeginThread函數(shù)啟動的新線程中調(diào)用InstallHook好像不能正常使用HOOK。

    3. 開始我的程序全部使用了CWindowDC作為獲取像素值的DC,后來改為用::GetPixel(g_GameDC,...),但是發(fā)現(xiàn)用這兩種方法獲取的同一個點(diǎn)的像素值不同,不知道是不是正常現(xiàn)象。

    4. 在模擬鼠標(biāo)點(diǎn)擊開始按鈕的時候我開始也使用了::SendMessage()函數(shù)去給游戲窗口發(fā)送消息,但是沒有成功。后來只能選擇用mouse_event來實(shí)現(xiàn)這個功能。但是在這個程序中mouse_event函數(shù)比起::SendMessage()函數(shù)有不足之處。我們需要保持窗口處于最頂層然后進(jìn)行點(diǎn)擊,造成了很多不便。不知道是何原因,可能還需要深入了解外掛知識。

    希望在今后的學(xué)習(xí)中能找到這些問題的原因及解決方法。

    程序存在的問題:

    目前來說程序還存在很多問題:

    1. 沒有強(qiáng)大的道具處理能力。在出現(xiàn)道具的情況下程序有時會失靈(我的錯...有bug沒解掉T_T)。

    2. 在運(yùn)行時需要將游戲窗口置于在前端,要不然也會有問題。因?yàn)橐@取顏色、用mouse_event等都需要將游戲窗口置前。

    3. 在遇到更強(qiáng)大的外掛的時候秒殺拼不過人家...郁悶...這個估計跟外掛的類型有關(guān)了...等待下一個版本的完成(通過偽造數(shù)據(jù)包的方法應(yīng)該會更快更強(qiáng)大)。

    4. 本來以為能解決前面三個問題程序就比較完美了,沒想到在寫這篇文章的時候花生米找我玩連連看,牛人啊,開口就讓我調(diào)半秒。一玩果然不虧zju連連看老大啊,速度相當(dāng)快。他提出了很多建設(shè)性意見,可以更好的偽裝。原來大牛們看外掛有自己的一套啊。趕緊記下來,在以后的版本中改進(jìn)改進(jìn):1.高手都用重列和指南針。本來以為用指南針會影響高手速度,原來不是的,高手都是重列完馬上指南針然后可以連擊下去。見識了見識了。2. 很多外掛不符合人眼規(guī)則,就是不消近的消遠(yuǎn)的。一直以為高手外連連看都狂點(diǎn)狂點(diǎn),想不到還有心思研究這個。牛啊。另外他還跟我說了一個秘密哈哈,全中國的連連看高手都在無道具場3和10。果然是高手,唉,真的見識了。下次再寫一個厲害一點(diǎn)的讓他去鑒定哈哈。

    5. 今天在測試的時候又遇到了一個新的問題...在vista下不能用...唉...問題多多,期待下一個版本的完成吧...

    在整個程序中也遇到了一些問題,至今還未解決,暫且一一列出:

    總結(jié)

    以上是生活随笔為你收集整理的连连看外挂1.0的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。