Hurricane Matthew and the Lightning Bolts

27

2

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!!

Zukaberg

Posted 2016-10-07T16:33:09.500

Reputation: 451

7Stay 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. – Erik the Outgolfer – 2016-10-07T16:36:10.210

17@EriktheGolfer you are not a true golfer then. – Blue – 2016-10-07T16:51:14.833

4I 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? – Greg Martin – 2016-10-07T18:19:00.643

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. – Greg Martin – 2016-10-07T18:19:47.387

@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. – Zukaberg – 2016-10-07T18:31:24.910

Ok on the first choice. But you should specify exactly what probabilities you want to occur to avoid the \/ situation. – Greg Martin – 2016-10-07T18:47:40.657

@GregMartin Because the order of the creation of the path is flexible as per last rule, i.e. if you create the left path first, then the left side of the split path would have the full probability 25/25/25/25, but then the right path would be limited to 50right/50end. I suppose one could argue that this would conflict with the original probability distribution, but since that rule is at the lowest priority, we would always want to adhere to these overlapping and dead end rules first if that makes sense. – Zukaberg – 2016-10-07T18:58:05.070

I'm sure there is a way of phrasing it so that it makes sense. I'm just pointing out that you need to do so :) – Greg Martin – 2016-10-07T19:10:38.413

Is any amount of whitespace to the left and the right of the bolt allowed ? – Ton Hospel – 2016-10-07T19:45:33.347

@TonHospel The left white spaces should be added such that the line above connects with the next one, however the white spaces to the right are not restricted – Zukaberg – 2016-10-07T20:08:52.907

@Zukaberg I understand that. But can I add the same amount of of whitespace in front (and possibly after) every line so that the whole bolt is shifted to the right. This is relevant for a top down construction so the bolt cannot get too wide – Ton Hospel – 2016-10-07T20:23:58.240

@TonHospel I've added a new rule that the left whitespaces (the amount that the whole structure is shifted to the right) must be less than the width of your structure – Zukaberg – 2016-10-07T20:28:51.803

@Zukaberg That's not very useful since the bolt structure can be only 1 wide if you are (un)lucky. What I'm obviously looking for is starting with as many white spaces as the height of the structure-1 – Ton Hospel – 2016-10-07T20:31:32.330

@TonHospel I suppose it would be reasonable to allow that, the challenge's focus is not on this minor detail, if it means better focus on the actual bolt structure sure I don't mind that – Zukaberg – 2016-10-07T20:42:01.260

Answers

6

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

Ton Hospel

Posted 2016-10-07T16:33:09.500

Reputation: 14 114

+1 Neat! you should include this online tester perl -M5.010 main.pl <<< 25, I've been getting some nice outputs!

– Zukaberg – 2016-10-07T21:45:07.637

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. – Zukaberg – 2016-10-07T21:54:14.263

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.

– Erik the Outgolfer – 2016-10-08T07:45:53.030

1@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 – Ton Hospel – 2016-10-08T07:59:54.083

@TonHospel Oh, I didn't know you used -E. If so, you are good. – Erik the Outgolfer – 2016-10-08T08:02:16.867

0

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:

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

Neil

Posted 2016-10-07T16:33:09.500

Reputation: 95 035