├── .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 | --------------------------------------------------------------------------------