趣味でドラフトシミュレーターを作っている過程を、思い出せる範囲でつらつら書いていく連載です。技術的な話も混ざりますが、基本は日記です。
第1回:仕事でAIを使うようになって、ふと思ったこと
正直に言うと、僕はプログラミングなんてやったことがない。学生の頃も社会人になってからも、コードを書く仕事とは無縁だった。ただここ最近、仕事で資料作成やちょっとした調べ物にAIを使う機会がどんどん増えてきていて、最初は半信半疑だったのが、いつの間にか「これがないと逆に不便」くらいの感覚になっていた。
そんな感じで仕事にAIを使い倒しているうちに、ふと「これって仕事の効率化以外にも使えるんじゃないか、自分でゲームとかも作れたりするのかな」と思った。半分は好奇心、半分は「どうせ自分には無理だろう」という斜に構えた気持ちだったと思う。何を作るか全然決めてなかったんだけど、野球が好きだったので、前から「自分だったらこの選手をこの順番で指名するのに」と考えていたドラフト会議のことを思い出した。だったら自分専用のドラフトシミュレーターを作ってみようと思い立った。
第2回:気づいたら動くものができていた
最初は半信半疑だった。コードなんて呪文にしか見えないし、自分の説明が下手すぎて途中で破綻するだろうと思っていた。でも実際にやってみると、「12球団分の選手データを用意して、指名し合う画面を作って」みたいな雑な指示でも、ちゃんとブラウザで動くものが返ってきた。画面を開いて実際にボタンを押して、選手名が表示された瞬間はちょっと鳥肌が立った。自分は一文字もコードを書いていないのに、目の前で「アプリ」が動いている。
手応えを感じて、じゃあ本格的に作り込んでいこう、と決めたのがこの頃。ただ、ここから先は思っていたよりずっと長い道のりになった。まず最初にぶつかった壁が、次に書く「選手選び」の問題だった。
第3回:選手選びが一番地味に大変だった
ドラフトシミュレーターである以上、実在の選手データが必要になる。12球団×8人、とりあえず96人分を用意することにしたんだけど、これが想像以上に時間を食った。「長打力」「確実性」「走力」「守備力」みたいなゲーム的な数値を、自分の感覚だけでゼロから査定するのはさすがに無理があったので、普段よく遊んでいる「プロ野球スピリッツA」の能力値を参考にさせてもらうことにした。あのゲームも似たような項目で選手を数値化してくれているので、いちから自分で査定するよりずっと現実的だった。
とはいえ、そのまま数値を持ってくるだけで済んだわけでもなかった。ゲームによって評価の甘辛さが違ったり、こっちのシステムだと守備位置ごとの適性の意味合いが微妙に違ったりで、結局は球団ごとに見比べながら微調整する作業が発生した。1人の選手に対して能力値を60〜90くらいのレンジでどう割り振るか、球団ごとの主力とそれ以外のバランスをどう取るか、というのを一球団ずつ地道に洗い出していく作業になった。派手さは全くない工程だけど、ここの精度がドラフト全体の説得力に直結するので、意外と一番時間をかけた部分かもしれない。
第4回:くじ引き演出にこだわりすぎた話
NPB方式のドラフトだと、1巡目で複数球団の希望が競合したときに抽選になる。あの瞬間の緊張感をゲームでも再現したくて、くじ引きの演出を作ることにした。ただ「抽選の結果、〇〇が当たりました」とテキストで出すだけだと、実際のドラフト会議の緊張感の欠片もない。
それで、封筒を選んでクリックすると開封されて、当たりなら「交渉権獲得」のスタンプが浮かび上がる、みたいなアニメーションを作り込むことにした。CSSでの見た目の動きなんて指定したこともなかったので、封筒のフタが開く角度やスタンプが出てくるタイミングを何度も微調整することになった。地味に時間がかかったけど、実際に自分の球団の封筒が当たりだったときの演出を見たら結構テンションが上がって、これは頑張ってよかったなと思えた回だった。
第5回:最初はAI採点で終わるはずだった
ドラフトで選手を集めて、スタメンを自分で組んで、最後にAIが採点してランク付けしてくれる。当初思い描いていたゴールはここまでだった。それだけで十分に遊べるミニゲームになると思っていたし、実際ランキング画面に自分のチームがそれっぽい点数で表示された時は、これでもう完成でいいんじゃないかと本気で思っていた。
試しに何度か遊んでみると、「採点は出たけど、実際に試合したらどっちが勝つんだろう」という気持ちがどうしても湧いてくる。採点だけで満足するつもりだったのに、気づけばもっと欲張りたくなっていた。
第6回:レーダーチャートがなかなか綺麗に出ない
採点結果を「長打力・確実性・機動力・守備力・先発陣・救援陣」の6項目でひと目にわかるようにしたくて、レーダーチャートを入れることにした。ところがこれがなかなかうまく描けない。強いチームを作るとほとんどの項目が100に張り付いてしまい、六角形が歪な形になって見た目が破綻する。逆に平凡なチームだと全部の点が真ん中付近に固まって、どこが強みなのか一目でわからない。
結局、能力値をそのままグラフに出すんじゃなくて、100に近づくほど伸びにくくなるような補正をかけることにした。数値をいじってはグラフを表示し直して、また微妙に歪んで見える、を何度も繰り返した。地味な作業だけど、ランキング画面の見栄えはここで決まると言ってもいいくらい重要な部分だった。
第7回:まさかの試合シミュレーションまで作ることになった
採点だけで満足するつもりが、結局「実際に試合をさせてみたい」という欲に負けた。1球ごとの打席結果、盗塁、エラー、ファインプレーまで再現する試合シミュレーション機能を作ることにしたんだけど、これが今までで一番の大工事だった。守備位置ごとの得意不得意、打順の相性、投手交代のタイミングまで考え出すと、考えることがどんどん増えていく。
せっかく試合ができるなら一発勝負より勝ち上がりがある方が盛り上がると思って、参加球団によるプレーオフのトーナメント機能もこのタイミングで追加した。最初にAI採点だけで完成のつもりだった頃からは、想像もしていなかったところまで来てしまった。
第8回:公開したら、まず自分がハマった
もともと個人的に遊ぶためだけに作っていたドラフトシミュレーターだったけど、思ったより形になってきたのでGitHub Pagesで公開してみることにした。リポジトリをパブリックにして、Pagesを有効にして、URLが発行されて……というだけの作業なんだけど、初めてやることだらけで地味に手間取った。
公開した直後、自分でスマホから触っていたら「選手詳細を見る」リンクを押しても何も起きないことに気づいて焦った。バグかと思って一通り調べたんだけど、リンク自体は正しく外部サイトへ飛ぶようになっていて、どうやら自分が使っていたプレビュー環境側が外部リンクを塞いでいただけらしいとわかった。実際にデプロイされたページで試したら普通に動いた。人騒がせな話。
第9回:指名の瞬間が地味すぎる問題
ドラフト方式には抽選ありのNPB方式と、順番に淡々と指名していくウェーバー方式があるんだけど、ウェーバーだと「今誰が指名されたか」が画面上パッと分からないことに気づいた。1巡目の抽選演出は結構作り込んだのに、2巡目以降が味気ないのはもったいない。
というわけで、ウェーバー中もAIが指名するたびに一件ずつ発表画面を挟むようにした。最初は表示時間を1.1秒にしてみたけど、テンポが悪い気がして2秒に伸ばし、結局1.5秒に落ち着いた。この手の「ちょうどいい間」の調整は数字をいじって実際に何度も見てみるしかない。全部の指名をいちいち見たくない人のために、ヘッダーからON/OFF切り替えられるようにもした。
第10回:「野球アイコンはダサい」と言われた日
指名発表のカードに⚾の絵文字を大きく置いていたんだけど、率直に「野球アイコンはダサいからやめてほしい、実際のドラフトのものを参考にしてほしい」と言われて、まあ確かにと納得した。実際のNPBドラフト会議の指名カードって、球団カラーの帯の下に白い台紙で選手名がドンと書いてあるあのシンプルなやつだ。あれを参考に作り直した。
絵文字を消して、球団カラーのバンドと白いカード本体だけの構成にしたら、思っていたよりそれっぽくなった。ついでに枠のサイズがカードごとにバラバラだったのを統一して、所属チーム名の文字が中央揃えになっていなかったのも直した。地味な調整だけど、見た目の説得力ってこういう細部の積み重ねなんだなと実感した。
第11回:AIがいつも同じ中継ぎ投手を指名する
ここから地味に長いAI調整編が始まる。ドラフト画面を見ていたら、AIの1位指名がほぼ毎回中継ぎ投手になっていることに気づいた。しかもエース級というより中堅どころの中継ぎばかり。明らかにおかしい。
原因を数値で追ってみたら、投手の評価式が野手より構造的に高く出るようになっていた。データセットの平均を比べると投手が76点前後、野手が70点前後で、単純に比較すると常に投手が有利になる。しかも中継ぎ・抑えの評価式が先発よりさらに甘い配分になっていたことも判明した。実測した差(だいたい6.3点)を投手の評価だけ差し引いて、野手と対等なスケールで比較できるように調整した。
第12回:足の速い選手ばかり評価される、の裏で起きていたこと
投手偏重が落ち着いたら、次は「走力・守備力が高い選手の方が、打力が高い選手より先に指名される」という報告が来た。現実のスカウティングだと走攻守そろった5ツール型より、長打力に秀でた一本足打法タイプの方が評価されやすいはずなので、これも直すことにした。走力・守備力より打力(長打力・確実性)を高めに評価する係数を能力ごとに設定して組み込んだ。
ところがこの調整、最初につけた係数の平均が1.0からわずかにズレていて、野手全体のスコアが知らないうちに沈んでしまい、また投手優位の状態がぶり返すという自分のミスをやらかした。気づいたきっかけはスクリーンショットで、AI3チームが揃って投手を3連続指名している場面を見せてもらったこと。数字を計算し直して、係数の平均がちゃんと1.0になるように直した。地味だけど、こういう「平均を保つ」みたいな制約は見落としやすい。
第13回:佐藤輝明と牧秀悟が全然指名されない事件
打力型の評価が上がってきたところで、今度は「佐藤輝明、レイエス、牧秀悟の評価が現実的に見て低すぎる」という話になった。人間がドラフトしたら3巡目までにいなくなりそうな選手たちが、なぜか中盤以降まで残ってしまう。
特に牧秀悟は、二塁という守備位置の中では打力がかなり抜けている選手のはずなのに、そのポジションでの希少性がまったく評価に反映されていなかった。そこで「同じポジションのライバル選手と比べてどれだけ頭一つ抜けているか」を測るロジックを追加した。最初は守備寄りの評価式で比較していたせいで、逆に守備型の選手の方が「傑出している」扱いになってしまうというねじれもあったので、打力そのもの(長打力と確実性の単純平均)で比較するように直した。これでようやく牧が坂本や丸より先に指名されるようになった。
第14回:コンボを狙うAIをやめることにした
ここまでの調整でAIの指名はだいぶ現実的になったんだけど、振り返ってみると「コンボ(長打力75以上の野手4人、みたいな編成ボーナス)を揃えにいく」ロジックがまだ結構強く効いていた。よく考えたら、AIは別にコンボを狙って野球をするわけじゃなく、単純に勝てるチームを作りたいはずだ。
というわけで、チームへの思い入れで無理に選手を集めるボーナスや、ユーザーの編成を妨害するためだけのボーナスを丸ごと削除して、残りのコンボ関連の加点も控えめな数値に減らした。素の実力・枠の必要性・ポジションでの希少性を主な判断材料にして、似たタイプの好選手が結果的に集まってコンボが揃うのはむしろ歓迎、というスタンスに落ち着いた。
第15回:全チームがAランクになる、に気づいた日
採点システムにはS〜Gの8段階のランクがあるんだけど、ふと「Gランクって実際に出るのか?」と気になって調べてみたら、まったく出ないことが判明した。それどころか、わざと一番弱い選手だけを選んで、わざと守備位置もめちゃくちゃに配置してみても、Bランク止まりだった。データ上、一番弱い選手でも打力60前後・投手も球威70前後はあるので、どう頑張って下手に組んでも平均があまり下がらない構造になっていたのが原因だった。
選手データを弱くするのは手間がかかりすぎるので、採点の計算式の方を直すことにした。「意図的に最悪の編成で組んだ場合」と「理想的な編成で組んだ場合」の実際の点数を測ってみて、その幅を0点から100点にまるごと引き伸ばすようにした。結果、最悪の編成が14点(Gランク)、理想の編成が91点(Sランク)まで届くようになって、ランダムなAI同士のドラフトでもD〜Bあたりに自然と散らばるようになった。ランクが全部同じだとやっぱり面白くないので、これは直してよかったと思う。
第16回:遊び方ページと選手名鑑を作った
ゲーム部分がだいぶ落ち着いてきたので、そろそろミニゲーム以外のコンテンツも増やそうと思って、遊び方ページと選手名鑑ページを作った。遊び方ページはドラフト方式やコンボの条件、採点のランク基準なんかをまとめただけなんだけど、初めて触る人には結構役立つはず。
選手名鑑は全96選手を球団やポジションで絞り込めるページ。データをこっちに複製すると選手を追加するたびに更新し忘れそうだったので、本体のゲーム画面から選手データを直接読み込んで表示するようにした。これで選手を増やしてもここは自動で追従してくれる。次は何を増やそうか考え中。
第17回:AIの指名ロジックにはこれからもこだわりたい
ここまで振り返ってみると、中継ぎ偏重、走力・守備型の過大評価、佐藤輝明や牧秀悟が指名されない問題、コンボ狙いのやめどき……と、結局一番手を入れ続けているのはAIの指名ロジックだった気がする。正直、他の部分よりだいぶしつこく直している自覚がある。でもここが一番「本物のドラフトっぽさ」を左右する部分だと思っていて、だからこそ気になった瞬間に放置できない。
実際、直したと思っても遊んでいるうちにまた別の偏りに気づくことの繰り返しだった。多分これで完成、というゴールはなくて、プレイする中で「これは違うだろ」と思ったらそのたびに調整していくんだと思う。地味な作業だけど、一番思い入れがある部分でもあるので、AIの指名ロジックはこれからも継続して手を入れ続けるつもりでいる。
第18回:独自ドメインを取って、球団紹介とFAQページを作った
GitHub Pagesの初期URLには自分のアカウント名がそのまま入っていて、よく見ると名前っぽい文字列が含まれているのがちょっと気になっていた。というわけで独自ドメインを取ることにして、色々悩んだ末「dreamteam9.com」に決定。DNSの設定は正直よく分かっておらず、AレコードだのCNAMEだの言われても最初は何のことか全然ピンとこなかったけど、言われた通りに数字を打ち込んで、あとは反映されるのをじりじり待つだけだった。
反映待ちの間手持ち無沙汰だったので、その時間で「球団紹介ページ」と「FAQページ」を作ることにした。球団紹介は、もともとAIの指名ロジックの中に持っていた「球団ごとのスカウト傾向」のデータをそのまま見える化しただけなんだけど、こういう裏側の数値を表に出すのは初めてで、自分で見返しても意外と面白かった。FAQの方は、検索結果に質問と回答がそのまま表示される仕組み(構造化データというらしい)があると聞いて試しに入れてみた。効果があるかはまだ分からないけど、やらない理由もないので入れておいた。ついでに、そろそろ広告も検討したいという話もしていたので、プライバシーポリシーのページも先に用意しておくことにした。
第19回:似たツールを見て、オーダーカードを作りたくなった
ドメインの反映待ちで地味に時間を溶かしていたとき、似たようなドラフト系ツールで「自分で組んだチームをカード風の画像にしてシェアできる」機能があるのを見かけた。スタメンが1番から9番まで縦に並んでいて、各選手にどこの球団の何巡目指名か、みたいなバッジが付いている見た目で、これは単純にかっこいいなと思って、うちにも欲しくなった。
採点結果のシェア画像は前からあったんだけど、あれはランクとレーダーチャートが主役で、実際に自分がどんな並びでチームを組んだかは文字がちょっと添えてあるだけだった。今回は逆に、編成そのものを主役にした縦長のカードを別で作ることにした。地味に手間だったのが、選手が「何巡目で指名されたか」をこれまで記録していなかったこと。指名が確定する場所が抽選ありなしで何箇所かに分かれていて、その全部に記録を仕込む必要があった。あと外国人選手の長い名前がバッジと重なってしまう問題があったので、名前の文字幅を測って、はみ出しそうならフォントサイズを自動で縮める処理を入れた。地味だけど、これがないと一発で見た目が崩れるので大事な部分だったと思う。
— 続く —