Skip to content

Instantly share code, notes, and snippets.

@pnlybubbles
Last active January 4, 2020 09:58
Show Gist options
  • Save pnlybubbles/258114a354ab5d79dc28 to your computer and use it in GitHub Desktop.
Save pnlybubbles/258114a354ab5d79dc28 to your computer and use it in GitHub Desktop.
Ruby Quine Generator. 2015年の書き初めです。
require "rmagick"
# require "pp"
raise "input file name" unless ARGV[0]
raise "file not found" unless File.exist?(ARGV[0].strip)
image = Magick::ImageList.new(ARGV[0])
img_depth = image.depth
fill = ARGV[1] || "#FFFFFF"
fill = "##{fill.upcase}" if fill !~ /^#/
STDERR.puts "Fill color:"
STDERR.puts fill
scale_factor = (ARGV[2] || ARGV[1]).chomp.to_i
scale_factor = 3 if scale_factor == 0
STDERR.puts "Scale factor:"
STDERR.puts scale_factor
font_size = [scale_factor, scale_factor * 2]
def font_size.x
self[0]
end
def font_size.y
self[1]
end
width = image.columns / font_size.x
height = image.rows / font_size.y
STDERR.puts "Input file size: "
STDERR.puts [image.columns, image.rows].to_s
STDERR.puts "Output quine size: "
STDERR.puts [width, height].to_s
img_da = []
prominent = {}
# pixel obj to dict array
image.each_pixel { |p, x, y|
img_da[y] ||= []
color = p.to_color(Magick::AllCompliance, false, img_depth, true)
img_da[y][x] = color
prominent[color] ||= 0
prominent[color] += 1
}
STDERR.puts "Prominent color: "
STDERR.puts prominent.sort_by { |_, v| v }.reverse[0..10].map { |v| v[0] }.join(' ')
# pixel data to 0,1
img_dot = (0...height).map { |y|
(0...width).map { |x|
(0...font_size.y).map { |fy|
(0...font_size.x).map { |fx|
img_da[y * font_size.y + fy][x * font_size.x + fx] == fill ? 1 : -1
}
}.flatten.inject(:+) >= 0 ? 1 : 0
}
}
# fix orphan pixels
chunk_start = []
chunk = 0
img_dot.each_with_index { |line, y|
line.each_with_index { |cell, x|
if cell == 1
chunk_start = [x, y] if chunk == 0
chunk += 1
else
break if chunk != 0
end
}
break if chunk != 0
}
ORPHAN_THRESHOLD = 10
if chunk < ORPHAN_THRESHOLD
# print to stdout
STDERR.puts "Fix orphan pixels: #{chunk}"
start_x = chunk_start[0] + chunk / 2 - ORPHAN_THRESHOLD / 2
start_y = chunk_start[1]
start_x = width - ORPHAN_THRESHOLD if start_x + ORPHAN_THRESHOLD > width
start_x = 0 if start_x < 0
ORPHAN_THRESHOLD.times { |i|
img_dot[start_y][start_x + i] = 1
}
end
# print to stdout
STDERR.puts "AA data: "
STDERR.puts img_dot.map { |r| r.join }.join("\n")
# output quine
bits = img_dot.flatten.join.reverse.to_i(2)
bin = [Marshal.dump(bits)].pack("m").gsub("\n", "")
$s = "b=\"#{bin}\";n=Marshal.load(b.unpack(\"m\")[0]);e=\"eval$s=%w\"<<39<<($s*<circulation-count>);o=\"\";j=-1;0.upto(#{height}*#{width}-1){|i|o<<((n[i]==1)?e[j+=1]:32);o<<((i%#{width}==#{width-1})?10:\"\")};o[-7,6]=\"\"<<39<<\".join\";puts(o)#"
fill_count = img_dot.flatten.join.count("1")
if fill_count < $s.gsub(/<circulation-count>/, "*").length
raise "AA's fill area is too small"
end
circulation_count = (fill_count.to_f / $s.gsub(/<circulation-count>/, "").length).ceil + 1
$s.gsub!(/<circulation-count>/, circulation_count.to_s)
STDERR.puts "quine: "
eval $s
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment