Minesweeper At Work

18

0

Everyone knows the old minesweeper game that shipped with Windows XP. It's a simple grid with a 9x9 matrix of cells containing either a number (indicating how many mines are adjacent to it) or a mine.

enter image description here

The challenge is to generate a random 9x9 grid with 10 bombs given any integer seed (up to whatever your machine/language's largest int is) with brownie points if you implement the PRNG yourself

example output: cells contain either numerals 0-8 or * for mines

*101*1000
110111000
123210000
1***10000
123210011
00000002*
00000114*
000112*3*
0001*2121

Shortest code in bytes wins.. standard rules etc, etc..

Aaron

Posted 2016-09-02T19:59:13.567

Reputation: 1 213

3You should indicate what the numbers mean :) – Nathan Merrill – 2016-09-02T20:11:47.630

4

Microsoft Minesweeper is a heck of a lot older than XP and minesweeper-like games date back to at least the 60s.

– AdmBorkBork – 2016-09-02T20:19:33.887

11Also, I don't have time to play Minesweeper while at work -- I'm too busy on PPCG. ;-) – AdmBorkBork – 2016-09-02T20:41:02.713

1What counts as a PRNG, exactly? How many different configurations must it be able to produce? Can we not use the seed and just generate a different configuration each time, if our language has a PRNG which is automatically initallized to a "random" seed? – Luis Mendo – 2016-09-02T21:07:33.920

1@TimmyD But XP's version is the first version that had a 9x9 grid. Anything older uses an 8x8 grid for Beginner. #outnerded ;) – mbomb007 – 2016-09-02T21:37:06.623

Answers

3

Dyalog APL, 40 bytes

⎕rl←1⋄(1,¨a)⍕¨{⍉3+/0,⍵,0}⍣2⊢a←9 9⍴9≥?⍨81

(assumes ⎕io←0)

the 1 in ⎕rl←1 is the seed

from right to left:

?⍨81 is the same as 81?81 - a random permutation

9≥ results in a bitmask containing ten 1s at random positions, the rest are 0s

a←9 9⍴ reshape to a 9-by-9 square and call it "a"

{ }⍣2 do the following twice:

⍉3+/0,⍵,0 sliding window sum of 3 columns (assume 0s outside), then transpose

(1,¨a)⍕¨ is format (convert to string) each. The left argument to specifies the total number of characters and fractional characters in the result. If can't format according to that spec it outputs a * - a lucky coincidence for this problem. a will be 1 where the mines are - trying to fit a whole and fractional part into a single char is impossible, so those will appear as *s.

ngn

Posted 2016-09-02T19:59:13.567

Reputation: 11 449

Can you explain the ⎕io←0 assumption? I'm not familiar with Dyalog APL... – Aaron – 2017-04-10T13:44:10.517

1

Array indices in Dyalog are 1-based by default. Setting ⎕io (the "Index Origin") to 0 makes them 0-based and changes some primitives accordingly, e.g. ⍳3 will be 0 1 2, not 1 2 3. That can be done either programmatically (⎕io←0) or from the preferences in the GUI. Having this choice is a 50-year-old mistake that still splits the tiny APL community of today.

– ngn – 2017-04-10T14:30:41.677

5

MATLAB, 94 93 bytes

rng(input(''));x(9,9)=~1;x(randperm(81,10))=1;y=[conv2(+x,ones(3),'s')+48 ''];y(x)=42;disp(y)

Example run (the first line after the code is the input typed by the user):

>> rng(input(''));x(9,9)=~1;x(randperm(81,10))=1;y=[conv2(+x,ones(3),'s')+48 ''];y(x)=42;disp(y)
99
*10001*2*
220001232
*201111*1
*312*1111
12*211000
011211000
0001*1000
000112110
000001*10

Explanation

rng(input(''));

takes an integer and uses it as seed. (This works in modern MATLAB versions. Old versions may need a different syntax.)

x(9,9)=~1;

assigns logical 0, or false (obtained by logically negating 1) to the entry (9,9) of a matrix x. The rest of the entries are automatically initiallized to logical 0 too.

x(randperm(81,10))=1; 

assigns 1 (autoomatically cast to logical 1, or true) to 10 of the 81 entries of x, chosen randomly without replacement. These entries are the ones that contain bombs.

conv2(+x,ones(3),'s')

is an abbreviation of conv2(+x,ones(3),'same'). It convolves the matrix x (which needs to be cast to double, using +) with a 3×3 neighbourhood containing 1. This counts how many bombs are adjacent to each entry. For entries that contain a bomb it includes that bomb, but the value there will be overwritten later.

y=[...+48 ''];

adds 48 to the value, to convert from number to ASCII code. Concatenating with the empty matrix casts these ASCII codes to chars.

y(x)=42;

assigns 42 (ASCII code for '*') to the positions of the bombs. These positions are given by x, which is here used as a logical index.

disp(y)

displays the result.

Luis Mendo

Posted 2016-09-02T19:59:13.567

Reputation: 87 464

4

Javascript (ES6), 204 or 198 bytes

Custom PRNG (204 bytes)

s=>(p=-1,S=n=>(x=p%9-(n+=p)%9)*x-64&&b[n]=='*',T=n=>S(n)+S(-n),b=[...'*00000000'.repeat(9)]).sort(_=>(s=(22695477*s+1)>>>0)&1||-1).map(c=>(p++,c=='0'?T(1)+T(8)+T(9)+T(10):c)).join``.match(/.{9}/g).join`
`

This code is using a linear congruential generator with multiplier 22695477 and increment 1 (this is the Borland C/C++ implementation).

Due to the poor efficiency of the PRNG during its warmup phase, I had to place one bomb per row (instead of 10 at the beginning or 10 at the end of the unshuffled array). So, there are only 9 bombs. I may try to fix that later.

Also, there must be a simpler/shorter way of processing the 'out of board' check (x=p%9-(n+=p)%9)*x-64 but I just can't figure it out right now.

Using Math.random() (198 bytes)

s=>(p=-1,S=n=>(x=p%9-(n+=p)%9)*x-64&&b[n]=='*',T=n=>S(n)+S(-n),b=[...'**********'+'0'.repeat(71)]).sort(_=>Math.random()-.5).map(c=>(p++,c=='0'?T(1)+T(8)+T(9)+T(10):c)).join``.match(/.{9}/g).join`
`

This one includes 10 mines as requested.

Demo

let f =
_=>(p=-1,S=n=>(x=p%9-(n+=p)%9)*x-64&&b[n]=='*',T=n=>S(n)+S(-n),b=[...'**********'+'0'.repeat(71)]).sort(_=>Math.random()-.5).map(c=>(p++,c=='0'?T(1)+T(8)+T(9)+T(10):c)).join``.match(/.{9}/g).join`
`
console.log(f())

Arnauld

Posted 2016-09-02T19:59:13.567

Reputation: 111 334

'**********'+'0' is equal to '**********'+0; that saves two bytes on the 198-byte-version. – Paul Schmitz – 2016-09-03T12:42:17.513

@PaulSchmitz - Unfortunately this '0' is supposed to be repeated and 0.repeat() wouldn't work. – Arnauld – 2016-09-03T13:58:22.380

Oops, I though it would get executed like ...('**********'+0).repeat(71). Sorry. – Paul Schmitz – 2016-09-03T14:40:55.897

3

R, 187 Bytes

set.seed();x=1:121;y=x[!(x%%11 %in% 0:1|(x-1)%/%11 %in% c(0,10))];z=sample(y,10);x=x*0;for(t in z){p=t+c(-10:-12,-1,1,10:12);x[p]=x[p]+1};x[z]=9;m=data.frame(matrix(x[y],9));m[m==9]='*';m

Try it on Ideone

Explanation:

set.seed() take a cst seed.

x is the index for a 11*11 matrix

y is the index of the 9*9 matrix in the 11*11 matrix

z is the index of the bomb

x=x*0 initialise the matrix value

The loop add 1 to x in case of adjacent bomb.

YCR

Posted 2016-09-02T19:59:13.567

Reputation: 131

1I think you have to take the argument to set.seed() as input. – BLT – 2017-04-08T03:51:16.563

3

Python 2, 269 266 264 bytes

from random import*
seed(input())
z=1,0,-1
n=range(9)
m=[[0]*9 for _ in n]
for x,y in sample([[x,y]for x in n for y in n],10):
 m[x][y]=-9
 for a in z:
  for b in z:
    if 0<=x+a<9>0<=y+b<9:m[x+a][y+b]+=1 # it gets displayed as 4 spaces, but the beginning of this line is a single tab
print("\n".join("".join([`c`,'*'][c<0]for c in l)for l in m))

Try it on ideone.com

Saved 2 bytes thanks to Aaron.

Most likely still golfable.

Explanation

random is imported for using seed to seed the PRNG and sample to select ten bomb locations randomly. m is a 9 x 9 matrix saving the board. For each of the bomb locations, the corresponding entry in m gets set to -9 and all neighbouring entries get incremented. This way m ends up containing the count of adjacent bombs for non-bomb cells and a negative number for bomb cells. The final print prints the whole board by iterating through all lines l in m and all cells c in l.

LevitatingLion

Posted 2016-09-02T19:59:13.567

Reputation: 331

What exactly is 'random' used for, exactly? – clismique – 2016-09-02T22:13:34.790

@Qwerp-Derp probably to seed the random number generator indirectly used by sample() – Patrick Roberts – 2016-09-02T23:10:57.227

save 2 bytes by mixing tab indents inside for a in z: block (only python 2.x) – Aaron – 2017-04-07T14:57:01.107

2

JavaScript ES6, 244 bytes

f=(a=[...Array(11)].map(_=>Array(11).fill(0)),n=10,r=_=>Math.random()*9|0,x=r(),y=r())=>a[x+1][y+1]>8||[0,1,2].map(i=>[0,1,2].map(j=>a[x+i][y+j]++),a[x+1][y+1]=9)&&--n?f(a,n):a.slice(1,-1).map(a=>a.slice(1,-1).map(n=>n<9?n:`*`).join``).join`
`
;o.textContent=f()
<pre id=o>

Neil

Posted 2016-09-02T19:59:13.567

Reputation: 95 035

You may want to elaborate on which part is your code. – NoOneIsHere – 2016-09-02T23:49:26.117

@NoOneIsHere The first 244 bytes, hopefully ;-) The first line should be 242 bytes long, then there's the newline and the ``` character. – Neil – 2016-09-03T00:10:41.517

1

Ruby, 181 194 183+1 = 184 bytes

Forgot to actually set the seed, whoops. Uses the -n flag.

Try it online!

srand$_.to_i
a=[0]*81
[*0..80].sample(10).map{|i|w=i%9;h=i/9;a[i]=-99
(r=-1..1).map{|x|x+=w
x<0||x>8?0:r.map{|y|y+=h
y<0||y>8?0:a[9*y+x]+=1}}}
puts a.map{|c|c<0??*:c}.join.scan /.{9}/

Value Ink

Posted 2016-09-02T19:59:13.567

Reputation: 10 608

0

Python 2, 172 bytes

from random import*
seed(input())
r=range
b=set(sample(r(81),10))
for j in r(9):print''.join([[`len({x-10,x-9,x-8,x-1,x+1,x+8,x+9,x+10}&b)`,'*'][x in b]for x in r(j,81,9)])

Try it online!

tsh

Posted 2016-09-02T19:59:13.567

Reputation: 13 072