Source Code Metamorphosis

29

6

mbomb007 asked us to make a self mutilating program. Its an interesting challenge, but I personally don't like to encourage my programs to be self-mutilating. I like to think that, at the heart of every program is a beautiful butterfly waiting to break out.

As such, for my first ever Code Golf challenge, I challenge Stack Exchange to metamorph a butterfly. Your program should accept its own code as input and output I am a caterpillar! followed by the code for another program in the same language (you may put a divider between the outputs or not... but we're talking butterflies, so prettiness counts!). This second program should output I am a beautiful butterfly!

Metamorphosis: This program should modify itself, which is why it is being passed itself as input. I don't want a boring nested program that is nothing but a print statement I am a caterpillar! followed by printing source code. You have to metamorph the code itself, so the most boring acceptable answer is one which prints I am a caterpillar, then rips a string out of the input (which happens to be code) and prints that. Of course, this is a very tricky little requirement to write, so it should be thought of as a "spirit of the challenge." The formal requirement is going to be set at "If your program is given a random string of the same length as your program, there should be less than a 5% chance of printing I am a butterfly when the second program is run."

Summary:

  • Write a program which, when provided its own code as input, prints I am a caterpillar! followed by a block of source code (with a delimiter between them or not).
  • This second block of source code, when compiled/interpreted as the same language, prints I am a beautiful butterfly!
  • If your program is not fed its own source code as input, and is instead given a random string of characters that does not match its own code, the result must either fail to compile/interpret, or not print I am a butterfly! for at least 95% of random strings (spirit: you are expected to read your own source code from the input, and stitch it together into a butterfly).
    • I'm comfortable with you not proving this, so long as you're holding to the spirit of the metamorphosis idea, modifying your own code, but if you're trying to play loophole games to work around using your inputs, expect to have to prove it.
  • Standard loopholes apply

I'd like these to be judged by their beauty, but that's a decidedly non-quantitative approach, so the usual "number of bytes" route can be used to judge

Bonus: -20% - pick any 3 letter string that doesn't match the first 3 letters of your program. If I run the caterpillar program, but modify the input so that the first 3 character are changed to the specified ones, print I am Batman! instead. Always be Batman. (Same random string rules apply for this... metamorphosis!)

Trivial example (python):

"""
    print "I am a beautiful butterfly!"
    #print "I am Batman!"
"""
import sys
code = sys.stdin.read()
if code.startswith("YYZ"):
   # swap the comments between the print statements
   code = code.replace("#print", "_a").replace("print", "#print").replace("_a", "print")
code = [3:] # remove """ or XYZ
code = code.split('"""')[0]
print "I am a caterpillar!"
print code

Cort Ammon

Posted 2015-10-07T19:51:52.613

Reputation: 508

As a note: I'd love to see ascii art programs where it looks like a caterpillar the first time, and turns into an ascii butterfly the next phase, but that's quite a bit more advanced than the challenge needs to be. – Cort Ammon – 2015-10-07T19:58:49.023

What should we do if a program has multiple files? – TheNumberOne – 2015-10-07T23:25:23.487

@TheNumberOne I'm curious what you have planned there! I don't know if I want to beat down the rules for that right away, without seeing the consequences, but it seems to me that, if you were to accept a multi-part set of files as input (rather than a single file's contents), and you were to output a multi-part set of outputs (rather than a single output), with a half-reasonable deliniator between the files, I think that should be a reasonable solution. If that doesn't work for you, I may need more insight into your proposed solution to better craft a rule. – Cort Ammon – 2015-10-07T23:47:14.030

@TheNumberOne And thank you for the typo catch. That typo was very clearly not a beautiful butterfly! – Cort Ammon – 2015-10-07T23:48:09.950

Answers

63

Befunge-98, 602 bytes

                                        "^@<"v@@
                                     "'I'"00v>00g::
                                   "@v"0v@@@>g::a">#"
                                 "a">v@@>      0g::'"1>                                 /
                             ":\"'"v@>            'm'"00g:a                       >"@v$"
 "lufituaeb"'"'v00ga"@v\"'":'b\00v@>                :'a\":*84>"00ga"\>@@@@_,#:>"00g:a'<'"
"et"'"'va'v'":'l\00g5k:'""y!">>v@>                    g::'''""ut"'"'>a'<'""fr"'"00g3k:'">
 "ma"00g>'I~48*-~48*-+#@_>:#,_@>                        '"aa"---"aa"!rallipretac"00g:'a\

Tested in PyFunge.

When run with a string not starting with 2 spaces (probability well over 95% when random) outputs nothing. This isn't exactly what the OP is looking for, but fits the specification.

When passed a string starting with 2 spaces (like itself) outputs this:

I am a caterpillar!

---

"!y"       "l"v
v"te"     "rf"<
>"tu"'   "b"\v@
 v"beautiful"<
  >:#,_@@@@>\
 >48*:"a"\:$v@
  "ma"   v@#>
   'I'   <@^

The lower part of that, in turn, outputs:

I am a beautiful butterfly!

PurkkaKoodari

Posted 2015-10-07T19:51:52.613

Reputation: 16 699

2Wow. This is amazing. – a spaghetto – 2015-10-08T00:12:12.413

3I knew someone would want to push the rules just like you did; I'm glad something beautiful came out of it =) – Cort Ammon – 2015-10-08T03:21:57.450

Both caterpillar and butterfly look like they are stuck in the Matrix. Beautiful! – Erno – 2015-10-08T13:07:10.850

where can I run this? – user1514631 – 2015-10-08T13:19:17.027

3I will never understand how you good folks at Code Golf manage to create such beautiful monstrosities. – Chris Cirefice – 2015-10-09T00:08:56.983

19

PHP, 74 Bytes

<?=~¶ßž’ßžß,$s="caterpillar!",strtr($argv[1],[$s=>~šžŠ‹–™Š“ߊ‹‹š™“†Þ]);
  • <?= is equivalent to <?php echo and can take several comma separated values to output
  • ¶ßž’ßžß and šžŠ‹–™Š“ߊ‹‹š™“†Þ are valid constant names in PHP, but because the constants do not exist are treated as string literal. ~ inverts them to "I am a " and "beautiful butterfly!" (saving a byte for one quotation mark each)
  • strtr replaces "caterpillar!" with "a beautiful butterfly!" in the argument

Fabian Schmengler

Posted 2015-10-07T19:51:52.613

Reputation: 1 972

2Care to provide an explanation? – Timwi – 2015-10-07T21:58:35.920

@Timwi no problem, explanation added – Fabian Schmengler – 2015-10-07T22:04:26.050

I fully expected someone to encode "beautiful butterfly" with something like ROT13, but the idea of matching it with PHP's string literal technique is, in the words of Kaylee, shiny! – Cort Ammon – 2015-10-07T23:52:12.013

7

Pyth, 50 46 bytes

"I am a caterpillar!"+<z8"beautiful butterfly!

Explanation

"I am a caterpillar!"        print "I am a caterpillar!"
 z<8                         first 8 chars of input
+   "beautiful butterfly!    add "beautiful butterfly!" to that and print

The resulting source code is

"I am a beautiful butterfly!

Which basically prints the text.

PurkkaKoodari

Posted 2015-10-07T19:51:52.613

Reputation: 16 699

6

Perl 6, 60 56 bytes

say "I am a caterpillar!"; s/\S+\!/beautiful butterfly!/

Requires -p to run properly; I have accounted for this in the byte count. Sadly one of the downsides to Perl 6 is that the syntax is much more strict now...

a spaghetto

Posted 2015-10-07T19:51:52.613

Reputation: 10 647

Caterpillars are vulnerable during metamorphosis. That's why they like to wrap themselves in c̶o̶c̶o̶o̶n̶s̶ variables: $a=caterpillar;say"I am a $a!";s/$a/beautiful butterfly/ (I'm assuming that Perl 6 allows barewords like Perl 5 with no strict 'subs' and s/// interpolates variables on the LHS.) – ThisSuitIsBlackNot – 2015-10-07T22:35:28.900

Huh, that's funny. I tried that and for some reason when I did wc it gave me the same amount of bytes. But upon typing it out again it gave me less bytes. Strange. – a spaghetto – 2015-10-07T23:13:34.363

Oh wait nevermind I remember why I didn't do it. When run the new code it throws an error because of the space between beautiful and butterfly. – a spaghetto – 2015-10-07T23:18:03.243

Ah, I've never used Perl 6 so can't help you there. I really just wanted an excuse to make a witty remark. ;) say"I am a caterpillar!";s/\S+!/beautiful butterfly!/ is shorter and will certainly work in Perl 6. – ThisSuitIsBlackNot – 2015-10-08T00:48:55.810

It throws the error in Perl 5 too. I think it needs quotes around it. And as for the other one, that works but I think it kinda defeats the purpose of the challenge since it replaces anything followed by ! with beautiful butterfly. The challenge says that it should only really replace your source code (Random strings should not work most of the time.) – a spaghetto – 2015-10-08T00:52:22.380

Ah, I see what you're saying. Yes, it does need to be quoted. As for the second approach, I really doubt that more than 5% of random 53-character strings will generate compiling Perl code that also prints I am a beautiful butterfly! (say"I am a + space + 40 non-spaces + !" hardly counts as random). The code reads and modifies itself, so I don't see why it would be against the spirit of the challenge. – ThisSuitIsBlackNot – 2015-10-08T08:57:06.700

1@ThisSuitIsBlackNot It does fit the challenge. The rules were just there to make sure you actually read and modify your code. In the end, the real spirit is trying to let the beautiful butterfly in the heart of the code break free. Beauty, of course, is in the eye of the beholder. We will each find different solutions more or less beautiful than others. As for solutions in PERL, well... let's just say PERL has its own peculiar definition of beautiful, and it suits its purposes just fine ;-) – Cort Ammon – 2015-10-08T20:08:22.943

4

Retina, 53 bytes

caterpillar
beautiful butterfly
^
I am a caterpillar!

Prints out:

I am a caterpillar!beautiful butterfly
beautiful butterfly
^
I am a beautiful butterfly!

Notice that there is no separator between I am a caterpillar! and the new program. The new program expects no input.

TheNumberOne

Posted 2015-10-07T19:51:52.613

Reputation: 10 855

3

bash / awk / cksum - 179.2 169.6 168 bytes

awk '/^xyz/{print"I am Batman!";exit}NF{("cksum<<<'"'"'"substr($0,100))|getline r;if(r!~/^6689751/)exit}{printf"I am a %s%."!NF"s!\n",NF?"caterpillar":"beautiful"," butterfly"}NF&&sub(/!NF/,10){print"echo|"$0}'

Demo:

$ awk '/^xyz/{print"I am Batman!";exit}NF{("cksum<<<'"'"'"substr($0,100))|getline r;if(r!~/^6689751/)exit}{printf"I am a %s%."!NF"s!\n",NF?"caterpillar":"beautiful"," butterfly"}NF&&sub(/!NF/,10){print"echo|"$0}'<<'E'
> awk '/^xyz/{print"I am Batman!";exit}NF{("cksum<<<'"'"'"substr($0,100))|getline r;if(r!~/^6689751/)exit}{printf"I am a %s%."!NF"s!\n",NF?"caterpillar":"beautiful"," butterfly"}NF&&sub(/!NF/,10){print"echo|"$0}'
> E
I am a caterpillar!
echo|awk '/^xyz/{print"I am Batman!";exit}NF{("cksum<<<'"'"'"substr($0,100))|getline r;if(r!~/^6689751/)exit}{printf"I am a %s%."10"s!\n",NF?"caterpillar":"beautiful"," butterfly"}NF&&sub(/!NF/,10){print"echo|"$0}'
$ echo|awk '/^xyz/{print"I am Batman!";exit}NF{("cksum<<<'"'"'"substr($0,100))|getline r;if(r!~/^6689751/)exit}{printf"I am a %s%."10"s!\n",NF?"caterpillar":"beautiful"," butterfly"}NF&&sub(/!NF/,10){print"echo|"$0}'
I am a beautiful butterfly!

# Batman!
$ awk '/^xyz/{print"I am Batman!";exit}NF{("cksum<<<'"'"'"substr($0,100))|getline r;if(r!~/^6689751/)exit}{printf"I am a %s%."!NF"s!\n",NF?"caterpillar":"beautiful"," butterfly"}NF&&sub(/!NF/,10){print"echo|"$0}'<<'E'
> xyzawk '/^xyz/{print"I am Batman!";exit}NF{("cksum<<<'"'"'"substr($0,100))|getline r;if(r!~/^6689751/)exit}{printf"I am a %s%."!NF"s!\n",NF?"caterpillar":"beautiful"," butterfly"}NF&&sub(/!NF/,10){print"echo|"$0}'
> E
I am Batman!

# Invalid input
$ awk '/^xyz/{print"I am Batman!";exit}NF{("cksum<<<'"'"'"substr($0,100))|getline r;if(r!~/^6689751/)exit}{printf"I am a %s%."!NF"s!\n",NF?"caterpillar":"beautiful"," butterfly"}NF&&sub(/!NF/,10){print"echo|"$0}'<<'E'
> awk '/^xyz/{print"I am Batman!";exit}NF{("cksum<<<'"'"'"substr($0,100))|getline r;if(r!~/^6689751/)exit}{printf"I am a %s%."!NF"s!\n",NF?"caterpillar":"beautiful"," butterfly"}NF&&sub(/!NF/,10){print"echo|"$0{'
> E

I hope the 20% applies for any string starting with xyz, otherwise the original count is 224 212 210 bytes (fixed the "I am Batman!" part).

Transformation done: replaces the only occurrence of the literal !NF with 10, so that " butterfly" is also printed using the printf.

Performs a simple cksum on a portion (i.e. the remainder) of the source code, hence its requirement as well.

Caveat: first input must end with '. Not so much a caveat as to suppress wrong input...

No second input is expected.

h.j.k.

Posted 2015-10-07T19:51:52.613

Reputation: 589

1WHOA. I was waiting for the awk person, and that's it. That's pretty cool. – jrg – 2015-10-08T21:19:04.317

@jrg thanks for the encouragement. :) – h.j.k. – 2015-10-09T01:58:11.753

1

Python - 184 bytes -20% for bonus = 147.2

""""""
import sys;c=sys.stdin.read();print"I am a caterpillar!";x=c[3:] if"\'''"!=c[:3] else c
print x+c[:3]
""";print"I am a beautiful butterfly!" """
''';print"I am Batman";'''

To print I am batman, replace the first three double quotes with single quotes in the input.(First line is '''""")

pppery

Posted 2015-10-07T19:51:52.613

Reputation: 3 987