Wireworld

Материал из Department of Theoretical and Applied Mechanics
Перейти к: навигация, поиск

Описание[править]

Реализация клеточного автомата Wireworld на языке программирования JavaScript

Исполнитель: Волков Денис


Группа 3630103/90001

Визуализация[править]

Исходный код[править]

GitHub

Код программы на языке JavaScript:
  1 const cnv = document.querySelector("#cnv");
  2 const ctx = cnv.getContext("2d");
  3 
  4 const wh = 50;
  5 
  6 const states = {
  7     wire: 1,
  8     head: 2,
  9     tail: 3,
 10     static: 4
 11 };
 12 
 13 const colors = (cell) => {
 14     switch (cell) {
 15         case 1:
 16             return "green";
 17         case 2:
 18             return "yellow";
 19         case 3:
 20             return "blue";
 21         case 4:
 22             return "black";
 23     }
 24 };
 25 
 26 let world = [];
 27 
 28 let isStarted = false;
 29 let selectedCell = states.wire;
 30 
 31 let wasLMBPressed = false;
 32 
 33 class Cell {
 34     constructor(x, y, state, neighbours) {
 35         this.x = x;
 36         this.y = y;
 37         this.state = state;
 38         this.newState = state;
 39         this.neighbours = neighbours;
 40     }
 41 }
 42 
 43 const tick = (cell) => {
 44     switch (cell.state) {
 45         case states.tail: {
 46             cell.newState = states.wire;
 47             break;
 48         }
 49         case states.head: {
 50             cell.newState = states.tail;
 51             break;
 52         }
 53         case states.wire: {
 54             let count = 0;
 55             cell.neighbours.forEach((item) => {
 56                 if (world[item].state === states.head) count++;
 57             })
 58             if ((count === 1) || (count === 2)) cell.newState = states.head
 59         }
 60     }
 61 };
 62 
 63 const worldTick = () => {
 64     world.forEach(cell => tick(cell));
 65     world.forEach(cell => {
 66         cell.state = cell.newState;
 67     })
 68 };
 69 
 70 const drawGrid = (x, y) => {
 71     ctx.fillStyle = "black";
 72     ctx.fillRect(0, 0, x, y);
 73 
 74     ctx.strokeStyle = "white";
 75 
 76     for (let i = 0; i < x / wh; i++) {
 77         for (let j = 0; j < y / wh; j++) {
 78             ctx.strokeRect(i * wh, j * wh, wh, wh);
 79         }
 80     }
 81     ctx.stroke();
 82 };
 83 
 84 const drawWorld = (world) => {
 85     world.forEach(cell => {
 86         ctx.fillStyle = colors(cell.state);
 87         ctx.fillRect(cell.x * wh, cell.y * wh, wh, wh);
 88     })
 89 };
 90 
 91 const redraw = () => {
 92     drawGrid(1000, 1000);
 93     drawWorld(world);
 94     requestAnimationFrame(redraw);
 95 };
 96 
 97 const updateNeighbours = () => {
 98     for (let i = 0; i < world.length; i++) {
 99         world[i].neighbours = [];
100         for (let j = 0; j < world.length; j++) {
101             if (i === j) continue;
102             if ((Math.abs(world[i].x - world[j].x) <= 1) && (Math.abs(world[i].y - world[j].y) <= 1)) {
103                 world[i].neighbours.push(j);
104             }
105         }
106     }
107 };
108 
109 
110 let timer = null;
111 
112 const start = () => {
113     if (isStarted) return;
114     isStarted = !isStarted;
115     timer = setInterval(worldTick, 100);
116 };
117 const stop = () => {
118     if (!isStarted) return;
119     isStarted = !isStarted;
120     clearTimeout(timer);
121 };
122 
123 //Обработчик кнопки
124 document.querySelector("#submit").onclick = (e) => {
125     const prevValue = e.target.value;
126 
127     switch (prevValue) {
128         case "Start": {
129             start();
130             e.target.value = "Stop";
131             break;
132         }
133         case "Stop": {
134             stop();
135             e.target.value = "Start";
136             break;
137         }
138     }
139 };
140 
141 
142 //Обработчик нажатий на Canvas
143 cnv.onmousemove = (e) => {
144     let rect;
145     if (wasLMBPressed) {
146 
147         rect = cnv.getBoundingClientRect();
148         const x = e.clientX - rect.left;
149         const y = e.clientY - rect.top;
150         const i = Math.trunc(x / wh);
151         const j = Math.trunc(y / wh);
152         if (selectedCell != states.static) {
153             const cell = new Cell(i, j, selectedCell, []);
154             let wasStated = false;
155             for (let k = 0; k < world.length; k++) {
156                 if ((world[k].x === cell.x) && (world[k].y === cell.y)) {
157                     world[k] = cell;
158                     wasStated = true;
159                     //updateNeighbours();
160                     break;
161                 }
162             }
163             if (!wasStated) {
164                 world.push(cell);
165                 //updateNeighbours();
166             }
167             redraw();
168         } else {
169             for (let k = 0; k < world.length; k++) {
170                 if ((world[k].x === i) && (world[k].y === j)) {
171                     world.splice(k, 1);
172                     //updateNeighbours();
173                     redraw();
174                 }
175             }
176         }
177     }
178 };
179 cnv.onmousedown = (e) => {
180     wasLMBPressed = true;
181     stop();
182     cnv.onmousemove(e)
183 }
184 cnv.onmouseup = () => {
185     wasLMBPressed = false;
186     updateNeighbours();
187 }
188 
189 
190 const drawInv = () => {
191     const ctxn = document.querySelector("#inv").getContext("2d");
192     ctxn.fillStyle = colors(states.wire);
193     ctxn.fillRect(0, 0, 100, 100);
194     ctxn.fillStyle = colors(states.tail);
195     ctxn.fillRect(100, 0, 100, 100);
196     ctxn.fillStyle = colors(states.head);
197     ctxn.fillRect(200, 0, 100, 100);
198     ctxn.fillStyle = colors(states.static);
199     ctxn.fillRect(300, 0, 100, 100);
200     ctxn.stroke();
201 
202     document.querySelector("#inv").onclick = (e) => {
203         const x = e.clientX;
204         if (x < 100) {
205             selectedCell = states.wire;
206             document.body.style.cursor = "url(green.png), auto";
207             return;
208         }
209         if (x < 200) {
210             selectedCell = states.tail;
211             document.body.style.cursor = "url(blue.png), auto";
212             return;
213         }
214         if (x < 300) {
215             selectedCell = states.head;
216             document.body.style.cursor = "url(yellow.png), auto";
217             return;
218         }
219         selectedCell = states.static;
220     }
221 };
222 
223 
224 const loadSave = () => {
225     const save = document.getElementById("name").value;
226     world = JSON.parse(save);
227     updateNeighbours();
228 };
229 
230 const saveWorld = () => {
231     const newWorld = world.slice();
232     for (let i = 0; i < newWorld.length; i++) {
233         delete newWorld[i].neighbours;
234         //delete newWorld[i].newState;
235     }
236     document.getElementById("name").value = JSON.stringify(world)
237 };
238 
239 document.getElementById("select").onchange = () => {
240     document.getElementById("name").value = document.getElementById("select").options[document.getElementById("select").selectedIndex].value;
241     loadSave();
242 };
243 
244 document.getElementById("saveButton").onclick = saveWorld;
245 document.getElementById("loadButton").onclick = loadSave;
246 requestAnimationFrame(redraw);
247 drawInv();