Perl, 92 90 89 84 bytes
Includes +1 for -n
Give height on STDIN:
perl -M5.010 bolt.pl <<< 15
bolt.pl
:
#!/usr/bin/perl -n
map{$_=$;until$;=$_,s/.6|3.?/53|16*rand/eg,/3|6/>/36/;say y|3615|\\/ |r}(1x$_.6)x$_
Explanation
If you call the offset of the starting point 0 (a point is at the corner of a character box), then on the next row you can have gone left or right (or not) and can end up with points on offsets -1,1
. The next row gives -2,0,2
as possible offsets etc. They all differ by 2. If you then call the character to the lower left of a point even and the character to the lower right odd you can extend that to assigning even or odd to each character position on a row such that even and odd alternate (in fact the whole plane is tiled in a checkerboard pattern). An even position can have a /
or
, an odd position can have \
or
.
The character just before a /
is in an odd position so it could be either \
or
, but \/
is forbidden so only
is possible. Similarly the character after a \
must be a
(assuming the row is padded with enough spaces to the left and the right so the row boundaries are no issue). So a lightning bolt continues on the next row always directly below a \
or below a /
. In either case the lower point is in the middle and the next row can have one of
, /
, \
or /\
directly below the top 2 characters. So to generate the next row I can simply replace any \
or /
by any of these 4 expansions with equal probability (you could also independently replace the first character by
or /
and the second character by
or \
). In perl you could do this with something like:
s#\\ | /#(" "," \\","/ ","/\\")[rand 4]#eg
If the resulting row however contains \/
(forbidden join) or no /
or \
at all (bolt dies and does not reach the bottom) the result is invalid. In that case I throw away the whole row and simply try again. A valid continuation always exists and if you try often enough one will be found (e.g everything dies except for 1 flow). This is a slightly different probability distribution from the suggested anti-overlap algorithm, but I think this is in fact better since it has no directional bias. Validity can be testing in a golfish way using
m#\\|/#>m#\\/#
The problem here is that the random substitution is so looooong and all these \
escapes also eat bytes. So I decided to build my rows using strings of digits and replace the appropriate digits by
, /
and \
just before printing. The basic random replacement is
53|16*rand
which gives one of 53
, 55
, 61
or 63
with equal probability. I then interpret 5
and 1
as
, 3
as \
and 6
as /
. That explains the row print:
say y|3615|\\/ |r
In a serious golf competition I would now start to systematically explore alternative magic formulas, but this should be pretty good (within 3 bytes of optimal)
The rest of the components of the program:
1x$_.6
This initializes $_
(see the next map) to height spaces followed by a /
. This is an invisible row above the first one being printed and makes sure the field is wide enough so the bolt can never run out of space at the left
map{ ... ; say ...}(1x$_.6)x$_
I will process this same initial string height times printing a new row each time
$_=$;until$;=$_,...
Save the current row in $;
. If the replacement turns out to be invalid restore $_
from $;
s/.6|3.?/53|16*rand/eg
Do the actual substitution. I don't have to check what is before /
or after \
since it must be a space. This is convenient since space can be represented by either 1
or 5
. Since I only padded the string to the left the space after the \
can still be absent, so make that character optional
/3|6/>/36/
Check if the new row is valid
Stay safe and have fun golfing!
Maybe also specify that if EAS strikes, abandon everything and follow orders! Golfing code is not your priority in such a situation. \$\endgroup\$ – Erik the Outgolfer Oct 7 '16 at 16:36\/
at any point. \$\endgroup\$ – Zukaberg Oct 7 '16 at 18:31