👕

Quineで会社のエンジニアパーカーを作成した話

2025/02/28に公開

はじめに

この記事では会社のエンジニアパーカーのデザインをQuine(クワイン)で作成する際に勉強になったことなどを紹介します。

前提として、著者はQuineについては全くの初心者でパーカーのデザインを作成するために一からQuineの作成方法を学びました。

Quineとは?

実行すると自分自身を出力するプログラムをQuineといいます。

Rubyでいえば、次のように動くプログラムはQuineです。

$ ruby quine.rb > quine2.rb

$ diff -s quine.rb quine2.rb
ファイル quine.rb と quine2.rb は同一です

RubyでQuineを作成する方法自体は「あなたの知らない超絶技巧プログラミングの世界」を本で学びました。
単純なQuine以外にも◯◯禁止やアニメーションなどを使ったQuine作品も紹介されており、読み物としても非常に面白かったのでおすすめです。

https://gihyo.jp/book/2015/978-4-7741-7643-7

Quineでロゴをカラーで出力する

先ずはQuineのベースとなるデザインを決めます。今回は弊社のプロダクトである「リプルア」のロゴを使用しました。

リプルアロゴ

こちらのロゴ画像をアスキーアートに変換します。画像をアスキーアートに変換する方法はいくつかありますが、今回は手っ取り早くGoogleで検索して一番最初に出てきた変換ツールを使用してアスキーアートを作成しました。

作成したAA

これをこのままQuineとして出力しても良いのですが、パーカーにした時に映えるようにせっかくであればカラーで出力していきたいと思います。

Quineをカラーで出力する方法についてはFindy Tech Blogの記事を参考にさせていただきました。

https://tech.findy.co.jp/entry/2024/05/23/093756

記事も参考にさせていただきつつ作成した、Quineのコードを生成するためのソースコード ( quine_generate.rb ) がこちらです。

# アスキーアートのテンプレート
aa = <<END
11111111111111111111111110000000111111110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
11111111111111111111111110000000111111111111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
11111111111111111111111110000000111111111111111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
11111111111111111111111110000000111111111111111110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
11111111111111111111111110000000111111111111111111100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
11111111111111111111111110000000111111111111111111110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
11111111111111111111111110000000111111111111111111111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
11111111111111111111111110000000111111111111111111111100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
11111111111111111111111110000000111111111111111111111110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
11111111111111111111111110000000111111111111111111111110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
11111111111111111111111110000000111111111111111111111111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
11111111111111111111111110000000111111111111111111111111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
11111111111111111111111110000000111111111111111111111111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
11111111111111111111111110000000111111111111111111111111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
11111111111111111111111110000000111111111111111111111110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
11111111111111111111111110000000111111111111111111111110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
11111111111111111111111110000000111111111111111111111100000000000000000111110001111110000000000011000000000000000000000011111000000000000011111000111111001111111000000111111110000000000111111100000000
11111111111111111111111110000000111111111111111111111000000000000000001111111111111110000111111111111110000000011111111111111111110000000111111111111111001111111000000111111110000001111111111111111111
11111111111111111111111110000000111111111111111111111000000000000000001111111111111110001111111111111111100000011111111111111111111100000111111111111111001111111000000111111110000111111111111111111111
11111111111111111111111110000000111111111111111111100000000000000000001111111111111110011111111000011111111000011111111111111111111100000111111111111111001111111000000111111110001111111111111111111111
11111111111111111111111110000000111111111111111110000000000000000000001111111110000000011111110000001111111000011111111100001111111110000111111110000000001111111000000111111110001111111110000111111111
11111111111111111111111110000000111111111111111000000000000000000000001111111100000000111111111111111111111000011111110000000011111111000111111100000000001111111000000111111110011111111000000001111111
11111111111111111111111110000000111111111111000000000000000000000000001111111000000000111111111111111111111000011111110000000011111111000111111100000000001111111000000111111110011111111000000001111111
11111111111111111111111110000000111111100000000000000000000000000000001111111000000000011111110000000000000000011111111000000111111111000111111100000000001111111000000111111110011111111100000011111111
11111111111111111111111110000000000000000000000000000000000000000000001111111000000000011111110000001111110000011111111111111111111110000111111100000000001111111100001111111110001111111111111111111111
11111111111111111111111110000000000000000000000000000000000000000000001111111000000000011111111111111111111000011111111111111111111100000111111100000000001111111111111111111100000111111111111111111111
11111111111111111111111110000000000000000000000000000000000000000000001111111000000000000111111111111111100000011111111111111111111000000111111100000000000111111111111111111000000011111111111111111111
11111111111111111111111110000000000000000000000000000000000000000000001111111000000000000001111111111110000000011111110111111111000000000111111100000000000001111111111111000000000000011111111001111111
11111111111111111111111110000000111111111111111111111111000000000000001111111000000000000000011111111000000000011111110001111100000000000111111100000000000000001111111000000000000000000111100000011100
01111111111111111111111110000000111111111111111111111111000000000000000000000000000000000000000000000000000000011111110000000000000000000000000000000000000000000000000000000000000000000000000000000000
01111111111111111111111110000000111111111111111111111111000000000000000000000000000000000000000000000000000000011111110000000000000000000000000000000000000000000000000000000000000000000000000000000000
00111111111111111111111110000000111111111111111111111110000000000000000000000000000000000000000000000000000000011111110000000000000000000000000000000000000000000000000000000000000000000000000000000000
00111111111111111111111110000000111111111111111111111110000000000000000000000000000000000000000000000000000000011111110000000000000000000000000000000000000000000000000000000000000000000000000000000000
00011111111111111111111110000000111111111111111111111100000000000000000000000000000000000000000000000000000000011111110000000000000000000000000000000000000000000000000000000000000000000000000000000000
00001111111111111111111110000000111111111111111111110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000011111111111111111110000000111111111111111111100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000001111111111111111110000000111111111111111110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000001111111111111110000000111111111111111100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000011111111111110000000111111111111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
END

blue_colors = [
  0..15,
  200..211,
  400..408,
  600..606,
  800..804,
  1000..1003,
  1200..1202,
  1400..1401,
  1600..1600,
  1800..1800,
  2800..2800,
  3000..3001,
  3200..3202,
  3400..3403,
  3600..3603,
  3800..3805,
  4000..4007,
  4200..4209,
  4400..4412,
  4600..4617,
  4800..4824,
  5000..5024,
  5200..5224,
  5400..5424,
  5600..5624,
  5801..5824,
  6001..6024,
  6202..6224,
  6402..6424,
  6603..6624,
  6804..6824,
  7006..7024,
  7207..7224,
  7410..7424,
  7612..7624,
]
cyan_colors = [
  16..25,
  212..225,
  409..425,
  607..625,
  805..825,
  1004..1025,
  1203..1225,
  1402..1425,
  1601..1625,
  1801..1825,
  2000..2025,
  2200..2225,
  2400..2425,
  2600..2625,
  2801..2825,
  3002..3025,
  3203..3225,
  3404..3425,
  3604..3625,
  3806..3825,
  4008..4025,
  4210..4225,
  4413..4425,
  4618..4625,
]
red_colors = [
  31..40,
  231..244,
  431..447,
  631..649,
  831..851,
  1031..1052,
  1231..1253,
  1431..1454,
  1631..1655,
  1831..1855,
  2031..2056,
  2231..2256,
  2431..2456,
  2631..2656,
  2831..2855,
  3031..3055,
  3231..3254,
  3431..3453,
  3631..3653,
  3831..3851,
  4031..4049,
  4231..4247,
  4431..4444,
  4631..4639,
]
green_colors = [
  5632..5655,
  5832..5855,
  6032..6055,
  6232..6254,
  6432..6454,
  6632..6653,
  6832..6852,
  7032..7050,
  7232..7249,
  7432..7447,
  7632..7644,
]

blank_color = 0

start_text = 'eval$s=%w'
end_text = '.join'

aa_data = aa.split("\n")
x_length = aa_data.first.length
y_length = aa_data.length
last_point = aa_data.last.split('1').last.length
end_text_start_point = last_point + end_text.length
output_text_length = "\e[32m#\e[m".length

bits = aa.gsub("\n", '').reverse.to_i(2)

bin = [Marshal.dump(bits)].pack('m').gsub("\n", '')
blue_colors_text = blue_colors.to_s.gsub(' ', '').gsub("\n", '')
cyan_colors_text = cyan_colors.to_s.gsub(' ', '').gsub("\n", '')
red_colors_text = red_colors.to_s.gsub(' ', '').gsub("\n", '')
green_colors_text = green_colors.to_s.gsub(' ', '').gsub("\n", '')

code = <<CODE
  b=eval("#{blue_colors_text}").flat_map(&:to_a)
  c=eval("#{cyan_colors_text}").flat_map(&:to_a)
  r=eval("#{red_colors_text}").flat_map(&:to_a)
  g=eval("#{green_colors_text}").flat_map(&:to_a)
  n=Marshal.load("#{bin}".unpack("m")[0])
  l=#{"'".ord}.chr
  m=#{"\e".ord}.chr
  e="#{start_text}"+l+($s*2)
  o=""
  j=-1

  0.upto(#{y_length * x_length - 1}){|i|
    t=b.include?(i)?34:c.include?(i)?36:r.include?(i)?31:g.include?(i)?32:#{blank_color}
    o<<(n[i]==1?m+"["+t.to_s+"m"+e[j+=1]+m+"[m":#{' '.ord})
    o<<(i%#{x_length}==#{x_length - 1}?#{"\n".ord}:"")
  }
  o[-#{end_text_start_point + end_text.length * output_text_length},#{(end_text.length + 1) * output_text_length}]=l+"#{end_text}"
  puts(o+m+"[0m")
CODE

code = code.split("\n").join(';').gsub(' ', '')
code << '#'

file = File.new('quine/quine_base.rb', 'w')
file.puts "#{start_text}'#{code}'#{end_text}"

quine_generate.rb を実行することでQuineのコード ( quine_base.rb ) が得られます。

eval$s=%w'b=eval("[0..15,200..211,400..408,600..606,800..804,1000..1003,1200..1202,1400..1401,1600..1600,1800..1800,2800..2800,3000..3001,3200..3202,3400..3403,3600..3603,3800..3805,4000..4007,4200..4209,4400..4412,4600..4617,4800..4824,5000..5024,5200..5224,5400..5424,5600..5624,5801..5824,6001..6024,6202..6224,6402..6424,6603..6624,6804..6824,7006..7024,7207..7224,7410..7424,7612..7624]").flat_map(&:to_a);c=eval("[16..25,212..225,409..425,607..625,805..825,1004..1025,1203..1225,1402..1425,1601..1625,1801..1825,2000..2025,2200..2225,2400..2425,2600..2625,2801..2825,3002..3025,3203..3225,3404..3425,3604..3625,3806..3825,4008..4025,4210..4225,4413..4425,4618..4625]").flat_map(&:to_a);r=eval("[31..40,231..244,431..447,631..649,831..851,1031..1052,1231..1253,1431..1454,1631..1655,1831..1855,2031..2056,2231..2256,2431..2456,2631..2656,2831..2855,3031..3055,3231..3254,3431..3453,3631..3653,3831..3851,4031..4049,4231..4247,4431..4444,4631..4639]").flat_map(&:to_a);g=eval("[5632..5655,5832..5855,6032..6055,6232..6254,6432..6454,6632..6653,6832..6852,7032..7050,7232..7249,7432..7447,7632..7644]").flat_map(&:to_a);n=Marshal.load("BAhsKwLeAf///wH/AAAAAAAAAAAAAAAAAAAAAAAAAAD///8B/w8AAAAAAAAAAAAAAAAAAAAAAAAA////Af9/AAAAAAAAAAAAAAAAAAAAAAAAAP///wH//wEAAAAAAAAAAAAAAAAAAAAAAAD///8B//8HAAAAAAAAAAAAAAAAAAAAAAAA////Af//DwAAAAAAAAAAAAAAAAAAAAAAAP///wH//x8AAAAAAAAAAAAAAAAAAAAAAAD///8B//8/AAAAAAAAAAAAAAAAAAAAAAAA////Af//fwAAAAAAAAAAAAAAAAAAAAAAAP///wH//38AAAAAAAAAAAAAAAAAAAAAAAD///8B////AAAAAAAAAAAAAAAAAAAAAAAA////Af///wAAAAAAAAAAAAAAAAAAAAAAAP///wH///8AAAAAAAAAAAAAAAAAAAAAAAD///8B////AAAAAAAAAAAAAAAAAAAAAAAA////Af//fwAAAAAAAAAAAAAAAAAAAAAAAP///wH//38AAAAAAAAAAAAAAAAAAAAAAAD///8B//8/AICPHwADAAAfAHz8/IF/AP4A////Af//HwDA/x/+f4D//wP+//yBf+D//////wH//x8AwP8f//+B//8P/v/8gX/4//////8B//8HAMD/n3/4h///D/7//IF//P//////Af//AQDAf4A/8If/8B/+AfyBf/yH/////wH/fwAAwD/A//+HP8A//gD8gX/+Af7///8B/w8AAMAfwP//hz/AP/4A/IF//gH+////AX8AAADAH4A/AIB/4D/+APyBf/4D/////wEAAAAAwB+AP/CD//8f/gD8w3/8//////8BAAAAAMAfgP//h///D/4A/P8/+P//////AQAAAADAHwD+/4H//wf+APj/H/D//////wEAAAAAwB8A+H+Av/8A/gDg/wOAf/7///8B////AMAfAOAfgD8+AP4AAH8AAB44/v//Af///wAAAAAAAIA/AAAAAAAAAAAAAP7//wH///8AAAAAAACAPwAAAAAAAAAAAAD8//8B//9/AAAAAAAAgD8AAAAAAAAAAAAA/P//Af//fwAAAAAAAIA/AAAAAAAAAAAAAPj//wH//z8AAAAAAACAPwAAAAAAAAAAAADw//8B//8PAAAAAAAAAAAAAAAAAAAAAAAAwP//Af//BwAAAAAAAAAAAAAAAAAAAAAAAID//wH//wEAAAAAAAAAAAAAAAAAAAAAAAAA/P8B//8AAAAAAAAAAAAAAAAAAAAAAAAAAPD/Af8P".unpack("m")[0]);l=39.chr;m=27.chr;e="eval$s=%w"+l+($s*2);o="";j=-1;;0.upto(7799){|i|;t=b.include?(i)?34:c.include?(i)?36:r.include?(i)?31:g.include?(i)?32:0;o<<(n[i]==1?m+"["+t.to_s+"m"+e[j+=1]+m+"[m":32);o<<(i%200==199?10:"");};o[-206,54]=l+".join";puts(o+m+"[0m")#'.join

こちらの quine_base.rb を実行し、最終的に以下を出力として得ることができます。

Quine

今回のQuineでは5色も使っているため、色情報を保持するためのコードがQuine内でなかなか収まり切らず苦労しましたが、最終的にはロゴのフォントサイズを少し大きめにすることで微調整をしました。

Quineでエンジニアパーカーを作成する

最終的にQuineをエンジニアパーカーにするために、完成したQuineの画像を最低350dpi以上で書き出しパーカーにしました。

本当はパーカーの前面にロゴを載せたかったのですが、業者によると前面だとプリントが小さくなってしまい、Quineのソースコードの文字が潰れて読めなくなってしまうという懸念があったため、今回は背中側に大きくプリントしました。

最終的に完成したQuineパーカーがこちらです。

Quineパーカー

写真では少し分かりづらいですが、文字も潰れず綺麗にプリントすることが出来ました。

さいごに

今回はQuineで会社のエンジニアパーカーを作成したお話を紹介しました。作成したエンジニアパーカーは会社のイベント事などで着ていきたいと思います。

ふとしたきっかけで触ることになったQuineですが普段業務ではあまり使わないRubyのテクニックなども学ぶことで出来て良い経験になりました。

ぜひ皆さんもQuineに入門してみてはいかがでしょうか。

GitHubで編集を提案
株式会社Inner Resource

Discussion