Skip to content

Instantly share code, notes, and snippets.

@tompng
Last active February 18, 2023 09:23
Show Gist options
  • Save tompng/f7376a042c211969b7e57002b6017281 to your computer and use it in GitHub Desktop.
Save tompng/f7376a042c211969b7e57002b6017281 to your computer and use it in GitHub Desktop.
Party Parrot Quine
▗▄▄▄▄▄███████▙▄▄▄▄▄.
▄▄████▛▀▀▀▀▀!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@;▜█
████████████████████████████████████████████████████████████████████████████████████████████████████████▌
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)
}
<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>
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