Last active
February 18, 2023 09:23
-
-
Save tompng/f7376a042c211969b7e57002b6017281 to your computer and use it in GitHub Desktop.
Party Parrot Quine
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
▗▄▄▄▄▄███████▙▄▄▄▄▄. | |
▄▄████▛▀▀▀▀▀!if%@▀▀▀▀▀▀████▄▄▖ | |
▄▄███▀▀▘t=4;data='Xig+jTEvljEy▀▀▜██▙▄ | |
▗▄██▛▀MzMzPTE0SSk/fDQqaUAqPjE0PzcyViV▝▜██▙▄ | |
▄▟██▀BXi0wSTMzPTQzPTQzcSVBXi0wHTMzPTMzOzQz▀▜██▄ | |
▗██▛▘eyhAcDExDzkwOzUxOzM0fCc9gjE1ADkyOTI0OjE2iD▀██▄ | |
▗▟█▛▘o5ljMzDDsvQTkxMzMzOzQzljMzaTsnMzMzQDE7SSk/fDQ▝▀█▙▖ | |
▄██▘qXTk1QjFAMzMz'.unpack1('m').chars.map{_1.ord-51};▝▜█▄ | |
▗▟█▀e=27.chr;w=[sp=32.chr]+"GL/E▗▄▟██████████▄▄;MJFI?K3HN▜█▙ | |
▄██▘7".bytes.map{▗▄▄(_1+9553).c▗▟█▛▀▘hr'utf-8▀▀██▖'};$>▄▄▖<▐█▙ | |
▟█▛<e+'[2J';at=??▗████▖.next;sc▗█▛▘ale=3;size=sc▝▜█▖ale▟███▙*▀█▌ | |
▗▟█▛100;Signal.trap█████▌(:INT){e██xit};loop{points▝█▌={▐█████};▜█▖ | |
▗▟█▘m=->a,b,t{a*(1-t█████▛)+b*t};b█▌ez=->(a,b,c,d){p=█▙->▐█████▘t{██ | |
▗██▘bc=m[b,c,t];m[m[m▜████▘[a,b,t],█▌bc,t],m[bc,m[c,d,▜█t]▝████▛,t]▝█▙ | |
██▘,t]};r=->(pt,pu,t,u▀█▛▘){pv=p[v=█▌(t+u)/2];(pt-pu).▟█abs▝▜█▀>1?r[▝█▙ | |
██▘pt,pv,t,v]&&r[pv,pu,v,u]:points[[█▌pv.real.floor,pv.█▌imag.floor]]=▜█▌ | |
▟█▘1};r[p[0],p[1],0.0,1.0]};canvas=si█▌ze.times.map{[0]*█▌284};t=(t+1)%7▜█▖ | |
▐█▘7;c,s=(Math::E**(-2i*Math::PI*t/11)█▙).rect;n=54-14*c+█▌s*13+(48-6*c-s▝██▖ | |
▗█▛*4).i;n=n.real+n.imag.i;plot=->a,b=0▐█{a.each_slice(5)▗█▌.map{[p=_1+_2.i▝▜█▖ | |
▟█,p-_4*d=d=Math::E**(-_3*0.0314i),p+_5▐█▖*d].map{|a|a+=b▐█;scale*(a.real+a.▝█▙ | |
▐█▌imag.i/2)}}.each_cons(2){bez[_1[0],_1[█▙2],_2[1],_2[0]]▟█};points.each{|(x,▝█▙ | |
▟█y),|(-1..1).each{|a|2.times{canvas[y+_1▜█▖][x+a]=1}}}};p█▌lot[[0,29,78,0,5,-7▜█▖ | |
█▌,7,50,10,5,0,0,0,5,5,6,7,-50,5,10,0,29,-█▙78,5,10],n];r▐█▘ange=canvas.map{|l|a█▌ | |
▐█▘=[*l.slice_when(&:!=)];a.size==5?[(a[0,2▜█▌].join.size+█▛1)/2,a[0,3].join.size▜█ | |
▐█/2]:[]};points={};plot[data.each_slice(3).██map{|x,y,z|▟█▘x+y*c+z*s}];[-15,10].▐█ | |
▐█each{rx=2;ry=3.5;cx,cy=(n+_1+5i).rect;((sc▝█▙ale*(cx-r▗█▛x)).floor..scale*(cx+r▐█ | |
▟█x)).each{|x|((scale*(cy-ry)/2).floor..scale▐█▙*(cy+ry▗██)/2).each{|y|((1.0*x/sc█▛ | |
▗▟██▖ale-cx)/rx+(2.0*y/scale-cy).i/ry).abs<1&&ca▝█▙▖nvas▗██▘[y][x]=1}}};rg=->{Regex█▌ | |
▄██▘█▙p.new(_1)};output=(size/14...size/4).map{|i|▝██▄s=▄██▘canvas[2*i].each_slice(▐█▘ | |
▗██▀2)▐█▖.zip(canvas[2*i+1].each_slice(2).to_a).map{|u▜█▙▟█▛,d|w[u[0]+u[1]*2+d[0]*4+d▐█ | |
▄██▛[1]*▝██▖8]}.join;s=s.gsub(rg[['[^','].+[^',']']*sp]){▝▀▀▘_1.gsub(sp,?#)};_,a,b,c=[*▐█ | |
▄▟█▛range[2*██▖@.then{|q|->r{q<<r;eval(q.gsub!(/[^!-~]/,''))}}[%@i],*range[2*i+1]].sort;s▐█▖ | |
▗██▀▘=(c)?s[0..▜█▙.a]+e+'[30m'+s[a...b].gsub(sp,'#')+e+'[m'+s[b..]:s;s.gsub(rg['['+e+'-~]+']█▌ | |
▗▟█▛▘){_1[sp]||_1[▝██▄▖e]?_1:[e,?[,31+t/4%7,?m,_1,e,'[m']*''}}*$/;output.sub!(?#*50,at+".then{█▙ | |
▟██▘|q|->r{q<<r;eval(▀██▙▄q.gsub!(/[^!-~]/,''))}}[%"+at).sub!(/[^#]+/){|a|a.gsub(rg['[^'+sp+$/+▐█▖ | |
▗██▘?]+sp]){_1[0]+?.}}.su▀▀██▙▄▖b!(/#.{4}/,'!if%'+at);output[output.rindex('##'),2]=at+?;;q.sub!(▝█▌ | |
▗█▛▘/[0-9]+/,t.to_s);c=(q+at+▀▀▜███▄▄▄'];%'+at+q).chars;$><<e+'[1;1H'+output.gsub(?#){c.shift};slee▜█▖ | |
▗█▛p(0.05)}@];%@t=4;data='Xig+jTEv▝▀▀▀██████▄▄▄▄▄▄▄▄▄▖ljEyMzMzPTE0SSk/fDQqaUAqPjE0PzcyViVBXi0wSTMzPT▝█▌ | |
▟█▘QzPTQzcSVBXi0wHTMzPTMzOzQzeyhAcDExDzkwOz▀▀▀▀▀▀▀▀▀▀▘UxOzM0fCc9gjE1ADkyOTI0OjE2iDo5ljMzDDsvQTkxMzMzO▜█▖ | |
█▌zQzljMzaTsnMzMzQDE7SSk/fDQqXTk1QjFAMzMz'.unpack1('m').chars.map{_1.ord-51};e=27.chr;w=[sp=32.chr]+"▝█▙ | |
█▌GL/E;MJFI?K3HN7".bytes.map{(_1+9553).chr'utf-8'};$><<e+'[2J';at=??.next;scale=3;size=scale*100;Sig@;▜█ | |
████████████████████████████████████████████████████████████████████████████████████████████████████████▌ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
data = [43,-11,11,90,-2,-4,99,-2,-1,0,0,0,10,-2,1,22,-10,12,73,1,-9,54,13,-9,11,-2,1,12,4,-1,35,-14,14,43,-6,-3,22,0,0,10,1,0,10,1,0,62,-14,14,43,-6,-3,-22,0,0,10,0,0,8,1,0,72,-11,13,61,-2,-2,-36,6,-3,8,2,-2,8,0,1,73,-12,10,79,-2,2,-51,6,-1,6,-1,1,7,-2,3,85,7,6,99,0,0,-39,8,-4,14,6,-2,0,0,0,8,1,0,99,0,0,54,8,-12,0,0,0,13,-2,8,22,-10,12,73,1,-9,42,6,2,15,-2,13,0,0,0] | |
sdata = [data.map{_1+51}.map(&:chr).join].pack('m').delete($/) | |
q=File.read(__FILE__).split(/[^\n]+CODE[^\n]+\n/).last.delete(" \n");#eval(q) | |
t=0; | |
data = 'Xig+jTEvljEyMzMzPTE0SSk/fDQqaUAqPjE0PzcyViVBXi0wSTMzPTQzPTQzcSVBXi0wHTMzPTMzOzQzeyhAcDExDzkwOzUxOzM0fCc9gjE1ADkyOTI0OjE2iDo5ljMzDDsvQTkxMzMzOzQzljMzaTsnMzMzQDE7SSk/fDQqXTk1QjFAMzMz'.unpack1('m').chars.map{_1.ord-51}; | |
e=27.chr; | |
w=[sp=32.chr]+"GL/E;MJFI?K3HN7".bytes.map{(_1+9553).chr'utf-8'}; | |
$><<e+'[2J'; | |
at=??.next; | |
scale=3; | |
size=scale*100; | |
Signal.trap(:INT){exit}; | |
loop{ | |
points={}; | |
m=->a,b,t{a*(1-t)+b*t}; | |
bez=->(a,b,c,d){ | |
p=->t{ | |
bc=m[b,c,t]; | |
m[m[m[a,b,t],bc,t],m[bc,m[c,d,t],t],t] | |
}; | |
r=->(pt, pu, t, u){ | |
pv=p[v=(t+u)/2]; | |
(pt-pu).abs>1?r[pt,pv,t,v]&&r[pv,pu,v,u]:points[[pv.real.floor,pv.imag.floor]]=1 | |
}; | |
r[p[0],p[1],0.0,1.0] | |
}; | |
canvas=size.times.map{[0]*284}; | |
t=(t+1)%77; | |
c,s=(Math::E**(-2i*Math::PI*t/11)).rect; | |
n=54-14*c+s*13+(48-6*c-s*4).i; | |
n=n.real+n.imag.i; | |
plot=->a,b=0{ | |
a.each_slice(5).map{ | |
[p=_1+_2.i,p-_4*d=d=Math::E**(-_3*0.0314i),p+_5*d].map{|a|a+=b;scale*(a.real+a.imag.i/2)} | |
}.each_cons(2){ | |
bez[_1[0],_1[2],_2[1],_2[0]] | |
}; | |
points.each{|(x,y),|(-1..1).each{|a|2.times{canvas[y+_1][x+a]=1}}} | |
}; | |
plot[[0,29,78,0,5,-7,7,50,10,5,0,0,0,5,5,6,7,-50,5,10,0,29,-78,5,10],n]; | |
range=canvas.map{|l| | |
a=[*l.slice_when(&:!=)]; | |
a.size==5?[(a[0,2].join.size+1)/2,a[0,3].join.size/2]:[] | |
}; | |
points={}; | |
plot[data.each_slice(3).map{|x,y,z|x+y*c+z*s}]; | |
[-15,10].each{ | |
rx=2; | |
ry=3.5; | |
cx,cy=(n+_1+5i).rect; | |
((scale*(cx-rx)).floor..scale*(cx+rx)).each{|x|((scale*(cy-ry)/2).floor..scale*(cy+ry)/2).each{|y| | |
((1.0*x/scale-cx)/rx+(2.0*y/scale-cy).i/ry).abs<1&&canvas[y][x]=1 | |
}} | |
}; | |
rg=->{Regexp.new(_1)}; | |
output = (size/14...size/4).map{|i| | |
s=canvas[2*i].each_slice(2).zip(canvas[2*i+1].each_slice(2).to_a).map{|u,d| | |
w[u[0]+u[1]*2+d[0]*4+d[1]*8] | |
}.join; | |
s=s.gsub(rg[['[^','].+[^',']']*sp]){_1.gsub(sp,?#)}; | |
_,a,b,c=[*range[2*i],*range[2*i+1]].sort; | |
s=(c)?s[0...a]+e+'[30m'+s[a...b].gsub(sp,'#')+e+'[m'+s[b..]:s; | |
s.gsub(rg['['+e+'-~]+']){ | |
_1[sp]||_1[e]?_1:[e,?[,31+t/4%7,?m,_1,e,'[m']*'' | |
} | |
}*$/; | |
output | |
.sub!(?#*50,at+".then{|q|->r{q<<r;eval(q.gsub!(/[^!-~]/,''))}}[%"+at) | |
.sub!(/[^#]+/){|a|a.gsub(rg['[^'+sp+$/+?]+sp]){_1[0]+?.}} | |
.sub!(/#.{4}/,'!if%'+at); | |
output[output.rindex('##'),2]=at+?;; | |
q.sub!(/[0-9]+/,t.to_s); | |
c=(q+at+'];%'+at+q).chars; | |
$><<e+'[1;1H'+output.gsub(?#){c.shift}; | |
sleep(0.05) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<style> | |
canvas:hover{ | |
background:white; | |
} | |
canvas:active{ | |
opacity: 0; | |
} | |
img, canvas{ | |
position:absolute; | |
left: 10px; | |
top: 10px; | |
width: 100px; | |
height: 100px; | |
box-shadow: 0 0 2px black; | |
} | |
</style> | |
<img src="images/parrot1.png"> | |
<img src="images/parrot3.png"> | |
<img src="images/parrot5.png"> | |
<img src="images/parrot7.png"> | |
<img src="images/parrot9.png"> | |
<script> | |
const canvas=document.createElement('canvas') | |
document.body.appendChild(canvas) | |
canvas.width=canvas.height=1000 | |
const g=canvas.getContext('2d') | |
g.scale(10,10) | |
function draw(points) { | |
g.beginPath() | |
g.moveTo(points[0][0],points[0][1]) | |
for (let i = 0; i < points.length - 1; i++) { | |
const [x, y, deg, a, b] = points[i] | |
const [x2, y2, deg2, a2, b2] = points[i + 1] | |
const len = Math.sqrt((x2 - x) ** 2, (y2 - y) ** 2) | |
const c1 = Math.cos(Math.PI * deg / 180) | |
const s1 = -Math.sin(Math.PI * deg / 180) | |
const c2 = Math.cos(Math.PI * deg2 / 180) | |
const s2 = -Math.sin(Math.PI * deg2 / 180) | |
g.bezierCurveTo(x + c1 * b, y + s1 * b, x2 - c2 * a2, y2 - s2 * a2, x2, y2) | |
} | |
g.strokeStyle = 'blue' | |
g.lineWidth = 3 | |
g.stroke() | |
g.beginPath() | |
points.forEach(([x, y]) => { | |
g.moveTo(x, y) | |
g.arc(x, y, 2, 0, 2 * Math.PI); | |
}) | |
g.fillStyle = 'rgba(255,0,0,0.5)' | |
g.fill() | |
} | |
function basesParams(values) { | |
let csum = 0, ssum = 0, vsum = 0 | |
values.forEach((v, i) => { | |
const th = 2 * Math.PI * i / values.length | |
vsum += v | |
csum += v * Math.cos(th) | |
ssum += v * Math.sin(th) | |
}) | |
return [ | |
vsum / values.length, | |
2 * ssum / values.length, | |
2 * csum / values.length | |
] | |
} | |
const base1 = [ | |
[55, 86, 180, 0, 10], | |
[34, 65, 80, 12, 10], | |
[50, 40, 40, 10, 10], | |
[77, 40, -40, 10, 8], | |
[85, 59, -70, 5, 10], | |
[83, 82, -90, 8, 10], | |
[87, 99, -80, 10, 0], | |
[8, 99, 90, 0, 18], | |
[34, 65, 68, 30, 0], | |
] | |
const base3 = [ | |
[55-20, 86, 170, 0, 8], | |
[34-18, 65+8, 120, 8, 15], | |
[50-24, 40-3, 40, 10, 10], | |
[77-24, 40-3, -40, 10, 8], | |
[85-20, 59-2, -50, 10, 7], | |
[83-16, 82-6, -80, 5, 5], | |
[94, 99, -60, 20, 0], | |
[10, 99, 100, 0, 15], | |
[34-18, 65+8, 100, 15, 0], | |
] | |
const base5 = [ | |
[55-25, 86+8, 180, 0, 10], | |
[34-28, 65+14, 120, 12, 15], | |
[50-35, 40+2, 40, 10, 10], | |
[77-35, 40+2, -40, 10, 8], | |
[85-31, 59+4, -60, 10, 7], | |
[83-27, 82-4, -90, 5, 5], | |
[87, 99, -50, 20, 0], | |
[6, 99, 120, 0, 5], | |
[34-28, 65+14, 68, 5, 0], | |
] | |
const base7 = [ | |
[55-17, 86+8, 180, 0, 8], | |
[34-15, 65+18, 100, 8, 10], | |
[50-16, 40+9, 40, 10, 10], | |
[77-16, 40+9, -40, 10, 8], | |
[85-17, 59+4, -60, 8, 7], | |
[83-10, 82-4, -90, 7, 5], | |
[70, 99, -80, 10, 0], | |
[10, 99, 120, 0, 5], | |
[34-15, 65+18, 68, 5, 0], | |
] | |
const base9 = [ | |
[55+3, 86+6, 180, 0, 15], | |
[34, 65+2, 70, 15, 8], | |
[50, 40+8, 40, 8, 8], | |
[77, 40+8, -40, 10, 6], | |
[85+1, 59+4, -80, 6, 7], | |
[83+4, 82, -110, 7, 10], | |
[85, 99, -80, 10, 0], | |
[6, 99, 60, 0, 20], | |
[34, 65+2, 70, 20, 0], | |
] | |
let frame = 0 | |
console.log(basesParams([0, -24, -34, -15, 1])) | |
console.log(basesParams([0, -3, 4, 11, 9])) | |
console.log( | |
base1.map((b, i) => { | |
return b.map((_, j) => { | |
return basesParams([base1[i][j], base3[i][j], base5[i][j], base7[i][j], base9[i][j]]).map(a=> | |
Math.round(j==2 ? 100*a/180 : a) | |
) | |
}) | |
}).join(',') | |
) | |
setInterval(() => { | |
g.clearRect(0, 0, 1000, 1000) | |
time = performance.now() / 1000 * 12 | |
const cos = Math.cos(time) | |
const sin = -Math.sin(time) | |
draw(base1.map((b, i) => { | |
return b.map((_, j) => { | |
const [v, c, s] = basesParams([base1[i][j], base3[i][j], base5[i][j], base7[i][j], base9[i][j]]) | |
return v + c * cos + s * sin | |
}) | |
})) | |
const [nx, nxc, nxs] = basesParams([0, -24, -34, -15, 1]) | |
const [ny, nyc, nys] = basesParams([0, -3, 4, 11, 9]) | |
var x=68+nx+cos*nxc+nxs*sin | |
var y=44+ny+cos*nyc+nys*sin | |
g.fillStyle = g.strokeStyle | |
g.beginPath() | |
g.save() | |
g.translate(x-15,y+5) | |
g.scale(1,1.5);g.arc(0, 0, 2.5, 0, 2 * Math.PI) | |
g.fill() | |
g.restore() | |
g.save() | |
g.translate(x+10,y+5) | |
g.scale(1,1.5);g.arc(0, 0, 2.5, 0, 2 * Math.PI) | |
g.fill() | |
g.restore() | |
draw([ | |
[x,29+y,140,0,5], | |
[-7+x,7+y,90,10,5], | |
[x,y,0,5,5], | |
[6+x,7+y,-90,5,10], | |
[x,29+y,-140,5,10], | |
]) | |
}, 10) | |
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'open3' | |
def delete_esc(s) | |
s.gsub /\e\[[\d;]*[a-zA-Z]/, '' | |
end | |
def read_frame(io) | |
s = 53.times.map { io.gets }.join | |
t, u = io.gets.split "\e[1;1H" | |
u.chars.reverse_each { io.ungetc _1 } | |
delete_esc s + t | |
end | |
frames = Open3.popen3 'ruby', 'partyparrot_generator.rb' do |input, output| | |
78.times.map { read_frame output } | |
end | |
raise unless frames[0] == frames[77] | |
raise unless frames.uniq.size == 77 | |
puts :loop_ok | |
frames.pop | |
embedded_codes = frames.map do |frame| | |
code = frame.gsub('@.then{|q|->r{q<<r;eval(') { _1.gsub 'eval', 'puts' } | |
embedded_code = Open3.capture3('ruby', '-e', code).first | |
end | |
normalized_codes = embedded_codes.map do |code| | |
n = nil | |
c = code.gsub(/t=\d+/) { | |
n = _1.scan(/\d+/).first | |
'0' | |
} | |
[n, c] | |
end | |
raise unless normalized_codes.map(&:first) == 77.times.to_a.rotate(1).map(&:to_s) | |
raise unless normalized_codes.map(&:last).uniq.size == 1 | |
puts :all_frame_syntax_ok | |
frames.zip(frames.rotate(1)).each_with_index do |(frame1, frame2), i| | |
next_frame = Open3.popen3('ruby', '-e', frame1) { |i, o| read_frame o } | |
raise unless next_frame == frame2 | |
puts "frame#{i} nextframe ok" | |
end | |
File.write 'partyparrot.rb', frames[3] + $/ | |
puts :success |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment