Wireworld
Материал из Department of Theoretical and Applied Mechanics
Описание[править]
Реализация клеточного автомата Wireworld на языке программирования JavaScript
Исполнитель: Волков Денис
Группа 3630103/90001
Визуализация[править]
Исходный код[править]
Код программы на языке 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();