2020年9月14日
ゲーム作りで学ぶVue.js (8) [番外編] Seleniumで最高スコアを獲得

最高スコアを取れる確率

前章でゲーム作りは完成しました。
ここでは番外編としてこのゲームで最高スコアを獲得しようと試みた結果を書いていきます。

将棋ポーカーのリンクはこちら
https://shogipoker.net

ゲームの性質からして最高スコアは30点になります。
獲得駒が角、金、銀、桂、香、歩の状態です。
short max edit

こうなるにはComが飛の時には歩で負けて、それ以外はComより一つ強い駒で勝てれば達成できます。

この確率がどれくらいか計算してみます。

こちらはこの順番で駒を選択するとします。
歩、香、桂、銀、金、角、飛

するとComはこの順番で選択される必要があります。
飛、歩、香、桂、銀、金、角

この確率を計算すると5040分の1になります。
1/7 * 1/6 * 1/5 * 1/4 * 1/3 * 1/2 = 1/5040

これを自力で達成するのは日が暮れるので、ブラウザ操作を自動で操作できるSeleniumを使いたいと思います。

Seleniumでゲーム操作を自動化

Seleniumは主にテストで使われるもので、所定のブラウザ操作をコードで実現することができます。

様々な言語で使用することができて非常に便利ですし、ブラウザが勝手に動いてくれるのが何となく楽しいです。

ここではRubyとGoで実装した例を紹介します。
環境構築はたくさん情報がありますので省略します。

Rubyの実装例

require 'selenium-webdriver'

url = 'https://shogipoker.net'
driver = Selenium::WebDriver.for :chrome
driver.navigate.to url

# 表示されるまで待機
wait = Selenium::WebDriver::Wait.new(:timeout => 10) 
wait.until {driver.find_element(:class, 'phase').displayed?}

# 操作する要素
phase = driver.find_element(:class, 'phase')
player_row = driver.find_elements(:css, '.player-row td')
com_koma = driver.find_element(:class, 'com-select')
reset_btn = driver.find_element(:class, 'reset-btn')
player_point = driver.find_element(:class, 'player-point')

max_point = 30  # 獲得ポイントの最大値
point = 0       # Playerの獲得ポイント
try_cnt = 0     # 試行回数
comkoma_expected = ['飛', '歩', '香', '桂', '銀', '金', '角'] # Comに選択して欲しい駒順

# 最高ポイントを獲得するまで繰り返す
while point < max_point
  try_cnt += 1

  # リセットをクリック
  reset_btn.click

  0.upto 6 do |n|
    # Playerは左から順に選択(歩、香、桂、銀、金、 角、飛)
    player_row[n].click

    # ○手目をクリック
    phase.click

    # Comが期待する駒でなければ終了
    if comkoma_expected[n] != com_koma.text.strip
      break
    end
  end

  # ポイントを格納
  point = player_point.text.to_i

  # 200回ごとに実行回数を出力
  puts try_cnt if try_cnt % 200 == 0
end

# スクリーンショットを保存
driver.save_screenshot('./max.png')

puts try_cnt.to_s + '回目で成功'

sleep 5

driver.quit

Goの実装例

package main

import (
    "fmt"
    "os"
    "time"
    "strconv"
    "github.com/sclevine/agouti"
)

func main() {
    driver := agouti.ChromeDriver()
    defer driver.Stop()

    if err := driver.Start(); err != nil {
        fmt.Fprintf(os.Stderr, "%s\n", err)
        return
    }

    page, err := driver.NewPage()
    if err != nil {
        fmt.Fprintf(os.Stderr, "%s\n", err)
        return
    }

	url := "https://shogipoker.net"
    page.Navigate(url)

	time.Sleep(time.Second * 1)

	// 操作する要素
	phase := page.FindByClass("phase")
	player_row := page.FindByClass("player-row").All("td")
	com_koma := page.FindByClass("com-select")
	reset_btn := page.FindByClass("reset-btn")
	player_point := page.FindByClass("player-point")

	max_point := 30  // 獲得ポイントの最大値
	point := 0       // Playerの獲得ポイント
	try_cnt := 0     // 試行回数
	comkoma_expected := [7]string{"飛", "歩", "香", "桂", "銀", "金", "角"} // Comに選択して欲しい駒順

	// 最高ポイントを獲得するまで繰り返す
	for point < max_point { 
		try_cnt++

		// リセットをクリック
		reset_btn.Click()

		for n := 0; n < 7; n++ {
			// Playerは左から順に選択(歩、香、桂、銀、金、角、飛)
			player_row.At(n).Click()

			// ○手目をクリック
			phase.Click()

			// Comが期待する駒でなければ終了
			com_koma_text, _ := com_koma.Text()
			if comkoma_expected[n] != com_koma_text {
				break
			}
		}

		// ポイントを格納
		point_str, _ := player_point.Text()
		point_int, _ := strconv.Atoi(point_str)
		point = point_int

		if try_cnt % 200 == 0 {
			fmt.Println(try_cnt, "回経過")
		}
	}

	fmt.Println(try_cnt, "回目で成功")

	page.Screenshot("max.png")

	time.Sleep(time.Second * 5)
}

実行動画はこちらです。

連載記事