ひねりを加えたサッパーのためのボット

ほとんどの場合、これは多くの場合に発生します。職場では何もする必要がありません。次のタスクを完了する前に考える必要があります。または、お茶においしいものがまったくない場合、手は自動的にマウスに届き、サッパーの演奏を開始します。 そして今、もう一つのサペロマニアの攻撃に合い、鉱山がどこにあるのか、以前は考えていなかった考えに感動しましたが、単に開発したアルゴリズムによって、私はフィールドを突いてマウスを壊しました。 そして、私はアルゴリズムに従って、あまり創造的な努力なしに行動するので、おそらくもっと注意深く、より速く、私の代わりに遊ぶボットを書くことができます。



したがって、暇なときに自分でサッパーを演奏する代わりに、ボットに次のことを行うプログラムをC#で書くように教えることにしました。



1)ゲームのウィンドウの画像に従って、サイズ16×30(プロモードのサッパーのフィールドサイズ)のマトリックスに、画面上の配置に応じた数字を入力します。

2)テンプレートアクションを実行するアルゴリズムを介してこのマトリックスを実行します。

3)アルゴリズムの実行中に、マウスでフィールドを突き、フラグを配置してフィールドを開き、最初のアイテムに戻ります。

4)3番目の点を犠牲にしてマウスがビジーであるため、プログラムを停止するには、オペレーティングシステムで押されたキーのインターセプトを構成する必要があります(プログラムではなく、サッパーウィンドウが常にアクティブであるため)。

5)前の4つのポイントをマスターしたので、プログラムを少なくとももう少し作るために、ひねりを加えることにしました

有用/使用可能-それからスプラッシュ画面を作成します。 ユーザーが指定した時間後にキーボードとマウスが非アクティブになったときに(もちろんユーザーの要求で)サッパーでゲームを自動的に開始します。



このプログラムは、XPまでのWindowsのバージョンにあった古典的なサッパー向けに作成され、テストされました。 それから、それをMineSweeperにも転送することにしました-MineSweeper-Windows7のサッパー、これについては記事の最後で詳しく説明します。



それでは、順番に行きましょう。



1)最初の段落では、ボットの目を作成します。 次のコードは、ゲームの画像を取得するのに役立ちます。



using Emgu.CV; using Emgu.CV.Structure; ................. DllImport("user32.dll", SetLastError = true)] public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect); public struct RECT { public int Left; public int Top; public int Right; public int Bottom; } public static Bitmap GetScreenImage(Rectangle rect) { Bitmap bmp = new Bitmap(rect.Width, rect.Height, PixelFormat.Format32bppArgb); using (Graphics graphics = Graphics.FromImage(bmp)) { graphics.CopyFromScreen(rect.Left, rect.Top, 0, 0, rect.Size, CopyPixelOperation.SourceCopy); } return bmp; } .................. RECT rect; //       IntPtr handle = FindWindow(null, ""); //    GetWindowRect(handle, out rect); Rectangle gameScreenRect = new System.Drawing.Rectangle(rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top); //      Bitmap gameBmp = GetScreenImage(gameScreenRect); Image<Bgr, Byte> img = new Emgu.CV.Image<Bgr, byte>(gameBmp)
      
      





出力では、ゲームのウィンドウのカラー画像を取得しました-今ではボットは私たちと同じように見えますが、それで何をすべきかまだわかりません、愚かなことです。 さて、この図では、16 * 30のマトリックスに-1から10までの数字を入力する必要があります(-1は右キーで設定されたチェックボックス、0-セルは地雷に触れません、1-8-このセルの最小近隣の数、9-未発見のセル、10-地雷(弱体化すると表示されます)。 つまり、各ゲームの開始時に、マトリックスは9で埋められ、負けた場合、少なくとも1ダースが表示されます。 マトリックスを埋めるために、まず何らかの文字認識ライブラリを使用したかったのですが、画像オプションが12個しかないため、大砲からスズメを撃つようなものだと思いました。独自の認識モジュールを作成することにしました。 サッパーのアプリケーションをWindows 7から転送したとき、画像の数が増え、それらを区別する方法が著しく複雑になったため、決定を後悔しました。



C# EmguCVの有名なOpenCVライブラリのシェルは、画像を認識してマトリックスを塗りつぶすのに役立ちます。

使用します。 アプリケーションでの認識は次のとおりです。前のステップで取得したゲームウィンドウを含む大きな画像から、小さな画像が順番に切り取られます-セルおよび事前に準備された標準と比較されます。 より効果的な比較のために、画像を黒と白にします。特定のピクセルのグレー強度がしきい値よりも小さい場合は白に、そうでない場合は黒に、ピクセルごとの比較が続きます。



 Image<Gray, Byte> normal = image.Convert<Gray, Byte>().ThresholdBinary(new Gray(THRESHOLD), new Gray(255));
      
      





2)ボットが見えるようになったので、考えるためにそれを教える必要があります。 ゲームアルゴリズムはボットの頭脳であり、いくつかの部分で構成されています。



ゲームを渡すためのアルゴリズムで数値を使用してテーブルを使用するために、セルタイプと座標に加えて、いくつかのプロパティを設定するSaperCellクラスを作成します。



 class SaperCell { public int value; public int X; public int Y; //       public int numberOf9TypeNeighbours; // ,        public int numberOfFlags; //   ,        public float Probability; }
      
      





アルゴリズムの最初の部分は最も単純な部分で、すべてのオープンセルを(1から8までの数字で)実行し、特定のセルの未オープンの隣接セルの数が、接触する地雷の数(このセルのタイプ)と等しいかどうかを確認します。 その場合、地雷はすべての隣接する未発見のセルにあることがわかります。 アルゴリズムのこの部分の後、次のすべての状況が処理されます。















アルゴリズムの2番目の部分は、お茶においしいものがなかったときに私が開発したすべての典型的な状況をキャッチします。 そのようなテンプレートがいくつかあります。



1)3つの1、2、1-この場合、地雷はユニットの前にあります。







2)4つの1、2、2、1-この場合、地雷は2つ向かい合っています。







3)閉じたトリプル1、1、1(これは、極端なユニットから斜めに未発見のセルがないことを意味します。つまり、トリプルの反対側にある3つの未オープンのセル)-この場合、鉱山は中央ユニットの反対側にあります。







4)閉じた4つの1、1、1、1-反対側の極端なユニットの地雷。







アルゴリズムの3番目の部分(呼び出すことができる場合):目的の数の鉱山に既に触れているすべてのセルを調べ、左右のマウスボタンを同時に押します。



アルゴリズムの4番目の部分はスマートクリックで、以前のアルゴリズムで結果が得られない場合(新しいものは一切表示されない)、鉱山がまったくないセルの検索が行われます:セルAの開かれていない近隣のセットがセルBの開かれていない近隣のサブセットであり、セルBが接触しない場合セルAよりも多くの地雷がある場合、セルAの近隣ではない、セルBのすべての未発見の近隣を安全に開くことができます。







次に、同様の方法で、まさに鉱山があるセルの検索があります。







3)これでボットは地雷がどこにあり、どこに絶対にないのかを知っていますが、ペンがない、キャンディーがないと言っているので何もできません。 ボットに手を接続します。サッパーウィンドウの目的のセルをマウスで左クリックできるコードを次に示します。



 [DllImport("user32.dll")] public static extern void mouse_event(uint dwFlags, int dx, int dy, uint dwData, int dwExtraInfo); [Flags] public enum MouseEventFlags { LEFTDOWN = 0x00000002, LEFTUP = 0x00000004, MIDDLEDOWN = 0x00000020, MIDDLEUP = 0x00000040, MOVE = 0x00000001, ABSOLUTE = 0x00008000, RIGHTDOWN = 0x00000008, RIGHTUP = 0x00000010 } public void LeftClick(int y, int x) { mouse_event((int)(MouseEventFlags.MOVE | MouseEventFlags.ABSOLUTE), x * 65536 / SCREEN_WIDTH, y * 65536 / SCREEN_HEIGHT, 0, 0); mouse_event((int)(MouseEventFlags.LEFTDOWN), (lx * 65536 / SCREEN_WIDTH, y* 65536 / SCREEN_HEIGHT, 0, 0); mouse_event((int)(MouseEventFlags.LEFTDOWN), x * 65536 / SCREEN_WIDTH, y * 65536 / SCREEN_HEIGHT, 0, 0); System.Threading.Thread.Sleep(10); mouse_event((int)(MouseEventFlags.LEFTUP), x * 65536 / SCREEN_WIDTH, y* 65536 / SCREEN_HEIGHT, 0, 0); }
      
      





さて、アルゴリズムの過程で、座標(x、y)を持つセルをクリックするには、次のように書くだけで十分です。



 mouse.LeftClick(x,y).
      
      





ここで、マウスはすべての種類のクリックを含むクラスであり、ゲーム中にマウスを操作しやすくします。



4)前の段落の後、ボットは止められません-マウスを占有して青になるまでサッパーをプレイする準備ができています。 したがって、停止するタイミングを聞くことができるように、彼に耳を付ける必要があります。 マウスはビジーなので、プログラムを停止するには、キーボードを使用する必要があります。 ただし、これには問題があります。サッパーウィンドウは常にアクティブであるため、オペレーティングシステムで押されたすべてのキーをキャッチする必要があります。 インターネットを検索して、この既成のソリューションに出会いました。 2つの別々のストリームを作成します。1つはボットが動作し、2つ目は押されたキーのインターセプター、およびそれらの相互作用のメカニズムです。



 Thread hooker = new Thread(KeyboardHook); hooker.IsBackground = true; hooker.Start(); Thread saper = new Thread(SaperGame); saper.IsBackground = true; saper.Start(); EventWaitHandle wh = new AutoResetEvent(false);
      
      





KeyboardHook関数で、キーが押されたとき:



 if (isPaused == false) { isPaused = true; } else { isPaused = false; wh.Set(); }
      
      





SaperGame関数で:



 if (isPaused == false) { wh.Set(); } else { wh.WaitOne(); }
      
      





ゲーム中のsaperストリームは、isPaused変数がtrueかどうか(キーが押された場合)を常にチェックし、ストリームはスローダウンし、イベントホワイトハンドラーからの信号を待機し、キーが再度押されたときにのみ通知します。



5)ボットはうまく機能するだけでなく、非常に素直になりました。 あらゆる種類の最適化(ボットレコード5秒)の後、他に何をすべきか分からなくなりましたが、何らかの熱意を加えたかったので、本当に魅了されました。



その結果、プログラムをスプラッシュスクリーンに変換することにしました。これにより、コンピューターがアイドル状態になり、さらに優れたパフォーマンスを発揮します。 Windows XPの場合、これは非常に簡単です-拡張子を.scrに変更し、必要なすべてのファイルをC \\ WINDOWS \\ system32に配置すると、残りの標準スクリーンセーバーに表示されます。間隔を選択するだけで、コンピューターがアイドル状態のときにプログラムが起動します。 しかし、7つのアプリケーションを使用できるように、普遍的なソリューションを作成することにしました。 これを行うために、トレイにハングするウィンドウアプリケーションを作成し、自動ロードに追加する機能を備えています(Windowsスクリーンセーバーは最初から機能するため)。また、このクラスをここにねじ込んでマウスの動きを追跡します。 これで、マウスまたはキーボードを操作するとタイマーが再起動し、時間が指定した間隔を超えるとゲームが開始されます。 もちろん、これはWindowsのスクリーンセーバーではなく、キーボードとマウスのみがここで追跡されますが、それでも満足しました。



次に、Windows7からマインスイーパをプレイするためのボットのトレーニングについて少し説明します。 プログラムがWindows XPで時計のように機能したが、Windows7では2、3のバーしか機能しないと思ったとき。 しかし、実際には認識プロセスをやり直すだけで済みましたが、実際には存在しませんでしたが、以前のすべてのコードを書くのとほぼ同じ時間がかかりました。 実際、Windows7のサッパーの同じタイプのセルは、フィールドのさまざまな部分で非常に異なっています。 したがって、セルのタイプごとに、一度にいくつかの標準を準備する必要がありましたが、このような写真の数に対して同じしきい値を確立することができなかったため、これは認識エラーを排除しませんでした。 したがって、画像全体の平均グレー強度により、認識時間は2倍になったため、外出先で各セルのTHRESHOLDを計算する必要がありました。 まあ、主なものは信頼できますが、その後でも横棒が時々滑ってしまい、段階的なデバッグに問題はありませんでした。 すべてがWindows7のサッパーウィンドウ自体のスムーズな更新であることが判明したため、各スクリーンショットの前に一時停止する必要がありました。 すべてが単純に思えますが、私はこれに着いたとき、私はMineSweeperの下でプログラムを終了し始めたことを呪いました。 しかし、良いことに、少し最適化した後、プログラムは許容可能な時間内にサッパーを配置し始め、ほとんど迷わなかった。



プログラムのソースコードはgithubで入手できます。



このようにして、かなり興味深いアプリケーションを作成し、プログラミングスキルを練習し、新しいことを学びました。 読んでくれた人、そして実際にボットをダウンロードして試した人に感謝します-どうもありがとう!



そして、ボットの例を含むビデオがあります:



All Articles