29
\$\begingroup\$

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
\$\endgroup\$
4
  • \$\begingroup\$ 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. \$\endgroup\$
    – Cort Ammon
    Commented Oct 7, 2015 at 19:58
  • \$\begingroup\$ What should we do if a program has multiple files? \$\endgroup\$ Commented Oct 7, 2015 at 23:25
  • \$\begingroup\$ @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. \$\endgroup\$
    – Cort Ammon
    Commented Oct 7, 2015 at 23:47
  • \$\begingroup\$ @TheNumberOne And thank you for the typo catch. That typo was very clearly not a beautiful butterfly! \$\endgroup\$
    – Cort Ammon
    Commented Oct 7, 2015 at 23:48

9 Answers 9

63
\$\begingroup\$

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!
\$\endgroup\$
5
  • 2
    \$\begingroup\$ Wow. This is amazing. \$\endgroup\$ Commented Oct 8, 2015 at 0:12
  • 3
    \$\begingroup\$ I knew someone would want to push the rules just like you did; I'm glad something beautiful came out of it =) \$\endgroup\$
    – Cort Ammon
    Commented Oct 8, 2015 at 3:21
  • \$\begingroup\$ Both caterpillar and butterfly look like they are stuck in the Matrix. Beautiful! \$\endgroup\$
    – Emond
    Commented Oct 8, 2015 at 13:07
  • \$\begingroup\$ where can I run this? \$\endgroup\$ Commented Oct 8, 2015 at 13:19
  • 3
    \$\begingroup\$ I will never understand how you good folks at Code Golf manage to create such beautiful monstrosities. \$\endgroup\$ Commented Oct 9, 2015 at 0:08
18
\$\begingroup\$

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
\$\endgroup\$
3
  • 2
    \$\begingroup\$ Care to provide an explanation? \$\endgroup\$
    – Timwi
    Commented Oct 7, 2015 at 21:58
  • \$\begingroup\$ @Timwi no problem, explanation added \$\endgroup\$ Commented Oct 7, 2015 at 22:04
  • \$\begingroup\$ 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! \$\endgroup\$
    – Cort Ammon
    Commented Oct 7, 2015 at 23:52
7
\$\begingroup\$

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.

\$\endgroup\$
6
\$\begingroup\$

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

\$\endgroup\$
7
  • \$\begingroup\$ 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.) \$\endgroup\$ Commented Oct 7, 2015 at 22:35
  • \$\begingroup\$ 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. \$\endgroup\$ Commented Oct 7, 2015 at 23:13
  • \$\begingroup\$ 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. \$\endgroup\$ Commented Oct 7, 2015 at 23:18
  • \$\begingroup\$ 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. \$\endgroup\$ Commented Oct 8, 2015 at 0:48
  • 1
    \$\begingroup\$ @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 ;-) \$\endgroup\$
    – Cort Ammon
    Commented Oct 8, 2015 at 20:08
4
\$\begingroup\$

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.

\$\endgroup\$
3
\$\begingroup\$

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.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ WHOA. I was waiting for the awk person, and that's it. That's pretty cool. \$\endgroup\$
    – jrg
    Commented Oct 8, 2015 at 21:19
  • \$\begingroup\$ @jrg thanks for the encouragement. :) \$\endgroup\$
    – h.j.k.
    Commented Oct 9, 2015 at 1:58
1
\$\begingroup\$

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 '''""")

\$\endgroup\$
0
\$\begingroup\$

FMSLogo, 169.6 bytes

from 212 * 0.8

to f[:b]1
type[I am]type char 32 ifelse name? "b[make "b last :b
print[a caterpillar!;beautiful butterfly
](foreach[23 8 19 2][10 32 13 97][repeat ?2[ignore pop "b]repeat ?1[type pop "b]])][print[Batman!]]print "

The online logo interpreters I know of do not support every logo feature this program is using. calormen.com, for example, does not support anonymous functions. The program works on FMSLogo 8.3.2.

Luckily for readability, the newlines are either necessary or have no cost. The program, in caterpillar form, is a variadic function which defaults to 1 argument. Pass the program code to it by pasting it between the bars in f "||. It will print "I am a caterpillar!", then splice together the output program from its own parts character by character by popping from the string. It will output this:

type[I am]type char 32 print[a beautiful butterfly!]

I should change the type char 32 part, but then the splicing code will look less epic.

In Batman form, achieved by replacing to with ; , thereby commenting out the function header, the Program will run when you load the file, and obviously not take input. For me, non-declarative code pasted into the edit window runs twice, but loading it as a file will still make it run once. To decide whether it is Batman, the program checks whether the input variable exists.

\$\endgroup\$
0
\$\begingroup\$

morsecco:  190  193 bytes

Morsecco itself ignores those boring strings without any dots or dashes, but if you provide it as input, why not use it and modify it?

I am a caterpillar!beautiful butterfly!.    .-. - - . - .-. -... -.. --.. --.- --.- -.-. -..-- - - --- -.-. --- - .. -.-. -.-.. - .-. -.-. . . . - . -.- .- -.-. .... .   -.- - ---  -.-. ... ---
  • . .-. - - . - .-. to Read the own source code and stdin
  • -... -.. --.. --.- --.- for a Binary Diff and to Zeroskip the Quit if it's identical
  • -.-. -..-- - - --- Cuts the caterpillar string, keeps a copy and Outputs it
  • -.-. --- - .. -.-. -.-.. - .-. -.-. . gets rid of the caterpillar and sets in the butterfly
  • . . - . -.- .- -.-. .... Konverts from ·Text to hide the butterfly in the caterpillar
  • . -.- - --- -.-. ... --- finally Concatenates the code for butterfly Output and put this code Out.

Other languages may need to do some ASCII art now, but the output of the code (if given the correct source, otherwise silently quit) will be:

I am a caterpillar!
.   -..-..- -..... --....- --.--.- -..... --....- -..... --...-. --..-.- --....- ---.-.- ---.-.. --.-..- --..--. ---.-.- --.--.. -..... --...-. ---.-.- ---.-.. ---.-.. --..-.- ---..-. --..--. --.--.. ----..- -....-  -.- - ---

and honestly: It looks a little like crawling caterpillars anyhow! Maybe like a millipede, but you wouldn't expect this to turn into a butterfly, wouldn't you? But execute it with morsecco and be surprised about the metamorphosis!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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