Slide 1

Slide 1 text

マインスイーパー Solverの話 gott

Slide 2

Slide 2 text

自己紹介 ● ‘16入学 ● システム創成学科 システムデザイン&マネジメントコース ● ‘13-’16 GNEX Inc. ● ‘16- UNIPRO Inc. ● Web屋 PHP / JavaScript ● 趣味を書いたりする

Slide 3

Slide 3 text

マインスイーパーとは 初級:9×9のマスに10個の地雷 中級:16×16のマスに40個の地雷 上級:30×16のマスに99個の地雷

Slide 4

Slide 4 text

課題 http://save.sys.t.u-tokyo.ac.jp/~fujii/lecture/pgmkiso/

Slide 5

Slide 5 text

制約 ● マインスイーパーのライブラリが用意されている ● Java使う

Slide 6

Slide 6 text

結果

Slide 7

Slide 7 text

自明な確定マス・安全マス 旗の立っているマスは,中心の 1マスの情報だけで爆弾があると分かる。 →自明な確定マス 右下のマスは自明な確定マスの情報だけで安全だと分かる →自明な安全マス

Slide 8

Slide 8 text

非自明な安全マス 右下のマスは自明な安全マスではないが,矛盾しない全ての 爆弾配置パターンにおいて安全 →非自明な安全マス

Slide 9

Slide 9 text

どうやって探すの

Slide 10

Slide 10 text

自明な安全マス すぐに思いつくように,全てのマスについて周囲 8マスを探索して確定マスを探し続ければよい

Slide 11

Slide 11 text

非自明な安全マス 全探索! 周囲の爆弾の個数がわかっているから,それまで考えたパターンと無矛盾なものを全列挙

Slide 12

Slide 12 text

定義 NumEdge 周囲8マスの中に,確定マスでない未オープンのマスがあるような数字マスの集合。 BoxEdge 周囲8マスの中に数字マスがあるような,確定マスでない未オープンのマスの集合。 関連する relates :: NumEdge -> BoxEdge -> Bool relates a b = a `next_to` b -- next_to: 斜めに隣接

Slide 13

Slide 13 text

非自明な安全マス (0, 1)はすでに爆弾が確定したマス。 a = (1, 1), b = (2, 1), c = (3, 1) d = (1, 2), e = (2, 2), f = (3, 2) cから出発したとする。 cの周りの爆弾配置は右の 2通り。 relates(c, f) ∧ relates(b, f)なので, b,続いてaについて調べる。 aについて調べると,左は矛盾する。

Slide 14

Slide 14 text

それでも見つからなかったら 運ゲーをやるしかないが,場所によって爆弾のありそうな確率は異なる →事前確率(なにも情報がないマスに爆弾がある確率 )より有意に小さければ開ける価値あり

Slide 15

Slide 15 text

変な工夫 double default_probability = (double)countNotFixedBombs() / board.count( (i) -> !i.isOpen() & !i.isFixed() && !i.isSafe() , this); // なにも情報がないマスに爆弾がある確率 if (min_prob < default_probability - BIAS) { min_cell.setSafe(); return true; } BIAS = 0.014で一番勝率が高くなった

Slide 16

Slide 16 text

書き方の話: 素の制御構文をたくさん書くな 例えばこんなfor文をたくさん書くとする。 for (int i = n - 1; i >= 0; i--) { /*...*/ } 一箇所でも赤が抜けると黙ってバグる。 こうしよう。 this.board.numEdge.forEach((i) -> { i.relatedEdges = new BoardCellSet(); i.getIterator(this).apply_around((j) -> j.isBoxEdge() && i.relatedEdges.add(j.getCell())); });

Slide 17

Slide 17 text

書き方の話: 抽象化 public class ProbPlayer extends Player このクラスのmainメソッドがエントリポイント。 Playerはコンパイル済みのライブラリ内にある。 player.open(int x, int y) を呼ぶと親クラスであるPlayerの public final boolean open(int x, int y) が呼ばれ,実際に(x, y)マスを開ける処理が走る。 →何が問題か?

Slide 18

Slide 18 text

書き方の話: 抽象化 答え: モジュールが密結合していてテストが難しい 関連ワード: テスタビリティ,依存性注入 (DI) Playerはあくまでinterfaceであるべき。その上で,ランダムに盤面を生成するようなロジックを implements Playerなるクラスに実装するべき。そうすれば,ユニットテストに都合のいい盤面を与えてやり,自分で書いたロ ジックがそれにきちんと対応できているか自動で調べることができるようになる。

Slide 19

Slide 19 text

書き方の話: 適切なクラスを使う public class GameClear extends RuntimeException おばけ。ゲームクリアは異常事態なのか? これのせいでtry-catchがうまくいかなくて詰まってた https://blogs.msdn.microsoft.com/nakama/2008/12/29/net-part-1/

Slide 20

Slide 20 text

Fin.