ブラウザゲーム用のマクロボットの作成

はじめに



少し前まで、ゲームはGoogle+に登場しました。 それについてトピックを読んだ後、私は何かをプレイすることにしました。 選択は、ゲームダイヤモンドダッシュに落ちました。 ゲーム中にしばらくして、プログラマーは同じタイプのアクションを自動化する必要があると私に話し始めました。 そして、それが何から来たのか...



*注:経験豊富なプレーヤーでも40万人以上を獲得するのは難しい



以前は、画面とマウスを操作するタスクに遭遇したことはありませんでした。 短いグーグル検索の後、 AutoItマクロ言語を使用して決定することになりました。

カットの下に、ゲームの簡単な説明、フィールドを認識する私の方法、クリックポイントを決定するアルゴリズム、およびいくつかの最適化があります。 また、githubスクリプトリポジトリへのリンクもあります。

UPDスクリプトのビデオを追加しました。



ゲームの概要



このゲームは、単純な「エリアをクリックして、3マス、1マス、1カラー」のパズルです。



5色の正方形で満たされた9 x 10のフィールドがあります。 最大ポイントを獲得するのに1分かかります。 3つ以上の同じ色のセルの領域をクリックすると、それは消え、それはその上に落ち、欠落は上から落ちます。 付与されるポイントの数は、エリアのサイズによって異なります。エリアが多いほど、ポイントが多くなります。

さらに、クリックをすばやく、ほぼ間違いなく実行すると、周囲のフィールドが点灯し、削除するたびに(この場合は爆発)、削除された領域に隣接するセルがキャプチャされます。



最後に、最後の機能:上部に、最後にダイヤモンドが付いたスケールがあり、セルが破壊されると塗りつぶされます(そして、誤ったクリックで減少します)。 それが満たされると、フィールド上のランダムな場所に燃えるダイヤモンドが表示されます。 それをクリックすると、時間が止まり、火の玉がフィールドの上に落ち、石が置かれた行と列のすべてのセルを破壊します。





ウィンドウ座標の定義



この部分は、角度の座標がコードにハードコーディングされる前の最後のターンに追加されました。 サードパーティのImageSearchライブラリの関数を使用して、保存された10 x 10ピクセルのテンプレートを検索します。 どうやら、背景がゲームごとにわずかに変化します。すべてのピースが登場したわけではないからです。

フォーラムは、実行時間が長いため、一般にImageSearchの使用を推奨しません。 しかし、ゲームの開始時に一度だけ座標を決定する必要があるため、時間のたるみの心配はありません。



色を認識してスクリーンショットを保存する



座標によってピクセルの色を決定するために、AutoItにはPixelGetColor関数があります。 しかし、実際に示されているように、90ピクセルの測定には2秒半しかかかりません。もちろん、これはあまり良くありません。 しかし、公平に言うと、この関数を使用して作成されたボットは40〜60万ポイントを獲得したと言わなければなりません。

フォーラムで、WinAPIを使用してビットマップにモニターの現在の状態を保存する方法が見つかりました。 ちなみに、このビットマップは、必要に応じて(デバッグ用など)、_ ScreenCapture関数を使用してファイルに保存できます。 次に、グリッド上にある90ピクセルの色を見て、それらを使用して正方形のカラーテーブルを作成します。

  1. Func _GetField ByRef $ aiField ; フィールドの色の配列を取得する
  2. ; WinAPIを使用したBitMapスクリーンショットのキャプチャ
  3. ローカル $ hWnd = WinGetHandle "Google+ゲーム -Google Chrome"
  4. ローカル $サイズ = WinGetClientSize $ hWnd
  5. ローカル $ hDC = _WinAPI_GetDC $ hWnd
  6. ローカル $ hMemDC = _WinAPI_CreateCompatibleDC $ hDC
  7. ローカル $ hBitmap = _WinAPI_CreateCompatibleBitmap $ hDC $ Size [ 0 ] $ Size [ 1 ]
  8. ローカル $ hSv = _WinAPI_SelectObject $ hMemDC $ hBitmap
  9. _WinAPI_BitBlt $ hMemDC 0、0 $サイズ [ 0 ] $サイズ [ 1 ] $ hDC 0、0 $ SRCCOPY
  10. _WinAPI_SelectObject $ hMemDC $ hSv
  11. _WinAPI_DeleteDC $ hMemDC
  12. _WinAPI_ReleaseDC $ hWnd $ hDC
  13. ローカル $ L = $サイズ [ 0 ] * $サイズ [ 1 ]
  14. ローカル $ tBits = DllStructCreate 'dword [' $ L ']'
  15. _WinAPI_GetBitmapBits $ hBitmap 4 * $ L DllStructGetPtr $ tBits
  16. ; セルの色の決定
  17. $ iCol = 0 から $ iNumColsの場合 -1
  18. $ iRow = $ iNumRowsの場合 -1 から 0 ステップ -1
  19. ; 平方メートル
  20. $ iX = $ iCornerX + $ iCol * 40 + $ iDeltaX
  21. $ iY = $ iCornerY + $ iRow * 40 + $ iDeltaY
  22. $ iPixelColor = Mod DllStructGetData $ tBits 1 $ iY * $ Size [ 0 ] + $ iX 0x1000000
  23. $ aiField [ $ iRow ] [ $ iCol ] = _GetCheckColor $ iPixelColor
  24. 次へ
  25. 次へ
  26. ; メモリリークを回避するためのデータ削除
  27. _WinAPI_DeleteObject $ hBitmap
  28. _WinAPI_DeleteObject $ hMemDC
  29. _WinAPI_DeleteObject $ tBits
  30. Endfunc




ここで、なぜ測定が1ポイントだけで行われるのかを言及する価値があります。 この方法は私が最初に試したもので、最終版に残っています。 これらの2つのポイントの間で、かなり多数の代替方法が試されました。その中には、正方形あたり64ポイント(8×8格子)を測定し、得られた値のさまざまな平均化、測定のための座標のランダムな選択、より良い精度のためにいくつかの最近の測定の履歴を保存しました...それらはすべて、最初の方法よりも正確性や利便性が低いことが判明しました。

私は画像認識のトピックから非常に遠いので、この問題で私を助けることができる単純な何かを知らない可能性があります。 この場合、私はどんな提案にも喜んでいるでしょう。 =)



カラーテーブルから単色領域を定義する



さて、そして最後に、適切な場所を見つけてそこをクリックすることが残っています。 これを行うために、フィールドを下から上に移動し(すべてが下に落ちるため、下のほうが上よりも空いているため)、モノクロパーツをチェックします。 これは、再帰的な深層検索( DFS )アルゴリズムを使用して行いました。 簡単に言えば、本質はこれです。開始セルをスタックに配置し、スタックが空になるまで、現在のセルをスタックから取り出し、スタックに配置したのと同じ色で隣接セルをバイパスします。 さて、私が言えることは、コードがより明確になったことです。 =)

  1. Func _DfsAreaSize ByRef $ aiField $ iStartX $ iStartY ; 非再帰的単色領域サイズ検索アルゴリズム
  2. ; 深さ探索法
  3. ローカル $ aiResult [ $ iNumCols * $ iNumRows ] [ 2 ] ; エリアに含まれるセルのリスト
  4. ローカル $ iResultSize = 0
  5. ローカル $ afMap [ $ iNumRows ] [ $ iNumCols ] ; 進捗フラグ
  6. $ iRow = 0 から $ iNumRows - 1の場合
  7. $ iCol = 0〜 $ iNumColsの場合 -1
  8. $ afMap [ $ iRow ] [ $ iCol ] = False
  9. 次へ
  10. 次へ
  11. $ afMap [ $ iStartX ] [ $ iStartY ] = True
  12. ローカル $ aiStack [ $ iNumRows * $ iNumCols ] [ 2 ] ; アクティブスタック
  13. ローカル $ iStackSize = 1
  14. $ aiStack [ 0 ] [ 0 ] = $ iStartX
  15. $ aiStack [ 0 ] [ 1 ] = $ iStartY
  16. $ iStackSize > 0
  17. $ iStackSize- = 1
  18. $ iX = $ aiStack [ $ iStackSize ] [ 0 ]
  19. $ iY = $ aiStack [ $ iStackSize ] [ 1 ]
  20. $ aiResult [ $ iResultSize ] [ 0 ] = $ iX
  21. $ aiResult [ $ iResultSize ] [ 1 ] = $ iY
  22. $ iResultSize + = 1
  23. $ iDirection = 0〜3の 場合 4つの近くのセルの列挙
  24. ローカル $ iNewX = $ iX
  25. ローカル $ iNewY = $ iY
  26. スイッチ $ iDirection
  27. 事例 0
  28. $ iNewY + = 1
  29. 事例 1
  30. $ iNewY- = 1
  31. 事例 2
  32. $ iNewX + = 1
  33. 事例 3
  34. $ iNewX- = 1
  35. エンドスイッチ
  36. If $ iNewX > = 0 And $ iNewX < $ iNumRows And _
  37. $ iNewY > = 0 And $ iNewY < $ iNumCols And _
  38. Not $ afMap [ $ iNewX ] [ $ iNewY ] And $ aiField [ $ iNewX ] [ $ iNewY ] = $ aiField [ $ iStartX ] [ $ iStartY ] そして
  39. $ afMap [ $ iNewX ] [ $ iNewY ] = True
  40. $ aiStack [ $ iStackSize ] [ 0 ] = $ iNewX
  41. $ aiStack [ $ iStackSize ] [ 1 ] = $ iNewY
  42. $ iStackSize + = 1
  43. エンディフ
  44. 次へ
  45. 終了
  46. $ iResultSizeを返す
  47. Endfunc




最適化





最適化1.ダイヤモンド


上記はすでに百万を得るのに十分です。 しかし、私はもっと欲しかった。 上記のアルゴリズムの最大の欠点は、おそらく、ダイヤモンドを判別できないことでした。 そのため、最後に、フィールドは次のようになりました。



一方、ダイヤモンドは非常に便利です。なぜなら、火の玉が落ちる間、タイマーが停止し、正方形が落ちるからです。 そのため、ギャップが埋められ、エラーが少なくなります。

ダイヤモンドを決定するには、まず、色の付いたセルが正しく検出され、ダイヤモンドがそれらの色のいずれにも該当しないように、測定値の座標を操作する必要がありました。 その後、サイズ3の$ aiDiams配列を作成します(すべてのダイヤモンドが遅かれ早かれそこに落ちるため、下の3行のみをチェックします)*幅(この場合-10)。 各測定で、下3行を確認し、セルが定義されている場合は、$ aiDiamsの対応する場所をリセットし、そうでない場合はインクリメントします。 したがって、ひし形のセルの場合、値は大きくなります。 特定のしきい値の累積で、クリックします。

  1. $ iRow = $ iNumRows - 1〜 $ iNumRows - 3 ステップ -1
  2. $ iCol = 0〜 $ iNumColsの場合 -1
  3. If aiField [ $ iRow ] [ $ iCol ] <> 0 Then
  4. $ aiDiams [ $ iRow ] [ $ iCol ] = 0
  5. その他
  6. $ aiDiams [ $ iRow ] [ $ iCol ] + = 1
  7. If aiDiams [ $ iRow ] [ $ iCol ] > 15 Then
  8. MouseClick "左" $ iCornerX + 30 + $ iCol * 40 $ iCornerY + 10 + $ iRow * 40 1 $ iMouseSpeed
  9. $ aiDiams [ $ iRow ] [ $ iCol ] = 0
  10. 睡眠 500
  11. 0を 返す
  12. エンディフ
  13. エンディフ
  14. 次へ
  15. 次へ




最適化2.過剰爆発


ここで、エラーの数を減らすことが非常に重要である理由と、私のスクリプトで隣接するスクリーンショットの間に1/10秒の遅延がある理由を説明する必要があります。 実際には、フィールドが点灯し、セルが爆発し始めると、ポイントの数が何度も増加します。 しかし、ミスをしすぎると、フィールドは点灯しなくなります。 したがって、エラーを最小限に抑えることは、認識時間を最適化することと同じくらい重要な部分です(そして、時間の余裕を考えると、それは一般的に唯一の重要なものです)。

隣接するスクリーンショット間の1/10秒の遅延にもかかわらず、一部のセルはまだ落下する時間がなく、その場所で決定されていません。 それらの数を減らすために、爆発テストが導入されました。 爆発が発生すると、ほぼ純粋な白色のハローが正方形に表示され(正確には#fffefc)、これは簡単に判断できます。 爆発の上のすべてのセルは、さらに苦労せずに、不定として貼り付けることができます。



最適化3.ラストクリックエリア


繰り返し発生するエラーに対する保護。その有効性は定かではありません。 実際には、ゲーム自体では、エラーが発生するとセルが灰色になり、アルゴリズムは灰色を未定義として決定します(トートロジーが判明=))。 しかし、このチェックは悪化することはありませんので、彼を生きさせてください。

一番下の行は、クリックするたびにクリックする領域を保存し、次のクリックではタッチしないことです。



まとめ



上記のすべての後、私の記録は次のようなものでした:



私は本当に200万人が欲しかったのですが、4日間の試行、数千行の実験コード(自己ログ記録とスクリーンショットの保存)、ログの注意深い喫煙、スクリーンショットとの調整はうまくいきませんでした。 =(

Githubリポジトリリンク: github.com/EvilTosha/DiamondDash



追記の代わりに。 AutoItについて一言



この言語がハブでほとんどカバーされていないことに非常に驚いた 実際、この不正を修正したいという思いから、このトピックを書くようになりました。

同時に、この言語は非常に多くのことを行うことができ、学習が驚くほど簡単です。 その存在を知ってから数時間後、私はすでにこのマクロを書くために必要なすべての知識を持っていました。

AutoItを使用すると、スクリーンショットの保存、プログラムのインストール(多くのコンピューターにインストールする必要がある場合)、どこかでの複数ログイン...のほぼすべての日常的なアクションを自動化できます。exe-shnikにコンパイルしてDLLを接続できます。

しかし、伝道者として、私は話しました。 =)



UPDビデオ作品。





ご清聴ありがとうございました。コメントをお待ちしております。



All Articles