Created
February 1, 2019 15:52
-
-
Save rellfy/e15e1942d2645dff98dc3d4c6b857255 to your computer and use it in GitHub Desktop.
Discord minesweeper generator
This file contains hidden or 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
var width; // set by calling "init" | |
var total; // set by calling "init", total n of mines | |
var mines; // set by calling "init" | |
var adjacent = new Array(); // count of adjacent mines | |
var mine = 9; // adjacency count for a mine | |
var exposed = new Array(); // exposure state / pending exposures | |
var listEnd = -1; // end marker in "exposed" | |
var incorrect = -2; // incorrect flag, at end of game | |
var exploded = -3; // exploded mine (at end of game!) | |
var unexposed = -4; // default state at start of game | |
var squares = []; | |
var charMine = "||:bomb:||"; | |
var charEmpty = "||:white_large_square:||"; | |
var numbers = ['','one','two','three','four','five','six','seven','eight','nine']; | |
function setSq(thisSquare) { | |
var sq = squares[thisSquare] ? squares[thisSquare] : charEmpty; | |
var exp = exposed[thisSquare]; | |
var adj = adjacent[thisSquare]; | |
if (exp == exploded) { | |
sq = charMine; | |
} else if (exp == incorrect) { | |
sq = charIncorrect; | |
} else if (adj == mine) { | |
sq = charMine; | |
} | |
squares[thisSquare] = sq; | |
} | |
function applyToNeighbours(thisSquare, f) { | |
var x = thisSquare % width; | |
if (thisSquare >= width) { // there's a row above | |
if (x > 0) f(thisSquare - width - 1); | |
f(thisSquare - width); | |
if (x+1 < width) f(thisSquare - width + 1); | |
} | |
if (x > 0) f(thisSquare - 1); | |
if (x+1 < width) f(thisSquare + 1); | |
if (thisSquare < total-width) { // there's a row below | |
if (x > 0) f(thisSquare + width - 1); | |
f(thisSquare + width); | |
if (x+1 < width) f(thisSquare + width + 1); | |
} | |
} | |
var tail = listEnd; | |
function expose1(thisSquare) { | |
// Expose square and add to pending exposure list. | |
if (exposed[thisSquare] <= unexposed && | |
exposed[thisSquare] != flagged) { | |
remaining--; | |
exposed[thisSquare] = listEnd; | |
exposed[tail] = thisSquare; | |
tail = thisSquare; | |
setSq(thisSquare); | |
} | |
} | |
function clickSq(event, thisSquare) { | |
if (!event) event = window.event; // IE versus the rest | |
if (!timer) startTimer(); | |
if (exposed[thisSquare] > unexposed) { | |
// already exposed: do nothing | |
} else if (!event.which && event.button == 0) { | |
// mouse-up after right-click on IE: do nothing | |
} else if (event.shiftKey || event.button == 2) { | |
// flag or unflag | |
var exp = exposed[thisSquare]; | |
if (exp == unexposed) { | |
exposed[thisSquare] = flagged; | |
} else if (exp == flagged) { | |
exposed[thisSquare] = queried; | |
} else if (exp == queried) { | |
exposed[thisSquare] = unexposed; | |
} | |
setSq(thisSquare); | |
} else if (adjacent[thisSquare] == mine) { | |
// exposing a mine: explode it and expose other mines | |
remaining--; | |
exposed[thisSquare] = exploded; | |
setSq(thisSquare); | |
var i; | |
for (i = 0; i < total; i++) { | |
if (i==thisSquare) { | |
} else if (adjacent[i] == mine && exposed[i] != flagged) { | |
remaining--; | |
exposed[i] = listEnd; | |
setSq(i); | |
} else if (adjacent[i] != mine && exposed[i] == flagged) { | |
remaining--; | |
exposed[i] = incorrect; | |
setSq(i); | |
} | |
} | |
} else { | |
// expose the square, if not already exposed | |
// If square has 0 adjacency, expose surrounding squares, | |
// and iterate | |
remaining--; | |
exposed[thisSquare] = listEnd; | |
tail = thisSquare; | |
setSq(thisSquare); | |
var pending = thisSquare; | |
// Until pending reaches the end of the exposure list, expose | |
// neighbors | |
while (pending != listEnd) { | |
if (adjacent[pending]==0) applyToNeighbours(pending, expose1); | |
pending = exposed[pending]; | |
} | |
if (remaining==mines) { | |
// End of game: flag all remaining unflagged mines | |
var i; | |
for (i = 0; i < total; i++) { | |
if (adjacent[i] == mine && exposed[i] <= unexposed && | |
exposed[i] != flagged ) { | |
exposed[i] = flagged; | |
setSq(i); | |
} | |
} | |
} | |
} | |
return false; | |
} | |
function neighbourIsMine(thisSquare) { | |
// Increase adjacency count, if this isn't itself a mine | |
if (adjacent[thisSquare] != mine) { | |
adjacent[thisSquare]++; | |
squares[thisSquare] = `||:${numbers[adjacent[thisSquare]]}:||`; | |
} | |
} | |
function layMines() { | |
// Lay the mines | |
var laid = 0; | |
while (laid < mines) { | |
var target = Math.floor(Math.random() * total); | |
// Despite what others might say, it's possible that "target | |
// = total". This is because although Math.random() is < 1, | |
// in an extreme case the multiplication by "total" will round up. | |
// We need to allow for this, if we really care about correctness. | |
if (target < total && adjacent[target] != mine) { | |
adjacent[target] = mine; | |
squares[target] = charMine; | |
applyToNeighbours(target, neighbourIsMine); | |
laid++; | |
} | |
} | |
} | |
function eraseRows() { | |
// erase square contents | |
var i; | |
for (i = 0; i < total; i++) { | |
adjacent[i] = 0; | |
if (exposed[i] != unexposed) { | |
exposed[i] = unexposed; | |
setSq(i); | |
} | |
} | |
} | |
function erase() { | |
eraseRows(); | |
layMines(); | |
return false; | |
} | |
function init(w, t, m) { | |
width = w; | |
total = t; | |
mines = m; | |
var i; | |
for (i = 0; i < total; i++) { | |
squares[i] = charEmpty; | |
} | |
erase(); | |
let output = ''; | |
let c2 = 0; | |
for (let c = 0; c < total; c++) { | |
c2++; | |
output += squares[c]; | |
if (c2 == width) { | |
output += '\n'; | |
c2 = 0; | |
} | |
} | |
console.log(output); | |
} | |
//init(10, 100, 12); | |
// width 10, tiles 10^2, bombs 12 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment