Skip to content

Instantly share code, notes, and snippets.

@shawa
Created November 21, 2017 18:37
Show Gist options
  • Save shawa/34b9d8db47ef53b9ee52e811957051b4 to your computer and use it in GitHub Desktop.
Save shawa/34b9d8db47ef53b9ee52e811957051b4 to your computer and use it in GitHub Desktop.
Token Katas solution, in APL
⍝ An example board, the 4x4 reshape of a 16-vector
board ← 4 4 ⍴ 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0:
board
0 1 0 0
0 1 0 0
0 0 0 0
0 0 0 0
⍝ A 3x3 matrix of boards, with each inner board shifted over
⍝ one cell, i.e. the top left board is the board shifted up
⍝ and left, top middle is shifted up, top right shifted up and
⍝ right etc.
offsets ← ¯1 0 1
shifteds ← { offsets ∘.⊖ offsets ∘.⌽ ⊂⍵}
shifteds board
┌───────┬───────┬───────┐
│0 0 0 0│0 0 0 0│0 0 0 0│
│0 0 1 0│0 1 0 0│1 0 0 0│
│0 0 1 0│0 1 0 0│1 0 0 0│
│0 0 0 0│0 0 0 0│0 0 0 0│
├───────┼───────┼───────┤
│0 0 1 0│0 1 0 0│1 0 0 0│
│0 0 1 0│0 1 0 0│1 0 0 0│
│0 0 0 0│0 0 0 0│0 0 0 0│
│0 0 0 0│0 0 0 0│0 0 0 0│
├───────┼───────┼───────┤
│0 0 1 0│0 1 0 0│1 0 0 0│
│0 0 0 0│0 0 0 0│0 0 0 0│
│0 0 0 0│0 0 0 0│0 0 0 0│
│0 0 1 0│0 1 0 0│1 0 0 0│
└───────┴───────┴───────┘
⍝ Collapse this into a matrix built from the board, where at
⍝ each cell is the number of surrounding mines, either up,
⍝ down, left, or right.
toAdjacents ← {⊃+/+/ ⍵}
toAdjacents shifteds board
2 2 2 0
2 2 2 0
1 1 1 0
1 1 1 0
⍝ Using the boolean matrix on the left, turn elements on the
⍝ right to be negative numbers. In our case, wherever there's
⍝ a mine, set the adjacency count to -1
mask ← {⍺ + ⍵ + (¯2×⍺) × ⍵}
board mask toAdjacents shifteds board
2 ¯1 2 0
2 ¯1 2 0
1 1 1 0
1 1 1 0
⍝ Given solved board, like the one above, let's overlay the
⍝ original board, replacing a 1 with a *
pretty ← { '*0123456789'[2 + ⍵]}
pretty board mask toAdjacents shifteds board
┌────┐
│2*20│
│2*20│
│1110│
│1110│
└────┘
⍝ And we're done!
⍝ ...almost; all of this so far is correct, but does not
⍝ properly implement the behaviour of minesweeper. Our geometry
⍝ is wrong!
⍝ In this solution, our solve function will wrap around, so
⍝ that cells on oposite edges are considered neighbours
⍝ (toroidal, or wrap-around geometry).
⍝ The following functions and operators can be applied in
⍝ combination with the solve function to correct this
⍝ behaviour, heavily inspired by John's implementation of
⍝ the Game of Life on the Dyalog website [1].
⍝ [1]: https://dfns.dyalog.com/s_life.htm
⍝ In essence, the solution is to pad the board on the top
⍝ and on the left with zeros before solving the minesweeper
⍝ problem so that edge cells don't count any of the other
⍝ mines on the board
⍝ Pad the top of the given argument with zeros
padTop ← {0⍪⍵}
padTop board
0 0 0 0
0 1 0 0
0 1 0 0
0 0 0 0
0 0 0 0
⍝ Perform the left function argument under transpose, i.e.
⍝ transpose the array, apply the function, then transpose
⍝ the result.
⍝ So to pad the left, we just perform a top pad, under
⍝ transpose.
underTranspose ← {⍉⍺⍺⍉⍵}
padTop underTranspose board
0 0 1 0 0
0 0 1 0 0
0 0 0 0 0
0 0 0 0 0
⍝ Now to pad both the top and the left, we then just pad the
⍝ array, then pad the array _under transpose_.
pad ← {padTop underTranspose padTop ⍵}
pad board
0 0 0 0 0
0 0 1 0 0
0 0 1 0 0
0 0 0 0 0
0 0 0 0 0
⍝ Now to invert these, to remove padding we drop the first
⍝ element.
unpadTop ← {1↓⍵}
⍝ To unpad the whole matrix, just unpad the top, then unpad
⍝ the top under transpose.
unpad ← {unpadTop underTranspose unpadTop ⍵}
⍝ Now, just as we have an operator above to apply a function
⍝ under transpose, we can define an operator to apply a
⍝ funciton _under padding_. Again, this really just
⍝ corresponds to padding the array top and left, applying
⍝ the function, then removing the padding.
⍝ The result of this operator is that any operation which
⍝ would wrap around, like a rotate, would behave as if there
⍝ was no wrap around.
underPadding ← {unpad ⍺⍺ pad ⍵}
⍝ Let's call it that
withoutWrapAround ← underPadding
⍝ For an example, take this character matrix
chars ← 3 3 ⍴ 'aaabbbccc'
aaa
bbb
ccc
⍝ If we rotate it, there's wrap around:
1 ⊖ chars
bbb
ccc
aaa
⍝ We can use the operator to modify the behaviour of ⊖ so
⍝ that what would have wrapped is instead a zero:
1 ⊖ withoutWrapAround chars
b b b
a a a
0 0 0
⍝ So to correctly solve minesweeper, we apply our solve
⍝ function as before, but without wrap around.
⍝ Let's put it all together!
solve ← {⍵ mask toAdjacents shifteds ⍵}
solve board
2 ¯1 2 0
3 ¯1 3 1
2 1 2 0
2 1 2 1
⍝ Note that board[2;4] below is 0, and not, erroneously, 1
solve withoutWrapAround board
2 ¯1 2 0
2 ¯1 3 1
1 1 2 0
0 0 1 1
⍝ And so...
mineSweep ← {pretty solve withoutWrapAround ⍵}
mineSweep board
┌────┐
│2*20│
│2*31│
│1120│
│0011│
└────┘
⍝ APL is Fun.
⍝ tryapl.org
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment