【Ruby】将棋ウォーズの棋譜から自分の苦手な戦法を割り出す
みなさん、将棋ライフは楽しんでいるでしょうか。
現在自分はウォーズ2級で伸び悩んではやふた月。
漫然とさしててもしょうがないと思い、しっかりと自分の棋譜を分析することにした。
自分の戦法は嬉野流一本。効率よく勝率を上げるために相手の戦型ごとの勝率を出して、優先的に対策を立てるべき戦型を割り出すことに。
大まかに以下の流れ。
- ウォーズから棋譜リンク取得
- converterでkif形式に変換
- 相手の飛車の振り先を集計、分析
まずはウォーズから棋譜のリンクを取得する。 2019年8月までは棋譜取得ツールが使えたようだが、仕様変更で現在は利用不可だそう。 のでクローラーを自作。 注意点としてはウォーズにログイン済のユーザプロファイルが必要。 一度Chromeでウォーズにログインし、その際に利用したユーザプロファイルを指定する。
require 'selenium-webdriver' class ShogiWarsScraper def initialize(username) @username = username end def geturls(url) urllist = [] options = Selenium::WebDriver::Chrome::Options.new options.add_argument("--headless") options.add_argument("--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36") options.add_argument('--start-maximized') options.add_argument("--disable-dev-shm-usage") options.add_argument("--no-sandbox") options.add_argument("--disable-setuid-sandbox") #ウォーズに認証済のブラウザのプロファイルが必要。 options.add_argument("--user-data-dir=~/Library/Application Support/Google/Chrome/Profile 1") driver = Selenium::WebDriver.for :chrome, options: options driver.navigate.to(url) wait = Selenium::WebDriver::Wait.new(:timeout => 3000) # seconds elements = wait.until { driver.find_elements(:class, 'game_replay') } elements.each do |v| urllist << v.find_element(:xpath, ".//a")[:href] end driver.quit urllist end def makewarslist(max_page) #warsより棋譜リンクを取得。max 8 urllist = [] for i in 1..max_page do url = "https://shogiwars.heroz.jp/games/history?&locale=ja&user_id=#{@username}&page=#{i}" #s = ShogiWarsScraper.new urllist << self.geturls(url) sleep(2) end urllist.flatten!.each do |v| file = File.open("warsurllist.txt", "a") do |f| f.puts(v) end end end end
ウォーズの棋譜については、csa形式ライクな棋譜がページに埋め込まれている。 良さげな変換機がぱっと見で見つからなかったので、一旦webツール(http://swks.sakura.ne.jp/wars/kifusave/)を利用させてもらうことにする。
require 'selenium-webdriver' class ConverterScraper def doTranslate(url) begin transtool = "http://swks.sakura.ne.jp/wars/kifusave/" options = Selenium::WebDriver::Chrome::Options.new options.add_argument("--headless") options.add_argument("--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36") options.add_argument('--start-maximized') options.add_argument("--disable-dev-shm-usage") options.add_argument("--no-sandbox") options.add_argument("--disable-setuid-sandbox") options.add_argument("--user-data-dir=/Users/K/Library/Application Support/Google/Chrome/Profile 1") driver = Selenium::WebDriver.for :chrome, options: options driver.navigate.to(transtool) # URLを開く wait = Selenium::WebDriver::Wait.new(:timeout => 3000) # seconds element = wait.until { driver.find_element(:class, 'keyword2') } # XPathで指定 element.click element.send_keys(url) sendbutton = driver.find_element(:id, 'searchbtn1').click savebutton = driver.find_element(:class, 'btn1').click kif = driver.find_element(:xpath, '/html/body/pre').text driver.quit rescue end kif end end
これでようやくkifファイルができた。
詳細な分析はやねうら王を使わせてもらうので、kifを分類するために相手の戦型判断は相手の手の開始10手以内の飛車の振り先でのみ判定。
class Kifbunseki def initialize(pn, file) setPlayername(pn) setfile(file) end def setPlayername(pn) @playername = pn end def setfile(file) @filename = file end def getPlayerTeban @file = File.open(@filename, "r") array = @file.to_a sente = array[4].to_s gote = array[5].to_s if sente.include?(@playername) @teban = 0 elsif gote.include?(@playername) @teban = 1 else raise "Invalid PlayerName Error" end end def extractSashite(sentegote) #0:先手 1:後手 @file = File.open(@filename, "r") @sSashite =[] @gSashite =[] array = @file.to_a array -= array[0..6] a_teb =[] array.each do |v| teb = "" v = v.split("") v.each do |vv| teb += vv.match(/[^ -~。-゚]/).to_s.chomp end a_teb << teb end c = a_teb.count for num in 0..c do if num % 2 == 0 @sSashite << a_teb[num] else @gSashite << a_teb[num] end end if sentegote == 0 @sSashite else @gSashite end end def identifySenkei(teban, sashite) analyze_t = sashite[0..10] senkei = "" t = analyze_t.map do |v| v if v.include?("飛") end if teban == 1 #sente # case Boolean if t.include?("7二飛") then senkei = "sode" elsif t.include?("3二飛") then senkei = "sanken" elsif t.include?("4二飛") then senkei = "shiken" elsif t.include?("5二飛") then senkei = "nakabisya" elsif t.include?("2二飛") then senkei = "mukaibisya" elsif t.include?("6二飛") then senkei = "migishiken" else senkei = "ibisya" end else #gote # case t if t.include?("3八飛") then senkei = "sode" elsif t.include?("7八飛") then senkei = "sanken" elsif t.include?("6八飛") then senkei = "shiken" elsif t.include?("5八飛") then senkei = "nakabisya" elsif t.include?("8八飛") then senkei = "mukaibisya" elsif t.include?("4八飛") then senkei = "migishiken" else senkei = "ibisya" end end return senkei end def win?(sashite) if sashite.include?("投了") return 0 else return 1 end end end
実行結果はこんな感じ。
summary==========================
分析対象:119局
相手戦型:ibisya 勝利数:28 敗北数:36 勝率:43.75%
相手戦型:sode 勝利数:1 敗北数:3 勝率:25.0%
相手戦型:sanken 勝利数:11 敗北数:9 勝率:55.0%
相手戦型:shiken 勝利数:12 敗北数:5 勝率:70.59%
相手戦型:mukaibisya 勝利数:2 敗北数:4 勝率:33.33%
相手戦型:migishiken 勝利数:0 敗北数:1 勝率:0.0%
相手戦型:nakabisya 勝利数:3 敗北数:4 勝率:42.86%
相居飛車は得意だと思っていたが、んなことはなかったわ!
コマンド実行完了時にSlack通知を飛ばす
ディスクコピーやhomebrewのアップデートなど、時間が少しかかる処理が終わった際にSlackに通知を飛ばしたくなった。
macの通知センターに通知を飛ばすのだと意外と気づかなかったりする。
slacknoti.rb
require 'net/http' require 'uri' require 'json' class SlackClient def postMessage(uri, text) uri = URI.parse("#{uri}") https = Net::HTTP.new(uri.host, uri.port) https.use_ssl = true req = Net::HTTP::Post.new(uri.request_uri) req["Content-Type"] = "application/json" payload = { "text" => "#{text}" }.to_json req.body = payload res = https.request(req) end end #send slack notification slack_url = "slack_api_url" text = "command executed." sc = SlackClient.new sc.postMessage(slack_url, text)
bash_profile
function snoti(){ ruby 'scrypt_path/slacknoti.rb' }
終わったらsource ~/.bash_profileを忘れずに。
これでsnotiでslackに通知が飛ぶようになりました。
始動プログラム2019参加者に選出されたのでこれからの意気込みを残しておこう。
やったぜ!!
ということで、受付時間最後の一分でギリギリ提出が間に合った事業プランで始動プログラム2019に参加させていただくこととなった。
始動「Next Innovator」プログラムとは
「始動Next Innovator」は、「シリコンバレーと日本の架け橋プロジェクト」の一環として次世代のイノベーションの担い手を育成することを目的に2015年度に立ち上がりました。過去4年間で約500名の多種多様なイントレプレナー・アントレプレナーが活躍しています。
より速く、より遠く、より力強く、ダイナミックに世の中を変えるには、グローバルにマーケットを捉え共に進むパートナーを見つけることが必要です。 「始動Next Innovator」はシリコンバレーに代表されるスタートアップの方法論を体得し、選抜者には実際にシリコンバレーに赴いて本場のスタートアップエコシステムを体感することで、世界に通用するイノベーターを育成。 参加者は日本のイノベーションエコシステムの一員として、日本の成長戦略に寄与することが期待されます。
本プログラムは、国内プログラム(全11日間)及び選抜者を対象としたシリコンバレープログラム(全10日間)、Demo Day(2020年2月下旬予定)で構成されており、様々な講師ならびにメンターとともに実践的な活動を遂行することで、イノベーターとしてのマインドセットとスキルセットの体得を目指します。
始動プログラムHPより(https://sido2019.com/#toppage)
要するに、経産省主催、WiL運営の起業家育成プログラム。
過去記事検索してヒットする過去参加者の方々が、錚々たる経歴をお持ちの方ばかりで始まる前から萎縮しそうだ。
去年度(2018年)の倍率は
全国から345
名の応募があり、一次選抜を通過した起業家や大企業の新事業担当者など126名が6 か月の国内プログラムに参加しました。 経産省HPより(https://www.meti.go.jp/press/2018/01/20190111001/20190111001.html)
2.73倍ほどだった模様。
はっきり言って資料は調査の甘さが目立つし、個人審査動画は結膜炎でとんでもない顔してるしでこれは落ちてしまったかな、、と8割方思っていたのでホッと一安心。
参加審査通過のために詰めるべきは
応募期日前日に知ってそれから編集を始めたため特に提出資料は客観的にみてひどい出来だったはずだが、ポイントを絞って伝えるようにしたのがよかったように思う。
事業案を伝えるのはもちろんだが、それ以上に応募フォーマットに記載されている審査基準に合致する内容に絞って資料作成を行った。
審査基準の中でも特に重視されていると思われるのは過去記事や自身の経験から
その事業を行うにあたって自分が"腹落ち"しているか?だろう。
どんな優れたプランであってもそれを推進しようとしている人が途中で折れてしまっては実現することなく過去の "頑張った思い出" になってしまう。
なので、事業プランが有望だと思われる(少なくとも自分自身は有望だと信じられる)ものであることは前提として、いかに自分が粘っこく、このプランに執着しており、実現することに本気であるか?を伝えられる内容になっているとよいのではないだろうか。
おそらく審査の合否を分けたのはそこの部分だと思う。
だって結局我々が思いつく事業案はすでに誰かが考えはしたが実現していない、もしくはなんらかの壁にぶち当たりスケールできずにいるかのどちらかであると考えるべきなのだから。
それをぶち抜き切ることができる人材ですよ!というアピールは必要不可欠だ。
このプログラムに自分が求めること
さて、このプログラムに参加することになり、自分が8ヶ月間で何を成し遂げたいかをまとめておこう。
- シリコンバレー派遣者に選ばれること
- 事業に邁進するために必要と思われるメンターとのネットワークを築くこと
- 同世代のライバルを見つけること
1. はこのプログラムに参加するなら言わずもがな。
ただ、過去のシリコンバレー派遣者の肩書きをみると、企業勤務者ないしはすでに法人として活動している起業家しか見当たらない。事業化にむけて具体的に体制を整えていることはおそらく必須に近い条件になると推測される。
2. 事業に邁進するために必要と思われるメンターとのネットワークを築くこと
このプログラムに参加する最大のメリットはここだろう。HPをみてもらえればわかるが、メンターの質と幅が圧倒的に高い。さすがは経産省主催である。無論事業に必要であれば個別に何が何でもコネクションを作ろうという努力は必要だが、これだけの人たちと接し、自分の事業プランをアピールする機会が用意されている状況というのはまず、ない。
つまり、チャンスである。
3. 同世代のライバルを見つけること
自分は現在24歳だが、これくらいの年代、もっと言えばより歳が下のライバルがいると個人的に燃える。負けられないな!と思うDoerとしての強力なエンジンの一つになるだろう。
まとめ
派遣対象者は、Day11:12月21日(土)に選抜結果を発表します。
ふえぇ、、シリコンバレーいきたいよぅ、、
同窓生となる120数名のみなさま、メンターの皆様、運営の皆様、何卒よろしくお願いします。