├── .github └── FUNDING.yml ├── CustomElements.js ├── LICENSE ├── Libraries ├── eatlegacy.js ├── habitat.js └── spacetode.js ├── Media ├── Fonts │ ├── Inconsolata.otf │ ├── Rosario-Bold.ttf │ ├── Rosario-BoldItalic.ttf │ ├── Rosario-Italic.ttf │ ├── Rosario-Regular.ttf │ ├── UbuntuMono-B.ttf │ ├── UbuntuMono-BI.ttf │ ├── UbuntuMono-R.ttf │ └── UbuntuMono-RI.ttf └── Images │ ├── RedTode.png │ └── favicon.png ├── README.md ├── Source ├── Elements │ ├── Clear.js │ ├── Explosive.js │ ├── Food.js │ ├── Globals.js │ ├── Life.js │ ├── Old │ │ ├── TodeSplat2 │ │ │ ├── Bondo.js │ │ │ ├── Clear.js │ │ │ ├── EdgeClear.js │ │ │ ├── FatFly.js │ │ │ ├── Pheromone.js │ │ │ ├── Platformer.js │ │ │ ├── PlayerSnake.js │ │ │ ├── Rope.js │ │ │ ├── SleepyFly.js │ │ │ ├── Snake.js │ │ │ └── SpaceShip.js │ │ └── TodeSplat3 │ │ │ ├── Explosive.js │ │ │ ├── Globals.js │ │ │ ├── Life.js │ │ │ ├── People.js │ │ │ ├── Presets.js │ │ │ ├── Sandbox.js │ │ │ ├── T2Tile.js │ │ │ ├── Tutorial.js │ │ │ └── Weather.js │ ├── Sandbox.js │ ├── Smell.js │ ├── State.js │ ├── T2Tile.js │ ├── Temperature.js │ ├── Testing.js │ ├── TwoTwoTwoTwo.js │ └── Weird.js └── Engine │ ├── Dropper.js │ ├── Floor.js │ ├── Lighting.js │ ├── Random.js │ ├── ShuffleWorker.js │ ├── Site.js │ ├── Space.js │ └── World.js ├── config.js ├── index.html ├── main.js └── ui.js /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: TodePond 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /CustomElements.js: -------------------------------------------------------------------------------- 1 | SpaceTode` 2 | 3 | 4 | ` 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 l2wilson94 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Libraries/eatlegacy.js: -------------------------------------------------------------------------------- 1 | 2 | // This is an older version of the Eat library. 3 | // I kept it because SpaceTode still uses it. 4 | 5 | // 6 | // Every result has three properties... 7 | // 8 | // 'success' 9 | // true if the desired thing was found 10 | // 11 | // 'snippet' 12 | // the desired thing 13 | // 14 | // 'code' 15 | // the rest of the code (with the desired thing removed) 16 | // 17 | 18 | EAT = {} 19 | HABITAT.EAT_LEGACY = true 20 | 21 | { 22 | 23 | //====================// 24 | // Control Structures // 25 | //====================// 26 | EAT.many = (func) => (source, ...args) => { 27 | 28 | // Buffers 29 | let success = undefined 30 | let code = source 31 | 32 | // Head 33 | let headResult = undefined 34 | headResult = {success, code} = func(code, ...args) 35 | if (!success) return {...headResult, code: source} 36 | 37 | // Tail 38 | let tailResult = undefined 39 | tailResult = {success, code} = EAT.many(func)(code, ...args) 40 | if (!success) return headResult 41 | tailResult.snippet = headResult.snippet + tailResult.snippet 42 | return tailResult 43 | 44 | } 45 | 46 | EAT.maybe = (func) => (source, ...args) => { 47 | 48 | let result = undefined 49 | let success = undefined 50 | let code = source 51 | 52 | result = {success, code} = func(code, ...args) 53 | if (!success) { 54 | result.success = true 55 | result.snippet = "" 56 | } 57 | 58 | return result 59 | } 60 | 61 | EAT.list = (...funcs) => (source, ...args) => { 62 | 63 | // Buffers 64 | let success = undefined 65 | let code = source 66 | 67 | // Head 68 | let headResult = undefined 69 | const headFunc = funcs[0] 70 | headResult = {success, code} = headFunc(code, ...args) 71 | if (!success) return {...headResult, code: source} 72 | 73 | // Tail 74 | let tailResult = undefined 75 | const tailFuncs = funcs.slice(1) 76 | if (tailFuncs.length == 0) return headResult 77 | tailResult = {success, code} = EAT.list(...tailFuncs)(code, ...args) 78 | tailResult.snippet = headResult.snippet + tailResult.snippet 79 | return tailResult 80 | 81 | } 82 | 83 | EAT.or = (...funcs) => (source, ...args) => { 84 | 85 | for (const func of funcs) { 86 | 87 | let result = undefined 88 | let success = undefined 89 | let code = source 90 | 91 | result = {success, code} = func(code, ...args) 92 | if (success) return result 93 | } 94 | 95 | const success = false 96 | const code = source 97 | const snippet = undefined 98 | return {success, snippet, code} 99 | } 100 | 101 | EAT.fail = (source) => ({success: false, snippet: undefined, code: source}) 102 | 103 | //====================// 104 | // In-Built Functions // 105 | //====================// 106 | EAT.string = (string) => (source) => { 107 | const success = source.slice(0, string.length) == string 108 | const snippet = success? string : undefined 109 | const code = success? source.slice(string.length) : source 110 | return {success, snippet, code} 111 | } 112 | 113 | EAT.regexp = EAT.regExp = EAT.regex = EAT.regEx = (regex) => (source) => { 114 | const fullRegex = new RegExp("^" + regex.source + "$") 115 | 116 | let i = 0 117 | while (i <= source.length) { 118 | const snippet = source.slice(0, i) 119 | const success = fullRegex.test(snippet) 120 | if (success) { 121 | const code = source.slice(snippet.length) 122 | return {success, snippet, code} 123 | } 124 | i++ 125 | } 126 | 127 | const success = false 128 | const snippet = undefined 129 | const code = source 130 | return {success, snippet, code} 131 | 132 | } 133 | 134 | EAT.space = EAT.string(" ") 135 | EAT.tab = EAT.string(" ") 136 | EAT.newline = EAT.newLine = EAT.string("\n") 137 | 138 | EAT.gap = EAT.many ( 139 | EAT.or ( 140 | EAT.space, 141 | EAT.tab, 142 | ) 143 | ) 144 | 145 | EAT.whitespace = EAT.whiteSpace = EAT.many ( 146 | EAT.or ( 147 | EAT.space, 148 | EAT.tab, 149 | EAT.newline, 150 | ) 151 | ) 152 | 153 | EAT.emptyLine = EAT.list ( 154 | EAT.maybe(EAT.gap), 155 | EAT.newline, 156 | ) 157 | 158 | EAT.emptyLines = EAT.many(EAT.emptyLine) 159 | 160 | EAT.name = EAT.list ( 161 | EAT.regexp(/[a-zA-Z_$]/), 162 | EAT.many(EAT.regex(/[a-zA-Z0-9_$]/)) 163 | ) 164 | 165 | EAT.margin = EAT.or ( 166 | EAT.many(EAT.tab), 167 | EAT.many(EAT.space), 168 | ) 169 | 170 | EAT.line = EAT.many(EAT.regex(/[^\n]/)) 171 | } -------------------------------------------------------------------------------- /Media/Fonts/Inconsolata.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TodePond/SandPond/6015bd3e4692ce7398408bdf7388f15cb8b50c09/Media/Fonts/Inconsolata.otf -------------------------------------------------------------------------------- /Media/Fonts/Rosario-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TodePond/SandPond/6015bd3e4692ce7398408bdf7388f15cb8b50c09/Media/Fonts/Rosario-Bold.ttf -------------------------------------------------------------------------------- /Media/Fonts/Rosario-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TodePond/SandPond/6015bd3e4692ce7398408bdf7388f15cb8b50c09/Media/Fonts/Rosario-BoldItalic.ttf -------------------------------------------------------------------------------- /Media/Fonts/Rosario-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TodePond/SandPond/6015bd3e4692ce7398408bdf7388f15cb8b50c09/Media/Fonts/Rosario-Italic.ttf -------------------------------------------------------------------------------- /Media/Fonts/Rosario-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TodePond/SandPond/6015bd3e4692ce7398408bdf7388f15cb8b50c09/Media/Fonts/Rosario-Regular.ttf -------------------------------------------------------------------------------- /Media/Fonts/UbuntuMono-B.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TodePond/SandPond/6015bd3e4692ce7398408bdf7388f15cb8b50c09/Media/Fonts/UbuntuMono-B.ttf -------------------------------------------------------------------------------- /Media/Fonts/UbuntuMono-BI.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TodePond/SandPond/6015bd3e4692ce7398408bdf7388f15cb8b50c09/Media/Fonts/UbuntuMono-BI.ttf -------------------------------------------------------------------------------- /Media/Fonts/UbuntuMono-R.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TodePond/SandPond/6015bd3e4692ce7398408bdf7388f15cb8b50c09/Media/Fonts/UbuntuMono-R.ttf -------------------------------------------------------------------------------- /Media/Fonts/UbuntuMono-RI.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TodePond/SandPond/6015bd3e4692ce7398408bdf7388f15cb8b50c09/Media/Fonts/UbuntuMono-RI.ttf -------------------------------------------------------------------------------- /Media/Images/RedTode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TodePond/SandPond/6015bd3e4692ce7398408bdf7388f15cb8b50c09/Media/Images/RedTode.png -------------------------------------------------------------------------------- /Media/Images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TodePond/SandPond/6015bd3e4692ce7398408bdf7388f15cb8b50c09/Media/Images/favicon.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # SandPond 4 | SandPond is an engine for cells that follow spatial rules.
5 | It's heavily inspired by Dave Ackley's [T2 Tile Project](https://t2tile.com/). 6 | 7 | **For more info, check out the [SandPond Saga](https://youtube.com/c/TodePond) video series.** 8 | 9 | ## Rules 10 | Atoms follow simple rules, like this one: 11 | ``` 12 | @ => _ 13 | _ @ 14 | ``` 15 | The `@` represents the atom.
16 | The `_` represents an empty space.
17 | So... the rule makes the atom fall down if there's an empty space below it.
18 | 19 | ## Try it out 20 | Try it out at [www.sandpond.cool](http://www.sandpond.cool).
21 | Or [download](https://github.com/l2wilson94/SandPond/archive/main.zip) it and open `index.html` in a browser.
22 | 23 | ## Make your own element 24 | Elements are written in the [SpaceTode](https://github.com/l2wilson94/SpaceTode) language.
25 | To learn SpaceTode, check out the [documentation](https://l2wilson94.gitbook.io/spacetode).
26 | Or look at the examples in the [Elements](https://github.com/l2wilson94/SandPond/tree/main/Source/Elements) folder. 27 | -------------------------------------------------------------------------------- /Source/Elements/Clear.js: -------------------------------------------------------------------------------- 1 | SpaceTode` 2 | 3 | element Eater { 4 | colour "brown" 5 | category "Clear" 6 | arg energy 10 7 | 8 | change D (self) => new Eater.Done(self.energy) 9 | change B (self) => new Eater.Bomb(self.energy) 10 | change E (self) => new Eater(self.energy) 11 | change Y (self) => new Eater(self.energy + 1) 12 | change N (self) => new Eater() 13 | 14 | given O (element) => element !== Empty && element !== Void && element !== Eater && element !== Eater.Done && element !== Eater.Bomb 15 | 16 | given e (self) => self.energy-- < 0 17 | e => D 18 | 19 | for(xyz.directions) { 20 | @O => YY 21 | } 22 | 23 | any(xyz.directions) { 24 | @_ => EE 25 | } 26 | 27 | element Done { 28 | colour "grey" 29 | emissive "black" 30 | arg energy 0 31 | @ => B 32 | } 33 | 34 | element Bomb { 35 | colour "lightgreen" 36 | emissive "green" 37 | arg energy 0 38 | all(xyz.others) { 39 | action @O => .N 40 | } 41 | 42 | @ => _ 43 | } 44 | } 45 | 46 | element Clear { 47 | colour "brown" 48 | category "Clear" 49 | arg timer 0 50 | 51 | given C (element) => element === Clear 52 | change C (self) => new Clear(self.timer) 53 | change c (self) => { 54 | //self.timer++ 55 | return new Clear(self.timer + 0.1) 56 | } 57 | 58 | given B (element) => element === Clear.Bomb 59 | change B (self) => new Clear.Bomb(self.timer) 60 | change b (atom) => new Clear.Bomb(atom.timer) 61 | 62 | keep T (self, atom) => self.timer = atom.timer + 20 63 | 64 | given D (element) => element === Clear.Done 65 | change D (self) => new Clear.Done(self.timer) 66 | 67 | // Other element 68 | given O (element) => element !== Void && element !== Clear && element !== Clear.Done && element !== Clear.Bomb 69 | 70 | all(xyz.directions) { 71 | //@B => .T 72 | @O => .c 73 | } 74 | any(xyz.directions) { 75 | @x => D. 76 | @D => D. 77 | } 78 | 79 | element Done { 80 | colour "grey" 81 | emissive "black" 82 | arg timer 0 83 | all(xyz.directions) @C => .. 84 | @ => B 85 | } 86 | 87 | element Bomb { 88 | colour "lightgreen" 89 | emissive "green" 90 | arg timer 0 91 | all(xyz.directions) action @C => @B 92 | all(xyz.others) { 93 | action @O => ._ 94 | } 95 | 96 | given t (self) => self.timer-- < 0 97 | t => _ 98 | } 99 | 100 | } 101 | 102 | element Clear2D { 103 | colour "brown" 104 | category "Clear" 105 | arg timer 0 106 | 107 | given C (element) => element === Clear2D 108 | change C (self) => new Clear2D(self.timer) 109 | change c (self) => { 110 | //self.timer++ 111 | return new Clear2D(self.timer + 0.2) 112 | } 113 | 114 | given B (element) => element === Clear2D.Bomb 115 | change B (self) => new Clear2D.Bomb(self.timer) 116 | change b (atom) => new Clear2D.Bomb(atom.timer) 117 | 118 | keep T (self, atom) => self.timer = atom.timer + 20 119 | 120 | given D (element) => element === Clear2D.Done 121 | change D (self) => new Clear2D.Done(self.timer) 122 | 123 | // Other element 124 | given O (element) => element !== Void && element !== Clear2D && element !== Clear2D.Done && element !== Clear2D.Bomb 125 | 126 | all(xy.rotations) { 127 | //@B => .T 128 | @O => .c 129 | } 130 | any(xy.rotations) { 131 | @x => D. 132 | @D => D. 133 | } 134 | 135 | element Done { 136 | colour "grey" 137 | emissive "black" 138 | arg timer 0 139 | all(xyz.directions) @C => .. 140 | @ => B 141 | } 142 | 143 | element Bomb { 144 | colour "lightgreen" 145 | emissive "green" 146 | arg timer 0 147 | all(xy.rotations) action @C => @B 148 | all(xy.others) { 149 | action @O => ._ 150 | } 151 | 152 | given t (self) => self.timer-- < 0 153 | t => _ 154 | } 155 | 156 | } 157 | 158 | element Wipe { 159 | colour "brown" 160 | category "Clear" 161 | arg timer 4 162 | 163 | given W (element) => element === Wipe 164 | change W (self) => new Wipe(self.timer) 165 | 166 | given E (element) => element === Wipe.Edge 167 | change E (self) => new Wipe.Edge(self.timer) 168 | change e (self) => new Wipe.Edge(self.timer + 0.1) 169 | 170 | given D (element) => element === Wipe.Done 171 | change D (self) => new Wipe.Done(self.timer) 172 | 173 | given B (element) => element === Wipe.Bomb 174 | change B (self) => new Wipe.Bomb(self.timer) 175 | 176 | // Other element 177 | given O (element) => element !== Void && element !== Wipe && element !== Wipe.Done && element !== Wipe.Edge && element !== Wipe.Bomb 178 | 179 | @O => @W 180 | @x => E. 181 | 182 | element Edge { 183 | arg timer 0 184 | colour "brown" 185 | 186 | all(yz.rotations) { 187 | O => e 188 | @ . 189 | } 190 | 191 | all(yz.rotations) { 192 | x => . 193 | @ D 194 | } 195 | 196 | all(yz.rotations) { 197 | @ => D 198 | D . 199 | } 200 | } 201 | 202 | element Done { 203 | colour "grey" 204 | emissive "black" 205 | arg timer 0 206 | 207 | all(yz.rotations) { 208 | @ => . 209 | E . 210 | 211 | @ => . 212 | E . 213 | 214 | @ => . 215 | D . 216 | } 217 | 218 | .@ => @B 219 | 220 | x@ => .B 221 | 222 | } 223 | 224 | element Bomb { 225 | colour "lightgreen" 226 | emissive "green" 227 | arg timer 0 228 | 229 | all(xyz.directions) action @E => @B 230 | all(xyz.others) { 231 | action @O => ._ 232 | } 233 | 234 | given t (self) => self.timer-- < 0 235 | t => _ 236 | } 237 | } 238 | 239 | element Wipe2D { 240 | colour "brown" 241 | category "Clear" 242 | //default true 243 | 244 | symbol W Wipe2D 245 | symbol E Wipe2D.Edge 246 | symbol D Wipe2D.Done 247 | symbol B Wipe2D.Bomb 248 | 249 | // Other element 250 | given O (element) => element !== Void && element !== Wipe2D && element !== Wipe2D.Done && element !== Wipe2D.Edge && element !== Wipe2D.Bomb 251 | 252 | @O => @W 253 | @x => E. 254 | 255 | element Edge { 256 | colour "brown" 257 | 258 | all(y) { 259 | O => E 260 | @ . 261 | } 262 | 263 | all(y) { 264 | x => . 265 | @ D 266 | } 267 | 268 | all(y) { 269 | @ => D 270 | D . 271 | } 272 | } 273 | 274 | element Done { 275 | colour "grey" 276 | emissive "black" 277 | 278 | all(y) { 279 | @ => . 280 | E . 281 | 282 | @ => . 283 | E . 284 | 285 | @ => . 286 | D . 287 | } 288 | 289 | .@ => @B 290 | 291 | x@ => .B 292 | 293 | } 294 | 295 | element Bomb { 296 | colour "lightgreen" 297 | emissive "green" 298 | arg timer 4 299 | 300 | all(xy.others) { 301 | action @O => ._ 302 | } 303 | 304 | given t (self) => self.timer-- < 0 305 | t => _ 306 | } 307 | } 308 | 309 | ` -------------------------------------------------------------------------------- /Source/Elements/Explosive.js: -------------------------------------------------------------------------------- 1 | 2 | SpaceTode` 3 | 4 | 5 | 6 | element Explosion any(xyz.directions) { 7 | colour "darkorange" 8 | emissive "red" 9 | opacity 0.3 10 | category "Explosive" 11 | prop temperature HOT 12 | prop state EFFECT 13 | arg timer 20 14 | arg explosionColour "orange" 15 | 16 | given i (self) => self.init !== true 17 | keep c (self, space) => { 18 | if (self.explosionColour === "blue") { 19 | self.colour = {r:0, g:0, b:255} 20 | self.emissive = {r:0, g:0, b:128} 21 | SPACE.updateAppearance(space) 22 | } 23 | else if (self.explosionColour === "red") { 24 | self.colour = {r:255, g:0, b:0} 25 | self.emissive = {r:128, g:0, b:0} 26 | SPACE.updateAppearance(space) 27 | } 28 | else if (self.explosionColour === "green") { 29 | self.colour = {r:0, g:255, b:0} 30 | self.emissive = {r:0, g:128, b:0} 31 | SPACE.updateAppearance(space) 32 | } 33 | else if (self.explosionColour === "yellow") { 34 | self.colour = {r:255, g:200, b:0} 35 | self.emissive = {r:128, g:100, b:0} 36 | SPACE.updateAppearance(space) 37 | } 38 | self.init = true 39 | } 40 | action i => c 41 | 42 | keep t (self) => self.timer-- 43 | action @ => t 44 | 45 | given t (self) => self.timer <= 0 46 | t => _ 47 | 48 | change E (self, selfElement) => { 49 | const e = new selfElement(self.timer, self.explosionColour) 50 | e.colour = self.colour 51 | e.emissive = self.emissive 52 | return e 53 | } 54 | @. => .E 55 | 56 | } 57 | 58 | element GunPowder { 59 | category "Explosive" 60 | colour "grey" 61 | emissive "brown" 62 | prop state SOLID 63 | prop temperature ROOM 64 | prop states () => ({ 65 | [HOT]: Explosion, 66 | }) 67 | 68 | mimic(Temperature) 69 | mimic(Powder) 70 | 71 | } 72 | 73 | element Firework { 74 | colour "grey" 75 | emissive "black" 76 | category "Explosive" 77 | prop state SOLID 78 | prop temperature WARM 79 | arg fuel 25 80 | 81 | change E () => { 82 | const rando = Math.floor(Math.random() * 5) 83 | if (rando < 1) return new Explosion(20, "orange") 84 | if (rando < 2) return new Explosion(20, "blue") 85 | if (rando < 3) return new Explosion(20, "red") 86 | if (rando < 4) return new Explosion(20, "yellow") 87 | if (rando < 5) return new Explosion(20, "green") 88 | } 89 | 90 | given t (self) => self.fuel <= 0 91 | t => E 92 | 93 | change F (self) => { 94 | self.fuel-- 95 | return new Fire() 96 | } 97 | 98 | _ => @ 99 | @ F 100 | 101 | @ => E 102 | } 103 | 104 | // TODO: implement direction and make it an arg 105 | // needs quite a lot of work - need firstclass directions in the SpaceTode language, similar to symmetries 106 | element Meteor { 107 | colour "#781a00" 108 | emissive "black" 109 | category "Explosive" 110 | prop state SOLID 111 | prop temperature WARM 112 | data stuck false 113 | prop states () => ({ 114 | [COLD]: Rock, 115 | [CHILLY]: Rock, 116 | [COOL]: [Rock, 0.4], 117 | }) 118 | 119 | mimic(Temperature) 120 | 121 | change F () => new Fire() 122 | action { 123 | _ => F 124 | @ . 125 | } 126 | 127 | given F (element) => element === Fire 128 | @ => _ 129 | F @ 130 | 131 | given E (element) => element === Explosion 132 | @ => _ 133 | E . 134 | 135 | given D (element) => element.state > SOLID 136 | select D (atom) => atom 137 | change D (selected) => selected 138 | @ => D 139 | D @ 140 | 141 | given M (element, selfElement, atom) => element === selfElement || atom.stuck === false 142 | @ => . 143 | M . 144 | 145 | change E () => new Explosion(35) 146 | @ => E 147 | 148 | mimic(Sticky) 149 | } 150 | 151 | ` 152 | -------------------------------------------------------------------------------- /Source/Elements/Food.js: -------------------------------------------------------------------------------- 1 | // Food Flags 2 | const PLANT = Flag(1) 3 | const MEAT = Flag(2) 4 | const WATER = Flag(3) 5 | const BUG = Flag(4) 6 | const DAIRY = Flag(5) 7 | 8 | SpaceTode` 9 | 10 | element Carrot { 11 | colour "rgb(200, 80, 0)" 12 | category "Life" 13 | prop state SOLID 14 | prop temperature ROOM 15 | prop food PLANT 16 | 17 | change i () => new Carrot.Leaf(Math.random(), false) 18 | @ => i 19 | 20 | given L (self, element, atom) => element === Carrot.Leaf && atom.id === self.id 21 | change L (self) => new Carrot.Leaf(self.id) 22 | 23 | given P (self, element, atom) => element === Carrot.Part && atom.id === self.id 24 | change P (self) => new Carrot.Part(self.id) 25 | 26 | given 1 (element) => element.state > SOLID 27 | select 1 (atom) => atom 28 | change 1 (selected) => selected 29 | 30 | given 2 (element) => element.state > SOLID 31 | select 2 (atom) => atom 32 | change 2 (selected) => selected 33 | 34 | given 3 (element) => element.state > SOLID 35 | select 3 (atom) => atom 36 | change 3 (selected) => selected 37 | 38 | given n (element) => element.state === undefined || element.state <= SOLID 39 | 40 | element Leaf { 41 | colour "green" 42 | arg id 43 | arg grown true 44 | prop state SOLID 45 | prop temperature ROOM 46 | prop food PLANT 47 | 48 | any(xz.rotations) { 49 | origin g 50 | given g (self) => !self.grown 51 | keep g (self) => self.grown = true 52 | g => g 53 | _ P 54 | _ P 55 | 56 | g => _ 57 | 58 | P 1 59 | P => P 60 | @ P 61 | 1 @ 62 | 63 | P 2 64 | P => 1 65 | @12 @PP 66 | n . 67 | 68 | @PP => 123 69 | 123 @PP 70 | 71 | @PP => 1P2 72 | 1n P. 73 | 2 @ 74 | 75 | PP@ => 1@2 76 | 1n P. 77 | 2 P 78 | 79 | @PP => P12 80 | 1 n P . 81 | 2 @ 82 | 83 | PP@ => @12 84 | 1 n P . 85 | 2 P 86 | } 87 | 88 | all(xyz.directions) @PP => ... 89 | mimic(Powder) 90 | 91 | } 92 | 93 | element Part { 94 | colour "rgb(200, 80, 0)" 95 | arg id 96 | prop state SOLID 97 | prop temperature ROOM 98 | prop food PLANT 99 | 100 | any(xz.rotations) { 101 | L 1 102 | @ => L 103 | P @ 104 | 1 P 105 | 106 | L 1 107 | @ => 2 108 | P12 P@L 109 | 110 | L 1 111 | P => 2 112 | @12 @PL 113 | n . 114 | 115 | L@P => 123 116 | 123 L@P 117 | 118 | LP@ => 123 119 | 123 LP@ 120 | } 121 | 122 | all(xyz.directions) { 123 | P@L => ... 124 | @PL => ... 125 | } 126 | mimic(Powder) 127 | } 128 | } 129 | 130 | element Plant { 131 | colour "green" 132 | emissive "green" 133 | category "Life" 134 | prop state SOLID 135 | prop temperature ROOM 136 | prop food PLANT 137 | prop states () => ({ 138 | [HOT]: [Charcoal, 0.12] 139 | }) 140 | 141 | mimic(Powder) 142 | mimic(Temperature) 143 | 144 | maybe(1/50) { 145 | 146 | any(xyz.directions) { 147 | @ => @ 148 | _ $ 149 | } 150 | } 151 | 152 | } 153 | 154 | element Cheese { 155 | colour "#fcc203" 156 | emissive "#fc4e03" 157 | category "Life" 158 | prop state SOLID 159 | prop temperature ROOM 160 | prop food DAIRY 161 | prop stickiness 0.5 162 | prop states () => ({ 163 | [WARM]: Fondue, 164 | [HOT]: Fondue, 165 | }) 166 | 167 | // Make holes 168 | given i (self) => self.init !== true 169 | keep i (self, origin) => { 170 | if (Math.random() < 0.3) return SPACE.setAtom(origin, new Empty(), Empty) 171 | self.init = true 172 | } 173 | action i => i 174 | 175 | symbol S Cheese.Smell 176 | 177 | data age 0 178 | data smelly false 179 | given E (self, element) => { 180 | if (self.smelly) return element === Empty 181 | self.age++ 182 | if (self.age < 20) return false 183 | self.smelly = true 184 | return false 185 | } 186 | maybe(0.2) any(xyz.directions) action @E => @S 187 | 188 | mimic(Temperature) 189 | mimic(Sticky) 190 | mimic(Solid) 191 | 192 | element Smell { 193 | colour "#fcc203" 194 | emissive "#fc4e03" 195 | opacity 0.1 196 | prop state GAS 197 | prop temperature ROOM 198 | prop smell STINK 199 | mimic(Smell) 200 | } 201 | } 202 | 203 | element Fondue { 204 | colour "#fcc203" 205 | emissive "#fc4e03" 206 | category "Life" 207 | opacity 0.3 208 | prop state SOLID 209 | prop temperature BODY 210 | prop food DAIRY 211 | prop states () => ({ 212 | [COLD]: Cheese, 213 | [CHILLY]: Cheese, 214 | [COOL]: Cheese, 215 | }) 216 | 217 | symbol S Fondue.Smell 218 | maybe(0.003) any(xyz.directions) action @_ => @S 219 | mimic(Temperature) 220 | mimic(Goo) 221 | 222 | element Smell { 223 | colour "#fcc203" 224 | emissive "#fc4e03" 225 | opacity 0.1 226 | prop state GAS 227 | prop temperature ROOM 228 | prop smell STINK 229 | mimic(Smell) 230 | } 231 | } 232 | 233 | ` 234 | -------------------------------------------------------------------------------- /Source/Elements/Globals.js: -------------------------------------------------------------------------------- 1 | SpaceTode` 2 | 3 | origin @ 4 | change @ (self) => self 5 | 6 | given ! (origin, self) => origin.atom !== self 7 | 8 | symbol _ Empty 9 | symbol x Void 10 | symbol * Void 11 | 12 | given . (element) => element !== Void 13 | keep . 14 | 15 | given # (element) => element !== Empty && element !== Void 16 | keep # 17 | 18 | given $ (element, Self) => element === Self 19 | change $ (Self) => new Self() 20 | 21 | given ? (element) => element !== Void 22 | select ? (atom) => atom 23 | change ? (selected) => selected 24 | 25 | check < (self) => self.continue === false 26 | check > (self) => self.continue === true 27 | keep < (self) => self.continue = false 28 | keep > (self) => self.continue = true 29 | 30 | ` 31 | -------------------------------------------------------------------------------- /Source/Elements/Life.js: -------------------------------------------------------------------------------- 1 | SpaceTode` 2 | 3 | element Fish { 4 | prop state SOLID 5 | prop temperature BODY 6 | prop food MEAT 7 | prop diet PLANT 8 | data stuck false 9 | colour "rgb(255, 100, 0)" 10 | arg id 11 | category "Life" 12 | 13 | { 14 | given i (self) => self.id === undefined 15 | keep i (self) => self.id = Math.random() 16 | action i => i 17 | } 18 | 19 | { 20 | change T (self) => new Fish.Tail(self.id) 21 | @ => T 22 | _ @ 23 | 24 | any(xyz.rotations) { 25 | 26 | change t (self, Self) => new Self.Tail(self.id, true) 27 | symbol W Water 28 | @W => t@ 29 | 30 | given s (element, self, atom) => element === Fish.Tail && atom.id === self.id 31 | select s (atom) => atom 32 | change s (selected) => selected 33 | @s => s@ 34 | 35 | s _ 36 | @_ => .s 37 | 38 | } 39 | } 40 | 41 | element Tail { 42 | colour "rgb(255, 100, 0)" 43 | arg id 44 | arg holdsWater false 45 | 46 | given H (element, self, atom) => element === Fish && self.id === atom.id 47 | all(xyz.directions) @H => .. 48 | 49 | change w (self) => self.holdsWater? new Water() : new Empty() 50 | @ => w 51 | } 52 | } 53 | 54 | element Smeller { 55 | prop state SOLID 56 | prop temperature BODY 57 | data target undefined 58 | data interest 0 59 | 60 | given i (self) => self.target === undefined 61 | keep i (self) => { 62 | self.target = [0, 0, 0] 63 | } 64 | i => i 65 | 66 | 67 | given D (element) => element.state > SOLID 68 | select D (atom) => atom 69 | change D (selected) => selected 70 | @ => D 71 | D @ 72 | 73 | given P (element) => element === Pheromone 74 | select P (atom) => atom.target 75 | keep P (self, selected) => { 76 | if (selected === undefined) return 77 | self.target = [...selected] 78 | self.interest = 1.0 79 | } 80 | action for(xz.directions) @P => P. 81 | 82 | given I (self) => self.interest > 0 83 | keep I (self) => self.interest -= 0.01 84 | action I => I 85 | 86 | given M (element, x, z, self) => { 87 | if (self.interest <= 0) return false 88 | if (element.state <= SOLID) return false 89 | if (x > 0 && self.target[0] < 0) return true 90 | if (x < 0 && self.target[0] > 0) return true 91 | if (z > 0 && self.target[2] < 0) return true 92 | if (z < 0 && self.target[2] > 0) return true 93 | return false 94 | } 95 | select M (atom, x, z) => [atom, x, z] 96 | change M (selected) => { 97 | const [atom, x, z] = selected 98 | if (atom.target !== undefined) { 99 | atom.target[0] -= x 100 | atom.target[2] -= z 101 | } 102 | return atom 103 | } 104 | change m (x, z, self) => { 105 | self.target[0] += x 106 | self.target[2] += z 107 | return self 108 | } 109 | for(xz.directions) { 110 | @M => Mm 111 | 112 | M => m 113 | @ M 114 | } 115 | 116 | maybe(0.15) any(xz.directions) @D => D@ 117 | 118 | } 119 | 120 | element Mouse { 121 | category "Life" 122 | colour "white" 123 | emissive "grey" 124 | prop state SOLID 125 | prop temperature BODY 126 | prop food MEAT 127 | prop diet PLANT | DAIRY 128 | prop pheromone LOVE | STINK 129 | data stuck false 130 | data target undefined 131 | data interest 0.0 132 | arg energy 0.85 133 | arg id 134 | 135 | given T (self, element, atom, Self) => element === Self.Tail && atom.id == self.id 136 | change T (self, Self) => new Self.Tail(self.id) 137 | 138 | change M (self, Self) => new Self(self.id) 139 | 140 | //======// 141 | // Init // 142 | //======// 143 | given i (self) => self.id === undefined 144 | change i (self) => { 145 | self.id = Math.random() 146 | self.target = [0, 0, 0] 147 | return self 148 | } 149 | i => i 150 | 151 | //=======// 152 | // Scent // 153 | //=======// 154 | origin p 155 | given p (self) => self.energy > 0.9 156 | change p (self) => new Pheromone(0.94, self) 157 | given ~ (element) => element === Empty || false 158 | 159 | //=======// 160 | // Breed // 161 | //=======// 162 | given B (element, atom, self, Self) => (element === Self || element === Self.Tail) && (atom.id !== self.id) && self.energy > 0.9 163 | keep B (atom) => atom.energy -= 0.0 164 | change b (self, Self) => { 165 | self.energy -= 0.6 166 | return new Self(0.6) 167 | } 168 | given , (element) => element === Empty || element.state === GAS 169 | for(xyz.directions) { 170 | action p~ => .p 171 | @B, => BBb 172 | @,B => BbB 173 | } 174 | 175 | //=====// 176 | // Eat // 177 | //=====// 178 | given F (Self, element) => Flag.has(element.food, Self.diet) 179 | change e (self) => { 180 | self.energy += 0.05 181 | if (self.energy > 1) self.energy = 1 182 | return new Empty() 183 | } 184 | 185 | for(xyz.directions) { 186 | T@F => eT@ 187 | } 188 | 189 | for(xyz.rotations) { 190 | F => @ 191 | T@ eT 192 | } 193 | 194 | //=======// 195 | // Sniff // 196 | //=======// 197 | mimic(Sniffer) 198 | 199 | //======// 200 | // Fall // 201 | //======// 202 | given D (element) => element.state > SOLID 203 | select D (atom) => atom 204 | change D (selected) => selected 205 | 206 | given d (element) => element.state > SOLID 207 | select d (atom) => atom 208 | change d (selected) => selected 209 | T => D 210 | @ T 211 | D @ 212 | 213 | @ => T 214 | T @ 215 | 216 | for(xz.directions) { 217 | @T => Dd 218 | Dd @T 219 | 220 | @T => Dd 221 | Dd @T 222 | } 223 | 224 | for(xz.directions) { 225 | T => D 226 | @D .T 227 | } 228 | 229 | //==============// 230 | // Follow Smell // 231 | //==============// 232 | given s (self) => Math.random() > self.energy + 0.1 233 | given f (element, x, z, self) => { 234 | if (self.interest <= 0) return false 235 | if (element.state <= SOLID) return false 236 | if (x > 0 && self.target[0] < 0) return true 237 | if (x < 0 && self.target[0] > 0) return true 238 | if (z > 0 && self.target[2] < 0) return true 239 | if (z < 0 && self.target[2] > 0) return true 240 | return false 241 | } 242 | select f (atom, x, z) => [atom, x, z] 243 | change f (selected, self) => { 244 | const [atom, x, z] = selected 245 | if (atom.target !== undefined) { 246 | atom.target[0] -= x 247 | atom.target[2] -= z 248 | } 249 | return atom 250 | } 251 | 252 | change F (self, x, z) => { 253 | self.target[0] += x 254 | self.target[2] += z 255 | return self 256 | } 257 | 258 | //======// 259 | // Move // 260 | //======// 261 | given h (self) => self.energy > 0.9 262 | maybe(0.75) h => . 263 | 264 | given m (element) => element.state > SOLID 265 | select m (atom) => atom 266 | change m (selected, self) => { 267 | self.energy -= 0.0005 268 | if (self.energy < 0) self.energy = 0 269 | return selected 270 | } 271 | 272 | for(xz.directions) { 273 | T@f => fTF 274 | 275 | df => TF 276 | T@ df 277 | } 278 | 279 | for(xz) pov(top) { 280 | f F 281 | T@ => .f 282 | } 283 | 284 | action { 285 | 286 | s => . 287 | 288 | for(xz.directions) { 289 | T@m => mT@ 290 | 291 | dm => T@ 292 | T@ dm 293 | } 294 | 295 | for(xz) pov(top) { 296 | m @ 297 | T@ => .m 298 | } 299 | } 300 | 301 | ! => . 302 | 303 | //===========// 304 | // Grow Tail // 305 | //===========// 306 | given g (element, self) => element === Empty && self.energy > 0.5 307 | change g (self, Self) => { 308 | self.energy -= 0.5 309 | return new Self.Tail(self.id) 310 | } 311 | for(xyz.directions) @T => .. 312 | any(xyz.directions) @g => .g 313 | 314 | //==================// 315 | // Act Without Tail // 316 | //==================// 317 | @ => D 318 | D @ 319 | 320 | action { 321 | s => . 322 | for(xz.directions) @m => m@ 323 | } 324 | 325 | // TODO: more behaviours here? eg: smell following 326 | 327 | ! => . 328 | 329 | element Tail { 330 | prop state SOLID 331 | prop temperature BODY 332 | prop food MEAT 333 | prop diet PLANT | DAIRY 334 | data stuck false 335 | colour "pink" 336 | emissive "rgb(255, 64, 128)" 337 | arg id 338 | 339 | given ^ (element, atom, self) => element === SpaceTode.global.elements.Mouse 340 | for(xyz.directions) @^ => .. 341 | mimic(Powder) 342 | } 343 | } 344 | 345 | element Rabbit { 346 | category "Life" 347 | colour "white" 348 | emissive "grey" 349 | prop state SOLID 350 | prop temperature BODY 351 | prop food MEAT 352 | prop diet PLANT 353 | prop pheromone LOVE 354 | data stuck false 355 | data target undefined 356 | data interest 0.0 357 | data energy 0.6 358 | 359 | //================// 360 | // Global Symbols // 361 | //================// 362 | // Rabbit 363 | given R (element, atom, self) => element === Rabbit && atom.id === self.id 364 | change R (self) => new Rabbit(self.id) 365 | 366 | // Rabbit Ears 367 | given E (element, atom, self) => element === Rabbit.Ear && atom.id === self.id 368 | change E (self) => new Rabbit.Ear(self.id) 369 | 370 | // Stretch 371 | given r (element, atom, self) => element === Rabbit.Stretch && atom.id === self.id 372 | change r (self) => new Rabbit.Stretch(self.id) 373 | 374 | // Stretch Ears 375 | given e (element, atom, self) => element === Rabbit.Stretch.Ear && atom.id === self.id 376 | change e (self) => new Rabbit.Stretch.Ear(self.id) 377 | 378 | //======// 379 | // Init // 380 | //======// 381 | { 382 | arg id 383 | given i (self) => self.id === undefined 384 | change i (self) => { 385 | self.id = Math.random() 386 | self.target = [0, 0, 0] 387 | return self 388 | } 389 | i => i 390 | } 391 | 392 | //===========// 393 | // Grow Ears // 394 | //===========// 395 | { 396 | data grown false 397 | origin g 398 | given g (self) => !self.grown 399 | keep g (self) => self.grown = true 400 | for(xz.swaps) { 401 | _ _ E E 402 | _g_ => EgE 403 | } 404 | } 405 | 406 | //======// 407 | // Land // 408 | //======// 409 | { 410 | 411 | given L (element) => element.state <= SOLID 412 | keep l (self) => { 413 | self.jumpRemaining = 5 414 | self.jumpDirection = undefined 415 | } 416 | 417 | maybe(1/25) { 418 | action { 419 | @ => l 420 | L . 421 | } 422 | 423 | for(xz.directions) action { 424 | E@ => .l 425 | L . 426 | } 427 | 428 | for(xz.directions) action { 429 | @E => l. 430 | L . 431 | } 432 | } 433 | } 434 | 435 | //======// 436 | // Move // 437 | //======// 438 | { 439 | // If I'm currently stretching, catch up with (or wait for) the stretch 440 | given g (element, atom, self) => element === Rabbit.Stretch && atom.id === self.id && atom.grown 441 | for(xz.rotations) { 442 | g => @ 443 | @ _ 444 | 445 | r . 446 | @ => > 447 | } 448 | } 449 | 450 | action @ => < 451 | action { 452 | 453 | // Check I have all my body parts (: 454 | all(xz.rotations) action { 455 | E E . . 456 | E@E => .>. 457 | } 458 | < => > 459 | 460 | // Start a new stretch 461 | given n (element, self, transformationNumber) => { 462 | if (self.jumpDirection === undefined) { 463 | self.jumpDirection = transformationNumber 464 | } 465 | else if (transformationNumber == self.jumpDirection) { 466 | 467 | } 468 | else return false 469 | self.jumpRemaining-- 470 | if (element === Empty && self.jumpRemaining >= 0) { 471 | return true 472 | } 473 | return false 474 | } 475 | change n (self, transformationNumber) => { 476 | let t = transformationNumber + 1 477 | if (t > 3) t -= 4 478 | //self.jumpRemaining-- 479 | //self.jumpRemaining = 5 480 | return new Rabbit.Stretch(self.id, t) 481 | } 482 | for(xz.rotations) { 483 | n n 484 | @ => < 485 | } 486 | } 487 | < => . 488 | 489 | //======// 490 | // Fall // 491 | //======// 492 | data jumpRemaining 0 493 | data jumpDirection 494 | { 495 | given 1 (element) => element.state > SOLID 496 | select 1 (atom) => atom 497 | change 1 (selected) => selected 498 | 499 | given 2 (element) => element.state > SOLID 500 | select 2 (atom) => atom 501 | change 2 (selected) => selected 502 | 503 | given 3 (element) => element.state > SOLID 504 | select 3 (atom) => atom 505 | change 3 (selected) => selected 506 | 507 | origin f 508 | given f (self) => self.jumpRemaining <= 0 509 | 510 | for(xz.rotations) { 511 | E E => 1 3 512 | EfE E2E 513 | 123 E@E 514 | } 515 | } 516 | 517 | //=========// 518 | // Injured // 519 | //=========// 520 | { 521 | given m (element, atom, self) => (element === Rabbit.Stretch.Ear || element === Rabbit.Ear) && atom.id === self.id 522 | for(xz.rotations) { 523 | m m . . 524 | m@m => ... 525 | 526 | r . 527 | @ => . 528 | 529 | @e => .. 530 | } 531 | 532 | // Fall 533 | given I (element) => element.state > SOLID 534 | select I (atom) => atom 535 | change I (selected) => selected 536 | @ => I 537 | I @ 538 | 539 | // Move 540 | any(xz.rotations) { 541 | @I => I@ 542 | } 543 | } 544 | 545 | //==============// 546 | // Sub-Elements // 547 | //==============// 548 | element Ear { 549 | //category "Life" 550 | colour "white" 551 | emissive "grey" 552 | prop state SOLID 553 | prop temperature BODY 554 | data stuck false 555 | arg id 556 | arg part 557 | 558 | // Catch up with my stretch 559 | given g (element, atom, self) => element === Rabbit.Stretch.Ear && atom.id === self.id 560 | for(xz.rotations) { 561 | g @ 562 | @ => _ 563 | } 564 | } 565 | 566 | element Stretch { 567 | colour "white" 568 | emissive "grey" 569 | //colour "pink" 570 | //emissive "purple" 571 | //opacity 0.5 572 | prop state SOLID 573 | prop temperature BODY 574 | prop food MEAT 575 | data stuck false 576 | arg id 577 | arg transformationNumber 578 | 579 | { 580 | data grown false 581 | origin g 582 | given g (self, transformationNumber) => !self.grown && self.transformationNumber === transformationNumber 583 | keep g (self) => self.grown = true 584 | for(xz.rotations) { 585 | _ _ e e 586 | _g_ => ege 587 | } 588 | 589 | origin G 590 | given G (self) => self.grown 591 | given m (element, atom, self) => (element === Rabbit.Stretch.Ear || element === Rabbit.Ear) && atom.id === self.id 592 | for(xz.rotations) { 593 | m m . . 594 | mGm => ... 595 | } 596 | 597 | @ => _ 598 | } 599 | 600 | element Ear { 601 | colour "white" 602 | emissive "grey" 603 | //colour "pink" 604 | //emissive "purple" 605 | //opacity 0.5 606 | prop state SOLID 607 | prop temperature BODY 608 | data stuck false 609 | arg id 610 | arg part 611 | 612 | } 613 | 614 | } 615 | 616 | } 617 | 618 | element Ant { 619 | colour "grey" 620 | emissive "black" 621 | category "Life" 622 | prop state SOLID 623 | prop temperature BODY 624 | prop food Flag.and(BUG, MEAT) 625 | 626 | given M (element) => element.state > LIQUID && element.state !== EFFECT 627 | select M (atom) => atom 628 | change M (selected) => selected 629 | given m (element) => element.state > LIQUID && element.state !== EFFECT 630 | 631 | given S (element, selfElement) => element.state <= SOLID && element !== selfElement 632 | any(xyz.rotations) { 633 | @M => M@ 634 | S . 635 | 636 | mM => .@ 637 | @S M. 638 | } 639 | 640 | all(xyz.directions) { 641 | @S => .. 642 | } 643 | 644 | given A (element, selfElement) => element === selfElement 645 | any(xz.rotations) { 646 | @M => M@ 647 | A . 648 | } 649 | 650 | given F (element) => element.state > LIQUID 651 | select F (atom) => atom 652 | change F (selected) => selected 653 | @ => F 654 | F @ 655 | 656 | } 657 | 658 | element OldRabbit { 659 | colour "white" 660 | emissive "grey" 661 | //category "Life" 662 | prop state SOLID 663 | prop temperature BODY 664 | prop food MEAT 665 | 666 | data id 667 | 668 | element Part { 669 | colour "white" 670 | emissive "grey" 671 | arg id 672 | prop state SOLID 673 | prop temperature BODY 674 | prop food MEAT 675 | 676 | given R (element, atom, self) => element === OldRabbit && atom.id === self.id 677 | @R => .. 678 | R@ => .. 679 | 680 | @ => . 681 | R . 682 | 683 | @ => . 684 | R . 685 | 686 | @ => _ 687 | } 688 | 689 | // Init ID 690 | given i (self) => self.id === undefined 691 | keep i (self) => self.id = Math.random() 692 | i => i 693 | 694 | // Grow body 695 | change P (self, atom) => { 696 | const part = new OldRabbit.Part(self.id) 697 | part.colour = self.colour 698 | part.emissive = self.emissive 699 | return part 700 | } 701 | @_ => .P 702 | _@ => P. 703 | 704 | _ => P 705 | @ . 706 | 707 | _ => P 708 | @ . 709 | 710 | // Die because can't grow 711 | given n (element, atom, self) => element !== OldRabbit.Part || atom.id !== self.id 712 | n => . 713 | @ _ 714 | _ . 715 | 716 | n => . 717 | @ _ 718 | _ . 719 | 720 | @n => _. 721 | _ . 722 | 723 | n@ => ._ 724 | _ . 725 | 726 | // Fall down 727 | given P (element, atom, self) => element === OldRabbit.Part && atom.id === self.id 728 | P P _ _ 729 | P@P => P_P 730 | ___ P@P 731 | 732 | any(xyz.directions) { 733 | 734 | // Eat 735 | given C (element) => element === Carrot.Leaf || element === Carrot.Part 736 | keep C (atom) => atom.eaten = true 737 | @.C => ..C 738 | @C => .C 739 | 740 | // Breed 741 | given R (element, atom, self) => (element === OldRabbit || element === OldRabbit.Part) && self.id !== atom.id 742 | change B () => new OldRabbit() 743 | maybe(1/15) { 744 | @_R => .B. 745 | @R_ => ..B 746 | _@R => B.. 747 | _@.R => B... 748 | } 749 | } 750 | 751 | // Move 752 | maybe(1/2) pov(right) any(z) { 753 | @_ => _@ 754 | } 755 | 756 | any(x) { 757 | P_P_ _P_P 758 | P@P_ => _P@P 759 | } 760 | 761 | } 762 | 763 | ` -------------------------------------------------------------------------------- /Source/Elements/Old/TodeSplat2/Bondo.js: -------------------------------------------------------------------------------- 1 | TodeSplat` 2 | 3 | element BigSandSeed { 4 | colour "pink" 5 | emissive "purple" 6 | //precise true 7 | //pour false 8 | category "sandbox" 9 | //default true 10 | 11 | data id -1 12 | 13 | input i extends _ ({self}) => self.id = Math.random() 14 | output P ({space, self}) => { 15 | const atom = ATOM.make(BigSandPart) 16 | atom.id = self.id 17 | SPACE.setAtom(space, atom) 18 | } 19 | 20 | rule { 21 | @ => P 22 | i P 23 | } 24 | 25 | } 26 | 27 | element BigSandPart { 28 | colour "pink" 29 | emissive "purple" 30 | hidden true 31 | 32 | data id -1 33 | 34 | input P extends # ({space, self}) => space.atom.element == BigSandPart && self.id == space.atom.id 35 | input T extends # ({space, self}) => space.atom.element == BigSandTrail && self.id == space.atom.id 36 | input t ({space, args, self}) => { 37 | if (space && space.atom && space.atom.element == BigSandTrail && self.id == space.atom.id) { 38 | args.success = true 39 | args.trailSpace = space 40 | } 41 | return true 42 | } 43 | 44 | output m ({self, space, trailSpace}) => { 45 | if (space == trailSpace) { 46 | SPACE.setAtom(space, self) 47 | } 48 | } 49 | 50 | output T ({self, space}) => { 51 | const atom = ATOM.make(BigSandTrail) 52 | atom.id = self.id 53 | SPACE.setAtom(space, atom) 54 | } 55 | 56 | input S extends # ({space, self}) => { 57 | if (space.atom.element == BigSandPart && space.atom.id == self.id) return false 58 | if (space.atom.element == BigSandTrail && space.atom.id == self.id) return false 59 | return true 60 | } 61 | 62 | rule XZ { @t => ?? => _m } 63 | rule { 64 | @ => _ 65 | T @ 66 | } 67 | 68 | rule { 69 | @ => T 70 | _ @ 71 | } 72 | 73 | rule xz { 74 | @_ => __ 75 | P_ P@ 76 | } 77 | 78 | rule xz { 79 | @_ => TT 80 | S_ S@ 81 | } 82 | 83 | } 84 | 85 | element BigSandTrail { 86 | colour "lightblue" 87 | emissive "blue" 88 | hidden true 89 | 90 | data id -1 91 | 92 | } 93 | 94 | element TallSand { 95 | 96 | colour "pink" 97 | emissive "purple" 98 | //precise true 99 | //pour false 100 | category "sandbox" 101 | 102 | input e extends _ ({args}) => args.id = Math.random() 103 | 104 | output p ({space, id}) => { 105 | const atom = ATOM.make(FloaterBondoPart) 106 | atom.id = id 107 | atom.subid = Math.random() 108 | SPACE.setAtom(space, atom) 109 | } 110 | 111 | rule xyz { @e => pp } 112 | 113 | } 114 | 115 | element FloaterBondoPart { 116 | colour "pink" 117 | emissive "purple" 118 | hidden true 119 | 120 | input F extends # ({space}) => space.atom.element == FloaterBondoPart 121 | input B extends F ({space, self, args}) => { 122 | if (space.atom.id == self.id) { 123 | args.buddy = space.atom 124 | return true 125 | } 126 | } 127 | 128 | input T extends # ({space, self}) => { 129 | return space.atom.element == FloaterBondoTrail && space.atom.id == self.id && space.atom.subid != self.subid 130 | } 131 | 132 | input t ({space, args, self}) => { 133 | if (space && space.atom && space.atom.element == FloaterBondoTrail && space.atom.id == self.id && space.atom.subid != self.subid) { 134 | args.success = true 135 | args.trailSpace = space 136 | } 137 | return true 138 | } 139 | 140 | output s ({space, trailSpace, self}) => { 141 | if (space == trailSpace) { 142 | SPACE.setAtom(trailSpace, self) 143 | } 144 | } 145 | 146 | output T ({space, self}) => { 147 | const atom = ATOM.make(FloaterBondoTrail) 148 | atom.id = self.id 149 | atom.subid = self.subid 150 | SPACE.setAtom(space, atom) 151 | } 152 | 153 | output B ({space, buddy}) => SPACE.setAtom(space, buddy) 154 | 155 | rule XZ { 156 | @t => ?? => Ts 157 | } 158 | 159 | rule { 160 | @ ? T 161 | t => ? => s 162 | } 163 | 164 | rule xz { 165 | B@ BT 166 | _ => @ 167 | } 168 | 169 | rule { 170 | B B 171 | @ => T 172 | _ @ 173 | } 174 | 175 | rule xz { 176 | B B 177 | @_ => T@ 178 | #_ #_ 179 | } 180 | 181 | } 182 | 183 | element FloaterBondoTrail { 184 | colour "lightblue" 185 | emissive "blue" 186 | hidden true 187 | 188 | input t ({space, args, self}) => { 189 | if (space && space.atom && space.atom.element == FloaterBondoPart && space.atom.id == self.id && space.atom.subid == self.subid) { 190 | args.success = true 191 | } 192 | return true 193 | } 194 | 195 | 196 | rule XYZ { 197 | @B => !! => _. 198 | } 199 | 200 | } 201 | 202 | 203 | 204 | ` -------------------------------------------------------------------------------- /Source/Elements/Old/TodeSplat2/Clear.js: -------------------------------------------------------------------------------- 1 | TodeSplat` 2 | 3 | element Clear { 4 | 5 | colour "brown" 6 | emissive "brown" 7 | precise true 8 | category "clear" 9 | 10 | input t ({space, args}) => { 11 | if (space && space.atom && space.atom.element == ClearDone) { 12 | args.success = true 13 | } 14 | return true 15 | } 16 | 17 | input e ({space, args}) => { 18 | if (!space) { 19 | args.success = true 20 | } 21 | return true 22 | } 23 | 24 | input D extends # ({space}) => space.atom.element == ClearDone 25 | output D ({space}) => SPACE.setAtom(space, ATOM.make(ClearDone)) 26 | output c ({space}) => { if (space) SPACE.setAtom(space, ATOM.make(Clear)) } 27 | 28 | rule XYZ { @t => ?? => D. } 29 | rule XY { @e => ?? => D. } 30 | rule XYZ { @- => .c } 31 | 32 | } 33 | 34 | element ClearDone { 35 | 36 | colour "blue" 37 | emissive "blue" 38 | hidden true 39 | 40 | input c ({space, args}) => { 41 | if (space && space.atom && space.atom.element == Clear) { 42 | args.success = false 43 | } 44 | return true 45 | } 46 | 47 | output B ({space}) => SPACE.setAtom(space, ATOM.make(ClearBomb)) 48 | 49 | rule XYZ { @c => !! => B. } 50 | 51 | } 52 | 53 | element ClearBomb { 54 | colour "black" 55 | emissive "black" 56 | hidden true 57 | 58 | output e ({space}) => { 59 | if (space) SPACE.setAtom(space, undefined) 60 | } 61 | 62 | rule XYZ { @- => ee } 63 | } 64 | 65 | ` -------------------------------------------------------------------------------- /Source/Elements/Old/TodeSplat2/EdgeClear.js: -------------------------------------------------------------------------------- 1 | TodeSplat` 2 | 3 | element EdgeClear { 4 | 5 | colour "brown" 6 | emissive "brown" 7 | precise true 8 | family "EdgeClear" 9 | category "clear" 10 | 11 | output E ({space}) => SPACE.setAtom(space, ATOM.make(EdgeClearEdge)) 12 | input N extends . ({space}) => { 13 | if (!space.atom) return true 14 | if (space.atom.element.family != "EdgeClear") return true 15 | } 16 | 17 | rule { @NN => @@@ } 18 | rule { @N => @@ } 19 | rule { @x => Ex } 20 | } 21 | 22 | element EdgeClearEdge { 23 | colour "blue" 24 | emissive "darkblue" 25 | hidden true 26 | family "EdgeClear" 27 | 28 | input E ({space}) => { 29 | if (!space) return true 30 | if (space.atom && space.atom.element.family == "EdgeClear") return true 31 | } 32 | 33 | output B ({space}) => SPACE.setAtom(space, ATOM.make(EdgeClearBomb)) 34 | 35 | rule Y { 36 | @ => B 37 | E . 38 | } 39 | 40 | rule Y { 41 | @ => @ 42 | . @ 43 | } 44 | 45 | } 46 | 47 | element EdgeClearBomb { 48 | colour "grey" 49 | emissive "black" 50 | hidden true 51 | family "EdgeClear" 52 | 53 | input N extends . ({space}) => { 54 | if (!space.atom) return true 55 | if (space.atom.element == EdgeClear) return true 56 | if (space.atom.element.family != "EdgeClear") return true 57 | } 58 | 59 | input B ({space}) => space && space.atom && space.atom.element == EdgeClearBomb 60 | input D ({space}) => space && space.atom && space.atom.element == EdgeClearDone 61 | output D ({space}) => SPACE.setAtom(space, ATOM.make(EdgeClearDone)) 62 | 63 | 64 | rule { NN@ => @@@ } 65 | rule { N@ => @@ } 66 | 67 | rule xy { D@ => DD } 68 | rule XY { B@ => BD } 69 | 70 | 71 | } 72 | 73 | element EdgeClearDone { 74 | colour "lightgreen" 75 | emissive "green" 76 | hidden true 77 | family "EdgeClear" 78 | 79 | input b ({space, args}) => { 80 | if (!space) return true 81 | if (!space.atom) return true 82 | if (space.atom.element == EdgeClearBomb) args.success = false 83 | return true 84 | } 85 | 86 | rule XY { b@ => !! => b_ } 87 | } 88 | ` -------------------------------------------------------------------------------- /Source/Elements/Old/TodeSplat2/FatFly.js: -------------------------------------------------------------------------------- 1 | TodeSplat` 2 | 3 | 4 | element FatFly { 5 | 6 | colour "royalblue" 7 | emissive "darkblue" 8 | precise true 9 | pour false 10 | category "life" 11 | 12 | input e ({space, args}) => { 13 | if (space && !space.atom) args.success = true 14 | return true 15 | } 16 | 17 | input L extends # ({space, args}) => { 18 | if (space.atom.element == FatFlyLimb) return true 19 | } 20 | 21 | output L ({space, args}) => { 22 | SPACE.setAtom(space, ATOM.make(FatFlyLimb)) 23 | } 24 | 25 | output l ({space}) => { 26 | if (space && !space.atom) { 27 | SPACE.setAtom(space, ATOM.make(FatFlyLimb)) 28 | } 29 | } 30 | 31 | // Grow (or regrow) limbs 32 | rule XYZ { 33 | @e => ?? => @l 34 | e ? l 35 | } 36 | 37 | rule xyz { @L_ => L@_ } 38 | 39 | } 40 | 41 | element FatFlyLimb { 42 | hidden true 43 | colour "royalblue" 44 | emissive "darkblue" 45 | 46 | input f ({space, args}) => { 47 | if (!space || !space.atom) return true 48 | if (space.atom.element == FatFly) args.success = true 49 | return true 50 | } 51 | 52 | rule XYZ { 53 | @f => ?? => .. 54 | f ? . 55 | } 56 | 57 | rule { @ => _ } 58 | 59 | } 60 | 61 | 62 | 63 | 64 | ` -------------------------------------------------------------------------------- /Source/Elements/Old/TodeSplat2/Pheromone.js: -------------------------------------------------------------------------------- 1 | TodeSplat` 2 | 3 | 4 | element Pheromone { 5 | 6 | colour "pink" 7 | emissive "purple" 8 | opacity 0.3 9 | 10 | hidden true 11 | data distance 0 12 | 13 | output m ({space, self}) => { 14 | SPACE.setAtom(space, undefined) 15 | self.distance++ 16 | } 17 | 18 | rule 0.001 { @ => _ } 19 | rule xyz { @_ => m@ } 20 | 21 | } 22 | 23 | 24 | 25 | 26 | ` -------------------------------------------------------------------------------- /Source/Elements/Old/TodeSplat2/Platformer.js: -------------------------------------------------------------------------------- 1 | TodeSplat` 2 | 3 | element Platformer { 4 | 5 | colour "red" 6 | emissive "darkred" 7 | precise true 8 | pour false 9 | category "player" 10 | 11 | input r () => Keyboard.ArrowRight 12 | input l () => Keyboard.ArrowLeft 13 | input f () => Keyboard.ArrowUp 14 | input b () => Keyboard.ArrowDown 15 | 16 | rule { 17 | @ => _ 18 | _ @ 19 | } 20 | 21 | rule { @_ => rr => _@ } 22 | rule { _@ => ll => @_ } 23 | 24 | rule side { @_ => bb => _@ } 25 | rule side { _@ => ff => @_ } 26 | 27 | rule { 28 | _ => r => @ 29 | @# rr _# 30 | } 31 | 32 | rule { 33 | _ => l => @ 34 | #@ ll #_ 35 | } 36 | 37 | rule side { 38 | _ => b => @ 39 | @# bb _# 40 | } 41 | 42 | rule side { 43 | _ => f => @ 44 | #@ ff #_ 45 | } 46 | 47 | } 48 | 49 | 50 | ` -------------------------------------------------------------------------------- /Source/Elements/Old/TodeSplat2/PlayerSnake.js: -------------------------------------------------------------------------------- 1 | let snakerDirection = "right" 2 | 3 | on.keydown(e => { 4 | if (snakerDirection == "right") { 5 | //if (e.key == "ArrowLeft") return snakerDirection = "forward" 6 | //if (e.key == "ArrowRight") return snakerDirection = "back" 7 | if (e.key == "ArrowUp") return snakerDirection = "up" 8 | if (e.key == "ArrowDown") return snakerDirection = "down" 9 | } 10 | if (snakerDirection == "left") { 11 | //if (e.key == "ArrowLeft") return snakerDirection = "back" 12 | //if (e.key == "ArrowRight") return snakerDirection = "forward" 13 | if (e.key == "ArrowUp") return snakerDirection = "up" 14 | if (e.key == "ArrowDown") return snakerDirection = "down" 15 | } 16 | /*if (snakerDirection == "forward") { 17 | if (e.key == "ArrowLeft") return snakerDirection = "left" 18 | if (e.key == "ArrowRight") return snakerDirection = "right" 19 | if (e.key == "ArrowUp") return snakerDirection = "up" 20 | if (e.key == "ArrowDown") return snakerDirection = "down" 21 | }*/ 22 | /*if (snakerDirection == "back") { 23 | if (e.key == "ArrowLeft") return snakerDirection = "right" 24 | if (e.key == "ArrowRight") return snakerDirection = "left" 25 | if (e.key == "ArrowUp") return snakerDirection = "up" 26 | if (e.key == "ArrowDown") return snakerDirection = "down" 27 | }*/ 28 | if (snakerDirection == "up") { 29 | if (e.key == "ArrowLeft") return snakerDirection = "left" 30 | if (e.key == "ArrowRight") return snakerDirection = "right" 31 | //if (e.key == "ArrowUp") return snakerDirection = "back" 32 | //if (e.key == "ArrowDown") return snakerDirection = "forward" 33 | } 34 | if (snakerDirection == "down") { 35 | if (e.key == "ArrowLeft") return snakerDirection = "left" 36 | if (e.key == "ArrowRight") return snakerDirection = "right" 37 | //if (e.key == "ArrowUp") return snakerDirection = "forward" 38 | //if (e.key == "ArrowDown") return snakerDirection = "back" 39 | } 40 | }) 41 | 42 | TodeSplat` 43 | 44 | element Snaker { 45 | 46 | colour "green" 47 | emissive "darkgreen" 48 | precise true 49 | pour false 50 | category "player" 51 | 52 | data score 0 53 | 54 | //===========// 55 | // FUNCTIONS // 56 | //===========// 57 | // Is it the next trail? 58 | input t ({space, args, self, tests}) => { 59 | if (tests.length == 0) tests[0] = ({isTrail}) => isTrail 60 | 61 | if (!space || !space.atom) return true 62 | if (space.atom.element != PlayerSnakeTrail) return true 63 | 64 | const trail = space.atom 65 | if (trail.score == self.score + 1) { 66 | args.isTrail = true 67 | args.trail = space.atom 68 | args.trailSpace = space 69 | } 70 | return true 71 | } 72 | 73 | // Place the trail that I ate 74 | output t ({space, self, trail}) => { 75 | SPACE.setAtom(space, trail) 76 | trail.score = self.score 77 | } 78 | 79 | // Place me in the place the trail was 80 | output o ({space, self, trailSpace}) => { 81 | if (space != trailSpace) return 82 | SPACE.setAtom(space, self) 83 | } 84 | 85 | // Create new trail 86 | output T ({space, self}) => { 87 | const trail = ATOM.make(PlayerSnakeTrail, {score: self.score}) 88 | SPACE.setAtom(space, trail) 89 | } 90 | 91 | // Am I waiting for my trail to be caught up on? 92 | input * ({space, args, self, tests}) => { 93 | if (tests.length == 0) tests[0] = ({isWaiting}) => isWaiting 94 | 95 | if (!space || !space.atom) return true 96 | if (space.atom.element != PlayerSnakeTrail) return true 97 | const trail = space.atom 98 | 99 | if (trail.score == self.score) { 100 | args.isWaiting = true 101 | } 102 | 103 | return true 104 | } 105 | 106 | // Are there higher scoring snakes around me? 107 | input ^ ({space, self, tests, args}) => { 108 | 109 | if (tests.length == 0) tests[0] = ({isObeying}) => isObeying 110 | 111 | if (!space || !space.atom) return true 112 | if (space.atom.element != Snaker && space.atom.element != PlayerSnakeTrail) return true 113 | 114 | if (space.atom.score == self.score + 1) { 115 | args.isObeying = true 116 | } 117 | return true 118 | } 119 | 120 | // Is it Res? 121 | input F extends # ({space}) => space.atom.element.isFood 122 | 123 | // Make a new leader 124 | output l ({space, self}) => { 125 | const leader = ATOM.make(Snaker, {score: self.score + 1}) 126 | self.leader = false 127 | SPACE.setAtom(space, leader) 128 | } 129 | 130 | //=======// 131 | // RULES // 132 | //=======// 133 | // Wait for snakes to catch up 134 | rule XYZ { @* => .. } 135 | 136 | // Move up trail 137 | rule XYZ { @t => to } 138 | 139 | // If there is a higher score snake/trail nearby, do nothing 140 | rule XYZ { @^ => .. } 141 | 142 | // Otherwise, assume I am the leader: 143 | input r () => snakerDirection == "right" 144 | rule { @F => rr => @l } 145 | rule { @_ => rr => T@ } 146 | 147 | input l () => snakerDirection == "left" 148 | rule { F@ => ll => l@ } 149 | rule { _@ => ll => @T } 150 | 151 | input f () => snakerDirection == "forward" 152 | rule side { F@ => ff => l@ } 153 | rule side { _@ => ff => @T } 154 | 155 | input b () => snakerDirection == "back" 156 | rule side { @F => bb => @l } 157 | rule side { @_ => bb => T@ } 158 | 159 | input u () => snakerDirection == "up" 160 | rule { 161 | F => u => l 162 | @ u @ 163 | } 164 | rule { 165 | _ => u => @ 166 | @ u T 167 | } 168 | 169 | input d () => snakerDirection == "down" 170 | rule { 171 | @ => d => @ 172 | F d l 173 | } 174 | rule { 175 | @ => d => T 176 | _ d @ 177 | } 178 | 179 | } 180 | 181 | element PlayerSnakeTrail { 182 | 183 | colour "blue" 184 | emissive "darkblue" 185 | 186 | // Can I die? 187 | input * ({space, self}) => { 188 | if (!space || !space.atom) return true 189 | if (space.atom.element != Snaker && space.atom.element != PlayerSnakeTrail) return true 190 | if (space.atom.score != self.score - 1) return true 191 | return false 192 | } 193 | rule XYZ { @* => _. } 194 | 195 | } 196 | 197 | ` -------------------------------------------------------------------------------- /Source/Elements/Old/TodeSplat2/Rope.js: -------------------------------------------------------------------------------- 1 | TodeSplat` 2 | 3 | element Rope { 4 | 5 | colour "brown" 6 | precise true 7 | category "sandbox" 8 | pour false 9 | 10 | data remaining 50 11 | 12 | input r ({self}) => { 13 | if (!self.id) self.id = Math.random() 14 | return self.remaining > 0 15 | } 16 | input f ({self}) => self.remaining <= 0 17 | 18 | output P ({space, self}) => { 19 | self.remaining-- 20 | const atom = ATOM.make(RopePart) 21 | atom.id = self.id 22 | SPACE.setAtom(space, atom) 23 | } 24 | 25 | rule { @f => _* } 26 | rule { @_ => P@ } 27 | rule { @x => Px } 28 | } 29 | 30 | element RopePart { 31 | colour "#3D1600" 32 | hidden true 33 | 34 | input P extends # ({space, self}) => space.atom.element == RopePart && space.atom.id == self.id 35 | 36 | input N ({space, self}) => { 37 | if (!space) return true 38 | if (!space.atom) return true 39 | if (space.atom.element == RopePart) { 40 | if (self.id != space.atom.id) return true 41 | } 42 | } 43 | 44 | input h ({space, args}) => { 45 | if (space && space.atom && (space.atom.element == Fire || space.atom.element == Lava)) args.success = true 46 | return true 47 | } 48 | 49 | output F ({space}) => { 50 | SPACE.setAtom(space, ATOM.make(Fire)) 51 | } 52 | 53 | rule XYZ { 54 | h => ? => . 55 | @h ?? F. 56 | } 57 | 58 | rule { 59 | NNN NNN 60 | @ => _ 61 | _ @ 62 | } 63 | 64 | rule x { 65 | PNN => PNN 66 | _@N @_N 67 | N N 68 | } 69 | 70 | rule x { 71 | _@ => @_ 72 | _PP _PP 73 | } 74 | 75 | rule x { 76 | PP_ => PP_ 77 | @_ _@ 78 | } 79 | 80 | rule x { 81 | P => P 82 | P@P P_P 83 | _ @ 84 | } 85 | 86 | /*rule x { 87 | @ => @ 88 | PP P_ 89 | _ P 90 | }*/ 91 | 92 | /*rule { 93 | NN NN 94 | @N => _N 95 | _PN @PN 96 | }*/ 97 | 98 | /*rule { 99 | P P 100 | @ => T 101 | _ @ 102 | }*/ 103 | 104 | } 105 | 106 | element RopeTrail { 107 | hidden true 108 | colour "black" 109 | 110 | } 111 | 112 | element Plank { 113 | colour "brown" 114 | 115 | precise true 116 | category "sandbox" 117 | pour false 118 | 119 | output p ({space, id}) => { 120 | if (!space) return 121 | const atom = ATOM.make(BrickPart, {id}) 122 | SPACE.setAtom(space, atom) 123 | } 124 | 125 | input i ({args, id}) => { 126 | if (!id) { 127 | args.id = Math.random() 128 | } 129 | else args.id = id 130 | return true 131 | } 132 | 133 | rule XZ top { 134 | @i* => ppp 135 | *** ppp 136 | } 137 | 138 | } 139 | 140 | element BrickPart { 141 | colour "saddlebrown" 142 | hidden true 143 | 144 | data id -1 145 | 146 | input p ({space, args, self}) => { 147 | if (space && space.atom && space.atom.element == BrickPart) { 148 | if (space.atom.id == self.id) args.success = true 149 | } 150 | return true 151 | } 152 | 153 | input n ({space, self}) => { 154 | if (!space) return true 155 | if (!space.atom) return true 156 | if (space.atom.element == BrickPart) { 157 | if (self.id != space.atom.id) return true 158 | } 159 | } 160 | 161 | rule XZ { 162 | n n n n 163 | @ => _ 164 | _ @ 165 | } 166 | 167 | } 168 | 169 | element Stretch { 170 | colour "blue" 171 | hidden true 172 | } 173 | 174 | ` -------------------------------------------------------------------------------- /Source/Elements/Old/TodeSplat2/SleepyFly.js: -------------------------------------------------------------------------------- 1 | TodeSplat` 2 | 3 | 4 | element SleepyFly { 5 | 6 | colour "royalblue" 7 | emissive "darkblue" 8 | precise true 9 | //hidden true 10 | pour false 11 | category "life" 12 | 13 | data sleeping false 14 | 15 | input s ({self}) => self.sleeping 16 | input a ({self}) => !self.sleeping 17 | 18 | output s ({self}) => self.sleeping = true 19 | output a ({self}) => self.sleeping = false 20 | 21 | // Go to sleep 22 | rule 0.001 { @a => @s } 23 | 24 | // Wake up 25 | rule 0.01 { @s => @a } 26 | 27 | // Move if awake 28 | rule xyz { @_ => aa => _@ } 29 | 30 | // Sleep if asleep 31 | rule { 32 | @ => s => _ 33 | _ s @ 34 | } 35 | 36 | } 37 | 38 | 39 | 40 | 41 | ` -------------------------------------------------------------------------------- /Source/Elements/Old/TodeSplat2/Snake.js: -------------------------------------------------------------------------------- 1 | TodeSplat` 2 | 3 | 4 | // NOT WORKING AT THE MOMENT BECause I removed "tests" 5 | 6 | element Snake { 7 | 8 | colour "green" 9 | emissive "darkgreen" 10 | precise true 11 | pour false 12 | category "life" 13 | hidden true 14 | 15 | data score 0 16 | 17 | //===========// 18 | // FUNCTIONS // 19 | //===========// 20 | // Is it the next trail? 21 | input t ({space, args, self, tests}) => { 22 | if (tests.length == 0) tests[0] = ({isTrail}) => isTrail 23 | 24 | if (!space || !space.atom) return true 25 | if (space.atom.element != SnakeTrail) return true 26 | 27 | const trail = space.atom 28 | if (trail.score == self.score + 1) { 29 | args.isTrail = true 30 | args.trail = space.atom 31 | args.trailSpace = space 32 | } 33 | return true 34 | } 35 | 36 | // Place the trail that I ate 37 | output t ({space, self, trail}) => { 38 | SPACE.setAtom(space, trail) 39 | trail.score = self.score 40 | } 41 | 42 | // Place me in the place the trail was 43 | output o ({space, self, trailSpace}) => { 44 | if (space != trailSpace) return 45 | SPACE.setAtom(space, self) 46 | } 47 | 48 | // Create new trail 49 | output T ({space, self}) => { 50 | const trail = ATOM.make(SnakeTrail, {score: self.score}) 51 | SPACE.setAtom(space, trail) 52 | } 53 | 54 | // Am I waiting for my trail to be caught up on? 55 | input * ({space, args, self, tests}) => { 56 | if (tests.length == 0) tests[0] = ({isWaiting}) => isWaiting 57 | 58 | if (!space || !space.atom) return true 59 | if (space.atom.element != SnakeTrail) return true 60 | const trail = space.atom 61 | 62 | if (trail.score == self.score) { 63 | args.isWaiting = true 64 | } 65 | 66 | return true 67 | } 68 | 69 | // Are there higher scoring snakes around me? 70 | input ^ ({space, self, tests, args}) => { 71 | 72 | if (tests.length == 0) tests[0] = ({isObeying}) => isObeying 73 | 74 | if (!space || !space.atom) return true 75 | if (space.atom.element != Snake && space.atom.element != SnakeTrail) return true 76 | 77 | if (space.atom.score == self.score + 1) { 78 | args.isObeying = true 79 | } 80 | return true 81 | } 82 | 83 | // Is it Food? 84 | input R extends # ({space}) => space.atom.element.isFood 85 | 86 | // Make a new leader 87 | output l ({space, self}) => { 88 | const leader = ATOM.make(Snake, {score: self.score + 1}) 89 | self.leader = false 90 | SPACE.setAtom(space, leader) 91 | } 92 | 93 | //=======// 94 | // RULES // 95 | //=======// 96 | // Wait for snakes to catch up 97 | rule XYZ { @* => .. } 98 | 99 | // Move up trail 100 | rule XYZ { @t => to } 101 | 102 | // If there is a higher score snake/trail nearby, do nothing 103 | rule XYZ { @^ => .. } 104 | 105 | // Otherwise, assume I am the leader: 106 | // Eat Res 107 | rule xyz { @R => @l } 108 | 109 | // Move into empty space 110 | rule xyz { @_ => T@ } 111 | 112 | } 113 | 114 | element SnakeTrail { 115 | 116 | colour "blue" 117 | emissive "darkblue" 118 | 119 | // Can I die? 120 | input * ({space, self}) => { 121 | if (!space || !space.atom) return true 122 | if (space.atom.element != Snake && space.atom.element != SnakeTrail) return true 123 | if (space.atom.score != self.score - 1) return true 124 | } 125 | rule XYZ { @* => _. } 126 | 127 | } 128 | 129 | ` -------------------------------------------------------------------------------- /Source/Elements/Old/TodeSplat2/SpaceShip.js: -------------------------------------------------------------------------------- 1 | TodeSplat` 2 | 3 | element SpaceShip { 4 | 5 | colour "pink" 6 | emissive "purple" 7 | precise true 8 | pour false 9 | category "player" 10 | 11 | input r () => Keyboard.ArrowRight 12 | input l () => Keyboard.ArrowLeft 13 | input f () => Keyboard.ArrowUp 14 | input b () => Keyboard.ArrowDown 15 | input d () => Keyboard[" "] 16 | 17 | output S ({space}) => SPACE.setAtom(space, ATOM.make(Sand)) 18 | 19 | action { 20 | @ => @ => @ 21 | d _ S 22 | } 23 | 24 | rule { @r => @_ => _@ } 25 | rule { l@ => _@ => @_ } 26 | 27 | rule top { 28 | b => _ => @ 29 | @ @ _ 30 | } 31 | 32 | rule top { 33 | @ => @ => _ 34 | f _ @ 35 | } 36 | 37 | } 38 | 39 | 40 | 41 | 42 | ` -------------------------------------------------------------------------------- /Source/Elements/Old/TodeSplat3/Explosive.js: -------------------------------------------------------------------------------- 1 | let fireworkColour = 0 2 | 3 | TodeSplat` 4 | 5 | element Spark { 6 | 7 | colour "lightyellow" 8 | emissive "orange" 9 | category "Explosive" 10 | state "effect" 11 | ignites true 12 | floor true 13 | given s (element, self) => element == self.element 14 | keep s 15 | 16 | rule 0.2 { @ => _} 17 | rule xyz { @s => _s } 18 | rule xyz 0.4 { @_ => @@ } 19 | 20 | 21 | } 22 | 23 | element BlueSpark { 24 | 25 | floor true 26 | colour "lightblue" 27 | emissive "lightblue" 28 | category "Explosive" 29 | state "effect" 30 | ignites true 31 | ruleset Spark 32 | 33 | } 34 | 35 | element Explosion { 36 | 37 | floor true 38 | colour "lightyellow" 39 | emissive "orange" 40 | category "Explosive" 41 | state "effect" 42 | ignites true 43 | data timer 20 44 | isHot true 45 | 46 | keep t (self, space) => self.timer-- 47 | given d (self) => self.timer <= 0 48 | change E (self) => new self.element({timer: self.timer}) 49 | 50 | action { @ => t } 51 | rule { @d => _. } 52 | 53 | rule xyz { @. => @E } 54 | 55 | } 56 | 57 | element RedExplosion { 58 | 59 | floor true 60 | colour "red" 61 | emissive "darkred" 62 | category "Explosive" 63 | state "effect" 64 | ignites true 65 | data timer 20 66 | isHot true 67 | 68 | ruleset Explosion 69 | 70 | } 71 | 72 | element BlueExplosion { 73 | 74 | floor true 75 | colour "blue" 76 | emissive "darkblue" 77 | category "Explosive" 78 | state "effect" 79 | ignites true 80 | data timer 20 81 | isHot true 82 | 83 | ruleset Explosion 84 | 85 | } 86 | 87 | element GunPowder { 88 | 89 | colour "grey" 90 | emissive "brown" 91 | category "Explosive" 92 | state "solid" 93 | 94 | given H (element) => element && element.ignites 95 | change E (self) => new Explosion() 96 | 97 | rule xyz { @H => EE } 98 | ruleset Powder 99 | 100 | } 101 | 102 | element Lightning { 103 | colour "yellow" 104 | emissive "orange" 105 | category "Explosive" 106 | state "effect" 107 | ignites true 108 | precise true 109 | pour false 110 | 111 | change S () => new Spark() 112 | action xz 0.3 { @_ => @S } 113 | 114 | rule { 115 | @ => @ 116 | _ @ 117 | } 118 | 119 | given N (space, element) => !space || element != Lightning 120 | keep N 121 | change F () => new LightningFlash() 122 | rule { 123 | @ => F 124 | N N 125 | } 126 | 127 | } 128 | 129 | element LightningFlash { 130 | colour "lightblue" 131 | emissive "lightblue" 132 | hidden true 133 | state "effect" 134 | ignites true 135 | 136 | change S () => new BlueSpark() 137 | action xz 0.5 { @_ => @S } 138 | 139 | change B () => new LightningBang 140 | rule { 141 | _ => _ 142 | @ B 143 | } 144 | } 145 | 146 | element LightningBang { 147 | colour "white" 148 | emissive "white" 149 | hidden true 150 | state "effect" 151 | ignites true 152 | 153 | given F (element) => element == LightningFlash 154 | 155 | rule { 156 | @ => _ 157 | F @ 158 | } 159 | 160 | rule { 161 | @ => _ 162 | } 163 | 164 | } 165 | 166 | element Firework { 167 | 168 | colour "grey" 169 | emissive "black" 170 | precise true 171 | pour false 172 | floor true 173 | category "Explosive" 174 | state "solid" 175 | 176 | data timer 25 177 | 178 | change E (self) => { 179 | if (self.timer > 0) return self 180 | fireworkColour++ 181 | if (fireworkColour >= 3) fireworkColour = 0 182 | if (fireworkColour == 0) return new Explosion() 183 | else if (fireworkColour == 1) return new RedExplosion() 184 | else if (fireworkColour == 2) return new BlueExplosion() 185 | } 186 | 187 | change F (self) => { 188 | self.timer-- 189 | return new Fire() 190 | } 191 | 192 | rule { 193 | _ => E 194 | @ F 195 | } 196 | 197 | } 198 | 199 | element Rocket { 200 | 201 | colour "grey" 202 | emissive "black" 203 | precise true 204 | pour false 205 | category "Explosive" 206 | state "solid" 207 | 208 | ruleset Solid 209 | 210 | given I (element) => element && element.ignites 211 | change F () => new Firework() 212 | rule xyz { @I => F. } 213 | 214 | } 215 | 216 | ` 217 | -------------------------------------------------------------------------------- /Source/Elements/Old/TodeSplat3/Globals.js: -------------------------------------------------------------------------------- 1 | TodeSplat` 2 | 3 | given . (space) => space 4 | keep . 5 | 6 | given @ (self, atom) => self == atom 7 | change @ (self) => self 8 | 9 | given # (atom) => atom 10 | keep # 11 | 12 | given _ (space, atom) => space && !atom 13 | change _ () => undefined 14 | 15 | given x (space) => !space 16 | keep x 17 | 18 | 19 | 20 | ` -------------------------------------------------------------------------------- /Source/Elements/Old/TodeSplat3/Life.js: -------------------------------------------------------------------------------- 1 | TodeSplat` 2 | 3 | element Food { 4 | 5 | colour "brown" 6 | emissive "brown" 7 | category "Life" 8 | isFood true 9 | state "solid" 10 | 11 | ruleset Powder 12 | 13 | } 14 | 15 | element FloatyFood { 16 | 17 | colour "brown" 18 | emissive "brown" 19 | category "Life" 20 | isFood true 21 | 22 | state "solid" 23 | 24 | rule xyz { @_ => _@ } 25 | 26 | } 27 | 28 | element Fly { 29 | 30 | colour "royalblue" 31 | emissive "darkblue" 32 | precise true 33 | pour false 34 | category "Life" 35 | state "solid" 36 | 37 | given F (element) => element && element.isFood 38 | rule 0.0005 { @ => _ } 39 | rule xyz 0.05 { @F => @@ } 40 | rule xyz { @F => @_ } 41 | rule xyz { @_ => _@ } 42 | 43 | } 44 | 45 | element Ant { 46 | 47 | colour "grey" 48 | emissive "black" 49 | precise true 50 | pour false 51 | category "Life" 52 | state "solid" 53 | 54 | given F (element) => element && element.isFood 55 | 56 | rule 0.0005 { @ => _ } 57 | 58 | ruleset Solid 59 | 60 | rule xyz 0.05 { @F => @@ } 61 | rule xyz { @F => _@ } 62 | 63 | rule xz { 64 | _ => @ 65 | @. _. 66 | } 67 | 68 | } 69 | 70 | element MountainMaker { 71 | 72 | colour "lightblue" 73 | emissive "red" 74 | category "Life" 75 | state "effect" 76 | 77 | change S () => new Sand() 78 | 79 | rule 0.4 { 80 | @ => S 81 | } 82 | 83 | rule xyz { 84 | @._ => ..@ 85 | } 86 | 87 | } 88 | 89 | element SandLeaver { 90 | 91 | colour "brown" 92 | emissive "brown" 93 | category "Life" 94 | state "solid" 95 | precise true 96 | pour false 97 | 98 | change F () => new Sand() 99 | rule xyz 0.45 { @_ => @F } 100 | 101 | rule { 102 | @ => _ 103 | _ @ 104 | } 105 | 106 | rule xz { 107 | _ => @ 108 | @ _ 109 | } 110 | 111 | } 112 | 113 | element FoodLeaver { 114 | 115 | colour "yellow" 116 | emissive "orange" 117 | category "Life" 118 | state "solid" 119 | precise true 120 | pour false 121 | 122 | change F () => new Food() 123 | rule xyz 0.45 { @_ => @F } 124 | 125 | rule { 126 | @ => _ 127 | _ @ 128 | } 129 | 130 | rule xz { 131 | _ => @ 132 | @ _ 133 | } 134 | 135 | } 136 | element Cycler { 137 | 138 | colour "grey" 139 | emissive "black" 140 | category "Life" 141 | state "solid" 142 | 143 | rule { 144 | x => x 145 | _@ @_ 146 | } 147 | 148 | rule { 149 | _ @ 150 | @x => _x 151 | } 152 | 153 | rule { 154 | @_ => _@ 155 | x x 156 | } 157 | 158 | rule { 159 | @ => _ 160 | _ @ 161 | } 162 | 163 | } 164 | 165 | element Plant { 166 | colour "green" 167 | category "Life" 168 | state "solid" 169 | 170 | // Burn 171 | given H (element) => element && element.ignites 172 | keep H 173 | change F () => new BurningPlant() 174 | rule xyz { @H => FH } 175 | 176 | // Gravity 177 | ruleset Powder 178 | 179 | // Grow 180 | rule xz 0.05 { @_ => @@ } 181 | 182 | } 183 | 184 | element BurningPlant { 185 | colour "green" 186 | category "Life" 187 | state "solid" 188 | hidden true 189 | ignites true 190 | 191 | rule 0.03 { @ => _ } 192 | 193 | change F () => new Fire() 194 | rule { 195 | _ => F 196 | @ @ 197 | } 198 | ruleset Plant 199 | 200 | } 201 | 202 | element Herbivore { 203 | colour "blue" 204 | emissive "darkblue" 205 | category "Life" 206 | state "solid" 207 | precise true 208 | pour false 209 | 210 | // Die 211 | rule 0.002 { @ => _ } 212 | 213 | // Gravity 214 | ruleset Powder 215 | 216 | // Reproduce 217 | given P (element) => element == Plant || (element && element.isFood) 218 | rule xyz 0.05 { @P => @@ } 219 | 220 | // Eat 221 | rule xyz { @P => @_ } 222 | 223 | // Move 224 | rule xz 0.5 { @_ => _@ } 225 | } 226 | 227 | element Fish { 228 | colour "rgb(255, 100, 0)" 229 | category "Life" 230 | precise true 231 | pour false 232 | state "solid" 233 | 234 | given W (element) => element == Water 235 | select W (atom) => atom 236 | change W (selected) => selected 237 | rule xyz { @W => W@ } 238 | 239 | rule { 240 | @ => . 241 | W . 242 | } 243 | ruleset Solid 244 | 245 | rule 0.05 xz { 246 | _ => @ 247 | 248 | @ _ 249 | } 250 | 251 | } 252 | 253 | element Giraffe { 254 | colour "rgb(128, 128, 0)" 255 | emissive "rgb(255, 128, 0)" 256 | state "solid" 257 | precise true 258 | pour false 259 | category "Life" 260 | 261 | data level 0 262 | 263 | given d (self) => self.level > 0 264 | given d (atom, element, self) => element != Giraffe || !atom || atom.level != self.level - 1 265 | rule { 266 | @ => _ 267 | d . 268 | } 269 | 270 | ruleset Solid 271 | 272 | given e (space, atom) => space && !atom 273 | given e (self) => self.level < 10 274 | change G (self) => new Giraffe({level: self.level + 1}) 275 | action { 276 | e => G 277 | @ . 278 | } 279 | rule xz 0.05 { @_ => _@ } 280 | 281 | 282 | } 283 | 284 | element StretchyGiraffe { 285 | colour "rgb(128, 128, 0)" 286 | emissive "rgb(255, 128, 0)" 287 | state "solid" 288 | precise true 289 | pour false 290 | category "Life" 291 | 292 | data level 0 293 | 294 | given d (self) => self.level == 0 295 | given d (element) => !element || element.state != "solid" 296 | given d (space) => space 297 | select d (atom) => atom 298 | change d (selected) => selected 299 | rule { 300 | @ => d 301 | d @ 302 | } 303 | 304 | given e (space, atom) => space && !atom 305 | given e (self) => self.level < 7 306 | change G (self) => new StretchyGiraffe({level: self.level + 1}) 307 | action { 308 | e => G 309 | @ . 310 | } 311 | 312 | given G (element) => element == StretchyGiraffe 313 | change T (self) => new GiraffeTrail({level: self.level + 1}) 314 | rule xz { 315 | G_ .T 316 | @d => d@ 317 | } 318 | 319 | given T (element, atom, self) => element && element == GiraffeTrail && atom.level == self.level 320 | rule xz { 321 | G_ .T 322 | @T => _@ 323 | } 324 | 325 | rule xz { 326 | _ . 327 | @T => _@ 328 | } 329 | 330 | } 331 | 332 | element GiraffeTrail { 333 | colour "grey" 334 | emissive "black" 335 | hidden true 336 | } 337 | 338 | ` -------------------------------------------------------------------------------- /Source/Elements/Old/TodeSplat3/People.js: -------------------------------------------------------------------------------- 1 | TodeSplat` 2 | 3 | element LukeWilson { 4 | 5 | colour "red" 6 | emissive "darkred" 7 | category "People" 8 | //default true 9 | precise true 10 | pour false 11 | 12 | state "solid" 13 | data id -1 14 | 15 | given i (self) => self.id == -1 16 | keep i (self) => self.id = Math.random() 17 | action { i => i} 18 | 19 | // Hatch when fallen 20 | change B (self) => new LukeBody({id: self.id}) 21 | change L (self) => new LukeLeg({id: self.id}) 22 | change H (self) => new LukeHair({id: self.id}) 23 | change A (self) => new LukeArm({id: self.id}) 24 | change F (self) => new LukeFace({id: self.id}) 25 | 26 | 27 | given H (element, atom, self) => atom && element == LukeHair && atom.id == self.id 28 | given F (element, atom, self) => atom && element == LukeFace && atom.id == self.id 29 | given A (element, atom, self) => atom && element == LukeArm && atom.id == self.id 30 | given L (element, atom, self) => atom && element == LukeLeg && atom.id == self.id 31 | given B (element, atom, self) => atom && element == LukeBody && atom.id == self.id 32 | action { 33 | ___ HHH 34 | ___ => HFH 35 | __@__ AB@BA 36 | _ _ L L 37 | } 38 | 39 | // Unsquish :) 40 | action { 41 | HHH ... 42 | HFH ... 43 | AB@B_ => ....A 44 | L L . . 45 | } 46 | 47 | given l (element, atom, self) => atom && (element != LukeLeg || atom.id != self.id) 48 | rule { 49 | HHH HHH 50 | _HFH_ AB@BA 51 | AB@B => _L_L 52 | L l _ . 53 | } 54 | 55 | rule { 56 | ___ HHH 57 | HHH => .F. 58 | AB@BA ..... 59 | L L . . 60 | } 61 | 62 | // Fall 63 | rule { 64 | HHH ___ 65 | HFH HHH 66 | AB@BA => _HFH_ 67 | _L_L_ AB@BA 68 | _ _ L L 69 | } 70 | 71 | // Walk Right 72 | rule { 73 | HHH_ _HHH 74 | HFH_ _HFH 75 | AB@BA => _AB@B 76 | L_L_ _L_L 77 | } 78 | 79 | // Walk Right up slope 80 | rule { 81 | HHH_ _HHH 82 | HFH_ _HFH 83 | AB@BA => _AB@B 84 | L_L _L_. 85 | } 86 | 87 | 88 | 89 | } 90 | 91 | element LukeArm { 92 | colour "#F2BB4F" 93 | hidden true 94 | state "solid" 95 | } 96 | 97 | element LukeFace { 98 | colour "#F2BB4F" 99 | hidden true 100 | state "solid" 101 | } 102 | 103 | element LukeHair { 104 | colour "#562c2c" 105 | hidden true 106 | state "solid" 107 | } 108 | 109 | element LukeLeg { 110 | colour "grey" 111 | emissive "black" 112 | hidden true 113 | state "solid" 114 | 115 | /*given H (element) => element == LukeHair 116 | given B (element) => element == LukeBody 117 | change B () => new LukeBody() 118 | rule { 119 | H . 120 | B . 121 | @ => B 122 | _ @ 123 | }*/ 124 | } 125 | 126 | element LukeBody { 127 | colour "red" 128 | emissive "darkred" 129 | hidden true 130 | state "solid" 131 | 132 | given B (element) => element == LukeBody 133 | change B () => new LukeBody() 134 | change H () => new LukeHair() 135 | 136 | /*action { @_B => .B. } 137 | action { B_@ => .B. }*/ 138 | 139 | } 140 | 141 | 142 | 143 | ` -------------------------------------------------------------------------------- /Source/Elements/Old/TodeSplat3/Presets.js: -------------------------------------------------------------------------------- 1 | TodeSplat` 2 | 3 | element Static { 4 | colour "grey" 5 | emissive "black" 6 | category "Rulesets" 7 | state "solid" 8 | } 9 | 10 | element Powder { 11 | 12 | colour "grey" 13 | category "Rulesets" 14 | //hidden true 15 | 16 | state "solid" 17 | 18 | given D (space, element) => space && (!element || element.state == "liquid" || element.state == "gloop" || element.state == "gas" || element.state == "effect") 19 | select D (atom) => atom 20 | change D (selected) => selected 21 | 22 | given T (space, element) => space && (!element || element.state == "liquid" || element.state == "gloop" || element.state == "gas" || element.state == "effect") 23 | select T (atom) => atom 24 | change T (selected) => selected 25 | 26 | rule { 27 | @ => D 28 | D @ 29 | } 30 | 31 | rule xz { 32 | @T => TD 33 | #D #@ 34 | } 35 | 36 | } 37 | 38 | element Solid { 39 | colour "grey" 40 | category "Rulesets" 41 | //hidden true 42 | 43 | state "solid" 44 | 45 | given D (space, element) => space && (!element || element.state == "liquid" || element.state == "gloop" || element.state == "gas" || element.state == "effect") 46 | select D (atom) => atom 47 | change D (selected) => selected 48 | 49 | rule { 50 | @ => D 51 | D @ 52 | } 53 | } 54 | 55 | element Liquid { 56 | 57 | colour "grey" 58 | category "Rulesets" 59 | //hidden true 60 | 61 | state "liquid" 62 | 63 | given D (space, element) => space && (!element || element.state == "gas" || element.state == "effect") 64 | select D (atom) => atom 65 | change D (selected) => selected 66 | 67 | given T (space, element) => space && (!element || element.state == "gas" || element.state == "effect") 68 | select T (atom) => atom 69 | change T (selected) => selected 70 | 71 | rule { 72 | @ => D 73 | D @ 74 | } 75 | 76 | rule xz { 77 | @T => TD 78 | #D #@ 79 | } 80 | 81 | rule xz { 82 | @D D@ 83 | # => # 84 | } 85 | 86 | } 87 | 88 | element Gloop { 89 | 90 | colour "grey" 91 | category "Rulesets" 92 | //hidden true 93 | 94 | state "gloop" 95 | 96 | given D (space, element) => space && (!element || element.state == "liquid" || element.state == "gas" || element.state == "effect") 97 | select D (atom) => atom 98 | change D (selected) => selected 99 | 100 | given T (space, element) => space && (!element || element.state == "liquid" || element.state == "gas" || element.state == "effect") 101 | select T (atom) => atom 102 | change T (selected) => selected 103 | 104 | rule { 105 | @ => D 106 | D @ 107 | } 108 | 109 | rule xz 0.1 { 110 | @T => TD 111 | #D #@ 112 | } 113 | 114 | rule xz 0.05 { 115 | @D D@ 116 | # => # 117 | } 118 | 119 | } 120 | 121 | ` -------------------------------------------------------------------------------- /Source/Elements/Old/TodeSplat3/Sandbox.js: -------------------------------------------------------------------------------- 1 | TodeSplat` 2 | 3 | element Sand { 4 | default true 5 | colour "#ffcc00" 6 | emissive "#ffa34d" 7 | category "Sandbox" 8 | 9 | ruleset Powder 10 | 11 | } 12 | 13 | element Water { 14 | 15 | colour "lightblue" 16 | emissive "blue" 17 | opacity 0.5 18 | category "Sandbox" 19 | state "liquid" 20 | 21 | given H (element) => element && element.isHot 22 | keep H 23 | 24 | change S () => new Steam() 25 | 26 | rule xyz { @H => SH } 27 | ruleset Liquid 28 | 29 | } 30 | 31 | element Fire { 32 | colour "orange" 33 | emissive "red" 34 | category "Sandbox" 35 | floor true 36 | ignites true 37 | isHot true 38 | 39 | state "effect" 40 | 41 | rule 0.3 { @ => _ } 42 | rule { 43 | _ => @ 44 | @ _ 45 | } 46 | 47 | rule { 48 | x => x 49 | @ _ 50 | } 51 | 52 | } 53 | 54 | /*element Flame { 55 | colour "orange" 56 | emissive "red" 57 | category "Sandbox" 58 | floor true 59 | ignites true 60 | isHot true 61 | precise true 62 | 63 | state "effect" 64 | 65 | rule 0.03 { @ => _ } 66 | 67 | change F () => new Fire() 68 | rule { 69 | _ => F 70 | @ @ 71 | } 72 | rule xyz { @_ => @F } 73 | 74 | }*/ 75 | 76 | element Lava { 77 | colour "red" 78 | emissive "darkred" 79 | category "Sandbox" 80 | opacity 0.7 81 | ignites true 82 | isHot true 83 | 84 | state "gloop" 85 | 86 | change F () => new Fire() 87 | 88 | action 0.25 { 89 | _ => F 90 | @ @ 91 | } 92 | 93 | ruleset Gloop 94 | 95 | } 96 | 97 | element Slime { 98 | colour "lightgreen" 99 | emissive "green" 100 | category "Sandbox" 101 | opacity 0.7 102 | state "gloop" 103 | ruleset Gloop 104 | } 105 | 106 | element Snow { 107 | colour "white" 108 | emissive "grey" 109 | 110 | category "Sandbox" 111 | state "solid" 112 | 113 | given H (element) => element && element.isHot 114 | keep H 115 | change W () => new Water() 116 | 117 | rule 0.0005 { @ => W } 118 | rule xyz { @H => WH } 119 | 120 | ruleset Powder 121 | 122 | } 123 | 124 | element Steam { 125 | colour "lightgrey" 126 | emissive "darkgrey" 127 | category "Sandbox" 128 | opacity 0.3 129 | floor true 130 | state "gas" 131 | 132 | change W () => new Water() 133 | 134 | rule 0.0002 { @ => W } 135 | 136 | rule { 137 | _ => @ 138 | @ _ 139 | } 140 | 141 | rule xz { @_ => _@ } 142 | 143 | } 144 | 145 | element WallSeed { 146 | colour "rgb(128, 128, 128)" 147 | emissive "rgb(2, 128, 200)" 148 | category "Sandbox" 149 | state "solid" 150 | 151 | data fuel 10 152 | 153 | ruleset Solid 154 | 155 | given W (element) => element == Solid || element == WallSeed 156 | given W (self) => self.fuel == 10 157 | rule { 158 | @ => _ 159 | W . 160 | } 161 | 162 | change W () => new Solid() 163 | change S (self) => { 164 | self.fuel-- 165 | if (self.fuel > 0) return self 166 | } 167 | 168 | rule { 169 | _ => S 170 | @ W 171 | } 172 | 173 | } 174 | 175 | element Platform { 176 | colour "rgb(128, 128, 128)" 177 | emissive "rgb(2, 128, 200)" 178 | category "Sandbox" 179 | state "solid" 180 | data fuel 100 181 | //default true 182 | precise true 183 | pour false 184 | 185 | given f (self) => self.fuel <= 0 186 | rule { f => _ } 187 | 188 | change S () => new Static() 189 | change P (self) => new Platform({fuel: (self.fuel-1)/2}) 190 | rule { _@_ => PSP} 191 | 192 | change B (self) => new Platform({fuel: self.fuel-1}) 193 | rule x { @_ => SB } 194 | 195 | /*rule { #@# => ._. }*/ 196 | 197 | rule x { @x => _. } 198 | } 199 | 200 | element Ball { 201 | colour "grey" 202 | emissive "black" 203 | state "solid" 204 | category "Sandbox" 205 | 206 | data fallSpeed 0 207 | 208 | keep f (self) => { 209 | self.fallSpeed += 0.015 210 | if (self.fallSpeed > 2) self.fallSpeed = 2 211 | } 212 | action { 213 | @ => f 214 | _ . 215 | } 216 | 217 | given s (atom) => atom 218 | select s (atom) => atom 219 | keep s (self, selected) => { 220 | self.fallSpeed += 0.015 221 | if (selected.fallSpeed != undefined) { 222 | if (selected.fallSpeed < self.fallSpeed) { 223 | selected.fallSpeed = self.fallSpeed 224 | } 225 | } 226 | if (self.fallSpeed < 0) self.fallSpeed = 0 227 | } 228 | action { 229 | @ => s 230 | s . 231 | } 232 | 233 | keep c (self) => self.fallSpeed = 0 234 | action { 235 | @ => c 236 | x . 237 | } 238 | 239 | given q (space, atom) => space && !atom 240 | given q (self) => Math.random() < self.fallSpeed / 2 241 | rule { 242 | @ => _ 243 | _ _ 244 | q @ 245 | } 246 | 247 | given e (space, atom) => space && !atom 248 | given e (self) => Math.random() < self.fallSpeed 249 | rule { 250 | @ => _ 251 | e @ 252 | } 253 | 254 | } 255 | 256 | ` 257 | -------------------------------------------------------------------------------- /Source/Elements/Old/TodeSplat3/T2Tile.js: -------------------------------------------------------------------------------- 1 | TodeSplat` 2 | 3 | element ForkBomb { 4 | 5 | colour "grey" 6 | emissive "black" 7 | category "T2Tile" 8 | 9 | rule xyz { @_ => @@ } 10 | 11 | } 12 | 13 | element Res { 14 | 15 | colour "slategrey" 16 | emissive "grey" 17 | opacity 0.3 18 | category "T2Tile" 19 | isFood true 20 | 21 | rule xyz { @_ => _@ } 22 | 23 | } 24 | 25 | element DReg { 26 | 27 | colour "brown" 28 | emissive "brown" 29 | opacity 0.3 30 | category "T2Tile" 31 | 32 | given D (element) => element == DReg 33 | given n (atom, element) => atom && element != DReg 34 | 35 | change R () => new Res() 36 | change D () => new DReg() 37 | 38 | rule xyz 0.001 { @_ => D@ } 39 | rule xyz 0.005 { @_ => R@ } 40 | rule xyz 0.1 { @D => _@ } 41 | rule xyz 0.01 { @n => _@ } 42 | rule xyz { @_ => _@ } 43 | 44 | } 45 | 46 | `; 47 | -------------------------------------------------------------------------------- /Source/Elements/Old/TodeSplat3/Tutorial.js: -------------------------------------------------------------------------------- 1 | TodeSplat` 2 | 3 | element SandDemo { 4 | 5 | colour "#ffcc00" 6 | emissive "#ffa34d" 7 | category "Tutorial" 8 | 9 | given D (space) => space 10 | given D (element) => element == WaterDemo || element == SlimeDemo || element == undefined 11 | select D (atom) => atom 12 | 13 | change D (selected) => selected 14 | 15 | rule { 16 | @ => D 17 | D @ 18 | } 19 | 20 | rule xz { 21 | @ => D 22 | #D #@ 23 | } 24 | 25 | } 26 | 27 | element WaterDemo { 28 | 29 | colour "lightblue" 30 | emissive "blue" 31 | opacity 0.5 32 | category "Tutorial" 33 | 34 | rule { 35 | @ => _ 36 | _ @ 37 | } 38 | 39 | rule xz { 40 | @_ => _@ 41 | # # 42 | } 43 | 44 | } 45 | 46 | element SlimeDemo { 47 | 48 | colour "lightgreen" 49 | emissive "green" 50 | opacity 0.7 51 | category "Tutorial" 52 | 53 | rule { 54 | @ => _ 55 | _ @ 56 | } 57 | 58 | rule xz 0.1 { 59 | @_ => _@ 60 | # # 61 | } 62 | 63 | } 64 | 65 | element SnowDemo { 66 | 67 | colour "white" 68 | emissive "grey" 69 | category "Tutorial" 70 | 71 | change W () => new WaterDemo() 72 | rule 0.0001 { @ => W } 73 | ruleset SandDemo 74 | 75 | } 76 | 77 | element PlantDemo { 78 | colour "green" 79 | category "Tutorial" 80 | 81 | // Gravity 82 | ruleset SandDemo 83 | 84 | // Grow 85 | rule xz 0.05 { @_ => @@ } 86 | 87 | } 88 | 89 | element HerbivoreDemo { 90 | colour "blue" 91 | emissive "darkblue" 92 | category "Tutorial" 93 | 94 | // Die 95 | rule 0.002 { @ => _ } 96 | 97 | // Gravity 98 | ruleset SandDemo 99 | 100 | // Reproduce 101 | given P (element) => element == PlantDemo 102 | rule xyz 0.05 { @P => @@ } 103 | 104 | // Eat 105 | rule xyz { @P => @_ } 106 | 107 | // Move 108 | rule xz 0.5 { @_ => _@ } 109 | } 110 | 111 | element LavaDemo { 112 | colour "red" 113 | emissive "darkred" 114 | category "Tutorial" 115 | opacity 0.7 116 | 117 | change F () => new FireDemo() 118 | action { 119 | _ => F 120 | @ @ 121 | } 122 | 123 | ruleset SlimeDemo 124 | } 125 | 126 | element FireDemo { 127 | colour "orange" 128 | emissive "red" 129 | category "Tutorial" 130 | opacity 0.5 131 | floor true 132 | 133 | rule 0.3 { @ => _ } 134 | 135 | rule { 136 | _ => @ 137 | @ _ 138 | } 139 | 140 | rule { @ => _ } 141 | 142 | } 143 | 144 | element EggDemo { 145 | colour "pink" 146 | emissive "grey" 147 | category "Tutorial" 148 | precise true 149 | pour false 150 | 151 | data timer 100 152 | 153 | // Countdown 154 | keep t (self) => self.timer-- 155 | action { @ => t } 156 | 157 | // Hatch 158 | given h (self) => self.timer < 0 159 | change H () => new HerbivoreDemo() 160 | rule { h => H } 161 | 162 | // Fall 163 | rule { 164 | @ => _ 165 | _ @ 166 | } 167 | } 168 | 169 | element WindySandDemo { 170 | colour SandDemo.colour 171 | emissive SandDemo.emissive 172 | category "Tutorial" 173 | 174 | rule 0.3 { _@ => @_ } 175 | rule 0.2 { 176 | _ => @ 177 | #@ #_ 178 | } 179 | 180 | ruleset SandDemo 181 | } 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | ` -------------------------------------------------------------------------------- /Source/Elements/Old/TodeSplat3/Weather.js: -------------------------------------------------------------------------------- 1 | TodeSplat` 2 | 3 | element WeatherBoy { 4 | colour "blue" 5 | emissive "darkblue" 6 | 7 | state "effect" 8 | 9 | isWeatherBoy true 10 | chance 0.01 11 | 12 | // init 13 | given i (self) => !self.initialised 14 | change I (self) => { 15 | self.birthday = new Date().getTime() 16 | self.initialised = true 17 | return self 18 | } 19 | rule { @i => I. } 20 | 21 | given d (space, atom) => space && !atom 22 | given d (self) => Math.random() < self.element.chance 23 | change D (self) => self.element.drop? new self.element.drop() : undefined 24 | rule { 25 | @ => @ 26 | d D 27 | } 28 | 29 | rule { 30 | _ => @ 31 | @ _ 32 | } 33 | 34 | given W (atom) => atom && atom.initialised 35 | given W (element) => element && element.isWeatherBoy 36 | given W (self, atom) => atom && self.birthday >= atom.birthday 37 | change W (self) => { 38 | const atom = new self.element() 39 | atom.birthday = self.birthday 40 | atom.initialised = true 41 | return atom 42 | } 43 | rule xz 0.2 { @_ => W@ } 44 | rule xz { @W => _@ } 45 | rule xz { @_ => _@ } 46 | } 47 | 48 | element Rainy { 49 | colour "lightblue" 50 | emissive "darkblue" 51 | opacity 0.0 52 | category "Weather" 53 | state "effect" 54 | 55 | isWeatherBoy true 56 | chance 0.01 57 | drop Water 58 | 59 | ruleset WeatherBoy 60 | } 61 | 62 | element Sandstorm { 63 | colour "#ffcc00" 64 | emissive "darkblue" 65 | opacity 0.0 66 | category "Weather" 67 | state "effect" 68 | 69 | isWeatherBoy true 70 | chance 0.01 71 | drop Sand 72 | 73 | ruleset WeatherBoy 74 | } 75 | 76 | element Snowstorm { 77 | colour "white" 78 | emissive "darkblue" 79 | opacity 0.0 80 | category "Weather" 81 | state "effect" 82 | 83 | isWeatherBoy true 84 | chance 0.01 85 | drop Snow 86 | 87 | ruleset WeatherBoy 88 | } 89 | 90 | element Sunny { 91 | colour "orange" 92 | emissive "darkblue" 93 | category "Weather" 94 | opacity 0.0 95 | state "effect" 96 | 97 | isWeatherBoy true 98 | chance 0.01 99 | drop undefined 100 | 101 | ruleset WeatherBoy 102 | } 103 | 104 | ` -------------------------------------------------------------------------------- /Source/Elements/Sandbox.js: -------------------------------------------------------------------------------- 1 | SpaceTode` 2 | 3 | element Sand { 4 | colour "#ffcc00" 5 | emissive "#ffa34d" 6 | category "Sandbox" 7 | prop state SOLID 8 | prop temperature ROOM 9 | prop states () => ({ 10 | [HOT]: Glass, 11 | }) 12 | 13 | mimic(Temperature) 14 | mimic(Powder) 15 | } 16 | 17 | /*element Salt { 18 | default true 19 | colour "white" 20 | emissive "grey" 21 | category "Sandbox" 22 | prop state SOLID 23 | prop temperature ROOM 24 | prop states () => ({ 25 | [HOT]: Glass, 26 | }) 27 | 28 | mimic(Temperature) 29 | mimic(Powder) 30 | }*/ 31 | 32 | element Water { 33 | colour "lightblue" 34 | emissive "blue" 35 | opacity 0.35 36 | category "Sandbox" 37 | prop state LIQUID 38 | prop temperature COOL 39 | prop states () => ({ 40 | [COLD]: [Ice, 0.05], 41 | [CHILLY]: [Snow, 0.05], 42 | [WARM]: [Steam, 0.4], 43 | [HOT]: Steam, 44 | }) 45 | 46 | mimic(Temperature) 47 | mimic(Liquid) 48 | } 49 | 50 | element Ice { 51 | colour "lightblue" 52 | emissive "#87c5c7" 53 | category "Sandbox" 54 | opacity 0.4 55 | prop state SOLID 56 | prop temperature COLD 57 | prop states () => ({ 58 | [COOL]: [Water, 0.0001], 59 | [ROOM]: [Water, 0.0001], 60 | [BODY]: [Water, 0.01], 61 | [WARM]: Water, 62 | [HOT]: Water, 63 | }) 64 | 65 | mimic(Temperature) 66 | mimic(Sticky) 67 | mimic(Powder) 68 | } 69 | 70 | element Snow { 71 | colour "white" 72 | emissive "grey" 73 | category "Sandbox" 74 | prop state SOLID 75 | prop temperature CHILLY 76 | prop states () => ({ 77 | [COOL]: [Water, 0.04], 78 | [ROOM]: [Water, 0.00005], 79 | [BODY]: [Water, 0.01], 80 | [WARM]: Water, 81 | [HOT]: Water, 82 | }) 83 | 84 | mimic(Temperature) 85 | mimic(Powder) 86 | } 87 | 88 | element Steam { 89 | colour "lightgrey" 90 | emissive "darkgrey" 91 | category "Sandbox" 92 | opacity 0.2 93 | prop state GAS 94 | prop temperature WARM 95 | prop states () => ({ 96 | [COLD]: Water, 97 | [CHILLY]: Water, 98 | [COOL]: [Water, 0.1], 99 | [ROOM]: [Empty, 0.1], 100 | }) 101 | 102 | mimic(Temperature) 103 | 104 | given D (element, selfElement) => element !== selfElement && element.state >= GAS 105 | select D (atom) => atom 106 | change D (selected) => selected 107 | D => @ 108 | @ D 109 | 110 | mimic(Gas) 111 | } 112 | 113 | element Stone { 114 | category "Sandbox" 115 | prop state SOLID 116 | prop temperature ROOM 117 | prop states () => ({ 118 | [HOT]: Magma, 119 | }) 120 | mimic(Temperature) 121 | mimic(Solid) 122 | } 123 | 124 | element Rock { 125 | colour "grey" 126 | emissive "black" 127 | prop state SOLID 128 | prop temperature ROOM 129 | prop stickiness 1.0 130 | data stuck false 131 | category "Sandbox" 132 | mimic(Sticky) 133 | mimic(Solid) 134 | } 135 | 136 | element Fire { 137 | colour "darkorange" 138 | emissive "red" 139 | category "Sandbox" 140 | opacity 0.25 141 | prop state EFFECT 142 | prop temperature HOT 143 | prop states () => ({ 144 | [COLD]: Empty, 145 | [CHILLY]: Empty, 146 | [COOL]: Empty, 147 | [ROOM]: [Empty, 0.3], 148 | [BODY]: [Empty, 0.1], 149 | }) 150 | 151 | mimic(Temperature) 152 | 153 | given D (element, selfElement) => element !== selfElement && element.state >= GAS 154 | select D (atom) => atom 155 | change D (selected) => selected 156 | D => @ 157 | @ D 158 | 159 | given n (element, selfElement) => element !== selfElement 160 | n . 161 | @ => _ 162 | 163 | } 164 | 165 | element Magma { 166 | colour "orange" 167 | emissive "brown" 168 | category "Sandbox" 169 | prop state SOLID 170 | prop temperature HOT 171 | prop states () => ({ 172 | [COLD]: Rock, 173 | [CHILLY]: Rock, 174 | [COOL]: Rock, 175 | [ROOM]: [Rock, 0.075], 176 | [BODY]: [Rock, 0.065], 177 | [WARM]: [Rock, 0.05], 178 | }) 179 | 180 | mimic(Temperature) 181 | mimic(Goo) 182 | } 183 | 184 | element Lava { 185 | colour "red" 186 | emissive "darkred" 187 | category "Sandbox" 188 | opacity 0.5 189 | prop state SOLID 190 | prop temperature HOT 191 | prop states () => ({ 192 | [COOL]: [Rock, 0.05], 193 | [COLD]: [Rock, 0.1], 194 | }) 195 | 196 | mimic(Temperature) 197 | 198 | change F () => new Fire() 199 | action { 200 | _ => F 201 | @ . 202 | } 203 | 204 | mimic(Goo) 205 | 206 | } 207 | 208 | element Laser { 209 | colour "red" 210 | opacity 0.2 211 | prop state EFFECT 212 | prop temperature HOT 213 | category "Sandbox" 214 | 215 | symbol L Laser 216 | symbol G Glass 217 | given n (element, selfElement) => (element.state === undefined || element.state < GAS) && element !== Glass && element !== Void 218 | @ => _ 219 | n @ 220 | n L 221 | 222 | @ => _ 223 | n @ 224 | 225 | @ => _ 226 | _ @ 227 | 228 | @ => _ 229 | G . 230 | . L 231 | 232 | @ => _ 233 | } 234 | 235 | element Slime { 236 | colour "lightgreen" 237 | emissive "green" 238 | category "Sandbox" 239 | opacity 0.65 240 | prop state SOLID 241 | prop temperature BODY 242 | prop states () => ({ 243 | [HOT]: Acid, 244 | }) 245 | 246 | mimic(Temperature) 247 | mimic(Goo) 248 | } 249 | 250 | element Acid { 251 | colour "lightgreen" 252 | emissive "green" 253 | opacity 0.35 254 | category "Sandbox" 255 | prop state LIQUID 256 | prop temperature ROOM 257 | prop states () => ({ 258 | [COLD]: Slime, 259 | [CHILLY]: Slime, 260 | }) 261 | 262 | mimic(Temperature) 263 | 264 | symbol S Steam 265 | given n (element, selfElement) => (element.state === undefined || element.state < GAS) && element !== Void && element !== Empty && element !== selfElement && element !== Slime && element !== Glass 266 | n => S 267 | @ _ 268 | 269 | @ => S 270 | n _ 271 | 272 | @ => _ 273 | _ @ 274 | 275 | any(xz.rotations) { 276 | @n => _S 277 | 278 | @ => . 279 | x . 280 | 281 | @_ => _@ 282 | } 283 | } 284 | 285 | element Cloud any(xz.rotations) { 286 | 287 | category "Structure" 288 | arg rain Water 289 | arg chance 1/100 290 | arg birthday 291 | opacity 0.35 292 | prop state GAS 293 | prop temperature ROOM 294 | 295 | given i (self) => self.birthday === undefined 296 | keep i (self, time) => self.birthday = time 297 | i => i 298 | 299 | given r (element, self) => element === Empty && (Math.random() < self.chance) 300 | change R (self) => new self.rain() 301 | @ => . 302 | r R 303 | 304 | _ => @ 305 | @ _ 306 | 307 | change W (selfElement, self) => new selfElement(self.rain, self.chance, self.birthday) 308 | maybe(1/5) @_ => W@ 309 | 310 | given W (element, selfElement, atom, self) => element === selfElement && self.birthday >= atom.birthday 311 | @W => _@ 312 | @_ => _@ 313 | 314 | } 315 | 316 | element Static { 317 | prop state SOLID 318 | prop temperature ROOM 319 | category "Structure" 320 | } 321 | 322 | element Wall { 323 | colour "rgb(128, 128, 128)" 324 | emissive "rgb(2, 128, 200)" 325 | category "Structure" 326 | prop state SOLID 327 | prop temperature ROOM 328 | arg fuel 10 329 | 330 | mimic(Solid) 331 | 332 | given W (element, selfElement) => element === selfElement 333 | @ => _ 334 | W . 335 | 336 | symbol S Stone 337 | @ => _ 338 | S . 339 | 340 | given B (element) => element === Wall.Builder 341 | change B (self, element) => new Wall.Builder(self.fuel) 342 | @ => _ 343 | B . 344 | 345 | @ => B 346 | 347 | @ => _ 348 | 349 | element Builder { 350 | colour "pink" 351 | emissive "red" 352 | prop state SOLID 353 | prop temperature ROOM 354 | arg fuel 10 355 | 356 | change G (self) => { 357 | self.fuel-- 358 | if (self.fuel < 0) return new Empty() 359 | return self 360 | } 361 | _ => G 362 | @ S 363 | 364 | x => . 365 | @ _ 366 | } 367 | 368 | } 369 | 370 | element Cardboard { 371 | colour "brown" 372 | //default true 373 | category "Structure" 374 | prop state SOLID 375 | prop temperature ROOM 376 | arg id 377 | arg ready false 378 | 379 | given S (element) => element.state > SOLID 380 | select S (atom) => atom 381 | change S (selected) => selected 382 | 383 | given i (self) => self.id === undefined 384 | keep i (self) => self.id = Cardboard.id 385 | action i => i 386 | 387 | given R (self) => !self.ready && !Mouse.down 388 | keep R (self) => self.ready = true 389 | action R => R 390 | 391 | 392 | given r (self) => !self.ready 393 | r => . 394 | 395 | for(xz.rotations) { 396 | $ . 397 | @ => . 398 | } 399 | 400 | any(xz.rotations) { 401 | @ => S 402 | S @ 403 | } 404 | } 405 | 406 | element Glass { 407 | opacity 0.1 408 | prop state SOLID 409 | prop temperature ROOM 410 | prop stickiness 1.0 411 | data stuck false 412 | category "Sandbox" 413 | mimic(Sticky) 414 | mimic(Solid) 415 | 416 | symbol G new Glass 417 | 418 | @ => @ 419 | _ G 420 | G G 421 | 422 | } 423 | 424 | element Charcoal { 425 | colour "#2e362d" 426 | emissive "#2e362d" 427 | category "Sandbox" 428 | prop state SOLID 429 | prop temperature ROOM 430 | prop states () => ({ 431 | [HOT]: [FlamingCharcoal, 0.05], 432 | }) 433 | 434 | mimic(Temperature) 435 | mimic(Solid) 436 | } 437 | 438 | element FlamingCharcoal { 439 | colour "#2e362d" 440 | emissive "#2e362d" 441 | //category "Sandbox" 442 | prop state SOLID 443 | prop temperature HOT 444 | prop states () => ({ 445 | [ROOM]: [Ash, 0.003], 446 | [COOL]: [Ash, 0.005], 447 | [COLD]: [Ash, 0.01], 448 | }) 449 | 450 | mimic(Temperature) 451 | 452 | change F () => new Fire() 453 | action { 454 | _ => F 455 | @ . 456 | } 457 | 458 | mimic(Solid) 459 | } 460 | 461 | element Ash { 462 | colour "black" 463 | emissive "black" 464 | category "Sandbox" 465 | prop state SOLID 466 | prop temperature WARM 467 | 468 | mimic(Temperature) 469 | mimic(Powder) 470 | } 471 | 472 | element Crystal { 473 | colour "pink" 474 | emissive "purple" 475 | //category "Sandbox" 476 | opacity 0.3 477 | 478 | data fuel 479 | 480 | given n (element, Self) => element !== Self 481 | 482 | given D (element) => element.state > SOLID && element.state !== EFFECT 483 | select D (atom) => atom 484 | change D (selected) => selected 485 | 486 | 487 | given g (element) => element.state <= SOLID 488 | 489 | 490 | 491 | $ => . 492 | @ . 493 | g . 494 | 495 | all(xz.directions) { 496 | $ => . 497 | @ . 498 | g . 499 | } 500 | 501 | maybe(0.3) any(xz.directions) { 502 | _@_ => $.$ 503 | } 504 | 505 | maybe(1/20) { 506 | _ => $ 507 | @ . 508 | } 509 | 510 | /*all(xz.directions) { 511 | @ => . 512 | $ . 513 | 514 | @$ => .. 515 | $ . 516 | }*/ 517 | 518 | /*@ => ? 519 | ? @*/ 520 | 521 | } 522 | 523 | 524 | 525 | ` 526 | 527 | on.mousedown(e => { 528 | if (UI.selectedElement === Cardboard) { 529 | if (e.buttons !== 1) return 530 | Cardboard.id = Math.random() 531 | } 532 | }) 533 | -------------------------------------------------------------------------------- /Source/Elements/Smell.js: -------------------------------------------------------------------------------- 1 | // Smell Flags 2 | const LOVE = Flag(1) 3 | const STINK = Flag(2) 4 | 5 | SpaceTode` 6 | 7 | element Sniffer { 8 | given P (element, self, atom, Self) => Flag.has(element.smell, Self.pheromone) && (atom.owner !== self) 9 | select P (atom) => atom.target 10 | keep P (self, selected) => { 11 | if (selected === undefined) return 12 | self.target = [...selected] 13 | self.interest = 1.0 14 | } 15 | action for(xz.directions) @P => P. 16 | 17 | given I (self) => self.interest > 0 18 | keep I (self) => self.interest -= 0.1 19 | action I => I 20 | } 21 | 22 | element Smell { 23 | prop state GAS 24 | prop temperature ROOM 25 | opacity 0.3 26 | 27 | given i (self) => self.target === undefined 28 | keep i (self) => { 29 | self.target = [0, 0, 0] 30 | if (self.strength === undefined) self.strength = 0.99 31 | } 32 | i => i 33 | 34 | given S (self) => Math.random() > self.strength 35 | S => _ 36 | 37 | given F (element, Self) => element.state >= GAS && element !== Self 38 | select F (atom) => atom 39 | change F (selected, self) => { 40 | self.target[1] -= 1 41 | return selected 42 | } 43 | @ => F 44 | F @ 45 | 46 | change M (self, x, y, z) => { 47 | self.target[0] += x 48 | self.target[1] += y 49 | self.target[2] += z 50 | return self 51 | } 52 | 53 | given D (element, Self) => element.state >= GAS && element !== Self 54 | select D (atom) => atom 55 | change D (selected) => selected 56 | for(xyz.directions) @D => DM 57 | 58 | } 59 | 60 | element Pheromone { 61 | category "Life" 62 | prop state GAS 63 | prop temperature ROOM 64 | prop smell LOVE 65 | arg strength 0.99 66 | arg owner undefined 67 | colour "pink" 68 | emissive "rgb(255, 64, 128)" 69 | opacity 0.3 70 | mimic(Smell) 71 | } 72 | 73 | ` 74 | -------------------------------------------------------------------------------- /Source/Elements/State.js: -------------------------------------------------------------------------------- 1 | 2 | const EFFECT = 0 3 | const VOID = 1 4 | const SOLID = 2 5 | const LIQUID = 3 6 | const GAS = 4 7 | 8 | Empty.state = GAS 9 | Void.state = VOID 10 | 11 | SpaceTode` 12 | 13 | element Solid { 14 | prop state SOLID 15 | //category "Rulesets" 16 | 17 | given D (element) => element.state > SOLID && element.state !== EFFECT 18 | select D (atom) => atom 19 | change D (selected) => selected 20 | @ => D 21 | D @ 22 | } 23 | 24 | element Powder { 25 | prop state SOLID 26 | //category "Rulesets" 27 | 28 | given D (element) => element.state > SOLID && element.state !== EFFECT 29 | select D (atom) => atom 30 | change D (selected) => selected 31 | @ => D 32 | D @ 33 | 34 | given S (element) => element.state > SOLID && element.state !== EFFECT 35 | select S (atom) => atom 36 | change S (selected) => selected 37 | given F (element) => element.state > SOLID 38 | any(xz.rotations) { 39 | @S => S@ 40 | F . 41 | } 42 | } 43 | 44 | element Liquid { 45 | prop state LIQUID 46 | //category "Rulesets" 47 | 48 | given D (element) => element.state > LIQUID && element.state !== EFFECT 49 | select D (atom) => atom 50 | change D (selected) => selected 51 | @ => D 52 | D @ 53 | 54 | @ => . 55 | x . 56 | 57 | given S (element) => element.state > SOLID && element.state !== EFFECT 58 | select S (atom) => atom 59 | change S (selected) => selected 60 | for(xz.rotations) @S => S@ 61 | } 62 | 63 | element Goo { 64 | prop state SOLID 65 | //category "Rulesets" 66 | 67 | given D (element) => element.state > SOLID 68 | select D (atom) => atom 69 | change D (selected) => selected 70 | 71 | given F (element) => element.state > SOLID 72 | 73 | @ => D 74 | D @ 75 | 76 | @ => . 77 | x . 78 | 79 | any(xz.rotations) { 80 | 81 | maybe(1/20) { 82 | @D => D@ 83 | F . 84 | } 85 | 86 | maybe(1/30) @D => D@ 87 | } 88 | } 89 | 90 | element Gas { 91 | prop state GAS 92 | //category "Rulesets" 93 | 94 | given D (element) => element.state >= GAS && element.state !== EFFECT 95 | select D (atom) => atom 96 | change D (selected) => selected 97 | any(xyz.directions) @D => D@ 98 | } 99 | 100 | element Sticky { 101 | prop state SOLID 102 | //category "Rulesets" 103 | 104 | // Init atom properties 105 | given i (self) => self.stickyInit === undefined 106 | keep i (self, time, Self) => { 107 | self.stickyInit = true 108 | self.stuckTime = -Infinity 109 | self.stuck = false 110 | if (Self.stickiness === undefined) Self.stickiness = 1.0 111 | if (Self.stickinessNormalised === undefined) Self.stickinessNormalised = Math.floor(Self.stickiness * 30) 112 | } 113 | action i => i 114 | 115 | // Debug colour 116 | /*keep c (self, time, origin, Self) => { 117 | let timeDiff = Math.round((time - self.stuckTime) * 255 / Self.stickinessNormalised) 118 | if (timeDiff > 255) timeDiff = 255 119 | self.colour.r = timeDiff 120 | self.colour.b = 255 - timeDiff 121 | self.colour.g = 255 - timeDiff 122 | SPACE.updateAppearance(origin) 123 | } 124 | action @ => c*/ 125 | 126 | // Contact with ground 127 | given n (element, selfElement, atom) => element !== selfElement && element.state <= SOLID && atom.stuck !== false 128 | keep t (self, time) => self.stuckTime = time 129 | action { 130 | @ => t 131 | n . 132 | } 133 | 134 | // Spread + Receive signal 135 | given s (selfElement, element, self, atom) => element === selfElement && self.stuckTime > atom.stuckTime 136 | keep s (self, atom) => atom.stuckTime = self.stuckTime 137 | given r (selfElement, element, self, atom) => element === selfElement && self.stuckTime < atom.stuckTime 138 | keep r (self, atom) => self.stuckTime = atom.stuckTime 139 | all(xyz.directions) { 140 | action @s => .s 141 | action @r => .r 142 | } 143 | 144 | /*all(xyz.shifts) { 145 | action { 146 | s => s 147 | @ . 148 | } 149 | action { 150 | r => r 151 | @ . 152 | } 153 | }*/ 154 | 155 | // Stuck! 156 | given S (self, time, Self) => { 157 | const stuck = time - self.stuckTime < Self.stickinessNormalised 158 | self.stuck = stuck 159 | return stuck 160 | } 161 | S => . 162 | 163 | /*given D (element) => element.state > SOLID 164 | select D (atom) => atom 165 | change D (selected) => selected 166 | // Fall 167 | @ => D 168 | D @*/ 169 | 170 | } 171 | 172 | ` -------------------------------------------------------------------------------- /Source/Elements/T2Tile.js: -------------------------------------------------------------------------------- 1 | SpaceTode` 2 | 3 | element Forkbomb any(xyz.directions) { 4 | colour "grey" 5 | emissive "black" 6 | category "T2Tile" 7 | keep F (space) => SPACE.setAtom(space, new Forkbomb(), Forkbomb) 8 | 9 | @_ => .F 10 | } 11 | 12 | element Res any(xyz.directions) { 13 | category "T2Tile" 14 | colour "slategrey" 15 | emissive "grey" 16 | opacity 0.2 17 | @_ => _@ 18 | } 19 | 20 | element DReg any(xyz.directions) { 21 | 22 | colour "brown" 23 | emissive "brown" 24 | opacity 0.2 25 | category "T2Tile" 26 | 27 | symbol D DReg 28 | symbol R Res 29 | given n (element) => element !== DReg && element !== Void 30 | 31 | maybe(1/1000) @_ => D@ 32 | maybe(1/200) @_ => R@ 33 | maybe(1/10) @D => _@ 34 | maybe(1/100) @n => _@ 35 | @_ => _@ 36 | 37 | } 38 | 39 | element SwapLine { 40 | colour "brown" 41 | category "T2Tile" 42 | 43 | symbol L SwapLine 44 | symbol D SwapLine.Done 45 | 46 | for(y) { 47 | _ => L 48 | @ . 49 | 50 | x => . 51 | @ D 52 | 53 | D => . 54 | @ D 55 | } 56 | 57 | given O (element) => element !== SwapLine 58 | for(y) { 59 | O => . 60 | @ D 61 | } 62 | 63 | element Done { 64 | colour "grey" 65 | emissive "black" 66 | 67 | 68 | for(y) { 69 | D . 70 | @ => . 71 | 72 | L . 73 | @ => . 74 | 75 | L . 76 | @ => . 77 | } 78 | 79 | @D => _@ 80 | @L => _@ 81 | @x => _. 82 | @? => ?@ 83 | } 84 | 85 | } 86 | 87 | element ThickSwapLine { 88 | colour "brown" 89 | category "T2Tile" 90 | 91 | symbol L ThickSwapLine 92 | symbol F ThickSwapLine.Front 93 | symbol B ThickSwapLine.Back 94 | 95 | given l (element) => element !== ThickSwapLine 96 | given f (element) => element !== ThickSwapLine.Front 97 | given b (element) => element !== ThickSwapLine.Back 98 | 99 | given N (element) => element === ThickSwapLine || element === ThickSwapLine.Front || element === ThickSwapLine.Back 100 | given n (element) => element !== ThickSwapLine && element !== ThickSwapLine.Front && element !== ThickSwapLine.Back 101 | 102 | ln .. 103 | @_n => .F. 104 | ln .. 105 | 106 | for(y) { 107 | nnnn .... 108 | __n => $F. 109 | @F .. 110 | } 111 | 112 | 113 | for(y) { 114 | 115 | xxf => ... 116 | @Ff B.. 117 | f . 118 | 119 | BFf => ... 120 | @Ff B.. 121 | f . 122 | 123 | N . 124 | nnf => ... 125 | @Ff B.. 126 | f . 127 | } 128 | 129 | 130 | element Back { 131 | colour "green" 132 | emissive "lightgreen" 133 | 134 | @f => _. 135 | 136 | for(y) { 137 | @ => _ 138 | F . 139 | } 140 | 141 | } 142 | 143 | element Front { 144 | colour "grey" 145 | emissive "black" 146 | 147 | for(y) { 148 | L => . 149 | @ _ 150 | } 151 | 152 | n@ => ._ 153 | 154 | B@x => __. 155 | 156 | @B => _. 157 | 158 | for(y) { 159 | B => . 160 | @ _ 161 | } 162 | 163 | for(y) { 164 | 165 | BF => .. 166 | @ . 167 | 168 | LF .. 169 | @ => . 170 | } 171 | 172 | n . 173 | B@_n => _B@. 174 | n . 175 | 176 | 177 | } 178 | 179 | } 180 | 181 | element SwapWall { 182 | colour "brown" 183 | category "T2Tile" 184 | arg timer 0 185 | 186 | given W (element) => element === SwapWall 187 | change W (self) => new SwapWall(self.timer) 188 | change w (self) => new SwapWall(self.timer + 1) 189 | 190 | given D (element) => element === SwapWall.Done 191 | change D (self) => new SwapWall.Done(self.timer) 192 | 193 | given E (element) => element === SwapWall.Egg 194 | change E (self) => new SwapWall.Egg(self.timer) 195 | 196 | for(yz.directions) { 197 | _ => w 198 | @ . 199 | } 200 | 201 | for(yz.directions) { 202 | x => . 203 | @ E 204 | 205 | E => . 206 | @ E 207 | 208 | D => . 209 | @ E 210 | } 211 | 212 | given O (element) => element !== SwapWall 213 | for(yz.directions) { 214 | O => . 215 | @ D 216 | } 217 | 218 | element Egg { 219 | colour "lightgreen" 220 | emissive "green" 221 | arg timer 0 222 | 223 | given t (self) => self.timer-- > 0 224 | t => . 225 | 226 | for(yz.directions) { 227 | W . 228 | @ => . 229 | } 230 | 231 | @ => D 232 | } 233 | 234 | element Done { 235 | colour "grey" 236 | emissive "black" 237 | arg timer 0 238 | 239 | for(yz) { 240 | D . 241 | @ => . 242 | 243 | W . 244 | @ => . 245 | 246 | E . 247 | @ => . 248 | 249 | W . 250 | @ => . 251 | 252 | E . 253 | @ => . 254 | } 255 | 256 | @D => _@ 257 | @W => _@ 258 | @x => _. 259 | @? => ?@ 260 | } 261 | 262 | } 263 | 264 | element SwapWallUp { 265 | colour "brown" 266 | category "T2Tile" 267 | arg timer 0 268 | 269 | given W (element) => element === SwapWallUp 270 | change W (self) => new SwapWallUp(self.timer) 271 | change w (self) => new SwapWallUp(self.timer + 1) 272 | 273 | given D (element) => element === SwapWallUp.Done 274 | change D (self) => new SwapWallUp.Done(self.timer) 275 | 276 | given E (element) => element === SwapWallUp.Egg 277 | change E (self) => new SwapWallUp.Egg(self.timer) 278 | 279 | pov(top) { 280 | 281 | for(xz.directions) { 282 | _ => w 283 | @ . 284 | } 285 | 286 | for(xz.directions) { 287 | x => . 288 | @ E 289 | 290 | E => . 291 | @ E 292 | 293 | D => . 294 | @ E 295 | } 296 | 297 | given O (element) => element !== SwapWallUp 298 | for(xz.directions) { 299 | O => . 300 | @ D 301 | } 302 | } 303 | 304 | element Egg pov(top) { 305 | colour "lightgreen" 306 | emissive "green" 307 | arg timer 0 308 | 309 | given t (self) => self.timer-- > 0 310 | t => . 311 | 312 | for(xz.directions) { 313 | W . 314 | @ => . 315 | } 316 | 317 | @ => D 318 | } 319 | 320 | element Done { 321 | colour "grey" 322 | emissive "black" 323 | arg timer 0 324 | 325 | for(xz) { 326 | @ => . 327 | D . 328 | 329 | @ => . 330 | W . 331 | 332 | E . 333 | @ => . 334 | 335 | W@ => .. 336 | 337 | E@ => .. 338 | } 339 | 340 | D @ 341 | @ => _ 342 | 343 | W @ 344 | @ => _ 345 | 346 | x . 347 | @ => _ 348 | 349 | ? @ 350 | @ => ? 351 | } 352 | 353 | } 354 | 355 | element Gravifloor { 356 | colour "brown" 357 | //category "T2Tile" 358 | //default true 359 | 360 | symbol G Gravifloor.Grower 361 | symbol B Gravifloor.Builder 362 | symbol W Gravifloor.Worker 363 | symbol b Gravifloor.Bullet 364 | 365 | @ => _ 366 | _ @ 367 | 368 | @ => _ 369 | $ @ 370 | 371 | @ => _ 372 | G . 373 | 374 | @ => _ 375 | B B 376 | 377 | @ => G 378 | * . 379 | 380 | @ => _ 381 | 382 | element Grower { 383 | colour "brown" 384 | for(xz.directions) @_ => @$ 385 | @ => B 386 | } 387 | 388 | element Builder { 389 | colour "brown" 390 | arg timer 1.0 391 | keep t (atom, space) => { 392 | if (atom.timer > 0) { 393 | atom.timer -= 0.05 394 | if (atom.timer < 0) atom.timer = 0 395 | atom.opacity = Math.floor(255 * atom.timer) 396 | SPACE.update(space) 397 | } 398 | } 399 | action @ => t 400 | 401 | given t (self) => self.timer <= 0 402 | t => W 403 | } 404 | 405 | element Worker { 406 | visible false 407 | 408 | maybe(0.001) { 409 | _ => b 410 | @ . 411 | } 412 | 413 | } 414 | 415 | element Bullet { 416 | colour "brown" 417 | 418 | arg timer 1.0 419 | keep t (atom, origin) => { 420 | if (atom.timer > 0) { 421 | atom.timer -= 0.025 422 | if (atom.timer < 0) atom.timer = 0 423 | atom.opacity = Math.floor(255 * atom.timer) 424 | SPACE.update(atom) 425 | } 426 | } 427 | action @ => t 428 | 429 | * => . 430 | @ _ 431 | 432 | _ => @ 433 | @ _ 434 | 435 | 436 | } 437 | } 438 | 439 | element RainbowRabbit { 440 | colour "white" 441 | emissive "grey" 442 | //category "Rainbow" 443 | data id 444 | arg hue 445 | 446 | element Part { 447 | colour "white" 448 | emissive "grey" 449 | arg id 450 | 451 | given R (element, atom, self) => element === RainbowRabbit && atom.id === self.id 452 | @R => .. 453 | R@ => .. 454 | 455 | @ => . 456 | R . 457 | 458 | @ => . 459 | R . 460 | 461 | @ => _ 462 | } 463 | 464 | // Init ID 465 | given i (self) => { 466 | return self.id === undefined 467 | } 468 | keep i (self) => { 469 | self.id = Math.random() 470 | if (self.hue === undefined) self.hue = Math.floor(Math.random() * 300) 471 | const colour = new THREE.Color("hsl(" + self.hue + ", 100%, 40%)") 472 | self.colour = { 473 | r: Math.floor(colour.r * 255), 474 | g: Math.floor(colour.g * 255), 475 | b: Math.floor(colour.b * 255), 476 | } 477 | self.emissive = self.colour 478 | } 479 | i => i 480 | 481 | // Grow body 482 | change P (self, atom) => { 483 | const part = new RainbowRabbit.Part(self.id) 484 | part.colour = self.colour 485 | part.emissive = self.emissive 486 | return part 487 | } 488 | @_ => .P 489 | _@ => P. 490 | 491 | _ => P 492 | @ . 493 | 494 | _ => P 495 | @ . 496 | 497 | // Die because can't grow 498 | given n (element, atom, self) => element !== RainbowRabbit.Part || atom.id !== self.id 499 | n => . 500 | @ _ 501 | _ . 502 | 503 | n => . 504 | @ _ 505 | _ . 506 | 507 | @n => _. 508 | _ . 509 | 510 | n@ => ._ 511 | _ . 512 | 513 | // Fall down 514 | given P (element, atom, self) => element === RainbowRabbit.Part && atom.id === self.id 515 | P P _ _ 516 | P@P => P_P 517 | ___ P@P 518 | 519 | // Move 520 | pov(right) { 521 | 522 | given H (self, atom, element) => { 523 | if (element !== RainbowRabbit) return false 524 | if ((self.hue < atom.hue)) { 525 | self.tempOtherHue = atom.hue 526 | return true 527 | } 528 | } 529 | 530 | change H (self) => new RainbowRabbit(self.tempOtherHue) 531 | 532 | @H => H@ 533 | @.H => H.@ 534 | maybe(1/2) any(z) @_ => _@ 535 | } 536 | 537 | given R (element) => element === RainbowRabbit 538 | @ _ 539 | R => . 540 | 541 | any(x) { 542 | P_P_ _P_P 543 | P@P_ => _P@P 544 | } 545 | } 546 | 547 | element Pulse { 548 | //category "T2Tile" 549 | //default true 550 | colour "brown" 551 | 552 | symbol H Pulse.Head 553 | symbol T Pulse.Tail 554 | 555 | ___ HHH 556 | _@_ => HTH 557 | ___ HHH 558 | 559 | @ => _ 560 | 561 | element Head { 562 | colour "brown" 563 | 564 | any(xy.directions) { 565 | H . 566 | @_ => T@ 567 | H . 568 | } 569 | } 570 | 571 | element Tail { 572 | colour "grey" 573 | emissive "black" 574 | 575 | given n (element) => element !== Pulse.Head && element !== Pulse.Tail && element !== Pulse 576 | 577 | all(xy.directions) { 578 | @n => _. 579 | } 580 | } 581 | } 582 | 583 | element Gravifull { 584 | //category "T2Tile" 585 | colour "pink" 586 | emissive "red" 587 | //default true 588 | 589 | symbol W Gravifull.Worker 590 | symbol B Gravifull.Builder 591 | 592 | // The "No Cheating" Method 593 | for(xyz.directions) @_ => .$ 594 | @ => B 595 | 596 | // The "Cheating" Method 597 | /*behave () => { 598 | for (const space of spaces) { 599 | SPACE.set(space, new Gravifull.Worker(), Gravifull.Worker) 600 | } 601 | }*/ 602 | 603 | element Builder { 604 | colour "pink" 605 | emissive "red" 606 | arg timer 1.0 607 | 608 | keep t (atom, space) => { 609 | if (atom.timer > 0) { 610 | atom.timer -= 0.5 611 | if (atom.timer < 0) atom.timer = 0 612 | atom.opacity = Math.floor(255 * atom.timer) 613 | SPACE.update(space) 614 | } 615 | } 616 | action @ => t 617 | //action any(xyz.directions) @$ => tt 618 | 619 | given t (self) => self.timer <= 0 620 | t => W 621 | } 622 | 623 | element Worker { 624 | visible false 625 | 626 | behave (sites, origin, Self) => { 627 | //print(sites) 628 | for (const site of sites) { 629 | const element = site.element 630 | if (element === Sandee) { 631 | const sandSites = site.sites 632 | const spaceBelow = sandSites[17] 633 | const elBelow = spaceBelow.element 634 | if (elBelow === Gravifull.Worker || elBelow === Wateree) { 635 | const atomBelow = spaceBelow.atom 636 | SPACE.set(spaceBelow, site.atom, Sandee) 637 | SPACE.set(site, atomBelow, elBelow) 638 | } 639 | else { 640 | const rando = Math.floor(Math.random() * 4) 641 | const slidesn = [18, 16, 55, 54][rando] 642 | const spaceSlide = sandSites[slidesn] 643 | const elSlide = spaceSlide.element 644 | if (elSlide === Gravifull.Worker || elSlide === Wateree) { 645 | const atomSlide = spaceSlide.atom 646 | SPACE.set(spaceSlide, site.atom, Sandee) 647 | SPACE.set(site, atomSlide, elSlide) 648 | } 649 | } 650 | } 651 | else if (element === Wateree) { 652 | const sandSites = site.sites 653 | const spaceBelow = sandSites[17] 654 | const elBelow = spaceBelow.element 655 | if (elBelow === Void) continue 656 | if (elBelow === Gravifull.Worker) { 657 | const atomBelow = spaceBelow.atom 658 | SPACE.set(spaceBelow, site.atom, Wateree) 659 | SPACE.set(site, atomBelow, Gravifull.Worker) 660 | } 661 | else { 662 | const rando = Math.floor(Math.random() * 4) 663 | const slidesn = [13, 11, 32, 37][rando] 664 | const spaceSlide = sandSites[slidesn] 665 | const elSlide = spaceSlide.element 666 | if (elSlide === Gravifull.Worker) { 667 | const atomSlide = spaceSlide.atom 668 | SPACE.set(spaceSlide, site.atom, Wateree) 669 | SPACE.set(site, atomSlide, Gravifull.Worker) 670 | } 671 | } 672 | } 673 | } 674 | } 675 | } 676 | 677 | } 678 | 679 | element Sandee { 680 | colour "#ffcc00" 681 | emissive "#ffa34d" 682 | //category "T2Tile" 683 | prop state SOLID 684 | prop temperature ROOM 685 | 686 | mimic(Gravifull.Worker) 687 | } 688 | 689 | element Wateree { 690 | colour "lightblue" 691 | emissive "blue" 692 | opacity 0.35 693 | //category "T2Tile" 694 | prop state LIQUID 695 | prop temperature COOL 696 | 697 | mimic(Gravifull.Worker) 698 | } 699 | 700 | element Huegene { 701 | colour "white" 702 | arg hue 703 | data coloured false 704 | opacity 0.1 705 | category "T2Tile" 706 | //default true 707 | 708 | // Cache hue RGB values 709 | given i () => !hueStepsInit 710 | keep i () => initHueStuff() 711 | action i => i 712 | 713 | // Pick the default hue if I haven't got one 714 | given h (self) => self.hue === undefined 715 | keep h (self) => self.hue = Math.round(HUE_DEFAULT) 716 | action h => h 717 | 718 | // Colour myself in with the correct RGB values 719 | given c (self) => !self.coloured 720 | keep c (origin, self) => { 721 | const offset = self.hue*3 722 | self.colour.r = HUE_RGBS[offset] 723 | self.colour.g = HUE_RGBS[offset+1] 724 | self.colour.b = HUE_RGBS[offset+2] 725 | self.emissive.r = self.colour.r 726 | self.emissive.g = self.colour.g 727 | self.emissive.b = self.colour.b 728 | SPACE.update(origin) 729 | self.coloured = true 730 | } 731 | action c => c 732 | 733 | // Mutate 734 | arg energy 1.0 735 | given e (self) => self.energy >= 1.0 736 | origin e 737 | change + (self) => { 738 | const mutation = Math.random() < 0.5? 1 : -1 739 | self.energy = 0.5 740 | return new Huegene(hueWrap(self.hue + mutation), 0.5) 741 | } 742 | any(xyz.rotations) { 743 | e_ => .+ 744 | } 745 | 746 | // Photosynethesise 747 | keep e (self) => { 748 | self.energy += 0.1 749 | if (self.energy > 1.0) self.energy = 1.0 750 | } 751 | @ => e 752 | 753 | } 754 | 755 | 756 | element HuegeneEater { 757 | colour "white" 758 | arg hue 759 | arg energy 1.0 760 | data coloured false 761 | //opacity 0.05 762 | category "T2Tile" 763 | 764 | // Cache hue RGB values 765 | given i () => !hueStepsInit 766 | keep i () => initHueStuff() 767 | action i => i 768 | 769 | // Pick the default hue if I haven't got one 770 | given h (self) => self.hue === undefined 771 | keep h (self) => self.hue = Math.round(HUE_DEFAULT) 772 | action h => h 773 | 774 | // Colour myself in with the correct RGB values 775 | given c (self) => !self.coloured 776 | keep c (origin, self) => { 777 | const offset = self.hue*3 778 | self.colour.r = HUE_RGBS[offset] 779 | self.colour.g = HUE_RGBS[offset+1] 780 | self.colour.b = HUE_RGBS[offset+2] 781 | self.emissive.r = self.colour.r 782 | self.emissive.g = self.colour.g 783 | self.emissive.b = self.colour.b 784 | SPACE.update(origin) 785 | self.coloured = false 786 | } 787 | action c => c 788 | //default true 789 | // Eat / Move / Reproduce 790 | given H (element, atom, self) => { 791 | if (element !== Huegene) return false 792 | const diff = Math.min( Math.abs((self.hue - atom.hue)/ HUE_STEPS), Math.abs((self.hue-HUE_STEPS - atom.hue)/ HUE_STEPS) ) 793 | return Math.random() > diff*3 794 | } 795 | given R (element, atom, self) => { 796 | if (element !== Empty) return false 797 | if (self.energy < 1.0) return false 798 | return true 799 | } 800 | given M (element, atom, self) => { 801 | self.energy -= 0.1 802 | if (element !== Empty) return false 803 | if (self.energy <= 0.0) return false 804 | return true 805 | } 806 | change E (atom, self) => { 807 | self.energy += atom.energy * 0.5 808 | if (self.energy > 1.0) self.energy = 1.0 809 | return self 810 | } 811 | change R (atom, self) => { 812 | const mutation = Math.random() < 0.5? 1 : -1 813 | self.energy = 0.5 814 | return new HuegeneEater(hueWrap(self.hue + mutation), 0.5) 815 | } 816 | change M (self) => { 817 | return self 818 | } 819 | any(xyz.directions) { 820 | @H => _E 821 | @R => .R 822 | @M => _M 823 | } 824 | 825 | given D (self) => self.energy <= 0.0 826 | D => _ 827 | 828 | //@ => _ 829 | 830 | 831 | } 832 | 833 | ` 834 | 835 | let hueStepsInit = false 836 | let HUE_STEPS = 50 837 | let HUE_DEFAULT = Math.round(3 * HUE_STEPS / 4) 838 | let HUE_RGBS = new Uint8Array(HUE_STEPS * 3) 839 | const initHueStuff = () => { 840 | for (let i = 0; i < HUE_STEPS; i++) { 841 | const colour = new THREE.Color() 842 | colour.setHSL(i / HUE_STEPS, 0.9, 0.4) 843 | HUE_RGBS[i*3] = Math.floor(colour.r * 255) 844 | HUE_RGBS[i*3 + 1] = Math.floor(colour.g * 255) 845 | HUE_RGBS[i*3 + 2] = Math.floor(colour.b * 255) 846 | } 847 | hueStepsInit = true 848 | } 849 | 850 | const hueWrap = (n) => { 851 | if (n >= HUE_STEPS) return hueWrap(n - HUE_STEPS) 852 | if (n < 0) return hueWrap(n + HUE_STEPS) 853 | return n 854 | } 855 | 856 | 857 | 858 | //DROPPER_OVERRIDE = true -------------------------------------------------------------------------------- /Source/Elements/Temperature.js: -------------------------------------------------------------------------------- 1 | 2 | const COLD = 0 // ... -10C 3 | const CHILLY = 1 // -10C to 0C 4 | const COOL = 2 // 0C to 15C 5 | const ROOM = 3 // 15C to 30C 6 | const BODY = 4 // 30C to 40C 7 | const WARM = 5 // 40C to 100C 8 | const HOT = 6 // 100C ... 9 | 10 | SpaceTode` 11 | 12 | element Temperature { 13 | 14 | //============// 15 | // Init Cache // 16 | //============// 17 | given i (selfElement) => selfElement.statesCache === undefined 18 | keep i (selfElement) => { 19 | const cache = {...(selfElement.states? selfElement.states() : {})} 20 | const chancesCache = {} 21 | for (const tempKey in cache) { 22 | const temp = cache[tempKey] 23 | if (temp.is(Array)) { 24 | cache[tempKey] = temp[0] 25 | chancesCache[tempKey] = temp[1] 26 | } 27 | } 28 | selfElement.statesChancesCache = chancesCache 29 | selfElement.statesCache = cache 30 | } 31 | i => i 32 | 33 | //==============// 34 | // Change State // 35 | //==============// 36 | select s (element) => element 37 | keep s (space, selfElement, selected) => { 38 | const selfTargets = selfElement.statesCache 39 | if (selfTargets === undefined) return 40 | 41 | let otherTemp = selected.temperature 42 | if (otherTemp === undefined) otherTemp = ROOM 43 | 44 | const selfTarget = selfTargets[otherTemp] 45 | if (selfTarget === undefined) return 46 | 47 | const selfChances = selfElement.statesChancesCache 48 | const selfChance = selfChances[otherTemp] 49 | if (selfChance !== undefined && Math.random() > selfChance) return 50 | 51 | SPACE.setAtom(space, new selfTarget(), selfTarget) 52 | } 53 | 54 | action any(xyz.directions) @s => s. 55 | 56 | given N (self, atom) => self !== atom 57 | N => . 58 | } 59 | 60 | ` -------------------------------------------------------------------------------- /Source/Elements/Testing.js: -------------------------------------------------------------------------------- 1 | SpaceTode` 2 | 3 | element SquishSand { 4 | 5 | colour "#ffcc00" 6 | emissive "#ffa34d" 7 | category "Weird" 8 | symbol D SquishSand.Double 9 | symbol S SquishSand.Single 10 | @ => D 11 | 12 | element Double any(xz) { 13 | //default true 14 | //category "Structure" 15 | colour "#ffa34d" 16 | emissive "#ffa34d" 17 | 18 | @ => _ 19 | _ @ 20 | 21 | @ => _ 22 | _ _ S S 23 | 24 | @ => _ 25 | _ @ 26 | 27 | @ => _ 28 | _ @ 29 | 30 | _ => S 31 | @ S 32 | 33 | } 34 | 35 | element Single any(xz) { 36 | colour "#ffcc00" 37 | emissive "#ffa34d" 38 | 39 | @ => _ 40 | _ @ 41 | 42 | _ . 43 | @ => _ 44 | $ D 45 | 46 | @ => D 47 | D @ 48 | 49 | @ => . 50 | _ _ . . 51 | 52 | @ => . 53 | $ $ . . 54 | 55 | @ => D 56 | D @ 57 | 58 | @ => D 59 | D @ 60 | 61 | @ => _ 62 | _ @ 63 | 64 | @ => _ 65 | _ @ 66 | 67 | } 68 | 69 | } 70 | 71 | element Rainblock { 72 | 73 | category "Weird" 74 | 75 | // Setup 76 | { 77 | data init false 78 | given i (self) => !self.init 79 | keep i (self, origin) => { 80 | self.init = true 81 | const hue = Math.floor(Math.random() * 360) 82 | const colour = new THREE.Color("hsl("+hue+", 100%, 50%)") 83 | self.colour.r = Math.round(colour.r * 255) 84 | self.colour.g = Math.round(colour.g * 255) 85 | self.colour.b = Math.round(colour.b * 255) 86 | self.emissive.r = Math.round(colour.r * 255) 87 | self.emissive.g = Math.round(colour.g * 255) 88 | self.emissive.b = Math.round(colour.b * 255) 89 | SPACE.update(origin) 90 | } 91 | i => i 92 | } 93 | 94 | // Move 95 | { 96 | given R (self, element, atom) => element === Rainblock && atom.colour.r > self.colour.r 97 | select R (atom) => atom 98 | change R (selected) => selected 99 | @R => R@ 100 | 101 | 102 | /*given r (self, element, atom) => element === Rainblock && atom.colour.r < self.colour.r 103 | select r (atom) => atom 104 | change r (selected) => selected 105 | r@ => @r*/ 106 | 107 | pov(right) { 108 | given G (self, element, atom) => element === Rainblock && atom.colour.g > self.colour.g 109 | select G (atom) => atom 110 | change G (selected) => selected 111 | @G => G@ 112 | 113 | 114 | /*given g (self, element, atom) => element === Rainblock && atom.colour.g < self.colour.g 115 | select g (atom) => atom 116 | change g (selected) => selected 117 | g@ => @g*/ 118 | } 119 | 120 | given B (self, element, atom) => element === Rainblock && atom.colour.b > self.colour.b 121 | select B (atom) => atom 122 | change B (selected) => selected 123 | B => @ 124 | @ B 125 | 126 | 127 | /*given b (self, element, atom) => element === Rainblock && atom.colour.b < self.colour.b 128 | select b (atom) => atom 129 | change b (selected) => selected 130 | @ => b 131 | b @*/ 132 | 133 | /*@ => _ 134 | _ @*/ 135 | 136 | any(xyz.directions) { 137 | @_ => _@ 138 | } 139 | } 140 | 141 | } 142 | 143 | element CrystalOld { 144 | 145 | category "Weird" 146 | colour "lightblue" 147 | emissive "blue" 148 | 149 | maybe(1/100) any(xz.directions) { 150 | ..... $...$ 151 | ..@.. => ..$.. 152 | ..... $...$ 153 | } 154 | 155 | 156 | 157 | } 158 | 159 | element Brancher { 160 | category "Weird" 161 | 162 | @ => _ 163 | _ @ 164 | 165 | symbol G Brancher.Grower 166 | symbol S Sand 167 | 168 | @ => G 169 | * . 170 | 171 | @ => _ 172 | 173 | element Grower { 174 | 175 | colour "rgb(70, 255, 128)" 176 | colour "rgb(35, 200, 100)" 177 | 178 | G => . 179 | @ . 180 | 181 | all(xz.directions) { 182 | G => . 183 | @ . 184 | } 185 | 186 | maybe(0.3) any(xz.directions) { 187 | _@_ => $.$ 188 | } 189 | 190 | /*maybe(0.3) any(xz.directions) { 191 | _ _ => $ $ 192 | @ . 193 | }*/ 194 | 195 | /*any(xz.directions) { 196 | _ => $ 197 | @ . 198 | }*/ 199 | 200 | maybe(1/20) { 201 | _ => $ 202 | @ . 203 | } 204 | 205 | } 206 | 207 | } 208 | 209 | element Labyrinth { 210 | category "Weird" 211 | 212 | data countBuffer 0 213 | 214 | // Reset buffer 215 | keep r (self) => self.countBuffer = 0 216 | action @ => r 217 | 218 | // Count neighbours 219 | given c (element, Self, self) => { 220 | if (element === Self) self.countBuffer++ 221 | return true 222 | } 223 | 224 | action { 225 | ccc ... 226 | c@c => ... 227 | ccc ... 228 | } 229 | 230 | pov(right) action { 231 | ccc ... 232 | c@c => ... 233 | ccc ... 234 | } 235 | 236 | pov(top) action { 237 | ccc ... 238 | c@c => ... 239 | ccc ... 240 | } 241 | 242 | // Die 243 | given d (self) => self.countBuffer > 4 244 | d => _ 245 | 246 | // Grow 247 | origin g 248 | given g (self) => self.countBuffer === 2 249 | for(xyz.directions) g_ => .$ 250 | 251 | } 252 | 253 | element SparkLife { 254 | category "Weird" 255 | colour "rgb(255, 255, 70)" 256 | data countBuffer 0 257 | 258 | // Reset buffer 259 | keep r (self) => self.countBuffer = 0 260 | action @ => r 261 | 262 | // Count neighbours 263 | given c (element, Self, self) => { 264 | if (element === Self) self.countBuffer++ 265 | return false 266 | } 267 | 268 | all(xyz.others) @c => .. 269 | 270 | // Die 271 | given d (self) => self.countBuffer >= 5 || self.countBuffer <= 2 272 | d => _ 273 | 274 | // Grow 275 | origin g 276 | given g (self) => self.countBuffer >= 0 && self.countBuffer <= Infinity 277 | for(xyz.directions) g_ => .$ 278 | 279 | } 280 | 281 | element GameOfLife { 282 | category "Weird" 283 | //category "Video" 284 | colour "brown" 285 | 286 | // Globals 287 | symbol D GameOfLife.Dead 288 | symbol A GameOfLife.Alive 289 | 290 | // Setup: Fill up the universe 291 | all(xy.directions) @_ => @$ 292 | 293 | // Then become a dead or alive space 294 | maybe(0.9) @ => D 295 | @ => A 296 | 297 | element Dead { 298 | opacity 1 299 | //visible false 300 | colour "white" 301 | emissive "grey" 302 | data tally 0 303 | //category "Video" 304 | 305 | // Reset tally 306 | keep r (self) => self.tally = 0 307 | action @ => r 308 | 309 | // Count alive neighbours 310 | given a (self, element) => { 311 | if (element === GameOfLife.Alive) { 312 | self.tally++ 313 | } 314 | return false 315 | } 316 | action { 317 | aaa ... 318 | a@a => ... 319 | aaa ... 320 | } 321 | 322 | // Live! 323 | given l (self) => self.tally === 3 324 | l => A 325 | } 326 | 327 | element Alive { 328 | //category "Video" 329 | prop override true 330 | data tally 0 331 | colour "grey" 332 | emissive "black" 333 | 334 | // Reset tally 335 | keep r (self) => self.tally = 0 336 | action @ => r 337 | 338 | // Count alive neighbours 339 | given a (self, element) => { 340 | if (element === GameOfLife.Alive) { 341 | self.tally++ 342 | } 343 | return false 344 | } 345 | action { 346 | aaa ... 347 | a@a => ... 348 | aaa ... 349 | } 350 | 351 | // Survive 352 | given s (self) => self.tally >= 2 && self.tally <= 3 353 | s => . 354 | 355 | // Die 356 | @ => D 357 | 358 | } 359 | 360 | } 361 | 362 | 363 | element SpreadWater { 364 | category "Weird" 365 | //category "Video" 366 | data direction 0 367 | colour "lightblue" 368 | emissive "blue" 369 | opacity 1 370 | 371 | prop state LIQUID 372 | prop temperature COOL 373 | @ => _ 374 | _ @ 375 | 376 | for(xz.directions) { 377 | @ => _ 378 | _ @ 379 | } 380 | 381 | given D (self, transformationNumber, element) => element === Empty && self.direction === transformationNumber 382 | all(xz.directions) { 383 | @D => _@ 384 | } 385 | 386 | keep N (self) => self.direction = Math.floor(Math.random() * 4) 387 | @ => N 388 | } 389 | 390 | 391 | 392 | element MomentumSand { 393 | category "Weird" 394 | //category "Video" 395 | emissive "#ffa34d" 396 | colour "#fc0" 397 | data movement 0 398 | 399 | keep g (self) => { 400 | self.movement += 0.05 401 | if (self.movement > 1) self.movement = 1 402 | } 403 | action { 404 | @ => g 405 | _ . 406 | } 407 | 408 | given F (element, self) => { 409 | if (element !== Empty && element !== SpreadWater) return false 410 | return self.movement > 2/3 411 | } 412 | select F (atom) => atom 413 | change F (selected) => selected 414 | 415 | given f (element, self) => { 416 | if (element !== Empty && element !== SpreadWater) return false 417 | return self.movement > 1/3 418 | } 419 | select f (atom) => atom 420 | change f (selected) => selected 421 | @ => _ 422 | _ F 423 | F @ 424 | 425 | @ => f 426 | f @ 427 | 428 | given b (Self, element) => element === Self 429 | keep b (atom, self) => { 430 | if (atom.movement < self.movement) { 431 | atom.movement = self.movement 432 | } 433 | } 434 | action { 435 | @ => . 436 | b b 437 | } 438 | 439 | given s (element, self) => { 440 | if (element !== Empty && element !== SpreadWater) return false 441 | return self.movement > 0.3 442 | } 443 | select s (atom) => atom 444 | change s (selected) => selected 445 | 446 | change m (self) => { 447 | self.movement -= 0.2 448 | if (self.movement < 0) self.movement = 0 449 | return self 450 | } 451 | 452 | 453 | any(xz.directions) { 454 | @ => s 455 | s m 456 | } 457 | 458 | } 459 | 460 | `; 461 | -------------------------------------------------------------------------------- /Source/Elements/TwoTwoTwoTwo.js: -------------------------------------------------------------------------------- 1 | SpaceTode` 2 | 3 | element TwoTwoTwoTwoOne { 4 | emissive "rgb(255, 0,0)" 5 | colour "rgb(255, 100, 100)" 6 | category "Stream" 7 | symbol 2 TwoTwoTwoTwoTwo 8 | 9 | keep W () => { 10 | const currentTime = performance.now() 11 | if (!window.lastTime) { 12 | window.lastTime = currentTime 13 | return 14 | } 15 | const deltaTime = currentTime - window.lastTime 16 | if (deltaTime < 200) { 17 | return 18 | } 19 | window.lastTime = currentTime 20 | const wordIndex = window.wordIndex || 0 21 | const word = words[wordIndex] 22 | window.wordIndex = (wordIndex + 1) % words.length 23 | for (let i = 0; i < 4; i++) { 24 | const letter = word[i] 25 | const coordinates = convertLetterIntoCoordinates(letter) 26 | for (const coordinate of coordinates) { 27 | const [x, y] = coordinate 28 | const space = WORLD.selectSpace(world, -10 + x + i*7, 10 - y, 18) 29 | SPACE.setAtom(space, new Pixel()) 30 | } 31 | } 32 | } 33 | 34 | maybe(0.5) { 35 | @ => _ 36 | _ @ 37 | 38 | @ => _ 39 | 2 @ 40 | 41 | action @ => W 42 | 43 | @ => 2 44 | } 45 | 46 | } 47 | 48 | element Pixel { 49 | colour "black" 50 | 51 | keep C (space, self) => { 52 | const FADE_AMOUNT = 2 53 | self.colour.r = Math.min(255, self.colour.r + FADE_AMOUNT) 54 | self.colour.g = Math.min(255, self.colour.g + FADE_AMOUNT) 55 | self.colour.b = Math.min(255, self.colour.b + FADE_AMOUNT) 56 | self.emissive.r = Math.min(255, self.emissive.r + FADE_AMOUNT) 57 | self.emissive.g = Math.min(255, self.emissive.g + FADE_AMOUNT) 58 | self.emissive.b = Math.min(255, self.emissive.b + FADE_AMOUNT) 59 | SPACE.update(space) 60 | } 61 | 62 | action @ => C 63 | 64 | pov(right) { 65 | @_ => _@ 66 | } 67 | 68 | maybe(0.01) { 69 | @ => _ 70 | } 71 | } 72 | 73 | element TwoTwoTwoTwoTwo { 74 | emissive "rgb(0, 0, 255)" 75 | colour "rgb(100, 100, 255)" 76 | 77 | symbol 1 TwoTwoTwoTwoOne 78 | 79 | maybe(0.5) { 80 | _ @ 81 | @ => _ 82 | 83 | @ _ 84 | 1 => @ 85 | 86 | @ => 1 87 | } 88 | } 89 | `; 90 | 91 | const alphabet = { 92 | A: [ 93 | // @prettier-ignore 94 | "..#..", 95 | ".#.#.", 96 | "#####", 97 | "#...#", 98 | "#...#", 99 | ], 100 | B: [ 101 | // @prettier-ignore 102 | "####.", 103 | "#...#", 104 | "####.", 105 | "#...#", 106 | "####.", 107 | ], 108 | C: [ 109 | // @prettier-ignore 110 | ".###.", 111 | "#....", 112 | "#....", 113 | "#....", 114 | ".###.", 115 | ], 116 | D: [ 117 | // @prettier-ignore 118 | "####.", 119 | "#...#", 120 | "#...#", 121 | "#...#", 122 | "####.", 123 | ], 124 | E: [ 125 | // @prettier-ignore 126 | "#####", 127 | "#....", 128 | "####.", 129 | "#....", 130 | "#####", 131 | ], 132 | F: [ 133 | // @prettier-ignore 134 | "#####", 135 | "#....", 136 | "####.", 137 | "#....", 138 | "#....", 139 | ], 140 | G: [ 141 | // @prettier-ignore 142 | ".###.", 143 | "#....", 144 | "#.###", 145 | "#...#", 146 | ".###.", 147 | ], 148 | H: [ 149 | // @prettier-ignore 150 | "#...#", 151 | "#...#", 152 | "#####", 153 | "#...#", 154 | "#...#", 155 | ], 156 | I: [ 157 | // @prettier-ignore 158 | "#####", 159 | "..#..", 160 | "..#..", 161 | "..#..", 162 | "#####", 163 | ], 164 | J: [ 165 | // @prettier-ignore 166 | "..###", 167 | "...#.", 168 | "...#.", 169 | "#..#.", 170 | ".##..", 171 | ], 172 | K: [ 173 | // @prettier-ignore 174 | "#..#.", 175 | "#.#..", 176 | "##...", 177 | "#.#..", 178 | "#..#.", 179 | ], 180 | L: [ 181 | // @prettier-ignore 182 | "#....", 183 | "#....", 184 | "#....", 185 | "#....", 186 | "#####", 187 | ], 188 | M: [ 189 | // @prettier-ignore 190 | "#...#", 191 | "##.##", 192 | "#.#.#", 193 | "#...#", 194 | "#...#", 195 | ], 196 | N: [ 197 | // @prettier-ignore 198 | "#...#", 199 | "##..#", 200 | "#.#.#", 201 | "#..##", 202 | "#...#", 203 | ], 204 | O: [ 205 | // @prettier-ignore 206 | ".###.", 207 | "#...#", 208 | "#...#", 209 | "#...#", 210 | ".###.", 211 | ], 212 | P: [ 213 | // @prettier-ignore 214 | "####.", 215 | "#...#", 216 | "####.", 217 | "#....", 218 | "#....", 219 | ], 220 | Q: [ 221 | // @prettier-ignore 222 | ".###.", 223 | "#...#", 224 | "#...#", 225 | "#.##.", 226 | ".##.#", 227 | ], 228 | R: [ 229 | // @prettier-ignore 230 | "####.", 231 | "#...#", 232 | "####.", 233 | "#.#..", 234 | "#..#.", 235 | ], 236 | S: [ 237 | // @prettier-ignore 238 | ".###.", 239 | "#....", 240 | ".###.", 241 | "...#.", 242 | "###..", 243 | ], 244 | T: [ 245 | // @prettier-ignore 246 | "#####", 247 | "..#..", 248 | "..#..", 249 | "..#..", 250 | "..#..", 251 | ], 252 | U: [ 253 | // @prettier-ignore 254 | "#...#", 255 | "#...#", 256 | "#...#", 257 | "#...#", 258 | ".###.", 259 | ], 260 | V: [ 261 | // @prettier-ignore 262 | "#...#", 263 | "#...#", 264 | "#...#", 265 | ".#.#.", 266 | "..#..", 267 | ], 268 | W: [ 269 | // @prettier-ignore 270 | "#...#", 271 | "#...#", 272 | "#.#.#", 273 | "##.##", 274 | "#...#", 275 | ], 276 | X: [ 277 | // @prettier-ignore 278 | "#...#", 279 | ".#.#.", 280 | "..#..", 281 | ".#.#.", 282 | "#...#", 283 | ], 284 | Y: [ 285 | // @prettier-ignore 286 | "#...#", 287 | "#...#", 288 | ".###.", 289 | "..#..", 290 | "..#..", 291 | ], 292 | Z: [ 293 | // @prettier-ignore 294 | "#####", 295 | "...#.", 296 | "..#..", 297 | ".#...", 298 | "#####", 299 | ], 300 | }; 301 | 302 | // console.log(Object.values(alphabet).length); 303 | 304 | function convertLetterIntoCoordinates(letter) { 305 | const letterArray = alphabet[letter]; 306 | const coordinates = []; 307 | for (let y = 0; y < letterArray.length; y++) { 308 | for (let x = 0; x < letterArray[y].length; x++) { 309 | if (letterArray[y][x] === "#") { 310 | coordinates.push([x, y]); 311 | } 312 | } 313 | } 314 | return coordinates; 315 | } 316 | 317 | // console.log(convertLetterIntoCoordinates("Z")); 318 | 319 | const wordsString = `ABET 320 | ABLE 321 | ABLY 322 | ABUT 323 | ACAI 324 | ACED 325 | ACES 326 | ACHE 327 | ACHY 328 | ACID 329 | ACME 330 | ACNE 331 | ACRE 332 | ACTS 333 | ADDS 334 | AEON 335 | AFAR 336 | AFRO 337 | AGED 338 | AGES 339 | AGOG 340 | AHEM 341 | AIDE 342 | AILS 343 | AIMS 344 | AIRS 345 | AIRY 346 | AJAR 347 | AKIN 348 | ALAS 349 | ALLY 350 | ALMS 351 | ALOE 352 | ALSO 353 | ALTO 354 | ALUM 355 | AMEN 356 | AMID 357 | AMMO 358 | AMOK 359 | AMPS 360 | ANDS 361 | ANEW 362 | ANKH 363 | ANON 364 | ANTI 365 | ANTS 366 | APES 367 | APEX 368 | APPS 369 | AQUA 370 | ARCH 371 | ARCS 372 | AREA 373 | ARIA 374 | ARID 375 | ARKS 376 | ARMS 377 | ARMY 378 | ARSE 379 | ARTS 380 | ARTY 381 | ASHY 382 | ASKS 383 | ATOM 384 | ATOP 385 | AUNT 386 | AURA 387 | AUTO 388 | AVID 389 | AVOW 390 | AWAY 391 | AWED 392 | AWES 393 | AWLS 394 | AWRY 395 | AXED 396 | AXES 397 | AXIS 398 | AXLE 399 | BABA 400 | BABE 401 | BABY 402 | BACH 403 | BACK 404 | BADS 405 | BAGS 406 | BAIL 407 | BAIT 408 | BAKE 409 | BALD 410 | BALE 411 | BALK 412 | BALL 413 | BALM 414 | BAND 415 | BANE 416 | BANG 417 | BANK 418 | BANS 419 | BARB 420 | BARD 421 | BARE 422 | BARF 423 | BARK 424 | BARN 425 | BARS 426 | BASE 427 | BASH 428 | BASK 429 | BASS 430 | BAST 431 | BATH 432 | BATS 433 | BAUD 434 | BAWL 435 | BAYS 436 | BEAD 437 | BEAK 438 | BEAM 439 | BEAN 440 | BEAR 441 | BEAT 442 | BEAU 443 | BECK 444 | BEDS 445 | BEEF 446 | BEEN 447 | BEEP 448 | BEER 449 | BEES 450 | BEET 451 | BEGS 452 | BELL 453 | BELT 454 | BEND 455 | BENT 456 | BERG 457 | BERM 458 | BEST 459 | BETA 460 | BETS 461 | BEVY 462 | BEYS 463 | BIAS 464 | BIBS 465 | BIDE 466 | BIDS 467 | BIFF 468 | BIKE 469 | BILE 470 | BILK 471 | BILL 472 | BIND 473 | BIOS 474 | BIRD 475 | BITE 476 | BITS 477 | BLAB 478 | BLAH 479 | BLEB 480 | BLED 481 | BLEW 482 | BLIP 483 | BLOB 484 | BLOC 485 | BLOG 486 | BLOT 487 | BLOW 488 | BLUE 489 | BLUR 490 | BOAR 491 | BOAS 492 | BOAT 493 | BOBS 494 | BODE 495 | BODS 496 | BODY 497 | BOGS 498 | BOGY 499 | BOIL 500 | BOLA 501 | BOLD 502 | BOLO 503 | BOLT 504 | BOMB 505 | BOND 506 | BONE 507 | BONG 508 | BONK 509 | BONY 510 | BOOK 511 | BOOM 512 | BOON 513 | BOOR 514 | BOOS 515 | BOOT 516 | BORE 517 | BORN 518 | BOSS 519 | BOTH 520 | BOTS 521 | BOUT 522 | BOWL 523 | BOWS 524 | BOXY 525 | BOYO 526 | BOYS 527 | BOZO 528 | BRAG 529 | BRAN 530 | BRAS 531 | BRAT 532 | BRAY 533 | BRED 534 | BREW 535 | BRIE 536 | BRIG 537 | BRIM 538 | BRIS 539 | BRIT 540 | BROS 541 | BROW 542 | BUCK 543 | BUDS 544 | BUFF 545 | BUGS 546 | BULB 547 | BULK 548 | BULL 549 | BUMP 550 | BUMS 551 | BUND 552 | BUNG 553 | BUNK 554 | BUNS 555 | BUNT 556 | BUOY 557 | BURB 558 | BURG 559 | BURL 560 | BURN 561 | BURP 562 | BURR 563 | BURY 564 | BUSH 565 | BUSK 566 | BUST 567 | BUSY 568 | BUTT 569 | BUYS 570 | BUZZ 571 | BYES 572 | BYTE 573 | CABS 574 | CAFE 575 | CAFF 576 | CAGE 577 | CAKE 578 | CALF 579 | CALL 580 | CALM 581 | CAME 582 | CAMO 583 | CAMP 584 | CANE 585 | CANS 586 | CAPE 587 | CAPO 588 | CAPS 589 | CARB 590 | CARD 591 | CARE 592 | CARP 593 | CARS 594 | CART 595 | CASE 596 | CASH 597 | CASK 598 | CAST 599 | CATS 600 | CAVE 601 | CAWS 602 | CAYS 603 | CEDE 604 | CELL 605 | CELS 606 | CELT 607 | CENT 608 | CESS 609 | CHAD 610 | CHAI 611 | CHAP 612 | CHAR 613 | CHAT 614 | CHEF 615 | CHEW 616 | CHIC 617 | CHIN 618 | CHIP 619 | CHIT 620 | CHOP 621 | CHOW 622 | CHUB 623 | CHUG 624 | CHUM 625 | CIAO 626 | CITE 627 | CITY 628 | CLAD 629 | CLAM 630 | CLAN 631 | CLAP 632 | CLAW 633 | CLAY 634 | CLEF 635 | CLIP 636 | CLOD 637 | CLOG 638 | CLOP 639 | CLOT 640 | CLUB 641 | CLUE 642 | COAL 643 | COAT 644 | COAX 645 | COBS 646 | COCA 647 | COCO 648 | CODA 649 | CODE 650 | CODS 651 | COED 652 | COGS 653 | COIL 654 | COIN 655 | COKE 656 | COLA 657 | COLD 658 | COLE 659 | COLT 660 | COMA 661 | COMB 662 | COME 663 | COMP 664 | CONE 665 | CONS 666 | COOK 667 | COOL 668 | COOP 669 | COOS 670 | COPE 671 | COPS 672 | COPY 673 | CORD 674 | CORE 675 | CORK 676 | CORN 677 | COST 678 | COSY 679 | COTS 680 | COUP 681 | COVE 682 | COWL 683 | COWS 684 | COZY 685 | CRAB 686 | CRAG 687 | CRAM 688 | CRAP 689 | CRAW 690 | CREW 691 | CRIB 692 | CRIT 693 | CROC 694 | CROP 695 | CROW 696 | CRUD 697 | CRUX 698 | CUBE 699 | CUBS 700 | CUDS 701 | CUED 702 | CUES 703 | CUFF 704 | CULL 705 | CULT 706 | CUPS 707 | CURB 708 | CURD 709 | CURE 710 | CURL 711 | CURT 712 | CUSP 713 | CUSS 714 | CUTE 715 | CUTS 716 | CYAN 717 | CYST 718 | CZAR 719 | DABS 720 | DADA 721 | DADS 722 | DAFT 723 | DAME 724 | DAMN 725 | DAMP 726 | DAMS 727 | DANK 728 | DARE 729 | DARK 730 | DARN 731 | DART 732 | DASH 733 | DATA 734 | DATE 735 | DAWN 736 | DAYS 737 | DAZE 738 | DEAD 739 | DEAF 740 | DEAL 741 | DEAN 742 | DEAR 743 | DEBT 744 | DECK 745 | DEED 746 | DEEM 747 | DEEP 748 | DEER 749 | DEFT 750 | DEFY 751 | DELI 752 | DELL 753 | DELT 754 | DEMO 755 | DENS 756 | DENT 757 | DERM 758 | DESK 759 | DEWS 760 | DEWY 761 | DIAL 762 | DICE 763 | DIED 764 | DIES 765 | DIET 766 | DIGS 767 | DILL 768 | DIME 769 | DIMS 770 | DINE 771 | DING 772 | DINK 773 | DINO 774 | DINT 775 | DIPS 776 | DIRE 777 | DIRT 778 | DISC 779 | DISH 780 | DISK 781 | DISS 782 | DIVA 783 | DIVE 784 | DOCK 785 | DOCS 786 | DODO 787 | DOER 788 | DOES 789 | DOFF 790 | DOGS 791 | DOJO 792 | DOLE 793 | DOLL 794 | DOLT 795 | DOME 796 | DONE 797 | DONG 798 | DOOM 799 | DOOR 800 | DOPE 801 | DORK 802 | DORM 803 | DORY 804 | DOSE 805 | DOTE 806 | DOTH 807 | DOTS 808 | DOUR 809 | DOVE 810 | DOWN 811 | DOXY 812 | DOZE 813 | DOZY 814 | DRAB 815 | DRAG 816 | DRAM 817 | DRAW 818 | DRAY 819 | DREW 820 | DRIP 821 | DROP 822 | DRUG 823 | DRUM 824 | DRYS 825 | DUAL 826 | DUBS 827 | DUCK 828 | DUCT 829 | DUDE 830 | DUDS 831 | DUEL 832 | DUES 833 | DUET 834 | DUFF 835 | DUKE 836 | DULL 837 | DULY 838 | DUMB 839 | DUMP 840 | DUNE 841 | DUNG 842 | DUNK 843 | DUNS 844 | DUOS 845 | DUPE 846 | DUSK 847 | DUST 848 | DUTY 849 | DYAD 850 | DYED 851 | DYER 852 | DYES 853 | EACH 854 | EARL 855 | EARN 856 | EARS 857 | EASE 858 | EAST 859 | EASY 860 | EATS 861 | EAVE 862 | EBBS 863 | ECHO 864 | EDDY 865 | EDGE 866 | EDGY 867 | EDIT 868 | EELS 869 | EGGS 870 | EGGY 871 | EGOS 872 | ELKS 873 | ELMS 874 | ELSE 875 | EMIT 876 | EMMY 877 | EMUS 878 | ENDS 879 | ENVY 880 | EONS 881 | EPEE 882 | EPIC 883 | ERAS 884 | ERRS 885 | ETCH 886 | EURO 887 | EVEN 888 | EVER 889 | EVES 890 | EVIL 891 | EWES 892 | EXAM 893 | EXEC 894 | EXIT 895 | EXON 896 | EXPO 897 | EYED 898 | EYES 899 | FABS 900 | FACE 901 | FACT 902 | FADE 903 | FADS 904 | FAIL 905 | FAIR 906 | FAKE 907 | FALL 908 | FAME 909 | FANG 910 | FANS 911 | FARE 912 | FARM 913 | FART 914 | FAST 915 | FATE 916 | FATS 917 | FAUN 918 | FAVA 919 | FAVE 920 | FAWN 921 | FAZE 922 | FEAR 923 | FEAT 924 | FEED 925 | FEEL 926 | FEES 927 | FEET 928 | FELL 929 | FELT 930 | FEND 931 | FENS 932 | FERN 933 | FESS 934 | FEST 935 | FETA 936 | FETE 937 | FEUD 938 | FIAT 939 | FIBS 940 | FIFE 941 | FIGS 942 | FILE 943 | FILL 944 | FILM 945 | FILO 946 | FIND 947 | FINE 948 | FINK 949 | FINS 950 | FIRE 951 | FIRM 952 | FIRS 953 | FISH 954 | FIST 955 | FITS 956 | FIVE 957 | FIZZ 958 | FLAB 959 | FLAG 960 | FLAK 961 | FLAM 962 | FLAN 963 | FLAP 964 | FLAT 965 | FLAW 966 | FLAX 967 | FLAY 968 | FLEA 969 | FLED 970 | FLEE 971 | FLEW 972 | FLEX 973 | FLIP 974 | FLIT 975 | FLOC 976 | FLOE 977 | FLOG 978 | FLOP 979 | FLOW 980 | FLUB 981 | FLUE 982 | FLUS 983 | FLUX 984 | FOAL 985 | FOAM 986 | FOBS 987 | FOCI 988 | FOES 989 | FOGS 990 | FOGY 991 | FOIL 992 | FOLD 993 | FOLK 994 | FOND 995 | FONT 996 | FOOD 997 | FOOL 998 | FOOT 999 | FOPS 1000 | FORD 1001 | FORE 1002 | FORK 1003 | FORM 1004 | FORT 1005 | FOUL 1006 | FOUR 1007 | FOWL 1008 | FOXY 1009 | FRAG 1010 | FRAT 1011 | FRAY 1012 | FREE 1013 | FRET 1014 | FRIG 1015 | FROG 1016 | FROM 1017 | FUEL 1018 | FUCK 1019 | FUGU 1020 | FULL 1021 | FUME 1022 | FUND 1023 | FUNK 1024 | FURL 1025 | FURS 1026 | FURY 1027 | FUSE 1028 | FUSS 1029 | FUTZ 1030 | FUZZ 1031 | GAFF 1032 | GAGS 1033 | GAIN 1034 | GAIT 1035 | GALA 1036 | GALE 1037 | GALL 1038 | GALS 1039 | GAME 1040 | GANG 1041 | GAPE 1042 | GAPS 1043 | GARB 1044 | GASH 1045 | GASP 1046 | GATE 1047 | GAVE 1048 | GAWK 1049 | GAYS 1050 | GAZE 1051 | GEAR 1052 | GEEK 1053 | GEES 1054 | GELS 1055 | GEMS 1056 | GENE 1057 | GENS 1058 | GENT 1059 | GERM 1060 | GETS 1061 | GHAT 1062 | GHEE 1063 | GIFT 1064 | GIGS 1065 | GILD 1066 | GILL 1067 | GILT 1068 | GINS 1069 | GIRD 1070 | GIRL 1071 | GIST 1072 | GITS 1073 | GIVE 1074 | GLAD 1075 | GLAM 1076 | GLEE 1077 | GLEN 1078 | GLIB 1079 | GLOB 1080 | GLOM 1081 | GLOP 1082 | GLOW 1083 | GLUE 1084 | GLUG 1085 | GLUM 1086 | GLUT 1087 | GNAR 1088 | GNAT 1089 | GNAW 1090 | GOAD 1091 | GOAL 1092 | GOAT 1093 | GOBS 1094 | GOBY 1095 | GODS 1096 | GOER 1097 | GOES 1098 | GOLD 1099 | GOLF 1100 | GONE 1101 | GONG 1102 | GOOD 1103 | GOOF 1104 | GOON 1105 | GOOP 1106 | GOOS 1107 | GORE 1108 | GORY 1109 | GOSH 1110 | GOTH 1111 | GOUT 1112 | GOWN 1113 | GRAB 1114 | GRAD 1115 | GRAM 1116 | GRAN 1117 | GRAY 1118 | GREW 1119 | GREY 1120 | GRID 1121 | GRIM 1122 | GRIN 1123 | GRIP 1124 | GRIT 1125 | GROG 1126 | GROW 1127 | GRUB 1128 | GUFF 1129 | GULF 1130 | GULL 1131 | GULP 1132 | GUMS 1133 | GUNK 1134 | GUNS 1135 | GURU 1136 | GUSH 1137 | GUST 1138 | GUTS 1139 | GUYS 1140 | GYMS 1141 | GYRE 1142 | GYRO 1143 | HACK 1144 | HAGS 1145 | HAIL 1146 | HAIR 1147 | HALF 1148 | HALL 1149 | HALO 1150 | HALT 1151 | HAMS 1152 | HAND 1153 | HANG 1154 | HANK 1155 | HAPS 1156 | HARD 1157 | HARE 1158 | HARK 1159 | HARM 1160 | HARP 1161 | HART 1162 | HASH 1163 | HATE 1164 | HATH 1165 | HATS 1166 | HAUL 1167 | HAVE 1168 | HAWK 1169 | HAWS 1170 | HAYS 1171 | HAZE 1172 | HAZY 1173 | HEAD 1174 | HEAL 1175 | HEAP 1176 | HEAR 1177 | HEAT 1178 | HECK 1179 | HEED 1180 | HEEL 1181 | HEFT 1182 | HEIR 1183 | HELD 1184 | HELL 1185 | HELM 1186 | HELP 1187 | HEMP 1188 | HEMS 1189 | HENS 1190 | HERB 1191 | HERD 1192 | HERE 1193 | HERO 1194 | HERS 1195 | HEST 1196 | HEWN 1197 | HEWS 1198 | HICK 1199 | HIDE 1200 | HIGH 1201 | HIKE 1202 | HILL 1203 | HILT 1204 | HIND 1205 | HINT 1206 | HIPS 1207 | HIRE 1208 | HISS 1209 | HITS 1210 | HIVE 1211 | HIYA 1212 | HOAR 1213 | HOAX 1214 | HOBO 1215 | HOCK 1216 | HOED 1217 | HOES 1218 | HOGS 1219 | HOLD 1220 | HOLE 1221 | HOLO 1222 | HOLT 1223 | HOLY 1224 | HOME 1225 | HONE 1226 | HONK 1227 | HOOD 1228 | HOOF 1229 | HOOK 1230 | HOOP 1231 | HOOT 1232 | HOPE 1233 | HOPS 1234 | HORN 1235 | HOSE 1236 | HOST 1237 | HOUR 1238 | HOVE 1239 | HOWL 1240 | HUBS 1241 | HUCK 1242 | HUED 1243 | HUES 1244 | HUFF 1245 | HUGE 1246 | HUGS 1247 | HULA 1248 | HULK 1249 | HULL 1250 | HUMP 1251 | HUMS 1252 | HUNG 1253 | HUNK 1254 | HUNT 1255 | HURL 1256 | HURT 1257 | HUSH 1258 | HUSK 1259 | HUTS 1260 | HYMN 1261 | HYPE 1262 | HYPO 1263 | ICED 1264 | ICES 1265 | ICKY 1266 | ICON 1267 | IDEA 1268 | IDLE 1269 | IDLY 1270 | IDOL 1271 | IFFY 1272 | ILLS 1273 | IMPS 1274 | INCH 1275 | INFO 1276 | INKS 1277 | INKY 1278 | INNS 1279 | INTO 1280 | IONS 1281 | IOTA 1282 | IRIS 1283 | IRKS 1284 | IRON 1285 | ISLE 1286 | ISMS 1287 | ITCH 1288 | ITEM 1289 | JABS 1290 | JACK 1291 | JADE 1292 | JAIL 1293 | JAKE 1294 | JAMB 1295 | JAMS 1296 | JAPE 1297 | JARS 1298 | JAVA 1299 | JAWA 1300 | JAWS 1301 | JAYS 1302 | JAZZ 1303 | JEAN 1304 | JEEP 1305 | JEER 1306 | JEEZ 1307 | JELL 1308 | JERK 1309 | JEST 1310 | JETS 1311 | JIBE 1312 | JIBS 1313 | JIGS 1314 | JILT 1315 | JINK 1316 | JINX 1317 | JIVE 1318 | JOBS 1319 | JOCK 1320 | JOGS 1321 | JOIN 1322 | JOKE 1323 | JOLT 1324 | JOTS 1325 | JOWL 1326 | JOYS 1327 | JUDO 1328 | JUGS 1329 | JUJU 1330 | JUKE 1331 | JUMP 1332 | JUNK 1333 | JURY 1334 | JUST 1335 | JUTE 1336 | JUTS 1337 | KALE 1338 | KEEL 1339 | KEEN 1340 | KEEP 1341 | KEGS 1342 | KELP 1343 | KEPT 1344 | KETO 1345 | KEYS 1346 | KHAN 1347 | KHAT 1348 | KICK 1349 | KIDS 1350 | KILL 1351 | KILN 1352 | KILO 1353 | KILT 1354 | KIND 1355 | KING 1356 | KIPS 1357 | KISS 1358 | KITE 1359 | KITS 1360 | KIWI 1361 | KNEE 1362 | KNEW 1363 | KNIT 1364 | KNOB 1365 | KNOT 1366 | KNOW 1367 | KOAN 1368 | KOOK 1369 | KOTO 1370 | LABS 1371 | LACE 1372 | LACK 1373 | LACY 1374 | LADS 1375 | LADY 1376 | LAGS 1377 | LAID 1378 | LAIN 1379 | LAIR 1380 | LAKE 1381 | LAMA 1382 | LAMB 1383 | LAME 1384 | LAMP 1385 | LAND 1386 | LANE 1387 | LAPS 1388 | LARD 1389 | LARK 1390 | LASH 1391 | LASS 1392 | LAST 1393 | LATE 1394 | LAUD 1395 | LAVA 1396 | LAWN 1397 | LAWS 1398 | LAYS 1399 | LAZE 1400 | LAZY 1401 | LEAD 1402 | LEAF 1403 | LEAK 1404 | LEAN 1405 | LEAP 1406 | LEAS 1407 | LEEK 1408 | LEER 1409 | LEET 1410 | LEFT 1411 | LEGS 1412 | LEND 1413 | LENS 1414 | LENT 1415 | LESS 1416 | LEST 1417 | LEWD 1418 | LIAR 1419 | LIBS 1420 | LICE 1421 | LICK 1422 | LIDS 1423 | LIED 1424 | LIEN 1425 | LIER 1426 | LIES 1427 | LIEU 1428 | LIFE 1429 | LIFT 1430 | LIKE 1431 | LILT 1432 | LILY 1433 | LIMA 1434 | LIMB 1435 | LIME 1436 | LIMO 1437 | LIMP 1438 | LINE 1439 | LING 1440 | LINK 1441 | LINT 1442 | LION 1443 | LIPS 1444 | LISP 1445 | LIST 1446 | LITE 1447 | LIVE 1448 | LOAD 1449 | LOAF 1450 | LOAM 1451 | LOAN 1452 | LOBE 1453 | LOBS 1454 | LOCH 1455 | LOCK 1456 | LOCO 1457 | LODE 1458 | LOFT 1459 | LOGE 1460 | LOGO 1461 | LOGS 1462 | LOIN 1463 | LONE 1464 | LONG 1465 | LOOK 1466 | LOOM 1467 | LOON 1468 | LOOP 1469 | LOOS 1470 | LOOT 1471 | LOPS 1472 | LORD 1473 | LORE 1474 | LOSE 1475 | LOSS 1476 | LOST 1477 | LOTH 1478 | LOTS 1479 | LOUD 1480 | LOUT 1481 | LOVE 1482 | LOWS 1483 | LUBE 1484 | LUCK 1485 | LUFF 1486 | LUGS 1487 | LULL 1488 | LUMP 1489 | LUNE 1490 | LUNG 1491 | LURE 1492 | LURK 1493 | LUSH 1494 | LUST 1495 | LUTE 1496 | LUVS 1497 | LYNX 1498 | LYRE 1499 | MACE 1500 | MACH 1501 | MACK 1502 | MACS 1503 | MADE 1504 | MAGE 1505 | MAGI 1506 | MAGS 1507 | MAID 1508 | MAIL 1509 | MAIM 1510 | MAIN 1511 | MAKE 1512 | MAKI 1513 | MALE 1514 | MALL 1515 | MALT 1516 | MAMA 1517 | MANE 1518 | MANS 1519 | MANY 1520 | MAPS 1521 | MARE 1522 | MARK 1523 | MARS 1524 | MART 1525 | MASH 1526 | MASK 1527 | MASS 1528 | MAST 1529 | MATE 1530 | MATH 1531 | MATS 1532 | MAUL 1533 | MAWS 1534 | MAXI 1535 | MAYO 1536 | MAZE 1537 | MAZY 1538 | MEAD 1539 | MEAL 1540 | MEAN 1541 | MEAT 1542 | MEEK 1543 | MEET 1544 | MEGA 1545 | MELD 1546 | MELT 1547 | MEMO 1548 | MEND 1549 | MENU 1550 | MEOW 1551 | MERE 1552 | MESA 1553 | MESH 1554 | MESS 1555 | META 1556 | METE 1557 | MEWL 1558 | MEWS 1559 | MICA 1560 | MICE 1561 | MICS 1562 | MIDS 1563 | MILD 1564 | MILE 1565 | MILK 1566 | MILL 1567 | MIME 1568 | MIND 1569 | MINE 1570 | MINI 1571 | MINK 1572 | MINT 1573 | MINX 1574 | MIRE 1575 | MISO 1576 | MISS 1577 | MIST 1578 | MITE 1579 | MOAN 1580 | MOAT 1581 | MOBS 1582 | MOCK 1583 | MODE 1584 | MODS 1585 | MOJO 1586 | MOLD 1587 | MOLE 1588 | MOLT 1589 | MOMS 1590 | MONK 1591 | MONO 1592 | MOOD 1593 | MOON 1594 | MOOR 1595 | MOOS 1596 | MOOT 1597 | MOPE 1598 | MOPS 1599 | MORE 1600 | MORN 1601 | MOSH 1602 | MOSS 1603 | MOST 1604 | MOTE 1605 | MOTH 1606 | MOVE 1607 | MOWS 1608 | MUCH 1609 | MUCK 1610 | MUDS 1611 | MUFF 1612 | MUGS 1613 | MULE 1614 | MULL 1615 | MUMS 1616 | MUON 1617 | MURK 1618 | MUSE 1619 | MUSH 1620 | MUSK 1621 | MUST 1622 | MUTE 1623 | MUTT 1624 | MYTH 1625 | NAAN 1626 | NABS 1627 | NAGS 1628 | NAIL 1629 | NAME 1630 | NANA 1631 | NANS 1632 | NAPE 1633 | NAPS 1634 | NARC 1635 | NARD 1636 | NARY 1637 | NAVY 1638 | NAYS 1639 | NEAR 1640 | NEAT 1641 | NECK 1642 | NEED 1643 | NEEM 1644 | NEON 1645 | NERD 1646 | NESS 1647 | NEST 1648 | NETS 1649 | NEWS 1650 | NEWT 1651 | NEXT 1652 | NIBS 1653 | NICE 1654 | NICK 1655 | NIGH 1656 | NINE 1657 | NITE 1658 | NOBS 1659 | NODE 1660 | NODS 1661 | NOEL 1662 | NOIR 1663 | NONE 1664 | NOOK 1665 | NOON 1666 | NOPE 1667 | NORI 1668 | NORM 1669 | NOSE 1670 | NOSH 1671 | NOSY 1672 | NOTE 1673 | NOUN 1674 | NOVA 1675 | NUBS 1676 | NUDE 1677 | NUKE 1678 | NULL 1679 | NUMB 1680 | NUNS 1681 | NUTS 1682 | OAFS 1683 | OAKS 1684 | OAKY 1685 | OARS 1686 | OATH 1687 | OATS 1688 | OBEY 1689 | OBIT 1690 | OBOE 1691 | ODDS 1692 | ODES 1693 | ODOR 1694 | OGLE 1695 | OGRE 1696 | OILS 1697 | OILY 1698 | OINK 1699 | OKAY 1700 | OKRA 1701 | OLDS 1702 | OMEN 1703 | OMIT 1704 | ONCE 1705 | ONES 1706 | ONLY 1707 | ONTO 1708 | ONUS 1709 | ONYX 1710 | OOPS 1711 | OOZE 1712 | OOZY 1713 | OPAL 1714 | OPEN 1715 | OPTS 1716 | OPUS 1717 | ORAL 1718 | ORBS 1719 | ORCA 1720 | ORES 1721 | ORGY 1722 | ORZO 1723 | OUCH 1724 | OURS 1725 | OUST 1726 | OUTS 1727 | OVAL 1728 | OVEN 1729 | OVER 1730 | OVUM 1731 | OWED 1732 | OWES 1733 | OWLS 1734 | OWNS 1735 | PACE 1736 | PACK 1737 | PACT 1738 | PADS 1739 | PAGE 1740 | PAID 1741 | PAIL 1742 | PAIN 1743 | PAIR 1744 | PALE 1745 | PALL 1746 | PALM 1747 | PALP 1748 | PALS 1749 | PANE 1750 | PANG 1751 | PANS 1752 | PANT 1753 | PAPA 1754 | PAPS 1755 | PARA 1756 | PARE 1757 | PARK 1758 | PARS 1759 | PART 1760 | PASS 1761 | PAST 1762 | PATE 1763 | PATH 1764 | PATS 1765 | PAVE 1766 | PAWN 1767 | PAWS 1768 | PAYS 1769 | PEAK 1770 | PEAL 1771 | PEAR 1772 | PEAS 1773 | PEAT 1774 | PECK 1775 | PECS 1776 | PEED 1777 | PEEK 1778 | PEEL 1779 | PEEP 1780 | PEER 1781 | PEES 1782 | PEGS 1783 | PELT 1784 | PENS 1785 | PENT 1786 | PEON 1787 | PERK 1788 | PERM 1789 | PERP 1790 | PESO 1791 | PEST 1792 | PETS 1793 | PEWS 1794 | PHEW 1795 | PICK 1796 | PICS 1797 | PIED 1798 | PIER 1799 | PIES 1800 | PIGS 1801 | PIKA 1802 | PIKE 1803 | PILE 1804 | PILL 1805 | PINE 1806 | PING 1807 | PINK 1808 | PINS 1809 | PINT 1810 | PIPE 1811 | PISS 1812 | PITA 1813 | PITH 1814 | PITS 1815 | PITY 1816 | PLAN 1817 | PLAT 1818 | PLAY 1819 | PLEA 1820 | PLEB 1821 | PLED 1822 | PLEX 1823 | PLOD 1824 | PLOP 1825 | PLOT 1826 | PLOW 1827 | PLOY 1828 | PLUG 1829 | PLUM 1830 | PLUS 1831 | POCK 1832 | PODS 1833 | POEM 1834 | POET 1835 | POKE 1836 | POKY 1837 | POLE 1838 | POLL 1839 | POLO 1840 | POLY 1841 | POMP 1842 | POND 1843 | PONG 1844 | PONY 1845 | POOF 1846 | POOL 1847 | POOP 1848 | POOR 1849 | POPE 1850 | POPS 1851 | PORE 1852 | PORK 1853 | PORT 1854 | POSE 1855 | POSH 1856 | POSY 1857 | POTS 1858 | POUF 1859 | POUR 1860 | POUT 1861 | PRAM 1862 | PRAY 1863 | PREP 1864 | PREY 1865 | PREZ 1866 | PRIG 1867 | PRIM 1868 | PROB 1869 | PROD 1870 | PROF 1871 | PROG 1872 | PROM 1873 | PROP 1874 | PROS 1875 | PROW 1876 | PUBS 1877 | PUCK 1878 | PUDS 1879 | PUFF 1880 | PUGS 1881 | PUKE 1882 | PULL 1883 | PULP 1884 | PUMA 1885 | PUMP 1886 | PUNK 1887 | PUNS 1888 | PUNT 1889 | PUNY 1890 | PUPA 1891 | PUPS 1892 | PURE 1893 | PURL 1894 | PURR 1895 | PUSH 1896 | PUSS 1897 | PUTS 1898 | PUTT 1899 | PUTZ 1900 | PYRE 1901 | PYRO 1902 | QUAD 1903 | QUID 1904 | QUIP 1905 | QUIT 1906 | QUIZ 1907 | RACE 1908 | RACK 1909 | RACY 1910 | RADS 1911 | RAFT 1912 | RAGA 1913 | RAGE 1914 | RAGS 1915 | RAID 1916 | RAIL 1917 | RAIN 1918 | RAJA 1919 | RAKE 1920 | RAKU 1921 | RALE 1922 | RAMP 1923 | RAMS 1924 | RANG 1925 | RANK 1926 | RANT 1927 | RAPS 1928 | RAPT 1929 | RARE 1930 | RASH 1931 | RASP 1932 | RATE 1933 | RATH 1934 | RATS 1935 | RAVE 1936 | RAYS 1937 | RAZE 1938 | RAZZ 1939 | READ 1940 | REAL 1941 | REAM 1942 | REAP 1943 | REAR 1944 | REDO 1945 | REED 1946 | REEF 1947 | REEK 1948 | REEL 1949 | REFS 1950 | REIN 1951 | RELY 1952 | REMS 1953 | REND 1954 | RENT 1955 | REPO 1956 | REPP 1957 | REPS 1958 | REST 1959 | RIBS 1960 | RICE 1961 | RICH 1962 | RIDE 1963 | RIDS 1964 | RIFE 1965 | RIFF 1966 | RIFT 1967 | RIGS 1968 | RILE 1969 | RILL 1970 | RIME 1971 | RIMS 1972 | RIND 1973 | RING 1974 | RINK 1975 | RIOT 1976 | RIPE 1977 | RIPS 1978 | RISE 1979 | RISK 1980 | RITE 1981 | RITZ 1982 | ROAD 1983 | ROAM 1984 | ROAN 1985 | ROAR 1986 | ROBE 1987 | ROBS 1988 | ROCK 1989 | RODE 1990 | RODS 1991 | ROES 1992 | ROIL 1993 | ROLE 1994 | ROLL 1995 | ROMP 1996 | ROMS 1997 | ROOF 1998 | ROOK 1999 | ROOM 2000 | ROOS 2001 | ROOT 2002 | ROPE 2003 | ROPY 2004 | ROSE 2005 | ROSY 2006 | ROTE 2007 | ROTI 2008 | ROTO 2009 | ROTS 2010 | ROUT 2011 | ROUX 2012 | ROVE 2013 | ROWS 2014 | RUBE 2015 | RUBS 2016 | RUBY 2017 | RUCK 2018 | RUDE 2019 | RUED 2020 | RUES 2021 | RUFF 2022 | RUGS 2023 | RUIN 2024 | RULE 2025 | RUMP 2026 | RUMS 2027 | RUNE 2028 | RUNG 2029 | RUNS 2030 | RUNT 2031 | RUSE 2032 | RUSH 2033 | RUST 2034 | RUTS 2035 | SACK 2036 | SACS 2037 | SAFE 2038 | SAGA 2039 | SAGE 2040 | SAGO 2041 | SAGS 2042 | SAID 2043 | SAIL 2044 | SAKE 2045 | SAKI 2046 | SALE 2047 | SALT 2048 | SAME 2049 | SAND 2050 | SANE 2051 | SANG 2052 | SANK 2053 | SAPS 2054 | SARI 2055 | SASH 2056 | SASS 2057 | SATE 2058 | SAVE 2059 | SAWN 2060 | SAWS 2061 | SAYS 2062 | SCAB 2063 | SCAD 2064 | SCAM 2065 | SCAN 2066 | SCAR 2067 | SCAT 2068 | SCOT 2069 | SCUD 2070 | SCUM 2071 | SEAL 2072 | SEAM 2073 | SEAR 2074 | SEAS 2075 | SEAT 2076 | SECT 2077 | SEED 2078 | SEEK 2079 | SEEM 2080 | SEEN 2081 | SEEP 2082 | SEER 2083 | SEES 2084 | SELF 2085 | SELL 2086 | SEMI 2087 | SEND 2088 | SENT 2089 | SEPT 2090 | SETS 2091 | SEWN 2092 | SEWS 2093 | SHAM 2094 | SHED 2095 | SHEW 2096 | SHIM 2097 | SHIN 2098 | SHIP 2099 | SHIT 2100 | SHIV 2101 | SHOD 2102 | SHOE 2103 | SHOO 2104 | SHOP 2105 | SHOT 2106 | SHOW 2107 | SHUN 2108 | SHUT 2109 | SIBS 2110 | SICK 2111 | SIDE 2112 | SIFT 2113 | SIGH 2114 | SIGN 2115 | SILK 2116 | SILL 2117 | SILO 2118 | SILT 2119 | SINE 2120 | SING 2121 | SINK 2122 | SINS 2123 | SIPS 2124 | SIRE 2125 | SIRS 2126 | SITE 2127 | SITS 2128 | SIZE 2129 | SKEW 2130 | SKID 2131 | SKIM 2132 | SKIN 2133 | SKIP 2134 | SKIS 2135 | SKIT 2136 | SLAB 2137 | SLAG 2138 | SLAM 2139 | SLAP 2140 | SLAT 2141 | SLAW 2142 | SLAY 2143 | SLED 2144 | SLEW 2145 | SLID 2146 | SLIM 2147 | SLIP 2148 | SLIT 2149 | SLOB 2150 | SLOE 2151 | SLOG 2152 | SLOP 2153 | SLOT 2154 | SLOW 2155 | SLUG 2156 | SLUM 2157 | SMOG 2158 | SMUG 2159 | SNAG 2160 | SNAP 2161 | SNIP 2162 | SNIT 2163 | SNOB 2164 | SNOT 2165 | SNOW 2166 | SNUB 2167 | SNUG 2168 | SOAK 2169 | SOAP 2170 | SOAR 2171 | SOBA 2172 | SOBS 2173 | SOCK 2174 | SODA 2175 | SODS 2176 | SOFA 2177 | SOFT 2178 | SOIL 2179 | SOLD 2180 | SOLE 2181 | SOLO 2182 | SOME 2183 | SONG 2184 | SONS 2185 | SOON 2186 | SOOT 2187 | SOPS 2188 | SORE 2189 | SORT 2190 | SOUL 2191 | SOUP 2192 | SOUR 2193 | SOWN 2194 | SOWS 2195 | SOYA 2196 | SPAM 2197 | SPAN 2198 | SPAR 2199 | SPAS 2200 | SPAT 2201 | SPAY 2202 | SPEC 2203 | SPED 2204 | SPEW 2205 | SPIN 2206 | SPIT 2207 | SPOT 2208 | SPRY 2209 | SPUD 2210 | SPUN 2211 | SPUR 2212 | STAB 2213 | STAG 2214 | STAR 2215 | STAT 2216 | STAY 2217 | STEM 2218 | STEP 2219 | STEW 2220 | STIR 2221 | STOP 2222 | STOW 2223 | STUB 2224 | STUD 2225 | STUN 2226 | STYE 2227 | SUBS 2228 | SUCH 2229 | SUCK 2230 | SUED 2231 | SUES 2232 | SUET 2233 | SUIT 2234 | SULK 2235 | SUMO 2236 | SUMP 2237 | SUMS 2238 | SUNG 2239 | SUNK 2240 | SUNS 2241 | SUPS 2242 | SURE 2243 | SURF 2244 | SUSS 2245 | SWAB 2246 | SWAG 2247 | SWAM 2248 | SWAN 2249 | SWAP 2250 | SWAT 2251 | SWAY 2252 | SWIG 2253 | SWIM 2254 | SWUM 2255 | SYNC 2256 | TABS 2257 | TACH 2258 | TACK 2259 | TACO 2260 | TACT 2261 | TAGS 2262 | TAIL 2263 | TAKE 2264 | TALC 2265 | TALE 2266 | TALK 2267 | TALL 2268 | TAME 2269 | TAMP 2270 | TANG 2271 | TANK 2272 | TANS 2273 | TAPE 2274 | TAPS 2275 | TARE 2276 | TARN 2277 | TARO 2278 | TARP 2279 | TARS 2280 | TART 2281 | TASK 2282 | TAUT 2283 | TAXI 2284 | TEAK 2285 | TEAL 2286 | TEAM 2287 | TEAR 2288 | TEAS 2289 | TEAT 2290 | TECH 2291 | TEEM 2292 | TEEN 2293 | TEES 2294 | TEFF 2295 | TELE 2296 | TELL 2297 | TEMP 2298 | TEND 2299 | TENT 2300 | TERM 2301 | TERN 2302 | TEST 2303 | TEXT 2304 | THAN 2305 | THAT 2306 | THAW 2307 | THEE 2308 | THEM 2309 | THEN 2310 | THEY 2311 | THIN 2312 | THIS 2313 | THOU 2314 | THRU 2315 | THUD 2316 | THUG 2317 | THUS 2318 | TICK 2319 | TIDE 2320 | TIDY 2321 | TIED 2322 | TIER 2323 | TIES 2324 | TIFF 2325 | TIKI 2326 | TILE 2327 | TILL 2328 | TILT 2329 | TIME 2330 | TINE 2331 | TING 2332 | TINS 2333 | TINT 2334 | TINY 2335 | TIPS 2336 | TIRE 2337 | TITS 2338 | TOAD 2339 | TODE 2340 | TOED 2341 | TOES 2342 | TOFU 2343 | TOGA 2344 | TOIL 2345 | TOKE 2346 | TOLD 2347 | TOLE 2348 | TOLL 2349 | TOMB 2350 | TOME 2351 | TONE 2352 | TONG 2353 | TOOK 2354 | TOOL 2355 | TOON 2356 | TOOT 2357 | TOPS 2358 | TORE 2359 | TORN 2360 | TORT 2361 | TORY 2362 | TOSS 2363 | TOTE 2364 | TOTS 2365 | TOUR 2366 | TOUT 2367 | TOWN 2368 | TOWS 2369 | TOYS 2370 | TRAM 2371 | TRAP 2372 | TRAY 2373 | TREE 2374 | TREK 2375 | TREY 2376 | TRIG 2377 | TRIM 2378 | TRIO 2379 | TRIP 2380 | TROD 2381 | TROT 2382 | TSAR 2383 | TUBA 2384 | TUBE 2385 | TUBS 2386 | TUCK 2387 | TUFF 2388 | TUFT 2389 | TUGS 2390 | TUMS 2391 | TUNA 2392 | TUNE 2393 | TURD 2394 | TURF 2395 | TURK 2396 | TURN 2397 | TUSH 2398 | TUSK 2399 | TUTS 2400 | TUTU 2401 | TWEE 2402 | TWIG 2403 | TWIN 2404 | TWIT 2405 | TWOS 2406 | TYKE 2407 | TYPE 2408 | TYPO 2409 | UDON 2410 | UGLY 2411 | UMPS 2412 | UNDO 2413 | UNIT 2414 | UNTO 2415 | UPON 2416 | URGE 2417 | URNS 2418 | USED 2419 | USER 2420 | USES 2421 | VAIL 2422 | VAIN 2423 | VAMP 2424 | VANE 2425 | VANS 2426 | VARY 2427 | VASE 2428 | VAST 2429 | VATS 2430 | VEAL 2431 | VEER 2432 | VEIL 2433 | VEIN 2434 | VEND 2435 | VENT 2436 | VERB 2437 | VERT 2438 | VERY 2439 | VEST 2440 | VETO 2441 | VETS 2442 | VIAL 2443 | VIBE 2444 | VICE 2445 | VIDS 2446 | VIED 2447 | VIES 2448 | VIEW 2449 | VILE 2450 | VIMS 2451 | VINE 2452 | VINO 2453 | VISA 2454 | VISE 2455 | VITA 2456 | VIVA 2457 | VOID 2458 | VOLE 2459 | VOLT 2460 | VOTE 2461 | VOWS 2462 | WACK 2463 | WADE 2464 | WADS 2465 | WAFT 2466 | WAGE 2467 | WAGS 2468 | WAIF 2469 | WAIL 2470 | WAIT 2471 | WAKE 2472 | WALK 2473 | WALL 2474 | WAND 2475 | WANE 2476 | WANT 2477 | WARD 2478 | WARE 2479 | WARM 2480 | WARN 2481 | WARP 2482 | WARS 2483 | WART 2484 | WARY 2485 | WASH 2486 | WASP 2487 | WATT 2488 | WAVE 2489 | WAVY 2490 | WAXY 2491 | WAYS 2492 | WEAK 2493 | WEAN 2494 | WEAR 2495 | WEBS 2496 | WEDS 2497 | WEED 2498 | WEEK 2499 | WEEN 2500 | WEEP 2501 | WELL 2502 | WENT 2503 | WERE 2504 | WEST 2505 | WHAT 2506 | WHEN 2507 | WHOM 2508 | WIDE 2509 | WIFE 2510 | WILD 2511 | WILL 2512 | WILT 2513 | WIND 2514 | WINE 2515 | WING 2516 | WINS 2517 | WIPE 2518 | WIRE 2519 | WISE 2520 | WISH 2521 | WITH 2522 | WOLF 2523 | WOOD 2524 | WOOL 2525 | WORD 2526 | WORE 2527 | WORK 2528 | WORM 2529 | WORN 2530 | WRAP 2531 | YARD 2532 | YARN 2533 | YEAH 2534 | YEAR 2535 | YOGA 2536 | YOUR 2537 | ZERO 2538 | ZINC 2539 | ZONE 2540 | ZOOM`; 2541 | 2542 | const words = wordsString.split("\n"); 2543 | // console.log(words); 2544 | -------------------------------------------------------------------------------- /Source/Elements/Weird.js: -------------------------------------------------------------------------------- 1 | SpaceTode` 2 | 3 | element RainbowSand { 4 | category "Weird" 5 | 6 | data ready false 7 | given i (self) => !self.ready 8 | keep i (space, self, time) => { 9 | const colour = new THREE.Color("hsl(" + time + ", 100%, 50%)") 10 | self.colour.r = colour.r * 255 11 | self.colour.g = colour.g * 255 12 | self.colour.b = colour.b * 255 13 | self.emissive.r = colour.r * 255 14 | self.emissive.g = colour.g * 255 15 | self.emissive.b = colour.b * 255 16 | self.ready = true 17 | SPACE.update(space) 18 | } 19 | action i => i 20 | 21 | any(xz.directions) { 22 | @ => _ 23 | _ @ 24 | 25 | @ => _ 26 | _ @ 27 | } 28 | } 29 | 30 | element RainglowSand { 31 | category "Weird" 32 | 33 | prop colours { 34 | const arr = [] 35 | for (let i = 0; i < 360; i++) { 36 | const colour = new THREE.Color("hsl(" + i + ", 100%, 50%)") 37 | const cs = [colour.r, colour.g, colour.b].map(c => Math.floor(c * 255)) 38 | arr.push(cs) 39 | 40 | } 41 | return arr 42 | } 43 | data ready false 44 | data offset 0 45 | keep i (space, self, time, Self) => { 46 | if (!self.ready) { 47 | self.offset = time 48 | self.ready = true 49 | } 50 | const [r, g, b] = Self.colours[(time + self.offset) % 360] 51 | self.colour.r = r 52 | self.colour.g = g 53 | self.colour.b = b 54 | self.emissive.r = r 55 | self.emissive.g = g 56 | self.emissive.b = b 57 | SPACE.update(space) 58 | } 59 | action @ => i 60 | 61 | any(xz.directions) { 62 | @ => _ 63 | _ @ 64 | 65 | @ => _ 66 | _ @ 67 | 68 | @ => @ 69 | } 70 | } 71 | 72 | element Glooper { 73 | 74 | colour "rgb(110, 120, 200)" 75 | category "Weird" 76 | 77 | arg direction "down" 78 | arg directionTime 0 79 | 80 | keep c (self) => { 81 | if (self.direction == "down") { 82 | self.colour.r = 110 83 | self.colour.g = 120 84 | self.colour.b = 200 85 | } 86 | else { 87 | self.colour.r = 110 88 | self.colour.g = 200 89 | self.colour.b = 1 90 | } 91 | } 92 | action @ => c 93 | 94 | keep u (self, time) => { 95 | self.direction = "up" 96 | self.directionTime = time 97 | } 98 | action { 99 | @ => u 100 | x . 101 | } 102 | 103 | keep d (self, time) => { 104 | self.direction = "down" 105 | self.directionTime = time 106 | } 107 | action { 108 | x . 109 | @ => d 110 | } 111 | 112 | given D (Self, element, self, atom) => { 113 | return Self === element && self.directionTime < atom.directionTime 114 | } 115 | keep D (self, atom) => { 116 | self.direction = atom.direction 117 | self.directionTime = atom.directionTime 118 | } 119 | for(xyz.directions) { 120 | @D => .D 121 | } 122 | 123 | for(xyz.directions) { 124 | @_$ => _@. 125 | } 126 | 127 | for(xyz.rotations) { 128 | @ => _ 129 | _$ @. 130 | } 131 | 132 | action @ => < 133 | for(xyz.directions) { 134 | action @$ => >. 135 | } 136 | < => _ 137 | 138 | given d (self, element) => element === Empty && self.direction == "down" 139 | // set to 0.2 to make it move down 140 | maybe(0.2) { 141 | @ => _ 142 | d @ 143 | } 144 | 145 | given u (self, element) => element === Empty && self.direction == "up" 146 | // set to 0.2 to make it move up 147 | maybe(0.2) { 148 | u => @ 149 | @ _ 150 | } 151 | 152 | any(xz.directions) { 153 | @ => _ 154 | $d .@ 155 | 156 | $u .@ 157 | @ => _ 158 | } 159 | 160 | } 161 | 162 | element AntiGlooper { 163 | 164 | colour "rgb(200, 120, 110)" 165 | category "Weird" 166 | 167 | arg direction "down" 168 | arg directionTime 0 169 | 170 | symbol G Glooper 171 | symbol E Eater 172 | 173 | for(xyz.directions) { 174 | @G => EE 175 | } 176 | 177 | mimic(Glooper) 178 | 179 | } 180 | 181 | element GloopGridder { 182 | 183 | colour "rgb(50, 150, 50)" 184 | category "Weird" 185 | 186 | arg timer 300 187 | symbol S Glooper 188 | 189 | given t (self) => self.timer-- <= 0 190 | t => S 191 | 192 | change G (self, Self) => new Self(self.timer) 193 | 194 | any(xyz.directions) { 195 | maybe(0.1) @_ => @G 196 | @$ => _@ 197 | @_ => _@ 198 | } 199 | 200 | } 201 | 202 | element UpSignal { 203 | 204 | symbol G Glooper 205 | keep u (atom, time) => { 206 | atom.direction = "up" 207 | atom.directionTime = time 208 | } 209 | //category "T2Tile" 210 | 211 | @ => _ 212 | _ @ 213 | 214 | @ => _ 215 | G u 216 | 217 | @ => _ 218 | } 219 | 220 | 221 | element DownSignal { 222 | 223 | symbol G Glooper 224 | keep d (atom, time) => { 225 | atom.direction = "down" 226 | atom.directionTime = time 227 | } 228 | //category "T2Tile" 229 | 230 | @ => _ 231 | _ @ 232 | 233 | 234 | @ => _ 235 | G d 236 | 237 | @ => _ 238 | } 239 | 240 | 241 | 242 | `; 243 | -------------------------------------------------------------------------------- /Source/Engine/Dropper.js: -------------------------------------------------------------------------------- 1 | //======// 2 | // Drop // 3 | //======// 4 | const DROPPER = {}; 5 | let UIstarted = false; 6 | let DROPPER_LOCATION = "default"; 7 | let DROPPER_SHADOW = true; 8 | let MAX_DROPPER = 2; 9 | let MAX_SHADOW = 21; 10 | 11 | let DROPPER_POUR = "default"; 12 | let DROPPER_HEIGHT = 5; 13 | 14 | let DROPPER_ARGS_NEEDS_EVAL = false; 15 | let DROPPER_ARGS_SOURCE = ""; 16 | let DROPPER_ARGS = []; 17 | 18 | let DROPPER_OVERRIDE = false; 19 | 20 | { 21 | //===========// 22 | // Constants // 23 | //===========// 24 | const SPREAD_CHANCE = 1; 25 | 26 | //=========// 27 | // Globals // 28 | //=========// 29 | let previousPosition; 30 | let down; 31 | 32 | const dropperShadowMaterial = new THREE.MeshLambertMaterial({ 33 | transparent: false, 34 | opacity: 0.5, 35 | //flatShading: true, 36 | side: THREE.DoubleSide, 37 | }); 38 | const dropperShadowGeometry = new THREE.BoxBufferGeometry( 39 | 1 * ATOM_SIZE * ATOM_SCALE, 40 | 1 * ATOM_SIZE * ATOM_SCALE, 41 | 1 * ATOM_SIZE * ATOM_SCALE 42 | ); 43 | const dropperShadow = []; 44 | 45 | for (let i = 0; i < MAX_SHADOW * MAX_SHADOW; i++) { 46 | dropperShadow[i] = new THREE.Mesh( 47 | dropperShadowGeometry, 48 | dropperShadowMaterial 49 | ); 50 | } 51 | 52 | DROPPER.refreshShadows = () => { 53 | const cutoff = MAX_DROPPER * 2 + 1; 54 | for (let i = cutoff; i < MAX_SHADOW * MAX_SHADOW; i++) { 55 | dropperShadow[i].visible = false; 56 | } 57 | }; 58 | 59 | //========// 60 | // Public // 61 | //========// 62 | DROPPER.tryDrop = (position) => { 63 | if (!UIstarted && !Mouse.down && Touches.length == 0) return; 64 | if (UI.selectedElement === undefined) return false; 65 | if (UI.clicking) return; 66 | 67 | if (position == undefined) { 68 | previousPosition = undefined; 69 | for (const shadow of dropperShadow) { 70 | shadow.visible = false; 71 | } 72 | return; 73 | } 74 | 75 | position.x /= ATOM_SIZE; 76 | position.y /= ATOM_SIZE; 77 | position.z /= ATOM_SIZE; 78 | 79 | if (position.y < 0) position.y = 0; 80 | 81 | if (DROPPER_SHADOW) { 82 | const x = Math.round(position.x); 83 | const y = Math.round(position.y); 84 | const z = Math.round(position.z); 85 | 86 | dropAtom(x, y, z, 0, true, 0); 87 | if ( 88 | true || 89 | (DROPPER_POUR == "default" && UI.selectedElement.pour) || 90 | DROPPER_POUR == "pour" 91 | ) { 92 | let id = 1; 93 | for (let i = -MAX_DROPPER; i <= MAX_DROPPER; i++) { 94 | for (let j = -MAX_DROPPER; j <= MAX_DROPPER; j++) { 95 | if (i == 0 && j == 0) continue; 96 | dropAtom(x + i, y, z + j, 0, true, id); 97 | id++; 98 | } 99 | } 100 | /*dropAtom(x + 1, y, z, 0, true, 1) 101 | dropAtom(x - 1, y, z, 0, true, 2) 102 | dropAtom(x, y, z + 1, 0, true, 3) 103 | dropAtom(x, y, z - 1, 0, true, 4) 104 | 105 | dropAtom(x + 1, y, z + 1, 0, true, 5) 106 | dropAtom(x + 1, y, z - 1, 0, true, 6) 107 | dropAtom(x - 1, y, z + 1, 0, true, 7) 108 | dropAtom(x - 1, y, z - 1, 0, true, 8)*/ 109 | } 110 | } 111 | 112 | const previousDown = down; 113 | down = Mouse.down; 114 | if (Touches.length == 1) { 115 | down = true; 116 | } 117 | 118 | if ( 119 | !(DROPPER_POUR == "default" && UI.selectedElement.pour) || 120 | DROPPER_POUR == "pour" 121 | ) { 122 | if (!position) return; 123 | if (down && !previousDown) { 124 | const x = Math.round(position.x); 125 | const y = Math.round(position.y); 126 | const z = Math.round(position.z); 127 | dropAtom(x, y, z); 128 | //if (!UI.selectedElement.pour) return 129 | let id = 1; 130 | for (let i = -MAX_DROPPER; i <= MAX_DROPPER; i++) { 131 | for (let j = -MAX_DROPPER; j <= MAX_DROPPER; j++) { 132 | if (i == 0 && j == 0) continue; 133 | dropAtom(x + i, y, z + j); 134 | id++; 135 | } 136 | } 137 | /*if (Math.random() < SPREAD_CHANCE) dropAtom(x + 1, y, z) 138 | if (Math.random() < SPREAD_CHANCE) dropAtom(x - 1, y, z) 139 | if (Math.random() < SPREAD_CHANCE) dropAtom(x, y, z + 1) 140 | if (Math.random() < SPREAD_CHANCE) dropAtom(x, y, z - 1) 141 | 142 | if (Math.random() < SPREAD_CHANCE) dropAtom(x + 1, y, z + 1) 143 | if (Math.random() < SPREAD_CHANCE) dropAtom(x + 1, y, z - 1) 144 | if (Math.random() < SPREAD_CHANCE) dropAtom(x - 1, y, z + 1) 145 | if (Math.random() < SPREAD_CHANCE) dropAtom(x - 1, y, z - 1)*/ 146 | //if (Math.random() < SPREAD_CHANCE) dropAtom(x, y + 1, z) 147 | //if (Math.random() < SPREAD_CHANCE) dropAtom(x, y - 1, z) 148 | } 149 | return; 150 | } 151 | 152 | if (!down || !position) { 153 | previousPosition = undefined; 154 | return; 155 | } 156 | 157 | //if (position.y < 0) position.y = 0 158 | if (previousPosition == undefined) { 159 | previousPosition = position; 160 | return; 161 | } 162 | 163 | const xDiff = position.x - previousPosition.x; 164 | const zDiff = position.z - previousPosition.z; 165 | const yDiff = position.y - previousPosition.y; 166 | 167 | /*if (xDiff == 0 && zDiff == 0 && yDiff == 0) { 168 | 169 | const xNew = Math.round(position.x) 170 | const yNew = Math.round(position.y) 171 | const zNew = Math.round(position.z) 172 | 173 | dropAtom(xNew, yNew, zNew) 174 | previousPosition = position 175 | return 176 | }*/ 177 | 178 | const xAbs = Math.abs(xDiff); 179 | const zAbs = Math.abs(zDiff); 180 | const yAbs = Math.abs(yDiff); 181 | 182 | let largest = Math.max(xAbs, zAbs, yAbs); 183 | if (largest == 0) largest = 0.00001; 184 | 185 | const xRatio = xAbs / largest; 186 | const zRatio = zAbs / largest; 187 | const yRatio = yAbs / largest; 188 | 189 | const xWay = Math.sign(xDiff); 190 | const zWay = Math.sign(zDiff); 191 | const yWay = Math.sign(yDiff); 192 | 193 | const xInc = xWay * xRatio; 194 | const zInc = zWay * zRatio; 195 | const yInc = yWay * yRatio; 196 | 197 | for (const i of largest) { 198 | const xNew = Math.round(position.x - xInc * i); 199 | const zNew = Math.round(position.z - zInc * i); 200 | const yNew = Math.round(position.y - yInc * i); 201 | 202 | if (Math.random() < 1) dropAtom(xNew, yNew, zNew); 203 | if ( 204 | !( 205 | (DROPPER_POUR == "default" && UI.selectedElement.pour) || 206 | DROPPER_POUR == "pour" 207 | ) 208 | ) 209 | continue; 210 | for (let i = -MAX_DROPPER; i <= MAX_DROPPER; i++) { 211 | for (let j = -MAX_DROPPER; j <= MAX_DROPPER; j++) { 212 | if (i == 0 && j == 0) continue; 213 | dropAtom(xNew + i, yNew, zNew + j); 214 | } 215 | } 216 | /*if (Math.random() < SPREAD_CHANCE) dropAtom(xNew + 1, yNew, zNew) 217 | if (Math.random() < SPREAD_CHANCE) dropAtom(xNew - 1, yNew, zNew) 218 | if (Math.random() < SPREAD_CHANCE) dropAtom(xNew, yNew, zNew + 1) 219 | if (Math.random() < SPREAD_CHANCE) dropAtom(xNew, yNew, zNew - 1) 220 | 221 | if (Math.random() < SPREAD_CHANCE) dropAtom(xNew + 1, yNew, zNew + 1) 222 | if (Math.random() < SPREAD_CHANCE) dropAtom(xNew + 1, yNew, zNew - 1) 223 | if (Math.random() < SPREAD_CHANCE) dropAtom(xNew - 1, yNew, zNew + 1) 224 | if (Math.random() < SPREAD_CHANCE) dropAtom(xNew - 1, yNew, zNew - 1)*/ 225 | //if (Math.random() < SPREAD_CHANCE) dropAtom(xNew, yNew + 1, zNew) 226 | //if (Math.random() < SPREAD_CHANCE) dropAtom(xNew, yNew - 1, zNew) 227 | } 228 | 229 | previousPosition = position; 230 | }; 231 | 232 | //=========// 233 | // Private // 234 | //=========// 235 | // This function is the messy result of adding one line of code every two weeks without much thought 236 | let dropperShadowReady = [false].repeated(9); 237 | const dropAtom = ( 238 | x, 239 | y, 240 | z, 241 | yOffset = 0, 242 | justShow = false, 243 | shadowNumber = 0, 244 | yOverride = Math.floor(DROPPER_HEIGHT) 245 | ) => { 246 | if (!UI) return; 247 | if (UI.selectedElement === undefined) return; 248 | const atomType = UI.selectedElement; 249 | const dropStart = MAX_Y - yOverride; 250 | let alteredY = dropStart + yOffset; 251 | let alteredZ = z; 252 | if (D2_MODE) alteredY = y; 253 | if (D2_MODE) alteredZ = 0; 254 | if (D1_MODE) alteredY = 0; 255 | const space = WORLD.selectSpace(world, x, alteredY, alteredZ); 256 | if (space.atom.element == Void) { 257 | if (justShow) dropperShadow[shadowNumber].visible = false; 258 | return; 259 | } 260 | if ( 261 | !D2_MODE && 262 | !D1_MODE && 263 | !DROPPER_OVERRIDE && 264 | space.atom.element != Empty 265 | ) { 266 | if (atomType != space.atom.element) 267 | return dropAtom(x, y, z, yOffset + 1, justShow, shadowNumber); 268 | } 269 | if (!justShow) { 270 | if (atomType === space.atom.element) return; 271 | if (DROPPER_ARGS_NEEDS_EVAL) { 272 | DROPPER_ARGS = JS("[" + DROPPER_ARGS_SOURCE + "]"); 273 | DROPPER_ARGS_NEEDS_EVAL = false; 274 | } 275 | const atom = new atomType(...DROPPER_ARGS); 276 | SPACE.setAtom(space, atom, atomType); 277 | } else { 278 | if (!dropperShadowReady[shadowNumber]) { 279 | scene.add(dropperShadow[shadowNumber]); 280 | dropperShadowReady[shadowNumber] = true; 281 | } 282 | 283 | dropperShadow[shadowNumber].visible = true; 284 | dropperShadow[shadowNumber].position.set( 285 | x * ATOM_SIZE, 286 | alteredY * ATOM_SIZE, 287 | alteredZ * ATOM_SIZE 288 | ); 289 | dropperShadow[shadowNumber].material.emissive.set(atomType.emissive); 290 | dropperShadow[shadowNumber].material.color.set(atomType.colour); 291 | dropperShadow[shadowNumber].material.opacity = 0.35; 292 | } 293 | }; 294 | } 295 | -------------------------------------------------------------------------------- /Source/Engine/Floor.js: -------------------------------------------------------------------------------- 1 | //=======// 2 | // Floor // 3 | //=======// 4 | { 5 | 6 | const floorMaterial = DARK_MODE? new THREE.MeshLambertMaterial({color: "rgb(45, 45, 61)", emissive: "rgb(45, 45, 61)"}) : new THREE.MeshLambertMaterial({color: "white", emissive: "grey"}) 7 | const floorMaterials = [ 8 | floorMaterial, 9 | floorMaterial, 10 | floorMaterial, 11 | floorMaterial, 12 | floorMaterial, 13 | floorMaterial, 14 | ] 15 | 16 | const noFloorMaterial = new THREE.MeshLambertMaterial({color: "white", emissive: "grey", opacity: 0.0, transparent: true}) 17 | const noFloorMaterials = [ 18 | noFloorMaterial, 19 | noFloorMaterial, 20 | noFloorMaterial, 21 | noFloorMaterial, 22 | noFloorMaterial, 23 | noFloorMaterial, 24 | ] 25 | 26 | makeFloor = (FLOOR_TYPE, width, depth) => { 27 | const floorGeometry = new THREE.BoxGeometry(width, ATOM_SIZE, depth) 28 | const floor = new THREE.Mesh(floorGeometry, FLOOR_TYPE == "floor"? floorMaterials : noFloorMaterials) 29 | floor.position.set(0, -1 * ATOM_SIZE, 0) 30 | floor.castShadow = false 31 | floor.receiveShadow = SHADOW_MODE 32 | return floor 33 | } 34 | 35 | make2DFloor = (FLOOR_TYPE, width, height) => { 36 | const floorGeometry = new THREE.BoxGeometry(width, height, ATOM_SIZE) 37 | const floor = new THREE.Mesh(floorGeometry, floorMaterials) 38 | floor.position.set(0, height / 2, -1 * ATOM_SIZE) 39 | return floor 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /Source/Engine/Lighting.js: -------------------------------------------------------------------------------- 1 | //==========// 2 | // Lighting // 3 | //==========// 4 | function makeSun(is2D = false) { 5 | const sun = new THREE.DirectionalLight() 6 | sun.position.set(-200, 400, 100) 7 | 8 | if (SHADOW_MODE) { 9 | sun.castShadow = true 10 | sun.shadow.mapSize.width = 1024 11 | sun.shadow.mapSize.height = 1024 12 | sun.shadow.camera.near = 450 13 | sun.shadow.camera.far = 475 14 | sun.shadow.bias = -0.0004 15 | } 16 | 17 | //const helper = new THREE.CameraHelper(sun.shadow.camera) 18 | //scene.add(helper) 19 | 20 | if (is2D) sun.position.set(-200, 100, 400) 21 | return sun 22 | } 23 | 24 | /*function makeBackground() { 25 | const background = new THREE.Color() 26 | background.setHSL(Math.random(), 1, 0.92) 27 | return background 28 | }*/ 29 | 30 | const bgColour = new THREE.Color() 31 | if (!DARK_MODE) bgColour.setHSL(Math.random(), 1, 0.85) 32 | else bgColour.setRGB(13 / 255, 16 / 255, 23 / 255) 33 | //else bgColour.setRGB(45 / 255, 45 / 255, 61 / 255) 34 | const bgColourString = `rgb(${Math.floor(bgColour.r * 255)}, ${Math.floor(bgColour.g * 255)}, ${Math.floor(bgColour.b * 255)})` 35 | 36 | function makeBackground() { 37 | return bgColour 38 | } 39 | 40 | function createScreen() { 41 | //const geo = new THREE.PlaneGeometry(WORLD_WIDTH * ATOM_SIZE, WORLD_HEIGHT * ATOM_SIZE 42 | const geo = new THREE.PlaneGeometry(WORLD_WIDTH, WORLD_HEIGHT) 43 | const mat = new THREE.MeshBasicMaterial({ 44 | color: bgColour, 45 | side: THREE.FrontSide, 46 | }) 47 | 48 | const mesh0 = new THREE.Mesh(geo, mat) 49 | mesh0.frustrumCulled = false 50 | mesh0.position.set ( 51 | mesh0.position.x, 52 | mesh0.position.y + ((WORLD_HEIGHT-1) * ATOM_SIZE) / 2, 53 | mesh0.position.z - ((WORLD_DEPTH) * ATOM_SIZE) / 2, 54 | ) 55 | if (D2_MODE) mesh0.position.z -= ATOM_SIZE 56 | scene.add(mesh0) 57 | 58 | const mesh1 = new THREE.Mesh(geo, mat) 59 | mesh1.frustrumCulled = false 60 | mesh1.position.set ( 61 | mesh1.position.x, 62 | mesh1.position.y + ((WORLD_HEIGHT-1) * ATOM_SIZE) / 2, 63 | mesh1.position.z + ((WORLD_DEPTH) * ATOM_SIZE) / 2, 64 | ) 65 | mesh1.rotateY(Math.PI) 66 | scene.add(mesh1) 67 | 68 | const mesh2 = new THREE.Mesh(geo, mat) 69 | mesh2.frustrumCulled = false 70 | mesh2.position.set ( 71 | mesh2.position.x - ((WORLD_WIDTH) * ATOM_SIZE) / 2, 72 | mesh2.position.y + ((WORLD_HEIGHT-1) * ATOM_SIZE) / 2, 73 | mesh2.position.z, 74 | ) 75 | mesh2.rotateY(Math.PI / 2) 76 | scene.add(mesh2) 77 | 78 | const mesh3 = new THREE.Mesh(geo, mat) 79 | mesh3.frustrumCulled = false 80 | mesh3.position.set ( 81 | mesh3.position.x + ((WORLD_WIDTH) * ATOM_SIZE) / 2, 82 | mesh3.position.y + ((WORLD_HEIGHT-1) * ATOM_SIZE) / 2, 83 | mesh3.position.z, 84 | ) 85 | mesh3.rotateY(-Math.PI / 2) 86 | scene.add(mesh3) 87 | 88 | } -------------------------------------------------------------------------------- /Source/Engine/Random.js: -------------------------------------------------------------------------------- 1 | 2 | const createRandomSpaceIds = (spaceCount, buffer) => { 3 | crypto.getRandomValues(buffer) 4 | } 5 | 6 | -------------------------------------------------------------------------------- /Source/Engine/ShuffleWorker.js: -------------------------------------------------------------------------------- 1 | shuffle = (array) => { 2 | postMessage(array.sort(() => Math.random() - 0.5)); 3 | requestAnimationFrame(() => shuffle(array)); 4 | }; 5 | -------------------------------------------------------------------------------- /Source/Engine/Site.js: -------------------------------------------------------------------------------- 1 | //======// 2 | // Site // 3 | //======// 4 | const SITE = {} 5 | 6 | { 7 | 8 | // Site Job Description 9 | //===================== 10 | // "I keep my SPACE." 11 | // "I keep my RELATIVE POSITION." 12 | 13 | //========// 14 | // Public // 15 | //========// 16 | SITE.make = (space, x, y, z) => { 17 | return space 18 | /*const site = { 19 | space, 20 | x, 21 | y, 22 | z, 23 | } 24 | return space*/ 25 | } 26 | 27 | 28 | } -------------------------------------------------------------------------------- /Source/Engine/Space.js: -------------------------------------------------------------------------------- 1 | //=======// 2 | // Space // 3 | //=======// 4 | const SPACE = {} 5 | 6 | { 7 | 8 | // Space Job Description 9 | //====================== 10 | // "I am responsible for my ATOM." 11 | // "I keep a CACHE of stuff to speed up rendering." 12 | 13 | //========// 14 | // Public // 15 | //========// 16 | SPACE.make = (world, id) => { 17 | return { 18 | 19 | // Real Data 20 | atom: new Empty(), 21 | element: Empty, 22 | 23 | // Cached Data 24 | id, 25 | sites: [], //EVENTWINDOW makes this 26 | colourOffset0: id*3 + 0, //not really my job to make this... WORLD should make this 27 | colourOffset1: id*3 + 1, 28 | colourOffset2: id*3 + 2, 29 | 30 | } 31 | } 32 | 33 | const updateAppearanceShadow = (space, _world = world) => { 34 | const atom = space.atom 35 | if (atom == undefined || !atom.visible) { 36 | //WORLD.setSpaceVisible(world, space, false) 37 | WORLD.hideSpace(_world, space) 38 | //WORLD.setSpaceColour(world, space, atom.colour, atom.emissive) 39 | //WORLD.setSpaceOpacity(world, space, 255) 40 | return 41 | } 42 | //WORLD.setSpaceVisible(world, space, true) 43 | WORLD.showSpace(_world, space) 44 | WORLD.setSpaceOpacity(_world, space, atom.opacity) 45 | WORLD.setSpaceColour(_world, space, atom.colour, atom.emissive) 46 | } 47 | 48 | 49 | const updateAppearanceNoShadow = (space, _world = world) => { 50 | const atom = space.atom 51 | if (atom == undefined || !atom.visible) { 52 | WORLD.setSpaceVisible(_world, space, false) 53 | //WORLD.hideSpace(world, space) 54 | //WORLD.setSpaceColour(world, space, atom.colour, atom.emissive) 55 | //WORLD.setSpaceOpacity(world, space, 255) 56 | return 57 | } 58 | WORLD.setSpaceVisible(_world, space, true) 59 | //WORLD.showSpace(world, space) 60 | WORLD.setSpaceOpacity(_world, space, atom.opacity) 61 | WORLD.setSpaceColour(_world, space, atom.colour, atom.emissive) 62 | } 63 | 64 | SPACE.updateAppearance = SHADOW_MODE? updateAppearanceShadow : updateAppearanceNoShadow 65 | SPACE.update = SPACE.updateAppearance 66 | 67 | SPACE.setAtom = (space, atom, element = atom.element) => { 68 | space.atom = atom 69 | space.element = element 70 | SPACE.updateAppearance(space) 71 | } 72 | 73 | SPACE.set = SPACE.setAtom 74 | 75 | } -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | //===========// 2 | // Constants // 3 | //===========// 4 | const urlParams = new URLSearchParams(window.location.search) 5 | 6 | const DOF_MODE = urlParams.has("dof") 7 | const SHADOW_MODE = urlParams.has("shadow") 8 | const SMALL_MODE = urlParams.has("small") || !urlParams.has("big") 9 | const MASSIVE_MODE = urlParams.has("massive") 10 | const D2_MODE = urlParams.has("2d") 11 | const D1_MODE = urlParams.has("1d") 12 | const VR_MODE = urlParams.has("vr") 13 | const TINY_MODE = urlParams.has("tiny")/* | true*/ 14 | const LONG_MODE = urlParams.has("long") 15 | const PURE_RANDOM_MODE = urlParams.has("pure") 16 | const SHUFFLE_MODE = urlParams.has("shuffle") 17 | const FIRING_MODE = urlParams.has("firing") 18 | const MEDIUM_MODE = urlParams.has("medium") 19 | const RIDICULOUS_MODE = urlParams.has("ridiculous") 20 | const NO_SECRET_MODE = urlParams.has("nosecret") 21 | 22 | const DARK_MODE = urlParams.has("dark") 23 | 24 | const DEFAULT_RANDOMNESS_MODE = "track" 25 | const RANDOM = SHUFFLE_MODE? "shuffle" : (PURE_RANDOM_MODE? "pure" : (FIRING_MODE? "firing" : DEFAULT_RANDOMNESS_MODE)) 26 | 27 | const FLOOR_TYPE = urlParams.has("nofloor")? "nofloor" : "floor" 28 | 29 | let MAX_X = (SMALL_MODE? 30 : 50) * (D1_MODE? 2 : 1) * (D2_MODE? 5 : 1) 30 | let MAX_Z = D1_MODE? 0 : (D2_MODE? 0 : MAX_X) 31 | let MAX_Y = D1_MODE? 0 : (SMALL_MODE? 30 : 40) * (D2_MODE? 8 : 1) 32 | 33 | if (TINY_MODE) { 34 | MAX_X = Math.floor(MAX_X * 0.6) 35 | MAX_Z = Math.floor(MAX_Z * 0.6) 36 | MAX_Y = Math.floor(MAX_Y * 0.7) 37 | } 38 | 39 | if (MASSIVE_MODE) { 40 | MAX_X = Math.floor(MAX_X * 2.5) 41 | MAX_Z = Math.floor(MAX_Z * 2.5) 42 | MAX_Y = Math.floor(MAX_Y * 1.5) 43 | } 44 | 45 | if (RIDICULOUS_MODE) { 46 | MAX_X = Math.floor(MAX_X * 3.0) 47 | MAX_Z = Math.floor(MAX_Z * 3.0) 48 | MAX_Y = Math.floor(MAX_Y * 1.5) 49 | } 50 | 51 | if (LONG_MODE) { 52 | MAX_X = Math.floor(MAX_X * 1.3) 53 | MAX_Z = Math.floor(MAX_Z * 1) 54 | MAX_Y = Math.floor(MAX_Y * 0.75) 55 | } 56 | 57 | if (MEDIUM_MODE) { 58 | MAX_X = 40 * (D1_MODE? 2 : 1) * (D2_MODE? 5 : 1) 59 | MAX_Y = 35 * (D2_MODE? 8 : 1) 60 | MAX_Z = D1_MODE? 0 : (D2_MODE? 0 : MAX_X) 61 | } 62 | 63 | let SIZE = SMALL_MODE? "small" : "big" 64 | if (TINY_MODE) SIZE = "tiny" 65 | else if (MASSIVE_MODE) SIZE = "massive" 66 | else if (MEDIUM_MODE) SIZE = "medium" 67 | 68 | 69 | const SHAPE = LONG_MODE? "long" : "cube" 70 | 71 | const MIN_X = -MAX_X 72 | const MIN_Z = -MAX_Z 73 | const MIN_Y = 0 74 | 75 | const WORLD_WIDTH = MAX_X * 2 + 1 76 | const WORLD_DEPTH = MAX_Z * 2 + 1 77 | const WORLD_HEIGHT = MAX_Y 78 | 79 | const WORLD_AREA = { 80 | x: [MIN_X, MAX_X], 81 | y: [MIN_Y, MAX_Y], 82 | z: [MIN_Z, MAX_Z], 83 | } 84 | 85 | 86 | const ATOM_SIZE = 0.01 87 | const ATOM_SCALE = 1.0 88 | 89 | const CAMERA_START_X = 0 90 | let CAMERA_START_Y = (D2_MODE? WORLD_HEIGHT/2 : (SMALL_MODE? 85 : 140)) * ATOM_SIZE 91 | let CAMERA_START_Z = (SMALL_MODE? 125 : 200) * (D2_MODE? 2 : 1) * ATOM_SIZE 92 | 93 | if (MEDIUM_MODE) { 94 | CAMERA_START_Y = CAMERA_START_Y * 1.25 95 | CAMERA_START_Z = CAMERA_START_Z * 1.25 96 | } 97 | 98 | if (MASSIVE_MODE) { 99 | CAMERA_START_Y = CAMERA_START_Y * 2.25 100 | CAMERA_START_Z = CAMERA_START_Z * 2.25 101 | } 102 | 103 | if (TINY_MODE) { 104 | CAMERA_START_Y = CAMERA_START_Y * 0.65 105 | CAMERA_START_Z = CAMERA_START_Z * 0.7 106 | } 107 | 108 | if (D2_MODE) { 109 | //CAMERA_START_Y = CAMERA_START_Y * 2.8 110 | CAMERA_START_Z = CAMERA_START_Z * 2.25 111 | } 112 | 113 | const CAMERA_SPEED = 2 114 | 115 | //window.history.replaceState({}, "", window.location.pathname) 116 | 117 | let SPEED_MOD = urlParams.get("speed") 118 | if (SPEED_MOD === null) SPEED_MOD = "1" 119 | SPEED_MOD = parseFloat(SPEED_MOD) -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | SandPond 4 | 7 | 8 | 9 | 10 | 11 | 12 | 43 | 44 | 45 | 46 |
47 | 48 |

Loading...

49 |
50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 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 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | //=============// 2 | // Stage Setup // 3 | //=============// 4 | const stage = new Stage(document.body, {start: false, shadow: SHADOW_MODE, postProcess: DOF_MODE}) 5 | const {canvas, renderer, scene, camera, raycaster, cursor, dummyCamera} = stage 6 | if (VR_MODE) { 7 | alert("Sorry VR mode is broken at the moment.") 8 | /*renderer.vr.enabled = true 9 | stage.vrEnabled = true 10 | document.body.appendChild(THREE.WEBVR.createButton(renderer))*/ 11 | 12 | /*const gamepads = navigator.getGamepads() 13 | for (const gamepad of gamepads) { 14 | print(gamepad) 15 | } 16 | 17 | window.addEventListener("gamepadconnected", ()=> print("hi"))*/ 18 | 19 | } 20 | 21 | //camera.position.set(0, 1.6, 0) 22 | camera.position.set(CAMERA_START_X, CAMERA_START_Y, CAMERA_START_Z) 23 | camera.lookAt(0, MAX_Y/2 * ATOM_SIZE, 0) 24 | 25 | const background = makeBackground() 26 | //scene.background = background 27 | 28 | const sun = makeSun(D2_MODE) 29 | scene.add(sun) 30 | 31 | const floor = D2_MODE? make2DFloor(FLOOR_TYPE, WORLD_WIDTH * ATOM_SIZE, WORLD_HEIGHT * ATOM_SIZE) : makeFloor(FLOOR_TYPE, WORLD_WIDTH * ATOM_SIZE, WORLD_DEPTH * ATOM_SIZE) 32 | scene.add(floor) 33 | 34 | createScreen() 35 | 36 | let bokehPass = undefined 37 | if (DOF_MODE) { 38 | bokehPass = new THREE.BokehPass(scene, camera, { 39 | maxblur: 0.009, 40 | //focus: 1.1, 41 | aperture: 0.02, 42 | }) 43 | stage.composer.addPass(bokehPass) 44 | } 45 | 46 | let orbit = new THREE.OrbitControls(camera, document.body) 47 | orbit.mouseButtons.LEFT = undefined 48 | orbit.mouseButtons.MIDDLE = THREE.MOUSE.PAN 49 | orbit.mouseButtons.RIGHT = THREE.MOUSE.ROTATE 50 | orbit.touches.ONE = undefined 51 | orbit.touches.TWO = THREE.TOUCH.DOLLY_ROTATE 52 | orbit.enableKeys = false 53 | orbit.enableDamping = true 54 | orbit.screenSpacePanning = D2_MODE 55 | orbit.panSpeed = 1.8 56 | orbit.target.set(0, MAX_Y/2 * ATOM_SIZE, 0) 57 | /*on.process(() => { 58 | orbit.update() 59 | //screen.position.set(-camera.position.x, 0, -camera.position.z) 60 | //screen.lookAt(camera) 61 | //screen.rotateY(Math.PI/2) 62 | //screen.rotateX(Math.PI/2) 63 | })*/ 64 | 65 | stage.start() 66 | 67 | //=============// 68 | // World Setup // 69 | //=============// 70 | const world = WORLD.make(WORLD_AREA) 71 | const spaceCount = world.spaces.length 72 | 73 | const MIN_SPACE = 0 74 | const MAX_SPACE = spaceCount - 1 75 | 76 | const spaces = world.spaces 77 | const spacesShuffled = world.spacesShuffled 78 | 79 | let spaceIds = spaces.map(space => space.id).sort(() => Math.random() - 0.5) 80 | 81 | let shuffleWorker = undefined 82 | try { 83 | shuffleWorker = new WorkerProxy("Source/Engine/ShuffleWorker.js") 84 | } 85 | catch { 86 | if (RANDOM == "shuffle") { 87 | const warning = "ERROR: Shuffle worker mode was selected but I couldn't make a web worker...\nIf you see this error, please let @todepond know :)" 88 | alert(warning) 89 | console.error(warning) 90 | } 91 | } 92 | 93 | if (shuffleWorker != undefined && SHUFFLE_MODE) { 94 | shuffleWorker.onmessage = (({data}) => spaceIds = data) 95 | shuffleWorker.shuffle(spaceIds) 96 | } 97 | 98 | $("#loading").innerHTML = "" 99 | 100 | //=======// 101 | // Stuff // 102 | //=======// 103 | let listenToMouse = 1 104 | on.touchstart(() => listenToMouse = -1) 105 | on.touchend(() => listenToMouse = -1) 106 | on.touchcancel(() => listenToMouse = -1) 107 | on.mousedown(() => { 108 | if (listenToMouse == -1) listenToMouse = 0 109 | else if (listenToMouse == 0) listenToMouse = 1 110 | }) 111 | const middleOfWorld = new THREE.Vector3(0, 0, 0) 112 | stage.process = (() => { 113 | orbit.update() 114 | const touchPosition3D = stage.getTouchPosition3D(0, (mesh) => mesh == floor) 115 | if (touchPosition3D != undefined) { 116 | DROPPER.tryDrop(touchPosition3D) 117 | } 118 | else if (listenToMouse >= 1) { 119 | const cursorIntersection = stage.getCursorIntersect((mesh) => mesh == floor) 120 | const cursorPosition3D = cursorIntersection? cursorIntersection.point : undefined 121 | DROPPER.tryDrop(cursorPosition3D) 122 | if (DOF_MODE === true) { 123 | 124 | const cameraDist = camera.position.distanceTo(middleOfWorld) 125 | //bokehPass.uniforms.aperture.value = (1 / cameraDist * 0.05) 126 | 127 | if (cursorPosition3D !== undefined) { 128 | 129 | const x = Math.round(cursorPosition3D.x) 130 | const y = Math.round(cursorPosition3D.y) 131 | const z = Math.round(cursorPosition3D.z) 132 | 133 | let space = WORLD.selectSpace(world, x, y, z) 134 | let i = 0 135 | while (space.element !== Empty && space.element !== Void) { 136 | space = WORLD.selectSpace(world, x, (y + i), z) 137 | i++ 138 | } 139 | const focusPoint = new THREE.Vector3(x * ATOM_SIZE, (y + i) * ATOM_SIZE, z * ATOM_SIZE) 140 | 141 | const dist = cursorIntersection.distance 142 | //const dist = cursorIntersection.distance + (cursorIntersection.distance - camera.position.distanceTo(focusPoint)) 143 | bokehPass.uniforms.focus.value = dist 144 | 145 | //bokehPass.uniforms.aperture.value = 1 / dist * 0.025 146 | //bokehPass.uniforms.maxblur.value = 5 147 | } 148 | } 149 | } 150 | else { 151 | DROPPER.tryDrop(undefined) 152 | } 153 | processWorld() 154 | }) 155 | 156 | function getHeightAtPosition(position) { 157 | print(position) 158 | } 159 | 160 | paused = false 161 | let stepCount = 0 162 | let shuffleCounter = 0 163 | 164 | let currentTrack = true 165 | 166 | if (RANDOM === "shuffle") { 167 | let time = 0 168 | processWorld = (() => { 169 | if (paused) { 170 | if (stepCount <= 0) return 171 | stepCount-- 172 | } 173 | for (let i = 0; i < spaceCount; i++) { 174 | const id = spaceIds[i] 175 | const space = spaces[id] 176 | const element = space.element 177 | if (element === Empty) continue 178 | element.behave(space, element, time) 179 | } 180 | time++ 181 | }) 182 | } 183 | else if (RANDOM === "pure") { 184 | let time = 0 185 | processWorld = (() => { 186 | if (paused) { 187 | if (stepCount <= 0) return 188 | stepCount-- 189 | } 190 | for (let i = 0; i < spaceCount; i++) { 191 | const space = spacesShuffled[i] 192 | const element = space.element 193 | if (element === Empty) continue 194 | element.behave(space, element, time) 195 | } 196 | time++ 197 | }) 198 | } 199 | else if (RANDOM === "track") { 200 | 201 | let time = 0 202 | processWorld = (() => { 203 | if (paused === true) { 204 | if (stepCount <= 0) return 205 | stepCount-- 206 | } 207 | 208 | for (let i = 0; i < spaceCount; i++) { 209 | const space = spaces[i] 210 | const element = space.element 211 | if (element === Empty) continue 212 | const atom = space.atom 213 | if (atom.track === currentTrack) continue 214 | atom.track = currentTrack 215 | element.behave(space, element, time, atom) 216 | } 217 | currentTrack = !currentTrack 218 | time++ 219 | }) 220 | } 221 | else if (RANDOM === "firing") { 222 | let time = 0 223 | 224 | // https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues#Exceptions 225 | let eventCount = 65536 / 4 226 | const idRatio = (1 / 2**32) * spaceCount 227 | const floatEventRatio = spaceCount / eventCount * SPEED_MOD 228 | const eventRatio = Math.ceil(floatEventRatio) 229 | const ids = new Uint32Array(eventCount) 230 | print(floatEventRatio) 231 | if (floatEventRatio < 1) { 232 | eventCount = Math.floor(eventCount * floatEventRatio) 233 | } 234 | 235 | processWorld = (() => { 236 | if (paused === true) { 237 | if (stepCount <= 0) return 238 | stepCount-- 239 | } 240 | 241 | for (let e = 0; e < eventRatio; e++) { 242 | crypto.getRandomValues(ids) 243 | for (let i = 0; i < eventCount; i++) { 244 | const id = Math.floor(ids[i] * idRatio) 245 | const space = spaces[id] 246 | const element = space.element 247 | if (element === Empty) continue 248 | element.behave(space, element, time) 249 | } 250 | } 251 | 252 | time++ 253 | 254 | }) 255 | } 256 | else { 257 | throw new Error(`[SandPond] Unrecognised randomness mode: '${RANDOM}'`) 258 | } 259 | 260 | function measureConcentration(filter = (atom => atom.element != Empty)) { 261 | let atomCount = 0 262 | for (let i = 0; i < spaceCount; i++) { 263 | const space = world.spaces[i] 264 | const atom = space.atom 265 | if (filter(atom)) atomCount++ 266 | } 267 | return `${((atomCount / spaceCount) * 100).toFixed(2)}%` 268 | } 269 | 270 | function measureConcentrationForever(filter = (atom => atom.element != Empty)) { 271 | print(measureConcentration(filter)) 272 | setTimeout(() => measureConcentrationForever(filter), 1000) 273 | } 274 | 275 | --------------------------------------------------------------------------------