Шлычков курсовая
Материал из Department of Theoretical and Applied Mechanics
Игра "Жизнь" Джона Конвея.
Выполнено Шлычковым Никитой на языке программирования JavaScript.
Правила[править]
- Место действия этой игры — «вселенная» — это размеченная на клетки поверхность или плоскость — безграничная, ограниченная, или замкнутая (в пределе — бесконечная плоскость).
- Каждая клетка на этой поверхности может находиться в двух состояниях: быть «живой» (заполненной) или быть «мёртвой» (пустой). Клетка имеет восемь соседей, окружающих её.
- Распределение живых клеток в начале игры называется первым поколением. Каждое следующее поколение рассчитывается на основе предыдущего по таким правилам:
- в пустой (мёртвой) клетке, рядом с которой ровно три живые клетки, зарождается жизнь;
- если у живой клетки есть две или три живые соседки, то эта клетка продолжает жить; в противном случае, если соседей меньше двух или больше трёх, клетка умирает («от одиночества» или «от перенаселённости»)
"Вселенная" игры[править]
Код программы[править]
Код программы на языке JavaScript:
1 const directions = document.createElement('span');
2 const panel = document.getElementById('panel');
3 const birth = document.createElement('INPUT');
4 const survival = document.createElement('INPUT');
5 const directions2 = document.createElement('SPAN');
6 const submit_button = document.createElement('input');
7 const space = document.createElement('span');
8 const nextbutton = document.createElement('input');
9 const togglebutton = document.createElement('input');
10 const speedslide = document.createElement('input');
11 const button1 = document.createElement('input');
12 const button2 = document.createElement('input');
13 var birt = birth.value;
14 var surv = survival.value;
15 var birt_array = [];
16 var surv_array = [];
17 var meaninglessvariable = 0;
18 var birtl = 0;
19 var survl = 0;
20 const canvas = document.getElementById('canbas');
21 const ctx = canvas.getContext('2d');
22 const h = canvas.height;
23 const w = canvas.width;
24 const gridinterval = 800/40;
25 var cells_array = [];
26 var next_cells_array = [];
27 var mousex = 0;
28 var mousey = 0;
29 var neighbours = [];
30 ctx.fillStyle = 'black';
31 ctx.strokeStyle = 'grey';
32 var cellnum = w*h/Math.pow(gridinterval, 2);
33 var neighbours_alive = 0;
34 var auto = false;
35
36 var Cell = function(x, y, alive){
37 this.x = x;
38 this.y = y;
39 this.alive = alive;
40 }
41
42 var current_cell = new Cell(gridinterval/2, gridinterval/2, false);
43
44
45 window.addEventListener('load', createControlBar1, false);
46
47
48 function createControlBar1(){
49 panel.innerHTML = '';
50 console.log('Creating rules panel', performance.now());
51 panel.appendChild(directions);
52 directions.innerHTML = 'Введите условия рождения и выживания клетки: B';
53 panel.appendChild(birth);
54 birth.setAttribute('size', '5');
55 birth.setAttribute('maxlength', '8');
56 birth.setAttribute('value', '3');
57 panel.appendChild(directions2);
58 directions2.innerHTML = '/S';
59 panel.appendChild(survival);
60 survival.setAttribute('size', '5');
61 survival.setAttribute('maxlength', '8');
62 survival.setAttribute('value', '23');
63 panel.appendChild(space);
64 space.innerHTML = ' ';
65 panel.appendChild(submit_button);
66 submit_button.setAttribute('type', 'button');
67 submit_button.setAttribute('value', 'Подтвердить');
68 submit_button.setAttribute('onclick', 'readRules()');
69 console.log('Rules panel created', performance.now());
70 }
71
72 function readRules() {
73 if (birth.value != '' && survival.value != ''){
74 console.log('Setting rules', performance.now());
75 birtl = birth.value.length;
76 survl = survival.value.length;
77 birt = parseInt(birth.value);
78 surv = parseInt(survival.value);
79 console.log('b', birt, 's', surv);
80 getRules();
81 createCells();
82 createControlBar2();
83 drawgrid();
84 } else {
85 alert('Ты чёрт');
86 }
87 }
88
89 function getRules() {
90 for (i = 0; i<birtl; i++){
91 let meaninglessvariable = birt % 10;
92 if (birt_array.indexOf(meaninglessvariable, 0) == -1){
93 birt_array.push(meaninglessvariable);
94 console.log('Birth condition ', i, ': ', meaninglessvariable);
95 }
96 birt = parseInt((birt - meaninglessvariable)/10);
97 }
98 for (i = 0; i<survl; i++){
99 let meaninglessvariable = surv % 10;
100 if (surv_array.indexOf(meaninglessvariable, 0) == -1){
101 surv_array.push(meaninglessvariable);
102 console.log('Survival condition ', i, ': ', meaninglessvariable);
103 }
104 surv = parseInt((surv - meaninglessvariable)/10);
105 }
106 console.log('Rules set', performance.now());
107 }
108
109 function createControlBar2() {
110 panel.innerHTML = '';
111 canvas.setAttribute('onclick', 'toggleCell()');
112 panel.appendChild(button1);
113 button1.setAttribute('type', 'button');
114 button1.setAttribute('value', 'Restart');
115 button1.setAttribute('onclick', 'restart()');
116 panel.appendChild(button2);
117 button2.setAttribute('type', 'button');
118 button2.setAttribute('value', 'Generate');
119 button2.setAttribute('onclick', 'pregen()');
120 panel.appendChild(nextbutton);
121 nextbutton.setAttribute('type', 'button');
122 nextbutton.setAttribute('value', 'Next');
123 nextbutton.setAttribute('onclick', 'stepp()');
124 panel.appendChild(togglebutton);
125 togglebutton.setAttribute('type', 'button');
126 togglebutton.setAttribute('value', 'Start');
127 togglebutton.setAttribute('onclick', 'autostep()');
128 panel.appendChild(directions);
129 directions.innerHTML = ' Speed';
130 panel.appendChild(speedslide);
131 speedslide.setAttribute('type', 'range');
132 speedslide.setAttribute('min', '10');
133 speedslide.setAttribute('max', '500');
134 speedslide.setAttribute('value', '100');
135
136 }
137
138 function drawgrid() {
139 for (var i=0; i<w/gridinterval; i++){
140 ctx.beginPath();
141 ctx.moveTo(gridinterval*i, 0);
142 ctx.lineTo(gridinterval*i, h);
143 ctx.stroke();
144 ctx.beginPath();
145 ctx.moveTo(0, gridinterval*i);
146 ctx.lineTo(w, gridinterval*i);
147 ctx.stroke();
148 }
149 }
150
151 function createCells() {
152 cells_array = [];
153 console.log('Filling cells array', performance.now());
154 for (j=0; j<h/gridinterval; j++) {
155 for (i=0; i<w/gridinterval; i++){
156 let cell = new Cell((i+0.5)*gridinterval, (j+0.5)*gridinterval, false);
157 cells_array.push(cell);
158 }
159 }
160 for (j=0; j<h/gridinterval; j++) {
161 for (i=0; i<w/gridinterval; i++){
162 let cell = new Cell((i+0.5)*gridinterval, (j+0.5)*gridinterval, false);
163 next_cells_array.push(cell);
164 }
165 }
166 console.log('Cells array filled', performance.now());
167 }
168
169 function getMouseCoords() {
170 mousex = event.clientX - canvas.getBoundingClientRect().left;
171 mousey = event.clientY - canvas.getBoundingClientRect().top;
172 }
173
174 function toggleCell() {
175 getMouseCoords();
176 let cellx = (mousex - (mousex % gridinterval)) + gridinterval/2;
177 let celly = (mousey - (mousey % gridinterval)) + gridinterval/2;
178 let cell_num = (celly/gridinterval - 0.5)*w/gridinterval + (cellx/gridinterval - 0.5);
179 console.log('x', cellx, '(', mousex, ')', 'y', celly, '(', mousey, ') - ', cell_num);
180 if (cells_array[cell_num].alive) {
181 ctx.clearRect(cellx-gridinterval/2 + gridinterval/20, celly-gridinterval/2 + gridinterval/20, gridinterval - gridinterval/10, gridinterval - gridinterval/10)
182 cells_array[cell_num].alive = false;
183 console.log('Kill');
184 } else {
185 ctx.beginPath();
186 ctx.arc(cellx, celly, gridinterval*0.4, 0, 2*Math.PI);
187 ctx.fill();
188 cells_array[cell_num].alive = true;
189 console.log('Spare');
190 }
191 }
192
193 function life(a) {
194 neighbours = [];
195 neighbours_alive = 0;
196 if (cells_array[a].y == gridinterval/2) { //Верхняя строка
197 if (cells_array[a].x == gridinterval/2) {
198 neighbours.push(cells_array[cellnum-1]);
199 neighbours.push(cells_array[cellnum-w/gridinterval]);
200 neighbours.push(cells_array[cellnum-w/gridinterval+1]);
201 neighbours.push(cells_array[a+1]);
202 neighbours.push(cells_array[w/gridinterval+1]);
203 neighbours.push(cells_array[w/gridinterval]);
204 neighbours.push(cells_array[2*w/gridinterval-1]);
205 neighbours.push(cells_array[w/gridinterval-1]);
206 }
207 if (cells_array[a].x == w - gridinterval/2) {
208 neighbours.push(cells_array[cellnum-2]);
209 neighbours.push(cells_array[cellnum-1]);
210 neighbours.push(cells_array[cellnum-w/gridinterval]);
211 neighbours.push(cells_array[0]);
212 neighbours.push(cells_array[w/gridinterval]);
213 neighbours.push(cells_array[a+w/gridinterval]);
214 neighbours.push(cells_array[a+w/gridinterval-1]);
215 neighbours.push(cells_array[a-1]);
216 } else {
217 neighbours.push(cells_array[cellnum - w/gridinterval + a - 1]);
218 neighbours.push(cells_array[cellnum - w/gridinterval + a]);
219 neighbours.push(cells_array[cellnum - w/gridinterval + a + 1]);
220 neighbours.push(cells_array[a+1]);
221 neighbours.push(cells_array[a+w/gridinterval+1]);
222 neighbours.push(cells_array[a+w/gridinterval]);
223 neighbours.push(cells_array[a+w/gridinterval-1]);
224 neighbours.push(cells_array[a-1]);
225 }
226 }
227 if (cells_array[a].y == h - gridinterval/2) { //Нижняя строка
228 if (cells_array[a].x == gridinterval/2) { //Левый нижний
229 neighbours.push(cells_array[a-1]);
230 neighbours.push(cells_array[a-w/gridinterval]);
231 neighbours.push(cells_array[a-w/gridinterval+1]);
232 neighbours.push(cells_array[a+1]);
233 neighbours.push(cells_array[1]);
234 neighbours.push(cells_array[0]);
235 neighbours.push(cells_array[w/gridinterval-1]);
236 neighbours.push(cells_array[cellnum-1]);
237 }
238 if (cells_array[a].x == w - gridinterval/2) { //Правый нижний
239 neighbours.push(cells_array[a-w/gridinterval-1]);
240 neighbours.push(cells_array[a-w/gridinterval]);
241 neighbours.push(cells_array[a - 2*w/gridinterval + 1]);
242 neighbours.push(cells_array[a-w/gridinterval+1]);
243 neighbours.push(cells_array[0]);
244 neighbours.push(cells_array[w/gridinterval-1]);
245 neighbours.push(cells_array[w/gridinterval-2]);
246 neighbours.push(cells_array[a-1]);
247 } else { //Не угловая клетка
248 neighbours.push(cells_array[a-w/gridinterval-1]);
249 neighbours.push(cells_array[a-w/gridinterval]);
250 neighbours.push(cells_array[a-w/gridinterval+1]);
251 neighbours.push(cells_array[a+1]);
252 neighbours.push(cells_array[w/gridinterval-(cellnum-a)+1]);
253 neighbours.push(cells_array[w/gridinterval-(cellnum-a)]);
254 neighbours.push(cells_array[w/gridinterval-(cellnum-a)-1]);
255 neighbours.push(cells_array[a-1]);
256 }
257 }
258 if (cells_array[a].x == gridinterval/2) { //Левый столбец
259 neighbours.push(cells_array[a-1]);
260 neighbours.push(cells_array[a-w/gridinterval]);
261 neighbours.push(cells_array[a-w/gridinterval+1]);
262 neighbours.push(cells_array[a+1]);
263 neighbours.push(cells_array[a+w/gridinterval+1]);
264 neighbours.push(cells_array[a+w/gridinterval]);
265 neighbours.push(cells_array[a+2*w/gridinterval-1]);
266 neighbours.push(cells_array[a+w/gridinterval-1]);
267 }
268 if (cells_array[a].x == w - gridinterval/2) { //Правый столбец
269 neighbours.push(cells_array[a-w/gridinterval-1]);
270 neighbours.push(cells_array[a-w/gridinterval]);
271 neighbours.push(cells_array[a-2*w/gridinterval+1]);
272 neighbours.push(cells_array[a-w/gridinterval+1]);
273 neighbours.push(cells_array[a+1]);
274 neighbours.push(cells_array[a+w/gridinterval]);
275 neighbours.push(cells_array[a+w/gridinterval-1]);
276 neighbours.push(cells_array[a-1]);
277 } else {
278 neighbours.push(cells_array[a-w/gridinterval-1]);
279 neighbours.push(cells_array[a-w/gridinterval]);
280 neighbours.push(cells_array[a-w/gridinterval+1]);
281 neighbours.push(cells_array[a+1]);
282 neighbours.push(cells_array[a+w/gridinterval+1]);
283 neighbours.push(cells_array[a+w/gridinterval]);
284 neighbours.push(cells_array[a+w/gridinterval-1]);
285 neighbours.push(cells_array[a-1]);
286 }
287
288 for (it=0; it<8; it++) {
289 if (neighbours[it].alive) { neighbours_alive++ }
290 }
291
292 if (cells_array[a].alive){
293 if (surv_array.indexOf(neighbours_alive) == -1) {
294 next_cells_array[a].alive = false;
295 } else {
296 next_cells_array[a].alive = true;
297 }
298 }
299 if (cells_array[a].alive == false){
300 if (birt_array.indexOf(neighbours_alive) != -1){ next_cells_array[a].alive = true; }
301 }
302 }
303
304 function devMarkNeigh(a) {
305 life(a);
306 for (i=0; i<8; i++){
307 ctx.beginPath();
308 ctx.arc(neighbours[i].x, neighbours[i].y, 0.2*gridinterval, 0, 2*Math.PI);
309 ctx.fill();
310 }
311 }
312
313 function draw(a) {
314 if (next_cells_array[a].alive) {
315 ctx.beginPath();
316 ctx.arc(next_cells_array[a].x, next_cells_array[a].y, 0.4*gridinterval, 0, 2*Math.PI);
317 ctx.fill();
318 } else {
319 ctx.clearRect(next_cells_array[a].x-gridinterval/2 + gridinterval/20, next_cells_array[a].y-gridinterval/2 + gridinterval/20, gridinterval - gridinterval/10, gridinterval - gridinterval/10);
320 }
321 }
322
323 function change(a) {
324 cells_array[a].alive = next_cells_array[a].alive;
325 }
326
327 function stepp() {
328 for (i=0; i<cellnum; i++){
329 life(i);
330 }
331 for (i=0; i<cellnum; i++){
332 draw(i);
333 }
334 for (i=0; i<cellnum; i++){
335 change(i);
336 }
337 if (auto) {
338 clearInterval(fps);
339 fps = setInterval(stepp, 100*100/speedslide.value)
340 }
341 }
342
343 function autostep() {
344 if (auto) {
345 clearInterval(fps);
346 auto = false;
347 togglebutton.setAttribute('value', 'Start');
348 } else {
349 fps = setInterval(stepp, 100*100/speedslide.value)
350 auto = true;
351 togglebutton.setAttribute('value', 'Stop');
352 }
353 }
354
355 function pregen() {
356 for (i = 0; i<cellnum; i++) {
357 let v = Math.random();
358 if (v < 0.1) {
359 ctx.beginPath();
360 ctx.arc(cells_array[i].x, cells_array[i].y, gridinterval*0.4, 0, 2*Math.PI);
361 ctx.fill();
362 cells_array[i].alive = true;
363 }
364 }
365 }
366
367 function restart() {
368 clearInterval(fps);
369 auto = false;
370 ctx.clearRect(0, 0, w, h);
371 createControlBar1();
372 }
373
374 // Да, "Restart" звучит лучше, чем "Перезапуск", так что я позволил себе посреди программы поменять язык, простите