27
\$\begingroup\$

Challenge

Inspired by this challenge and the nasty Hurricane Matthew, we will be generating some lightning bolts dynamically.

n = 15:

   \
   /\
  /  \
 /   /
/\  /\
 /  \ \
/   / /\
   /\ \
  / /  \
 /\ \  /\
  /  \ \
 /\  /  
   \
    \
    /\

Input

Positive Integer n determines the depth of the lightning round.

Rules and Constraints

  • / and \ should be used
  • The probability guiding the lightning's direction is as follow:
    • 25% Splits into 2 paths
    • 25% Path reaches dead end
    • 25% Goes Left
    • 25% Goes Right
    • There are a few exceptions regarding overlapping and dead end below:
  • The code should not be deterministic, a new lightning bolt should be generated randomly every time
  • The bolts should not overlap: e.g. if there is already a bolt to the left of the current bolt, the current bolt should either end or go right, but not go left or split (probability still applies, in this case it become 50%end/50%right)
  • If there does not exist another available split path, the path should not end: e.g. in the beginning when there is only 1 path, the path should not end until it splits, also applies when there are multiple paths but all except one path are dead, (probability becomes 33%split/33%left/33%right) your goal is to reach the bottom
  • Whitespaces can be added on the left (all you would need should only be height-1)
  • However you want to generate the bolt is up to you, you can go from bottom up, left to right, etc. As long as all the rules above are satisfied

Another example

n = 10

 \
 /
 \
 /\
  /\
 / /
/\ \
 / /\
 \   \
 /

Hurricane Matthew is apparently shooting red bolts up the sky, called sprites

Stay safe and have fun golfing! Please golf responsibly only when you are in a safe area!!

\$\endgroup\$
  • 7
    \$\begingroup\$ 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
  • 17
    \$\begingroup\$ @EriktheGolfer you are not a true golfer then. \$\endgroup\$ – Blue Oct 7 '16 at 16:51
  • 4
    \$\begingroup\$ I don't believe that "The center-most path should be the one reaching the ground" is consistent with the rest of the random-generation description. For example, it's randomly possible for the original bolt to split twice and then for the middle two bolts to end; how can this possibility be overridden while still preserving the probabilities indicated? \$\endgroup\$ – Greg Martin Oct 7 '16 at 18:19
  • \$\begingroup\$ Also, what happens if (for example) the first two steps are both splits? Then the middle two bolts are touching each other, which seems problematic but also isn't ruled out by the special cases. \$\endgroup\$ – Greg Martin Oct 7 '16 at 18:19
  • \$\begingroup\$ @GregMartin Good point on the center-most part, originally I was hoping it to generate a balanced bolt, but now that I think about it even without that constraint about 50% of the time it should end up somewhere in the middle, a depth of 15 would only have 1-2% chance where the right or left most path lands. I'll remove that rule. And for the 2 steps splitting part, the only thing to prevent for is that no 2 paths should 2 paths join: \/ at any point. \$\endgroup\$ – Zukaberg Oct 7 '16 at 18:31
6
\$\begingroup\$

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

\$\endgroup\$
  • \$\begingroup\$ +1 Neat! you should include this online tester perl -M5.010 main.pl <<< 25, I've been getting some nice outputs! \$\endgroup\$ – Zukaberg Oct 7 '16 at 21:45
  • \$\begingroup\$ Mind explaining a little bit how it works? I'm having too much fun generating them haha, I honestly didn't anticipate such good results. \$\endgroup\$ – Zukaberg Oct 7 '16 at 21:54
  • \$\begingroup\$ Unfortunately, you need to add 3 bytes for -n, because the space and dash count too. The same rule is for command-line arguments. See "Special Invocations", second bullet-point: I count those as a difference in character count to the shortest equivalent invocation without them. \$\endgroup\$ – Erik the Outgolfer Oct 8 '16 at 7:45
  • 1
    \$\begingroup\$ @EriktheGolfer No, +1 is OK because this program works fine from the commandline using -nE which is only 1 character more than -E.(see the article you referenced. This also gets rid of the need for -M5.010) I always present my code as files because it is more convenient, but I always count options like this: If it can be run from the commandline I don't count the space and the dash. If it must be in a file (e.g. because it uses do$0) I do count the space and the dash \$\endgroup\$ – Ton Hospel Oct 8 '16 at 7:59
  • \$\begingroup\$ @TonHospel Oh, I didn't know you used -E. If so, you are good. \$\endgroup\$ – Erik the Outgolfer Oct 8 '16 at 8:02
0
\$\begingroup\$

JavaScript (ES6), 154 bytes

f=(n,r=[],s=" ".repeat(n)+"/",t=s.replace(/ \/|\\ |\\$/g,_=>"  /  \\/\\".substr(Math.random()*8&6,2)))=>n?/^ +$|\\\//.test(t)?f(n,r,s):f(n-1,[...r,t],t):r
<input type="number" min=1 oninput=o.textContent=f(this.value).join`\n`><pre id=o>

I struggled with the implementation until I saw @TonHospel's answer, at which point it just degenerated into a port. Sample output:

         /\
        / /\
       /\   \
        /\   \
         /\  /
          / /\
         / / /\
            / /\
            \   \
             \
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.