Hearthstoneの放置botに立ち向かう

# 何が起こっているのか
皆さん、Hearthstoneというゲームを知っていますか？遊戯王とかシャドウバースみたいな感じでカードからモンスターを出して相手のライフを0にするやつです。知らなければそのままのあなたでいてください。そして、ウィンドウズに付いてくるソリティアなどの脳にやさしいゲームをしましょう。今、Hearthstoneでは放置botが流行しています。

このカードゲームは**1ターン最大1分強**の時間を使い緻密な戦略を立てながら(皮肉)カードを切っていく大人気基本無料タイトルです(皮肉)。なんと！無料プレイでも試合をしていくうちにゲーム内通貨が得られ、新しいカードをゲットできます(皮肉)！ただ、最近報酬体系が変わり、放置するメリットが増え、放置botが急増しています！

放置botは様々なゲームでありふれたものですから、Hearthstone運営側も以前から対策はしています。**「カードを出す」など意味のある行為を自分のターン中にしないと、次のターンの制限時間が約15秒に短縮されてしまう**のです。相手が完全な放置なら7ターンもあれば勝てますから、損する時間は2分もありません。おまけにタダで白星が得られます。

もちろん放置側もさらに対策を講じており、様々な変遷を経て現在流行っているのが「ヒロパbot」です。「ヒロパbot」は動作が安定しており、何よりあからさまにbotであることがわかるものです。

まず、Heaethstoneでカードを出すにはカードをドラッグ＆ドロップしなければいけませんし、そもそもカードを選ぶのに画像認識やら戦略やらが必要になります。一方で「ヒロパbot」は画面の固定された場所にあるボタンをクリックするだけでいいので安定です。(「ヒロパ」とはHearthstone特有の要素で、カードを消費せずに相手にダメージを与えたりできるものです。ドメイン知識を活用したbotですね。)

# 何で怒っているのか
次が問題です。「ヒロパbot」はあからさまに判別できます。「何が起ころうとヒロパ[^hbot]」「制限時間をフルに使う」の2点で判別できます。「あーまた放置botかよ10分くらいかかるんだよなぁコンシ(降参)しよ」というルートで勝ちを狙っているのです[^why]。**許せねえよなあ？**

せっかく運営が報酬体系まで変えて俺たちを楽しませ(皮肉)ようとしてんのによ！それを悪用して放置たぁふてぇやつだ！だいたい人様の時間取ってあわよくば試合に勝とうって魂胆が気にくわねぇ。あとbotならbotで凝ってくれよ。

相手をbotとは断言できない。めちゃくちゃ機械的に動く人間かもしれない。あるいはbotのふりして相手の降参を誘ってるのかもしれないし。でもカードゲームという「知恵比べ(皮肉)」から「根競べ」に土俵を移すならさ、もっとエモート連発するとかさ、全力で不快にしてこいよと思ってしまいます。

俺こういうの許せないんで放置にはなるべく時間をかけて勝ちます。寸止めで20ターンくらい放置し返します。1試合50分とか余裕です。..............辛い

# これが俺のHearthstoneだ[^description]
```powershell:AutoClicker.ps1
Add-Type -AssemblyName System.Windows.Forms

Class ClickTimer{
  $clicker
  $timer

  ClickTimer([int]$interval){
    $this.setClicker()
    $this.timer = New-Object System.Windows.Forms.Timer
    $this.timer.Interval = $interval * 1000
    $this.AddTick({ $this.doClick() }) # なんか勝手に動き出して止まらなくなる
  }

  AddTick($block){  # $clickTimer.AddTick({$clickTimer.doClick()})
    $this.timer.Add_Tick($block)
  }

  doClick(){
    $this.clicker::mouse_event(0x00000002, 0, 0, 0, 0); # MOUSEEVENTF_LEFTDOWN
    $this.clicker::mouse_event(0x00000004, 0, 0, 0, 0); # MOUSEEVENTF_LEFTUP
  }

  setClicker(){
    $signature=
 @' 
      [DllImport("user32.dll",CharSet=CharSet.Auto, CallingConvention=CallingConvention.StdCall)]
      public static extern void mouse_event(long dwFlags, long dx, long dy, long cgetLocBtns, long dwExtraInfo);
'@ 
    $this.clicker = Add-Type -memberDefinition $signature -name "BenBenBenBenBenBrode" -namespace Win32Functions -passThru
  }

  Start(){$this.timer.Start()}
  Stop(){$this.timer.Stop()}
}

function getForm{
    # フォーム
    $form = New-Object System.Windows.Forms.Form
    $form.Size = "2000,2000"
    $form.StartPosition = "CenterScreen"

    # 定期クリック開始ボタン
    $startBtn = New-Object System.Windows.Forms.Button
    $startBtn.Location = "100,500"
    $startBtn.Size = "0,0"
    $startBtn.Text = "On"
    $startBtn.Add_Click({$clickTimer.Start()})
    $form.Controls.Add($startBtn)

    # 停止ボタン
    $stopBtn = New-Object System.Windows.Forms.Button
    $stopBtn.Location = "100,500"
    $stopBtn.Size = "0,0"
    $stopBtn.Text = "Off"
    $stopBtn.Add_Click({$clickTimer.Stop()})
    $form.Controls.Add($stopBtn)

    return $form
}

$clickTimer = [ClickTimer]::new(2000)

(getForm).Showdialog()
```

# いかがでしたか？
- **サービス運営上の学び**
  - 勝ち以外の様々な努力(ターン数をかける、カードをいっぱい使う) に報酬を与えるというアプデは意義のあるものだった
  - 世の常として、新しい制度には悪い輩がたかってくる
  - 放置botは以前からあったリスクで、運営は回避する手段を用意できたかもしれない
  - 制度を変えるか(放置が儲からなくなるが、アプデの意義も薄れかねない)、bot規制を強化するか(技術的な進歩、開発研究コスト・運用コストがかかる)
  - Ben仕事しろ(退社済み)
  
- **自動化に取り組んでみた上での学び**
  - Windowsの自動化は今回使った「キー入力(SendInputやmouse_event)」「定期実行(Timer)」を頻繁に使いそう
  - C#などからも使えるが、標準でインストールされているPowershellの方が汎用性は良さそう
  - なんならPowershellでC#を評価実行できる(あんまいいとは思えない)
  - Powershellからフォームを作れるのはユーザフレンドリーだと思う
  - Powershellのスコープ周りの仕様とかは敵だと思う[^aboutScope]
  - {}で囲むと[スクリプトブロック](https://docs.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_script_blocks?view=powershell-7.1)ができます。これでイベントリスナーを渡したりします。ここでややつまづきました。

- **コンプライアンスやルールという観点**
  - あんまりにもbotが流行ってるからアリになったんかなと思ったけどそんなことなかった
  - デッキトラッカー(引いてないカードが把握できたりする外部ツール)について運営側の一人は「[紙とペンを使って既に代替できるツールならOK](https://twitter.com/bbrode/status/511151446038179840)」と言っていた。
  - そこらへんが曖昧な気がしてて「紙とペンで自動クリック器は作れる」とかいう屁理屈いうやつが出てきそうだなぁとおもった。

# してはいけない
「ボット: すなわち、例えばゲーム内キャラクターの自動コントロールなど、Blizzardにより明示的に認められていないコードやソフトウェアを利用してゲーム、プラットフォーム、および／またはそれらの構成要素または機能の自動コントロールをすること」は[利用規約](https://www.blizzard.com/ja-jp/legal/4bc63644-91f0-498d-a3ca-d920b36df9a1/blizzard-%E3%82%A8%E3%83%B3%E3%83%89%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E4%BD%BF%E7%94%A8%E8%A8%B1%E8%AB%BE%E5%A5%91%E7%B4%84)により禁止されています。[処罰例](https://hs-exp.jp/2015-04-21/news-bot-ban-05/)もいくつかあります。
ちなみに本記事のコードは意図的な不具合により(バグが直せなかった)まともに動きません。規約に対してどころか中身もクリーンじゃない。


[^hbot]: 実は、組み合わせると強いカードが手札にきて、さらにピンチだったらそれを使うみたいな行動をするbotも普通にあってびっくりしました。ていうかシンプルなヒロパだけbotの方が珍しい。
[^why]: 相手の根負け以外にも、「戦闘に合計50ターンかけたら報酬が得られる」などの目的があります。報酬体系が変わってbotが増えたというのはここら辺が原因です。
[^description]: Windows10/Powershellです。WindowsのAPIでフォームを作ります。Startボタンを押すとTimerが有効になって設定したクリックイベントが定期的に実行されます。[mouse_event](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-mouse_event)を使っていますが非推奨なので[SendInput](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput)を使ってくださいという気持ちがあります。

[^aboutScope]: 
 https://docs.microsoft.com/ja-jp/previous-versions/technet-magazine/hh551144(v=msdn.10)?redirectedfrom=MSDN
 ISE では、スクリプトは、グローバル スコープで実行されますが、通常のシェルのコンソールでは、スクリプトごとにスコープが設定されます。
 https://teratail.com/questions/174707
 コンソールとISEで最初に読み込まれているアセンブリが異なる