Squeak SmalltalkでMinesweeperを500文字プログラミングしようとして挫折した


恒例のシリーズ第三弾。

今回はテンプレに従い、ちゃんと(?)挫折しました。w アルゴリズムも @enu7 さんのをほぼ踏襲したので直訳版も省略。^^;

| out H W X map input bombs check |

out := Transcript.
H := W := X := 10.
map := (Array new: H withAll: (String new: W withAll: $.)) deepCopy.

input := [:msg |
   out cr; show: msg.
   (FillInTheBlank request: msg) ifEmpty: [^self] ifNotEmpty: [:ans |
      out show: ans.
      ans asInteger]
].

bombs := Set new.
X timesRepeat: [bombs add: W atRandom @ H atRandom].

check := nil.
check := [:pos |
   (pos >= (1@1) and: [pos <= (W@H)] and: [((map at: pos y) at: pos x) = $.]) ifTrue: [
      | count |
      count := (bombs intersection: pos eightNeighbors) size.
      (map at: pos y) at: pos x put: count asHexDigit.
      count = 0 ifTrue: [pos fourNeighbors do: check]
   ]
].

[:exit |
   [(map concatenation occurrencesOf: $.) > X] whileTrue: [
      | x y |
      x := y := 0.
      out cr; showln: map asStringWithCr.
      [x between: 1 and: W] whileFalse: [x := input value: 'x? '].
      [y between: 1 and: H] whileFalse: [y := input value: 'y? '].
      (bombs includes: x@y)
         ifTrue: [out cr; showln: 'BOM!'. exit value]
         ifFalse: [check value: x@y].
   ]
] valueWithExit.

bombs do: [:pos | (map at: pos y) at: pos x put: $*].
out cr; showln: map asStringWithCr
..........
..........
..........
..........
..........
..........
..........
..........
..........
..........
x? 1
y? 1

0000000000
1100011100
.10001..11
11001.....
00001.....
00001.....
111001....
..1001....
11101.....
00001.....
x? 10
y? 4

BOM!

0000000000
1100011100
*10001*.11
11001....*
00001*....
00001.....
111001**..
.*1001....
11101.....
00001**...


Smalltalk、特に無駄に API が充実している Squeak Smalltalk のよいところは、仕様をそれっぽくコードに書き下すことができるところ。

bombs := Set new.
X timesRepeat: [bombs add: W atRandom @ H atRandom].

とか、

count := (bombs intersection: pos eightNeighbors) size.
count = 0 ifTrue: [pos fourNeighbors do: check]

とか、ほれぼれしませんか? そうですか、しませんか。 はい…。