Though the challenge was edited to show that reading your source is allowed, I was already creating my solution without that. So, to show that it's possible, I finished it. No reading of the source file:
s='s=%r;print s%%s\nfrom random import*;L=4;f=open(__file__,"wa"[L>5]);R=randint\nf.write("\\n".join((s%%s).split("\\n")[1:5:2]).replace("4",`map(ord,s%%s)`))\nif L>5:exec\'b=[];h=%%d\\nwhile~-h:b+=[h%%%%1000];h/=1000\\nwhile b:r,p,n=b[-3:];b=b[:-3];L=[L[:p]+L[p+1:],L[:p]+[r]+L[p+n:]][n<2if L else 1]\\nprint"".join(map(chr,L))\'%%1\n\nn=R(0,2);p=R(0,len(L if L>5else s%%s));r=R(0,255);f.write("%%03d"*3%%(n,p,r))';print s%s
from random import*;L=4;f=open(__file__,"wa"[L>5]);R=randint
f.write("\n".join((s%s).split("\n")[1:5:2]).replace("4",`map(ord,s%s)`))
if L>5:exec'b=[];h=%d\nwhile~-h:b+=[h%%1000];h/=1000\nwhile b:r,p,n=b[-3:];b=b[:-3];L=[L[:p]+L[p+1:],L[:p]+[r]+L[p+n:]][n<2if L else 1]\nprint"".join(map(chr,L))'%1
n=R(0,2);p=R(0,len(L if L>5else s%s));r=R(0,255);f.write("%03d"*3%(n,p,r))
Try it online! (Note that this won't modify the source. You have to run it locally for that to work)
To show that the transformations work, here is a test program (currently set up to always pick 100
for r
, and it prints the result for every combination of n
and p
for the initial list.)
Explanation:
s='s=%r;print s%%s...';print s%s...
The first line is your classic quine, but a lot longer to account for what comes after.
from random import*;L=4;f=open(__file__,"wa"[L>5]);R=randint
Import for random integers. L
will become of list of ordinals of the source code, but initially it is an integer not used anywhere else in the source to allow for a string replacement. Open the file to write the new source. On later runs, it will open to append instead.
f.write("\n".join((s%s).split("\n")[1:5:2]).replace("4",`map(ord,s%s)`))
Remove the first and third lines of code. Replace the 4
above with the list of ordinals.
if L>5:exec'b=[];h=%d\nwhile~-h:b+=[h%%1000];h/=1000\nwhile b:r,p,n=b[-3:];b=b[:-3];L=[L[:p]+L[p+1:],L[:p]+[r]+L[p+n:]][n<2if L else 1]\nprint"".join(map(chr,L))'%1
n=R(0,2);p=R(0,len(L if L>5else s%s));r=R(0,255);f.write("%03d"*3%(n,p,r))
In pieces:
if L>5:
- Skips this line on first execution. Later, L
will be a list, and this will run. I'll explain the exec
last, because it's not run the first time.
n
- A random number 0-2. This determines which modification occurs (0 = insert, 1 = replace, 2 = delete).
p
- A random position in the list that the modification will occur at.
r
- A random number to insert or replace in the list
f.write("%03d"*3%(n,p,r))
- Append the 3 randoms to the end of the source file. Every run, this will be adding on to an integer that encodes all the changes to the initial source that have occurred.
exec'b=[];h=%d...'%1...
- Get the random numbers (found after %1
on later runs), apply the changes to the list, and print.
while~-h:b+=[h%%1000];h/=1000
- Build a list of the randoms generated so far, accounting for the leading 1
, which prevents problems with leading zeros.
while b:r,p,n=b[-3:];b=b[:-3]
- Assign the randoms for this iteration.
L=[L[:p]+L[p+1:],L[:p]+[r]+L[p+n:]][n<2if L else 1]
- (0 = insert, 1 = replace, 2 = delete)
print"".join(map(chr,L))
- Print the modified source.