Created
November 21, 2017 18:37
-
-
Save shawa/34b9d8db47ef53b9ee52e811957051b4 to your computer and use it in GitHub Desktop.
Token Katas solution, in APL
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
⍝ 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