GEN MUTO'S HOMEPAGE > Cell_雀公式サイト > 開発秘話コラム
コラム Cell_雀開発秘話 (十年越しのリベンジ!)
 
■ 若気の至り
 

 「ああ…終わった(まだバグだらけだけど…)」
 Cell_雀、最後の機能を実装し終えたとき、脳裏に十年前の風景が走馬灯のように甦ってきました。思えば私が最初に麻雀ゲームの製作を試みたのは十年前、某製造メーカーで社内SEの卵として憶えたてのVBA(当時はEXCEL5でした)で業務補助アプリケーションの開発をしていたときでした。
 
 「よーし! じゃあ次は麻雀だ!」
 少し前にEXCELでカードゲームのブラックジャックを完成させた私は、その勢いで麻雀ゲームの製作にチャレンジしようとしました。思えば若気の至り、無知蒙昧です。ちょっとばかし齧った程度のVBAとエクセルの知識でいったい何をしようと思っていたのでしょうか…。
 「あれれ…むずかしいなあ…」
 夕日の射す事務所の中で私は首をかしげていました。まず最初に上がり判定の部分を作ろうとしたのですが、何度やってもうまくいきません。それもそのはず、当時の私は何とワークシート関数で上がり判定ルーチンをコツコツ作ろうとしていたのですから! そのときはこう考えていました。
 「四面子・一雀頭と上がりの形は決まっているのだから、ワークシートに条件式さえ上手く作ればマトリクス判定で真偽が分かるはず、それをVBAで上がりと認識させよう!」
 フム…考え方はこれでいいのですが、ご存知のように麻雀の上がり判定はとんでもなく複雑です。(面前清一色の待ち牌を想像していただけば分かると思います)
 面子が雀頭になったり他の面子の一部と入れ替わったりと、多種多様です。これをワークシート関数だけで処理するとしたら、一体どれだけの条件分岐をネストさせればよいのやら…(てゆーか無理です)。さらに国士無双・七対子などは、どう判定させる気だったのでしょう…(てゆーか不可能です)。
 
 小一時間ほど悪戦苦闘が続いた後、ワークシート関数では無理! とやっと判断した私は、今度はVBAを使ってプログラムから判定させようとしました。しかし悲しいかな当時の実力では、なにをどう判定させたらよいのか皆目見当もつきません。何の値を変数にとって、どんなルーチンで処理させたらよいのか…。むなしく時間だけが過ぎていきました…。
 「…帰るか…(腹へったし)」
 こうして私の麻雀ゲーム・ファーストチャレンジは見事なまでの完敗に終わりました。これ以来、私の中では麻雀ゲームは「作ることが不可能なプログラム」として強烈にインプットされ、永久に封印されたはずでした。
 
 時は流れ、それから十年…私は某製造メーカーを退職し、名古屋にある某ソフトハウス(現在の職場)にSEとして転職していました。今回の製作のきっかけとなったのは、その職場でのひょんな出来事がきっかけです。
 ある日の朝、報告書を提出しようと上司の席を訪れたときのことでした。上司があわててPCの画面を隠そうとしているではないですか! しかもモニターには、はっきりと麻雀ゲームの画面が…ジャーン!
 「あー麻雀なんかやってるー」
 そのときは、ただそう思っただけでしたが、不思議と時間が経ってもそのことが脳裏を離れません。
…たんに上司の姿が滑稽だっただけかもしれませんが…(笑)。
 「たしかに麻雀ゲームほど、仕事サボってるのがバレバレのゲームってないよなあ…」
 そのとき私の頭の中にピーン! と次の図式が浮かびました。
 
 「仕事中に麻雀をやりたい人は、きっといっぱいいるはず…」>「仕事中にやっててもバレない麻雀ゲームがあればいい」>「きっとニーズがあるぞ!」>「じゃあ作ろう!」 (マジです)
 さて、そうと決まればもう開発言語はEXCELしかありません! 何しろインターフェイスとしては日本一有名なお仕事アプリですから。
 「よーし! つくるぞ! つっくるぞー!」意気揚々とする私の脳裏に十年前のあの想い出がよみがえってきました。
 「ということは…まず普通の麻雀ゲームを作らなくちゃならないんだよな…」
 できるだろうか? 疑問が心の中に湧きあがります。
 「大丈夫、できるさ! あの頃とは開発スキルが全然違うじゃないか!」そう自分に言い聞かせた私でした。

 
 
 

当時EXCEL5で作ったブラックジャックです
なんとEXCEL2000で正常動作しました!


 
 

初期のCell_雀、開発画面です
結構、この画面デザイン気に入ってます
■ 十年ぶりの再チャレンジ
 
 そして、その日のうちに早速、麻雀ゲーム再チャレンジを開始しました。結論から言います。やっぱり上がり判定は難しかった…(ダメじゃん! トホホ)。さすがにワークシート関数は使おうとしませんでしたが、完璧な上がり判定ルーチンは難しいです。
 「何か、いい方法があるはず。どんな上がり形でも完璧に判定してしまうロジックがきっとあるはず!」
 そう思いながら、それからしばらくはそのことがずっと頭を離れませんでした。
 
 そんなある日、フリーのSEである前澤さんが会社の事務所を訪れました。
 「前澤さん、麻雀の上がり判定ってどうやってルーチン組めばいいと思う?」私は、即行で尋ねました。
 「麻雀? 俺やったこと無いもん」前澤さんの回答です。
 「マジっすか…!? (ダメダメじゃん!)」
 それでもメゲずに五分ほどで簡単なルール説明をすると、
 「ふーん…それなら意外と簡単に実装できるんじゃないかな?」
 「でも、こんな場合だってあるんですよ!」
 私はワザと判定が難しいパターンをホワイトボードに書いて説明しました。(てゆーか、おまいら仕事しろ! って感じですね)
 「あーそっかー難しいねー」
 本当はもっと難しいカンバン方式の受発注システムの打ち合わせに来てるのに…前澤さん! すんません!
 「あーでも…それなら多分、出来そうだよ」しばし黙考した後、前澤さんが言いました。
 「えーどうやるんですか?」
 「自分の中に繰り返し見にいくループ処理を作ればいいんだよ」
 「ほう」私は思わず身を乗り出しました。
 「再帰ルーチンとでもいうのかな、とにかくある条件になるまでは繰り返し見にいく」
 「パラメータはどうするんですか?」
 「配列にもたせて、面子か雀頭になったら値ごと消していけばいんだよ」
 「おおーー!!」
 頭の中のモヤモヤが一気に晴れていくのを私は感じてました。配列の値を面子か雀頭ができたときにだけ消していけば、全ての値が消えているとき=「上がり」ということになります。
 「ありがとうございます! いけそうです!」
 「いえいえ…ところで…」その後、何事もなかったかのように打ち合わせを進めた前澤氏。わずか五分程度の説明でルールを理解し、上がり判定のヒントをくれた前澤氏! 何を隠そうこの人はその昔、光栄で「信長の野望 全国版」のHEX戦闘画面のコンピュータ側思考ルーチンを開発した伝説のSEです。(前澤さん、このときは本当にありがとうございました)
 「よーしいけるぞ! 上がり判定さえクリアすれば開発の半分は終わりじゃーん!」そう思った私に、これから数々の苦行難行が降りかかってくるとは…このときはまだ知る由もありませんでした。
 
 その日、家に帰った私は早速、前澤氏のヒントを元に再帰ルーチンを作成しました。
 「おーー! こりはすごい!!」
 作られた再帰ルーチンは完璧に「上がり」を判定していきます。ワザと判定が難しい九連宝灯などを試しても、いとも簡単に上がり判定を行いました。
 目の前がパッと明るくなり、「もうでけた! もう大丈夫!」私は小躍りして喜びました。
 しかしここからが一ヶ月半にも及ぶ苦闘の日々の幕開けになるのですが…。
 
 とりあえず麻雀の形にしなくては…、上がり判定のできた私はとりあえず麻雀の形で動くように簡単なプログラムを追加することにしました。本当は、掘りごたつを囲むような標準四角型の麻雀卓画面にしたかったのですが、画面制御が難しくなるのと最終的目標が「お仕事モード」であることを考えるとあまり意味がありません。そこで初期ファミコンの「四人打ち麻雀」のように横に牌を並べる並列型画面を採用しました。
 さて、ゲームにする為にはまず配牌をさせる必要があります。イカサマは一切考えていなかった私は、迷うことなく乱数で136枚の牌をランダムに並べ替えさせました。それを四人に交互に配っていく、普通の麻雀と同じように配牌させるようにします。コンピュータ側はツモったら乱数で適当な牌を捨て、人間側は捨牌を選択できるようにインターフェイス部分を作りました。カンの鋭いあなた! そう正解です。EXCEL−VBAのワークシート・セレクションチェンジイベントを使うのです! こうしてインターフェイス部を実装すると、とりあえずツモって捨てるだけの単純な麻雀ゲーム(らしきもの)が完成しました。相手の牌を鳴くこともないし、リーチもロンもない、ただツモ上がりできるだけのシンプルな麻雀です。
 「でーけた! でけた!」
 もう私は、ほとんど完成したかの勢いで跳びハネてました。このままだと136枚の牌を全てツモるとそこでエラーになるのでカウンターを付け、136枚から王牌の14枚を除いた122枚をツモったところで流局になるようにしました。
 「これは予想より早くリリースできるんじゃ…」早くも私の脳裏にヨコシマな考えがよぎります。当初自分で開発期間を一ヶ月と考えていましたので、この分なら一〜二週間でリリースできるじゃん! すごいじゃん>オレ! とお気楽に考えていました。しかしその考えは、次に実装する「鳴き」処理で見事に打ち砕かれるのでした。(ワクワク)

■ 地獄のカン判定
 

 とりあえず、ツモってばかりいてもしょうがないので、次にチーポンカンの処理をさせることにしました。(本当はロンの実装を先にすべきなのですが…)鳴きの中でも一番判定の簡単そうなポン処理をまず実装させることにします。ここでひとつの難問が発生しました。それは鳴いた後の処理です。鳴くということは当然それまでの順番を無視するわけで、一気に鳴いた相手に順番が回ってしまいます。現実の麻雀では当たり前のことですが、プログラム制御となるとかなりやっかいです。
 「これは…難しいぞ…(汗)」
 それまで単にシーケンシャルに処理させればよかったターン処理が、鳴かれた相手に応じて順番を切り替えるというだけで、一気に難しさ倍増です! とりあえず最初に作ったターン処理は使い物にならないので、鳴かれることを前提としたターン処理に一から作り直しました。これが思ったより大変な作業で、コンピュータはしょっちゅう多牌・少牌するわ、存在しない牌を捨てるわ…。この辺でそれまでお気楽に考えていた私の心に、俄かに暗雲が立ち込めてきたのでした。
 
 とりあえずポンできるようになったので、次はカン処理です。多分麻雀ゲームのなかで「上がり判定」の次に難しいのがカン処理です。なにしろカン処理は例外処理のオンパレードですから…。ご存知のようにカンには三つの種類があります。同種牌を暗刻でもっていて四枚目がきたときにカンする暗カン、四枚目を鳴いてサラす大明カン、サラしている明刻に四枚目を加える加カンです。これだけでも大変なのですが、カンした牌のロン(槍カン)判定、嶺上牌のツモ判定、さらにカン判定を繰り返すことも必要です(嶺上ツモ牌がさらにカンできる場合があります)。もうこの辺で頭グチャグチャです…。とりあえずひとつずつ実装していかなければどうにもなりませんので、忍耐の日々が続きます。
 このとき私が気がついたことは、「麻雀は例外処理の宝庫だ!」ということでした。さすが中国四千年の歴史の重みを感じましたね(ルールも練りに練り直されて今の形があるのでしょう)。ウンウン唸りながらもなんとか全ての鳴き処理を実装し終え、それなりにゲームも動くようになって来ました。このあたりで気がついたのですが、ロン判定も鳴き判定と同じところに実装する必要がありました。
 「どっひー! (泣)」またもやウンウン唸りながら…(以下略)。
 
 さーて鳴きもできるようになると、ますます麻雀ゲームらしくなってきましたが、ここでもうひとつ越えねばならない大きな山があります。それは「リーチ判定」です。リーチを判定させるということは当然テンパイを判定させる必要があります。上がり判定にもうひとつワザを仕込む必要がありました。リーチもまたカンと同じく例外処理の嵐です。リーチをしたら当然手は変えられないし、ダブリー・一発の判定もいる。残りツモがなければリーチはかけられないし、持ち点が千点を切っていてもダメ。しかし何といっても苦労したのは、コンピュータに「何を切ったらリーチができるか」を教えることでした。これは次のコンピュータの思考ルーチンの作成で書きますが、めっちゃくっちゃ苦労しました。結局、またウンウン唸りながら(仕事でも毎度です)、全ての処理を実装させていったのですが…。
 
■ おバカなコンピュータ
 

 さて、リーチもできるようになったところでそろそろコンピュータ側の思考ルーチンも考えなくてはなりません。この頃になって、ひとつ気になっていたことがありました。それは「コンピュータが上がらない」ことです。まあ、乱数で適当な牌を切らせているのだから、上がれなくてもしょうがないとは思いましたが、それにしてもリーチすらただの一度もかけられないとは…。あとで思い直したのですが、これはむしろ当然の結果のようです。毎回、乱数でツモって乱数で捨てているのですから、上がれる確率はほぼ天和と同じです。リーチがかかる確率はダブルリーチとほぼ同じ確立でしょう。(牌の残数が減っていくので厳密には同じではないはずですが…)
 「…思考ルーチンを組み込むだけで、本当に上がれるようになるのかな…」
 私の頭に嫌な予感が走ります。ここまで作りこんで今さらゲームにならない! なんてことになれば、それこそシャレにもなりません。かといってイカサマを組み込むことだけは嫌だったので(イカサマ組み込むほうがめんどくさいです)、とにかく思考ルーチンを作ってみよう! きっと大丈夫、上がれるさ! と自分を励ましたのでした。
 
 麻雀の基本といえばメンタンピンです。思考ルーチンも最初はメンタンピンで組み込もうと思いました。やり方は意外と簡単です。14枚の手牌(ツモ含む)に点数を付け一番点数の低いものを切っていけばいいはずです。まず中張牌は面子になる可能性が高いので当然点数を高くします。次にヤオ九牌、字牌と点数を低くしていきます。字牌でも三元牌と場風・自風は点数をやや高めに、オタ風は点数なしです。単独に牌を評価させた後、対子、塔子の評価もします。当然面子になりやすい順に高得点にします。これを毎ツモ&鳴き時に行なえばよいのです。さあ、この貧弱思考ルーチンでコンピュータはどこまでがんばれるでしょうか?
 結果は感動的なものでした! コンピュータは次々と不要牌を捨てていき、何と六順目で見事にリーチしたのです! このときの感動を私は生涯忘れることはないでしょう。たった数十行のコードでコンピュータはいかにも人間のごとくふるまい、生意気にリーチをかけてきたのです! 意気揚々とした私は、次に手牌の状況に応じてコンピュータの攻め方を変えさせるコードを追加しました。
 今ではコンピュータは、手配の状況に応じて染め手、トイトイ、クイタン、メンタンと豊富なバリエーションで手を変え品を変え攻めてきます。もっと賢くできるはずですが、それはとりあえず次回のバージョンアップに回すことにして、とりあえずゲームの完成を急ごうと未実装部分の着手へと進みました。このときは気がつきませんでしたが、実はこの思考ルーチンには大きな落とし穴がポッカリとあいていたのです。
 
■ 役判定と点数計算じゃあ!
 

 とりあえずコンピュータ・人間共々、上がれるようになったので、次に必要になってくるのが役判定と点数計算です。この部分は正直言って力仕事です。ひたすら上がり時の形に応じて役判定をガンガン実装していきます。簡単に判定できるのは四暗刻とかトイトイでした。逆に苦労したのは平和と九連宝灯です。特に平和は上がり時の制限が多く、何かと苦労させられたのを憶えています。点数計算は意外と簡単でした。符計算は待ち種類の判定だけ苦労しましたが、あとは「中張牌の暗刻なら四符」とか積算していって最後に切り上げるだけです。
 
 このとき気がついたのですが、麻雀の点数計算は中国人の経済観がモロにでているような気がします。彼らの発想は全て「切り上げ」なのです。二で割って割り切れなければ端数は全て切り上げ…、これはお金をもらう人の権利を保護し、払う人はたとえ損をしても、もらう人は決して損をしない(してはいけない!)という考え方が根底にあるような気がしました。
 
 とにもかくにも、点数計算ができるようになったのでいよいよ麻雀ゲームらしくなってきました。と、このあたりで気がついたのですが…どうもコンピュータがリーチ後、流局したときにテンパイしていないケースがチラホラと見受けられるようになりました。最初は流局時のテンパイ判定のバグだろうと思っていたのですが、どうもそのあたりにバグが見当たりません。さらにときおり不思議な牌(存在しない牌)を捨ててくるという以前直したはずのバグが再出するようにもなりました。
 「何かがおかしい…」そう思いながらもバグの出所がイマイチはっきりしません。そう、これらのバグは…作者の想像もつかない場所で発生していたのですから…!

 
 

開発中にさんざん参考にさせていただいた
「板橋浪人」麻雀サイトです
http://gatoh.sakura.ne.jp/


 
 

ダウンロードはもちろんこちらから!
EXCELで動かす会、会長
近田氏のホームページです。
http://www1.plala.or.jp/chikada/
■ 作戦指令! バグを撲滅せよ!
 

 リーチ後の流局時にテンパイ判定しないバグは、意外なところに存在していました。コンピュータの手牌を全て見える状態にし、何度かテストプレイをしてみたところ…。何とコンピュータはリーチをかけた後、テンパイを確定する牌を切らずにテンパイを崩す牌を切っていたのです(毎回そうではない、というところがミソです)。バグの原因は、実は先ほどの思考ルーチンにありました。思考ルーチンはテンパりやすいように牌を切る設計になっていますが、実際にテンパったときに切るべき牌は判定できないのです(盲点でした)。がーん! 要するにキチンとテンパイする為には、それ専用の全く別の判定ルーチンが必要だったということです。(今考えてみれば、ごく当たり前ですが…)このことに気づくまでに、何度テストプレイを重ね不毛な時間を費やしたことか! でも原因さえ分かれば後は実装あるのみです! リーチ判定をするたびに、同時にテンパイ時に切らねばならない牌(テンパイを確定させる牌)を判定するロジックを追加し、リーチ時にその牌を確実に切らせるよう修正しました。これでノーテンリーチの問題は解決しました。
 
 さて、存在しない牌を捨ててくるバグはどうだったかというと…。これは、チョット説明が非常に難しいのですが、簡単に言ってしまえば私の考え方が間違っていたということです。要するにテンパイ判定には二種類あって、流局時のテンパイ判定(本当のテンパイ判定)と、リーチ時のテンパイ判定(これは正確にはテンパイ判定ではなくてイーシャンテン判定です)は、似てて非なるものだ! と気づいたということです。(もう私の頭の中のほうがテンパってました…)
 
 このバグ修正でほぼ目立ったバグはなくなり、ようやく人に見せられる状態になったと判断した私は、VBAで動かす会の会長、近田氏の下に開発版をメール送信したのでした。すぐに近田氏から「これはすごい! 全面的に応援する!」と暖かいご支援のメールが返ってきました。早速リリースまでの詳細なスケジュールの打ち合わせが、近田氏と私のもとで何度もやり取りされ、氏からはその都度、絶えず励ましのお言葉と様々なサポートをいただきました。本当にありがとうございました。
 
■ お仕事モードはどうなったん?
 

 さて、ゲームの完成もいよいよ間近に迫ってきたのですが、ここでふと初心に立ち返ってみると…。
 「そもそも今回のテーマは、お仕事中にできる麻雀ゲームを作る! ということだったじゃないか!!」
 そうです。忘れてました。お仕事モードを作らなければこのゲーム製作はコンプリートしないのです!
 
 お仕事モードには、以前から幾つかのアイデアがありました。
 ・牌表示その他から麻雀っぽさを完全に排除しよう。
 ・EXCELにはグラフ機能があるから点数はグラフで表示させよう。
 ・名称を五時までモードと五時からモードにしようなど…。
 ついでに牌表示の部分は編集機能をつけて、完全フリーソフトだけどカンパは大歓迎!ということにしよう。そうだ! それがいい! そうすれば小遣いかせ…もとい、われわれEXCELゲーム開発者の励みになるじゃないか! もう、勝手にそうとしてしまおう! (笑)
 
 こうして「お仕事モード=五時までモード」の画面設計と実装を終了させ、ついに私の二ヶ月にも及ぶ死闘(十年越しのリベンジ)に終止符が打たれました…。
 
 開発初期、上がり判定ルーチンのヒントをくださいました伝説のSE前澤さん。
 開発途中からリリースまでの間、絶えずご助言・サポートいただき、リリース後も全面的に応援いただいております「動かす会」会長の近田さん。
 開発版をプレイされた後、すぐにご自身のサイトにてトピックス紹介いただけるとご連絡くださいました「動かす会」戦略部長の谷さん。
 ご多忙の中、どこよりも早く雑誌掲載の依頼をいただき、さらに〆切の延長まで申し出てくださいましたフリーライターの鈴木さん。
 そして、このコラムを最後まで読んでくださった「あなた」! (根性あるなー)
 
 皆様のおかげでCell_雀は、完成までこぎつけることができました。この場を借りまして今一度、厚く御礼申し上げたいと思います。
 本当にありがとうございました。
 
 (2004年11月28日 Cell_雀 ver1.00 正式リリース)
 
All Rights Reserved, Copyright© Gen Muto

GEN MUTO'S HOMEPAGE TOPへ Cell_雀のダウンロードサイトへ