12月20日、CodeIQと@ITは合同で「リアル版:サンタのためのコードゴルフコンペ&クリパ」を開催した。クリスマスを目前に、エンジニアが集う!
12月20日、ITエンジニアのための実務スキル評価サービス「CodeIQ」と@ITは合同で「リアル版:サンタのためのコードゴルフコンペ&クリパ」を開催した。イベントは、CodeIQであらかじめ出題されていた「サンタのためのコードゴルフ」との連動企画。クリスマスを目前に、エンジニアが集うガチイベントの模様をレポートする。
「コードゴルフ」とは、ゴルフがカップインまでの打数を競うように、プログラムの文字数を削り、なるべく短くプログラムを書く遊びのことをいう。スポーツのゴルフでは、打数が少なければ少ないほど高得点が得られるが、コードゴルフでは、ソースコードの文字数が少なければ少ないほど良いとされる。従って、最短コードを書いた人が勝者となる。イベントでは、「サンタのためのコードゴルフ」問題の出題者であるクロノス・クラウンの柳井政和氏によるコードレビューが行われた。
今回出題された問題は、アスキーアニメ。4枚のアスキーアートでアニメーションさせる問題である。テーマは、「配列」と「ループ処理」だ。
というわけで、サンタ見習いの智美ちゃんが振られた仕事を、こっそりと手伝ってあげて下さい。サンタ見習いの智美ちゃんのコードは、下記に掲載しています。JavaScriptを使って、このコードゴルフにチャレンジしてください。
以下の関数yourCode()に書かれているコードと同じ文字列を返す処理を、なるべく短い文字数で書いてください。短いほど評価が高いです。ただし、関数外から直接答えを取ったり、通信で答えを得るなどした場合は、ランキング外になってしまいますので、行わないでください。
function yourCode() {
var arrayMax = 4; var resArray = new Array(arrayMax); // アニメ用の4枚のアスキーアートを返す var w = 80; var h = 40; var illumination = 160; // キャンバス用配列を初期化 var canvasArray = new Array(4); for (var a = 0; a < arrayMax; a ++) { canvasArray[a] = new Array(h); for (var y = 0; y < h; y ++) { canvasArray[a][y] = new Array(w); } } // ツリーを作成 for (var a = 0; a < arrayMax; a ++) { for (var y = 0; y < h; y ++) { for (var x = 0; x < w; x ++) { canvasArray[a][y][x] = "_"; var treeTop = Math.floor(y / 8) * 4; var treeW = Math.floor(y / 8 + 1) * 8; if (Math.abs(x - w / 2) < (y - treeTop) % treeW) { canvasArray[a][y][x] = "%"; } } } } // イルミネーションを作成 for (var a = 0; a < arrayMax; a ++) { for (var i = 0; i < illumination; i ++) { var r = (a + i) * i * 49999 + 59999 & 0xFFFF; var x = 1 + r % (w - 2); var y = 1 + r % (h - 2); canvasArray[a][y][x] = "*"; canvasArray[a][y][x - 1] = "-"; canvasArray[a][y][x + 1] = "-"; canvasArray[a][y - 1][x] = "|"; canvasArray[a][y + 1][x] = "|"; } } // 文字列化 for (var a = 0; a < arrayMax; a ++) { var arrayY = new Array(h); for (var y = 0; y < h; y ++) { arrayY[y] = canvasArray[a][y].join(""); } resArray[a] = arrayY.join("\n"); } // 戻り値を戻して終了 return resArray;
この基のコードには、あらかじめ冗長性を設けているという。短く書けるコードを、わざと長く書いているのだ。「大きなトラップが3つある」と柳井氏はいう。
最初のトラップは、「不要な処理の削除」である。例えば、
// キャンバス用配列を初期化 var canvasArray = new Array(4); for (var a = 0; a < arrayMax; a ++) { canvasArray[a] = new Array(h); for (var y = 0; y < h; y ++) { canvasArray[a][y] = new Array(w); } }
このブロックは丸ごと削除できる。[x][y]の配列ではなく、[x + w * y]の配列にすれば、そもそも初期化は不要になるためだ。
第2のトラップは、「ループの統合」。基のコードは、ループの統合が可能なように、本来ランダムに行うべき処理を一意に決まるコードで書いている。
var r = (a + i) * i * 49999 + 59999 & 0xFFFF;
「勘の鋭い人は、線形合同法の変形だと気付くだろう」と柳井氏は解説を続ける。線形合同法とは、擬似乱数列を生成するアルゴリズムの一つである。
例えば、Visual BasicのRnd関数は次のような構造になっている。
static long x=327680; float Rnd(void) { x=x*16598013+12820163&16777215; return x*(1.0/16777216.0); }
今回の問題では、この「線形合同法もどき」を利用し、星の表示場所を散らしている。従って、次の部分は丸ごと短くできる。
var r = (a + i) * i * 49999 + 59999 & 0xFFFF;
ここは、どの順番で計算しても可能であるため、ソースコードの中に複雑に埋め込むことができる。あえてここを正しい乱数にしなかったのは、「計算順番は問わないので好きに計算する位置を変えてください」という出題者からのサインで問題を解くヒントになる。何度も登場するループは、乱数の処理順にとらわれずに統合できるため、柳井氏は「統合してね」というメッセージを問題の中に隠したそうだ。問題を見た瞬間にここまで分かるかどうかが、この問題の大きなハードルである。
第3のハードルは、「多数の配列」だ。問題のコードでは、ネストした配列が出てくる。アスキーアートの4枚の配列は、まず木を書いて、その後に星を書いている。すなわち、4枚の同じ絵のレイヤーを作り、その後個別の絵を描くという処理だ。「『この処理をどこまで統合できるか』というところで、文字数の差が付くようにしている」と柳井氏は語る。
今回のイベントには、1人の勇者がいた。2バイト文字も「1文字」として扱ったコードをとくとご覧あれ。
for(var i=0,C,Z="";C="景爨癡爠刽孝ⱡ㴴ⱷ㴸ㄻ愭ⴻ剛慝 㵃潩渨∢⤩筃㵛崻景爨椽㌲㌹ⱚ㴲?椭ⴻ⥃孩崽繩╷㽩╷㸴〭娦 椥眼㐰⭚㼢┢㨢弢㨨娫㵾椯眥㠿ⴱ㨳Ⱒ屮∩㭦潲⠻娼ㄶ〻䍛 椭睝㵃孩⭷崽≼∩椽⡡⭚⤪娫⬪㐹㤹㤭㔵㌷☶㔵㌵Ⱪ㵩┳㠪眫椥 㜸眬䍛楝㴢⨢ⱃ孩ⴱ崽䍛椫ㅝ㴢ⴢ絒".charCodeAt(i++);) Z+=String.fromCharCode(C>>8&255,C&25 5);return eval(Z)
for( var i=0,C,Z=""; C="景爨癡爠刽孝ⱡ㴴ⱷ㴸ㄻ愭ⴻ剛慝㵃潩渨∢⤩筃㵛崻景爨椽㌲㌹ⱚ㴲?椭ⴻ⥃孩崽繩╷㽩╷㸴〭娦椥眼㐰⭚㼢┢㨢弢㨨娫㵾椯眥㠿ⴱ㨳Ⱒ屮∩㭦潲⠻娼ㄶ〻䍛椭睝㵃孩⭷崽≼∩椽⡡⭚⤪娫⬪㐹㤹㤭㔵㌷☶㔵㌵Ⱪ㵩┳㠪眫椥㜸眬䍛楝㴢⨢ⱃ孩ⴱ崽䍛椫ㅝ㴢ⴢ絒" .charCodeAt(i++); ) Z+=String.fromCharCode(C>>8&255,C&255); return eval(Z)
for(var R=[],a=4,w=81;a--;R[a]=C.join("")){C=[];fo r(i=3239,Z=23;i--;)C[i]=~i%w?i%w>40-Z&i%w<40+Z?"%" :"_":(Z+=~i/w%8?-1:3,"\n");for(;Z<160;C[i-w]=C[i+w ]="|")i=(a+Z)*Z++*49999-5537&65535,i=i%38*w+i%78-~ w,C[i]="*",C[i-1]=C[i+1]="-"}R
for( var R=[], a=4, w=81; a--; R[a]=C.join("") ){ C=[]; for( i=3239, Z=23; i--; ) C[i]= ~i%w ? i%w>40-Z & i%w<40+Z ? "%" : "_" : ( Z+=~i/w%8 ? -1 : 3, "\n" ); for( ; Z<160; C[i-w]=C[i+w]="|" ) i=(a+Z)*Z++*49999-5537&65535, i=i%38*w+i%78-~w, C[i]="*", C[i-1]=C[i+1]="-" } R
柳井氏によるコードレビューの後、チーム対抗戦「ミニ・コードゴルフ コンペ」が行われた。まるで当たり前のようにサンタの帽子をかぶって、コードゴルフに打ち込む姿は貴重な光景だった。
コンペが終わると、クリスマスパーティーに突入。しかし、集まったエンジニアたちは、いかにコードを短く書くかに夢中だった。ヘソ出しサンタがすぐそこにいるというのに、このありさまだ。
こうして、「リアル版:サンタのためのコードゴルフコンペ&クリパ」は幕を閉じた。JavaScriptエンジニアは、ヘソ出しサンタよりコードが好きだった。
Copyright © ITmedia, Inc. All Rights Reserved.