どうも、ゴトーだ。
一昨日からJavaScriptでオセロを作り始めていて、昨日投下した分はバグがあったり、コンピューターが弱すぎたりしたので、その調整をメインにいくつか改良したので、その紹介をしたい。
それとプログラミング記事もSEOを意識した書き方にしていくので、これまでと少しテイストが違うかもしれない。
15日目の成果物 ~コンピューターがマシになったオセロ~
14日目にオセロを投下したが、コンピューターは置けるマスの中からランダムに打ってくるだけで弱かったので、いくつかのオセロの定石を踏まえてパワーアップさせることに成功した。
コンピューターは3つの思考回路しか持っていないが、それでもダイジョーブ博士の手術で成功したくらいには変わったと思う。
前回の分はひっくり返すロジックに一部間違いがあって、ひっくり返せないところもひっくり返せてしまうこと、そして試合途中でもお互い打つところがなかったときに試合が終了しないというバグも修正した。
コンピューターの思考パターン
思考パターンは単純で、以下の順番にコンピューターはフィルタリングしている。
- 打てる場所を全部探す
- その中に隅が含まれているかを探す
- 隅があれば最優先で打つ
- 隅がなければ、隅の隣である合計12箇所を排除する
- 排除したものの中から、より中央に近いポイントを打つ
- 隅の隣しか打てないなら、その中からランダムで打つ
俺はオセロはそれほど強くないが、普段意識していることをコンピューターに実践してもらうだけで随分強くなった気がする。
それなりに真面目に打てば勝てるけど、適当にやってまえという感じでやると負けることが多くなった。
隅かどうかの判定
ある場所が隅かどうかについてはシンプルに判定している。
盤面を2次元配列で管理していて、一番左上を[0, 0]、右下を[7, 7]とみなして、[x座標, y座標]という形式になっている。
つまり隅は[0, 0], [7, 0], [0, 7], [7, 7]の4地点だけでそれを全てif文でチェックして隅かどうかを判別する。
手こずった点としては、JavaScriptの値の検証で[0, 0] == [0, 0]というイコール文がtrueとしてかえってこなくて、最初は隅じゃないと判定されてばかりで、そこに気づいて修正するまでに時間がかかった。
結果的に以下のような関数を書いて値を検証することにした。
function arrayIsSame(array1, array2) { if (array1.length == array2.length) { var result = true; for (var i = 0; i < array1.length; i++) { if (array1[i] != array2[i]) { result = false; } } return result; } else { return false; } }
隅の隣かどうかについても、12パターンを列挙して全て検証している。
中央に近いかどうか
これはx座標、y座標の中央値を3.5とみなして、あるポイントにおける【「x座標 - 3.5の絶対値」 + 「y座標 - 3.5の絶対値」】が小さいほど中央に近いというみなし方をした。
中央から離れるということは、絶対値が大きいという発想に至ればあとは簡単だ。
例えば[0, 7]という座標と[3, 3]という座標があったとして、[0, 7]の絶対値の合計は7、[3, 3]の絶対値の合計は1となるので[3, 3]のほうが中央に近く、そちらを優先的に打つということをしている。
オセロでは辺を取ったほうが有利と思われているが、少し調べたところ辺はなるべく取らないほうがいいらしいので、この計算式によって辺まではみ出さずに打ってくれるようになった。
その他の修正点
ひっくり返すロジック
ある地点においた時に、相手の石をひっくり返すロジックは昨日から修正して以下のように実装した。
- 置こうとしたマスの周囲8マスを見渡して、相手の石がある方向のみを見る
- 周囲に相手の石がなければひっくり返すことができないので、そこには置けない
- 相手の石がある方向をずっと辿っていって、空白なしで相手の石のみが続き、その奥に自分の石が見つかれば、その間を挟めるものとみなす
- 盤面をはみ出すまでチェックしていく
自分が●で、相手が◯とすると
「空◯◯◯●」
という盤面なら、空のところからみて右方向を探索していき、右方向を辿っていくと「空白なしで相手の石が続き」、「その奥に自分の石がある」ので空の地点におけるとみなすことができる。
ヒント機能
もともと「盤面の中からどこに置けるか」を全て探索できるようにしていて、仮にどこにも置けないと自動でパスされるようになっていた。
これを応用して「現在どこに置けるか」をヒントとして提示するようにしている。
これの実装は簡単で、盤面を表示する時に、置けるポイントに対してHTML属性を加えて、それをCSSでデザインしているだけで実現できた。
戦績保存機能
これはマルバツゲームとほとんど全く同じプログラムで導入した。
終了判定時に自分と相手のスコアを比較して勝敗を判定し、それをAjaxで値を送信し、PHPからMySQLにデータを保存している。
今の所は全ユーザーで共通の戦績となっている。
オセロで学んだこと
オセロというよりもゲームを作る時に共通していたのが、あらかじめルールを決めておいて、それを実現するためのプログラムを書くということ。
これまで手を動かしながら試行錯誤していたが、ルールは最初から決めておかないとこんがらがって何をすればよいかわからなくなるので、仕様は決めておいたほうが良いと思った。
それとオセロほどの単純なものでも500行くらいになってしまったので、複雑なゲームは何十万行とか平気でいきそうで、ゲーム作ってる人はすごいなあと思いました。(小並感)
ところで必須スキルと言われるjQueryなしで問題なく作れているが、いつか必要な時がくるのだろうか。
次回やること
はてなブロガー的にはアプリーチ的なものを作りたいと思っているので、PHPを使ったクロールについて勉強予定。