ビリヤードボット:作成の歴史

こんにちは、habrahabr! この記事では、ビリヤードボットを作成するプロセスの詳細を説明します。このプロセスは、人間の介入なしでゲームプールビリヤードをプレイし、ポイントを獲得して決定を下します。 この記事は、ボットやプログラミングの作成が好きな人にとって有用で興味深いものになります。



まえがき


好きなゲームやスポーツがあります。 最初のものと2番目のものが一致するのは素晴らしいことです。 スポーツやスポーツプロジェクトの趣味に加えて、コンピューターゲームも大好きです。 ライブでも仮想でも、私のお気に入りのゲームの1つはもちろん、ビリヤードです。 ビリヤード、プール、スヌーカー...好きなものは何でも-私はそれらすべてが大好きです! たとえば、スヌーカーは「非離散的」チェスであるという多くの意見を共有しています。 一連の特定のボールをポケットに打ち込むだけでは十分ではありません。また、信じられないほどの戦略的闘争もあります。 スヌーカー、ポジション、そしてプロのビリヤードプレイヤーが持っている素晴らしい装備の戦い-私はただぼろぼろで静かにしています。

この間違いなく貴族的なゲームの利点は、非常に長い間リストされています。 しかし、記事の本質を理解しましょう。 私の5年前のお気に入りのビリヤードゲームは、 今日までFacebookのPool Pooliardです。 審美的にだけでなく、技術的にも有名です。 肉眼で、クールな書面の物理エンジン、思慮深いゲームプレイ、アクションのクライアントサーバー検証、エラー処理、設計、統計システム、ストア、最後にチャットを見ることができます。 ゲームはプロによって明確に作成され、トップにあります。 それをプレイするのはとてもいいことです...そして勝つ!

長い間、考えが頭に浮かぶまで、それを演奏しました。 はい、ゲームボットを作成するのに最適です! 。 勝つことは素晴らしいことですが、ロボットで勝つことは自動的に2倍になります! ナビゲーションシステムを取得し、キューボールを引き締めた有料プレーヤーに勝ち、テクニックと美しさの面で素晴らしいヒットを見せ、顎を垂れたままにしておくのは3倍楽しい! さらに、経験値とコインの自動セット:ロボットを夜に残して、朝は最高です! さらに、観客としても、何時間もビリヤードを観るのが大好きです。

作成


作成を開始する前に、私はその時点ですでにプールビリヤードでかなりの経験を積んでいたので、鉛筆と紙を手に持ってすべてを慎重に考えました。 もちろん、すべてを予測することは不可能ですが、「スケルトン」が作成されました。 スケルトンを持ち込んでください! ボットを作成するために、C ++を使用しました。

夕方にオプションモードでコードを記述してデバッグすると、ほぼ4か月かかりました。 同意します。特に、100のパーティの1つで発生するまれなケースや例外的なケースをテストする必要がある場合、クローズドシステムで何かをデバッグすることは困難です。 私はボールを軽々しく配置して、テーブルでこの状況またはその状況をシミュレートすることはできません。

ロボットはゲームとどのように相互作用しますか?


ボットは、人の完全なシミュレーション(パターン認識と人間の入力の模倣:マウス、キーボード)を通じてブラウザと対話することに気付きました。 神に感謝します。毎回ヒットする前にキャプチャを入力する必要はありません:)だから私はそうしました:私は人間の行動をシミュレートするシステムが好きです。

つまり、画面上でゲームのある地域を見つけ、それとやり取りし、ゲームから特定の信号を受信して​​処理し、最も難しいのは正しくストライクを実行できる必要があるということです。 ボットが実行できる必要なアクションのリストを作成しました(たとえば、特定のゲームへの参加、チャットへの特定のメッセージの送信、特定のポイントへのボールの配置、ポイント数の確認、特定の角度での特定の力の行使、ピラミッドの破壊など)ボットが認識できる特定の信号のリスト(たとえば、動きが始まった、ラウンドが突然終了した、敵がenemyめた、お金がほとんどない、など)。 合計で、約40の状況が判明しました。 非同期のものとそうでないものがありました。 私はプログラムを作成し、可能な限り、それぞれについて慎重にテストしました(これについては個別に)。その後、ボットのビルディングブロックの準備が整いました。

仕事の一般的なロジックは何ですか?


もちろん、仕事の一般的なロジックはステートマシンであると決めました。 まず、ボットはゲーム用に調整され、次に利用可能な金額を計算し、次にどのベットをするかを決定します。 以下はゲームプレイ自体です。その後、ボットは再び新しいゲームを入力する最適な方法を決定します。 ロジックが単純であればあるほど、システムはより信頼性が高く予測可能になります。 バグをキャッチするのも簡単です。

仕事の詳細なロジックは何ですか?


詳細なロジックは厳しくてひげを生やしており、ブランチと考えられる状況がすべて含まれています。 それはすべて、ゲームが始まるという事実とボット自体から始まります。 ゲーム(ゲームの開始画面を含むブラウザウィンドウ)が画面上にあります。 その座標は記録され、これはその後のすべてのマウス入力および状況の認識の開始点として機能します。

さらに、ボットはどのような賭けができるかを理解するために私のお金の額を決定します。 ベットのサイズを選択した後、ボットは対応する部屋に入り、ゲームが始まります。 相手は既にこの1秒からブラウザーを閉じることができ、特定のタイムアウト後にラウンドが無効になるため、いつでも終了できます。 対戦相手はいつでもラウンドを離れることができます。この場合、勝利は私に与えられます。 これらすべての状況の認識は、ここから始まります。

さらに、サーバーは誰がゲームを開始するかをランダムに決定します:私または対戦相手。 ボットはターンの開始を待っています。 コース全体で、わずか20秒が与えられます。 この時間の間に、あなたはその動きが既に私のものであると判断し、ボール、テーブルからグラスを数え、それらを正しく認識し、正しい打撃を見つけて、それを取る時間をとる時間が必要です。

ボット自体がピラミッドを破るかどうかを判断します。この場合、いくつかの単純な素晴らしいオプションがあります。 ピラミッドを壊す必要がない場合、ボットはテーブル上のボールの座標、スコアリングできるボール(色付き、縞模様、または黒以外の色またはゲーム内の状況によって判断する黒以外)、ポイント数、その他すべてのギミックを認識します。その後、ボットは計算し、ストライキ。

ボットは、基本的なアクティビティに加えて、ゲームの不利益に加えて、相手とチャットすることも好みます。 通常、このような一時的なウィンドウは、ストライキの直後に発生します。 これは、ボットがチャットメッセージを対戦相手に書き込む場所です。 打撃の後、動きは対戦相手に行くか、プレイを続けるか、勝ったか負けました。 ボットは、これらの状況のいずれかの兆候を待っています。 敗北または勝利の場合、ボットはゲームの現在のウィンドウと結果のサマリーウィンドウを閉じ、金額の認識に再び進みます。

ステートマシンは基本的にこのようなものです。



画面上のゲームはどうですか?


C ++で画面コンテキスト全体を読みました。 画面上のゲームは非常に簡単です。 ゲームのデザインは「ゴム」ではなく(伸縮しない)固定されているため、すべての要素は常に同じ距離にあります。 そのため、ゲームの画面上で、視覚的に(グラフィック的に)変化しない特定の静的要素を見つけ、それに関連するウィンドウの開始点と終了点の座標を計算する必要があります。 いくつかのスクリーンショットを作成し、Photoshopですべてを処理したので、私はそうしました。 シンプルで信頼できるテーブル認識マーカーを作成しました。 私は、各ヒットの前にウィンドウの座標を認識するという考えを放棄することにしました。 これはストレスが多く、完璧な打撃を計算する貴重な瞬間を奪います。 そのため、ボットはゲームの位置を1回だけ(起動時に)決定します。 ボットがゲームウィンドウを見つけた後、ブラウザウィンドウを1ピクセルだけ移動することは厳密に禁止されています。ビリヤードテーブルでのゲーム中、長いストローク中に1ピクセルでも不正確になると、非常に大きなエラーが蓄積されます。 ここからはすべての打撃が不正確になります。

ゲームマーカーは、ゲームウィンドウの画像の不変の部分です。



金額はどのように決定されますか?


ここで私は幸運だった。 ブラウザにスニファーを置いてゲームの放送を聞いて、金額を確認したり、画面からキャラクターを認識したりする必要はありませんでした。 すべてが平凡です:実際には、ゲームではすべてがフラッシュで実行されるわけではありません。 コインの合計を含む要素は、マウスで簡単に強調表示されるシンプルなテキストフィールドです。 次のことを真似します。マウスをフィールドの先頭に移動し、LMBを押しながらフィールドの末尾まで右にスワイプし、LMBを放します。 その結果、ボットは単にフィールドにお金を割り当てます。 次に、それをクリップボードにコピーし、スペース、コンマ、ピリオドなどの特殊文字を削除します... ポケットの中のコインの量。

ゲームコインは、単に強調表示されたフィールドです。



ゲームの部屋を選択する方法は?


ゲームにはいくつかの部屋があり、それぞれに特定の料金(入場料)が含まれています。 2人の対戦相手が部屋に入り、それぞれ100枚のコインを置きます。 勝者は140コインの賞金を受け取ります(ご想像のとおり200枚ではありません(これはゲームから常に多くのお金を失うためにゲームデザイナーによって行われ、人々は店を使ってコインを実際のお金で購入してプレイするか、待つか、毎日の運命の輪がゲームのためにコインをもたらすまで))。 敗者は何も残しません。 ロジックは簡単です。

安い部屋では、初心者、敗者など、あらゆる瓦が永遠に垂れ下がっています。 楽しみのために、時々プロがそこに行きます。 そこで勝つ可能性は非常に高いです。

より高価な部屋では、中年のプレイヤーに会うことができます。時々、新参者は愚かにそこに行き、時にはスーパープロファイルがそこに行きます(明らかに、誰かを引き裂きます)。 そこで勝つ確率は約50%です。

高価な部屋では、エリートはたむろします。スーパープロファイル、または購入したナビゲーションシステムやヒントなどの支払い者のいずれかです。 おそらく、そこに損失が生じます。

ボットには、ベットを選択するためのいくつかのモードがあります:強制モード(ボットが常に指定した量をボットが投入する場合)と自動モード。 自動モードは非常に興味深い方法で作成されます。 さまざまなベットの部屋でボットが勝つ統計を収集し、それに基づいて非常に興味深いプログラムを作成しました。 それはいくつかの数字だけを出しますが、非常に重要な数字です。 これらは、いわゆるしきい値のコインです。 たとえば、300個のコインがある場合、ボットには最初の部屋でのみプレイする権利がありますが、しきい値の740コインを超えると、ボットには1番目と2番目の部屋で遊ぶ権利があります。 もちろん、ボットは最も高価な部屋でプレイしようとします。

したがって、このプログラムは、アルゴリズムに従ってこれらの同じしきい値を自動的に選択し、それ自体が後続の損失のリスクを予測するためにさらに使用するという点で興味深いです。 特定の部屋でプレイすることに関する何百万ものパーティーと決定を模倣し(モンテカルロを殻出し)、コインの正しい境界量を発行します。 さらに、アルゴリズムが収束していることに気付きました!

部屋に直接入るには、画面の特定の断片をクリックします。そこには、特定の賭けを入力して行うボタンがあります。

部屋を選択したら、適切なボタンをクリックします。



移動の可能性はどのように判断されますか?


ここでも、マーカーが助けになります。 ゲーム画面では、すべてがメインのものと同じように静的であり、すべての要素は常に同じ場所にあります。 これは万能薬です。 対戦相手のアバター、統計、メッセージボックス、その他のアイテムは常に左側にあります。 鉱山は右側にあります。 また、コースを決定するためのシンプルで信頼できるマーカーを作成しました。 ボットがそれを読み取るとすぐに、次のフェーズに進みます。



ボールはどのように認識されますか?


これは3幕の全編です。 この問題に関する2か月間の苦労をすべて説明するのではなく、何が起こっているかを簡単に説明します。 ボールを認識した瞬間に撮影されたテーブルの画像から、Photoshopで私が慎重に準備したきれいなテーブルの画像が差し引かれます。 さらに、コントラストの高いゾーンがあり、その上にボールのスポットがあります。 特定の特定のライトマーカーによると、重なりの許容できないことを考慮して、ボールの中心が配置されます。 次に、明るさのコントラストによって並べ替えられ、破片が画面に入るのを防ぎます(キュー、テーブルの碑文)。 その後、ボールの位置は、それらの周りの影をチェックすることによって検証されます。 最後に、画像内の必要なボールセンターの数のリストを取得しました。

ボールを違った方法で探しようとしないとすぐに! そして、影の定義の助けと、Canny borderメソッドの助けと、巧妙な分類子の助けを借りて:これらすべての方法や他の多くの方法は、私が個人的に書いた方法と比べて非常に効果的で不正確でした。 Wolfram Mathematicaを統計にも設定しましたが、これは特に明確な答えを与えませんでした。 多分悪いセット。

しかし、ボールの中心を知るだけでは十分ではありません。ストライプがどこにあるか、どこがソリッドか、どこが白(キューボール)、どこが黒(8)ボールかを判断する必要があります。 たくさんの問題を統合する必要があるため、無限のファンタジーに制限はありませんでした。 たとえば、緑色のボールは布と非常に強く結合します。縞模様の黄色のボールが停止して白いポールが厳密にカメラの方向を向くと、キューボールとほぼ同じ白色になります。 微妙なものは10億個になり、それらすべてで私は戦わなければなりませんでした。

私は解決策の海全体を調べ、精度の欠如のためにそれらを破棄し、最後に、私は自分でそれらの最も正確なものを書きました。 決定的なツリーを取得しました。各ノードには、自己記述分類器があります。 見つかったボールごとに、平均{R、G、B}、平均{H、S、B}、カラースポットを除く最も明るい色と最も暗い色の色特性を決定します。 それから私は間違いなくキューボールを最も白いボールとして見つけます。 次に、8を最も暗いボールとして決定しようとします。 結局のところ、ストライプから色を分離する必要があります。 しかし、theを開くのは簡単ではありませんでした.3次元のサンプルテーブルをコンパイルし、ネットワークをトレーニングするためにボールを1000回撮影しましたが、それでもソリッドボールとストライプボールのクラスターを明確に分離する超平面を構築することはできませんでした。 とにかく、エラーがあります。 このケースでは、小さなエラー修正をその場で行いました。 これについて-少し低い。

ここでは、ターンのさまざまな正規直交基底、定義済みのボールの色を使用したクラスター統計、およびその他の数学的な基底が使用されましたが、それは...ではなく...多くのオプションを試しました。 最終的に、ボットにはかなり正確な特性を持つ特定のボールセットがあります。常に白と黒のボールがあり、ソリッド/ストライプのセットがあります。 私を信じて、少しの仕事もされていません。

ピラミッドを破る可能性はどのように決定されますか?


ここで私はリラックスしました。ピラミッドの位置の単純な分類子を作成し(各ボールはピラミッドのポイントにあります)、私たちの動きと動きが最初であれば、それを壊します。 しかし、そこにありました! 何かがうまくいきませんでした! cなプログラマーがピラミッドのボールの初期位置にノイズを追加したことがわかりました。 賢い! そうでなければ、「理想的なゲーム」を記録することができます-勝利につながるパンチの紛れもないシリーズであり、常にそれを再現して、勝利します。 しかし、彼らはここでそのような保護を提供しました。 分類子をわずかに修正して、ピラミッドを破る瞬間を認識することに成功しました。

ピラミッドを分割する必要がある場合、ボットは、ピラミッドに厳密にプログラムされた分割のためのいくつかの豪華なオプションの1つを選択し、実装します。 上記のオプションのほとんどすべてで、1つまたは2つのボールがすぐにポケットに飛び込みます。 まあ、またはナクラニヤクはテーブル上の非常に多くのボールを散乱させますが、これは私のアルゴリズムだけです。



テーブルのポイント数はどのように決定されますか?


私のアバターと対戦相手のアバターの近くには、詰まったボールの山があります。 詰まったボールがテーブルの上よりもわずかに小さい半径で配置されたストリップです。 しかし、何をすべきか? テーブル上のボールを認識するだけでは、私がプレーするシリーズ(ストライプまたはソリッド)を理解するのに十分ではありません。 そのため、スタック内のボールの数だけでなく、ボール自体も認識する必要があります! Pffff ...「テーブルを減算する」アルゴリズムはここでは適切ではなく、サイズは同じではありません。
...
他のレコグナイザーを書くことを自分に強制する意志の信じられないほどの努力
...
2番目の信じられないほどの努力は、これらのレコグナイザーの両方を1つのユニバーサル認識機能に結合することです。 実際、私はすべてをゼロから書かなければなりませんでした。 ここにある-不十分な計画の問題。 しかし、ボールの領域とサイズを示し、出力で各ボールの座標と特性を取得します! 心の勝利!

私と対戦相手によって詰まったボールの数とタイプを決定すると、テーブルの状況は完全に決定論的になります。 これで、ボットは何をどのようにスコアリングする必要があるかがわかります。



どのボールを打つかを決める方法は?


ここでは、厳密に決定的なアルゴリズムがプログラムされています。 ゲームの開始時にピラミッドを壊せば、どんなボールでも打つことができます(とにかく、黒のボールには入りません-ピラミッドの中に隠されています)。 最初の動きが既に完了している場合、得点されたボールの数を見ます。 ない場合は、黒以外の誰でも倒すことができます。 敵だけが持っている場合、反対のタイプを取り、ヒットします。 私が持っている場合(そして私のものだけでなく)-同じタイプを破ります。 最後に、黒以外のシリーズのすべてのボールが詰まっている場合は、黒を獲得します。 それだけです! ;)



対戦相手のファウルはどのように決定されますか?


時々、相手は芝刈りをします。 この場合、ゲームのルールによりフリーキックが義務付けられています。テーブルのどこかにキューボールを置いて、ストライキをする必要があります。 そのため、2つの問題があります。相手のファウルを認識することと、攻撃を受けているボールを正しく設定することです。

最初の問題は比較的簡単に解決されます。 別のスレッドで、特定のトラッカーがこの状況を見つけようとしています。これで、相手の動きと「Fault!」マーカーがテーブルに現れました。 この場合、彼はメインストリームに、プロダクションで次の打撃を行う必要があることを通知します。

2番目の問題である定式化問題は、もう少し複雑です。 私はあなたに告白しますが、友人たち、ここで私は偽造しました。 私のシリーズのボールの1つを詰まらせる線に沿ってキューボールを配置しようとはしていません。 いや 私はボールを普遍的に配置しようとしています-障害物のないテーブルの中央にできるだけ近い場所に。 まず、テーブル上の状況を認識し、次にテーブルの中心に閉じていない最も近いポイントを計算し、そこにボールを置きます。 ステージングの進行中に、ほぼ確実に完璧なショットを計算します。



角度と衝撃力はどのように計算されますか?


だから、私の前に、テーブル上のボールの特定の構成と打つための特定のルールを使用して、一定の時間にタスクが発生し、すべての可能な有効なパンチを計算し、それらの中から最適なものを選択しました。 設定時間の可能な値:{20秒}。 可能なボールの構成:それら?..打つための可能なルール:{黒以外、ソリッドのみ、ストライプのみ、ブラックのみ}。

ボールの詰まり

ビリヤードのジャングルに入る前に、まず何が起こっているかの物理学を理解する必要があります。 ボールを獲得するには何が必要ですか?



これを行うには、ポケットの中心からボールの中心まで想像上のセグメントを描画し、ボールの境界との交点までわずかに延長します。 交点は衝撃の点です。 ボールを打つと、ポケットに飛びます。



キューボールがこのポイントにヒットするためには、キューポイントにまったく向かわず、同じ想像線に沿ってキューボールの半径によってインパクトポイントから離れたポイントに向けられなければなりません。



さて、あなたはすでに捕まえたと思います。 キューボールが必要な場所に飛ぶためには、ターゲットポイントからキューボールの中心を通る線を作成し、キューの円の遠端と交差させる必要があります。



したがって、ここから一般的なルールが生まれます。任意のボールを特定のポイントAに向けるには、ポイントAとこのボールの中心を想像上の線で接続し、ボールの円の遠い境界との交点まで延長する必要があります。 これがボールを打つ場所です。 ボットのゲーム戦略を人々と同じにすることを決めました。ボットは攻撃中のボールを1つ選択し、それを打ちます。 それだけです システムが単純であればあるほど、デバッグの信頼性が高まり、簡単になります。

ポケット

ポケットは異なります。 そして、それらに入ることは異なる法律の対象となります。 たとえば、さまざまな方法でコーナーポケットに入ることができます。



しかし、ポケットごとに普遍的なポイントを見つけることができました。ボールを転がすと、どこからでもポケットに入ることができます 。



これらのポイントを狙うためだけにボットをプログラムしました。 長い側面のポケットがそれ自体に課している唯一の制限:それらのボールの充填は70度の開きの場合にのみ可能です。 また、コードに埋め込まれています。

模擬スペース

私はリバウンドを放棄することにしました。 いや、もちろん、そういう意味ではないので、怖がらないでください。)リバウンドを完全に忘れるために、そのようなシミュレートされた空間を構築すること。 それらはゲーム内にあり、残りますが、スペースにはありません。 私は夕方にのみこの問題の解決に取り組みました。 私は気付きました:半径Rのボールと厚さ0の側面との衝突は、半径0の質点と厚さRの側面との衝突と同じです:



これは、物理学の計算を大幅に簡素化できることを意味します。軌跡に関しては、ボールではなく、質点で作業します。 さらに:このジャンルの法則によれば、ボード上のボールの入射角は反射角に等しくなります。 光線と鏡面の場合のように。 あなたは尋ねる:それは何と関係があるのか​​? 答えます。 マテリアルポイントの側面との衝突(側面に跳ね返る)は、マテリアルポイントが側面を通り、この側面からミラーリングされた空間に移動することと同じです。 つまり、ボールがその動きの方向に前方を「見る」ことができ、側面が鏡である場合、これは次のようになります。



このような「ミラーボード」スペースでは、もはやリコケットについて考える必要はありません。 彼らは自分で流れます。 さらに、重要なポイントの動きの規則をつなぐと、一般的に豪華であることがわかります。

シミュレートされた空間では、すべてのストロークは直線であり、リバウンドはありません。 しかし、彼らはすでにテーブルにいます。



最大衝撃力とキューボール経路

すばらしい、たくさんの作業が完了しました! しかし、私たちがまったく触れなかった非常に重要な質問がまだあります:キューボールが行くことができる最大の方法は何ですか? その中のエネルギーは何ですか? 毎回全力でボールを打ちたくありません。 これにより、キューボールが誤ってポケットに転がる危険があります。 衝撃力を非常に正確かつ正確に計算したいです。これを行うには、キューボールからのキューの長さで最小の影響が発生するピクセルとその長さ、およびピクセルでキューのキューからのキューの長さで最大の影響が発生する時間とその時間を知る必要があります。また、ボールが側面に当たったときにボールからどのようなエネルギーが得られるかを知る必要があります。今、これらすべてを見つけることは残っています。最小限のバンプで、すべてが散発的です:それはいつでも測定されます。最大値では、はるかに複雑です。テーブルの対角線は平均的なヒットでも十分ではないため、明らかにそのパスの長さを測定することはできません。

これは、水平と垂直の2つの場合に思い付きました。適切な測定を行い、方程式を一致させた後、ボールの最大軌道の全長とボード上の減衰エネルギー係数を取得します。だから私はやった。相手がファウルを受け取ってキューボールをサイドの端にそっと置くと、キューボールの水平方向と垂直方向の動きが妨げられないようになるまで待ちました。そして、彼は全力で二度攻撃しました。一回は水平に、もう一回は垂直に。彼は指標を取りました。垂直の衝撃で、キューボールは長辺で8回跳ね返り、他の方法で残りのエネルギーに達したので、彼は停止しました。水平方向の影響で、リバウンドの数はテールで5でした。ここから、テーブルの寸法を知っているので、方程式を取得することは難しくありません。最大経路の全長を取得する方程式を数値的に解き、およびブランキング比。

xをピクセルの側面と衝突しないボールの転がり経路の最大長とし、p <1を側面に直角に衝突したときのエネルギー損失係数とします。テーブルの幅wと高さhはピクセル単位で既知です。水平の場合を考えてみましょう:


最後の方程式をゼロに等しくします。同様に、垂直の場合の方程式を取得します。この2つの方程式の非線形システムを2つの未知数で数値的に解くと、目的のxとpが得られました。

チェーンルール

もちろん、十分なエネルギーがある場合でも、影響の深さは任意です。しかし、エラーの蓄積により、4番で停止することにしました。つまり、関連するオブジェクト(ポケットを含む)の最大数は4です。キューボールはボールAにヒットし、ボールBにヒットし、ポケットに落ちます。そして、(!)精度が非常に失われるため、このような攻撃にいくつかの制限を導入しました。側面に跳弾がなく、ボールとポケットの間の距離がほぼ等しく、攻撃の角度が弾性衝撃に近いはずです。しかし、そのような空想は私にはまだ輝いていません。



ロールバックキューボール

魅惑的に傑作が当たった後でもキューボールがロールバックしてポケットに落ちた場合、すべての苦しみと計算は無駄になります。リバウンドしても。したがって、私はそのような打撃を直ちに考慮から除外し、その後、ボットは失敗する可能性があります。キューボールのロールバックを計算するには、キューボールをターゲットボールに当てた後のロールバックの強さと方向を計算します。すべてが簡単だと思いました:弾性/非弾性衝撃後の残りのエネルギー、キューボールがどのくらいの方向とどの方向にロールバックするか、相対攻撃角のコサインを使用した方向を計算し、軌道がポケットを通過するかどうかを確認します。しかし、いや、ここにも落とし穴が待っていました。一連のテストを実施した後、実際のロールバックはシミュレートされたロールバックとは大幅に異なることに気付きました。

ゲームのナビゲーションシステム(黄色)と計算の常識の両方が、キューボールが緑の線に沿ってロールバックすることを示しています。しかし、ヒットした後、彼は赤に転がります:



これはtrapです。ゲームでは、ボールのねじれの慣性とその方向も考慮されることがわかります。ほぼ4晩のロールバック軌道を計算する非線形関数を記述してテストする必要がありました。そのようなささいなことは腹立たしいが、最終的にこれらはささいなことではないことが判明した。試行錯誤だけで、精度の低い関数を決定することができました。しかし、私はやった!これで、ロールバックの結果として、キューボールをポケットに導く打撃を安全に投げることができます。

より良いヒット機能

テーブルの構成を分析した結果、多数のヒットのコレクションを受け取りました。しかし、それらのベストを選択する方法は?脳を含めて、各影響パラメータを評価する必要があります。論理的に、私たちは何を望みますか?キューボールからターゲットボールへのリバウンドの数を最小限に抑えることが望まれます。リバウンドごとに小さなエラーが発生します。キューボールからターゲットボールまでの距離は、正確に照準を合わせることができるように、可能な限り小さくする必要がありますが、それほど小さくはなりません。奇妙なことに、これらは2つの異なる条件です。考えてみてください。 次。 ターゲットボールからそれが落ちるべきポケットまでの距離は、可能な限り小さくする必要があります。 もちろん。そして、このパスには最小限のリバウンド、できればゼロを含める必要があります。ストロークは中程度の強さであり、強すぎず(トーションエラーとゲームの物理エンジン)、弱すぎない(既にボットのエラー)ことが望ましいです。ターゲットボールへのキューボールの打撃は、可能な限り「直接」かつ弾力性のあるものでなければなりません。ここから最小ロールバック長が自動的に続きます。長時間のスキューバダイビングと落とし穴の分析から、追加の条件(それほど重要ではないがまだ重要)が明らかになりました。ターゲットボールは、45度にできるだけ近い角度でコーナーポケットに、そして長い側面のポケットに-斜めに飛ぶ必要があります35度以下。追加の条件がいくつかありますが、それらについては黙っています。









しかし、本質的に、これらすべてのことは、互いに完全に匹敵するものではありません。たとえば、迎え角(衝撃弾性)が他の迎え角(たとえば、0度は50度よりも良い)となんらかの形で匹敵する場合、衝撃力などの特性は...?軌道の長さが異なると、距離が異なり、切断が異なるため、異なる力が必要になります。衝撃力を相対的な指標に変換する場合、切断はどうしますか?確かに、さまざまなストロークに対して、彼女は彼女自身です!また、リバウンドの数(0から10などの数値)や、ボールが長辺のポケットに入る角度など、比類のないカテゴリーを比較する方法は?もっと重要なことは何ですか?

Pffff ...私の前で別の重大な問題が発生しました。これらすべての特性をある程度一貫させる必要がありました。たとえば、0から1までの数値範囲で、0は非常に悪いか不可能であり、1は完全に良好です。これは作業の次の大きなステップでした。廊下で提示されたすべての特性を反映する(および適切に反映する)関数の検索と選択[0、1]。そして、これは大きな仕事です。リバウンドの数を取ります。はい、0〜10の範囲で指定できますが、平均で1〜2回のリバウンドが標準と見なされます。 8-これは例外的な状況であり、ゲームでは非常にまれです。したがって、範囲[0、10]をセグメント[0、1]に直線的に変換することは愚かで不当です。私はすべての特性の「逆」分布関数を探して、フィットし、それらを掘り出し、指から吸い出す必要がありました。その結果、すべてがかなり許容範囲内になりましたが、77ポットが残っていました。

さて、次のステップは、これらの特性の重みを見つけることです。重量はパフォーマンスの重要性です。そして、ここで再びジレンマ。何よりも重要なのは何ですか?最初は、すべての特性に同じ重みを付けようとしましたが、ボットは嫌になりました。その後、別の作業段階が始まりました-長く退屈な作業です。必要な係数の適合と検索。この段階でも数週間かかりましたが、幸運でした。私はすでにすべての状況を非常に感じているので、適応法は非常に良い選択に達しました。これまでのところ、私はより良いものを見つけていません;)



これで、定義されるメイン関数の準備が整いました!私は、それが重み係数とフィッティング関数の積の合計で構成されていることを思い出します。その結果、ストロークごとに、0〜1の「正常な」ヒットの数が得られます(正規化)。さて、それは技術の問題です。ベストストライクとヒットを選択します。

ボール切断補正

数か月のテストの間に、私は興味深い不具合を発見しました。エンジンの不正確さのため、または私の側柱のいくつかのために、側の近くに立っているショックボールは常に打撃の一般的な法則に従うわけではありません。必見。起こるのは次のとおりです。ボードに厳密に平行に飛んでポケットに落ちるのではなく、長辺の近くに立っているショックボールを完全にポケットに向ける状況では、何らかの理由で側面に当たって跳ね返り、間違った方向に飛んでいます。私はアンダーカットしているように感じます。切断を理想に近づけるための特別な修正機能を導入しました。天井から再び取られており、完全にフィットしています。しかし、それは機能します。



20秒で適応検索

すべてについては、20秒しかないので、ボットにはヒットを見つける時間がありません。したがって、優先度の高いストリームで、いわゆる緊急ストライキを探しています。これらは、ボールを打つことではなく、少なくともボールに触れることを目的とした打撃です。検索から15秒以内にボットがブロックする打撃を1つも見つけなかった場合、ファットを回避するためにタッチで緊急ヒットを即座に実装します。そして、私は影響の適応性を模索しました。まず、テーブル自体がシミュレートされた(同じミラー)スペースとして使用されます。打撃が見つからなかった場合、シミュレートされたスペースは水平および垂直に3倍に増加します。ボールとポケットに沿った垂直反射はテーブルの左右に「アタッチ」され、水平反射は上下にあります。ここで打撃が見つからない場合-シミュレートされたスペースの別の完了があります。そして、15秒が経過するか、テーブルの「ブロック」の数がリバウンドの最大許容数を超えないようになります。ボールに乗るだけではエネルギーが足りません。

緑色のボールを獲得する必要があります。



狙い撃ち

彼はすべてを達成したように思えます。照準点と攻撃力の両方があります。それを実装するためだけに残っています。しかし、ここでも、プログラミングの不正確さ、またはプールビリヤードの仲間のプログラマーからの隠された保護のどちらかで、困難に遭遇しました。しかし、まず最初に。照準点とキューボールの間には、衝撃の角度があります。27度だとしましょう:



ボットは、マウスをそこに置いて、衝撃線が正確に27度になるように、テーブル上でそのようなピクセルを見つける必要があります。ピクセルは特別な検索領域で離散的ですが連続的ではないため、指定された角度に最適なピクセルの検索が行われます。



まあ、それがすべてのようです!マウスを目的のピクセルに置き、指定されたピクセル数だけキューボールに合わせて放します。しかし、違います。間違った風船が飛ぶ!なんて呪いだ!?場合によっては、ボールは完全にヒットしますが、一部は何らかの形で疑わしく、時にはまったくそうではありません!このフローティングエラーを見つけるのに4日かかりました。コード全体をシャベルしました。問題は私の側にまったくないことが判明しました。物理的なマウス(画面)はゲーミングマウスと一致しませんでした。フラッシュで行われます。これはバグまたは機能のいずれかですが、座標は水平方向に4ピクセル、垂直方向に1ピクセル異なります。ある奇跡によって、私は偶然これに気づきました。適切な修正を導入すると、素晴らしい結果が得られました。

ボールの誤認識の修正

はい、ボール認識は私にとって完璧ではありません。 ボットがストライプボールをソリッドとして誤って分類する場合と、その逆の場合があります。 この状況を修正する最も簡単な方法は、誤って認識されたボールがテーブルの上にある場合です。 最も難しいのは、スタックにボールが詰まっている場合です。
ボットがテーブル上のボールを誤って認識したとします。 明確にするために、ボットはボールが縞模様になっていると考えてみましょう。 ボットのスコアが縞模様になります。 たまたま、ヒットするように選ばれたのはこのボールでした。 次に何が起こりますか? ボットがボールを打たない場合、フォールトと失敗したターンを受け取ります。 そして、これは良くありません。 私はこれをやった:ボールの近くのゲームインターフェースでボールを狙った瞬間に(バウンスがない場合)緑の矢印が点灯したら、打つことができます。



赤の場合、ボットはすぐに反対のタイプに分類し直し(現在はボットに対して堅固です)、見つかったすべてのヒットを破棄し、この特定のボールに触れずに次のベストヒットを取得します。 ボットは別のボールを打とうとしています。 また、誤って分類されている場合、物語は繰り返されます。 そして、ボットがタイムアウトになるか、...
...ロジックはずっと複雑です。 結局のところ、そのような状況も可能です:パーティーの始まり。 ボットは最初で唯一のボールを獲得します。 実際、ボットはそれが縞模様で詰まっていると考えている間、それはしっかりしています。 これは、セクションの冒頭で説明した2番目の状況にすぎません。

次に何が起こりますか? ボットは、実際には-固体の場合に、縞模様のスコアリングが必要であることを絶対に確信します。 ボットは最初の縞模様のボール-赤い矢印、2番目の縞模様のボール-再び赤い矢印、3番目-同じことを目指します。 実際、このプロセスはタイムアウトするまで続きません。 この方法でプログラムしました。テーブルの半分以上のボールが赤い矢印を示している場合、詰まったボールのスタック内のボールのタイプが単に誤って認識されていることを意味します。 その後、ボットは詰まるボールのタイプを不規則なストライプから通常のソリッドボールにすぐに再分類し、すでにソリッドパンチを探して、急なピークから自身を削除します。 そしてそれはすべてです-時間が残っている場合。

チャットはどうですか?


戦争では、あらゆる手段が優れています。 したがって、そのような資金を求めることは可能であり、必要です。 たとえば、敵への影響の手段。 このゲームは、基本的に互いに相手を完全に閉じます。 ゲームテーブルを介してのみ相互作用します。 ゲームテーブルと...チャット! はい、ゲームにはチャットがあります。 そして、これは口頭コミュニケーションの最大の利用可能なチャネルです。 そしてそれは露出を意味します。 敵とチャットすることで何ができますか? もちろん、降伏するように彼を呼ぶことはうまくいきませんが、冷静に彼の注意をそらすことができて、彼の神経を軽くたたくことさえできます! そうそうメソッド!

放送テキストの一貫性を気にしないことにしました。 結局のところ、チャットは二次的なタスクです。 短いメッセージにしましょう。

別のケースとして、挨拶用のいくつかのオプションをプログラムしました。 それらは、ゲームの開始時にボットによって話され、a)対戦相手をチャット機能に引き付け、b)すぐに彼をセットアップして、彼と話します。 そして、良い繁殖は私に答えさせます、私はあなたに言います。 答えは、会話やメッセージへの反応というゲーム機能から気をそらすように脳に接続することです。 プレイヤーの大多数が挨拶で挨拶に応答することに注意してください。 いいね! 人が彼らと遊んでいると彼らに考えさせます(彼らはここでロボットを考えることさえできません)!

それでは、ソーセージショップです! ここでは、ジョークとジョーク、ジョークとティーザー、一貫性のないナンセンス、やる気を起こさせるフレーズが登場します... 10人のうち、約2人がボットと真剣にコミュニケーションを取ります。 多くのわいせつに答えます。 激怒。 ボットはそれらを強制します。 これはすごい!

チャット機能自体は、ボットがヒットし、物理がトリガーされるのを待った後に実装されます。 「話す」には絶好の場所。 選択したフレーズをクリップボードに置き、画面上の風船を通して相手にメッセージを送信します。 ここでのトリックはゼロです。

それだけです


これで、ボットに関する長くて骨の折れる作業が完了しました。 労働の成果を享受するだけです。

いくつかのビデオ


非常に古いバージョンのボットのプレゼンテーションビデオを投稿しています。



自動モードでの数時間にわたるゲームの新しいバージョンのビデオ:



ご清聴ありがとうございました!

Source: https://habr.com/ru/post/J251101/


All Articles