Browse Source

Initial version

master
Figg 4 years ago
commit
60e764bca7
7 changed files with 447 additions and 0 deletions
  1. 21
    0
      css/style.css
  2. 39
    0
      index.html
  3. 23
    0
      js/cell.js
  4. 8
    0
      js/game.js
  5. 106
    0
      js/grid.js
  6. 3
    0
      js/p5.min.js
  7. 247
    0
      js/sketch.js

+ 21
- 0
css/style.css View File

@@ -0,0 +1,21 @@
1
+body {
2
+	padding: 0;
3
+	margin: 0;
4
+}
5
+
6
+menu {
7
+	padding: 0;
8
+	margin: 5px;
9
+}
10
+
11
+menuitem {
12
+	margin-right: 10px;
13
+}
14
+
15
+menuitem label::after {
16
+	content: " :";
17
+}
18
+
19
+menuitem input {
20
+	max-width: 50px;
21
+}

+ 39
- 0
index.html View File

@@ -0,0 +1,39 @@
1
+<!DOCTYPE html>
2
+<html lang="">
3
+
4
+<head>
5
+	<meta charset="utf-8">
6
+	<meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+	<title>p5.js example</title>
8
+	<link href="./css/style.css" rel="stylesheet">
9
+	<script src="./js/p5.min.js"></script>
10
+	<script src="./js/cell.js"></script>
11
+	<script src="./js/grid.js"></script>
12
+	<script src="./js/sketch.js"></script>
13
+</head>
14
+
15
+<body>
16
+	<menu>
17
+		<menuitem>
18
+			<label>Columns</label>
19
+			<input type="number" size="5" id="columns" value="40">
20
+		</menuitem>
21
+		<menuitem>
22
+			<label>Rows</label>
23
+			<input type="number" size="5" id="rows" value="30">
24
+		</menuitem>
25
+		<menuitem>
26
+			<label>Mines</label>
27
+			<input type="number" size="5" id="mines" value="50">
28
+		</menuitem>
29
+		<menuitem>
30
+			<label>Cell size</label>
31
+			<input type="number" size="5" id="cellsize" value="20">
32
+		</menuitem>
33
+		<button type="button" onclick="newGame()">New game</button>
34
+	</menu>
35
+	<main oncontextmenu="return false;">
36
+	</main>
37
+</body>
38
+
39
+</html>

+ 23
- 0
js/cell.js View File

@@ -0,0 +1,23 @@
1
+class Cell {	
2
+	constructor(x, y, m) {
3
+		this.x = x;
4
+		this.y = y;
5
+		this.hasMine = m;
6
+		this.revealed = false;
7
+		this.flagged = false;
8
+		this.weight = -1;
9
+		this.neighbors = null;
10
+	}
11
+	
12
+	reveal () {
13
+		if (this.flagged) return;
14
+		
15
+		this.revealed = true;
16
+	}
17
+		
18
+	toggleFlag () {
19
+		if (this.revealed) return;
20
+		
21
+		this.flagged = !this.flagged;
22
+	}
23
+}

+ 8
- 0
js/game.js View File

@@ -0,0 +1,8 @@
1
+class Game {
2
+	const GAME_STATES = {
3
+		DEFEAT: 1,
4
+		VICTORY: 2,
5
+		ONGOING: 3,
6
+		STOPPED: 4
7
+	}
8
+}

+ 106
- 0
js/grid.js View File

@@ -0,0 +1,106 @@
1
+class Grid {
2
+	cols;
3
+	rows;
4
+	cells;
5
+	nbMines;
6
+	nbCells;
7
+	
8
+	constructor (cols, rows, nbMines) {
9
+		this.cols = cols;
10
+		this.rows = rows;
11
+		this.nbCells = rows * cols;
12
+		this.nbMines = nbMines;
13
+		
14
+		this.init();
15
+	}
16
+	
17
+	init () {
18
+		var remainingMines = this.nbMines;
19
+		var remainingCells = this.nbCells;
20
+		
21
+		this.cells = new Array(this.cols);
22
+		for (var i = 0; i < this.cols; i++) {
23
+			this.cells[i] = new Array(this.rows);
24
+			for (var j = 0; j < this.rows; j++) {
25
+				var setMine = Math.random() < (remainingMines / remainingCells);
26
+				
27
+				this.cells[i][j] = new Cell(i, j, setMine);
28
+				
29
+				if (setMine) {remainingMines--;}
30
+				remainingCells--;
31
+			}
32
+		}
33
+	}
34
+	
35
+	cellNeighbors(cell) {
36
+		if (cell.neighbors != null) return cell.neighbors;
37
+		
38
+		var neighbors = [];
39
+		var x = cell.x;
40
+		var y = cell.y;
41
+		
42
+		if (x > 0 && y > 0)                     neighbors.push(this.cell(x-1, y-1));
43
+		if (x > 0)                              neighbors.push(this.cell(x-1, y  ));
44
+		if (x > 0 && y < this.rows-1)           neighbors.push(this.cell(x-1, y+1));
45
+		if (y > 0)                              neighbors.push(this.cell(x  , y-1));
46
+		if (y < this.rows-1)                    neighbors.push(this.cell(x  , y+1));
47
+		if (x < this.cols-1 && y > 0)           neighbors.push(this.cell(x+1, y-1));
48
+		if (x < this.cols-1)                    neighbors.push(this.cell(x+1, y  ));
49
+		if (x < this.cols-1 && y < this.rows-1) neighbors.push(this.cell(x+1, y+1));
50
+		
51
+		cell.neighbors = neighbors;
52
+		return neighbors;
53
+	}
54
+	
55
+	computeCellWeight (cell) {
56
+		if (cell.weight >= 0) return cell.weight;
57
+		
58
+		var weight = 0;
59
+		var neighbors = this.cellNeighbors(cell);
60
+		
61
+		neighbors.forEach(neighbor => {
62
+			if (neighbor.hasMine) weight ++;
63
+		})
64
+		
65
+		cell.weight = weight;
66
+		return weight;
67
+	}
68
+	
69
+	revealCell (cell) {
70
+		if (cell.flagged || cell.revealed) return 0;
71
+		if (cell.hasMine) return -1;
72
+		
73
+		var nbRevealed = 1
74
+		
75
+		cell.weight = this.computeCellWeight(cell);
76
+		cell.reveal();
77
+		
78
+		if (cell.weight == 0) {
79
+			var neighbors = this.cellNeighbors(cell);
80
+			neighbors.forEach(neighbor => {
81
+				nbRevealed += this.revealCell(neighbor);
82
+			});
83
+		}
84
+		
85
+		return nbRevealed;
86
+	}
87
+	
88
+	cell (x, y) {
89
+		return this.cells[x][y];
90
+	}
91
+	
92
+	fullReveal () {
93
+		for (var i = 0; i < this.cols; i++) {
94
+			for (var j = 0; j < this.rows; j++) {
95
+				var cell = this.cell(i, j);
96
+				grid.computeCellWeight(cell);
97
+				cell.flagged = false;
98
+				cell.reveal();
99
+			}
100
+		}
101
+	}
102
+
103
+	inbounds(x, y) {
104
+		return x >= 0 && x < this.cols && y >= 0 && y < this.rows;
105
+	}
106
+}

+ 3
- 0
js/p5.min.js
File diff suppressed because it is too large
View File


+ 247
- 0
js/sketch.js View File

@@ -0,0 +1,247 @@
1
+var COLS;
2
+var ROWS;
3
+var MINES;
4
+var W;
5
+
6
+const GAME_STATE = {
7
+	DEFEAT:  1,
8
+	VICTORY: 2,
9
+	ONGOING: 3,
10
+	STOPPED: 4
11
+	}
12
+
13
+function setup() {
14
+	newGame();
15
+}
16
+
17
+function newGame() {
18
+	let params = gatherMenuValues();
19
+	
20
+	COLS = params.COLUMNS;
21
+	ROWS = params.ROWS;
22
+	MINES = params.MINES;
23
+	W = params.CELLSIZE;
24
+	console.log(COLS, ROWS, MINES);
25
+	this.grid = new Grid(COLS, ROWS, MINES);
26
+	this.nbCellsToWin = COLS * ROWS - MINES;
27
+	this.gamestate = GAME_STATE.ONGOING;
28
+	
29
+	createCanvas(COLS * W, ROWS * W);
30
+	//resizeCanvas(COLS * W, ROWS * W);
31
+	needUpdate = true;
32
+}
33
+
34
+function mousePressed(e) {
35
+	if (this.gamestate != GAME_STATE.ONGOING) return;
36
+	
37
+	let cellX = floor(mouseX / W);
38
+	let cellY = floor(mouseY / W);
39
+	
40
+	if (!this.grid.inbounds(cellX, cellY)) return;
41
+	
42
+	let clickedCell = this.grid.cell(cellX, cellY);
43
+	
44
+	if (mouseButton === LEFT) {
45
+		handleLeftClickAtCell(clickedCell);
46
+	}
47
+	
48
+	if (mouseButton === RIGHT) {
49
+		clickedCell.toggleFlag();
50
+	}
51
+	
52
+	needUpdate = true;
53
+	return false;
54
+}
55
+
56
+function gatherMenuValues() {
57
+	let menu_columns = parseInt(document.getElementById("columns").value);
58
+	let menu_rows = parseInt(document.getElementById("rows").value);
59
+	let menu_mines = parseInt(document.getElementById("mines").value);
60
+	let menu_cellsize = parseInt(document.getElementById("cellsize").value);
61
+	
62
+	return {
63
+		COLUMNS: menu_columns,
64
+		ROWS: menu_rows,
65
+		MINES: menu_mines,
66
+		CELLSIZE: menu_cellsize
67
+		};
68
+}
69
+
70
+function handleLeftClickAtCell(cell) {
71
+	let nbRevealed = this.grid.revealCell(cell);
72
+		
73
+	if (nbRevealed == -1) { // hit mine
74
+		defeat();
75
+		return;
76
+	}
77
+	
78
+	this.nbCellsToWin -= nbRevealed;
79
+	if (this.nbCellsToWin == 0) {
80
+		victory();
81
+	}
82
+}
83
+
84
+function victory() {
85
+	grid.fullReveal();
86
+	this.gamestate = GAME_STATE.VICTORY;
87
+}
88
+
89
+function defeat() {
90
+	grid.fullReveal();
91
+	this.gamestate = GAME_STATE.DEFEAT;
92
+}
93
+
94
+function displayMessage(message, color) {
95
+	textSize(W);
96
+	textAlign(CENTER, CENTER);
97
+	fill(200);
98
+	stroke(1);
99
+	strokeWeight(2);
100
+	
101
+	let textW = textWidth(message);
102
+	let textH = textAscent(message);
103
+	let padding = 10;
104
+	
105
+	rect (
106
+		width / 2 - padding,
107
+		height / 2 - textH / 2 - padding,
108
+		2 * padding + textW,
109
+		2 * padding + textH
110
+		);
111
+		
112
+	noFill();
113
+	noStroke();
114
+	fill(color);
115
+	text(message, width / 2 + textW / 2, height / 2);
116
+}
117
+
118
+function displayVictoryMessage() {
119
+	displayMessage('Victory !', 'green');
120
+}
121
+
122
+function displayDefeatMessage() {
123
+	displayMessage('Perdu !', 'red');
124
+}
125
+
126
+var needUpdate = true; // optimization trick
127
+function draw() {
128
+	if (!needUpdate) return;
129
+	background(0);
130
+	
131
+	drawGrid();
132
+	
133
+	if (this.gamestate == GAME_STATE.DEFEAT) displayDefeatMessage();
134
+	if (this.gamestate == GAME_STATE.VICTORY) displayVictoryMessage();
135
+	needUpdate = false;
136
+}
137
+
138
+function drawGrid() {
139
+	for (let i = 0; i < COLS; i++) {
140
+		for (let j = 0; j < ROWS; j++) {
141
+			drawCell(this.grid.cell(i, j));
142
+		}
143
+	}
144
+}
145
+
146
+function drawCell(cell) {
147
+	if (cell.revealed) {
148
+		drawRevealedCell(cell)
149
+	} else {
150
+		drawHiddenCell(cell);
151
+	}
152
+}
153
+
154
+function drawRevealedCell(cell) {
155
+	let drawX = cell.x * W;
156
+	let drawY = cell.y * W;
157
+	
158
+	fill(255);
159
+	strokeWeight(0);
160
+	
161
+	rect (drawX, drawY, W, W);
162
+	
163
+	if (cell.hasMine) {
164
+		drawMine(drawX, drawY, W);
165
+	}
166
+	
167
+	if (cell.weight > 0) {
168
+		drawCellMinesNumber(cell);
169
+	}
170
+}
171
+
172
+function drawCellMinesNumber(cell) {
173
+	let drawX = cell.x * W;
174
+	let drawY = cell.y * W;
175
+	
176
+	textAlign(CENTER, CENTER);
177
+	textSize(W / 2);
178
+	fill(0);
179
+		
180
+	text (cell.weight, drawX, drawY, W, W);
181
+}
182
+
183
+function drawHiddenCell(cell) {
184
+	let drawX = cell.x * W;
185
+	let drawY = cell.y * W;
186
+	
187
+	stroke(0);
188
+	strokeWeight(1);
189
+	fill(155);
190
+	
191
+	rect (drawX, drawY, W, W);
192
+	
193
+	if (cell.flagged) {
194
+		drawFlag(drawX, drawY, W);
195
+	}
196
+}
197
+
198
+function drawFlag(x, y, w) {
199
+	stroke(0);
200
+	strokeWeight(1);
201
+	
202
+	line (
203
+		x + w * 0.3,
204
+		y + w * 0.2,
205
+		x + w * 0.3,
206
+		y + w * 0.8,
207
+	);
208
+	
209
+	fill('red');
210
+	triangle(
211
+		x + w * 0.3,
212
+		y + w * 0.2,
213
+		x + w * 0.7,
214
+		y + w * 0.35,
215
+		x + w * 0.3,
216
+		y + w * 0.5
217
+	);
218
+}
219
+
220
+let sin60 = Math.sin(Math.PI / 3); // optimization
221
+function drawMine(x, y, w) {
222
+	let r = w * 0.4;
223
+	
224
+	stroke(0);
225
+	strokeWeight(1);
226
+	fill(0);
227
+	
228
+	circle (
229
+		r * 1.2 + x,
230
+		r * 1.2 + y,
231
+		r * 1.4
232
+	);
233
+	
234
+	triangle(
235
+		r * 1.7 + x, r * (sin60 + 1.2) + y,
236
+		r * 1.7 + x, r * (1.2 - sin60) + y,
237
+		r * 0.2 + x, r * 1.2 + y
238
+	);
239
+	
240
+	triangle (
241
+		r * 0.7 + x, r * (sin60 + 1.2) + y,
242
+		r * 0.7 + x, r * (1.2 - sin60) + y,
243
+		r * 2.2 + x, r * 1.2 + y
244
+	);
245
+	
246
+	
247
+}

Loading…
Cancel
Save