『第5回デスマコロシアム』に参加しました

はじめに

『第5回デスマコロシアム』に参加しました。

「第5回デスマコロシアム」問題のトーナメント結果発表です!──優勝者は…! #デスマコロシアム|CodeIQ MAGAZINE

要は FizzBuzz ですが、15の倍数のときは大文字の FIZZBUZZ にするというのが特徴です。

問題を読んで、とりあえず書いた Perl (51文字) のコードに「とりあえず」とだけコメントを付けて、とりあえず提出しました。後で日々の集計を見て、短く書けそうな言語で再提出するつもりでした。「とりあえず」のコードは、暫定の全言語最短になりました。

ところが、それから何日経っても記録が更新されず、帯状疱疹を患って入院したり退院したりしているうちに締め切りを迎えました。結局、「とりあえず」のコードのまま最短賞をいただきました。チャンピオンバッジは、これで4回連続4個目です。(「決勝進出おめでとうございます!」と書いてあるのですが、決勝どころか準決勝にすら進んだことがない私が4個も持っていていいのかという妙な背徳感が…)

コード

print$_%5?$_%3?$_:fizz:$_%3?buzz:FIZZBUZZ for 1..50

Perl (51文字) です。5の倍数かどうかで場合分けし、それぞれの場合を3の倍数かどうかで場合分けするという、なんの工夫もないコードですが、これで実際に最短賞が取れてしまったので仕方ありません。

ちなみに、15の倍数のときが小文字の fizzbuzz ならば工夫の余地があって、 Perl で 45 文字で書けますので、挑戦してみてください(ググる前に!)

 

『3番目のダンジョン』私の解答

テンプレ

この問題(に限らずダンジョンシリーズ)の面白いところは、定められた文字数制限と使用禁止文字列を満たす JavaScript のコードを提出すれば正解となってバッジが付与されるのですが、実際には場外(Twitter とか CodeIQ MAGAZINE とか)で出題者様主導で解答コードの短さが競われているのです! いわゆるコードゴルフです。このことは問題文には一切書かれていなくて、バッジほしさに適当に解答を送ると、知らずにコードゴルフに引きずり込まれるという、恐ろしい罠が仕掛けられているのです!

はじめに

今回の問題は、相異なる5個の数値 a, b, c, d, e が与えられたときに、3番目の大きさのものを求める、というものでした。具体的な問題は、解説記事をご覧ください。

解説記事(CodeIQ MAGAZINE)
https://codeiq.jp/magazine/2014/08/14753/

今回もいつものように、途中経過を見ながら縮めていこうと思って、とりあえず様子見でコードを提出したのですが、いつもと違って途中経過の発表がほとんどありませんでした。いぶかしく思っているうちに、帯状疱疹を患って入院してしまい、入院中に締め切りとなってしまいました。しかし、蓋を開けてみると、とりあえず提出したコードがそのまま最短コードになっていました。途中経過の発表がほとんど無かったのは、そもそも動きがほとんど無かったからのようですね。

LV2の私のコード

(a<b)-(a>c)^(a>d)-(a<e)?arguments.callee(b,c,d,e,a):a

53文字です。コードゴルフでは忌み嫌われる優先順位の括弧が、なんと4組も使われています。しかし +/ が使用禁止なので、まあ仕方ないところです。

(a<b)-(a>c)^(a>d)-(a<e) の部分がやや解りにくいですが、 ^ の両辺を表にすると次のようになります。

条件(a<b)-(a>c) の値
a < b,c 1
b < a < c 0
c < a < b
b,c < a -1
条件(a>d)-(a<e) の値
d,e < a 1
d < a < e 0
e < a < d
a < d,e -1

この表から、 a の大きさが3番目のときに限り (a<b)-(a>c)^(a>d)-(a<e) が 0 になることがわかります。

LV2の私の面白コード

上記のような経緯のため提出はしませんでしたが、私の考えた面白いコードです。

f=Math.min,-f(-f(a,b,c),-f(a,b,d),-f(a,b,e),-f(a,c,d),-f(a,c,e),-f(a,d,e),-f(b,c,d),-f(b,c,e),-f(b,d,e),-f(c,d,e))

114文字です。 -f() だけで答えを求めています。

『ダブル数列のダンジョン』私の解答

テンプレ

この問題(に限らずダンジョンシリーズ)の面白いところは、定められた文字数制限と使用禁止文字列を満たす JavaScript のコードを提出すれば正解となってバッジが付与されるのですが、実際には場外(Twitter とか CodeIQ MAGAZINE とか)で出題者様主導で解答コードの短さが競われているのです! いわゆるコードゴルフです。このことは問題文には一切書かれていなくて、バッジほしさに適当に解答を送ると、知らずにコードゴルフに引きずり込まれるという、恐ろしい罠が仕掛けられているのです!

はじめに

今回の問題は、等差数列又は等比数列の最初の2項 a, b が与えられたときに、3項目を求める、というものでした。初項の値の範囲(10~99)と、公差・公比の値の範囲(2~9)とが定められているため、等差数列なのか等比数列なのかは一意に定まるようになっています。具体的な問題は、解説記事をご覧ください。

解説記事(CodeIQ MAGAZINE)
https://codeiq.jp/magazine/2014/06/11998/

今回も、各レベルでの最短コードに到達することができました。途中経過がほぼ2日ごとに発表されたのですが、LV3は、追い抜いたと思って次の発表を見たら先を行かれ、今度こそ追いついたと思ったらさらに突き放され、という状況で、大変精神的につらか楽しめました。幸運にも、最終的にはなんとか追いつくことができました。

私のLV3の最終コード

(b*(b/a<<13)^b%a)%8191

22文字です。LV3では、LV2の禁止文字列に加えて + - が禁止されているため、等差数列の場合の b*2-a の値をどうやって表現するかが最大の焦点になります。上記のコードでは、13ビットずらしてORをとり、さらに8191での剰余をとることによって、加算を表現しています。

私のLV3の途中経過

提出しなかったものも含めて、約60のコードを作りました。以下に全て公開します。

(b/a>=2)*(b*b/a)^!(b/a>=2)*(Math.log(Math.exp(b)*Math.exp(b)/Math.exp(a)*1.6))
m=Math,(b/a>=2)*(b*b/a)^(b/a<2)*(m.log(m.exp(b)*m.exp(b)/m.exp(a)*1.6))
m=Math,e=m.exp,(b/a>=2)*(b*b/a)^(b/a<2)*(m.log(e(b)*e(b)/e(a)*1.6))
e=Math.exp,(b/a>=2)*(b*b/a)^(b/a<2)*(Math.log(e(b)*e(b)/e(a)*1.6))
e=Math.exp,[Math.log(e(b)*e(b)/e(a)*1.6)^0,b*b/a][1>>b%a]
e=Math.exp,[Math.log(e(b)*e(b)/e(a)/.8)^0,b*b/a][1>>b%a]
(e=Math.exp)^[Math.log(e(b)*e(b)/e(a)/.8),b*b/a][1>>b%a]
e=Math.exp,[~Math.log(e(a)/e(b)/e(b)*.3),b*b/a][1>>b%a]
e=Math.exp,[~Math.log(e(a)/e(b)/e(b)/3),b*b/a][1>>b%a]
e=Math.exp,[~Math.log(e(a)/e(b*2)/3),b*b/a][1>>b%a]
m=Math,[~m.log(m.exp(a)/m.exp(b*2)/3),b*b/a][1>>b%a]
~~(b/a)*b*Math.log(Math.exp(b%a/b)*2.7183)^0
~~(b/a)*b*Math.log(Math.exp(b%a/b)/.36787)^0
(b/a^0)*b*Math.log(Math.exp(b%a/b)*2.7183)^0
!!(b%a)*((2*b^256)%(a^256))^!(b%a)*(b*b/a)
x=!(b%a),!x*(2*b^256)%(a^256)^x*b*b/a
x=b%a,(2*b^x<<8)%(a^x<<8)^!x*b*b/a
!(x=b%a)*b*b/a^(2*b^x<<8)%(a^x<<8)
!(x=b%a<<8)*b*b/a^(2*b^x)%(a^x)
x=b%a<<8,!x*b*b/a^(2*b^x)%(a^x)
x=b%a<<13,(2*b^x)%(a^=x)^b*b/a
(2*b^(x=b%a<<13))%(a^=x)^b*b/a
b*b/(a^=x=b%a<<13)^(2*b^x)%a
a^=x=b%a<<13,b*b/a^(b*2^x)%a
a^=!!(b%a)*b<<13,b*b/a^b*8194%a
a^=!!(b%a)*b<<8,b*b/a^b*258%a
a^=!!(b%a)*b<<7,b*b/a^b*130%a
a^=b>>!(b%a)*13<<7,b*b/a^b*130%a
a^=b*!!(b%a)<<7,b*b/a^b*130%a
a^=b/!!(b%a)<<7,b*b/a^b*130%a
a^=b*(b<a*2)<<7,b*b/a^b*130%a
a^=b*(2>>b/a)<<7,b*b/a^b*130%a
x=a^b%a<<13,b*b/x^(b*2^x^a)%x
a^=b%a<<13,b*b/a^(b*2^a^a%256)%a
a^=x=b%a<<13,b*b/a^(b*2^x)%a
!(b%a)*b*b/a^!!(b%a)*(a<<8^b*2)%257
!(b%a)*b*b/a^!!(b%a)*(b%a<<8^b)%255
!(b%a)*b*b/a^!!(b%a)*(b%a^b<<7)%127
!(b%a)*b*b/a^(b<<7^(b%=a))%127*!!b
!(b%a)*b*b/a^!!(b%a)*(a<<7^b)*2%257
(b%a^b<<11)%2047*~~(b/a)
(b%a^b<<10)%1023*~~(b/a)
(b^b%a<<10)%1023*~~(b/a)
(b*~~(b/a)^b%a<<13)%8191
(b*~~(b/a)<<13^b%a)%8191
(b*~~(b/a)^b%a<<26)%8191
(b%a^b*992)%991*~~(b/a)
(b%a^b*896)%895*~~(b/a)
(b%a^b<<13)%~(8191^b/a)
(b%a^b<<13)%(~8191^b/a)
(b%a^b*1e4)%(~9999^b/a)
(b%a^b*8032)%(~8031^b/a)
(b%a^b/a<<14)%(~16383^b)
(b%a^b/a<<14)%(16383^~b)
(b%a^b/a<<14)%~(16383^b)
(b<<13)%(a*~8191^b)%8191
b*8192%(a*~8191^b)%8191
(b*(b/a<<13)^b%a)%8191
(b*(b/a<<45)^b%a)%8191
(b*(b/a<<77)^b%a)%8191

『第4回デスマコロシアム』私の nasm (72)

はじめに

第4回デスマコロシアム』で、途中で提出した nasm (72) のコードです。途中集計の最終回の直前まで、このコードが単独で全言語最小でした。しかし、 Perl に抜かれる可能性があったため、自ら Perl (57) を提出し、この nasm のコードはお蔵入りとなりました。結局、 nasm (72) に naoki_kp さんが並びましたが、 Perl (57) を提出せずとも、この nasm (72) でも最小賞が取れていたようです。

コード

db`BCTYv	MU_OW]^j$ば~,z\u340X)蔔P<tqあ`

機械語バイトコードを直接文字コードで表現したものです。 v と M の間は TAB 文字です。

ソース

このコードのソースは、次の通りです。左の列がアセンブラのソース、中央がバイトコード(16進)、右が文字表現です。

inc edx        ; 42     ; B
inc ebx        ; 43     ; C
push esp       ; 54     ; T
pop ecx        ; 59     ; Y

L1:
jbe L2         ; 76 09  ; v[TAB]
dec ebp        ; 4d     ; M
push ebp       ; 55     ; U
pop edi        ; 5f     ; _
dec edi        ; 4f     ; O
push edi       ; 57     ; W
pop ebp        ; 5d     ; ]
pop esi        ; 5e     ; ^
push 36        ; 6a 24  ; j$

L2:
jnecx L2-126   ; e3 81  ; ば
mov al,7eh     ; b0 7e  ; ~
sub al,7ah     ; 2c 7a  ; ,z
int 80h        ; cd 80  ; \u340
pop eax        ; 58     ; X
sub eax,ebp    ; 29 e8  ; )蔔
xchg eax,esp   ; 94     ;
xchg eax,esp   ; 94     ;
push eax       ; 50     ; P
cmp al,116     ; 3c 74  ; <t
jno L1         ; 71 e3  ; qあ
db 81h,82h     ; 81 82  ;

解説

Perl (57) のコードと違って、この nasm (72) のコードは、さほどトリッキーなことはしていません。気を付けた点としては、

  • int 80h で \ を使うので、他では使わないようにした。
  • 同じレジスタを2つデクリメントする代わりに、別々のレジスタで1回ずつ dec するようにした。

といった程度です。しかし、文法上どうしても ` を2回使ってしまうため、文字数に対する乗率が2になってしまうことが避けられません。 Perl (57) よりこちらのほうが文字数は少ないのですが、大きさでは上回ってしまいます。

参考にしたもの

1.インテル:日本語技術資料のダウンロード:IA-32 アーキテクチャ

http://www.intel.co.jp/content/www/jp/ja/developer/download.html#ia32

特に「中巻 A」「中巻 B」は必須でした。

2.命令コード表

http://dl.dropboxusercontent.com/u/2476414/TechResources/x86_opcodemap_1_a4.pdf

『ハンド (逆) アセンブルのための x86 ニーモニックの覚え方』
http://d.hatena.ne.jp/a4lg/20120225/1330180431
に掲載されているものを使わせていただきました。ありがとうございます。これをプリントして、6日間ひたすら眺めていたら、妻に怪訝な顔をされましたw

 

『第4回デスマコロシアム』私の Perl (57)

はじめに

第4回デスマコロシアム』に参加しました。今回のルールは、

  • $&(*,.02468:<>@BDFHJLNPRTVXZ\^`bdfhjlnprt$(,048<@DHLPTX\`dhlpt$*06<BHNTZ`flr$,4<DLT\dlt$.8BLV`jt$0<HT`l$2@N\j$4DTdt
    という文字列を出力するプログラムを書く
  • 評価基準は【文字数×(重複文字数+1)】が小さいほど良い
  • 言語ペナルティやトーナメントはいつもと同じ

といったところです。要するに、短くて、しかもなるべく同じ文字を使わないものが高評価ということです。

出題者様による結果記事
https://codeiq.jp/magazine/2014/07/12364/

私は、最終的に Perl (57) でエントリして、最小賞をいただきました。前々回と前回に続いて、3度目の最小賞受賞です。おめでとうございます。ありがとうございます。

結果の公開後、私のコードに対して、ツイッター上で「すごい」「頭おかしい」「変態」など、見ず知らずの多くの方々からお褒めの言葉をいただきました。ゴルファー冥利に尽きます。

コードとその解説

私が提出した Perl (57) のコードは、次の通りです。

eval
q<悃int ch掃雛_四郎囎2抛36f恃雎刈40/把郛;>=~y{遂胄刊掛}[$o-s*.+]drx8

文字数 57、重複文字 0で、大きさは 57×(0+1) =57 です。

文字化けしているわけではありません。わけがわからないと思いますが、正直、書いた本人にもさっぱり読めません。ともあれ、1つずつ解釈していくことにします。

まず、

y{遂胄刊掛}[$o-s*.+]dr

の部分を見てみましょう。 Ideone.com では、日本語文字は UTF-8 として解釈されますから、1文字につき3バイト(例えば「遂」という文字は \xe9, \x81, \x82 の3バイト)です。 Perl の y/// は、どうやらマルチバイト文字は特に認識せずに、単純にバイト単位で置換を行うようで、この置換の対応を表にすると次のようになります。

元の文字置換後の文字
\xe9 $
\x81 o
\x82 p
\xe8 q
\x83 r
\x84 s
\xe5 *
\x88 .
\x8a +
\xe6 (削除)
\x8e
\x9b

この置換を、

悃int ch掃雛_四郎囎2抛36f恃雎刈40/把郛;

という文字列に適用しています。(なお、コードでこの文字列を囲んでいる q<......> というのは、展開を伴わない文字列を表す '......' の別記法です。)例えば先頭の「悃」という文字のバイト値は \xe6, \x82, \x83 なので、上の表によって pr という2文字に置換されることがわかります。文字列全体を置換すると、

print chr$_*$r*2+36for$*..40/++$r;

という文字列になります。

この文字列、どこかで見たな… という鋭い方もいらっしゃるかと思います。そう、結果記事で解説されている antimon2 さんの文字列とほとんど同じなのです(こういう一致は嬉しいですね(^^))。違いとしては、私の文字列では、 $k$r に、  0$* になっていますが、置換の圧縮効果のため、私の解法ではこのほうが文字数的に有利なのであって、コードとしての意味は一緒です。

この後のプログラムの動作は antimon2 さんの解説の通りで、 y/// に r オプションを付けることで結果の文字列を取得し、それを x8 で8回繰り返し、それを eval で評価しています(詳しくは結果記事の antimon2 さんの解説を読んでください…)。めでたし、めでたし。

結局、置換対象を日本語文字で表現する解法には、

  • 同じバイト値を複数回使っても、組み合わせを変えて別の文字にすることで、重複文字になるのを回避できる。
  • 最大でコード3文字を日本語1文字で表現できるので、圧縮が効く

という一石二鳥の利点があります。ちなみに、 NeoCat さんも全く同じアプローチだったようです。

余談:「機種依存文字

ところで、デスマコロシアムの解答のルールとして、 CodeIQ の入力欄で「機種依存文字」と判定される文字は使用禁止という制限があります。これは問題文には記載されていない(出題者様のブログのQ&Aに記載されている)細則なのですが、今回はこの細則が本質的に重要だったように思います。というのも、「機種依存文字」でない漢字は、 Unicode のコードポイントにおいて飛び飛びに(しかも不規則に)存在しているため、任意の3バイトを組み合わせた文字が解答で使用可能とは限らない(むしろ使用不可能な文字の方が圧倒的に多い)からです。

そこで、私の解法では、手探りでいろいろ試行錯誤して「遂」「胄」「刊」「掛」の4文字を選び、これらを構成するバイト値を組み替えて文字列を表現することにしました。その結果、日本語文字1文字でコード3文字を表現できたのは「刈」→ *.. の1か所だけで、他は全て1文字で1~2文字しか表現できていない、非効率なものになってしまっています。日本語文字の選択や置換の割り当てを最適化するツールを作って文字列を生成すれば、もっと短い解が得られたはずだと思いますが、今回は解答期間の多くを nasm での解の短縮につぎ込み、この Perl の解は途中集計の最終回になんとか間に合わせたため、そこまでする余裕がありませんでした。(解答をファイルでアップロードする形式か、 Ideone.com のURLを解答する形式だったら、「機種依存文字」を気にしなくていいので、もう少し楽ができたなあ…と思います。)

 

『魔方陣ヌルヌル』別の解法

はじめに

CodeIQ で出題された『魔方陣ヌルヌル』について、公式のまとめ記事が出た後で解説を書こうと思って構想を温めていたのですが、公式まとめ記事に全部書かれてしまったため、ほとんど書くことがなくなってしまいました。

公式まとめ記事
https://codeiq.jp/magazine/2014/06/11455/

一応、問題の内容を大雑把に言うと、

  • タテ・ヨコ・ナナメの和が 0 になるような魔方陣を作る。
  • 方陣のサイズは 3×3,4×4,5×5 。
  • 方陣の各マスに入れる数は互いに異なる整数でなければならない。

といったところです。また、ここから暗黙にわかることとして、

  • 数は正の整数でなくてもよい。
  • 数は連続していなくてもよい。

ということが言えます(そうでないと作れない)。

素直な解法

公式まとめ記事では、通常の魔方陣を元に、各マスの数から一定の数を引く方法が紹介されています。私も 3×3 と 5×5 はこの方法で作りました。通常の魔方陣の作り方は、 Wikipedia の魔方陣の記事で非常に詳しく説明されています。

Wikipedia : 魔方陣
http://ja.wikipedia.org/wiki/%E9%AD%94%E6%96%B9%E9%99%A3

4×4 については、公式まとめ記事には「全体を2倍してから 17 を引く」方法と「9 以上の数だけから 17 を引く」方法が解説されていますが、私は「1~16 を -8~8 (ただし途中 0 を飛ばす)にマッピングする」という方法を採りました。まあ、結果的には「9 以上の数だけから 17 を引く」方法と大して変わりません。

別の解法

公式まとめ記事にも Wikipedia にも紹介されていない、別の解法を1つ紹介したいと思います。例として 5×5 のサイズを作りますが、他のサイズにも応用可能です。

まず、和が 0 になるように、中央の1行を次のように作ります。

         
         
-2 -1 0 1 2
         
         

これを左右に巡回させながら、上下の行に並べます。

0 1 2 -2 -1
-1 0 1 2 -2
-2 -1 0 1 2
2 -2 -1 0 1
1 2 -2 -1 0

これで、タテ・ヨコ・ナナメの和が 0 の行列を作ることができました。しかし、このままでは、同じ数が使われてしまっています。

次に、これと同じようなものを、もう1つ作ります。ただし、中央の行は

-10 -5 0 5 10

とし、巡回の向きも逆になるようにします。

5 10 -10 -5 0
10 -10 -5 0 5
-10 -5 0 5 10
-5 0 5 10 -10
0 5 10 -10 -5

もちろん、この行列も、タテ・ヨコ・ナナメの和が 0 になっていますね。

最後に、この2つの行列を加えます。

0 1 2 -2 -1
-1 0 1 2 -2
-2 -1 0 1 2
2 -2 -1 0 1
1 2 -2 -1 0
5 10 -10 -5 0
10 -10 -5 0 5
-10 -5 0 5 10
-5 0 5 10 -10
0 5 10 -10 -5
5 11 -8 -7 -1
9 -10 -4 2 3
-12 -6 0 6 12
-3 -2 4 10 -9
1 7 8 -11 -5

これで 5×5 ヌルヌル魔方陣の完成です!

めでたし、めでたし。

ちなみに、サイズが奇数×奇数の場合には、この方法で数が連続するヌルヌル魔方陣を作ることができるので、最小の数が 1 になるように全体に一定の数を加えれば、普通の魔方陣になります。

『Ruby警官から警告を受けろ』環境構築チュートリアル(超初心者向け)

(6/1 環境構築の説明を書き直しました。誤りがありましたら是非ご指摘ください。ここには書かれていないハマリ点とその回避方法の情報も歓迎します。)

はじめに

Ruby警官から警告を受けろ Lv1』 https://codeiq.jp/ace/tbpgr_badge/q883

Ruby警官から警告を受けろ Lv2』 https://codeiq.jp/ace/tbpgr_badge/q884

oO(これ、面白そうなんだけど、なんか準備がいろいろ大変そうだなあ… あまり大変じゃないのならやってみたいんだけど、実際どうなんだろう…)

そんなことを思っている人、多いと思います。私もそうでした。でも、実際は意外と簡単ですよ。特に5月27日に出題者様が32ビット版の環境を用意してくださってからは、私の Celeron M のような廉価な CPU でも、問題なく環境構築できるようになりました。必要なのは、

  • Windows (7 及び Vista で確認済み)
  • 2~3GB の HDD 空き容量
  • 1~2時間ほどの作業時間

といったところです。え、2時間もかかるのかって? ダウンロードするものが全部で 700MB ほどありますので、環境にもよりますが、私のようにネット代をケチって ADSL 接続にしていたりすると、どうしても時間がかかってしまいますね… まあ、作業時間の半分がダウンロード待ち時間、残りも後述の vagrant up というコマンドの待ち時間です。

必要なモノの紹介

とは言っても、やっぱり不安で踏み切れないと思います。私の場合、今思えば、不安を感じていた最大の要因は、必要とされるモノの正体がわからない、というところにありました。そこで、これらのモノの正体を簡単に紹介したいと思います。

まずは、自分で用意する必要のない(自動で準備される)モノです。

Ruby

これはさすがにいいですよね。 Ruby スクリプトを実行するための処理系です。バージョンが指定されていますが、自動で準備されるので、気にしなくて構いません。そもそも、書いたスクリプトは、実行するのではなく、静的解析するだけですので、処理系のことはまったく気にする必要がありません。

rubocop

Ruby スクリプトを静的解析して警告を出してくれるツールのです。この rubocop で警告をわざと多く出すのが、この問題の目的です。 rubocop の出す警告の種類は設定で変更することができ、問題の Lv1 用と Lv2 用とで異なる設定が用意されています。

rspec

書いたスクリプトが 100文字以下という条件を満たすか、 rubocop の警告数がバッジの目標数に届いているか、といったチェックをやってくれるツールです。解答スクリプトを書いて rspec を実行すれば、内部で自動的に rubocop が呼ばれて、どのバッジが取得できるか判定してくれます。なので、環境構築後の作業ルーチンは、解答スクリプトの修正と rspec の実行との繰り返しです。

Ubuntu

「俺 Windows だし…」と、これで腰が引けている人が多いのではないかと思いますが、心配いりません。読み進めていただければわかりますが、すべて Windows 上で実行できます。とは言っても、上記の Ruby, rubocop, rspecUbuntu 上に用意されますので、 Linux のシェル(bash)の知識が多少必要です。 cd とか ls とかの基本コマンドを使えるのに加えて、 vi でスクリプトの書き換えができると作業が格段に捗ります。

そして、以下は自分で用意する必要があるモノです。

VirtualBox

仮想マシンの実行環境です。これで Windows 上で Ubuntu を実行します。上記の rubocop や rspec は、 VirtualBox 上の Ubuntu で実行されることになります。 VirtualBox の入手方法は、後で紹介する出題者様の「環境構築について」に書かれています。

Vagrant

設定通りの VM仮想マシン)を全自動で構築してくれるコマンドラインツールです。この問題では、指定のバージョンの Ruby, rubocop, rspec をセットアップした UbuntuVM をコマンド一発で作ってくれます(ただし数十分かけて)。 Vagrant の入手方法も、出題者様の「環境構築について」に書かれています。

Git (または ZIP 展開ツール

出題者様がこの問題用に用意した各種ファイル(Vagrant の設定ファイルや各レベル用の作業環境)を取得するために Git というツールが必要ですが、なければ zip ファイルをダウンロードして展開するのでも構いません(私はそうしました)。

SSH クライアント

VirtualBox 上で実行されている Ubuntu に接続するために SSH が必要です(Vagrant から呼ばれます)。Git をインストールするとついてくるようですが、私はたまたま Cygwin 使いなので持っていました。

環境構築

さて、環境構築の具体的なやり方ですが、出題者様の「環境構築について」と「環境構築支援ツール」の README.md に素直に従うのが吉です。

「環境構築について」
http://d.hatena.ne.jp/tbpg/20140520/1400595738

「環境構築支援ツール」(GitHub リポジトリ
https://github.com/tbpgr/codeiq_vagrant_for_rubocop

「環境構築について」の「ホストOSにVagrantVirtualBox環境がないが、これから入れようとしている場合」に基づいて、下準備やハマり点などを補足しつつ説明します。ホスト OS としては、とりあえず Windows Vista 32bit 版を仮定しますので、適宜読み替えてください。

SSH クライアントをインストール

「環境構築について」には明示されていませんが、作成した VM に接続するために VagrantSSH クライアントを要求しますので、先に準備しておきましょう。

SSH クライアントは、 Windows ネイティブ版でも Cygwin 版でも構いません。普段 Cygwin を使っている人は、http://www.cygwin.com/ の setup-86.exe で追加するのが最も簡単です。ただし、 VagrantCygwin ターミナル上で実行する必要があります。

どうせならネイティブの SSH がいいという方は、 Git をインストールすれば一緒に付いてくるようです(未確認)。もちろん OpenSSH など SSH 単独で提供されているものを入手しても構いません(未確認)。または  TeraTermPuTTY など GUISSH クライアントでも構いませんが、この場合には後述の vagrant ssh コマンドが利用できないため、自分で VM に接続することになります。

VirtualBox のインストール

https://www.virtualbox.org/wiki/Downloads からインストーラ(100MB程度)をダウンロードできます。 Windows 用は「VirtualBox 4.3.12 for Windows hosts - x86/amd64」一択なので、迷うことはないと思います。ダウンロードしたものを実行すると、ネットワークドライバなどのデバイスドライバWindows にびしばしとインストールされますが、驚かないようにしましょう。

環境変数 PATH に VirtualBox のパスを追加

VirtualBoxVagrant から呼び出されるため、 VirtualBox のパスが環境変数 PATH に含まれている必要がありますが、 VirtualBoxインストーラは PATH の設定をしてくれませんので、自分でやる必要があります。 VirtualBox のパスは、デフォルトのインストール設定では C:\Program Files\Oracle\VirtualBox です。

念のため、環境変数の設定の仕方を説明しておきます。エクスプローラで「コンピュータ」を右クリックして「プロパティ」を選択(または、ショートカット [Win]+[Pause/Break])で「システム」ウィンドウを開きます。その中に「システムの詳細設定」というのがありますのでクリックすると別のウィンドウが開きます。その中の下のほうに「環境変数」というボタンがありますのでクリックします。すると「ユーザの環境変数」「システムの環境変数」という上下2段のボックスが現れますので、下側の「システムの環境変数」のボックスの中から「Path」という項目を選択して、ボックスの下の「編集」ボタンをクリックします。「変数名」「変数値」という項目が表示されますので、「変数値」の文字列の最後に「;」(半角セミコロン)を追加し、その後に VirtualBox のパスを追加してください。このとき、最初から設定されている文字列を誤って消したり書き換えたりしないよう、くれぐれもご注意ください。

Vagrant のインストール

http://www.vagrantup.com/downloads.html からインストーラ(200MB程度)をダウンロードできます。こちらも Windows 用は「WINDOWS Universal (32 and 64-bit)」一択なので、迷うことはないと思います。

支援ファイルの取得

出題者様が用意してくれた、この問題用の支援ファイルを、上記「環境構築支援ツール」(GitHubリポジトリ)から取得します。「支援」と言いつつ、問題挑戦に不可欠なファイルが含まれていますので、 Vagrant を使うつもりのない剛の者も、とりあえず一式取得しましょう。

取得は、 Git でリポジトリを clone する方法と、 ZIP ファイルをダウンロードして展開する方法がありますが、ここでは後者を説明します。「環境構築支援ツール」ページの右端にある「Download ZIP」というボタンをクリックして ZIP ファイルをダウンロードし、適当なフォルダに展開してください。

32bit 版 Ubuntu を利用するための設定

ダウンロードした支援ファイルは、そのままの状態では、 64bit 版 Ubuntu を利用する設定になっていますので、 32bit 版 Ubuntu を利用するように変更します。 Vagrantfile というファイルの

config.vm.box = "precise64"

という行を

config.vm.box = "precise32"

に書き換えればOKのようです(「ようです」というのは、私がやったときには確か書き換えるべき行が2行指定されていたはずで、現状を未確認なので)。

Vagrantfile の書き換えは、やらなくても動く可能性はありますが、やれば確実にハマリ要因が減ります。特に、 Celeron M など一部の廉価 CPU では 64bit 版 UbuntuVM が動かないため、この場合には必須の作業になります。

VM の構築及び起動

コマンドプロンプトCygwinSSH を使う予定の場合には Cygwin ターミナル)を開き、 ZIP を展開したフォルダに移動して、そこで

vagrant up

を実行します。初回実行時には、まず VM が作成されます。このとき、 300MB 程度のファイルのダウンロードに引き続いて、数十分の構築作業が行われますので、気長に待ちましょう。

構築が終わると、引き続いて VM が起動します(2回目以降に VM を起動するときも vagrant up です)。ここで VM が起動せずエラーが表示される場合には、  VirtualBox GUIOracle VM VirtualBox マネージャー)から VM を起動してみましょう。そこで「VT-x is not available. (VERR_VMX_NO_VMX).」と表示される場合には、 64bit 版 Ubuntu が動かない環境でそれを動かそうとしている可能性が大です。こうなってしまった場合には、一旦 vagrant destroyVM を削除し、その後 Vagrantfile を 32bit 用に書き換え、再度 vagrant up しましょう。

VM に接続

上記に引き続き、同じディレクトリで vagrant ssh を実行します(PuTTY 等を利用する場合には、 vagrant ssh で表示されるエラーメッセージに従って、自分で接続します)。

これで問題に挑戦する準備が整いました。

別の方法

このように、 VirtualBox のインストールと Vagrant のインストールと Git の clone さえすれば、あとは vagrant up 一発で必要な環境が全て整うように、出題者様がすべて用意してくださっているのです。しかし、 VirtualBoxVagrant がそれぞれ巨大でディスク容量を食ううえに、 vagrant up の初回の実行は非常に時間がかかります。最初に「2~3GB」「1~2時間」と書いたのは、この辺りの事情のためです。

そこで、ここまでのコストをかけたくないという人は、もっと簡易に環境を構築することも、一応できます。問題を解いてチェックするために絶対に必要なのは rubocop と rspec だけなので、これを Windows に直接インストールしてしまえばいいのです。

それぞれの方法の特徴を比較してみます。

VirtualBox + Vagrant + Ubuntu を利用する方法

利点

  • 適切なバージョンの Ruby, rubocop, rspec が自動で準備される
  • 出題者様の採点環境と同じ環境でローカルチェックができる
  • 環境を汚さなくて済む(特に Windows にインストールされている Ruby のバージョンを変えなくて済む)

欠点

  • ディスク容量を食う
  • セットアップに時間がかかる
  • Linux の知識が多少必要

Windows に直接 Ruby + rubocop + rspec をインストールする方法

利点

  • 比較的素早くセットアップできる
  • Ruby が既にインストールされていれば利用できる

欠点

  • 採点環境と異なるので、事前に正確な結果がわからない
  • Ruby, rubocop, rspec のバージョンが指定のものと異なる場合にも結果に影響しうる

さらに別のやり方として、 Vagrant は使わないけど VirtualBox + Ubuntu は使う、というハイブリッド的な方法もあります。私は、やむを得ずそのやり方にしましたが、 32bit 版 Ubuntu の環境が提供された今となっては、真似しても得はないと思います。詳しく知りたい方は、奮闘記をご覧ください。

奮闘記 http://tails.hatenablog.jp/entry/2014/05/27/034208

おわりに

この問題の締め切りは6月9日です。本稿を公開した時点ではまだまだ日数がありますので、尻込みをしていた方も、是非挑戦してみてください。(追記:締め切られました。このブログエントリは2014年のものです。)

出題者様へ:

いろいろサポートしていただいたのに、奮闘記とか書いてしまって、すみませんでした! 挑戦者増えるといいですね(^^)