Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Improved guessinging and interface
  • Loading branch information
timjb committed Apr 25, 2012
1 parent 7a9c1d1 commit 4663091
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 46 deletions.
76 changes: 57 additions & 19 deletions app.js
@@ -1,30 +1,68 @@
function main () {
var field = new MineSweeper.Field(16, 16, 40);
var solver = new MineSweeper.Solver(field);
solver.onGuess = function () {
console.log("Guess");
};

/*
function loop () {
setTimeout(function () {
solver.step();
render();
if (field.left > 0) { loop(); }
}, 200);
var WIDTH = 16, HEIGHT = 16, MINES = 40;
var field, solver, lost;

function byId (id) {
return document.getElementById(id);
}
*/

var fieldEl = document.getElementById('field');
var fieldEl = byId('field');
var statusEl = byId('status');

function reset () {
field = new MineSweeper.Field(WIDTH, HEIGHT, MINES);
solver = new MineSweeper.Solver(field);
lost = null;
solver.guess = function () {
statusEl.innerHTML += "Guessing … ";
MineSweeper.Solver.prototype.guess.apply(this);
};
field.blowUp = function (x, y) {
lost = [x, y];
MineSweeper.Solver.prototype.blowUp.apply(this);
};
statusEl.innerHTML = "Ready!";
render();
}
function render() {
fieldEl.innerHTML = solver.render();
if (lost) {
var tds = fieldEl.getElementsByTagName('td');
var td = tds[field.getIndex(lost[0], lost[1])];
td.className = 'mine';
}
}

function step () {
if (!lost && field.left > 0) {
wrap(function () { solver.step(); });
}
}

function run () {
if (!lost) {
wrap(function () { solver.solve(); });
}
}

function wrap (fn) {
statusEl.innerHTML = '';
try { fn(); } catch (exc) {}
if (lost) {
statusEl.innerHTML += "BOOOM!";
} else if (field.left === 0) {
statusEl.innerHTML += "Field cleared!";
} else {
statusEl.innerHTML += field.left + " cells left.";
}
render();
}

var stepBtn = document.getElementById('step-btn');
stepBtn.onclick = function () { solver.step(); render(); };
byId('step-btn').onclick = step;
byId('run-btn').onclick = run;
byId('reset-btn').onclick = reset;

render();
//loop();
reset();
}

main();
7 changes: 6 additions & 1 deletion index.html
Expand Up @@ -10,8 +10,13 @@
<body>
<div id="wrapper">
<h1>Minesweeper Solver</h1>
<input id="step-btn" type="button" value="Step" />
<div id="field"></div>
<p id="status"></p>
<div id="toolbar">
<input type="button" id="step-btn" value="Step" />
<input type="button" id="run-btn" value="Run" />
<input type="button" id="reset-btn" value="Reset" />
</div>
</div>
<script src="solver.js"></script>
<script src="app.js"></script>
Expand Down
73 changes: 53 additions & 20 deletions solver.js
Expand Up @@ -11,6 +11,10 @@ var MineSweeper = (function () {
return Math.floor(Math.random() * n);
}

function randomElement (arr) {
return arr[randomInt(arr.length)];
}

function range (s, e) {
var arr = [];
while (s <= e) { arr.push(s++); }
Expand Down Expand Up @@ -64,21 +68,25 @@ var MineSweeper = (function () {
return this.covered[this.getIndex(x, y)];
};

Field.prototype.blowUp = function (x, y) {
throw new Error("KAAAWUMMMMM!");
};

Field.prototype.uncover = function (x, y) {
var self = this;

function isMine(x, y) { return self.mines[self.getIndex(x, y)]; }

if (isMine(x, y)) {
return this.blowUp(x, y);
}

var i = this.getIndex(x, y);
if (this.covered[i]) {
this.covered[i] = false;
this.left--;
}

if (isMine(x, y)) {
throw new Error("KAAAWUMMMMM!");
}

var count = 0;
forEachNeighbor(x, y, this.width, this.height, function (x, y) {
if (isMine(x, y)) { count++; }
Expand All @@ -101,8 +109,6 @@ var MineSweeper = (function () {
this.createConstraint(field.minesCount, range(0, n-1));
}

Solver.prototype.onGuess = function () {};

function Constraint (mines, cells) {
this.mines = mines;
this.cells = cells;
Expand Down Expand Up @@ -225,24 +231,46 @@ var MineSweeper = (function () {
this.createConstraint(mines, neighbors, mines === 0);
};

Solver.prototype.randomCell = function () {
this.onGuess();
var x, y;
do {
x = randomInt(this.width);
y = randomInt(this.height);
} while (!this.field.isCovered(x, y));
return [x, y];
Solver.prototype.getProbability = function (x, y) {
var constraints = this.cells[this.field.getIndex(x, y)];
var p = 0;
for (var i = 0, l = constraints.length; i < l; i++) {
var constraint = constraints[i];
p = Math.max(p, constraint.mines / constraint.cells.length);
}
return p;
};

Solver.prototype.guess = function () {
var best = [], bestP = 1;
for (var x = 0; x < this.width; x++) {
for (var y = 0; y < this.height; y++) {
if (this.field.isCovered(x, y)) {
var p = this.getProbability(x, y);
if (p === bestP) {
best.push([x, y]);
} else if (p < bestP) {
bestP = p;
best = [[x, y]];
}
}
}
}
var pos = randomElement(best);
this.uncover(pos[0], pos[1]);
};

Solver.prototype.fromIndex = function (i) {
return [i % this.width, Math.floor(i/this.width)];
};

Solver.prototype.step = function () {
var pos = this.frontier.length > 0 ? this.fromIndex(this.frontier.shift())
: this.randomCell();
this.uncover(pos[0], pos[1]);
if (this.frontier.length > 0) {
var pos = this.fromIndex(this.frontier.shift());
this.uncover(pos[0], pos[1]);
} else {
this.guess();
}
};

Solver.prototype.solve = function () {
Expand All @@ -265,11 +293,16 @@ var MineSweeper = (function () {
for (var y = 0; y < this.height; y++) {
html += '<tr>';
for (var x = 0; x < this.width; x++) {
html += '<td>';
if (this.frontier.indexOf(this.field.getIndex(x, y)) !== -1) {
html += '<td class="frontier">';
} else {
html += '<td>';
}
if (!this.field.isCovered(x, y)) {
html += this.field.uncover(x, y);
var mines = this.field.uncover(x, y);
html += '<span class="mines-' + mines + '">' + mines + '</span>';
} else if (isFlagged(x, y)) {
html += '<span class="flag">M</span>';
html += '<span class="flag">F</span>';
}
html += '</td>';
}
Expand Down
41 changes: 35 additions & 6 deletions style.css
Expand Up @@ -2,23 +2,23 @@

body {
font: 16px Georgia, serif;
background: #eee;
}

#wrapper {
background: #fff;
width: 500px;
padding: 20px;
margin: 20px auto;
margin: 40px auto;
}

h1 {
font-size: 48px; font-weight: normal;
font-size: 48px; line-height: 64px; font-weight: normal;
}

h1, #field, #status, #toolbar {
margin: 0 0 20px;
text-align: center;
}

#field table {
font-family: monospace; font-weight: bold;
margin: 0 auto;
border-collapse: collapse;
}
Expand All @@ -30,6 +30,35 @@ h1 {
line-height: 24px; text-align: center;
}

td.mine {
background: #000;
}
td.frontier {
background: #fee;
}

td .flag {
font-weight: bold; color: #ffff00;
}
.mines-0 { color: #999; }
.mines-1 { color: #55a; }
.mines-2 { color: #5a5; }
.mines-3 { color: #a55;}
.mines-4 { color: #338; }
.mines-5 { color: #383;}
.mines-6, .mines-7, .mines-8 { color: #833; }

#toolbar {
margin: 0 0 20px;
font-size: 15px;;
}

#toolbar input {
margin-right: 8px;
padding: 4px 6px;
}

#status {
font: 14px Helvetica, Arial, sans-serif;
color: #888;
}

0 comments on commit 4663091

Please sign in to comment.