Minesweeper.2.0
Материал из Department of Theoretical and Applied Mechanics
Описание[править]
Реализация компьютерной игры "Сапер" на языке JavaScript.
Исполнители: Капитонова Анна Кочикян Анастасия
Группа 3630103/00003 Высшая школа Теоретической механики
Визуализация[править]
Код программы[править]
Код программы на языке JavaScript файл 1:
1 function getMatrix (columns, rows) {//создаем матрицу
2 const matrix = [];
3
4 let idCounter = 1;//счетчик id
5 for (let y = 0; y < rows; y++) {//создание строк
6 const row = [];
7
8 for (let x = 0; x < columns; x++) {//создание ячеек
9 row.push({
10 id: idCounter++,//увеличение id
11 //присваеваем характеристики ячейкам
12 left: false,
13 right: false,
14 show: false,
15 flag: false,
16 mine: false,
17 potencial: false,
18 number: 0,
19 x,
20 y
21 })
22 }
23
24 matrix.push(row);
25 }
26
27 return matrix;
28 }
29
30 function getRandomFreeCell (matrix) {//находим пустую клетку
31 const freeCells = [];
32
33 for (let y = 0; y < matrix.length; y++) {
34 for (let x = 0; x < matrix[y].length; x++) {
35 const cell = matrix[y][x];//проверяем свободна ли клетка
36
37 if (!cell.mine) {//если да добавляем индек с в массив
38 freeCells.push(cell);
39 }
40 }
41 }
42
43 const index = Math.floor(Math.random() * freeCells.length);
44 return freeCells[index];
45 }
46
47 function setRandomMine (matrix) {//создаем мину в пустой клетке
48 const cell = getRandomFreeCell(matrix);
49 cell.mine = true;
50
51 const cells = getAroundCells(matrix, cell.x, cell.y);
52
53 for (const cell of cells) {
54 cell.number++;
55 }
56 }
57
58 function getCell (matrix, x, y) {//возвращаем клетку, если она есть
59 if(!matrix[y] || !matrix[y][x]) {
60 return false;
61 }
62
63 return matrix[y][x]
64 }
65
66 function getAroundCells (matrix, x, y) {//создаем массив клеток вокруг клетки с координатами х и у
67 const cells = [];
68
69 for (let dx = -1; dx <= 1; dx++) {
70 for (let dy = -1; dy <= 1; dy++) {
71 if (dx == 0 && dy == 0) {
72 continue;
73 }
74
75 const cell = getCell(matrix, x + dx, y + dy);
76
77 if (!cell) {
78 continue;
79 }
80
81 cells.push(cell);
82 }
83 }
84
85 return cells;
86 }
87
88 function getCellById (matrix, id) {
89 for (let y = 0; y < matrix.length; y++) {
90 for (let x = 0; x < matrix[y].length;x++) {
91 const cell = matrix[y][x]
92
93 if (cell.id === id) {
94 return cell;
95 }
96 }
97 }
98 return false;
99 }
100
101 function matrixToHtml (matrix) {//преобразуем матрицу в dom-дерево
102 const gameElement = document.createElement('div');//создаем основной элемент - сапер
103 gameElement.classList.add('sapper');
104
105 for (let y = 0; y < matrix.length; y++) {//создаем отдельный элемент для каждой строки внутри сапера
106 const rowElement = document.createElement('div');
107 rowElement.classList.add('row');
108
109 for (let x = 0; x < matrix[y].length; x++) {//создаем отдельный элемент по каждую клетку
110 const cell = matrix[y][x];
111 const imgElement = document.createElement('img');
112
113 imgElement.draggable = false;//запрещаем двигать картинки
114 imgElement.oncontextmenu = () => false;//запрещаем открывать контекстное меню
115 imgElement.setAttribute('data-cell-id', cell.id)//присваеваем id изображениям
116 rowElement.append(imgElement);
117
118 if (cell.flag) {//вставляем в клетку картинку в зависимости от содержимого
119 imgElement.src = '11.png';
120 continue;
121 }
122
123 if (cell.potencial) {
124 imgElement.src = '12.png';
125 continue;
126 }
127
128 if (!cell.show) {
129 imgElement.src = '10.png';
130 continue;
131 }
132
133 if (cell.mine) {
134 imgElement.src = '9.png';
135 continue;
136 }
137
138 if (cell.number) {
139 imgElement.src = cell.number + '.png';
140 continue;
141 }
142
143 imgElement.src = '0.png';
144
145 }
146
147 gameElement.append(rowElement);
148 }
149
150 return gameElement;
151 }
152
153 function forEach (matrix, handler) {
154 for (let y = 0; y < matrix.length; y++) {
155 for (let x = 0; x < matrix[y].length;x++) {
156 handler(matrix[y][x]);
157 }
158 }
159 }
160
161 function openSpace (matrix, x, y) {//открываем клетки где нет мин, флагов, и чисел по соседству с щелкнутой
162 const cell = getCell(matrix, x, y);
163
164 if (cell.flag || cell.number || cell.mine) {
165 return;
166 }
167
168 forEach(matrix, x => x._marked = false);
169
170 cell._marked = true;
171
172 let flag = true;
173 while (flag) {
174 flag = false;
175
176 for (let y = 0; y < matrix.length; y++) {
177 for (let x = 0; x < matrix[y].length;x++){
178 const cell = matrix[y][x];
179
180 if (!cell._marked || cell.number) {
181 continue;
182 }
183
184 const cells = getAroundCells(matrix, x, y);
185 for (const cell of cells) {
186 if (cell._marked) {
187 continue;
188 }
189
190 if (!cell.flag && !cell.mine) {
191 cell._marked = true;
192 flag = true;
193 }
194 }
195 }
196 }
197 }
198
199 forEach(matrix, x => {
200 if (x._marked) {
201 x.show = true;
202 }
203
204 delete x._marked});
205 }
206
207 function isWin (matrix) {//условия победы
208 const flags = [];
209 const mines = [];
210
211 forEach(matrix, cell => {
212 if (cell.flag) {
213 flags.push(cell);
214 }
215
216 if (cell.mine) {
217 mines.push(cell);
218 }
219 })
220
221 if (flags.length !== mines.length) {
222 return false;
223 }
224
225 for (const cell of mines) {
226 if (!cell.flag) {
227 return false;
228 }
229 }
230
231 for (let y = 0; y < matrix.length; y++) {
232 for (let x = 0; x < matrix[y].length;x++){
233 const cell = matrix[y][x];
234
235 if(!cell.mine && !cell.show) {
236 return false;
237 }
238 }
239
240 }
241
242 return true;
243
244 }
245
246 function isLoose (matrix) {//условия поражения
247 for (let y = 0; y < matrix.length; y++) {
248 for (let x = 0; x < matrix[y].length;x++){
249 const cell = matrix[y][x];
250
251 if(cell.mine && cell.show) {
252 return true;
253
254 }
255 }
256 }
257 }
Код программы на языке JavaScript файл 2:
1 function main() {
2 rest.onclick = function(){
3 min = 0;
4 hour = 0;
5 init()}
6 //Секундомер
7 //изначальные переменные
8 min = 0;
9 hour = 0;
10 //Оставляем вашу функцию
11 function init() {
12 sec = 0;
13 setInterval(tick, 1000);
14 }
15
16 //Основная функция tick()
17 function tick() {
18 if (running) {
19 sec++;
20 if (sec >= 60) { //задаем числовые параметры, меняющиеся по ходу работы программы
21 min++;
22 sec = sec - 60;
23 }
24 if (min >= 60) {
25 hour++;
26 min = min - 60;
27 }
28 if (sec < 10) { //Визуальное оформление
29 if (min < 10) {
30 if (hour < 10) {
31 document.getElementById('timer').innerHTML ='0' + hour + ':0' + min + ':0' + sec;
32 } else {
33 document.getElementById('timer').innerHTML = hour + ':0' + min + ':0' + sec;
34 }
35 } else {
36 if (hour < 10) {
37 document.getElementById('timer').innerHTML = '0' + hour + ':' + min + ':0' + sec;
38 } else {
39 document.getElementById('timer').innerHTML = hour + ':' + min + ':0' + sec;
40 }
41 }
42 } else {
43 if (min < 10) {
44 if (hour < 10) {
45 document.getElementById('timer').innerHTML = '0' + hour + ':0' + min + ':' + sec;
46 } else {
47 document.getElementById('timer').innerHTML = hour + ':0' + min + ':' + sec;
48 }
49 } else {
50 if (hour < 10) {
51 document.getElementById('timer').innerHTML = '0' + hour + ':' + min + ':' + sec;
52 } else {
53 document.getElementById('timer').innerHTML = hour + ':' + min + ':' + sec;
54 }
55 }
56 }
57
58 }
59 }
60
61 init();
62 }
63
64
65
66
67 let matrix = null;
68 let running = null;
69 let k = null;
70
71
72 let columns = 9;
73 let rows = 9;
74 let mines = 10
75
76 function rad()
77 {var rarr=document.getElementsByName('difficulty');
78 //задаем количество мин и размер поля в зависимости от сложности
79
80 if(rarr[0].checked){
81 columns = 9;
82 rows = 9;
83 mines = 10
84 }
85 if(rarr[1].checked){
86 //То выбран первый radio
87 columns = 16;
88 rows = 16;
89 mines = 40
90 }
91 if(rarr[2].checked){
92 columns = 16;
93 rows = 30;
94 mines = 99
95 }
96
97 }
98
99 init(columns, rows, mines);
100
101 document
102 .querySelector('.restart')
103 .addEventListener('click', () => init(columns, rows, mines));
104
105 function init (columns, rows, mines) {
106 rad();
107 matrix = getMatrix(columns, rows); //создаем матрицу
108 running = true;
109 for (let i = 0; i < mines; i++) { //расставляем мины
110 setRandomMine(matrix);
111 }
112
113 update();
114 }
115
116
117 let closeWin = document.querySelector('.close.youwin');
118 let win = document.querySelector('.win');
119
120 closeWin.addEventListener('click', function (evt) {//закрыть уведомление по клику
121 evt.preventDefault();
122 win.classList.toggle('appear');
123 });
124
125 let closeOver = document.querySelector('.close.over');
126 let gameover = document.querySelector('.gameover');
127
128 closeOver.addEventListener('click', function (evt) {//закрыть уведомление по клику
129 evt.preventDefault();
130 gameover.classList.toggle('appear');
131
132 });
133
134
135 function update () {//обновляем игровое поле
136 if (!running) {
137 return;
138 }
139
140 const gameElement = matrixToHtml(matrix);//создаем игровой элемент
141
142 const appElement = document.querySelector('#app');//добавляем его в app
143 appElement.innerHTML = '';
144 appElement.append(gameElement);
145
146 document.getElementsByClassName('gameover modal')[0].style.display = "none"//скрываем картинку о победе или поражении по умолчанию
147 document.getElementsByClassName('win modal')[0].style.display = "none"
148 appElement
149 .querySelectorAll('img')//получаем информацию о нажатиях мыши
150 .forEach(imgElement => {
151 imgElement.addEventListener('mousedown', mousedownHandler);
152 imgElement.addEventListener('mouseup', mouseupHandler);
153 imgElement.addEventListener('mouseleave', mouseleaveHandler)
154 });
155
156 if (isLoose(matrix)) {
157 let gameover = document.querySelector('.gameover');
158 gameover.classList.add('appear');
159 running = false;
160 document.getElementsByClassName('gameover modal')[0].style.display = "block"//проявляем изображение
161
162 }
163
164 else if (isWin(matrix)) {
165 document.getElementsByClassName('win modal')[0].style.display = "block" //проявляем изображение
166 let win = document.querySelector('.win');
167 win.classList.add('appear');
168 running = false;
169
170 }
171 }
172
173 function mousedownHandler (event) {
174 //получаем информацию о том, какая кнопка мыши нажата
175 const {cell, left, right } = getInfo(event);
176
177 if (left) {
178 cell.left = true;
179 }
180
181 if (right) {
182 cell.right = true;
183 }
184
185 if (cell.left && cell.right) {
186 bothHandler(cell);
187 }
188
189 update();
190 }
191
192 function mouseupHandler (event) {//информация о нажатии левой/правой/обеих кнопок мыши фиксируется
193 const {left, right, cell } = getInfo(event);
194
195 const both = cell.right && cell.left && (left || right);
196 const leftMouse = !both && cell.left && left;
197 const rightMouse = !both && cell.right && right;
198
199 if (both) {
200 forEach(matrix, x => x.potencial = false);
201 }
202
203 if (left) {
204 cell.left = false;
205 }
206
207 if (right) {
208 cell.right = false;
209 }
210
211 if (leftMouse) {
212 leftHandler(cell)
213 }
214
215 else if (rightMouse) {
216 rightHandler(cell)
217 }
218
219 update();
220 }
221
222 function mouseleaveHandler (event) {//возвращем информацию о то, что мышь больше не наведена на клетку
223 const info = getInfo(event);
224
225 info.cell.left = false;
226 info.cell.right = false;
227
228 update();
229 }
230
231 function getInfo (event) {//говорим что на мыши нажато и по какой клетке
232 const element = event.target;
233 const cellId = parseInt(element.getAttribute('data-cell-id'));
234
235 return {
236 left: event.which === 1,
237 right: event.which === 3,
238 cell: getCellById(matrix, cellId)
239 }
240 }
241
242 function leftHandler (cell) {
243 if (cell.show || cell.flag) {//открываем клетку нажатием левой кнопки мыши
244 return;
245 }
246 cell.show = true;
247
248 openSpace(matrix, cell.x, cell.y);
249 }
250
251 function rightHandler (cell) {//ставим в клетку флаг нажатием правой кнопки мыши
252 if (!cell.show) {
253 cell.flag = !cell.flag;
254 }
255 }
256
257 function bothHandler (cell) {//показываем где могут быть мины
258 if (!cell.show || !cell.number) {
259 return;
260 }
261
262 const cells = getAroundCells(matrix, cell.x, cell.y);
263 const flags = cells.filter(x => x.flag).length;
264
265 if (flags === cell.number) {//открываем поле вокруг если флагов вокруг столько сколько должно быть мин
266 cells
267 .filter(x => !x.flag && !x.show)
268 .forEach(cell => {
269 cell.show = true
270 openSpace(matrix, cell.x, cell.y)
271 })
272 }
273
274 else {//показываем где могут быть мины
275 cells
276 .filter(x => !x.flag && !x.show)
277 .forEach(cell => cell.potencial = true)
278 }
279 }