cgol0606.html 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  6. <title>conway</title>
  7. <style>
  8. body,
  9. html {
  10. margin: 0px;
  11. overflow: hidden;
  12. }
  13. .button {
  14. border: none;
  15. color: white;
  16. padding: 15px 32px;
  17. text-align: center;
  18. text-decoration: none;
  19. display: inline-block;
  20. font-size: 16px;
  21. margin: 4px 2px;
  22. cursor: pointer;
  23. }
  24. canvas {
  25. padding: 0;
  26. border: 0;
  27. background-color: white;
  28. image-rendering: pixelated;
  29. image-rendering: crisp;
  30. aspect-ratio: 1 / 1;
  31. position: relative;
  32. order : 0;
  33. }
  34. .controls {
  35. order : 1;
  36. vertical-align: middle;
  37. display: inline;
  38. border: 1px solid;
  39. position: relative;
  40. }
  41. .outer {
  42. top:0;
  43. right:0;
  44. left:0;
  45. justify-content: center;
  46. object-fit: contain;
  47. display: flexbox;
  48. background: gold;
  49. flex-wrap: wrap;
  50. align-items: center;
  51. width: 100%;
  52. height: auto;
  53. box-sizing: border-box;
  54. max-width: 100vw;
  55. max-height: 100vh;
  56. border: 1px dashed;
  57. }
  58. }
  59. </style>
  60. </head>
  61. <body>
  62. <div class="outer">
  63. <canvas class="canvas"></canvas>
  64. <div class="controls">
  65. <span id="debugbar">init</span>
  66. <span id="progressbar">init</span>
  67. <button type="button" id="clickme" onclick="evolve();rend()">
  68. Evolve!
  69. </button>
  70. <button type="button" id="clickme2" onclick="start_eloop();">
  71. start Evolve!
  72. </button>
  73. <button type="button" id="clickme3" onclick="pause_eloop();">
  74. pause Evolve!
  75. </button>
  76. <button type="button" id="clickme63" onclick="do_undo();">undo!</button>
  77. <button type="button" id="clickme4" onclick="bigger();rend();">
  78. bigger!
  79. </button>
  80. <textarea></textarea>
  81. <div>
  82. export <button onclick="export_copy();">(click to copy)</button>
  83. </div>
  84. </div>
  85. </div>
  86. <script>
  87. const dbg = document.getElementById("debugbar");
  88. dbg.innerText = "unlife";
  89. const canvas = document.querySelector("canvas");
  90. const ctx = canvas.getContext("2d");
  91. M = 30;
  92. N = 30;
  93. arr = new Uint8Array(M * N);
  94. arr2 = new Uint8Array(M * N);
  95. const evolve = () => {
  96. for (var y = 0; y < N; ++y) {
  97. for (var x = 0; x < M; ++x) {
  98. nei = [
  99. [-1, -1],
  100. [-1, 0],
  101. [-1, 1],
  102. [0, -1],
  103. [0, 1],
  104. [1, -1],
  105. [1, 0],
  106. [1, 1],
  107. ];
  108. arr2[M * y + x] = 0;
  109. nei.forEach(([a, b]) => {
  110. arr2[M * y + x] += arr[M * ((N + y + a) % N) + ((M + x + b) % M)];
  111. });
  112. arr2[M * y + x] = arr[M * y + x]
  113. ? arr2[M * y + x] == 3 || arr2[M * y + x] == 2
  114. : arr2[M * y + x] == 3;
  115. }
  116. }
  117. arr = [arr2, (arr2 = arr)][0];
  118. };
  119. const observer = new ResizeObserver(() => {
  120. canvas.width = canvas.parentElement.clientWidth;
  121. canvas.height = canvas.parentElement.clientHeight;
  122. mx = Math.min(canvas.height, canvas.width);
  123. canvas.width = mx;
  124. canvas.height = mx;
  125. rend();
  126. });
  127. observer.observe(canvas);
  128. var drawing = false;
  129. var positive = false;
  130. var mousePos = { x: 0, y: 0 };
  131. var lastPos = mousePos;
  132. const tc = (sx, sy) => {
  133. pos = getMousePos(canvas, sx, sy);
  134. lx = Math.floor(((pos.x - canvas.clientLeft) / canvas.clientWidth) * M);
  135. ly = Math.floor(((pos.y - canvas.clientTop) / canvas.clientHeight) * N);
  136. return [lx, ly];
  137. };
  138. const setAt = (xy) => {
  139. arr[xy[0] + M * xy[1]] |= 1;
  140. };
  141. const clearAt = (xy) => {
  142. arr[xy[0] + M * xy[1]] &= 0xfe;
  143. };
  144. const isOnAt = (xy) => {
  145. return arr[xy[0] + M * xy[1]] & 1;
  146. };
  147. const onTouchStart = (ev) => {
  148. if (event.targetTouches !== undefined) {
  149. x = event.targetTouches[0].clientX;
  150. y = event.targetTouches[0].clientY;
  151. } else {
  152. x = event.clientX;
  153. y = event.clientY;
  154. }
  155. ev.preventDefault();
  156. let xy = tc(x, y);
  157. if (isOnAt(xy)) {
  158. clearAt(xy);
  159. positive = false;
  160. } else {
  161. setAt(xy);
  162. positive = true;
  163. }
  164. drawing = true;
  165. };
  166. const onMoveDraw = (ev) => {
  167. let xy = tc(ev.x, ev.y);
  168. if (positive) {
  169. setAt(xy);
  170. } else {
  171. clearAt(xy);
  172. }
  173. rend();
  174. };
  175. const onTouchEnd = (ev) => {
  176. drawing = false;
  177. rend();
  178. };
  179. canvas.addEventListener(
  180. "touchstart",
  181. function (e) {
  182. onTouchStart(e);
  183. },
  184. false
  185. );
  186. canvas.addEventListener(
  187. "touchend",
  188. function (e) {
  189. onTouchEnd(e);
  190. },
  191. false
  192. );
  193. canvas.addEventListener(
  194. "mousedown",
  195. function (e) {
  196. onTouchStart(e);
  197. },
  198. false
  199. );
  200. canvas.addEventListener(
  201. "mouseup",
  202. function (e) {
  203. onTouchEnd(e);
  204. },
  205. false
  206. );
  207. canvas.addEventListener(
  208. "mousemove",
  209. function (e) {
  210. if (drawing) {
  211. onMoveDraw(e);
  212. }
  213. },
  214. false
  215. );
  216. function getMousePos(canva, cx, cy) {
  217. var rect = canva.getBoundingClientRect();
  218. return {
  219. x: cx - rect.left,
  220. y: cy - rect.top,
  221. };
  222. }
  223. const rend = () => {
  224. ctx.fillStyle = "white";
  225. ctx.fillRect(0, 0, canvas.width, canvas.height);
  226. ctx.lineWidth = 0.5;
  227. ctx.strokeStyle = "black";
  228. ys = canvas.height / N;
  229. xs = canvas.width / M;
  230. for (var x = 0; x < M; ++x) {
  231. if (M <= 60) {
  232. ctx.beginPath();
  233. ctx.moveTo(x * xs, 0);
  234. ctx.lineTo(x * xs, canvas.height);
  235. ctx.stroke();
  236. }
  237. }
  238. for (var y = 0; y < N; ++y) {
  239. if (N <= 60) {
  240. ctx.beginPath();
  241. ctx.moveTo(0, y * ys);
  242. ctx.lineTo(canvas.width, y * ys);
  243. ctx.stroke();
  244. }
  245. for (var x = 0; x < M; ++x) {
  246. if (arr[x + M * y] % 2) {
  247. ctx.fillStyle = "blue";
  248. ctx.fillRect(x * xs, y * ys, xs, ys);
  249. }
  250. }
  251. }
  252. };
  253. rend();
  254. running = false;
  255. const export_copy = () => {
  256. onCells = [];
  257. for (var y = 0; y < N; ++y) {
  258. for (var x = 0; x < M; ++x) {
  259. if (arr[x + M * y] % 2) {
  260. onCells.push([x, y]);
  261. }
  262. }
  263. }
  264. let txt = JSON.stringify({ onCells });
  265. dbg.innerText = txt;
  266. navigator.clipboard.writeText(txt);
  267. };
  268. const start_eloop = () => {
  269. if (!running) {
  270. intvl = setInterval(() => {
  271. evolve();
  272. rend();
  273. }, 200);
  274. running = true;
  275. }
  276. };
  277. const pause_eloop = () => {
  278. running = false;
  279. clearInterval(intvl);
  280. };
  281. const bigger = () => {
  282. Mold = M;
  283. Nold = N;
  284. M *= 2;
  285. N *= 2;
  286. arrn = new Uint8Array(M * N);
  287. arr2 = new Uint8Array(M * N);
  288. for (var y = 0; y < Nold; ++y) {
  289. for (var x = 0; x < Mold; ++x) {
  290. arrn[(Math.floor(Nold / 2) + y) * M + Math.floor(Mold / 2) + x] =
  291. arr[y * Mold + x];
  292. }
  293. }
  294. arr = arrn;
  295. };
  296. </script>
  297. </body>
  298. </html>