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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

用算法去扫雷(go语言)

發布時間:2023/12/20 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 用算法去扫雷(go语言) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
  • 最初的準備
    首先得完成數據的錄入,及從掃雷的程序讀取界面數據成為我的算法可識別的數據
    其次是設計掃雷的算法,及如何才能判斷格子是雷或者可以點擊鼠標左鍵和中鍵。
    然后將步驟2的到的結果通過我的程序實現鼠標的點擊動作
    下面是一個成功的gif圖片,放在前面容易吸引人啊,哈哈。
  • 首先實現第一步
    將掃雷程序界面數據讀取并保存為我的代碼可識別的數據。我們知道程序界面的各個數字都有不同的顏色,那么我們可以通過這些顏色得到每個數字的特征碼,及我的程序可以通過這些特征碼識別這些數據。
    什么是圖片的特征碼,及圖片和特征碼為一對一的關系,知道特征碼就能確定這個圖片是啥,因此我的程序內置了特征碼,然后獲取雷區截圖,通過分析rgb值得到特征碼和已有的特征碼比較,相同則確定了該位置是個啥。我借鑒了該文章圖像識別技術的圖像灰化,及將一張彩色圖片灰化為兩個不同的值,及圖片二值化。使用如下算法

    經過我的處理可以得到下面的值,我只拿其中一個數字說明,下面是數字2做圖像灰化后得到的二值圖像碼
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,
    1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,
    1,1,0,0,0,1,1,1,1,0,0,0,1,1,1,1,
    1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,
    1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,
    1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,
    1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,
    1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,
    1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,
    1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
    得到所有圖像的特征碼如下,我用go語言做了處理
  • GridNumbers = map[GridDefine][]int{ // 每個標識的特殊位置的顏色值,用于區分這些位置的值DefOne: {0, 1, 0, 1, 1, 1, 1, 1, 1, 1}, // 1DefTwo: {0, 0, 0, 0, 0, 0, 0, 1, 0, 1}, // 2DefThree: {0, 1, 0, 0, 0, 0, 0, 1, 0, 1}, // 3DefFour: {0, 1, 0, 1, 0, 1, 0, 1, 1, 1}, // 4DefFive: {0, 1, 0, 0, 1, 0, 1, 1, 0, 1}, // 5DefSix: {0, 0, 0, 0, 1, 1, 1, 1, 1, 1}, // 6DefSeven: {1, 1, 0, 1, 0, 1, 0, 1, 0, 1}, // 7DefEight: {0, 0, 0, 0, 0, 1, 0, 1, 0, 1}, // 8DefFlag: {0, 1, 1, 1, 1, 1, 1, 1, 1, 0}, // 紅旗DefMine: {0, 1, 0, 1, 0, 1, 0, 1, 0, 1}, // 地雷DefRedMine: {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // 標紅地雷,表示輸了DefClick: {1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, // 可點擊白板DefNotNeed: {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, // 不可點擊}
  • 找到圖像識別方案,下面就說說算法吧
    第一步,找到必定為雷的位置,如下圖所示,之所以能得出標旗位置,是因為周圍有數字可以確定這是一個雷。因此有下面的準則
    可點擊數 + 已經標旗數 = 本格子雷數,右鍵把可點擊位置全部標雷
    此時這些點已經沒有意義,可以不考慮了

    第二步,找到有不確定的位置,但是標旗數已經等于本格雷數,所以該格子周圍可點擊處一定不是雷。所以還有一下準則
    標旗數 = 本格子雷數,鼠標中鍵點擊格子,點開可點擊位置
    此時對于左邊的3右邊的2來說雷的數量已經夠了,可以中鍵點擊這兩個格子。此時這些點已經沒有意義,可以不考慮了

    第三步,如果遍歷所有數字點,沒有滿足第一和第二步的情況,那么將除了第一步和第二步排除后的數字點列為不確定的點。此時需要相鄰2個點同時做判斷。下面介紹一種不確定狀態下標雷技巧
    對于中間的2上面3個點有二個雷,2右邊的1表示上面三個點中右邊二個點只能有一個雷,因此對于中間的2來說剩余的一個點一定是雷。下面是準則。
    兩個相鄰格子待標雷數多的為A,待標雷數少的為B
    A待標雷數 - B待標雷數 = A可點擊數 - AB相交位置數
    表示A除了相交位置以外的可點擊位置一定全是雷

    第四步,下面的情況我一定知道紅色位置不是雷,因為對于4而言下面二個點只能有一個雷,那么4左邊的2的已經把雷確認完了,所以紅色位置肯定不是雷,下面是準則。
    兩個相鄰格子A和B待標雷數相同,A的可點擊數為a,格子B的可點擊數為b,兩個格子相交數為c
    如果a = c,則格子B除開相交位置的可點擊位置一定不是雷
    如果b = c,則格子A除開相交位置的可點擊位置一定不是雷
  • 根據上面四個準則,掃雷一定能進行到除了猜雷就沒轍的情況。意思是按照上面的準則,全都沒法判定是雷不是雷的情況,那么只能乖乖的猜雷了。然而我多出查詢資料得到猜雷最多憑概率,沒有辦法做到一定成功。因此我的掃雷算法的猜雷只是簡單的在不確定點周圍隨機點一個左鍵。所以用我的程序自動猜雷是會出現猜錯的情況。
  • 下面公布我的代碼吧
  • package mainimport ("log""math/rand""reflect""time""unsafe""fmt""github.com/lxn/win" // 另外的源碼,github.com/CodyGuo/win )type Pos struct {x int /* 表示一個點的橫坐標,縱坐標 */y int }/* 定義格子內容的類型,不直接用int避免使用時混亂 */ type GridDefine intconst ( /* 枚舉類型,標記格子內容 */DefClick GridDefine = iota // 可點擊白板DefOne // 1DefTwo // 2DefThree // 3DefFour // 4DefFive // 5DefSix // 6DefSeven // 7DefEight // 8DefFlag // 紅旗DefNotNeed // 不可點擊的空白,以及標識無用的數字位置DefMine // 地雷DefRedMine // 標紅地雷,表示輸了 )const (GameName = "掃雷" // 游戲窗體名稱GameHigh = 16 // 雷區高度GameWide = 30 // 雷區寬度GridLen = 16 // 雷區每個格子長寬//GameMine = 99 // 存在雷的個數,貌似用不上了 )var (StartBtn win.POINT // 笑臉的位置,點擊可以開始游戲StartNum win.POINT // 標記剩余雷數的位置StartMine win.POINT // 雷區起始位置GameHwnd win.HWND // 掃雷窗體對象GuessMineOk int // 1表示啟動啟動猜雷,0表示玩家自己猜雷TeachModel int // 1表示每一步都顯示操作,0表示不顯示每一步的操作CntNotSure int // 標記不確定點個數flagStart int // 標記是否已經開局,如果開局則永遠不會等于0NotSurePos [GameHigh * GameWide]Pos // 緩存一次掃描中所有不確定點位置GridSave [][]GridDefine // 保存數據的二維數組GridNumbers = map[GridDefine][]int{ // 每個標識的特殊位置的顏色值,用于區分這些位置的值DefOne: {0, 1, 0, 1, 1, 1, 1, 1, 1, 1}, // 1DefTwo: {0, 0, 0, 0, 0, 0, 0, 1, 0, 1}, // 2DefThree: {0, 1, 0, 0, 0, 0, 0, 1, 0, 1}, // 3DefFour: {0, 1, 0, 1, 0, 1, 0, 1, 1, 1}, // 4DefFive: {0, 1, 0, 0, 1, 0, 1, 1, 0, 1}, // 5DefSix: {0, 0, 0, 0, 1, 1, 1, 1, 1, 1}, // 6DefSeven: {1, 1, 0, 1, 0, 1, 0, 1, 0, 1}, // 7DefEight: {0, 0, 0, 0, 0, 1, 0, 1, 0, 1}, // 8DefFlag: {0, 1, 1, 1, 1, 1, 1, 1, 1, 0}, // 紅旗DefMine: {0, 1, 0, 1, 0, 1, 0, 1, 0, 1}, // 地雷DefRedMine: {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // 標紅地雷,表示輸了DefClick: {1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, // 可點擊白板DefNotNeed: {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, // 不可點擊}GridSlice = make([]byte, GameWide*GridLen*GameHigh*GridLen*4) // 緩存截屏數據,全局初始化,避免重復申請內存 )/** * 初始化數據 * 做準備工作 **/ func init() {var RectPos, ClientPos win.RECT // 找出窗體左上,右下的坐標,以及窗體除標題欄的寬高GameHwnd = win.FindWindow(win.StringToBSTR(GameName), win.StringToBSTR(GameName))if !win.ShowWindow(GameHwnd, win.SW_RESTORE) { // 激活窗口,如果當前為最小化則還原窗體log.Fatal("請運行掃雷游戲...") /* 找不到游戲窗體,直接退出 */}win.UpdateWindow(GameHwnd) // 更新窗體win.GetWindowRect(GameHwnd, &RectPos) /* 得到窗體左上右下的坐標 */win.GetClientRect(GameHwnd, &ClientPos) /* 得到窗體處標題欄以外的長和寬 */var leftX, topY = RectPos.Right - ClientPos.Right, RectPos.Bottom - ClientPos.BottomStartBtn.X, StartBtn.Y = (RectPos.Left+RectPos.Right)/2, topY+25 // 初始化開始游戲按鈕StartMine.X, StartMine.Y = leftX+10, topY+53 // 初始化雷區起始位置StartNum.X, StartNum.Y = leftX+15, topY+14 // 初始化數字區位置GridSave = make([][]GridDefine, GameHigh) // 產生二維數組保存雷區數據for i := 0; i < GameHigh; i++ { // 遍歷高GridSave[i] = make([]GridDefine, GameWide)}fmt.Print("1 [教學模式],0 [自動模式],請輸入:")fmt.Scanln(&TeachModel)if TeachModel == 0 { /* 如果是自動模式則需要選擇猜雷方法 */fmt.Print("1 [自動猜雷],0 [人工猜雷],請輸入:")fmt.Scanln(&GuessMineOk)} /* 如果是教學模式,則默認人工猜雷 */ }/** * 開始執行 **/ func main() {var (i, j, flagSure, flagAuto int)win.SetCursorPos(StartBtn.X, StartBtn.Y) // 媽逼的只能鼠標點擊把窗口激活了,試過win32 api不行啊win.MouseEvent(win.MOUSEEVENTF_LEFTDOWN|win.MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)time.Sleep(time.Millisecond * 200) // 雙擊開始的笑臉win.MouseEvent(win.MOUSEEVENTF_LEFTDOWN|win.MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)rand.Seed(time.Now().UnixNano()) //初始化隨機數種子log.Println("游戲開始!")for RefreshGrid() && GetRemainMineCnt() > 0 { /* 當遇到紅色地雷 或 地雷 以及剩余雷數為0 */CntNotSure, flagSure = 0, 0for i = 0; i < GameHigh; i++ { // 遍歷高度for j = 0; j < GameWide; j++ { //遍歷寬度if GridSave[i][j] >= DefOne && GridSave[i][j] <= DefEight { /* 代表數字的位置 */switch flagPos, style := GetAroundCount(i, j, 0); style {case 1: /* 類型1表示周圍可點擊格子一定是雷 */for _, v := range flagPos {ClickPos(v, "right")}GridSave[i][j] = DefNotNeed /* 標完雷,此位置已無效 */flagSure = 1 // 表示本次遍歷出現可點右鍵標雷的點case 2: /* 類型2表示本格可以點鼠標中鍵了 */ClickPos(Pos{x: j, y: i}, "center")GridSave[i][j] = DefNotNeed /* 點開一片區域,此位置已無效 */flagSure = 2 // 表示本次出現可點中鍵的點if RefreshGrid() == false { /* 點中鍵后需要刷新當前界面避免重復點擊中鍵 */goto EndLoop // 當然如果遇到地雷則退出循環}case 3: /* 周圍地雷已經標完,且不需要點擊中鍵 */GridSave[i][j] = DefNotNeed /* 點開一片區域,此位置已無效 */flagSure = 3 // 表示本次出現可點中鍵的點default:NotSurePos[CntNotSure].x = iNotSurePos[CntNotSure].y = jCntNotSure++ // 記錄本次遍歷所有無法確定雷的點}}} //GameWide} // GameHighif flagSure == 0 { /* 本次遍歷全是不確定的點 */if flagStart == 0 { /* 開局,如果沒有出現必定能判斷的點,則一直隨機點鼠標左鍵 */ClickPos(Pos{x: rand.Intn(GameWide), y: rand.Intn(GameHigh)}, "left")continue // 開局點擊后如無法出現必定可以繼續的點,直接進入下一個循環}for i = 0; i < CntNotSure; i++ { // 遍歷不確定的點if GetAroundNotSureCount(NotSurePos[i].x, NotSurePos[i].y) {break // 如果對雷區有操作則需要重新整個界面掃雷}flagSure++ // 如果把不確定點全部遍歷則表示需要猜雷了}if flagSure == CntNotSure {/** 猜測某個點沒有雷,運氣成分,如需提高勝率可優化下面代碼,下面是關于猜雷的思路* 1.(https://tieba.baidu.com/p/1761431400?red_tag=3267954760)* 上面是我看到比較靠譜的理論,由于計算機雖然笨但運算快,因此我打算實現這個方案* 2.死猜,及無論如何都不可能判定哪個是雷,那就只能隨機猜一個了* 3.這里還要注意一點,及剩余雷數,有時候根據剩余雷數可以提高勝率* 按照上面3個步驟,還沒發確定是不是雷,媽逼只能靠運氣了* 到處找教程最終沒能找到一個好點的方案,還是隨機點擊一個位置**/if GuessMineOk == 1 { /* 自動猜雷是隨機點一個 */i = rand.Intn(CntNotSure) // 下面是最挫的猜雷方案,隨機找一個不確定點,在該點周圍隨機點一個點var tPos, _ = GetAroundCount(NotSurePos[i].x, NotSurePos[i].y, 1)i = rand.Intn(len(tPos)) // 在不確定列表中隨機找一個點,隨機點這個點周圍的一個可點擊點ClickPos(Pos{x: tPos[i].x, y: tPos[i].y}, "left")} else {if flagAuto == 0 {flagAuto = 1log.Println("請你猜雷吧,確認后按空格鍵繼續...")WaitKeyboard(win.VK_SPACE) // 等待空格鍵按下并松開}time.Sleep(time.Millisecond * 500) // 玩家猜雷,可以允許延時}}} else { /* 將開局標志賦值,此時flagStart代表已經不是開局了 */flagStart = flagSureflagAuto = 0 // 經歷了猜雷到不猜}} EndLoop:if GetRemainMineCnt() == 0 {log.Println("你贏了比賽!")} else {log.Println("你輸了比賽!")}fmt.Println("請按回車退出...")fmt.Scanln() // 雙擊打開時避免最終一閃而逝! }/** * 找到不可確定點 * 與周圍的不可確定點一起 * 看能否確定一些雷和可點擊位置 * return,返回true表示一定點了數字或標了雷 **/ func GetAroundNotSureCount(x, y int) bool {var (i, j, cntMix intclickPos, needMine = GetAroundCount(x, y, 1) /* 得到本點可點擊位置,以及剩余雷數 */clickCnt = len(clickPos) // 可點擊數個數MorePos = [2]struct {//* 記錄在點a不在點b周圍可點擊的位置 */pos [8]Pos //最大也就8個點,數組完全夠用cnt int // 記錄點的個數,用于遍歷時使用}{} // 多于點的位置,以及點的個數endClick = struct {k int // 最終需要點擊MorePos數組的哪一個key string // 鼠標鍵值,因為有時候需要左鍵數字,有時候需要右鍵標雷}{}posInNotSure = func(x1, y1 int) bool { //閉包,確定這個點在不確定列表中for i1 := 0; i1 < CntNotSure; i1++ {if NotSurePos[i1].x == x1 && NotSurePos[i1].y == y1 {return true}}return false})/* 只找x相同或y相同的點 */for i = x - 1; i <= x+1; i++ { // 以下雙層循環遍歷本點四周的點for j = y - 1; j <= y+1; j++ { /* 只會找上下左右4個點,并且本點是一個數字點,且必須在不確定列表中 */if i >= 0 && j >= 0 && i < GameHigh && j < GameWide && (i == x && j != y || i != x && j == y) && (GridSave[i][j] >= DefOne && GridSave[i][j] <= DefEight) && posInNotSure(i, j) {cntMix = 0 // 記錄兩個點重合可點擊位置個數/* 根據(x,y),(i,j)這兩個不確定點找還能標雷或點擊的位置 */var nowPos, nowMine = GetAroundCount(i, j, 1) // 得到周圍的這個不確定點,周圍可點擊位置和待標雷數MorePos[0].cnt = 0for _, v1 := range clickPos {endClick.k = 0 // 這里該變量作為標記使用,避免定義太多變量了for _, v2 := range nowPos {if v1 == v2 {endClick.k = 1cntMix++ // 記錄相交的點個數break}}if endClick.k == 0 {MorePos[0].pos[MorePos[0].cnt] = v1MorePos[0].cnt++ // 記錄在clickPos中且不在nowPos中的點}} // range clickPosMorePos[1].cnt = 0for _, v1 := range nowPos {endClick.k = 0for _, v2 := range clickPos {if v1 == v2 {endClick.k = 1break}}if endClick.k == 0 {MorePos[1].pos[MorePos[1].cnt] = v1MorePos[1].cnt++ // 記錄在clickPos中且不在nowPos中的點}} // range nowPosendClick.k = -1 // 當賦值其他數據時,表示一定需要點擊endClick.key = "right" // 因為只有nowMine == needMine才為left,設置默認值if nowMine == needMine { // 兩個點待標雷個數相同if cntMix == clickCnt { // 表示一個點全部在相交位置,此時另一個點附近不相交的點只能是數字endClick.k = 1} else if cntMix == len(nowPos) { // 同上,只是換了一個點而已endClick.k = 0}endClick.key = "left"} else if nowMine > needMine { // 待標雷個數大的一方if nowMine-needMine == len(nowPos)-cntMix {endClick.k = 1 // 2個點待標雷數相減 = 可點擊數大的點減去重合點的個數,表示可點擊數多的點多出的位置一定全是雷}} else {if needMine-nowMine == clickCnt-cntMix {endClick.k = 0 // 同上,只是點不一樣而已}}if endClick.k >= 0 && MorePos[endClick.k].cnt > 0 {for i = 0; i < MorePos[endClick.k].cnt; i++ {ClickPos(MorePos[endClick.k].pos[i], endClick.key) // 需要操作的格子不是地雷則隨便搞}return true // 已經點擊數字或標雷,整個界面需要重新判定}} // end if} // end j} // end ireturn false // 當前點沒有合適的判定點 }/** * 找到x,y周圍8個點中 * 可點擊的點個數,已經標為紅旗的個數 * 返回可點擊的坐標位置,且返回當前這個點的類型 * 類型有2=>需要點擊鼠標中鍵,1=>需要把周邊的可點擊點標小旗 * 剩下的類型需要更深層次的計算了 **/ func GetAroundCount(x, y, inTpye int) (flagPos []Pos, status int) {var (i, j intcntClick, cntFlag GridDefine /* 標記可點擊,標記紅旗 */)for i = x - 1; i <= x+1; i++ { // 以下雙層循環遍歷本點四周的點for j = y - 1; j <= y+1; j++ {if i >= 0 && j >= 0 && i < GameHigh && j < GameWide && (i != x || j != y) { // 剔除超過邊界點,以及x,y所在點if DefClick == GridSave[i][j] {flagPos = append(flagPos, Pos{x: j, y: i})cntClick++} else if DefFlag == GridSave[i][j] {cntFlag++}}} // y} // xif 1 == inTpye { /* 返回周圍可點擊點位置,并且返回當前點剩余雷的個數 */return flagPos, int(GridSave[x][y] - cntFlag)}if GridSave[x][y] == cntFlag {if cntClick == 0 { /* 如果可點擊數量為空,則當前位置不需要點鼠標中鍵 */return flagPos, 3}return flagPos, 2 /* 小旗個數等于本格子雷數,點擊鼠標中鍵 */}if cntClick+cntFlag == GridSave[x][y] {return flagPos, 1 /* 可點擊 + 小旗 = 本格子雷數,表示可點擊一定全是雷 */}return flagPos, 0 /* 剩下的情況一定是可點擊格數大于本格剩余雷數 */ }/** * 傳入ClickTask對象 * 模擬鼠標點擊某個位置 * 單擊左鍵中鍵右鍵 **/ func ClickPos(pos Pos, key string) {var NowPos = uintptr((pos.x*GridLen + 21) | (pos.y*GridLen+64)<<16)switch key {case "left": // 確定不是雷,則隨便點左鍵win.SendMessage(GameHwnd, win.WM_LBUTTONDOWN, 0, NowPos)win.SendMessage(GameHwnd, win.WM_LBUTTONUP, 0, NowPos)case "right":if GridSave[pos.y][pos.x] == DefClick { /* 右鍵位置為可點擊才點,否則不點,避免重復標雷 */win.SendMessage(GameHwnd, win.WM_RBUTTONDOWN, 0, NowPos)win.SendMessage(GameHwnd, win.WM_RBUTTONUP, 0, NowPos)GridSave[pos.y][pos.x] = DefFlag // 并且此處標記為地雷}case "center": // 中鍵點開一片區域win.SendMessage(GameHwnd, win.WM_MBUTTONDOWN, 0, NowPos)win.SendMessage(GameHwnd, win.WM_MBUTTONUP, 0, NowPos)}if TeachModel == 1 && flagStart != 0 { /* 開局以后的操作才顯示 */var tmpPos = win.POINT{X: int32(pos.x*GridLen + 21), Y: int32(pos.y*GridLen + 64)}win.ClientToScreen(GameHwnd, &tmpPos) /* 將相對窗體位置轉化為相對整個屏幕的位置 */win.SetCursorPos(tmpPos.X, tmpPos.Y)log.Printf("點擊鼠標按鍵:%6s,請按空格鍵繼續...\n", key)WaitKeyboard(win.VK_SPACE) // 等待空格鍵按下并松開} }/** * 等待一個按鍵按下并松開 **/ func WaitKeyboard(key int32) {for win.GetKeyState(key) >= 0 { /* 有按鍵退出循環 */time.Sleep(time.Millisecond * 100)}for win.GetKeyState(key) < 0 { /* 松開按鍵退出循環 */time.Sleep(time.Millisecond * 100)} }/** * 用到獲取屏幕截圖代碼 * 已經去掉robotgo,讓程序沒有dll依賴 * 截屏代碼摘自https://github.com/vova616/screenshot * 這里需要學習指針轉換的操作,以及內存拷貝的操作 **/ func RefreshGrid() bool {var (w, h int32 = GameWide * GridLen, GameHigh * GridLena, b, c, d, e int // 只是作為循環變量而已screen, screenMem win.HDCdib win.HBITMAPbi win.BITMAPINFOptr = unsafe.Pointer(uintptr(0))obj win.HGDIOBJtmpArr [10]int // 緩存那幾個點的值GrayControl = func(r, g, b byte) int { /* 灰化圖像,閾值為150 */if float32(r)*0.11+float32(g)*0.59+float32(b)*0.3 >= 150 {return 1}return 0})bi.BmiHeader.BiSize = uint32(reflect.TypeOf(bi.BmiHeader).Size())bi.BmiHeader.BiWidth = wbi.BmiHeader.BiHeight = -h /* Non-cartesian, please */bi.BmiHeader.BiPlanes = 1bi.BmiHeader.BiBitCount = 32bi.BmiHeader.BiCompression = win.BI_RGBbi.BmiHeader.BiSizeImage = uint32(4 * w * h)bi.BmiHeader.BiXPelsPerMeter = 0bi.BmiHeader.BiYPelsPerMeter = 0bi.BmiHeader.BiClrUsed = 0bi.BmiHeader.BiClrImportant = 0if screen = win.GetDC(0); screen == 0 {return false}defer win.ReleaseDC(0, screen)dib = win.CreateDIBSection(screen, &bi.BmiHeader, win.DIB_RGB_COLORS, &ptr, 0, 0)if dib == 0 || win.GpStatus(dib) == win.InvalidParameter {return false}defer win.DeleteObject(win.HGDIOBJ(dib))if screenMem = win.CreateCompatibleDC(screen); screenMem == 0 {return false}defer win.DeleteDC(screenMem)if obj = win.SelectObject(screenMem, win.HGDIOBJ(dib)); obj == 0 || obj == 0xffffffff {return false}defer win.DeleteObject(obj)if !win.BitBlt(screenMem, 0, 0, w, h, screen, StartMine.X, StartMine.Y, win.SRCCOPY) {return false // 截屏}hDrp := (*reflect.SliceHeader)(unsafe.Pointer(&GridSlice))hDrp.Data = uintptr(ptr) /* 將指針中的數據映射到[]byte中 *///hDrp.Len = int(w * h * 4)//hDrp.Cap = int(w * h * 4)for a = 0; a < GameHigh; a++ { // 遍歷高度for b = 0; b < GameWide; b++ { //遍歷寬度if DefNotNeed == GridSave[a][b] || DefFlag == GridSave[a][b] {continue /* 該點已沒意義 或 該點已經標記為地雷,所以不用計算 */}for c, e = 0, 0; c < 5; c++ { // 找到那幾個特殊的點,備注找到更少點確定值則可以越快得到數據d = (a*GridLen+7-c)*4*GameWide*GridLen + 4*(b*GridLen+c+7)tmpArr[e] = GrayControl(GridSlice[d+2], GridSlice[d+1], GridSlice[d])e++/* 注意本處的特征碼是根據灰化后的圖像得出,我也是花了九牛二虎之力才搞到的額 */d = (a*GridLen+c+9)*4*GameWide*GridLen + 4*(b*GridLen+2)tmpArr[e] = GrayControl(GridSlice[d+2], GridSlice[d+1], GridSlice[d])e++}for k, v := range GridNumbers { // 遍歷map,得到本格子的實際信息for c = 0; c < 10; c++ {if v[c] != tmpArr[c] {c = -1 // 標識該位置與當前v不匹配break}}if c != -1 { /* 如果z=-1表示沒有匹配到當前v的特征值 */if k == DefMine || k == DefRedMine {return false /* 遇到標紅的雷或者黑色的雷,游戲結束 */}GridSave[a][b] = kbreak}}} // 內層for結束} // 外層for結束return true }/** * 從界面得到剩余雷的個數 **/ func GetRemainMineCnt() int {var (i, x, y intNum [3]intFlagCnt [7]int/* __0__* 5| |1* |__6__|* 4| |2* |__3__|* 按照上面的順序標記一個數字* 相鄰顏色值相同則賦值為1,不同則賦值為0* 根據對應的map匹配得到該位置具體數字**/FlagDot = map[int][]int{0: {1, 1, 1, 1, 1, 1, 0},1: {0, 1, 1, 0, 0, 0, 0},2: {1, 1, 0, 1, 1, 0, 1},3: {1, 1, 1, 1, 0, 0, 1},4: {0, 1, 1, 0, 0, 1, 1},5: {1, 0, 1, 1, 0, 1, 1},6: {1, 0, 1, 1, 1, 1, 1},7: {1, 1, 1, 0, 0, 0, 0},8: {1, 1, 1, 1, 1, 1, 1},9: {1, 1, 1, 1, 0, 1, 1},}NumPosX = []int32{StartNum.X, StartNum.X + 13, StartNum.X + 26} /* 三個數字左上角x坐標 */hdc = win.CreateDC(win.StringToBSTR("DISPLAY"), nil, nil, nil))defer win.DeleteDC(hdc) // 用完hdc對象要釋放for i = 0; i < 3; i++ {if win.GetPixel(hdc, NumPosX[i]+5, StartNum.Y) == win.GetPixel(hdc, NumPosX[i]+5, StartNum.Y+1) {FlagCnt[0] = 1} else {FlagCnt[0] = 0}if win.GetPixel(hdc, NumPosX[i]+9, StartNum.Y+4) == win.GetPixel(hdc, NumPosX[i]+9, StartNum.Y+5) {FlagCnt[1] = 1} else {FlagCnt[1] = 0}if win.GetPixel(hdc, NumPosX[i]+9, StartNum.Y+14) == win.GetPixel(hdc, NumPosX[i]+9, StartNum.Y+15) {FlagCnt[2] = 1} else {FlagCnt[2] = 0}if win.GetPixel(hdc, NumPosX[i]+5, StartNum.Y+18) == win.GetPixel(hdc, NumPosX[i]+5, StartNum.Y+19) {FlagCnt[3] = 1} else {FlagCnt[3] = 0}if win.GetPixel(hdc, NumPosX[i]+1, StartNum.Y+14) == win.GetPixel(hdc, NumPosX[i]+1, StartNum.Y+15) {FlagCnt[4] = 1} else {FlagCnt[4] = 0}if win.GetPixel(hdc, NumPosX[i]+1, StartNum.Y+4) == win.GetPixel(hdc, NumPosX[i]+1, StartNum.Y+5) {FlagCnt[5] = 1} else {FlagCnt[5] = 0}if win.GetPixel(hdc, NumPosX[i]+5, StartNum.Y+9) == win.GetPixel(hdc, NumPosX[i]+5, StartNum.Y+10) {FlagCnt[6] = 1} else {FlagCnt[6] = 0}for x = 0; x < 10; x++ {for y = 0; y < 7; y++ {if FlagCnt[y] != FlagDot[x][y] {y = -1 // 不匹配當前數字,不必遍歷所有值break}}if -1 != y { // 如果全部匹配則就是這個數字了Num[i] = xbreak /* 找到數字值,不必再循環 */}}}return Num[0]*100 + Num[1]*10 + Num[2] /* 轉換為剩余雷數 */ }
  • 這里我要說一下,我的程序可以教你掃雷。如下圖輸入1或0回車即可

    如果選擇了教學模式,那么只能用人工猜雷。此時教學模式會把每一步鼠標操作都展現出來,方便大家思考和學習。這時候按一下空格鍵才會自動進行下一步,而且會顯示鼠標位置到底是右鍵標雷還是左鍵點擊還是中鍵操作。你可以根據當前操作分析周圍的點,看看我的程序是如何做到判斷一個位置是否有雷的。

  • 剩下的自動模式無非就程序自動掃雷,此時會選擇自動猜雷還是人工猜雷,如果人工猜雷則需要等程序無法判定能否操作時由你自己去猜雷。自動猜雷就是隨便點了一個點而已。綜上所述,我的掃雷代碼算是完成了,除了猜雷沒有好的方案完成以外,其他都做的很不錯。希望給大家一些啟發。
    這里是程序哈掃雷游戲下載程序

  • 當然如果你沒有csdn積分可以用百度云,密碼49nt

    總結

    以上是生活随笔為你收集整理的用算法去扫雷(go语言)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 自拍偷拍欧美视频 | 制服.丝袜.亚洲.中文.综合懂 | 自拍偷拍视频在线 | 日本妈妈9 | 日韩精品v| 黄色短视频在线播放 | 800av免费在线观看 | 人人爽久久涩噜噜噜网站 | 精品一区三区 | 中文字幕 国产精品 | 97精品人妻麻豆一区二区 | 伦理片av | 久久久久久综合网 | 亚洲精品一品 | 成人性色生活片 | 国产精品成人自拍 | 国产精品熟女一区二区不卡 | 国产处女 | 免费av网站在线 | 久草精品视频在线观看 | 欧美精品黄色片 | 不卡视频在线播放 | 国产精品无圣光 | 中文字幕在线观看av | 六月丁香久久 | 福利在线一区二区三区 | 少妇偷人精品无码人妻 | 国产经典久久 | 亚洲第一页在线 | 女王脚交玉足榨精调教 | 第一毛片 | 新亚洲天堂 | 欧美在线观看一区二区三区 | 杨贵妃颤抖双乳呻吟求欢小说 | 日韩一区二区三区三四区视频在线观看 | 成人羞羞国产免费游戏 | 欧美性受xxxx黑人猛交88 | 午夜欧美在线 | 美国一区二区三区 | 欧美黑人三级 | 久久亚洲AV无码专区成人国产 | 91区国产 | 久久久久99精品成人片 | 欧美精品久久久久性色 | 久久精彩视频 | 在线aaa| 亚洲视屏一区 | 成人久久久精品乱码一区二区三区 | 国产美女网 | 亚洲男人的天堂网 | 日韩三级精品 | 99精品欧美 | 一区二区免费av | 亚洲精选久久 | sm国产在线调教视频 | 杨幂毛片午夜性生毛片 | 久久国色 | 变态另类丨国产精品 | 亚洲一二三四区 | 五月婷婷综合久久 | 日韩有码av| 色五丁香 | 日本久久综合 | 青青国产视频 | 三级视频网 | 亚洲色图14p | 日韩国产欧美一区二区三区 | 天堂av2014| 久久手机视频 | 97色在线观看 | 午夜激情网址 | 国产小视频免费在线观看 | 欧美激情喷水 | 国产黑丝91 | 91日韩精品| 精品国产乱码久久久久久蜜臀网站 | 奇米精品一区二区三区四区 | 美女屁股眼视频网站 | 涩五月婷婷 | 中文字幕人妻丝袜乱一区三区 | 欧美一级一区 | 中文字幕国产视频 | 国产日b视频 | 男性裸体全身精光gay | 色天天色| 中文字幕一区二区人妻视频 | 日日夜夜爱爱 | 午夜男人影院 | 亚洲中文字幕无码一区 | 可以直接看的无码av | 粗大黑人巨茎大战欧美成人 | www.com捏胸挤出奶 | 男人的天堂手机在线 | 91sex国产 | 青青草成人在线 | 黄色视屏在线免费观看 | 亚洲欧美一二三区 | 西西人体www大胆高清 | 色噜|