33
\$\begingroup\$

...at least for some definition of "self-modification".

The Task

In this challenge, your task is to write three strings A, B and C that satisfy the following properties.

  • The string B has length at least 1.

  • For every n ≥ 0, the string ABnC is a valid program (meaning full runnable program or function definition) in your programming language of choice. The superscript denotes repetition, so this means the strings AC, ABC, ABBC, ABBBC etc. Each program takes one string as input, and returns one string as output.

  • For any m, n ≥ 0, if the program ABmC is run with input ABnC, it returns ABm*n+1C. For inputs not of this form, the program may do anything, including crash.

Some examples in the format program(input) -> output:

AC(AC) -> ABC
ABC(AC) -> ABC
ABBBBBC(AC) -> ABC
AC(ABC) -> ABC
AC(ABBBBC) -> ABC
ABC(ABC) -> ABBC
ABBC(ABC) -> ABBBC
ABBBBC(ABBBC) -> ABBBBBBBBBBBBBC
ABBBC(ABBBBBBC) -> ABBBBBBBBBBBBBBBBBBBC

Rules and Scoring

Your score is the total length of A and C, lower score being better. Note that while B is not counted toward the score, it must be produced by A and C as in the first example.

Standard loopholes are disallowed. The programs are not allowed to directly or indirectly access their own source code (except when they are given it as input). You are required to identify the strings A, B and C in your answer in some way, and encouraged to explain your solution.

\$\endgroup\$

11 Answers 11

16
\$\begingroup\$

CJam, 9 8 bytes

A: 1
B: 0
C:  r,(#0q

Try it online in the CJam interpreter.

How it works

(ABcode) e# Push the integer 10 ** len(Bcode).
<SP>     e# Noop. Separates (AB) and C for input reading.
r        e# Read the first whitespace-separated token from STDIN (ABinput).
,(       e# Push the string length minus 1: len(Binput)
#        e# Power operator: 10 ** len(Bcode) len(Binput) # ->
         e#   (10 ** len(Bcode)) ** len(Binput) = 10 ** (len(Bcode) * len(Binput))
0        e# Push an additional 0 to complete len(Bcode) * len(Binput) + 1 zeroes.
q        e# Read the remaining input (C).
\$\endgroup\$
12
\$\begingroup\$

CJam, 15 13 11 bytes

A: rl"
B: <SP>
C: <LF>",(*SNq

Try it online in the CJam interpreter.

How it works

e# A

r     e# Read a whitespace-separated token from STDIN.
      e# This reads the input up to the first space, but does not consume it.
l     e# Read the rest of the first line from STDIN.
      e# This reads up to the first linefeed and consumes it.

"     e# Initiate a string.

e# B

<SP>  e# Fill the string with as many spaces as there are copies of B.

e# C

<LF>" e# Terminate the string with a linefeed.
      e# This serves as a delimiter for the `l' command.
,(    e# Compute the length of the string minus 1 (to account for the LF).
*     e# Repeat the string read by `l' that many times.
SN    e# Push a space and a linefeed.
q     e# Read the remaining input (i.e., the second line) from STDIN.

At the end, the stack contains the token read by r, the space produced by *, the space and linefeed pushed by SN and the line read by q. CJam prints all these automatically.

\$\endgroup\$
  • \$\begingroup\$ Hah, nice use of quotes there :D \$\endgroup\$ – Optimizer May 25 '15 at 19:10
9
\$\begingroup\$

Pyth, 10

A: w*\0hl*w[<newline>
B: 0
C: <empty>

We split the source in two lines. The first line is A, the second line are the Bs. Since A is on the first line, the first w just prints A - easy, done.

In Pyth leading zeroes are seperate tokens, so [00) actually is [0, 0]. Note that the first line ends in l[, and the second line consists of 0000.... So l[ actually counts the number of Bs in this program. The second w reads in the second line of the input - this is the number of Bs of the input. From here it's a simple multiply, increment and outputting that many zeroes.

\$\endgroup\$
9
\$\begingroup\$

Retina, 25 19 bytes

A: ]\]<LF>
B: ]]
C: <LF>m`^]*$<LF>]$0]

<LF> stands for newline

Example ABC code:

]\]
]]
m`^]*$
]$0]

The code has two substitute steps:

  • change the input AB^mC into AB^(m*n)C by changing every B to B^n:

    • ]\] matches every B in the input and nothing else thanks to escaping in the pattern lines
    • ]]...]] is B^n
  • change B^(m*n) to B^(m*n+1) by

    • m`^]*$ taking the line with only ]'s
    • ]$0] adding an extra pair of ]] to it in a way that this line doesn't match the first regex

I've added 3 bytes to the score for the -s multi-line flag which is needed so the whole Retina code could be in one file.

2 bytes saved thanks to @MartinBüttner.

\$\endgroup\$
8
\$\begingroup\$

Python 3, 51 bytes

A: lambda s:s[:28]+"x"*(1+len("
B: x
C: ")*(len(s)-51))+s[-23:]

Example usage:

>>> f=lambda s:s[:28]+"x"*(1+len("xx")*(len(s)-51))+s[-23:]
>>> f('lambda s:s[:28]+"x"*(1+len("xxx")*(len(s)-51))+s[-23:]')
'lambda s:s[:28]+"x"*(1+len("xxxxxxx")*(len(s)-51))+s[-23:]'

The function computes n*m+1 with (1+len("xxx")*(len(s)-51)) where there are m x's in the string (xxx part is the B^m). Multiplying the string "x" with this number gives B^(n*m+1) and the function takes A and C out of the input and concatenates all of these to get AB^(n*m+1)C.

The same approach in J:

J, 35 bytes

A: (19{.]),('x'#~1+(#'
B: x
C: ')*35-~#),_16{.]
\$\endgroup\$
5
\$\begingroup\$

CJam, 22

A:<empty>
B:{])`\,q,K/(*))*"_~"}
C:{])`\,q,K/(*))*"_~"}_~

Example run:

ABBC(ABC) -> ABBBC

which translates to

{])`\,q,K/(*))*"_~"}{])`\,q,K/(*))*"_~"}{])`\,q,K/(*))*"_~"}_~

with input as

{])`\,q,K/(*))*"_~"}{])`\,q,K/(*))*"_~"}_~

which gives the following output:

{])`\,q,K/(*))*"_~"}{])`\,q,K/(*))*"_~"}{])`\,q,K/(*))*"_~"}{])`\,q,K/(*))*"_~"}_~

How it works:

Lets take a look at what programs AC and ABC look like:

AC :{])`\,q,K/(*))*"_~"}_~
ABC:{])`\,q,K/(*))*"_~"}{])`\,q,K/(*))*"_~"}_~

We notice that C = B_~

Lets look at what B is doing:

{])`\,q,K/(*))*"_~"}

{                  }    e# This is a code block. Alone, this does nothing except
                        e# pushing this block to stack as is
 ]                      e# Wrap everything on stack in an array
  )`                    e# Take out the last part and convert it to its string representation
    \,                  e# Take length of remaining array
      q,K/              e# Read the input, take its length and int divide by K (i.e. 20)
          (*            e# Decrement and multiply by the array length on stack
            ))          e# Add two to the product
              *         e# Repeat the string representation on stack that many times
               "_~"     e# Put this string on stack

Now lets see what running AC without any input will do:

{])`\,q,K/(*))*"_~"}_~                      e# Copy the block and run it
{])`\,q,K/(*))*"_~"}{])`\,q,K/(*))*"_~"}~   e# Block is copied, run it
{      ...         } ])                     e# Wrapped array has the block in it.
                       `\,                  e# Stringify it and take length of remaining = 0
                          q,K/              e# No input so 0
                              (*))          e# 0 * -1 = 0. 0 + 2 = 2
                                  *         e# Repeat the stringified block 2 times:
                                            e# "{])`\,q,K/(*))*"_~"}{])`\,q,K/(*))*"_~"}"
                                   "_~"     e# Put this string. Program ends, so print stack:
                                            e# {])`\,q,K/(*))*"_~"}{])`\,q,K/(*))*"_~"}_~

Wow, the output is ABC.

We basically count how many B exist in the code. Then how many are in the input (using length). Multiply them, increment twice (since C also has B) and append _~ to get C

Try it online here

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

Haskell, 50 bytes

f is a function taking and returning a String.

The string B is just a single space, while C starts with one.

A:_:b="
B: 
C: ";f s|a:c<-words s=unwords$a:(drop 50s>>b):c

Try it online!

  • _:b=" " assigns all but the first of the spaces in the string literal to b, making that equal to the program's m B copies.
  • s is the input string. a:c<-words s splits it into space-separated words, so that a becomes A and c becomes a list of the words comprising C. The B copies are ignored since words squeezes multiple spaces (which the rest of the program avoids).
  • drop 50s is a string with length equal to the number n of B copies in the input. drop 50s>>b concatenates that many copies of b, giving mn spaces.
  • unwords$a:(drop 50s>>b):c joins all the strings back together with spaces. Since there is an extra "word" (drop 50s>>b) inserted in the list, there is also an extra joining space, automatically adding the +1 to the multiplication.
\$\endgroup\$
2
\$\begingroup\$

Matlab, 85

First time for me to do such an abstract challenge, so for me it was more of a coding challenge than a code-golf challenge!

The three strings are, without the quotation marks:

A:    "X=strsplit(input('','s'));m=0 "
B:    "+1 "
C:    ";[X{1},32,repmat(['+1',32],1,m*(length(X)-2)+1),X{end}]"

How it works: I split the input argument on whitespace, so n can be determined from the number of string parts. B works as a sort of counter to get m. For reconstructing the answer I use A and C from the split, repeat B m*n+1 times and I insert the spaces by using their ASCII value, so that no unwanted splits occur in C.

EDIT: whoops, accidentally counted A+B

\$\endgroup\$
1
\$\begingroup\$

C (gcc), 81 bytes

The requirement to identify the strings seems at odds with our being allowed arbitrary behaviour for illegal input, unless we have rather lax standards of what identifying entails. Naturally, I took the interpretation that sheds the most bytes.

Try it online!

A: m;f(char*s){m=strrchr(s+66,32)-s-65;printf("%.66s%*s",s,m*strlen("
B: <SPACE>
C: ")+16,s+m+66);}
\$\endgroup\$
  • \$\begingroup\$ By identification I just meant that it should be clear from your answer which code snippets are A, B and C. It's not a requirement for the program. \$\endgroup\$ – Zgarb Jul 28 '18 at 14:33
1
\$\begingroup\$

TI-Basic (83 series), 65 bytes

Segment A (33 bytes):

Input Str1:sub(Str1,1,27:For(I,0,(length(Str1)-55)(length("

Segment B:

X

Segment C (32 bytes):

Y")-1:Ans+"X":End:Ans+sub(Str1,length(Str1)-27,28

I am really excited about finding this quine-like challenge! Most quines don't work in TI-Basic without at least a little cheating, because there is no way to escape the " symbol. (In both senses of the word "escape".) But here, we get an input string via the Input command, and typing in a " there is perfectly fine.

There is still some amount of TI-Basic silliness to work around here: an empty string is invalid, so the naive solution of inserting the string "XXX...XX" in a loop won't work when n=0. Instead, we manually compute the value of mn+1, and insert the string "X" that many times.

The magic constants 27 and 28 in the program are slightly off from the byte counts 33 and 32, because Str1, sub(, and length( are two-byte tokens that only contribute 1 to the length of a string.

If we use newlines instead of :, it looks like when can save a few bytes by leaving off ending quotes, but this doesn't actually work. First of all, you need a hex editor before you can add the newline character into a string: you can't just type it in, because if you press ENTER during an Input command, it submits the input. When I tried the hex editor approach, I ended up getting a weird buffer overflow error that modified the contents of my program, so don't try this at home with your expensive calculator.

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

Java 11, 135 65+26=91 bytes

A

s->{var p=s.split("\\(\"|\"\\.");return p[0]+"(\"B"+p[1].repeat("

B

B

C

".length())+'"'+'.'+p[2];}

Try it online here (TIO does not have Java 11 yet, so this relies on a helper method instead of String::repeat()).

Ungolfed:

s -> { // lambda taking and returning a String
    var p = s.split("\\(\"|\"\\."); // split input into parts A, B^n, C (where n may be 0) at the "(\"" and "\"."
    return p[0] + "(\"B" + // put it back together: A plus the part the split shaved off, plus an extra B ...
    p[1].repeat("BBB".length()) + // ... plus B^(n*m)
    '"' + '.' + p[2]; // plus C, with the part the split shaved off reattached
}
\$\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.