カメヲラボ

主にプログラミングとお勉強全般について書いてます

climpetさんのものすごいC

climpetさんも194Bのコードを公開してくださっているので、紹介しておきます。

char z['÷'];
i,j,c,n,w;
main(h){
  scanf("%d%d%d %[^_]",&n,&h,&w,z);
  for(h*=++w;i<n*h;++i)
    for(c=j=9;j--;z[h+i]=~i%w?c-15&&z[i]%c?46:42:10)
      c+=z[(i%w-j%3+w)%~-w+(i/w-j/3-~h)*w%h+i/h*h]%3;
  n=!puts(z+i);
}

全体的にはtailsさんと同じですが、配列サイズに'÷'という定数を用いています。'÷'はUTF-8でC3B7(50103)で、今回はn, h, wの最大値がそれぞれ30, 40, 35ですので、30×40×35=42000あれば良いので十分な大きさです。

特に素晴らしいのはやはり生死判定の部分ですね。カウンタ変数cの初期化コストを抑えるため、近傍走査のためのループ変数jと同じ値(9)をセットしています。'.', '*'のASCIIコードはそれぞれ46, 42で、これらに対してで3の剰余を計算するとそれぞれ1, 0になることから、死んでいるセルをカウントしています。このようにすると、

  • 中心セルが「死」で周囲3セルが「生」の場合、cは6加算
  • 中心セルが「生」で周囲2セルが「生」の場合もcが6加算

されることになります。つまり、これら2つの条件はcが9+6=15であるかどうかの判定だけで済みます。残りの条件の判定を行うため、同じようにcの値を見てみると

  • 中心セルが「生」で周囲3セルが「生」の場合、cは5加算

なので、cの値は9+5=14になります。しかし、今度は14かどうかの判定をしようとすると、

  • 中心セルが「死」で周囲5セルが「生」の場合もcが5加算

となり、14かどうかを調べるだけでは判定できません。これらの区別するのに、'.'と'*'のASCIIコードすなわち46と42を利用しています。

  • 中心セルが「死」の場合はASCIIコードが46で、14で割り切れない
  • 中心セルが「生」の場合はASCIIコードが42で、14で割り切れる

これを上手く利用しています。cの初期値が9であることと、死んでいるセルをカウントするということ、ASCIIコードの値の調和がたまらないコードですね!!