https://www.reddit.com/r/factorio/comments/1t57q1p/another_qr_code_solar_farm_no_urls_used/




이렇게 생겨먹은 태양광 청사진






줌아웃하면 QR코드 모양을 하고있는데


이걸 스캔하면 루아 스크립트가 나옴


s=[===[N,E,L,G=177,{},{},{}Z=bit32 X=Z.bxor R=Z.rshift K=Z.lshift B=Z.band O=Z.bor x=1 for i=0,254 do E[i]=x L[x]=i x=K(x,1)if x>255 then x=X(x,285)end end for i=255,511 do E[i]=E[i-255]end G={212,246,77,73,195,192,75,98,5,70,103,177,22,217,138,51,181,246,72,25,18,46,228,74,216,195,11,106,130,150} function rs(d)local r={}for i=1,30 do r[i]=0 end for i=1,#d do f=X(d[i],r[1]) for j=1,29 do r[j]=r[j+1]end r[30]=0 for j=1,30 do if f~=0 then r[j]=X(r[j],E[L[f]+L[G[j]]])end end end return r end function enc(z)BT,n={},0 function a(v,l)for i=l-1,0,-1 do n=n+1 BT[n]=B(R(v,i),1)end end a(4,4)a(#z,16)for i=1,#z do a(z:byte(i),8)end t=23648-n if t>4 then t=4 end a(0,t)while n%8~=0 do n=n+1 BT[n]=0 end o,m={},0 for i=1,n,8 do m=m+1 b=0 for j=0,7 do b=O(b,K(BT[i+j],7-j))end o[m]=b end p=236 while m<2956 do m=m+1 o[m]=p p=253-p end return o end function mkc(d)D,C={},{}p=1 for b=1,25 do n=b<20 and 118 or 119 l={}for i=1,n do l[i]=d[p]p=p+1 end D[b]=l C[b]=rs(l)end r,m={},0 for i=1,119 do for b=1,25 do if D[b][i]then m=m+1 r[m]=D[b][i]end end end for i=1,30 do for b=1,25 do m=m+1 r[m]=C[b][i]end end return r end g,v={},{} for y=1,N do g[y]={}v[y]={}for x=1,N do g[y][x]=0 end end function w(x,y,u)g[y][x]=u v[y][x]=1 end A=math.abs for _,p in ipairs{{4,4},{N-3,4},{4,N-3}}do for dy=-4,4 do for dx=-4,4 do Q,Y=p[1]+dx,p[2]+dy if Q>=1 and Q<=N and Y>=1 and Y<=N then ax,ay=A(dx),A(dy) w(Q,Y,ax<=3 and ay<=3 and(ax<=1 and ay<=1 or ax>2 or ay>2)and 1 or 0) end end end end for i=9,N-8 do if not v[7][i]then w(i,7,i%2)end if not v[i][7]then w(7,i,i%2)end end H={7,31,59,87,115,143,171} for _,ay in ipairs(H)do for _,ax in ipairs(H)do if not(ax<=8 and ay<=8 or ax>=N-7 and ay<=8 or ax<=8 and ay>=N-7)then for dy=-2,2 do for dx=-2,2 do ad=A(dx)>A(dy)and A(dx)or A(dy) w(ax+dx,ay+dy,ad~=1 and 1 or 0) end end end end end w(9,N-7,1) for i=0,7 do v[9][i+1]=1 v[i+1][9]=1 v[9][N-i]=1 v[N-i][9]=1 end v[9][9]=1 for i=0,5 do for j=0,2 do v[N-10+j][i+1]=1 v[i+1][N-10+j]=1 end end S="s=[===["..s.."]".."===] load(s)()" cw=mkc(enc(S)) bt={}for _,c in ipairs(cw)do for i=7,0,-1 do bt[#bt+1]=B(R(c,i),1)end end bi,col=1,N while col>=1 do if col==7 then col=6 end for ri=0,N-1 do for dc=0,1 do x=col-dc y=R(N-col,1)%2==0 and N-ri or ri+1 if x>=1 and x<=N and y>=1 and y<=N and not v[y][x]and bi<=#bt then g[y][x]=bt[bi]bi=bi+1 end end end col=col-2 end for y=1,N do for x=1,N do if not v[y][x]and(x-1)*(y-1)%2+(x-1)*(y-1)%3==0 then g[y][x]=X(g[y][x],1)end end end for i=0,14 do b=B(R(25368,14-i),1) px=i<6 and i+1 or i<7 and 8 or 9 py=i<8 and 9 or i==8 and 8 or 15-i g[py][px]=b g[i<7 and N-i or 9][i<7 and 9 or N-14+i]=b end for i=0,5 do for j=0,2 do b=B(R(167017,i*3+j),1) g[i+1][N-10+j]=b g[N-10+j][i+1]=b end end e={}n=0 for y=-3,181 do for x=-3,181 do n=n+1 e[n]={entity_number=n,name=g[y]and g[y][x]==1 and"solar-panel"or"accumulator",position={x*3,3-y*3}}end end local s=game.player.cursor_stack s.set_stack("blueprint")s.set_blueprint_entities(e)]===] load(s)()


이 스크립트를 콘솔창에 붙여넣고 실행하면??





원본 청사진이 나온다



위와 같이 스스로의 소스코드를 내뱉는 프로그램을 콰인(Quine)이라고 한다더라고




+원본 댓글에 어떻게 만드는지 약간 설명되어있던 부분이 있던데 이해한 만큼만 적어봄


일단 스크립트가 s=[... 로 시작하고 중간에 S="s=[===["..s.."]".."===] load(s)()" 부분이 있어서 S라는 변수 안에 자기 자신이 들어가게 됨


이게 콰인을 가능하게 해주는 부분


루아에서는 스트링을 그냥 실행할 수 있어서 만들기 쉬운 편이라고 함




다음부터는 어떻게 정보를 압축했는지에 대한 내용


팩토리오 청사진은 JSON 형태로 되어있음



여기서 흰색, 빨간색으로 되어있는 부분은 싹 다 반복되는 부분이라 한번만 적어두면 그 다음부턴 그냥 버릴 수 있음


entity number, position 뒤에 적혀있는 숫자들도 일정한 규칙이 있으니 한두줄로 압축할 수 있고


결국 중요한건 이 부분이 태양판인지 축전지인지 하는 1비트짜리 정보밖에 안 남는다


그래서 입력으로 0에서 255 사이의 숫자들을 받고 이진수로 바꾼 다음 0이 오면 solar-panel을, 1이 오면 accumulator를 넣도록 함


"163"이면 "0b10100011"이 되고 


{"entity_number":45,"name":"accumulator","position":{"x":133.0,"y":1.0}}

{"entity_number":46,"name":"solar-panel","position":{"x":136.0,"y":1.0}}

{"entity_number":47,"name":"accumulator","position":{"x":139.0,"y":1.0}}

...


이렇게 변환시켜주는거




또 QR코드에 있는 테두리라든가 인식용 네모들이라든가 일부분이 가려져도 스캔할 수 있도록 하는 오류 수정 알고리즘같은 것들도 당연히 필요없는 부분


코드의 시작 부분에 문자열 -> QR 변환 알고리즘을 그냥 넣어뒀더라고




근데 이렇게 압축해도 스크립트 안에 자기 자신을 온전히 담을 수는 없음


결국 스크립트 입력으로 숫자들이 잔뜩 필요한데 이 숫자들조차 스크립트의 일부니까


콰인을 만드려면 코드 안에 코드에 대한 정보가 온전히 담겨 있어야 하는데 1바이트로 1바이트를 초과하는 정보를 설명하는건 정보이론상 불가능함




이를 위해서 이해하기 힘든... 수학적 기법을 이용해 추가적인 인코딩을 진행한다

for i=9,N-8 do if not v[7][i]then W(i,7,i%2)end if not v[i][7]then W(7,i,i%2)end end H={7,31,59,87,115,143,171}for _,n in I(H)do for _,m in I(H)do if not(m<=8 and n<=8 or m>=N-7 and n<=8 or m<=8 and n>=N-7)then for p=-2,2 do for o=-2,2 do r=A(o)>A(p)and A(o)or A(p) W(m+o,n+p,r~=1 and 1 or 0) end end end end end W(9,N-7,1)for i=0,7 do v[9][i+1]=1 v[i+1][9]=1 v[9][N-i]=1 v[N-i][9]=1 end v[9][9]=1 for i=0,5 do for j=0,2 do v[N-10+j][i+1]=1 v[i+1][N-10+j]=1 end end a,b,d,J=1,N,M(F(S)),{}for _,c in I(d)do for i=7,0,-1 do J[#J+1]=B(R(c,i),1)end end


작성자는 '이 코드만을 위한 극히 특수한 압축 알고리즘을 작성하는 것과 같다'고 표현함


비유하자면 그림을 이미지 파일로 저장하는게 아니라 어디에 먹칠을 해라 붓을 어떤 방향으로 휘둘러라 같은 설명서를 적어둔다고 하면 될듯


패턴이 전혀 없는 무작위적인 노이즈가 아닌 이상에야 그림 자체의 용량보다 크기가 작은 설명서를 쓸 수 있을 테니까


다만 콰인의 규칙상 그 설명서조차도 그림의 일부가 되어야 하고, 설명서를 추가하면 결국 그림 전체의 모양이 바뀌게 되니까 설명서를 다시 작성해야 함


언제까지? 될 때까지.





손으로 하면 한달정도 걸리는 개노가다라서 여기까지 만들고 나머지는 그냥 클로드 돌렸다고 함


어제 올라온 게시글(스캔하면 자기가 올라와있는 factoriobin 링크로 데려다주는 거였음) 보고 만들었다고 하니까 딱 하루가 걸린 셈




QR코드 버전 40은 177 x 177 크기로 2,953바이트를 담을 수 있는데 딱 1바이트 남기고 들어간다고 하더라ㅋㅋ