night/regex/fall.sed

94 lines
4.0 KiB
Sed

# 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 <http://www.gnu.org/licenses/>.
#
# 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!