# Render next frame of falling balls with rockets and stuff # # Copyright (C) 2018 Mike Gerwitz # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # A remarkably interactive environment can be created using only regular # expressions and treating the input frame as a character matrix. By # encoding all rules and state into the characters themselves, we can create # a dynamic world using only a series of deterministic finite automata # (DFAs). # # The core concept here is that, if we load the entire frame into the # pattern space so that we can match on newlines, and we know the width of # the frame (every line must be 40 characters), then we can easily match on # the lines above and below by matching 40 characters, +/- some number. # # There is one caveat here that I haven't bothered addressing: if some # movable object is caught up in the pattern of another object (e.g. is on # the same line), then it will not be processed until the following # frame. This could be mitigated by looping using `t', but it's not quite # that simple: can you think of what's needed? # # This script only renders one frame. To use it for an animation, run it # in a loop, or use the provided helper script `animate'. ## # Accumulate all text (including newlines) into pattern space so that # regular expressions can span lines. :a; N; $!ba # A ball is represented by `o' and is moved left or right depending on # what object is underneath it (`<' for left and `>' for right), provided # that there is space to move into. s#o \(.\{40\}>\)# o\1#g s# o\(.\{41\}<\)#o \1#g # Angled walls are represented by `\' and `/' and cause movement at an # angle, again provided that space is available. s#o\(.\{41\}\\\) # \1o#g s#o\(.\{40\}\) /# \1o/#g # Plungers look like `=|' or `|=' when retracted and will push to the # right or left (respectively) when a ball appears next to it. Expanded # plungers will retract if no ball is next to it. The effect is that one # frame will push the ball and then it will retract the next frame. s#=~|#=| #g; s#=|o #=~|o#g s#|~=# |=#g; s# o|=#o|~=#g # A rocket (`^') will explode if there is no room left to push a # ball. Explosions will destroy the character above the ball, the ball # itself, and the rocket. The character above the ball and the ball will # be replaced with smoke (`#'), which will disappear the following frame. s|#| |g s|[^ ]\(.\{41\}\)o\(.\{41\}\)\^|#\1#\2 |g # Rockets leave a trail `:', which needs to be cleaned up in the event of # an explosion if no rocket is above it. s#\([^^:].\{41\}\):#\1 #g # Rockets (^) rise when they have a ball on top of them, but fall # otherwise. They leave behind a trail (`:'), which is used as a guide # for falling back into its original position. s# \(.\{41\}\)o\(.\{41\}\)\^#o\1^\2:#g s#\([^o].\{41\}\)\^\(.\{41\}\):#\1 \2^#g # Finally, balls will fall if there is nothing beneath them. We do this # last so that ball aren't found floating in a frame (e.g. if they fall # off of an edge, they do so immediately, not float over the edge until # the next frame). s#o\(.\{41\}\) # \1o#g # If no substitutions were made, exit with a non-zero status so that the # animation script will stop animating. t q1 # All of the above makes for a pretty interesting animation, but doesn't # allow for a whole lot of creativity with regards to world manipulation; # it lacks useful primitives. That's the topic of future hacks. Have # fun, and happy hacking!