├── .gitignore
├── README.md
├── assembly-script
├── assembly
│ ├── index.ts
│ ├── tsconfig.json
│ └── tslint.json
├── build
│ ├── optimized.wasm
│ ├── optimized.wasm.map
│ ├── optimized.wat
│ ├── untouched.wasm
│ ├── untouched.wasm.map
│ └── untouched.wat
├── package-lock.json
└── package.json
├── csharp
├── App.config
├── Form1.Designer.cs
├── Form1.cs
├── Form1.resx
├── Program.cs
├── Properties
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ ├── Resources.resx
│ ├── Settings.Designer.cs
│ └── Settings.settings
├── WireWorldWindows.csproj
└── wireworld.txt
├── deno-gpu
├── main.ts
├── package.json
├── tsconfig.json
└── wireworld.txt
└── web
├── .gitignore
├── .prettierrc
├── index.html
├── package.json
├── pnpm-lock.yaml
├── public
└── vite.svg
├── src
├── asm
│ └── build
│ │ ├── optimized.wasm
│ │ ├── optimized.wasm.map
│ │ ├── optimized.wat
│ │ ├── untouched.wasm
│ │ ├── untouched.wasm.map
│ │ └── untouched.wat
├── common.ts
├── entry.ts
├── javascript.ts
├── vite-env.d.ts
├── webASM.ts
├── webGPU.ts
└── wireworld.txt
└── tsconfig.json
/.gitignore:
--------------------------------------------------------------------------------
1 | #OS junk files
2 | [Tt]humbs.db
3 | *.DS_Store
4 |
5 | #Visual Studio files
6 | *.[Oo]bj
7 | *.user
8 | *.aps
9 | *.pch
10 | *.vspscc
11 | *.vssscc
12 | *_i.c
13 | *_p.c
14 | *.ncb
15 | *.suo
16 | *.tlb
17 | *.tlh
18 | *.bak
19 | *.[Cc]ache
20 | *.ilk
21 | *.log
22 | *.lib
23 | *.sbr
24 | *.sdf
25 | ipch/
26 | obj/
27 | [Bb]in
28 | [Dd]ebug*/
29 | [Rr]elease*/
30 | Ankh.NoLoad
31 |
32 | #Tooling
33 | _ReSharper*/
34 | *.resharper
35 | [Tt]est[Rr]esult*
36 |
37 |
38 | #Subversion files
39 | .svn
40 |
41 | # Office Temp Files
42 | ~$*
43 |
44 | # Logs
45 | logs
46 | *.log
47 | npm-debug.log*
48 | yarn-debug.log*
49 | yarn-error.log*
50 | pnpm-debug.log*
51 | lerna-debug.log*
52 |
53 | node_modules
54 | dist
55 | dist-ssr
56 | *.local
57 |
58 | # Editor directories and files
59 | .vscode/*
60 | !.vscode/extensions.json
61 | .idea
62 | .DS_Store
63 | *.suo
64 | *.ntvs*
65 | *.njsproj
66 | *.sln
67 | *.sw?
68 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WireWorld
2 | An implementation of the The Wireworld computer simulation by Brian Silverman. This is a simple cellular automaton that simulates the flow of electrons through a wire. It is a Turing complete computer.
3 |
4 | More details on wireworld can be found here:
5 |
6 | https://en.wikipedia.org/wiki/Wireworld
7 |
8 | http://www.quinapalus.com/wi-index.html
9 |
--------------------------------------------------------------------------------
/assembly-script/assembly/index.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | // @external('console', 'logger')
4 | // declare function logger(offset: usize): void;
5 |
6 | const copperLen: u32 = 8;
7 | const size = 605129;
8 | const arrLen = 7000;
9 | const coppersSize = size * copperLen;
10 | const headsArrayOffset = coppersSize + size;
11 | const headsGridOffset = headsArrayOffset + arrLen;
12 | const tailsArrayOffset = headsGridOffset + size;
13 | const tailsGridOffset = tailsArrayOffset + arrLen;
14 |
15 | const newHeadsArrayOffset = tailsGridOffset + size;
16 | const newHeadsGridOffset = newHeadsArrayOffset + arrLen;
17 |
18 | @inline
19 | function loadBit(offset: u32): u32 {
20 | return load(offset << alignof());
21 | }
22 |
23 | @inline
24 | function storeBit(offset: u32, value: u32): void {
25 | store(offset << alignof(), value);
26 | }
27 |
28 | @inline
29 | function loadCopper(offset: u32, pos: u8): u32 {
30 | return loadBit(offset * copperLen + pos + 1);
31 | }
32 |
33 | export function init(): void {
34 | memory.grow(800);
35 | }
36 |
37 | export function tick(): void {
38 | let newHeadArrayIndex = 0;
39 | let hLen = loadBit(headsArrayOffset);
40 | for (let index: u32 = 1; index <= hLen; ++index) {
41 | let headKey = loadBit(headsArrayOffset + index);
42 | let hCopperLen = loadBit(headKey * copperLen);
43 | for (let i: u8 = 0; i < hCopperLen; ++i) {
44 | let copperStateIndex = loadCopper(headKey, i);
45 | if (
46 | !loadBit(tailsGridOffset + copperStateIndex) &&
47 | !loadBit(headsGridOffset + copperStateIndex) &&
48 | !loadBit(newHeadsGridOffset + copperStateIndex)
49 | ) {
50 | let headNeighbors = 0;
51 | let hnCopperLen = loadBit(copperStateIndex * copperLen);
52 | for (let j: u8 = 0; j < hnCopperLen; ++j) {
53 | let stateIndex = loadCopper(copperStateIndex, j);
54 | if (loadBit(headsGridOffset + stateIndex) === 1) {
55 | ++headNeighbors;
56 | if (headNeighbors === 3) {
57 | headNeighbors = 0;
58 | break;
59 | }
60 | }
61 | }
62 | if (headNeighbors > 0) {
63 | storeBit(newHeadsGridOffset + copperStateIndex, 1);
64 | storeBit(newHeadsArrayOffset + newHeadArrayIndex + 1, copperStateIndex);
65 | newHeadArrayIndex = newHeadArrayIndex + 1;
66 | }
67 | }
68 | }
69 | }
70 | storeBit(newHeadsArrayOffset, newHeadArrayIndex);
71 | }
72 |
--------------------------------------------------------------------------------
/assembly-script/assembly/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../node_modules/assemblyscript/std/assembly.json",
3 | "include": [
4 | "./**/*.ts"
5 | ]
6 | }
--------------------------------------------------------------------------------
/assembly-script/assembly/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "defaultSeverity": "error",
3 | "extends": [
4 | "tslint:recommended",
5 | "tslint-react",
6 | "tslint-eslint-rules",
7 | "tslint-config-prettier"
8 | ],
9 | "jsRules": {
10 | },
11 | "rules": {
12 | "radix": false,
13 | "prefer-for-of": false,
14 | "member-ordering": false,
15 | "no-namespace": false,
16 | "prefer-const": false,
17 | "no-reference": false,
18 | "trailing-comma": [
19 | false,
20 | {
21 | "multiline": {
22 | "objects": "always",
23 | "arrays": "always",
24 | "functions": "never",
25 | "typeLiterals": "ignore"
26 | }
27 | }
28 | ],
29 | "no-console": false,
30 | "interface-over-type-literal": false,
31 | "no-bitwise": false,
32 | "variable-name": false,
33 | "array-type": false,
34 | "no-empty": false,
35 | "object-literal-sort-keys": false,
36 | "jsx-no-lambda": false,
37 | "jsx-boolean-value": false,
38 | "no-empty-interface": false,
39 | "no-conditional-assignment": false,
40 | "max-classes-per-file": false,
41 | "no-unused-expression": false,
42 | "arrow-parens": false,
43 | "no-eval": false,
44 | "interface-name": false,
45 | "member-access": [
46 | true,
47 | "no-public"
48 | ],
49 | "prettier": true,
50 | "ter-arrow-body-style": [
51 | false,
52 | "never"
53 | ]
54 | },
55 | "rulesDirectory": [
56 | "tslint-plugin-prettier"
57 | ]
58 | }
--------------------------------------------------------------------------------
/assembly-script/build/optimized.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dested/WireWorld/5420896a3b75a608297dc2a488c0063cec63b43c/assembly-script/build/optimized.wasm
--------------------------------------------------------------------------------
/assembly-script/build/optimized.wasm.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["assembly/index.ts"],"names":[],"mappings":"4FAiCE,AAAY,YAKZ,UACK,AAAiB,MAAtB,EAAyB,OAEvB,AAAyB,AADzB,AAAsB,wBAEjB,AAAY,MAAjB,EAAoB,OAGhB,AAAS,AAFX,wCAGE,AAAS,kBADT,IAEA,AAAS,kBAHX,IAKE,AAAoB,IACpB,AAA0B,UACrB,AAAY,MAAjB,EAAoB,OAElB,AAAI,AAlC6B,AAAxB,AAkCG,AAxBL,YAA2B,eAVf,MAkC2B,KAE5C,AAAI,AADJ,OACsB,KACpB,AAAgB,IAChB,IAN+B,WAUrC,AAAI,EAAgB,KACT,gCAET,AAAoB,EAAoB,OAtBV,WAHE,WAfxC","sourceRoot":"assemblyscript:///","sourceContents":["/// \n\n// @external('console', 'logger')\n// declare function logger(offset: usize): void;\n\nconst copperLen: u32 = 8;\nconst size = 605129;\nconst arrLen = 7000;\nconst coppersSize = size * copperLen;\nconst headsArrayOffset = coppersSize + size;\nconst headsGridOffset = headsArrayOffset + arrLen;\nconst tailsArrayOffset = headsGridOffset + size;\nconst tailsGridOffset = tailsArrayOffset + arrLen;\n\nconst newHeadsArrayOffset = tailsGridOffset + size;\nconst newHeadsGridOffset = newHeadsArrayOffset + arrLen;\n\n@inline\nfunction loadBit(offset: u32): u32 {\n return load(offset << alignof());\n}\n\n@inline\nfunction storeBit(offset: u32, value: u32): void {\n store(offset << alignof(), value);\n}\n\n@inline\nfunction loadCopper(offset: u32, pos: u8): u32 {\n return loadBit(offset * copperLen + pos + 1);\n}\n\nexport function init(): void {\n memory.grow(800);\n}\n\nexport function tick(): void {\n let newHeadArrayIndex = 0;\n let hLen = loadBit(headsArrayOffset);\n for (let index: u32 = 1; index <= hLen; ++index) {\n let headKey = loadBit(headsArrayOffset + index);\n let hCopperLen = loadBit(headKey * copperLen);\n for (let i: u8 = 0; i < hCopperLen; ++i) {\n let copperStateIndex = loadCopper(headKey, i);\n if (\n !loadBit(tailsGridOffset + copperStateIndex) &&\n !loadBit(headsGridOffset + copperStateIndex) &&\n !loadBit(newHeadsGridOffset + copperStateIndex)\n ) {\n let headNeighbors = 0;\n let hnCopperLen = loadBit(copperStateIndex * copperLen);\n for (let j: u8 = 0; j < hnCopperLen; ++j) {\n let stateIndex = loadCopper(copperStateIndex, j);\n if (loadBit(headsGridOffset + stateIndex) === 1) {\n ++headNeighbors;\n if (headNeighbors === 3) {\n headNeighbors = 0;\n break;\n }\n }\n }\n if (headNeighbors > 0) {\n storeBit(newHeadsGridOffset + copperStateIndex, 1);\n storeBit(newHeadsArrayOffset + newHeadArrayIndex + 1, copperStateIndex);\n newHeadArrayIndex = newHeadArrayIndex + 1;\n }\n }\n }\n }\n storeBit(newHeadsArrayOffset, newHeadArrayIndex);\n}\n"]}
--------------------------------------------------------------------------------
/assembly-script/build/optimized.wat:
--------------------------------------------------------------------------------
1 | (module
2 | (type $v (func))
3 | (import "env" "memory" (memory $0 0))
4 | (table $0 1 anyfunc)
5 | (elem (i32.const 0) $null)
6 | (export "memory" (memory $0))
7 | (export "table" (table $0))
8 | (export "init" (func $assembly/index/init))
9 | (export "tick" (func $assembly/index/tick))
10 | (func $assembly/index/init (; 0 ;) (type $v)
11 | i32.const 800
12 | grow_memory
13 | drop
14 | )
15 | (func $assembly/index/tick (; 1 ;) (type $v)
16 | (local $0 i32)
17 | (local $1 i32)
18 | (local $2 i32)
19 | (local $3 i32)
20 | (local $4 i32)
21 | (local $5 i32)
22 | (local $6 i32)
23 | (local $7 i32)
24 | (local $8 i32)
25 | (local $9 i32)
26 | i32.const 21784644
27 | i32.load
28 | set_local $6
29 | i32.const 1
30 | set_local $3
31 | loop $repeat|0
32 | block $break|0
33 | get_local $3
34 | get_local $6
35 | i32.gt_u
36 | br_if $break|0
37 | get_local $3
38 | i32.const 5446161
39 | i32.add
40 | i32.const 2
41 | i32.shl
42 | i32.load
43 | tee_local $7
44 | i32.const 5
45 | i32.shl
46 | i32.load
47 | set_local $8
48 | i32.const 0
49 | set_local $4
50 | loop $repeat|1
51 | block $break|1
52 | get_local $4
53 | get_local $8
54 | i32.ge_u
55 | br_if $break|1
56 | get_local $4
57 | i32.const 255
58 | i32.and
59 | get_local $7
60 | i32.const 3
61 | i32.shl
62 | i32.add
63 | i32.const 1
64 | i32.add
65 | i32.const 2
66 | i32.shl
67 | i32.load
68 | tee_local $1
69 | i32.const 6065290
70 | i32.add
71 | i32.const 2
72 | i32.shl
73 | i32.load
74 | i32.eqz
75 | tee_local $0
76 | if
77 | get_local $1
78 | i32.const 5453161
79 | i32.add
80 | i32.const 2
81 | i32.shl
82 | i32.load
83 | i32.eqz
84 | set_local $0
85 | end
86 | get_local $0
87 | if
88 | get_local $1
89 | i32.const 6677419
90 | i32.add
91 | i32.const 2
92 | i32.shl
93 | i32.load
94 | i32.eqz
95 | set_local $0
96 | end
97 | get_local $0
98 | if
99 | i32.const 0
100 | set_local $0
101 | get_local $1
102 | i32.const 5
103 | i32.shl
104 | i32.load
105 | set_local $9
106 | i32.const 0
107 | set_local $5
108 | loop $repeat|2
109 | block $break|2
110 | get_local $5
111 | get_local $9
112 | i32.ge_u
113 | br_if $break|2
114 | get_local $5
115 | i32.const 255
116 | i32.and
117 | get_local $1
118 | i32.const 3
119 | i32.shl
120 | i32.add
121 | i32.const 1
122 | i32.add
123 | i32.const 2
124 | i32.shl
125 | i32.load
126 | i32.const 5453161
127 | i32.add
128 | i32.const 2
129 | i32.shl
130 | i32.load
131 | i32.const 1
132 | i32.eq
133 | if
134 | get_local $0
135 | i32.const 1
136 | i32.add
137 | tee_local $0
138 | i32.const 3
139 | i32.eq
140 | if
141 | i32.const 0
142 | set_local $0
143 | br $break|2
144 | end
145 | end
146 | get_local $5
147 | i32.const 1
148 | i32.add
149 | set_local $5
150 | br $repeat|2
151 | end
152 | end
153 | get_local $0
154 | i32.const 0
155 | i32.gt_s
156 | if
157 | get_local $1
158 | i32.const 6677419
159 | i32.add
160 | i32.const 2
161 | i32.shl
162 | i32.const 1
163 | i32.store
164 | get_local $2
165 | i32.const 6670420
166 | i32.add
167 | i32.const 2
168 | i32.shl
169 | get_local $1
170 | i32.store
171 | get_local $2
172 | i32.const 1
173 | i32.add
174 | set_local $2
175 | end
176 | end
177 | get_local $4
178 | i32.const 1
179 | i32.add
180 | set_local $4
181 | br $repeat|1
182 | end
183 | end
184 | get_local $3
185 | i32.const 1
186 | i32.add
187 | set_local $3
188 | br $repeat|0
189 | end
190 | end
191 | i32.const 26681676
192 | get_local $2
193 | i32.store
194 | )
195 | (func $null (; 2 ;) (type $v)
196 | nop
197 | )
198 | )
199 |
--------------------------------------------------------------------------------
/assembly-script/build/untouched.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dested/WireWorld/5420896a3b75a608297dc2a488c0063cec63b43c/assembly-script/build/untouched.wasm
--------------------------------------------------------------------------------
/assembly-script/build/untouched.wasm.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["assembly/index.ts"],"names":[],"mappings":"8KAiCE,AAAY,YAIZ,AAAwB,IACxB,AAAW,EAAQ,eACnB,EAAK,AAAiB,MAAG,EAAS,MAAe,EAC/C,AAAc,EAAQ,EAAmB,gBACzC,AAAiB,EAAQ,EAAU,gBACnC,EAAK,AAAY,MAAG,EAAI,MAAiB,EACvC,AAAuB,iCACvB,AACE,AAAC,EAAQ,EAAkB,mBAC3B,AAAC,EAAQ,EAAkB,0BAC3B,AAAC,EAAQ,EAAqB,wBAE9B,AAAoB,IACpB,AAAkB,EAAQ,EAAmB,gBAC7C,EAAK,AAAY,MAAG,EAAI,MAAkB,EACxC,AAAiB,EAvBkB,EAA5B,EAAS,GAAY,OAAM,iBAwBlC,AAAI,EAAQ,EAAkB,KAlCG,AAAxB,EAAU,OAkC2B,KAC5C,AAAE,OACF,AAAI,EAAkB,KACpB,AAAgB,IAChB,KAN+B,AAAE,aAUvC,AAAI,EAAgB,KACT,EAAqB,KAAkB,cACvC,EAAsB,GAAoB,eACnD,AAAoB,EAAoB,QAtBV,AAAE,cAHA,AAAE,aA8BjC,IA7CT,AAAW,EAAU,GAAgB","sourceRoot":"assemblyscript:///","sourceContents":["/// \n\n// @external('console', 'logger')\n// declare function logger(offset: usize): void;\n\nconst copperLen: u32 = 8;\nconst size = 605129;\nconst arrLen = 7000;\nconst coppersSize = size * copperLen;\nconst headsArrayOffset = coppersSize + size;\nconst headsGridOffset = headsArrayOffset + arrLen;\nconst tailsArrayOffset = headsGridOffset + size;\nconst tailsGridOffset = tailsArrayOffset + arrLen;\n\nconst newHeadsArrayOffset = tailsGridOffset + size;\nconst newHeadsGridOffset = newHeadsArrayOffset + arrLen;\n\n@inline\nfunction loadBit(offset: u32): u32 {\n return load(offset << alignof());\n}\n\n@inline\nfunction storeBit(offset: u32, value: u32): void {\n store(offset << alignof(), value);\n}\n\n@inline\nfunction loadCopper(offset: u32, pos: u8): u32 {\n return loadBit(offset * copperLen + pos + 1);\n}\n\nexport function init(): void {\n memory.grow(800);\n}\n\nexport function tick(): void {\n let newHeadArrayIndex = 0;\n let hLen = loadBit(headsArrayOffset);\n for (let index: u32 = 1; index <= hLen; ++index) {\n let headKey = loadBit(headsArrayOffset + index);\n let hCopperLen = loadBit(headKey * copperLen);\n for (let i: u8 = 0; i < hCopperLen; ++i) {\n let copperStateIndex = loadCopper(headKey, i);\n if (\n !loadBit(tailsGridOffset + copperStateIndex) &&\n !loadBit(headsGridOffset + copperStateIndex) &&\n !loadBit(newHeadsGridOffset + copperStateIndex)\n ) {\n let headNeighbors = 0;\n let hnCopperLen = loadBit(copperStateIndex * copperLen);\n for (let j: u8 = 0; j < hnCopperLen; ++j) {\n let stateIndex = loadCopper(copperStateIndex, j);\n if (loadBit(headsGridOffset + stateIndex) === 1) {\n ++headNeighbors;\n if (headNeighbors === 3) {\n headNeighbors = 0;\n break;\n }\n }\n }\n if (headNeighbors > 0) {\n storeBit(newHeadsGridOffset + copperStateIndex, 1);\n storeBit(newHeadsArrayOffset + newHeadArrayIndex + 1, copperStateIndex);\n newHeadArrayIndex = newHeadArrayIndex + 1;\n }\n }\n }\n }\n storeBit(newHeadsArrayOffset, newHeadArrayIndex);\n}\n"]}
--------------------------------------------------------------------------------
/assembly-script/build/untouched.wat:
--------------------------------------------------------------------------------
1 | (module
2 | (type $v (func))
3 | (import "env" "memory" (memory $0 0))
4 | (table $0 1 anyfunc)
5 | (elem (i32.const 0) $null)
6 | (global $assembly/index/copperLen i32 (i32.const 8))
7 | (global $assembly/index/size i32 (i32.const 605129))
8 | (global $assembly/index/arrLen i32 (i32.const 7000))
9 | (global $assembly/index/coppersSize i32 (i32.const 4841032))
10 | (global $assembly/index/headsArrayOffset i32 (i32.const 5446161))
11 | (global $assembly/index/headsGridOffset i32 (i32.const 5453161))
12 | (global $assembly/index/tailsArrayOffset i32 (i32.const 6058290))
13 | (global $assembly/index/tailsGridOffset i32 (i32.const 6065290))
14 | (global $assembly/index/newHeadsArrayOffset i32 (i32.const 6670419))
15 | (global $assembly/index/newHeadsGridOffset i32 (i32.const 6677419))
16 | (global $HEAP_BASE i32 (i32.const 8))
17 | (export "memory" (memory $0))
18 | (export "table" (table $0))
19 | (export "init" (func $assembly/index/init))
20 | (export "tick" (func $assembly/index/tick))
21 | (func $assembly/index/init (; 0 ;) (type $v)
22 | i32.const 800
23 | grow_memory
24 | drop
25 | )
26 | (func $assembly/index/tick (; 1 ;) (type $v)
27 | (local $0 i32)
28 | (local $1 i32)
29 | (local $2 i32)
30 | (local $3 i32)
31 | (local $4 i32)
32 | (local $5 i32)
33 | (local $6 i32)
34 | (local $7 i32)
35 | (local $8 i32)
36 | (local $9 i32)
37 | (local $10 i32)
38 | (local $11 i32)
39 | i32.const 0
40 | set_local $0
41 | block $assembly/index/loadBit|inlined.0 (result i32)
42 | get_global $assembly/index/headsArrayOffset
43 | set_local $1
44 | get_local $1
45 | i32.const 2
46 | i32.shl
47 | i32.load
48 | end
49 | set_local $1
50 | block $break|0
51 | i32.const 1
52 | set_local $2
53 | loop $repeat|0
54 | get_local $2
55 | get_local $1
56 | i32.le_u
57 | i32.eqz
58 | br_if $break|0
59 | block
60 | block $assembly/index/loadBit|inlined.1 (result i32)
61 | get_global $assembly/index/headsArrayOffset
62 | get_local $2
63 | i32.add
64 | set_local $3
65 | get_local $3
66 | i32.const 2
67 | i32.shl
68 | i32.load
69 | end
70 | set_local $3
71 | block $assembly/index/loadBit|inlined.2 (result i32)
72 | get_local $3
73 | get_global $assembly/index/copperLen
74 | i32.mul
75 | set_local $4
76 | get_local $4
77 | i32.const 2
78 | i32.shl
79 | i32.load
80 | end
81 | set_local $4
82 | block $break|1
83 | i32.const 0
84 | set_local $5
85 | loop $repeat|1
86 | get_local $5
87 | get_local $4
88 | i32.lt_u
89 | i32.eqz
90 | br_if $break|1
91 | block
92 | block $assembly/index/loadCopper|inlined.0 (result i32)
93 | block $assembly/index/loadBit|inlined.3 (result i32)
94 | get_local $3
95 | get_global $assembly/index/copperLen
96 | i32.mul
97 | get_local $5
98 | i32.const 255
99 | i32.and
100 | i32.add
101 | i32.const 1
102 | i32.add
103 | set_local $6
104 | get_local $6
105 | i32.const 2
106 | i32.shl
107 | i32.load
108 | end
109 | end
110 | set_local $6
111 | block $assembly/index/loadBit|inlined.7 (result i32)
112 | get_global $assembly/index/tailsGridOffset
113 | get_local $6
114 | i32.add
115 | set_local $7
116 | get_local $7
117 | i32.const 2
118 | i32.shl
119 | i32.load
120 | end
121 | i32.eqz
122 | tee_local $7
123 | if (result i32)
124 | block $assembly/index/loadBit|inlined.8 (result i32)
125 | get_global $assembly/index/headsGridOffset
126 | get_local $6
127 | i32.add
128 | set_local $7
129 | get_local $7
130 | i32.const 2
131 | i32.shl
132 | i32.load
133 | end
134 | i32.eqz
135 | else
136 | get_local $7
137 | end
138 | tee_local $7
139 | i32.const 0
140 | i32.ne
141 | if (result i32)
142 | block $assembly/index/loadBit|inlined.9 (result i32)
143 | get_global $assembly/index/newHeadsGridOffset
144 | get_local $6
145 | i32.add
146 | set_local $7
147 | get_local $7
148 | i32.const 2
149 | i32.shl
150 | i32.load
151 | end
152 | i32.eqz
153 | else
154 | get_local $7
155 | end
156 | i32.const 0
157 | i32.ne
158 | if
159 | i32.const 0
160 | set_local $7
161 | block $assembly/index/loadBit|inlined.10 (result i32)
162 | get_local $6
163 | get_global $assembly/index/copperLen
164 | i32.mul
165 | set_local $8
166 | get_local $8
167 | i32.const 2
168 | i32.shl
169 | i32.load
170 | end
171 | set_local $8
172 | block $break|2
173 | i32.const 0
174 | set_local $9
175 | loop $repeat|2
176 | get_local $9
177 | get_local $8
178 | i32.lt_u
179 | i32.eqz
180 | br_if $break|2
181 | block
182 | block $assembly/index/loadCopper|inlined.1 (result i32)
183 | block $assembly/index/loadBit|inlined.11 (result i32)
184 | get_local $6
185 | get_global $assembly/index/copperLen
186 | i32.mul
187 | get_local $9
188 | i32.const 255
189 | i32.and
190 | i32.add
191 | i32.const 1
192 | i32.add
193 | set_local $10
194 | get_local $10
195 | i32.const 2
196 | i32.shl
197 | i32.load
198 | end
199 | end
200 | set_local $10
201 | block $assembly/index/loadBit|inlined.13 (result i32)
202 | get_global $assembly/index/headsGridOffset
203 | get_local $10
204 | i32.add
205 | set_local $11
206 | get_local $11
207 | i32.const 2
208 | i32.shl
209 | i32.load
210 | end
211 | i32.const 1
212 | i32.eq
213 | if
214 | get_local $7
215 | i32.const 1
216 | i32.add
217 | set_local $7
218 | get_local $7
219 | i32.const 3
220 | i32.eq
221 | if
222 | i32.const 0
223 | set_local $7
224 | br $break|2
225 | end
226 | end
227 | end
228 | get_local $9
229 | i32.const 1
230 | i32.add
231 | set_local $9
232 | br $repeat|2
233 | unreachable
234 | end
235 | unreachable
236 | end
237 | get_local $7
238 | i32.const 0
239 | i32.gt_s
240 | if
241 | get_global $assembly/index/newHeadsGridOffset
242 | get_local $6
243 | i32.add
244 | set_local $9
245 | i32.const 1
246 | set_local $10
247 | get_local $9
248 | i32.const 2
249 | i32.shl
250 | get_local $10
251 | i32.store
252 | get_global $assembly/index/newHeadsArrayOffset
253 | get_local $0
254 | i32.add
255 | i32.const 1
256 | i32.add
257 | set_local $10
258 | get_local $10
259 | i32.const 2
260 | i32.shl
261 | get_local $6
262 | i32.store
263 | get_local $0
264 | i32.const 1
265 | i32.add
266 | set_local $0
267 | end
268 | end
269 | end
270 | get_local $5
271 | i32.const 1
272 | i32.add
273 | set_local $5
274 | br $repeat|1
275 | unreachable
276 | end
277 | unreachable
278 | end
279 | end
280 | get_local $2
281 | i32.const 1
282 | i32.add
283 | set_local $2
284 | br $repeat|0
285 | unreachable
286 | end
287 | unreachable
288 | end
289 | get_global $assembly/index/newHeadsArrayOffset
290 | set_local $2
291 | get_local $2
292 | i32.const 2
293 | i32.shl
294 | get_local $0
295 | i32.store
296 | )
297 | (func $null (; 2 ;) (type $v)
298 | )
299 | )
300 |
--------------------------------------------------------------------------------
/assembly-script/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "requires": true,
3 | "lockfileVersion": 1,
4 | "dependencies": {
5 | "@protobufjs/utf8": {
6 | "version": "1.1.0",
7 | "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
8 | "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=",
9 | "dev": true
10 | },
11 | "assemblyscript": {
12 | "version": "github:AssemblyScript/assemblyscript#d5f72e32d7e99cec3f8c37e6bb2916864706f4f0",
13 | "dev": true,
14 | "requires": {
15 | "@protobufjs/utf8": "1.1.0",
16 | "binaryen": "55.0.0-nightly.20181130",
17 | "glob": "7.1.3",
18 | "long": "4.0.0"
19 | }
20 | },
21 | "balanced-match": {
22 | "version": "1.0.0",
23 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
24 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
25 | "dev": true
26 | },
27 | "binaryen": {
28 | "version": "55.0.0-nightly.20181130",
29 | "resolved": "https://registry.npmjs.org/binaryen/-/binaryen-55.0.0-nightly.20181130.tgz",
30 | "integrity": "sha512-RfMiI0vavw7Sy7KX8h1xOs4D3zp9nehmtE87DSfY6nXyd2EAdMwJ97tWdepuhOc6JWZsntOfzUA2fqu/sYTTLg==",
31 | "dev": true
32 | },
33 | "brace-expansion": {
34 | "version": "1.1.11",
35 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
36 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
37 | "dev": true,
38 | "requires": {
39 | "balanced-match": "1.0.0",
40 | "concat-map": "0.0.1"
41 | }
42 | },
43 | "concat-map": {
44 | "version": "0.0.1",
45 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
46 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
47 | "dev": true
48 | },
49 | "fs.realpath": {
50 | "version": "1.0.0",
51 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
52 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
53 | "dev": true
54 | },
55 | "glob": {
56 | "version": "7.1.3",
57 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
58 | "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
59 | "dev": true,
60 | "requires": {
61 | "fs.realpath": "1.0.0",
62 | "inflight": "1.0.6",
63 | "inherits": "2.0.3",
64 | "minimatch": "3.0.4",
65 | "once": "1.4.0",
66 | "path-is-absolute": "1.0.1"
67 | }
68 | },
69 | "inflight": {
70 | "version": "1.0.6",
71 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
72 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
73 | "dev": true,
74 | "requires": {
75 | "once": "1.4.0",
76 | "wrappy": "1.0.2"
77 | }
78 | },
79 | "inherits": {
80 | "version": "2.0.3",
81 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
82 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
83 | "dev": true
84 | },
85 | "long": {
86 | "version": "4.0.0",
87 | "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
88 | "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==",
89 | "dev": true
90 | },
91 | "minimatch": {
92 | "version": "3.0.4",
93 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
94 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
95 | "dev": true,
96 | "requires": {
97 | "brace-expansion": "1.1.11"
98 | }
99 | },
100 | "once": {
101 | "version": "1.4.0",
102 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
103 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
104 | "dev": true,
105 | "requires": {
106 | "wrappy": "1.0.2"
107 | }
108 | },
109 | "path-is-absolute": {
110 | "version": "1.0.1",
111 | "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
112 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
113 | "dev": true
114 | },
115 | "wrappy": {
116 | "version": "1.0.2",
117 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
118 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
119 | "dev": true
120 | }
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/assembly-script/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "asbuild:untouched": "asc assembly/index.ts -b ../web/src/asm/untouched.wasm -t ../web/src/asm/untouched.wat --sourceMap --validate --debug --importMemory",
4 | "asbuild:optimized": "asc assembly/index.ts -b ../web/src/asm/optimized.wasm -t ../web/src/asm/optimized.wat --sourceMap --validate -O3 --importMemory",
5 | "asbuild": "npm run asbuild:untouched && npm run asbuild:optimized"
6 | },
7 | "devDependencies": {
8 | "assemblyscript": "github:AssemblyScript/assemblyscript"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/csharp/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/csharp/Form1.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace WireWorldWindows
2 | {
3 | partial class Form1
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.picFront = new System.Windows.Forms.PictureBox();
32 | this.label1 = new System.Windows.Forms.Label();
33 | this.picBack = new System.Windows.Forms.PictureBox();
34 | ((System.ComponentModel.ISupportInitialize)(this.picFront)).BeginInit();
35 | ((System.ComponentModel.ISupportInitialize)(this.picBack)).BeginInit();
36 | this.SuspendLayout();
37 | //
38 | // picFront
39 | //
40 | this.picFront.Location = new System.Drawing.Point(0, 0);
41 | this.picFront.Margin = new System.Windows.Forms.Padding(2);
42 | this.picFront.Name = "picFront";
43 | this.picFront.Size = new System.Drawing.Size(779, 463);
44 | this.picFront.TabIndex = 1;
45 | this.picFront.TabStop = false;
46 | //
47 | // label1
48 | //
49 | this.label1.AutoSize = true;
50 | this.label1.ForeColor = System.Drawing.Color.Black;
51 | this.label1.Location = new System.Drawing.Point(36, 32);
52 | this.label1.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
53 | this.label1.Name = "label1";
54 | this.label1.Size = new System.Drawing.Size(35, 13);
55 | this.label1.TabIndex = 2;
56 | this.label1.Text = "label1";
57 | //
58 | // picBack
59 | //
60 | this.picBack.BackColor = System.Drawing.Color.Transparent;
61 | this.picBack.Location = new System.Drawing.Point(0, 0);
62 | this.picBack.Name = "picBack";
63 | this.picBack.Size = new System.Drawing.Size(779, 463);
64 | this.picBack.TabIndex = 3;
65 | this.picBack.TabStop = false;
66 | //
67 | // Form1
68 | //
69 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
70 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
71 | this.AutoScroll = true;
72 | this.ClientSize = new System.Drawing.Size(779, 463);
73 | this.Controls.Add(this.label1);
74 | this.Controls.Add(this.picBack);
75 | this.Controls.Add(this.picFront);
76 | this.DoubleBuffered = true;
77 | this.Margin = new System.Windows.Forms.Padding(2);
78 | this.Name = "Form1";
79 | this.Text = "Form1";
80 | ((System.ComponentModel.ISupportInitialize)(this.picFront)).EndInit();
81 | ((System.ComponentModel.ISupportInitialize)(this.picBack)).EndInit();
82 | this.ResumeLayout(false);
83 | this.PerformLayout();
84 |
85 | }
86 |
87 | #endregion
88 |
89 | private System.Windows.Forms.PictureBox picFront;
90 | private System.Windows.Forms.Label label1;
91 | private System.Windows.Forms.PictureBox picBack;
92 | }
93 | }
94 |
95 |
--------------------------------------------------------------------------------
/csharp/Form1.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Data;
5 | using System.Diagnostics;
6 | using System.Drawing;
7 | using System.IO;
8 | using System.Linq;
9 | using System.Text;
10 | using System.Threading.Tasks;
11 | using System.Windows.Forms;
12 | using System.Windows.Forms.VisualStyles;
13 |
14 | namespace WireWorldWindows
15 | {
16 | public partial class Form1 : Form
17 | {
18 | private Programa p;
19 |
20 | public Form1()
21 | {
22 | this.SetStyle(ControlStyles.UserPaint |ControlStyles.AllPaintingInWmPaint |ControlStyles.DoubleBuffer, true);
23 |
24 | InitializeComponent();
25 | this.p = new Programa();
26 | p.Start(picFront);
27 | picFront.Width = picBack.Width = (int) (Board.BoardWidth * Programa.magnify);
28 | picFront.Height = picBack.Height = (int) (Board.BoardHeight * Programa.magnify);
29 |
30 | picFront.Paint += PicFront_Paint;
31 | picFront.BackColor = Color.Transparent;
32 |
33 | picBack.Paint += PicBackPaint1;
34 |
35 |
36 | picFront.Parent = picBack;
37 |
38 | Timer t = new Timer();
39 | t.Interval = 1;
40 | t.Tick += (a, b) =>
41 | {
42 | picFront.Refresh();
43 | };
44 | t.Start();
45 | }
46 |
47 | private void PicBackPaint1(object sender, PaintEventArgs e)
48 | {
49 | this.p.drawBack(e.Graphics);
50 |
51 | }
52 |
53 | private void PicFront_Paint(object sender, PaintEventArgs e)
54 | {
55 | label1.Text = Programa.ticker + " nanoseconds per iteration";
56 | this.p.drawFront(e.Graphics, Programa.boardState);
57 | }
58 |
59 | }
60 |
61 |
62 |
63 | class Programa
64 | {
65 |
66 |
67 | public static float magnify = 1f;
68 | public static BoardState boardState = null;
69 | public static string ticker = "";
70 | public void Start(PictureBox picFront)
71 | {
72 | StatePosition lastPoint = null;
73 |
74 |
75 | var file = File.ReadAllText("wireworld.txt");
76 | new Board(file);
77 | BoardState.EducatedHeadCount = Board.BoardWidth * Board.BoardHeight;
78 | BoardState.SetupArraySwitch();
79 | boardState = generateInitialBoardState();
80 | BoardState.SetupArraySwitch();
81 |
82 | Stopwatch sw = new Stopwatch();
83 | long iterations = 0;
84 |
85 | Task.Factory.StartNew(() =>
86 | {
87 | while (true)
88 | {
89 | sw.Restart();
90 | boardState = tickBoard(boardState);
91 | sw.Stop();
92 | iterations++;
93 |
94 | if (iterations % 5000 == 0)
95 | {
96 | ticker = ((sw.ElapsedTicks / (double)(Stopwatch.Frequency / (double)1000 / 1000 / 1000)) / (double)iterations).ToString("F10");
97 | Debug.WriteLine($"MS Per Run: {ticker}");
98 | }
99 | if (iterations % 500 == 0)
100 | {
101 | sw.Reset();
102 | }
103 |
104 | }
105 | });
106 | }
107 |
108 |
109 |
110 |
111 |
112 | public void drawBack(Graphics context)
113 | {
114 | context.FillRectangle(new SolidBrush(Color.Black), 0, 0, Board.BoardWidth * magnify, Board.BoardHeight * magnify);
115 | for (int y = 0; y < Board.BoardHeight; y++)
116 | {
117 | for (int x = 0; x < Board.BoardWidth; x++)
118 | {
119 | if (Board.CopperGrid[x + y * Board.BoardWidth])
120 | {
121 | context.FillRectangle(copper, x * magnify, y * magnify, magnify, magnify);
122 | }
123 | }
124 | }
125 | }
126 | private static SolidBrush copper = new SolidBrush(Color.FromArgb(16, 0, 168));
127 | private static SolidBrush head = new SolidBrush(Color.FromArgb(255, 245, 155));
128 | private static SolidBrush tail = new SolidBrush(Color.FromArgb(137, 210, 255));
129 |
130 | public void drawFront(Graphics context, BoardState boardState)
131 | {
132 | var heads = boardState.HeadsArray;
133 | var tails = boardState.TailsArray;
134 |
135 | for (int index = 0, headLength = boardState.HeadsArrayLength; index < headLength; index++)
136 | {
137 | context.FillRectangle(head, StatePosition.GetX(heads[index]) * magnify, StatePosition.GetY(heads[index]) * magnify, magnify, magnify);
138 | }
139 | for (int index = 0, tailLength = boardState.TailsArrayLength; index < tailLength; index++)
140 | {
141 | context.FillRectangle(tail, StatePosition.GetX(tails[index]) * magnify, StatePosition.GetY(tails[index]) * magnify, magnify, magnify);
142 | }
143 | }
144 |
145 |
146 | private static readonly StatePosition[] neighbors =
147 | {
148 | new StatePosition(-1, -1),
149 | new StatePosition(-1, 0),
150 | new StatePosition(-1, 1),
151 | new StatePosition(0, -1),
152 | new StatePosition(0, 1),
153 | new StatePosition(1, -1),
154 | new StatePosition(1, 0),
155 | new StatePosition(1, 1)
156 | };
157 |
158 |
159 | private static BoardState generateInitialBoardState()
160 | {
161 | var boardState = new BoardState();
162 | int boardWidth = Board.BoardWidth;
163 | int boardHeight = Board.BoardHeight;
164 | boardState.TailsGrid = new bool[boardWidth * boardHeight];
165 | boardState.TailsArray = new int[boardWidth * boardHeight];
166 |
167 | Board.CopperGrid = new bool[boardWidth * boardHeight];
168 | Board.Coppers = new int[boardWidth * boardHeight][];
169 | BoardState.EducatedHeadCount = 0;
170 |
171 | for (int i = 0; i < boardWidth * boardHeight; i++)
172 | {
173 | Board.Coppers[i] = new int[0];
174 | }
175 |
176 | for (int y = 0; y < boardHeight; y++)
177 | {
178 | for (int x = 0; x < boardWidth; x++)
179 | {
180 | var statePos = new StatePosition(x, y);
181 | switch (Board.InitialStates[statePos.StateIndex])
182 | {
183 | case WireState.Head:
184 | Board.CopperGrid[statePos.StateIndex] = true;
185 | boardState.HeadsArray[boardState.HeadsArrayLength++] = (statePos.StateIndex);
186 | boardState.HeadsGrid[statePos.StateIndex] = true;
187 | BoardState.EducatedHeadCount++;
188 | break;
189 | case WireState.Tail:
190 | Board.CopperGrid[statePos.StateIndex] = true;
191 | boardState.TailsArray[boardState.TailsArrayLength++] = (statePos.StateIndex);
192 | boardState.TailsGrid[statePos.StateIndex] = true;
193 | break;
194 | case WireState.Copper:
195 | Board.CopperGrid[statePos.StateIndex] = true;
196 | break;
197 | }
198 | }
199 | }
200 | BoardState.EducatedHeadCount = (int)(BoardState.EducatedHeadCount * 1.5);
201 |
202 | buildCoppers();
203 | return boardState;
204 | }
205 |
206 | private static void buildCoppers()
207 | {
208 | int boardHeight = Board.BoardHeight;
209 | int boardWidth = Board.BoardWidth;
210 | for (int y = 0; y < boardHeight; y++)
211 | {
212 | for (int x = 0; x < boardWidth; x++)
213 | {
214 | int stateIndex = x + y * boardWidth;
215 | if (Board.CopperGrid[stateIndex])
216 | {
217 | Board.Coppers[stateIndex] = getNeighborStates(x, y);
218 | }
219 | }
220 | }
221 | }
222 |
223 | private static int[] getNeighborStates(int x, int y)
224 | {
225 | int boardWidth = Board.BoardWidth;
226 |
227 | var statePositions = new List();
228 |
229 | for (int index = 0; index < neighbors.Length; index++)
230 | {
231 | StatePosition statePosition = neighbors[index];
232 | int stateIndex = (x + statePosition.X) + (y + statePosition.Y) * boardWidth;
233 | if (Board.CopperGrid[stateIndex])
234 | {
235 | statePositions.Add(new StatePosition(x + statePosition.X, y + statePosition.Y).StateIndex);
236 | }
237 | }
238 | return statePositions.ToArray();
239 | }
240 |
241 | private static BoardState tickBoard(BoardState boardState)
242 | {
243 | var newBoardState = new BoardState();
244 |
245 |
246 | var headsGrid = boardState.HeadsGrid;
247 | var tailsGrid = boardState.TailsGrid;
248 | var newHeadsGrid = newBoardState.HeadsGrid;
249 | // int _1count, _2count, _3count, _4count, _5count, _6count = 0;
250 | // unchecked
251 | {
252 |
253 | var coppers = Board.Coppers;
254 |
255 | var collection = boardState.HeadsArray;
256 | for (int index = 0, colLen = boardState.HeadsArrayLength; index < colLen; index++)
257 | {
258 | int[] positions = coppers[collection[index]];
259 | for (int i = 0, posLength = positions.Length; i < posLength; i++)
260 | {
261 | int copperStateIndex = positions[i];
262 | //if position is copper
263 | if (!tailsGrid[copperStateIndex] && !headsGrid[copperStateIndex] && !newHeadsGrid[copperStateIndex])
264 | {
265 | int[] states = coppers[copperStateIndex];
266 | int headNeighbors = 0;
267 | switch (states.Length)
268 | {
269 | case 1:
270 | // _1count++;
271 | if (headsGrid[states[0]])
272 | {
273 | goto good;
274 | }
275 | break;
276 |
277 | case 2:
278 | // _2count++;
279 | if (headsGrid[states[0]] || headsGrid[states[1]])
280 | {
281 | goto good;
282 | }
283 | break;
284 | case 3:
285 | // _3count++;
286 | var s0 = headsGrid[states[0]];
287 | var s1 = headsGrid[states[1]];
288 | var s2 = headsGrid[states[2]];
289 | if (s0 ^ s1 || s0 ^ s2)
290 | {
291 | goto good;
292 | }
293 | break;
294 | case 4:
295 | // _4count++;
296 | for (int ind2 = 0, statesLen = states.Length; ind2 < statesLen; ind2++)
297 | {
298 | if (headsGrid[states[ind2]])
299 | {
300 | headNeighbors++;
301 | if (headNeighbors == 3)
302 | {
303 | headNeighbors = 0;
304 | break;
305 | }
306 | }
307 | }
308 | if (headNeighbors > 0)
309 | {
310 | goto good;
311 | }
312 | break;
313 | case 5:
314 | // _5count++;
315 | for (int ind2 = 0, statesLen = states.Length; ind2 < statesLen; ind2++)
316 | {
317 | if (headsGrid[states[ind2]])
318 | {
319 | headNeighbors++;
320 | if (headNeighbors == 3)
321 | {
322 | headNeighbors = 0;
323 | break;
324 | }
325 | }
326 | }
327 | if (headNeighbors > 0)
328 | {
329 | goto good;
330 | }
331 | break;
332 | case 6:
333 | // _6count++;
334 | for (int ind2 = 0, statesLen = states.Length; ind2 < statesLen; ind2++)
335 | {
336 |
337 | if (headsGrid[states[ind2]])
338 | {
339 | headNeighbors++;
340 | if (headNeighbors == 3)
341 | {
342 | headNeighbors = 0;
343 | break;
344 | }
345 | }
346 | }
347 | if (headNeighbors > 0)
348 | {
349 | goto good;
350 | }
351 | break;
352 | default:
353 | throw new Exception("no");
354 | }
355 | goto bad;
356 |
357 | good:
358 | newBoardState.HeadsArray[newBoardState.HeadsArrayLength++] = copperStateIndex;
359 | newHeadsGrid[copperStateIndex] = true;
360 |
361 | bad:;
362 | }
363 | }
364 | }
365 |
366 |
367 |
368 |
369 | newBoardState.TailsArray = collection;
370 | newBoardState.TailsArrayLength = boardState.HeadsArrayLength;
371 | newBoardState.TailsGrid = headsGrid;
372 | }
373 |
374 | // Debug.WriteLine($"{_1count} {_2count} {_3count} {_4count} {_5count} {_6count}");
375 | return newBoardState;
376 | }
377 |
378 |
379 | }
380 | public class Board
381 | {
382 | public static WireState[] InitialStates;
383 | public static int BoardWidth;
384 | public static int BoardHeight;
385 | public static int[][] Coppers;
386 | public static bool[] CopperGrid;
387 |
388 | public Board(int width, int height)
389 | {
390 | InitialStates = new WireState[width * height];
391 | BoardWidth = width;
392 | BoardHeight = height;
393 | }
394 |
395 | public Board(string starting)
396 | {
397 | string[] rows = starting.Replace("\r", "").Split('\n');
398 | BoardWidth = rows[0].Length;
399 | BoardHeight = rows.Length;
400 |
401 | StatePosition.BoardWidth = BoardWidth;
402 |
403 | InitialStates = new WireState[BoardWidth * BoardHeight];
404 | for (int rowIndex = 0; rowIndex < rows.Length; rowIndex++)
405 | {
406 | string row = rows[rowIndex];
407 |
408 | for (int characterIndex = 0; characterIndex < row.Length; characterIndex++)
409 | {
410 | char character = row[characterIndex];
411 |
412 | InitialStates[characterIndex + rowIndex * BoardWidth] = charToState(character);
413 | }
414 | }
415 | }
416 |
417 | public Board()
418 | {
419 | }
420 |
421 | public static string ToString(BoardState state)
422 | {
423 | var sb = new StringBuilder();
424 | for (int y = 0; y < BoardHeight; y++)
425 | {
426 | for (int x = 0; x < BoardWidth; x++)
427 | {
428 | int statePos = x + y * BoardWidth;
429 | bool isCopper = CopperGrid[statePos];
430 |
431 | if (isCopper)
432 | {
433 | if (state.HeadsGrid[statePos])
434 | {
435 | sb.Append(stateToChar(WireState.Head));
436 | }
437 | else if (state.TailsGrid[statePos])
438 | {
439 | sb.Append(stateToChar(WireState.Tail));
440 | }
441 | else
442 | {
443 | sb.Append(stateToChar(WireState.Copper));
444 | }
445 | }
446 | else
447 | {
448 | sb.Append(" ");
449 | }
450 | }
451 | sb.AppendLine();
452 | }
453 | return sb.ToString();
454 | return "";
455 | }
456 |
457 | private static WireState charToState(char character)
458 | {
459 | switch (character)
460 | {
461 | case '#':
462 | return WireState.Copper;
463 | case '@':
464 | return WireState.Head;
465 | case '~':
466 | return WireState.Tail;
467 | default:
468 | return WireState.Empty;
469 | }
470 | }
471 |
472 | private static char stateToChar(WireState state)
473 | {
474 | switch (state)
475 | {
476 | case WireState.Head:
477 | return '@';
478 | case WireState.Tail:
479 | return '~';
480 | case WireState.Copper:
481 | return '#';
482 | default:
483 | return ' ';
484 | }
485 | }
486 | }
487 |
488 | public enum WireState
489 | {
490 | Empty = 0,
491 | Head = 1,
492 | Tail = 2,
493 | Copper = 3
494 | }
495 |
496 | public class StatePosition
497 | {
498 | public StatePosition(int x, int y)
499 | {
500 | X = x;
501 | Y = y;
502 | StateIndex = X + Y * BoardWidth;
503 | }
504 |
505 | public static int GetY(int z)
506 | {
507 | return z / BoardWidth;
508 | }
509 | public static int GetX(int z)
510 | {
511 | return z % BoardWidth;
512 | }
513 |
514 | public int StateIndex;
515 |
516 | public static int BoardWidth;
517 | public int X;
518 | public int Y;
519 | }
520 |
521 | public class BoardState
522 | {
523 | public int[] HeadsArray;
524 | public int HeadsArrayLength;
525 | public int[] TailsArray;
526 | public int TailsArrayLength;
527 |
528 | public bool[] HeadsGrid;
529 | public bool[] TailsGrid;
530 |
531 | private static int SwitchArray = 0;
532 | private static bool[] SwitchArray1;
533 | private static bool[] SwitchArray2;
534 | private static bool[] SwitchArray3;
535 |
536 | private static int[] SwitchArrayHead1;
537 | private static int[] SwitchArrayHead2;
538 | private static int[] SwitchArrayHead3;
539 | public static int EducatedHeadCount;
540 |
541 | public static void SetupArraySwitch()
542 | {
543 | SwitchArray1 = new bool[Board.BoardWidth * Board.BoardHeight];
544 | SwitchArray2 = new bool[Board.BoardWidth * Board.BoardHeight];
545 | SwitchArray3 = new bool[Board.BoardWidth * Board.BoardHeight];
546 | SwitchArrayHead1 = new int[EducatedHeadCount];
547 | SwitchArrayHead2 = new int[EducatedHeadCount];
548 | SwitchArrayHead3 = new int[EducatedHeadCount];
549 | }
550 | public BoardState()
551 | {
552 |
553 | switch (SwitchArray)
554 | {
555 | case 0:
556 | SwitchArray = 1;
557 | HeadsGrid = SwitchArray1;
558 | HeadsArray = SwitchArrayHead1;
559 | break;
560 | case 1:
561 | SwitchArray = 2;
562 | HeadsGrid = SwitchArray2;
563 | HeadsArray = SwitchArrayHead2;
564 | break;
565 | case 2:
566 | SwitchArray = 0;
567 | HeadsGrid = SwitchArray3;
568 | HeadsArray = SwitchArrayHead3;
569 | break;
570 | }
571 |
572 | Array.Clear(HeadsGrid, 0, HeadsGrid.Length);
573 | // Array.Clear(HeadsArray, 0, Board.BoardWidth * Board.BoardHeight);
574 | HeadsArrayLength = 0;
575 | }
576 | }
577 | }
578 |
--------------------------------------------------------------------------------
/csharp/Form1.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
--------------------------------------------------------------------------------
/csharp/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using System.Windows.Forms;
6 |
7 | namespace WireWorldWindows
8 | {
9 | static class Program
10 | {
11 | ///
12 | /// The main entry point for the application.
13 | ///
14 | [STAThread]
15 | static void Main()
16 | {
17 | Application.EnableVisualStyles();
18 | Application.SetCompatibleTextRenderingDefault(false);
19 | Application.Run(new Form1());
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/csharp/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("WireWorldWindows")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("WireWorldWindows")]
13 | [assembly: AssemblyCopyright("Copyright © 2016")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("16e09683-96fe-4739-8b36-f96c34cbe19e")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/csharp/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace WireWorldWindows.Properties
12 | {
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources
26 | {
27 |
28 | private static global::System.Resources.ResourceManager resourceMan;
29 |
30 | private static global::System.Globalization.CultureInfo resourceCulture;
31 |
32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
33 | internal Resources()
34 | {
35 | }
36 |
37 | ///
38 | /// Returns the cached ResourceManager instance used by this class.
39 | ///
40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
41 | internal static global::System.Resources.ResourceManager ResourceManager
42 | {
43 | get
44 | {
45 | if ((resourceMan == null))
46 | {
47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WireWorldWindows.Properties.Resources", typeof(Resources).Assembly);
48 | resourceMan = temp;
49 | }
50 | return resourceMan;
51 | }
52 | }
53 |
54 | ///
55 | /// Overrides the current thread's CurrentUICulture property for all
56 | /// resource lookups using this strongly typed resource class.
57 | ///
58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
59 | internal static global::System.Globalization.CultureInfo Culture
60 | {
61 | get
62 | {
63 | return resourceCulture;
64 | }
65 | set
66 | {
67 | resourceCulture = value;
68 | }
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/csharp/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/csharp/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace WireWorldWindows.Properties
12 | {
13 |
14 |
15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
18 | {
19 |
20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
21 |
22 | public static Settings Default
23 | {
24 | get
25 | {
26 | return defaultInstance;
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/csharp/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/csharp/WireWorldWindows.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {16E09683-96FE-4739-8B36-F96C34CBE19E}
8 | WinExe
9 | Properties
10 | WireWorldWindows
11 | WireWorldWindows
12 | v4.5.2
13 | 512
14 | true
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | Form
51 |
52 |
53 | Form1.cs
54 |
55 |
56 |
57 |
58 | Form1.cs
59 |
60 |
61 | ResXFileCodeGenerator
62 | Resources.Designer.cs
63 | Designer
64 |
65 |
66 | True
67 | Resources.resx
68 |
69 |
70 | SettingsSingleFileGenerator
71 | Settings.Designer.cs
72 |
73 |
74 | True
75 | Settings.settings
76 | True
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 | Always
85 |
86 |
87 |
88 |
95 |
--------------------------------------------------------------------------------
/deno-gpu/main.ts:
--------------------------------------------------------------------------------
1 | const wireworldtxt = await Deno.readTextFile('./wireworld.txt');
2 |
3 | const rows = wireworldtxt.replace(new RegExp('\r', 'g'), '').split('\n');
4 |
5 | const boardWidth = rows[0].length;
6 | const boardHeight = rows.length;
7 | const workgroupSize = 256;
8 | async function main() {
9 | let inputData = new Uint32Array(boardWidth * boardHeight);
10 |
11 | for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
12 | const row = rows[rowIndex];
13 |
14 | for (let characterIndex = 0; characterIndex < row.length; characterIndex++) {
15 | const character = row[characterIndex];
16 | inputData[characterIndex + rowIndex * boardWidth] = charToState(character);
17 | }
18 | }
19 |
20 | const adapter = (await navigator.gpu.requestAdapter())!;
21 | const device = await adapter!.requestDevice();
22 |
23 | let inputBufferA = device.createBuffer({
24 | size: inputData.byteLength,
25 | usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC,
26 | mappedAtCreation: true,
27 | });
28 | new Uint32Array(inputBufferA.getMappedRange()).set(inputData);
29 | inputBufferA.unmap();
30 |
31 | let inputBufferB = device.createBuffer({
32 | size: inputData.byteLength,
33 | usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC,
34 | mappedAtCreation: true,
35 | });
36 | new Uint32Array(inputBufferB.getMappedRange()).set(inputData);
37 | inputBufferB.unmap();
38 |
39 | const readbackBuffer = device.createBuffer({
40 | size: inputData.byteLength,
41 | usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,
42 | });
43 | // Define Shader
44 | const computeShaderCode = `
45 | struct Data {
46 | data: array
47 | };
48 |
49 | @group(0) @binding(0) var src: Data;
50 | @group(0) @binding(1) var dst: Data;
51 |
52 | fn get_cell_state(x: u32, y: u32) -> u32 {
53 | return src.data[x + y * ${boardWidth}];
54 | }
55 |
56 | fn get_cell_state2(x: u32, y: u32) -> u32 {
57 | return select(0u, 1u, src.data[x + y * ${boardWidth}] == 1);
58 | }
59 |
60 | @compute @workgroup_size(${workgroupSize})
61 | fn main(@builtin(global_invocation_id) global_id: vec3) {
62 | let current_state = src.data[global_id.x];
63 | let x: u32 = global_id.x % ${boardWidth};
64 | let y: u32 = global_id.x / ${boardWidth};
65 |
66 | switch(current_state) {
67 | case 0: {
68 | dst.data[global_id.x] = 0;
69 | break;
70 | }
71 | case 1: {
72 | dst.data[global_id.x] = 2;
73 | break;
74 | }
75 | case 2: {
76 | dst.data[global_id.x] = 3;
77 | break;
78 | }
79 | default: {
80 | var electron_head_count=get_cell_state2(x - 1, y - 1) +
81 | get_cell_state2(x , y - 1) +
82 | get_cell_state2(x + 1, y - 1) +
83 | get_cell_state2(x - 1, y ) +
84 | get_cell_state2(x + 1, y ) +
85 | get_cell_state2(x - 1, y + 1) +
86 | get_cell_state2(x , y + 1) +
87 | get_cell_state2(x + 1, y + 1);
88 |
89 | if(electron_head_count == 1 || electron_head_count == 2){
90 | dst.data[global_id.x]=1;
91 | }else{
92 | dst.data[global_id.x]=3;
93 | }
94 | break;
95 | }
96 | }
97 | }`;
98 |
99 | // Create pipeline
100 | const pipeline = device.createComputePipeline({
101 | compute: {
102 | module: device.createShaderModule({
103 | code: computeShaderCode,
104 | }),
105 | entryPoint: 'main',
106 | },
107 | layout: 'auto',
108 | });
109 |
110 | const bindGroupA = device.createBindGroup({
111 | layout: pipeline.getBindGroupLayout(0),
112 | entries: [
113 | {
114 | binding: 0,
115 | resource: {buffer: inputBufferA},
116 | },
117 | {
118 | binding: 1,
119 | resource: {buffer: inputBufferB},
120 | },
121 | ],
122 | });
123 | const bindGroupB = device.createBindGroup({
124 | layout: pipeline.getBindGroupLayout(0),
125 | entries: [
126 | {
127 | binding: 0,
128 | resource: {buffer: inputBufferB},
129 | },
130 | {
131 | binding: 1,
132 | resource: {buffer: inputBufferA},
133 | },
134 | ],
135 | });
136 |
137 | let i = 0;
138 | let msPerRun = 0;
139 | let isAInput = true;
140 |
141 | while (true) {
142 | let timeRun = performance.now();
143 | i++;
144 | // Dispatch the shader
145 |
146 | let commandEncoder = device.createCommandEncoder();
147 | let passEncoder = commandEncoder.beginComputePass();
148 | passEncoder.setPipeline(pipeline);
149 | passEncoder.setBindGroup(0, isAInput ? bindGroupA : bindGroupB);
150 | passEncoder.dispatchWorkgroups(Math.ceil(inputData.length / workgroupSize));
151 | passEncoder.end();
152 |
153 | device.queue.submit([commandEncoder.finish() ]);
154 |
155 | timeRun = performance.now() - timeRun;
156 | msPerRun += timeRun;
157 |
158 | if (i % 5000 === 0) {
159 | commandEncoder = device.createCommandEncoder();
160 | commandEncoder.copyBufferToBuffer(
161 | isAInput ? inputBufferA : inputBufferB,
162 | 0,
163 | readbackBuffer,
164 | 0,
165 | inputData.byteLength,
166 | );
167 | device.queue.submit([commandEncoder.finish()]);
168 | await readbackBuffer.mapAsync(GPUMapMode.READ);
169 | const readData = new Uint32Array(readbackBuffer.getMappedRange());
170 | // console.log(readData);
171 | readbackBuffer.unmap();
172 | console.log(msPerRun / i);
173 | }
174 | isAInput = !isAInput;
175 | }
176 | }
177 |
178 | export enum WireState {
179 | Empty = 0,
180 | Head = 1,
181 | Tail = 2,
182 | Copper = 3,
183 | }
184 |
185 | function charToState(character: string): WireState {
186 | switch (character) {
187 | case '#':
188 | return WireState.Copper;
189 | case '@':
190 | return WireState.Head;
191 | case '~':
192 | return WireState.Tail;
193 | default:
194 | return WireState.Empty;
195 | }
196 | }
197 |
198 | main();
199 |
200 |
--------------------------------------------------------------------------------
/deno-gpu/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wireworld-gpu-node",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "start": "deno run --allow-read=. --allow-write=. --unstable main.ts"
8 | },
9 | "devDependencies": {
10 | },
11 | "dependencies": {
12 | "esbuild": "^0.19.2",
13 | "typescript": "^5.1.6",
14 | "@webgpu/types": "^0.1.34",
15 | "prettier": "^3.0.1",
16 | "webgpu": "^0.1.16"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/deno-gpu/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "useDefineForClassFields": true,
5 | "module": "ESNext",
6 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
7 | "skipLibCheck": true,
8 |
9 | "types": ["@webgpu/types"],
10 | /* Bundler mode */
11 | "moduleResolution": "bundler",
12 | "allowImportingTsExtensions": true,
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "noEmit": true,
16 |
17 | /* Linting */
18 | "strict": true,
19 | "noFallthroughCasesInSwitch": true
20 | },
21 | "include": ["src"]
22 | }
23 |
--------------------------------------------------------------------------------
/web/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/web/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 2,
3 | "singleQuote": true,
4 | "printWidth": 120,
5 | "bracketSpacing": false
6 | }
7 |
--------------------------------------------------------------------------------
/web/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Wireworld
7 |
42 |
43 |
44 |
45 |
46 |
47 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/web/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wireworld-gpu",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "preview": "vite preview"
10 | },
11 | "devDependencies": {
12 | "typescript": "^5.0.2",
13 | "vite": "^4.4.5"
14 | },
15 | "dependencies": {
16 | "@preact/signals-core": "^1.4.0",
17 | "@webgpu/types": "^0.1.34",
18 | "lil-gui": "^0.18.2",
19 | "prettier": "^3.0.1",
20 | "vite-plugin-wasm": "^3.2.2"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/web/pnpm-lock.yaml:
--------------------------------------------------------------------------------
1 | lockfileVersion: '6.0'
2 |
3 | settings:
4 | autoInstallPeers: true
5 | excludeLinksFromLockfile: false
6 |
7 | dependencies:
8 | '@preact/signals-core':
9 | specifier: ^1.4.0
10 | version: 1.4.0
11 | '@webgpu/types':
12 | specifier: ^0.1.34
13 | version: 0.1.34
14 | lil-gui:
15 | specifier: ^0.18.2
16 | version: 0.18.2
17 | prettier:
18 | specifier: ^3.0.1
19 | version: 3.0.1
20 | vite-plugin-wasm:
21 | specifier: ^3.2.2
22 | version: 3.2.2(vite@4.4.5)
23 |
24 | devDependencies:
25 | typescript:
26 | specifier: ^5.0.2
27 | version: 5.0.2
28 | vite:
29 | specifier: ^4.4.5
30 | version: 4.4.5
31 |
32 | packages:
33 |
34 | /@esbuild/android-arm64@0.18.20:
35 | resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==}
36 | engines: {node: '>=12'}
37 | cpu: [arm64]
38 | os: [android]
39 | requiresBuild: true
40 | optional: true
41 |
42 | /@esbuild/android-arm@0.18.20:
43 | resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==}
44 | engines: {node: '>=12'}
45 | cpu: [arm]
46 | os: [android]
47 | requiresBuild: true
48 | optional: true
49 |
50 | /@esbuild/android-x64@0.18.20:
51 | resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==}
52 | engines: {node: '>=12'}
53 | cpu: [x64]
54 | os: [android]
55 | requiresBuild: true
56 | optional: true
57 |
58 | /@esbuild/darwin-arm64@0.18.20:
59 | resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==}
60 | engines: {node: '>=12'}
61 | cpu: [arm64]
62 | os: [darwin]
63 | requiresBuild: true
64 | optional: true
65 |
66 | /@esbuild/darwin-x64@0.18.20:
67 | resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==}
68 | engines: {node: '>=12'}
69 | cpu: [x64]
70 | os: [darwin]
71 | requiresBuild: true
72 | optional: true
73 |
74 | /@esbuild/freebsd-arm64@0.18.20:
75 | resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==}
76 | engines: {node: '>=12'}
77 | cpu: [arm64]
78 | os: [freebsd]
79 | requiresBuild: true
80 | optional: true
81 |
82 | /@esbuild/freebsd-x64@0.18.20:
83 | resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==}
84 | engines: {node: '>=12'}
85 | cpu: [x64]
86 | os: [freebsd]
87 | requiresBuild: true
88 | optional: true
89 |
90 | /@esbuild/linux-arm64@0.18.20:
91 | resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==}
92 | engines: {node: '>=12'}
93 | cpu: [arm64]
94 | os: [linux]
95 | requiresBuild: true
96 | optional: true
97 |
98 | /@esbuild/linux-arm@0.18.20:
99 | resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==}
100 | engines: {node: '>=12'}
101 | cpu: [arm]
102 | os: [linux]
103 | requiresBuild: true
104 | optional: true
105 |
106 | /@esbuild/linux-ia32@0.18.20:
107 | resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==}
108 | engines: {node: '>=12'}
109 | cpu: [ia32]
110 | os: [linux]
111 | requiresBuild: true
112 | optional: true
113 |
114 | /@esbuild/linux-loong64@0.18.20:
115 | resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==}
116 | engines: {node: '>=12'}
117 | cpu: [loong64]
118 | os: [linux]
119 | requiresBuild: true
120 | optional: true
121 |
122 | /@esbuild/linux-mips64el@0.18.20:
123 | resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==}
124 | engines: {node: '>=12'}
125 | cpu: [mips64el]
126 | os: [linux]
127 | requiresBuild: true
128 | optional: true
129 |
130 | /@esbuild/linux-ppc64@0.18.20:
131 | resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==}
132 | engines: {node: '>=12'}
133 | cpu: [ppc64]
134 | os: [linux]
135 | requiresBuild: true
136 | optional: true
137 |
138 | /@esbuild/linux-riscv64@0.18.20:
139 | resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==}
140 | engines: {node: '>=12'}
141 | cpu: [riscv64]
142 | os: [linux]
143 | requiresBuild: true
144 | optional: true
145 |
146 | /@esbuild/linux-s390x@0.18.20:
147 | resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==}
148 | engines: {node: '>=12'}
149 | cpu: [s390x]
150 | os: [linux]
151 | requiresBuild: true
152 | optional: true
153 |
154 | /@esbuild/linux-x64@0.18.20:
155 | resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==}
156 | engines: {node: '>=12'}
157 | cpu: [x64]
158 | os: [linux]
159 | requiresBuild: true
160 | optional: true
161 |
162 | /@esbuild/netbsd-x64@0.18.20:
163 | resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==}
164 | engines: {node: '>=12'}
165 | cpu: [x64]
166 | os: [netbsd]
167 | requiresBuild: true
168 | optional: true
169 |
170 | /@esbuild/openbsd-x64@0.18.20:
171 | resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==}
172 | engines: {node: '>=12'}
173 | cpu: [x64]
174 | os: [openbsd]
175 | requiresBuild: true
176 | optional: true
177 |
178 | /@esbuild/sunos-x64@0.18.20:
179 | resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==}
180 | engines: {node: '>=12'}
181 | cpu: [x64]
182 | os: [sunos]
183 | requiresBuild: true
184 | optional: true
185 |
186 | /@esbuild/win32-arm64@0.18.20:
187 | resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==}
188 | engines: {node: '>=12'}
189 | cpu: [arm64]
190 | os: [win32]
191 | requiresBuild: true
192 | optional: true
193 |
194 | /@esbuild/win32-ia32@0.18.20:
195 | resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==}
196 | engines: {node: '>=12'}
197 | cpu: [ia32]
198 | os: [win32]
199 | requiresBuild: true
200 | optional: true
201 |
202 | /@esbuild/win32-x64@0.18.20:
203 | resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==}
204 | engines: {node: '>=12'}
205 | cpu: [x64]
206 | os: [win32]
207 | requiresBuild: true
208 | optional: true
209 |
210 | /@preact/signals-core@1.4.0:
211 | resolution: {integrity: sha512-5iYoZBhELLIhUQceZI7sDTQWPb+xcVSn2qk8T/aNl/VMh+A4AiPX9YRSh4XO7fZ6pncrVxl1Iln82poVqYVbbw==}
212 | dev: false
213 |
214 | /@webgpu/types@0.1.34:
215 | resolution: {integrity: sha512-9mXtH+CC8q+Ku7Z+1XazNIte81FvfdXwR2lLRO7Ykzjd/hh1J1krJa0gtnkF1kvP11psUmKEPKo7iMTeEcUpNA==}
216 | dev: false
217 |
218 | /esbuild@0.18.20:
219 | resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==}
220 | engines: {node: '>=12'}
221 | hasBin: true
222 | requiresBuild: true
223 | optionalDependencies:
224 | '@esbuild/android-arm': 0.18.20
225 | '@esbuild/android-arm64': 0.18.20
226 | '@esbuild/android-x64': 0.18.20
227 | '@esbuild/darwin-arm64': 0.18.20
228 | '@esbuild/darwin-x64': 0.18.20
229 | '@esbuild/freebsd-arm64': 0.18.20
230 | '@esbuild/freebsd-x64': 0.18.20
231 | '@esbuild/linux-arm': 0.18.20
232 | '@esbuild/linux-arm64': 0.18.20
233 | '@esbuild/linux-ia32': 0.18.20
234 | '@esbuild/linux-loong64': 0.18.20
235 | '@esbuild/linux-mips64el': 0.18.20
236 | '@esbuild/linux-ppc64': 0.18.20
237 | '@esbuild/linux-riscv64': 0.18.20
238 | '@esbuild/linux-s390x': 0.18.20
239 | '@esbuild/linux-x64': 0.18.20
240 | '@esbuild/netbsd-x64': 0.18.20
241 | '@esbuild/openbsd-x64': 0.18.20
242 | '@esbuild/sunos-x64': 0.18.20
243 | '@esbuild/win32-arm64': 0.18.20
244 | '@esbuild/win32-ia32': 0.18.20
245 | '@esbuild/win32-x64': 0.18.20
246 |
247 | /fsevents@2.3.2:
248 | resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
249 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
250 | os: [darwin]
251 | requiresBuild: true
252 | optional: true
253 |
254 | /lil-gui@0.18.2:
255 | resolution: {integrity: sha512-DgdrLy3/KGC0PiQLKgOcJMPItP4xY4iWgJ9+91Zaxfr8GCTmMps05QS9w9jW7yspILlbscbquwjOwxmWnSx5Uw==}
256 | dev: false
257 |
258 | /nanoid@3.3.6:
259 | resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==}
260 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
261 | hasBin: true
262 |
263 | /picocolors@1.0.0:
264 | resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
265 |
266 | /postcss@8.4.27:
267 | resolution: {integrity: sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==}
268 | engines: {node: ^10 || ^12 || >=14}
269 | dependencies:
270 | nanoid: 3.3.6
271 | picocolors: 1.0.0
272 | source-map-js: 1.0.2
273 |
274 | /prettier@3.0.1:
275 | resolution: {integrity: sha512-fcOWSnnpCrovBsmFZIGIy9UqK2FaI7Hqax+DIO0A9UxeVoY4iweyaFjS5TavZN97Hfehph0nhsZnjlVKzEQSrQ==}
276 | engines: {node: '>=14'}
277 | hasBin: true
278 | dev: false
279 |
280 | /rollup@3.28.0:
281 | resolution: {integrity: sha512-d7zhvo1OUY2SXSM6pfNjgD5+d0Nz87CUp4mt8l/GgVP3oBsPwzNvSzyu1me6BSG9JIgWNTVcafIXBIyM8yQ3yw==}
282 | engines: {node: '>=14.18.0', npm: '>=8.0.0'}
283 | hasBin: true
284 | optionalDependencies:
285 | fsevents: 2.3.2
286 |
287 | /source-map-js@1.0.2:
288 | resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
289 | engines: {node: '>=0.10.0'}
290 |
291 | /typescript@5.0.2:
292 | resolution: {integrity: sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==}
293 | engines: {node: '>=12.20'}
294 | hasBin: true
295 | dev: true
296 |
297 | /vite-plugin-wasm@3.2.2(vite@4.4.5):
298 | resolution: {integrity: sha512-cdbBUNR850AEoMd5nvLmnyeq63CSfoP1ctD/L2vLk/5+wsgAPlAVAzUK5nGKWO/jtehNlrSSHLteN+gFQw7VOA==}
299 | peerDependencies:
300 | vite: ^2 || ^3 || ^4
301 | dependencies:
302 | vite: 4.4.5
303 | dev: false
304 |
305 | /vite@4.4.5:
306 | resolution: {integrity: sha512-4m5kEtAWHYr0O1Fu7rZp64CfO1PsRGZlD3TAB32UmQlpd7qg15VF7ROqGN5CyqN7HFuwr7ICNM2+fDWRqFEKaA==}
307 | engines: {node: ^14.18.0 || >=16.0.0}
308 | hasBin: true
309 | peerDependencies:
310 | '@types/node': '>= 14'
311 | less: '*'
312 | lightningcss: ^1.21.0
313 | sass: '*'
314 | stylus: '*'
315 | sugarss: '*'
316 | terser: ^5.4.0
317 | peerDependenciesMeta:
318 | '@types/node':
319 | optional: true
320 | less:
321 | optional: true
322 | lightningcss:
323 | optional: true
324 | sass:
325 | optional: true
326 | stylus:
327 | optional: true
328 | sugarss:
329 | optional: true
330 | terser:
331 | optional: true
332 | dependencies:
333 | esbuild: 0.18.20
334 | postcss: 8.4.27
335 | rollup: 3.28.0
336 | optionalDependencies:
337 | fsevents: 2.3.2
338 |
--------------------------------------------------------------------------------
/web/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/src/asm/build/optimized.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dested/WireWorld/5420896a3b75a608297dc2a488c0063cec63b43c/web/src/asm/build/optimized.wasm
--------------------------------------------------------------------------------
/web/src/asm/build/optimized.wasm.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["assembly/index.ts"],"names":[],"mappings":"4FAiCE,AAAY,YAKZ,UACK,AAAiB,MAAtB,EAAyB,OAEvB,AAAyB,AADzB,AAAsB,wBAEjB,AAAY,MAAjB,EAAoB,OAGhB,AAAS,AAFX,wCAGE,AAAS,kBADT,IAEA,AAAS,kBAHX,IAKE,AAAoB,IACpB,AAA0B,UACrB,AAAY,MAAjB,EAAoB,OAElB,AAAI,AAlC6B,AAAxB,AAkCG,AAxBL,YAA2B,eAVf,MAkC2B,KAE5C,AAAI,AADJ,OACsB,KACpB,AAAgB,IAChB,IAN+B,WAUrC,AAAI,EAAgB,KACT,gCAET,AAAoB,EAAoB,OAtBV,WAHE,WAfxC","sourceRoot":"assemblyscript:///","sourceContents":["/// \n\n// @external('console', 'logger')\n// declare function logger(offset: usize): void;\n\nconst copperLen: u32 = 8;\nconst size = 605129;\nconst arrLen = 7000;\nconst coppersSize = size * copperLen;\nconst headsArrayOffset = coppersSize + size;\nconst headsGridOffset = headsArrayOffset + arrLen;\nconst tailsArrayOffset = headsGridOffset + size;\nconst tailsGridOffset = tailsArrayOffset + arrLen;\n\nconst newHeadsArrayOffset = tailsGridOffset + size;\nconst newHeadsGridOffset = newHeadsArrayOffset + arrLen;\n\n@inline\nfunction loadBit(offset: u32): u32 {\n return load(offset << alignof());\n}\n\n@inline\nfunction storeBit(offset: u32, value: u32): void {\n store(offset << alignof(), value);\n}\n\n@inline\nfunction loadCopper(offset: u32, pos: u8): u32 {\n return loadBit(offset * copperLen + pos + 1);\n}\n\nexport function init(): void {\n memory.grow(800);\n}\n\nexport function tick(): void {\n let newHeadArrayIndex = 0;\n let hLen = loadBit(headsArrayOffset);\n for (let index: u32 = 1; index <= hLen; ++index) {\n let headKey = loadBit(headsArrayOffset + index);\n let hCopperLen = loadBit(headKey * copperLen);\n for (let i: u8 = 0; i < hCopperLen; ++i) {\n let copperStateIndex = loadCopper(headKey, i);\n if (\n !loadBit(tailsGridOffset + copperStateIndex) &&\n !loadBit(headsGridOffset + copperStateIndex) &&\n !loadBit(newHeadsGridOffset + copperStateIndex)\n ) {\n let headNeighbors = 0;\n let hnCopperLen = loadBit(copperStateIndex * copperLen);\n for (let j: u8 = 0; j < hnCopperLen; ++j) {\n let stateIndex = loadCopper(copperStateIndex, j);\n if (loadBit(headsGridOffset + stateIndex) === 1) {\n ++headNeighbors;\n if (headNeighbors === 3) {\n headNeighbors = 0;\n break;\n }\n }\n }\n if (headNeighbors > 0) {\n storeBit(newHeadsGridOffset + copperStateIndex, 1);\n storeBit(newHeadsArrayOffset + newHeadArrayIndex + 1, copperStateIndex);\n newHeadArrayIndex = newHeadArrayIndex + 1;\n }\n }\n }\n }\n storeBit(newHeadsArrayOffset, newHeadArrayIndex);\n}\n"]}
--------------------------------------------------------------------------------
/web/src/asm/build/optimized.wat:
--------------------------------------------------------------------------------
1 | (module
2 | (type $v (func))
3 | (import "env" "memory" (memory $0 0))
4 | (table $0 1 anyfunc)
5 | (elem (i32.const 0) $null)
6 | (export "memory" (memory $0))
7 | (export "table" (table $0))
8 | (export "init" (func $assembly/index/init))
9 | (export "tick" (func $assembly/index/tick))
10 | (func $assembly/index/init (; 0 ;) (type $v)
11 | i32.const 800
12 | grow_memory
13 | drop
14 | )
15 | (func $assembly/index/tick (; 1 ;) (type $v)
16 | (local $0 i32)
17 | (local $1 i32)
18 | (local $2 i32)
19 | (local $3 i32)
20 | (local $4 i32)
21 | (local $5 i32)
22 | (local $6 i32)
23 | (local $7 i32)
24 | (local $8 i32)
25 | (local $9 i32)
26 | i32.const 21784644
27 | i32.load
28 | set_local $6
29 | i32.const 1
30 | set_local $3
31 | loop $repeat|0
32 | block $break|0
33 | get_local $3
34 | get_local $6
35 | i32.gt_u
36 | br_if $break|0
37 | get_local $3
38 | i32.const 5446161
39 | i32.add
40 | i32.const 2
41 | i32.shl
42 | i32.load
43 | tee_local $7
44 | i32.const 5
45 | i32.shl
46 | i32.load
47 | set_local $8
48 | i32.const 0
49 | set_local $4
50 | loop $repeat|1
51 | block $break|1
52 | get_local $4
53 | get_local $8
54 | i32.ge_u
55 | br_if $break|1
56 | get_local $4
57 | i32.const 255
58 | i32.and
59 | get_local $7
60 | i32.const 3
61 | i32.shl
62 | i32.add
63 | i32.const 1
64 | i32.add
65 | i32.const 2
66 | i32.shl
67 | i32.load
68 | tee_local $1
69 | i32.const 6065290
70 | i32.add
71 | i32.const 2
72 | i32.shl
73 | i32.load
74 | i32.eqz
75 | tee_local $0
76 | if
77 | get_local $1
78 | i32.const 5453161
79 | i32.add
80 | i32.const 2
81 | i32.shl
82 | i32.load
83 | i32.eqz
84 | set_local $0
85 | end
86 | get_local $0
87 | if
88 | get_local $1
89 | i32.const 6677419
90 | i32.add
91 | i32.const 2
92 | i32.shl
93 | i32.load
94 | i32.eqz
95 | set_local $0
96 | end
97 | get_local $0
98 | if
99 | i32.const 0
100 | set_local $0
101 | get_local $1
102 | i32.const 5
103 | i32.shl
104 | i32.load
105 | set_local $9
106 | i32.const 0
107 | set_local $5
108 | loop $repeat|2
109 | block $break|2
110 | get_local $5
111 | get_local $9
112 | i32.ge_u
113 | br_if $break|2
114 | get_local $5
115 | i32.const 255
116 | i32.and
117 | get_local $1
118 | i32.const 3
119 | i32.shl
120 | i32.add
121 | i32.const 1
122 | i32.add
123 | i32.const 2
124 | i32.shl
125 | i32.load
126 | i32.const 5453161
127 | i32.add
128 | i32.const 2
129 | i32.shl
130 | i32.load
131 | i32.const 1
132 | i32.eq
133 | if
134 | get_local $0
135 | i32.const 1
136 | i32.add
137 | tee_local $0
138 | i32.const 3
139 | i32.eq
140 | if
141 | i32.const 0
142 | set_local $0
143 | br $break|2
144 | end
145 | end
146 | get_local $5
147 | i32.const 1
148 | i32.add
149 | set_local $5
150 | br $repeat|2
151 | end
152 | end
153 | get_local $0
154 | i32.const 0
155 | i32.gt_s
156 | if
157 | get_local $1
158 | i32.const 6677419
159 | i32.add
160 | i32.const 2
161 | i32.shl
162 | i32.const 1
163 | i32.store
164 | get_local $2
165 | i32.const 6670420
166 | i32.add
167 | i32.const 2
168 | i32.shl
169 | get_local $1
170 | i32.store
171 | get_local $2
172 | i32.const 1
173 | i32.add
174 | set_local $2
175 | end
176 | end
177 | get_local $4
178 | i32.const 1
179 | i32.add
180 | set_local $4
181 | br $repeat|1
182 | end
183 | end
184 | get_local $3
185 | i32.const 1
186 | i32.add
187 | set_local $3
188 | br $repeat|0
189 | end
190 | end
191 | i32.const 26681676
192 | get_local $2
193 | i32.store
194 | )
195 | (func $null (; 2 ;) (type $v)
196 | nop
197 | )
198 | )
199 |
--------------------------------------------------------------------------------
/web/src/asm/build/untouched.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dested/WireWorld/5420896a3b75a608297dc2a488c0063cec63b43c/web/src/asm/build/untouched.wasm
--------------------------------------------------------------------------------
/web/src/asm/build/untouched.wasm.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["assembly/index.ts"],"names":[],"mappings":"8KAiCE,AAAY,YAIZ,AAAwB,IACxB,AAAW,EAAQ,eACnB,EAAK,AAAiB,MAAG,EAAS,MAAe,EAC/C,AAAc,EAAQ,EAAmB,gBACzC,AAAiB,EAAQ,EAAU,gBACnC,EAAK,AAAY,MAAG,EAAI,MAAiB,EACvC,AAAuB,iCACvB,AACE,AAAC,EAAQ,EAAkB,mBAC3B,AAAC,EAAQ,EAAkB,0BAC3B,AAAC,EAAQ,EAAqB,wBAE9B,AAAoB,IACpB,AAAkB,EAAQ,EAAmB,gBAC7C,EAAK,AAAY,MAAG,EAAI,MAAkB,EACxC,AAAiB,EAvBkB,EAA5B,EAAS,GAAY,OAAM,iBAwBlC,AAAI,EAAQ,EAAkB,KAlCG,AAAxB,EAAU,OAkC2B,KAC5C,AAAE,OACF,AAAI,EAAkB,KACpB,AAAgB,IAChB,KAN+B,AAAE,aAUvC,AAAI,EAAgB,KACT,EAAqB,KAAkB,cACvC,EAAsB,GAAoB,eACnD,AAAoB,EAAoB,QAtBV,AAAE,cAHA,AAAE,aA8BjC,IA7CT,AAAW,EAAU,GAAgB","sourceRoot":"assemblyscript:///","sourceContents":["/// \n\n// @external('console', 'logger')\n// declare function logger(offset: usize): void;\n\nconst copperLen: u32 = 8;\nconst size = 605129;\nconst arrLen = 7000;\nconst coppersSize = size * copperLen;\nconst headsArrayOffset = coppersSize + size;\nconst headsGridOffset = headsArrayOffset + arrLen;\nconst tailsArrayOffset = headsGridOffset + size;\nconst tailsGridOffset = tailsArrayOffset + arrLen;\n\nconst newHeadsArrayOffset = tailsGridOffset + size;\nconst newHeadsGridOffset = newHeadsArrayOffset + arrLen;\n\n@inline\nfunction loadBit(offset: u32): u32 {\n return load(offset << alignof());\n}\n\n@inline\nfunction storeBit(offset: u32, value: u32): void {\n store(offset << alignof(), value);\n}\n\n@inline\nfunction loadCopper(offset: u32, pos: u8): u32 {\n return loadBit(offset * copperLen + pos + 1);\n}\n\nexport function init(): void {\n memory.grow(800);\n}\n\nexport function tick(): void {\n let newHeadArrayIndex = 0;\n let hLen = loadBit(headsArrayOffset);\n for (let index: u32 = 1; index <= hLen; ++index) {\n let headKey = loadBit(headsArrayOffset + index);\n let hCopperLen = loadBit(headKey * copperLen);\n for (let i: u8 = 0; i < hCopperLen; ++i) {\n let copperStateIndex = loadCopper(headKey, i);\n if (\n !loadBit(tailsGridOffset + copperStateIndex) &&\n !loadBit(headsGridOffset + copperStateIndex) &&\n !loadBit(newHeadsGridOffset + copperStateIndex)\n ) {\n let headNeighbors = 0;\n let hnCopperLen = loadBit(copperStateIndex * copperLen);\n for (let j: u8 = 0; j < hnCopperLen; ++j) {\n let stateIndex = loadCopper(copperStateIndex, j);\n if (loadBit(headsGridOffset + stateIndex) === 1) {\n ++headNeighbors;\n if (headNeighbors === 3) {\n headNeighbors = 0;\n break;\n }\n }\n }\n if (headNeighbors > 0) {\n storeBit(newHeadsGridOffset + copperStateIndex, 1);\n storeBit(newHeadsArrayOffset + newHeadArrayIndex + 1, copperStateIndex);\n newHeadArrayIndex = newHeadArrayIndex + 1;\n }\n }\n }\n }\n storeBit(newHeadsArrayOffset, newHeadArrayIndex);\n}\n"]}
--------------------------------------------------------------------------------
/web/src/asm/build/untouched.wat:
--------------------------------------------------------------------------------
1 | (module
2 | (type $v (func))
3 | (import "env" "memory" (memory $0 0))
4 | (table $0 1 anyfunc)
5 | (elem (i32.const 0) $null)
6 | (global $assembly/index/copperLen i32 (i32.const 8))
7 | (global $assembly/index/size i32 (i32.const 605129))
8 | (global $assembly/index/arrLen i32 (i32.const 7000))
9 | (global $assembly/index/coppersSize i32 (i32.const 4841032))
10 | (global $assembly/index/headsArrayOffset i32 (i32.const 5446161))
11 | (global $assembly/index/headsGridOffset i32 (i32.const 5453161))
12 | (global $assembly/index/tailsArrayOffset i32 (i32.const 6058290))
13 | (global $assembly/index/tailsGridOffset i32 (i32.const 6065290))
14 | (global $assembly/index/newHeadsArrayOffset i32 (i32.const 6670419))
15 | (global $assembly/index/newHeadsGridOffset i32 (i32.const 6677419))
16 | (global $HEAP_BASE i32 (i32.const 8))
17 | (export "memory" (memory $0))
18 | (export "table" (table $0))
19 | (export "init" (func $assembly/index/init))
20 | (export "tick" (func $assembly/index/tick))
21 | (func $assembly/index/init (; 0 ;) (type $v)
22 | i32.const 800
23 | grow_memory
24 | drop
25 | )
26 | (func $assembly/index/tick (; 1 ;) (type $v)
27 | (local $0 i32)
28 | (local $1 i32)
29 | (local $2 i32)
30 | (local $3 i32)
31 | (local $4 i32)
32 | (local $5 i32)
33 | (local $6 i32)
34 | (local $7 i32)
35 | (local $8 i32)
36 | (local $9 i32)
37 | (local $10 i32)
38 | (local $11 i32)
39 | i32.const 0
40 | set_local $0
41 | block $assembly/index/loadBit|inlined.0 (result i32)
42 | get_global $assembly/index/headsArrayOffset
43 | set_local $1
44 | get_local $1
45 | i32.const 2
46 | i32.shl
47 | i32.load
48 | end
49 | set_local $1
50 | block $break|0
51 | i32.const 1
52 | set_local $2
53 | loop $repeat|0
54 | get_local $2
55 | get_local $1
56 | i32.le_u
57 | i32.eqz
58 | br_if $break|0
59 | block
60 | block $assembly/index/loadBit|inlined.1 (result i32)
61 | get_global $assembly/index/headsArrayOffset
62 | get_local $2
63 | i32.add
64 | set_local $3
65 | get_local $3
66 | i32.const 2
67 | i32.shl
68 | i32.load
69 | end
70 | set_local $3
71 | block $assembly/index/loadBit|inlined.2 (result i32)
72 | get_local $3
73 | get_global $assembly/index/copperLen
74 | i32.mul
75 | set_local $4
76 | get_local $4
77 | i32.const 2
78 | i32.shl
79 | i32.load
80 | end
81 | set_local $4
82 | block $break|1
83 | i32.const 0
84 | set_local $5
85 | loop $repeat|1
86 | get_local $5
87 | get_local $4
88 | i32.lt_u
89 | i32.eqz
90 | br_if $break|1
91 | block
92 | block $assembly/index/loadCopper|inlined.0 (result i32)
93 | block $assembly/index/loadBit|inlined.3 (result i32)
94 | get_local $3
95 | get_global $assembly/index/copperLen
96 | i32.mul
97 | get_local $5
98 | i32.const 255
99 | i32.and
100 | i32.add
101 | i32.const 1
102 | i32.add
103 | set_local $6
104 | get_local $6
105 | i32.const 2
106 | i32.shl
107 | i32.load
108 | end
109 | end
110 | set_local $6
111 | block $assembly/index/loadBit|inlined.7 (result i32)
112 | get_global $assembly/index/tailsGridOffset
113 | get_local $6
114 | i32.add
115 | set_local $7
116 | get_local $7
117 | i32.const 2
118 | i32.shl
119 | i32.load
120 | end
121 | i32.eqz
122 | tee_local $7
123 | if (result i32)
124 | block $assembly/index/loadBit|inlined.8 (result i32)
125 | get_global $assembly/index/headsGridOffset
126 | get_local $6
127 | i32.add
128 | set_local $7
129 | get_local $7
130 | i32.const 2
131 | i32.shl
132 | i32.load
133 | end
134 | i32.eqz
135 | else
136 | get_local $7
137 | end
138 | tee_local $7
139 | i32.const 0
140 | i32.ne
141 | if (result i32)
142 | block $assembly/index/loadBit|inlined.9 (result i32)
143 | get_global $assembly/index/newHeadsGridOffset
144 | get_local $6
145 | i32.add
146 | set_local $7
147 | get_local $7
148 | i32.const 2
149 | i32.shl
150 | i32.load
151 | end
152 | i32.eqz
153 | else
154 | get_local $7
155 | end
156 | i32.const 0
157 | i32.ne
158 | if
159 | i32.const 0
160 | set_local $7
161 | block $assembly/index/loadBit|inlined.10 (result i32)
162 | get_local $6
163 | get_global $assembly/index/copperLen
164 | i32.mul
165 | set_local $8
166 | get_local $8
167 | i32.const 2
168 | i32.shl
169 | i32.load
170 | end
171 | set_local $8
172 | block $break|2
173 | i32.const 0
174 | set_local $9
175 | loop $repeat|2
176 | get_local $9
177 | get_local $8
178 | i32.lt_u
179 | i32.eqz
180 | br_if $break|2
181 | block
182 | block $assembly/index/loadCopper|inlined.1 (result i32)
183 | block $assembly/index/loadBit|inlined.11 (result i32)
184 | get_local $6
185 | get_global $assembly/index/copperLen
186 | i32.mul
187 | get_local $9
188 | i32.const 255
189 | i32.and
190 | i32.add
191 | i32.const 1
192 | i32.add
193 | set_local $10
194 | get_local $10
195 | i32.const 2
196 | i32.shl
197 | i32.load
198 | end
199 | end
200 | set_local $10
201 | block $assembly/index/loadBit|inlined.13 (result i32)
202 | get_global $assembly/index/headsGridOffset
203 | get_local $10
204 | i32.add
205 | set_local $11
206 | get_local $11
207 | i32.const 2
208 | i32.shl
209 | i32.load
210 | end
211 | i32.const 1
212 | i32.eq
213 | if
214 | get_local $7
215 | i32.const 1
216 | i32.add
217 | set_local $7
218 | get_local $7
219 | i32.const 3
220 | i32.eq
221 | if
222 | i32.const 0
223 | set_local $7
224 | br $break|2
225 | end
226 | end
227 | end
228 | get_local $9
229 | i32.const 1
230 | i32.add
231 | set_local $9
232 | br $repeat|2
233 | unreachable
234 | end
235 | unreachable
236 | end
237 | get_local $7
238 | i32.const 0
239 | i32.gt_s
240 | if
241 | get_global $assembly/index/newHeadsGridOffset
242 | get_local $6
243 | i32.add
244 | set_local $9
245 | i32.const 1
246 | set_local $10
247 | get_local $9
248 | i32.const 2
249 | i32.shl
250 | get_local $10
251 | i32.store
252 | get_global $assembly/index/newHeadsArrayOffset
253 | get_local $0
254 | i32.add
255 | i32.const 1
256 | i32.add
257 | set_local $10
258 | get_local $10
259 | i32.const 2
260 | i32.shl
261 | get_local $6
262 | i32.store
263 | get_local $0
264 | i32.const 1
265 | i32.add
266 | set_local $0
267 | end
268 | end
269 | end
270 | get_local $5
271 | i32.const 1
272 | i32.add
273 | set_local $5
274 | br $repeat|1
275 | unreachable
276 | end
277 | unreachable
278 | end
279 | end
280 | get_local $2
281 | i32.const 1
282 | i32.add
283 | set_local $2
284 | br $repeat|0
285 | unreachable
286 | end
287 | unreachable
288 | end
289 | get_global $assembly/index/newHeadsArrayOffset
290 | set_local $2
291 | get_local $2
292 | i32.const 2
293 | i32.shl
294 | get_local $0
295 | i32.store
296 | )
297 | (func $null (; 2 ;) (type $v)
298 | )
299 | )
300 |
--------------------------------------------------------------------------------
/web/src/common.ts:
--------------------------------------------------------------------------------
1 | import {signal} from '@preact/signals-core';
2 |
3 | export const timePerTick = signal(0);
4 | export const runFast = signal(false);
5 | export const runFastTicks = signal(50);
6 | export const runFastTicksGPU = signal(500);
7 |
--------------------------------------------------------------------------------
/web/src/entry.ts:
--------------------------------------------------------------------------------
1 | import * as dat from 'lil-gui';
2 | import {runFast, timePerTick} from './common.ts';
3 | import {WireworldJavascript} from './javascript.ts';
4 | import {WireworldWebASM} from './webASM.ts';
5 | import {WireworldWebGPU} from './webGPU.ts';
6 |
7 | async function main() {
8 | var obj = {
9 | message: '',
10 | type: 'javascript',
11 | runFast: false,
12 | get timePerTick() {
13 | return `MS Per Run: ${timePerTick.peek().toFixed(5)}`;
14 | },
15 | };
16 |
17 | var gui = new dat.GUI();
18 |
19 | gui.add(obj, 'runFast').onChange((e) => {
20 | runFast.value = e;
21 | });
22 |
23 | // gui.add(obj, 'maxSize').min(-10).max(10).step(0.25);
24 |
25 | // Choose from accepted values
26 | let current: {start: () => void; stop: () => void} = new WireworldJavascript();
27 | current.start();
28 |
29 | gui.add(obj, 'type', ['javascript', 'webasm', 'webgpu']).onChange((e) => {
30 | current.stop();
31 | switch (obj.type) {
32 | case 'javascript':
33 | current = new WireworldJavascript();
34 | break;
35 | case 'webasm':
36 | current = new WireworldWebASM();
37 | break;
38 | case 'webgpu':
39 | current = new WireworldWebGPU();
40 | break;
41 | }
42 | current.start();
43 | });
44 | let div2 = document.createElement('div');
45 | div2.style.marginTop = '20px';
46 | div2.style.marginBottom = '20px';
47 | div2.style.marginLeft = '8px';
48 | div2.style.marginRight = '8px';
49 | gui.$children.prepend(div2);
50 |
51 | let a = document.createElement('a');
52 | a.style.fontSize = '20px';
53 | a.style.marginTop = '20px';
54 | a.style.marginBottom = '20px';
55 | a.style.marginLeft = '8px';
56 | a.style.marginRight = '8px';
57 | a.style.display = 'block';
58 | a.style.color = 'white';
59 | a.innerText = 'About me';
60 | a.href = 'https://dested.com';
61 | gui.$children.prepend(a);
62 | a = document.createElement('a');
63 | a.style.fontSize = '20px';
64 | a.style.marginTop = '20px';
65 | a.style.marginBottom = '20px';
66 | a.style.marginLeft = '8px';
67 | a.style.marginRight = '8px';
68 | a.style.display = 'block';
69 | a.style.color = 'white';
70 | a.innerText = 'Github';
71 | a.href = 'https://github.com/dested/WireWorld';
72 | gui.$children.prepend(a);
73 |
74 | a = document.createElement('a');
75 | a.style.fontSize = '20px';
76 | a.style.marginTop = '20px';
77 | a.style.marginBottom = '20px';
78 | a.style.marginLeft = '8px';
79 | a.style.marginRight = '8px';
80 | a.style.color = 'white';
81 | a.style.display = 'block';
82 | a.innerText = 'Wireworld Computer';
83 | a.href = 'https://www.quinapalus.com/wi-index.html';
84 | gui.$children.prepend(a);
85 | a = document.createElement('a');
86 | a.style.fontSize = '20px';
87 | a.style.marginTop = '20px';
88 | a.style.marginBottom = '20px';
89 | a.style.marginLeft = '8px';
90 | a.style.marginRight = '8px';
91 | a.style.display = 'block';
92 | a.style.color = 'white';
93 | a.innerText = 'WireWorld';
94 | a.href = 'https://en.wikipedia.org/wiki/Wireworld';
95 | gui.$children.prepend(a);
96 |
97 | const div = document.createElement('div');
98 | div.style.fontSize = '20px';
99 | div.style.marginTop = '20px';
100 | div.style.marginBottom = '20px';
101 | div.style.marginLeft = '8px';
102 | div.style.marginRight = '8px';
103 | div.innerText = 'This is the WireWorld Cellular Automata running in WebGPU, WebASM, and Javascript.';
104 | gui.$children.prepend(div);
105 |
106 | const tpt = gui.add(obj, 'timePerTick');
107 | setInterval(() => {
108 | tpt.updateDisplay();
109 | }, 1000);
110 | }
111 |
112 | main();
113 |
--------------------------------------------------------------------------------
/web/src/javascript.ts:
--------------------------------------------------------------------------------
1 | import {runFast, runFastTicks, timePerTick} from './common.ts';
2 | import wireworldtxt from './wireworld.txt?raw';
3 |
4 | const copperLen = 8;
5 | const size = 605129;
6 | const arrLen = 7000;
7 | const coppersSize = size * copperLen;
8 | const headsArrayOffset = coppersSize + size;
9 | const headsGridOffset = headsArrayOffset + arrLen;
10 | const tailsArrayOffset = headsGridOffset + size;
11 | const tailsGridOffset = tailsArrayOffset + arrLen;
12 |
13 | const newHeadsArrayOffset = tailsGridOffset + size;
14 | const newHeadsGridOffset = newHeadsArrayOffset + arrLen;
15 | const newTailsArrayOffset = newHeadsGridOffset + size;
16 | const newTailsGridOffset = newTailsArrayOffset + arrLen;
17 |
18 | export class StatePosition {
19 | stateIndex;
20 |
21 | static getY(z: number): number {
22 | return (z / Board.boardWidth) | 0;
23 | }
24 |
25 | static getX(z: number): number {
26 | return z % Board.boardWidth;
27 | }
28 |
29 | constructor(
30 | public x: number,
31 | public y: number,
32 | ) {
33 | this.stateIndex = this.x + this.y * Board.boardWidth;
34 | }
35 | }
36 |
37 | export class WireworldJavascript {
38 | magnify = 1;
39 | private iterations = 0;
40 | mem32: Uint32Array;
41 |
42 | timeout = 0;
43 | drawTimeout = 0;
44 | stop() {
45 | clearInterval(this.timeout);
46 | clearInterval(this.drawTimeout);
47 | this.mem32 = null;
48 | }
49 |
50 | start() {
51 | let boardState: BoardState = null;
52 | const down = false;
53 | let canvasBack: HTMLCanvasElement;
54 | let contextBack: CanvasRenderingContext2D;
55 | let canvasFront: HTMLCanvasElement;
56 | let contextFront: CanvasRenderingContext2D;
57 | const draw = true;
58 | if (draw) {
59 | canvasBack = document.getElementById('canvasBack') as HTMLCanvasElement;
60 | contextBack = canvasBack.getContext('2d') as CanvasRenderingContext2D;
61 |
62 | canvasFront = document.getElementById('canvasFront') as HTMLCanvasElement;
63 | contextFront = canvasFront.getContext('2d') as CanvasRenderingContext2D;
64 | }
65 |
66 | const board = new Board(wireworldtxt);
67 | // ele.Html(board.ToString());
68 | boardState = this.generateInitialBoardState();
69 | let ticks = 0;
70 | let totalMs = 0;
71 | this.mem32 = new Uint32Array(121 * 64 * 1024);
72 |
73 | this.mem32[headsArrayOffset] = boardState.headsArray.length;
74 | this.mem32[tailsArrayOffset] = boardState.tailsArray.length;
75 |
76 | for (let i = 0; i < boardState.headsArray.length; i++) {
77 | this.mem32[headsArrayOffset + i + 1] = boardState.headsArray[i];
78 | }
79 | for (let i = 0; i < boardState.tailsArray.length; i++) {
80 | this.mem32[tailsArrayOffset + i + 1] = boardState.tailsArray[i];
81 | }
82 |
83 | for (let y = 0; y < Board.boardHeight; y++) {
84 | for (let x = 0; x < Board.boardWidth; x++) {
85 | const pos = y * Board.boardWidth + x;
86 | this.mem32[headsGridOffset + pos] = boardState.headsGrid[pos] ? 1 : 0;
87 | this.mem32[tailsGridOffset + pos] = boardState.tailsGrid[pos] ? 1 : 0;
88 | }
89 | }
90 |
91 | for (let y = 0; y < Board.boardHeight; y++) {
92 | for (let x = 0; x < Board.boardWidth; x++) {
93 | const pos = y * Board.boardWidth + x;
94 | if (!Board.coppers[pos]) {
95 | continue;
96 | }
97 | this.mem32[pos * copperLen] = Board.coppers[pos].length;
98 | for (let i = 0; i < Board.coppers[pos].length; i++) {
99 | this.mem32[pos * copperLen + i + 1] = Board.coppers[pos][i];
100 | }
101 | }
102 | }
103 |
104 | this.timeout = setInterval(() => {
105 | if (down) {
106 | return;
107 | }
108 | const crank = runFast.peek() ? runFastTicks.peek() : 1;
109 |
110 | for (let i = 0; i < crank; i++) {
111 | const perf = performance.now();
112 | this.tickBoard();
113 | this.iterations++;
114 | const res = performance.now() - perf;
115 | totalMs += res;
116 | ticks++;
117 | if (ticks % 100 === 0) {
118 | timePerTick.value = totalMs / ticks;
119 | totalMs = 0;
120 | ticks = 0;
121 | }
122 | }
123 | }, 1);
124 | if (draw) {
125 | canvasBack.width = canvasFront.width = Board.boardWidth * this.magnify;
126 | canvasBack.height = canvasFront.height = Board.boardHeight * this.magnify;
127 |
128 | this.drawBack(contextBack);
129 | this.drawFront(contextFront, boardState);
130 | this.drawTimeout = setInterval(() => {
131 | const newBoardState = new BoardState();
132 | newBoardState.headsGrid = new Array(size);
133 | newBoardState.tailsGrid = new Array(size);
134 | newBoardState.headsArray = [];
135 | newBoardState.tailsArray = [];
136 | const hLen = this.mem32[headsArrayOffset];
137 | for (let i = 0; i < hLen; i++) {
138 | newBoardState.headsArray.push(this.mem32[headsArrayOffset + i + 1]);
139 | }
140 | const tLen = this.mem32[tailsArrayOffset];
141 | for (let i = 0; i < tLen; i++) {
142 | newBoardState.tailsArray.push(this.mem32[tailsArrayOffset + i + 1]);
143 | }
144 |
145 | for (let y = 0; y < Board.boardHeight; y++) {
146 | for (let x = 0; x < Board.boardWidth; x++) {
147 | const pos = y * Board.boardWidth + x;
148 | newBoardState.headsGrid[pos] = this.mem32[pos + headsGridOffset] === 1;
149 | newBoardState.tailsGrid[pos] = this.mem32[pos + tailsGridOffset] === 1;
150 | }
151 | }
152 |
153 | boardState = newBoardState;
154 | // console.log(boardState.headsArray.length, boardState.tailsArray.length);
155 | this.drawFront(contextFront, boardState);
156 | }, 1000 / 60);
157 | }
158 | }
159 |
160 | drawBack(context: CanvasRenderingContext2D): void {
161 | context.fillStyle = '#000000';
162 | context.fillRect(0, 0, Board.boardWidth * this.magnify, Board.boardHeight * this.magnify);
163 |
164 | for (let y = 0; y < Board.boardHeight; y++) {
165 | for (let x = 0; x < Board.boardWidth; x++) {
166 | if (Board.copperGrid[x + y * Board.boardWidth]) {
167 | context.fillStyle = '#1000A8';
168 | context.fillRect(x * this.magnify, y * this.magnify, this.magnify, this.magnify);
169 | }
170 | }
171 | }
172 | }
173 |
174 | redrawBack(context: CanvasRenderingContext2D, updates: {[key: number]: boolean}): void {
175 | for (let y = 0; y < Board.boardHeight; y++) {
176 | for (let x = 0; x < Board.boardWidth; x++) {
177 | const stateIndex = x + y * Board.boardWidth;
178 | if (updates[stateIndex]) {
179 | if (Board.copperGrid[stateIndex]) {
180 | context.fillStyle = '#1000A8';
181 | context.fillRect(x * this.magnify, y * this.magnify, this.magnify, this.magnify);
182 | } else {
183 | context.fillStyle = '#000000';
184 | context.fillRect(x * this.magnify, y * this.magnify, this.magnify, this.magnify);
185 | }
186 | }
187 | }
188 | }
189 | }
190 |
191 | drawFront(context: CanvasRenderingContext2D, boardState: BoardState): void {
192 | context.clearRect(0, 0, Board.boardWidth * this.magnify, Board.boardHeight * this.magnify);
193 |
194 | const heads = boardState.headsArray;
195 | const tails = boardState.tailsArray;
196 | context.save();
197 | context.fillStyle = '#FFF59B';
198 | for (let index = 0, headLength = heads.length; index < headLength; index++) {
199 | context.fillRect(
200 | StatePosition.getX(heads[index]) * this.magnify,
201 | StatePosition.getY(heads[index]) * this.magnify,
202 | this.magnify,
203 | this.magnify,
204 | );
205 | }
206 | context.fillStyle = '#89D2FF';
207 | for (let index = 0, tailLength = tails.length; index < tailLength; index++) {
208 | context.fillRect(
209 | StatePosition.getX(tails[index]) * this.magnify,
210 | StatePosition.getY(tails[index]) * this.magnify,
211 | this.magnify,
212 | this.magnify,
213 | );
214 | }
215 | context.restore();
216 | }
217 |
218 | private static neighbors: StatePosition[];
219 |
220 | generateInitialBoardState(): BoardState {
221 | const boardState = new BoardState();
222 | const boardWidth = Board.boardWidth;
223 | const boardHeight = Board.boardHeight;
224 | boardState.tailsGrid = new Array(boardWidth * boardHeight);
225 | boardState.tailsArray = new Array(boardWidth * boardHeight);
226 |
227 | WireworldJavascript.neighbors = [
228 | new StatePosition(-1, -1),
229 | new StatePosition(-1, 0),
230 | new StatePosition(-1, 1),
231 | new StatePosition(0, -1),
232 | new StatePosition(0, 1),
233 | new StatePosition(1, -1),
234 | new StatePosition(1, 0),
235 | new StatePosition(1, 1),
236 | ];
237 |
238 | Board.copperGrid = new Array(boardWidth * boardHeight);
239 | Board.coppers = new Array(boardWidth * boardHeight);
240 |
241 | for (let y = 0; y < boardHeight; y++) {
242 | for (let x = 0; x < boardWidth; x++) {
243 | const statePos = new StatePosition(x, y);
244 | switch (Board.initialStates[statePos.stateIndex]) {
245 | case WireState.Head:
246 | Board.copperGrid[statePos.stateIndex] = true;
247 | boardState.headsArray[boardState.headsArray.length] = statePos.stateIndex;
248 | boardState.headsGrid[statePos.stateIndex] = true;
249 | break;
250 | case WireState.Tail:
251 | Board.copperGrid[statePos.stateIndex] = true;
252 | boardState.tailsArray[boardState.tailsArray.length] = statePos.stateIndex;
253 | boardState.tailsGrid[statePos.stateIndex] = true;
254 | break;
255 | case WireState.Copper:
256 | Board.copperGrid[statePos.stateIndex] = true;
257 | break;
258 | }
259 | }
260 | }
261 | this.buildCoppers();
262 | return boardState;
263 | }
264 |
265 | buildCoppers(): void {
266 | const boardHeight = Board.boardHeight;
267 | const boardWidth = Board.boardWidth;
268 | for (let y = 0; y < boardHeight; y++) {
269 | for (let x = 0; x < boardWidth; x++) {
270 | const stateIndex = x + y * boardWidth;
271 | if (Board.copperGrid[stateIndex]) {
272 | Board.coppers[stateIndex] = this.getNeighborStates(x, y);
273 | }
274 | }
275 | }
276 | }
277 |
278 | getNeighborStates(x: number, y: number): number[] {
279 | const boardWidth = Board.boardWidth;
280 |
281 | const statePositions: number[] = [];
282 |
283 | for (let index = 0; index < WireworldJavascript.neighbors.length; index++) {
284 | const statePosition = WireworldJavascript.neighbors[index];
285 | const stateIndex = x + statePosition.x + (y + statePosition.y) * boardWidth;
286 | if (Board.copperGrid[stateIndex]) {
287 | statePositions.push(new StatePosition(x + statePosition.x, y + statePosition.y).stateIndex);
288 | }
289 | }
290 | return statePositions;
291 | }
292 |
293 | tickBoard() {
294 | const loadBit = (offset: number) => {
295 | return this.mem32[offset];
296 | };
297 |
298 | const storeBit = (offset: number, value: number) => {
299 | this.mem32[offset] = value;
300 | };
301 |
302 | const loadCopper = (offset: number, pos: number): number => {
303 | return loadBit(offset * copperLen + pos + 1);
304 | };
305 | let newHeadArrayIndex = 0;
306 | const hLen = loadBit(headsArrayOffset);
307 | for (let index = 1; index <= hLen; index++) {
308 | const headKey = loadBit(headsArrayOffset + index);
309 | const hCopperLen = loadBit(headKey * copperLen);
310 | for (let i = 0; i < hCopperLen; i++) {
311 | const copperStateIndex = loadCopper(headKey, i);
312 | if (
313 | loadBit(tailsGridOffset + copperStateIndex) === 0 &&
314 | loadBit(headsGridOffset + copperStateIndex) === 0 &&
315 | loadBit(newHeadsGridOffset + copperStateIndex) === 0
316 | ) {
317 | let headNeighbors = 0;
318 | const hnCopperLen = loadBit(copperStateIndex * copperLen);
319 | for (let ind2 = 0; ind2 < hnCopperLen; ind2++) {
320 | const stateIndex = loadCopper(copperStateIndex, ind2);
321 | if (loadBit(headsGridOffset + stateIndex) === 1) {
322 | headNeighbors++;
323 | if (headNeighbors === 3) {
324 | headNeighbors = 0;
325 | break;
326 | }
327 | }
328 | }
329 | if (headNeighbors > 0) {
330 | storeBit(newHeadsGridOffset + copperStateIndex, 1);
331 | storeBit(newHeadsArrayOffset + newHeadArrayIndex + 1, copperStateIndex);
332 | newHeadArrayIndex = newHeadArrayIndex + 1;
333 | }
334 | }
335 | }
336 | }
337 | storeBit(newHeadsArrayOffset, newHeadArrayIndex);
338 |
339 | this.mem32.copyWithin(tailsArrayOffset, headsArrayOffset, tailsArrayOffset);
340 | this.mem32.copyWithin(headsArrayOffset, newHeadsArrayOffset, newTailsArrayOffset);
341 | this.mem32.fill(0, newHeadsArrayOffset, newTailsArrayOffset);
342 | }
343 | }
344 |
345 | export class Board {
346 | static boardHeight: number;
347 | static boardWidth: number;
348 | static initialStates: WireState[];
349 | static coppers: number[][];
350 | static copperGrid: boolean[];
351 |
352 | /* constructor(width: number, height: number) {
353 | Board.initialStates = new Array(width * height);
354 | Board.boardWidth = width;
355 | Board.boardHeight = height;
356 | }*/
357 |
358 | constructor(starting: string) {
359 | const rows = starting.replace(new RegExp('\r', 'g'), '').split('\n');
360 | Board.boardWidth = rows[0].length;
361 | Board.boardHeight = rows.length;
362 | BoardState.setupArraySwitch();
363 |
364 | Board.initialStates = new Array(Board.boardWidth * Board.boardHeight);
365 | for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
366 | const row = rows[rowIndex];
367 |
368 | for (let characterIndex = 0; characterIndex < row.length; characterIndex++) {
369 | const character = row[characterIndex];
370 | Board.initialStates[characterIndex + rowIndex * Board.boardWidth] = this.charToState(character);
371 | }
372 | }
373 | }
374 |
375 | charToState(character: string): WireState {
376 | switch (character) {
377 | case '#':
378 | return WireState.Copper;
379 | case '@':
380 | return WireState.Head;
381 | case '~':
382 | return WireState.Tail;
383 | default:
384 | return WireState.Empty;
385 | }
386 | }
387 |
388 | /*
389 | toString(state: BoardState): string;
390 |
391 |
392 | stateToChar(state: WireState): string;
393 | */
394 | }
395 |
396 | export enum WireState {
397 | Empty = 0,
398 |
399 | Head = 1,
400 |
401 | Tail = 2,
402 |
403 | Copper = 3,
404 | }
405 |
406 | export class BoardState {
407 | headsArray: number[];
408 | tailsArray: number[];
409 |
410 | headsGrid: boolean[];
411 | tailsGrid: boolean[];
412 |
413 | private static totalItems: number;
414 |
415 | static setupArraySwitch() {
416 | this.totalItems = Board.boardWidth * Board.boardHeight;
417 | }
418 |
419 | constructor() {
420 | this.headsGrid = new Array(BoardState.totalItems);
421 | this.headsArray = [];
422 | }
423 | }
424 |
--------------------------------------------------------------------------------
/web/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/web/src/webASM.ts:
--------------------------------------------------------------------------------
1 | import wireworldtxt from './wireworld.txt?raw';
2 | import {runFast, runFastTicks, timePerTick} from './common.ts';
3 |
4 | let instance: any;
5 | const copperLen = 8;
6 | const size = 605129;
7 | const arrLen = 7000;
8 | const coppersSize = size * copperLen;
9 | const headsArrayOffset = coppersSize + size;
10 | const headsGridOffset = headsArrayOffset + arrLen;
11 | const tailsArrayOffset = headsGridOffset + size;
12 | const tailsGridOffset = tailsArrayOffset + arrLen;
13 |
14 | const newHeadsArrayOffset = tailsGridOffset + size;
15 | const newHeadsGridOffset = newHeadsArrayOffset + arrLen;
16 | const newTailsArrayOffset = newHeadsGridOffset + size;
17 | const newTailsGridOffset = newTailsArrayOffset + arrLen;
18 |
19 | export class StatePosition {
20 | stateIndex;
21 |
22 | static getY(z: number): number {
23 | return (z / Board.boardWidth) | 0;
24 | }
25 |
26 | static getX(z: number): number {
27 | return z % Board.boardWidth;
28 | }
29 |
30 | constructor(
31 | public x: number,
32 | public y: number,
33 | ) {
34 | this.stateIndex = this.x + this.y * Board.boardWidth;
35 | }
36 | }
37 |
38 | export class WireworldWebASM {
39 | magnify = 1;
40 | private iterations = 0;
41 | mem32: Uint32Array;
42 | timeout = 0;
43 | drawTimeout = 0;
44 | stop() {
45 | clearInterval(this.timeout);
46 | clearInterval(this.drawTimeout);
47 | this.mem32 = null;
48 | }
49 |
50 | start() {
51 | const draw = true;
52 | let boardState: BoardState | null = null;
53 | const down = false;
54 | let canvasBack: HTMLCanvasElement;
55 | let contextBack: CanvasRenderingContext2D;
56 | let canvasFront: HTMLCanvasElement;
57 | let contextFront: CanvasRenderingContext2D;
58 |
59 | if (draw) {
60 | canvasBack = document.getElementById('canvasBack') as HTMLCanvasElement;
61 | contextBack = canvasBack.getContext('2d') as CanvasRenderingContext2D;
62 |
63 | canvasFront = document.getElementById('canvasFront') as HTMLCanvasElement;
64 | contextFront = canvasFront.getContext('2d') as CanvasRenderingContext2D;
65 | }
66 |
67 | const board = new Board(wireworldtxt);
68 | // ele.Html(board.ToString());
69 | boardState = this.generateInitialBoardState();
70 | let ticks = 0;
71 | let totalMs = 0;
72 | instance.init();
73 | this.mem32 = new Uint32Array(instance.memory.buffer);
74 |
75 | this.mem32[headsArrayOffset] = boardState.headsArray.length;
76 | this.mem32[tailsArrayOffset] = boardState.tailsArray.length;
77 |
78 | for (let i = 0; i < boardState.headsArray.length; i++) {
79 | this.mem32[headsArrayOffset + i + 1] = boardState.headsArray[i];
80 | }
81 | for (let i = 0; i < boardState.tailsArray.length; i++) {
82 | this.mem32[tailsArrayOffset + i + 1] = boardState.tailsArray[i];
83 | }
84 |
85 | for (let y = 0; y < Board.boardHeight; y++) {
86 | for (let x = 0; x < Board.boardWidth; x++) {
87 | const pos = y * Board.boardWidth + x;
88 | this.mem32[headsGridOffset + pos] = boardState.headsGrid[pos] ? 1 : 0;
89 | this.mem32[tailsGridOffset + pos] = boardState.tailsGrid[pos] ? 1 : 0;
90 | }
91 | }
92 |
93 | for (let y = 0; y < Board.boardHeight; y++) {
94 | for (let x = 0; x < Board.boardWidth; x++) {
95 | const pos = y * Board.boardWidth + x;
96 | if (!Board.coppers[pos]) {
97 | continue;
98 | }
99 | this.mem32[pos * copperLen] = Board.coppers[pos].length;
100 | for (let i = 0; i < Board.coppers[pos].length; i++) {
101 | this.mem32[pos * copperLen + i + 1] = Board.coppers[pos][i];
102 | }
103 | }
104 | }
105 |
106 | this.timeout = setInterval(() => {
107 | if (down) {
108 | return;
109 | }
110 | const crank = runFast.peek() ? runFastTicks.peek() : 1;
111 |
112 | for (let i = 0; i < crank; i++) {
113 | const perf = performance.now();
114 | this.tickBoard();
115 | this.iterations++;
116 | const res = performance.now() - perf;
117 | totalMs += res;
118 | ticks++;
119 | if (ticks % 100 === 0) {
120 | timePerTick.value = totalMs / ticks;
121 |
122 | totalMs = 0;
123 | ticks = 0;
124 | }
125 | }
126 | }, 1);
127 | if (draw) {
128 | canvasBack.width = canvasFront.width = Board.boardWidth * this.magnify;
129 | canvasBack.height = canvasFront.height = Board.boardHeight * this.magnify;
130 |
131 | this.drawBack(contextBack);
132 | this.drawFront(contextFront, boardState);
133 | this.drawTimeout = setInterval(() => {
134 | const newBoardState = new BoardState();
135 | newBoardState.headsGrid = new Array(size);
136 | newBoardState.tailsGrid = new Array(size);
137 | newBoardState.headsArray = [];
138 | newBoardState.tailsArray = [];
139 | const hLen = this.mem32[headsArrayOffset];
140 | for (let i = 0; i < hLen; i++) {
141 | newBoardState.headsArray.push(this.mem32[headsArrayOffset + i + 1]);
142 | }
143 | const tLen = this.mem32[tailsArrayOffset];
144 | for (let i = 0; i < tLen; i++) {
145 | newBoardState.tailsArray.push(this.mem32[tailsArrayOffset + i + 1]);
146 | }
147 |
148 | for (let y = 0; y < Board.boardHeight; y++) {
149 | for (let x = 0; x < Board.boardWidth; x++) {
150 | const pos = y * Board.boardWidth + x;
151 | newBoardState.headsGrid[pos] = this.mem32[pos + headsGridOffset] === 1;
152 | newBoardState.tailsGrid[pos] = this.mem32[pos + tailsGridOffset] === 1;
153 | }
154 | }
155 |
156 | boardState = newBoardState;
157 | // console.log(boardState.headsArray.length, boardState.tailsArray.length);
158 | this.drawFront(contextFront, boardState);
159 | }, 1000 / 60);
160 | }
161 | }
162 |
163 | drawBack(context: CanvasRenderingContext2D): void {
164 | context.fillStyle = '#000000';
165 | context.fillRect(0, 0, Board.boardWidth * this.magnify, Board.boardHeight * this.magnify);
166 |
167 | for (let y = 0; y < Board.boardHeight; y++) {
168 | for (let x = 0; x < Board.boardWidth; x++) {
169 | if (Board.copperGrid[x + y * Board.boardWidth]) {
170 | context.fillStyle = '#1000A8';
171 | context.fillRect(x * this.magnify, y * this.magnify, this.magnify, this.magnify);
172 | }
173 | }
174 | }
175 | }
176 |
177 | drawFront(context: CanvasRenderingContext2D, boardState: BoardState): void {
178 | context.clearRect(0, 0, Board.boardWidth * this.magnify, Board.boardHeight * this.magnify);
179 |
180 | const heads = boardState.headsArray;
181 | const tails = boardState.tailsArray;
182 | context.save();
183 | context.fillStyle = '#FFF59B';
184 | for (let index = 0, headLength = heads.length; index < headLength; index++) {
185 | context.fillRect(
186 | StatePosition.getX(heads[index]) * this.magnify,
187 | StatePosition.getY(heads[index]) * this.magnify,
188 | this.magnify,
189 | this.magnify,
190 | );
191 | }
192 | context.fillStyle = '#89D2FF';
193 | for (let index = 0, tailLength = tails.length; index < tailLength; index++) {
194 | context.fillRect(
195 | StatePosition.getX(tails[index]) * this.magnify,
196 | StatePosition.getY(tails[index]) * this.magnify,
197 | this.magnify,
198 | this.magnify,
199 | );
200 | }
201 | context.restore();
202 | }
203 |
204 | private static neighbors: StatePosition[];
205 |
206 | generateInitialBoardState(): BoardState {
207 | const boardState = new BoardState();
208 | const boardWidth = Board.boardWidth;
209 | const boardHeight = Board.boardHeight;
210 | boardState.tailsGrid = new Array(boardWidth * boardHeight);
211 | boardState.tailsArray = new Array(boardWidth * boardHeight);
212 |
213 | WireworldWebASM.neighbors = [
214 | new StatePosition(-1, -1),
215 | new StatePosition(-1, 0),
216 | new StatePosition(-1, 1),
217 | new StatePosition(0, -1),
218 | new StatePosition(0, 1),
219 | new StatePosition(1, -1),
220 | new StatePosition(1, 0),
221 | new StatePosition(1, 1),
222 | ];
223 |
224 | Board.copperGrid = new Array(boardWidth * boardHeight);
225 | Board.coppers = new Array(boardWidth * boardHeight);
226 |
227 | for (let y = 0; y < boardHeight; y++) {
228 | for (let x = 0; x < boardWidth; x++) {
229 | const statePos = new StatePosition(x, y);
230 | switch (Board.initialStates[statePos.stateIndex]) {
231 | case WireState.Head:
232 | Board.copperGrid[statePos.stateIndex] = true;
233 | boardState.headsArray[boardState.headsArray.length] = statePos.stateIndex;
234 | boardState.headsGrid[statePos.stateIndex] = true;
235 | break;
236 | case WireState.Tail:
237 | Board.copperGrid[statePos.stateIndex] = true;
238 | boardState.tailsArray[boardState.tailsArray.length] = statePos.stateIndex;
239 | boardState.tailsGrid[statePos.stateIndex] = true;
240 | break;
241 | case WireState.Copper:
242 | Board.copperGrid[statePos.stateIndex] = true;
243 | break;
244 | }
245 | }
246 | }
247 | this.buildCoppers();
248 | return boardState;
249 | }
250 |
251 | buildCoppers(): void {
252 | const boardHeight = Board.boardHeight;
253 | const boardWidth = Board.boardWidth;
254 | for (let y = 0; y < boardHeight; y++) {
255 | for (let x = 0; x < boardWidth; x++) {
256 | const stateIndex = x + y * boardWidth;
257 | if (Board.copperGrid[stateIndex]) {
258 | Board.coppers[stateIndex] = this.getNeighborStates(x, y);
259 | }
260 | }
261 | }
262 | }
263 |
264 | getNeighborStates(x: number, y: number): number[] {
265 | const boardWidth = Board.boardWidth;
266 |
267 | const statePositions: number[] = [];
268 |
269 | for (let index = 0; index < WireworldWebASM.neighbors.length; index++) {
270 | const statePosition = WireworldWebASM.neighbors[index];
271 | const stateIndex = x + statePosition.x + (y + statePosition.y) * boardWidth;
272 | if (Board.copperGrid[stateIndex]) {
273 | statePositions.push(new StatePosition(x + statePosition.x, y + statePosition.y).stateIndex);
274 | }
275 | }
276 | return statePositions;
277 | }
278 |
279 | tickBoard() {
280 | instance.tick();
281 |
282 | this.mem32.copyWithin(tailsArrayOffset, headsArrayOffset, tailsArrayOffset);
283 | this.mem32.copyWithin(headsArrayOffset, newHeadsArrayOffset, newTailsArrayOffset);
284 | this.mem32.fill(0, newHeadsArrayOffset, newTailsArrayOffset);
285 | }
286 | }
287 |
288 | export class Board {
289 | static boardHeight: number;
290 | static boardWidth: number;
291 | static initialStates: WireState[];
292 | static coppers: number[][];
293 | static copperGrid: boolean[];
294 |
295 | /* constructor(width: number, height: number) {
296 | Board.initialStates = new Array(width * height);
297 | Board.boardWidth = width;
298 | Board.boardHeight = height;
299 | }*/
300 |
301 | constructor(starting: string) {
302 | const rows = starting.replace(new RegExp('\r', 'g'), '').split('\n');
303 | Board.boardWidth = rows[0].length;
304 | Board.boardHeight = rows.length;
305 | BoardState.setupArraySwitch();
306 |
307 | Board.initialStates = new Array(Board.boardWidth * Board.boardHeight);
308 | for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
309 | const row = rows[rowIndex];
310 |
311 | for (let characterIndex = 0; characterIndex < row.length; characterIndex++) {
312 | const character = row[characterIndex];
313 | Board.initialStates[characterIndex + rowIndex * Board.boardWidth] = this.charToState(character);
314 | }
315 | }
316 | }
317 |
318 | charToState(character: string): WireState {
319 | switch (character) {
320 | case '#':
321 | return WireState.Copper;
322 | case '@':
323 | return WireState.Head;
324 | case '~':
325 | return WireState.Tail;
326 | default:
327 | return WireState.Empty;
328 | }
329 | }
330 |
331 | /*
332 | toString(state: BoardState): string;
333 |
334 |
335 | stateToChar(state: WireState): string;
336 | */
337 | }
338 |
339 | export enum WireState {
340 | Empty = 0,
341 |
342 | Head = 1,
343 |
344 | Tail = 2,
345 |
346 | Copper = 3,
347 | }
348 |
349 | export class BoardState {
350 | headsArray: number[] = [];
351 | tailsArray: number[] = [];
352 |
353 | headsGrid: boolean[] = [];
354 | tailsGrid: boolean[] = [];
355 |
356 | private static totalItems: number;
357 |
358 | static setupArraySwitch() {
359 | this.totalItems = Board.boardWidth * Board.boardHeight;
360 | }
361 |
362 | constructor() {
363 | this.headsGrid = new Array(BoardState.totalItems);
364 | this.headsArray = [];
365 | }
366 | }
367 |
368 | import('./asm/build/optimized.wasm?url')
369 | .then(async (result) => {
370 | const responsePromise = fetch(result.default);
371 | const module = await WebAssembly.instantiateStreaming(responsePromise, {
372 | env: {
373 | memory: new WebAssembly.Memory({initial: 121}),
374 | abort(_msg, _file, line, column) {
375 | console.error('abort called at index.ts:' + line + ':' + column);
376 | },
377 | },
378 | console: {
379 | logger(arg) {
380 | console.log(arg);
381 | },
382 | },
383 | config: {},
384 | });
385 | return module;
386 | })
387 | .then((module) => {
388 | instance = module.instance.exports;
389 | });
390 |
--------------------------------------------------------------------------------
/web/src/webGPU.ts:
--------------------------------------------------------------------------------
1 | import wireworldtxt from './wireworld.txt?raw';
2 | import {runFast, timePerTick, runFastTicks, runFastTicksGPU} from './common.ts';
3 |
4 | const rows = wireworldtxt.replace(new RegExp('\r', 'g'), '').split('\n');
5 |
6 | const boardWidth = rows[0].length;
7 | const boardHeight = rows.length;
8 | const workgroupSize = 256;
9 |
10 | export class WireworldWebGPU {
11 | running = true;
12 | async start() {
13 | this.running = true;
14 | let inputData = new Uint32Array(boardWidth * boardHeight);
15 | for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
16 | const row = rows[rowIndex];
17 |
18 | for (let characterIndex = 0; characterIndex < row.length; characterIndex++) {
19 | const character = row[characterIndex];
20 | inputData[characterIndex + rowIndex * boardWidth] = charToState(character);
21 | }
22 | }
23 | draw(inputData);
24 |
25 | // Create GPU context
26 | const adapter = (await navigator.gpu.requestAdapter())!;
27 | const device = await adapter!.requestDevice();
28 |
29 | let inputBufferA = device.createBuffer({
30 | size: inputData.byteLength,
31 | usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC,
32 | mappedAtCreation: true,
33 | });
34 | new Uint32Array(inputBufferA.getMappedRange()).set(inputData);
35 | inputBufferA.unmap();
36 |
37 | let inputBufferB = device.createBuffer({
38 | size: inputData.byteLength,
39 | usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC,
40 | mappedAtCreation: true,
41 | });
42 | new Uint32Array(inputBufferB.getMappedRange()).set(inputData);
43 | inputBufferB.unmap();
44 |
45 | const readbackBuffer = device.createBuffer({
46 | size: inputData.byteLength,
47 | usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,
48 | });
49 | // Define Shader
50 | const computeShaderCode = `
51 | struct Data {
52 | data: array
53 | };
54 |
55 | @group(0) @binding(0) var src: Data;
56 | @group(0) @binding(1) var dst: Data;
57 |
58 | fn get_cell_state(x: u32, y: u32) -> u32 {
59 | return src.data[x + y * ${boardWidth}];
60 | }
61 |
62 | fn get_cell_state2(x: u32, y: u32) -> u32 {
63 | return select(0u, 1u, src.data[x + y * ${boardWidth}] == 1);
64 | }
65 |
66 | @compute @workgroup_size(${workgroupSize})
67 | fn main(@builtin(global_invocation_id) global_id: vec3) {
68 | let current_state = src.data[global_id.x];
69 | switch(current_state) {
70 | case 0: {
71 | dst.data[global_id.x] = 0;
72 | break;
73 | }
74 | case 1: {
75 | dst.data[global_id.x] = 2;
76 | break;
77 | }
78 | case 2: {
79 | dst.data[global_id.x] = 3;
80 | break;
81 | }
82 | default: {
83 | let x: u32 = global_id.x % ${boardWidth};
84 | let y: u32 = global_id.x / ${boardWidth};
85 | var electron_head_count=get_cell_state2(x - 1, y - 1) +
86 | get_cell_state2(x , y - 1) +
87 | get_cell_state2(x + 1, y - 1) +
88 | get_cell_state2(x - 1, y ) +
89 | get_cell_state2(x + 1, y ) +
90 | get_cell_state2(x - 1, y + 1) +
91 | get_cell_state2(x , y + 1) +
92 | get_cell_state2(x + 1, y + 1);
93 |
94 | if(electron_head_count == 1 || electron_head_count == 2){
95 | dst.data[global_id.x]=1;
96 | }else{
97 | dst.data[global_id.x]=3;
98 | }
99 | break;
100 | }
101 | }
102 | }`;
103 |
104 | // Create pipeline
105 | const pipeline = device.createComputePipeline({
106 | compute: {
107 | module: device.createShaderModule({
108 | code: computeShaderCode,
109 | }),
110 | entryPoint: 'main',
111 | },
112 | layout: 'auto',
113 | });
114 |
115 | const bindGroupA = device.createBindGroup({
116 | layout: pipeline.getBindGroupLayout(0),
117 | entries: [
118 | {
119 | binding: 0,
120 | resource: {buffer: inputBufferA},
121 | },
122 | {
123 | binding: 1,
124 | resource: {buffer: inputBufferB},
125 | },
126 | ],
127 | });
128 | const bindGroupB = device.createBindGroup({
129 | layout: pipeline.getBindGroupLayout(0),
130 | entries: [
131 | {
132 | binding: 0,
133 | resource: {buffer: inputBufferB},
134 | },
135 | {
136 | binding: 1,
137 | resource: {buffer: inputBufferA},
138 | },
139 | ],
140 | });
141 |
142 | let i = 0;
143 | let isAInput = true;
144 |
145 | const run = () => {
146 | if (!this.running) return;
147 | let timeRun = performance.now();
148 | let runs = runFast.peek() ? runFastTicksGPU.peek() : 5;
149 | for (let i = 0; i < runs; i++) {
150 | let commandEncoder = device.createCommandEncoder();
151 | let passEncoder = commandEncoder.beginComputePass();
152 | passEncoder.setPipeline(pipeline);
153 | passEncoder.setBindGroup(0, isAInput ? bindGroupA : bindGroupB);
154 | passEncoder.dispatchWorkgroups(Math.ceil(inputData.length / workgroupSize));
155 | passEncoder.end();
156 |
157 | device.queue.submit([commandEncoder.finish()]);
158 |
159 | isAInput = !isAInput;
160 | }
161 | timePerTick.value = (performance.now() - timeRun) / runs;
162 |
163 | let commandEncoder = device.createCommandEncoder();
164 | commandEncoder.copyBufferToBuffer(
165 | isAInput ? inputBufferA : inputBufferB,
166 | 0,
167 | readbackBuffer,
168 | 0,
169 | inputData.byteLength,
170 | );
171 | device.queue.submit([commandEncoder.finish()]);
172 | readbackBuffer.mapAsync(GPUMapMode.READ).then(() => {
173 | const readData = new Uint32Array(readbackBuffer.getMappedRange());
174 | draw(readData);
175 | readbackBuffer.unmap();
176 | setTimeout(() => {
177 | run();
178 | }, 0);
179 | });
180 | };
181 | run();
182 | }
183 | stop() {
184 | this.running = false;
185 | }
186 | }
187 |
188 | export enum WireState {
189 | Empty = 0,
190 | Head = 1,
191 | Tail = 2,
192 | Copper = 3,
193 | }
194 |
195 | function charToState(character: string): WireState {
196 | switch (character) {
197 | case '#':
198 | return WireState.Copper;
199 | case '@':
200 | return WireState.Head;
201 | case '~':
202 | return WireState.Tail;
203 | default:
204 | return WireState.Empty;
205 | }
206 | }
207 | let magnify = 1;
208 |
209 | function draw(data: Uint32Array) {
210 | let canvasBack: HTMLCanvasElement;
211 | let contextBack: CanvasRenderingContext2D;
212 | let canvasFront: HTMLCanvasElement;
213 | let contextFront: CanvasRenderingContext2D;
214 | canvasBack = document.getElementById('canvasBack') as HTMLCanvasElement;
215 | contextBack = canvasBack.getContext('2d') as CanvasRenderingContext2D;
216 |
217 | canvasFront = document.getElementById('canvasFront') as HTMLCanvasElement;
218 | contextFront = canvasFront.getContext('2d') as CanvasRenderingContext2D;
219 | contextFront.clearRect(0, 0, boardWidth * magnify, boardHeight * magnify);
220 | canvasBack.width = /*canvasFront.width =*/ boardWidth * magnify;
221 | canvasBack.height = /* canvasFront.height = */ boardHeight * magnify;
222 |
223 | drawBack(contextBack, data);
224 | }
225 |
226 | function drawBack(context: CanvasRenderingContext2D, data: Uint32Array): void {
227 | context.fillStyle = '#000000';
228 | context.fillRect(0, 0, boardWidth * magnify, boardHeight * magnify);
229 |
230 | for (let y = 0; y < boardHeight; y++) {
231 | for (let x = 0; x < boardWidth; x++) {
232 | if (data[x + y * boardWidth] === 3) {
233 | context.fillStyle = '#1000A8';
234 | context.fillRect(x * magnify, y * magnify, magnify, magnify);
235 | } else if (data[x + y * boardWidth] === 2) {
236 | context.fillStyle = '#89D2FF';
237 | context.fillRect(x * magnify, y * magnify, magnify, magnify);
238 | } else if (data[x + y * boardWidth] === 1) {
239 | context.fillStyle = '#FFF59B';
240 | context.fillRect(x * magnify, y * magnify, magnify, magnify);
241 | }
242 | }
243 | }
244 | }
245 |
--------------------------------------------------------------------------------
/web/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "useDefineForClassFields": true,
5 | "module": "ESNext",
6 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
7 | "skipLibCheck": true,
8 |
9 | "types": ["@webgpu/types"],
10 | /* Bundler mode */
11 | "moduleResolution": "bundler",
12 | "allowImportingTsExtensions": true,
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "noEmit": true,
16 |
17 | /* Linting */
18 | "strict": true,
19 | "noFallthroughCasesInSwitch": true
20 | },
21 | "include": ["src"]
22 | }
23 |
--------------------------------------------------------------------------------