# 3-Dimensional Minesweeper

I've been on a minesweeper kick for a while, but all the questions are for 2 dimensions (or that one in 1D). But 2D is 2 easy!

## Challenge

Write the shortest code possible to fill in the clues for a 3-dimensional minesweeper grid, since extra clicks are not optimal.

## Input

The board dimensions, taken in any order and reasonable format, and the mines as a list of 3-dimensional coordinates in any reasonable format and order.

Coordinates can be 0 or 1-indexed, but must be in the same order as the board dimensions.

## Output

A 3-D list resembling a 2-D list of columns (consistent order) or a list of 2-D slices (in order, any perspective) to form the final 3-D board. The number of adjacent mines can either be a number 1-26 or the corresponding letter a-z (case insensitive, just be consistent), unless there are no adjacent mines and then it is 0. Mines can be any character/string that is not a number 0-26 or a letter a-z if those are used.

## test cases

Board: 3x3x3
Mines: [[1,1,1]]
Output:
[[[1,1,1],
[1,1,1],
[1,1,1]],

[[1,1,1],
[1,*,1],
[1,1,1]],

[[1,1,1],
[1,1,1],
[1,1,1]]]

• feel free to add test cases, I have finals this week and don't feel like formatting them :p Commented May 6 at 14:46
• Must all mines be the same?
– Tbw
Commented May 6 at 19:04
• @tbw yes, it would be too confusing otherwise Commented May 6 at 22:19
• can the downvoters please elaborate? Commented May 9 at 22:14

# JavaScript (ES6), 147 bytes

-1 thanks to emanresu A

Expects (dim0, dim1, dim2, mines), where mines is a list of comma-separated strings.

Output format: integers for clues and Infinity for mines.

(p,q,r,a)=>(g=(s,c)=>[...Array(s).keys(n=0)].map(c))(r,z=>g(q,y=>g(p,x=>g(27,d=>a.map(v=>n+=v==[x+d%3-1,y+~-(d/3%3),z+~-(d/9%3)]&&1/(d!=13)))&&n)))


Try it online!

• -1 if you take the mines as a list of comma-separated strings Commented May 6 at 21:23

# JavaScript (Node.js), 117 bytes

m=>s=>(g=(d,j=2,i=s[j])=>i--?[...g(d,j,i),g(m.map((v,k)=>~~d[k]+(v[j]-i)**2),j-1)]:i?[]:d.some(n=>(j-=n<4,!n))||~j)


Try it online!

(mines: [number, number, number][]) => (size: [number, number, number]) => (number | true)[][][]

• -2 bytes by l4m2
• 119
– l4m2
Commented May 7 at 8:00

# Charcoal, 34 bytes

ＥθＥη⭆Ｅζ⟦ιλν⟧⎇№εν*§⁺0βＬΦε⬤ν‹↔⁻ς§πσ²


Try it online! Link is to verbose version of code. Takes the dimensions as three arguments followed by the list of mines and uses 0a-z* for output. Explanation:

 θ                                  First dimension
Ｅ                                   Map over implicit range
η                                Second dimension
Ｅ                                 Map over implicit range
ζ                             Third dimension
Ｅ                              Map over implicit range
⟦ιλν⟧                        List of coordinates
⭆                               Map over list of lists
№                      Count of
ν                    Current coordinates in
ε                     List of mines
⎇                       If found then
*                   Literal string * else
0                Literal string 0
⁺                 Concatenated with
β               Predefined variable lowercase alphabet
§                  Indexed by
ε            List of mines
Φ             Filtered where
ν          Current coordinates
⬤           All coordinates satisfy
ς      Current coordinate
↔⁻       Absolute difference with
π    Current mine
§     Indexed by
σ   Innermost index
‹         Is less than
²  Literal integer 2
Ｌ              Take the length
Implicitly print


# MATLAB, 99 bytes

function M(s,m)
b=zeros(s);for n=m;b(n(1),n(2),n(3))=1;end;c=convn(b,ones(3,3,3),'same');c(b==1)=-1


Uses convn to do the convolution. Mines are marked with -1.

Test run

>> M( [3,5,3], ...
[1,2; ...
1,2; ...
1,2 ] )
c(:,:,1) =
-1     2     1     0     0
2     2     1     0     0
1     1     1     0     0
c(:,:,2) =
2     2     1     0     0
2    -1     1     0     0
1     1     1     0     0
c(:,:,3) =
1     1     1     0     0
1     1     1     0     0
1     1     1     0     0


Ungolfed, with comments describing parameters and results

function [markedGrid, clueGrid] = MinesweeperClues3D(sz, mineIndices)
%MINESWEEPERCLUES3D make a 3D minesweeper grid

% for https://codegolf.stackexchange.com/questions/272974/3-dimensional-minesweeper

% param: sz (1,3) = size of game grid
% param: mineIndices (3,:) = positions of mines in columns
% result: clueGrid size(sz) = count of present and adjacent mines
% result: markedGrid size(sz) = clueGrid with mines marked as negative

% make mineGrid with mines marked as 1, else 0
mineGrid = zeros(sz);
for mineIndex = mineIndices
% mark this mine position
mineGrid(mineIndex(1), mineIndex(2), mineIndex(3)) = 1;
end

% 3D convolution with 3x3x3 array of ones, for clues
clueGrid = convn(mineGrid, ones(3,3,3),'same');

% negate mine positions. (Could just overwrite with -1 or whatever.)
markedGrid = clueGrid;
markedGrid( mineGrid==1 ) = -clueGrid( mineGrid==1);

end


# Jelly, 19 bytes

ŒpạṀỊɗÐḟⱮŒṬ¬S⁹ŒṬN¤o


A dyadic Link that accepts the dimensions of the board on the left and the mine locations (1-indexed) on the right and yields the fully clued board with -1 representing mines.

Try it online!

#### How?

ŒpạṀỊɗÐḟⱮŒṬ¬S⁹ŒṬN¤o - Link: list of integers, D; list of lists of integers, M
Œp                  - Cartesian product {D, rangified} -> AllCoords
Ɱ           - map across {M} with:
Ðḟ            -   filter discard those of {AllCoords} for which:
ɗ              -     last three links as a dyad - f(Coord, MineCoord):
ạ                 -       {Coord} absolute difererence {MineCoord} (vectorises)
Ṁ                -       maximum -> max orthogonal distance to mine
Ị               -       insignificant? -> mine or neighbour?
ŒṬ         - as boolean multidimensional arrays
¬        - logical NOT (vectorises)
S       - sum -> Distances (except positive numbers at mines)
¤  - nilad followed by link(s) as a nilad:
⁹      -   chain's right argument -> M
ŒṬ    -   as a boolean multidimensional array
N   -   negate (vectorises) -> Flags = -1 at mines, 0 elsewhere
o - {Flags} logical OR {Distances} (vectorises)


# JavaScript (Node.js), 116 bytes

a=>g=(w,x,y,z=0)=>w?[...--w?g(w,x,y,z):[],g(x,y,z,w)]:a.map(v=>w+=[1/0,1][0|Math.hypot(x-v[0],y-v[1],z-v[2])]||0)&&w


Try it online!

# Uiua 0.11.0, 2823 bytes SBCS

⍜⊡±‿¯:/+≡⬚0↻☇1¬⇡↯.3¤⊸∊⇡


Try on Uiua Pad!

# Python 3, 189 bytes

lambda x,y,z,s,r=range:[[[[str([sum((i%x-m[0])**8+(i//x%y-m[1])**8+(i//x//y-m[2])**8<4for m in s)for i in r(x*y*z)][a+b*x+c*y*x]),'@'][[a,b,c]in s]for a in r(x)]for b in r(y)]for c in r(z)]


-2 thanks to corvus

Try it online!

• 195 bytes: Put lambda x,y,z,s,r=range and put f= in the header (not counting towards the size). Commented May 7 at 15:39

# Python 3, 96 bytes

f=lambda m,a=0,*s:[f([y+m*(x!=i)for x,*y in m if-2<x-i<2],*s)for i in range(a)]or~-all(m)|len(m)


Try it online!

Resursive approach, works with any number of dimensions

# J, 24 bytes

[:+/2>(>.&|/@:-"1/]#:i.)


Try it online!

• ]#:i.) Create all possible coordinates for the dimensions
• (>.&|/@:-"1/] Create a multi-dimension "table" of the max of the absolute element-wise differences between all the coordinates and all the bombs. Any time this max is 1 or less, a bomb is adjacent.
• [:+/2> So just "sum the planes" where the number is less than 2. This will automatically return the answer in the right shape.

# APL(Dyalog Unicode), 27 bytes SBCS

{¯1@⍵+⌿∘∊⍤⊢⌺(3⌿3)∨⌿⍵∘.≡⍳⍺}



A dfn takes dimension as its left argument and indices of mines as its right argument. It marks the mines with -1 in the output.

{¯1@⍵+⌿∘∊⍤⊢⌺(3⌿3)∨⌿⍵∘.≡⍳⍺}
⍳⍺    coordinates of the grid
∨⌿⍵∘.≡⍳⍺    bit map of the mindefield
+⌿∘∊⍤⊢⌺(3⌿3)            3 by 3 by 3 adjacent count
¯1@⍵                        with the mines marked as -1


Try it on APLgolf!

Another simpler one that has the same length.

​{¯1@⍵{+⌿∊⍵}⌺3 3 3⊢1@⍵⊢⍺⍴0}


# Scala 3, 312 bytes

A port of @AnrimO's Python answer in Scala.

Golfed version. Attempt This Online!

(x,y,z,s)=>(for(c<-0to z-1)yield{(for(b<-0to y-1)yield{(for(a<-0to x-1)yield{if(s.contains(List(a,b,c)))"@"else(for(i<-0to(x*y*z-1))yield{if(s.exists(m=>(math.pow((i%x-m(0)),8)+math.pow((i/x%y-m(1)),8)+math.pow((i/x/y-m(2)),8))<4))"1"else"0"}).mkString("").charAt(a+b*x+c*y*x).toString}).toList}).toList}).toList


Ungolfed version. Attempt This Online!

object Main {
def f(x: Int, y: Int, z: Int, s: List[List[Int]]): List[List[List[String]]] = {
def inRange(m: List[Int], i: Int): Boolean = {
(math.pow((i % x - m(0)), 8) + math.pow((i / x % y - m(1)), 8) + math.pow((i / x / y - m(2)), 8)) < 4
}

val result = for (c <- 0 until z) yield {
val layer = for (b <- 0 until y) yield {
val row = for (a <- 0 until x) yield {
val cell = for (i <- 0 until (x * y * z)) yield {
val condition = s.exists(m => inRange(m, i))
if (condition) "1" else "0"
}
var value = cell.mkString("").charAt(a + b * x + c * y * x).toString
if (s.contains(List(a, b, c))) value = "@"
value
}
row.toList
}
layer.toList
}
result.toList
}

def main(args: Array[String]): Unit = {
val result = f(4, 3, 3, List(List(1, 1, 1)))
println(result.map(_.map(_.mkString(" ")).mkString("\n")).mkString("\n\n"))
}
}