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 }