Skip to content

Commit b4a7d25

Browse files
committed
Add 't' key to teach better moves when opening cells
1 parent ceab6a7 commit b4a7d25

File tree

2 files changed

+70
-18
lines changed

2 files changed

+70
-18
lines changed

src/game.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,13 @@ export class Game {
5050
return true;
5151
}
5252

53-
tryOpen(x: number, y: number): boolean {
53+
canOpen(x: number, y: number): boolean {
5454
const state = this.stateMap[y][x];
55-
if (state === 'revealed' || state === 'marked') return false;
55+
return state !== 'revealed' && state !== 'marked';
56+
}
57+
58+
tryOpen(x: number, y: number): boolean {
59+
if (!this.canOpen(x, y)) return false;
5660

5761
if (this.mineMap === null)
5862
this.initializeMines({ x, y });

src/renderer.ts

+64-16
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ export class Renderer {
1212
private heatmap: Heatmap | null = null;
1313
private drawBestMoves = false;
1414
private alwaysDrawBestMoves = false;
15+
private teachBestMoves = false;
16+
private autoFlag = false;
17+
private autoOpen = false;
1518

1619
constructor(game: Game, minefieldContainer: HTMLElement) {
1720
this.game = game;
@@ -93,8 +96,23 @@ export class Renderer {
9396
this.isMouseCaptured = false;
9497

9598
if (this.mouseDownCell) {
96-
if (this.game.tryOpen(this.mouseDownCell.x, this.mouseDownCell.y))
97-
this.afterMove();
99+
if (this.game.canOpen(this.mouseDownCell.x, this.mouseDownCell.y)) {
100+
let skipMove = false;
101+
102+
if (this.teachBestMoves && !this.drawBestMoves && this.heatmap !== null) {
103+
const loseLikelihood = this.heatmap.getBombLikelihood(this.mouseDownCell.x, this.mouseDownCell.y);
104+
if (loseLikelihood !== undefined && loseLikelihood > this.getBestMoveLoseLikelihood()!) {
105+
this.drawBestMoves = true;
106+
skipMove = true;
107+
}
108+
}
109+
110+
if (!skipMove) {
111+
this.game.tryOpen(this.mouseDownCell.x, this.mouseDownCell.y);
112+
this.afterMove();
113+
}
114+
}
115+
98116
this.mouseDownCell = null;
99117
this.render();
100118
}
@@ -112,39 +130,60 @@ export class Renderer {
112130
this.drawBestMoves = this.alwaysDrawBestMoves;
113131
this.render();
114132
break;
133+
case 't':
134+
this.teachBestMoves = !this.teachBestMoves;
135+
break;
136+
case 'a':
137+
this.autoFlag = !this.autoFlag;
138+
this.automate();
139+
break;
140+
case 'A':
141+
this.autoOpen = !this.autoOpen;
142+
this.automate();
143+
break;
115144
}
116145
}
117146

118147
private afterMove() {
119148
if (!this.alwaysDrawBestMoves) this.drawBestMoves = false;
120149

121-
const autoFlag = false;
122-
const autoOpen = false;
150+
this.heatmap = Heatmap.compute(this.game);
151+
this.automate();
152+
}
123153

124-
let anyCellOpened;
125-
do {
126-
anyCellOpened = false;
154+
private automate() {
155+
if (this.heatmap === null) return;
127156

128-
this.heatmap = Heatmap.compute(this.game);
157+
let needsRender = false;
129158

130-
if (autoFlag) {
159+
while (true) {
160+
let anyCellOpened = false;
161+
162+
if (this.autoFlag) {
131163
for (const candidate of this.heatmap.candidates) {
132164
if (candidate.bombLikelihood === 1) {
133165
this.game.tryToggleMark(candidate.x, candidate.y);
166+
needsRender = true;
134167
}
135168
}
136169
}
137170

138-
if (autoOpen) {
171+
if (this.autoOpen) {
139172
for (const candidate of this.heatmap.candidates) {
140173
if (candidate.bombLikelihood === 0) {
141174
this.game.tryOpen(candidate.x, candidate.y);
175+
needsRender = true;
142176
anyCellOpened = true;
143177
}
144178
}
145179
}
180+
181+
if (!anyCellOpened) break;
182+
183+
this.heatmap = Heatmap.compute(this.game);
146184
}
147-
while (anyCellOpened);
185+
186+
if (needsRender) this.render();
148187
}
149188

150189
private onDoubleClick(ev: MouseEvent) {
@@ -218,13 +257,11 @@ export class Renderer {
218257
let drawBestMovesAtLoseLikelihood: number | null = null;
219258

220259
if (this.heatmap !== null) {
221-
const fullCertainty = this.heatmap.candidates.some(c => c.bombLikelihood === 0 || c.bombLikelihood === 1);
222-
drawHeatmap = alwaysShowHeatmap || !fullCertainty;
260+
const bestMoveLoseLikelihood = this.getBestMoveLoseLikelihood()!;
261+
drawHeatmap = alwaysShowHeatmap || bestMoveLoseLikelihood > 0;
223262

224263
if (this.drawBestMoves && this.game.conclusion === null) {
225-
drawBestMovesAtLoseLikelihood = fullCertainty ? 0 : Math.min(
226-
...this.heatmap.candidates.map(c => c.bombLikelihood),
227-
...(this.heatmap.bombLikelihoodElsewhere !== undefined ? [this.heatmap.bombLikelihoodElsewhere] : []));
264+
drawBestMovesAtLoseLikelihood = bestMoveLoseLikelihood;
228265
}
229266
}
230267

@@ -235,6 +272,17 @@ export class Renderer {
235272
}
236273
}
237274

275+
private getBestMoveLoseLikelihood() {
276+
if (this.heatmap === null) return null;
277+
278+
if (this.heatmap.candidates.some(c => c.bombLikelihood === 0 || c.bombLikelihood === 1))
279+
return 0;
280+
281+
return Math.min(
282+
...this.heatmap.candidates.map(c => c.bombLikelihood),
283+
...(this.heatmap.bombLikelihoodElsewhere !== undefined ? [this.heatmap.bombLikelihoodElsewhere] : []));
284+
}
285+
238286
private drawCell(coords: CellCoords, cellBounds: Rectangle, cellSize: number, drawHeatmap: boolean, drawBestMovesAtLoseLikelihood: number | null) {
239287
const { x, y } = coords;
240288

0 commit comments

Comments
 (0)