Skip to content

Commit 4185955

Browse files
authored
Merge pull request #2 from Tuggster/solver
Add guessless
2 parents 7b693cd + 71bb3a1 commit 4185955

7 files changed

Lines changed: 250 additions & 40 deletions

File tree

config.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ LOCAL_STORAGE_SHORTCUTS_REVEAL_KEY = "ms_shortcuts_reveal_enabled";
66
LOCAL_STORAGE_SHORTCUTS_FLAG_KEY = "ms_shortcuts_flag_enabled";
77
LOCAL_STORAGE_COLOR_KEY = "ms_color_enabled";
88
LOCAL_STORAGE_MARKS_KEY = "ms_marks_enabled";
9+
LOCAL_STORAGE_GUESS_FREE_KEY = "ms_guess_free";
10+
11+
LEADERBOARD_ENABLED = false;
912

1013
// name
1114
// onChange ==> (state) => ...
@@ -86,7 +89,6 @@ const preferences = [
8689
lsKey: LOCAL_STORAGE_SHORTCUTS_FLAG_KEY,
8790
value: false,
8891
},
89-
9092
{
9193
name: "marks",
9294
onChange: (state) => {
@@ -112,6 +114,18 @@ const preferences = [
112114
lsKey: LOCAL_STORAGE_COLOR_KEY,
113115
value: true,
114116
},
117+
{
118+
name: "guessfree",
119+
onChange: (state) => {
120+
handleRadioUpdate("guessfree", state);
121+
newGame();
122+
},
123+
onLsLoad: (value) => {
124+
return value === "true";
125+
},
126+
lsKey: LOCAL_STORAGE_GUESS_FREE_KEY,
127+
value: false,
128+
},
115129
];
116130

117131
document.addEventListener("DOMContentLoaded", () => {

contextmenucontroller.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ window.addEventListener("load", function () {
2828
setPreferenceValue("marks", selected);
2929
break;
3030
}
31+
case "guessfree": {
32+
setPreferenceValue("guessfree", selected);
33+
break;
34+
}
3135
}
3236
};
3337

game.js

Lines changed: 90 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,20 @@ let won = false;
1313
let buttonX, buttonY, buttonW;
1414

1515
class MinesweeperRound {
16-
constructor(width, height, mines) {
16+
constructor(width, height, mines, guessFree) {
1717
this.mines = mines;
1818
this.flagged = 0;
1919
this.width = width;
2020
this.height = height;
2121
this.hasStart = false;
22+
this.guessFree = guessFree;
2223
this.gameTimer = 0;
2324
gameOver = false;
2425
won = false;
2526

27+
this.startX = 0;
28+
this.startY = 0;
29+
2630
this.grid = Array();
2731
this.initGame();
2832
}
@@ -85,23 +89,57 @@ class MinesweeperRound {
8589
}
8690

8791
reserveStart(sX, sY) {
88-
for (let x = -startReserved; x <= startReserved; x++) {
89-
for (let y = -startReserved; y <= startReserved; y++) {
90-
let gX = sX + x;
91-
let gY = sY + y;
92-
if (gX < 0 || gX >= game.width || gY < 0 || gY >= game.height) {
93-
continue;
94-
}
95-
this.grid[gX + gY * this.width].reserved = true;
96-
}
97-
}
92+
clearStartingArea(this.grid, this.width, sX, sY);
9893
this.hasStart = true;
94+
this.startX = sX;
95+
this.startY = sY;
9996
}
10097

101-
revealCell(x, y) {
98+
applyGame = (grid) => {
99+
this.grid = grid.map((cell) => {
100+
return new Cell(cell.x, cell.y, cell.mine);
101+
});
102+
};
103+
104+
startGame = (x, y) => {
105+
clearStartingArea(this.grid, this.width, x, y);
106+
seedBoard(this.mines, this.grid);
107+
this.reserveStart(x, y);
108+
};
109+
110+
startGameWithGuessless = async (x, y) => {
111+
let success = false;
112+
113+
let testGrid = Array();
114+
let attempts = 0;
115+
while (!success) {
116+
testGrid = Array(this.grid.length);
117+
for (let i = 0; i < this.width * this.height; i++) {
118+
testGrid[i] = new Cell(i % this.width, Math.floor(i / this.width), false);
119+
}
120+
121+
clearStartingArea(testGrid, this.width, x, y);
122+
seedBoard(this.mines, testGrid);
123+
const results = await validateBoardFromStart(testGrid, this.width, this.height, this.startX, this.startY);
124+
success = results.success;
125+
attempts++;
126+
}
127+
128+
console.log(`Found guess-free board after ${attempts} attempts`);
129+
this.guessFree = true;
130+
131+
this.applyGame(testGrid);
132+
this.reserveStart(x, y);
133+
calculateNumbers(this.grid);
134+
};
135+
136+
async revealCell(x, y) {
102137
if (!this.hasStart) {
103-
this.reserveStart(x, y);
104-
this.seedGame();
138+
if (this.guessFree) {
139+
await this.startGameWithGuessless(x, y);
140+
} else {
141+
this.startGame(x, y);
142+
}
105143
}
106144
let gCell = this.grid[x + y * this.width];
107145
if (!gCell.flagged) {
@@ -322,27 +360,6 @@ class MinesweeperRound {
322360
}
323361
}
324362

325-
seedGame() {
326-
let rejects = 0;
327-
for (let i = 0; i < this.mines; 0) {
328-
let mI = Math.floor(Math.random() * this.grid.length);
329-
330-
if (this.grid[mI].count >= 0 && !this.grid[mI].reserved && !this.grid[mI].mine) {
331-
this.grid[mI].mine = true;
332-
i++;
333-
} else {
334-
rejects++;
335-
continue;
336-
}
337-
338-
if (rejects >= this.mines * 5) break;
339-
}
340-
341-
for (let i = 0; i < this.grid.length; i++) {
342-
this.grid[i].calculateNearby();
343-
}
344-
}
345-
346363
drawGame() {
347364
for (let x = 0; x < this.width; x++) {
348365
for (let y = 0; y < this.height; y++) {
@@ -361,3 +378,41 @@ function isMobile() {
361378
return false;
362379
}
363380
}
381+
382+
const clearStartingArea = (board, width, sX, sY) => {
383+
for (let x = -startReserved; x <= startReserved; x++) {
384+
for (let y = -startReserved; y <= startReserved; y++) {
385+
let gX = sX + x;
386+
let gY = sY + y;
387+
if (gX < 0 || gX >= game.width || gY < 0 || gY >= game.height) {
388+
continue;
389+
}
390+
board[gX + gY * width].reserved = true;
391+
}
392+
}
393+
};
394+
395+
const calculateNumbers = (grid) => {
396+
for (let i = 0; i < grid.length; i++) {
397+
grid[i].calculateNearby();
398+
}
399+
};
400+
401+
const seedBoard = (mines, grid) => {
402+
let rejects = 0;
403+
for (let i = 0; i < mines; 0) {
404+
let mI = Math.floor(Math.random() * grid.length);
405+
406+
if (grid[mI].count >= 0 && !grid[mI].reserved && !grid[mI].mine) {
407+
grid[mI].mine = true;
408+
i++;
409+
} else {
410+
rejects++;
411+
continue;
412+
}
413+
414+
if (rejects >= mines * 5) break;
415+
}
416+
417+
calculateNumbers(grid);
418+
};

index.html

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
<script src="md5.js"></script>
8686
<script src="leaderboard.js"></script>
8787
<script src="config.js"></script>
88+
<script src="solver.js"></script>
8889

8990
<!-- <div class="btn-container" style="width:100%">
9091
<img src="../imgs/face1.png" alt="" id="resetButton" class="vertical-center play-button" onclick="newGame()">
@@ -199,6 +200,10 @@ <h2>Mines:</h2>
199200
<hr />
200201
<p onclick="getLeaderboardForDifficulty(true)">Best Times...</p>
201202
<hr />
203+
<div class="cmenu-radio optional">
204+
<p id="guessfree" class="selected">Ensure no guessing</p>
205+
</div>
206+
<hr />
202207
<p>Exit</p>
203208
</div>
204209

@@ -262,6 +267,8 @@ <h2>Mines:</h2>
262267
canvasHTML = document.getElementById("defaultCanvas0"); //createCanvas(windowWidth - border*2, windowHeight - border*2);
263268
ellipseMode(CENTER);
264269

270+
const guessFree = localStorage.getItem("LOCAL_STORAGE_GUESS_FREE_KEY") === "true";
271+
265272
let context = canvasElement.getContext("2d");
266273
context.mozImageSmoothingEnabled = false;
267274
context.webkitImageSmoothingEnabled = false;
@@ -278,7 +285,7 @@ <h2>Mines:</h2>
278285
bombColors.push(color(128, 128, 128));
279286
screenWidth = windowWidth - border * 2;
280287
screenHeight = windowHeight - border * 2 - headerSize;
281-
game = new MinesweeperRound(rows, cols, mines);
288+
game = new MinesweeperRound(rows, cols, mines, guessFree);
282289
setCanvasSize(cellSize * game.width + border * 2, cellSize * game.height + headerSize + border * 2);
283290
touch = isMobile();
284291
}
@@ -525,7 +532,10 @@ <h2>Mines:</h2>
525532
let windowGrabberHeight = document.querySelector(".window-header").getBoundingClientRect().height;
526533
screenWidth = windowWidth - border * 2;
527534
screenHeight = windowHeight - border * 2 - headerSize - 50;
528-
game = new MinesweeperRound(rows, cols, mines);
535+
536+
const guessFree = getPreferenceValue("guessfree");
537+
538+
game = new MinesweeperRound(rows, cols, mines, guessFree);
529539
setCanvasSize(cellSize * game.width + border * 2, cellSize * game.height + headerSize + border * 2);
530540
getLeaderboardForDifficulty(false);
531541
}

leaderboard.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const getCollapsed = () => {
1010
return this.localStorage.getItem(LOCAL_STORAGE_COLLAPSE_KEY) === "true";
1111
};
1212

13-
const createDifficultyHash = () => MD5(`${game.width},${game.height},${game.mines}`);
13+
const createDifficultyHash = () => MD5(`${game.width},${game.height},${game.mines}${game.guessFree ? "noguess" : ""}`);
1414

1515
const getLeaderboardForDifficulty = async (shouldShow) => {
1616
const difficultyHash = createDifficultyHash();
@@ -57,6 +57,10 @@ const getLeaderboardName = () => {
5757
};
5858

5959
const uploadToLeaderboard = async () => {
60+
if (!LEADERBOARD_ENABLED) {
61+
throw new Error("leaderboard disabled");
62+
}
63+
6064
const username = getLeaderboardName();
6165
const difficultyHash = createDifficultyHash();
6266
const time = game.gameTimer / 1000;

0 commit comments

Comments
 (0)