AOJでゴルフ

誤字脱字って恥ずかしいね!穴があったら入りたい気分。
それはさておき、AOJでゴルフするのに邪魔なreturnやexitですが、省ける時と省けない時があるみたい。
そもそもゴルフするところじゃないのかもしれませんが、できるんだったらやってみたくなる。
ということで、あまり検証はしてないのだけど現状分かってることだけメモ。
対象はVolume 0の0045 Sum and Averageです。
ついでに95Bのコードも。チートだけど。


追記
なんでreturnやexitを省略できるのか、どうしてそうなるのかichirin2501さんのところに書いてありました。
ebpやesp辺りの問題なのかなとか見当違いな考えをしてたのが恥ずかしいorz
パタヘネとか読み直そう。


追記の追記
ありがたいことに、わざわざ私のreturn不要のコードが何で動くのか、アセンブリコードも含めてincrementさんが解説して下さいました
感謝してもしきれないと同時に、自分の無知さが恥ずかしいよorz
return 0って書くと確かにeaxに即値で0を入れてる。なるほどなー
勉強になること、勉強することはまだまだたくさんある。


どうしてかわかった以上、以下の内容は役に立たないけど恥ずかしい思いをした記録として残しておきます。
Nyhxは教訓を手に入れた!


これは通らない。
0を返してないのでRuntime Error。

j, k, l, m;

main(i)
{
  for(; ~scanf("%d,%d", &i, &j); l += j, m++)
     k += i * j;
  printf("%d\n%d\n", k, l / m + 1);
}


exitを付ければ通る。

j, k, l, m;

main(i)
{
  for(; ~scanf("%d,%d", &i, &j); l += j, m++)
     k += i * j;
  printf("%d\n%d\n", k, l / m + 1);
  exit(0);
}


0は返していないけど通る。

j, k, l, m;

main(i)
{
  for(; i; l += j, m++)
    ~scanf("%d,%d", &i, &j) ? k += i * j : (i = !printf("%d\n%d\n", k, l / m + 1));
}

アセンブリコードを見てもなんで通るのかはさっぱり。知識が足りてないのもあるけど。
for文のループの継続条件を満たさなくなったら、leaveとretが実行されるラベルに飛んでるんだけどそのせい?
とはいえ、printfで終わってるコードもprintfをcallした後、すぐにleaveとretが実行されてるんだけどなあ・・・
とりあえずreturnとexitを省略したいなら、for文1個にコードを押し込めば通るっていうことがわかった。気がする。
そこまでしてもあんまり意味はないみたいだけど・・・
あー、何でかわかるようになりたい。

SRM453 DIV2

眠い目を擦りつつ参戦したまではいいものの、サーバーが落ちてキャンセルされた。
250点問題はそこそこ高得点取れそうだったのに。惜しいなあ。
500点と1000点は見られなかったので、今回は触れないことにする。
と言っても毎回触れてないようなものだけどorz

250

トーナメントの結果表から行われた試合数を求めろっていう問題。
勝っても負けても引き分けても、1試合行われるごとにポイントの合計点が2点づつ増えるので
合計点を求めて2で割った答えが行われた試合数になる。割り切れなければ-1を返す。
いやっほう!高得点!とか思ってたらコンパイルタイムアウトしまくりで焦った。
自分だけなのかと思って落ち込んでたら、サーバーごと落ちたので安心したと言うかがっかりしたと言うか・・・

#include <iostream>
#include <vector>

using namespace std;

class TheTournamentDivTwo
{
  public:
  int find(vector <int> points)
  {
    int ans = 0;

    for(int i = 0; i < points.size(); i++)
      ans += points[i];

    if(ans % 2)  return -1;
    else    return ans / 2;
  }
};

アルゴリズム

短いコードを書くためには良いアルゴリズムが必要なんだと思った。
もっとたくさんコードを書いたり読んだりしよう。

delete last line

ふと閃いたコードを試してみたらあっさり縮んだ。14B縮んで51B。
入力が短ければもっと短くできそうなんだけど、長い入力にも対応できないとなあ・・・
使う関数なんかでは縮められそうな要素がなさそうなので、ここから先はアルゴリズムが違うのかな。

c;

main(i)
{
  for(; gets(&i); strcpy(&c, &i))
    c && puts(&c);
}

rotate lines

入力された文章を一個上にずらせっていう問題。
一発で最短に到達できたので狂喜乱舞。環境依存のコードでした。
最短コードはとりあえず内緒にしておきます。

考えが浅いような

前々からのことだけど、時間をかけて考えれば割とまともな答えも出せるけど、短時間ではなかなか上手くいかない。
どうすればいい答えに短時間で辿り付けるようになるんだろうか。

invert case

昨日の今日で大幅に縮めることに成功した。13B縮んで42B。
大文字と小文字が第5ビットが立ってるか否かで判別できるなら、反転させるだけでいいじゃない。
それとgetcharで読み込むより、readで読み込んだ方が短くて綺麗になった。
ANDを取って判別してる時点で気付きそうなものだけど、ゆっくり考えてからじゃないと気が付かないのはなんか悔しい。
しかしここから後2Bか・・・

main(i)
{
  for(; read(0, &i, 1);)
    putchar(i ^ 32);
}

さぼり気味な最近

ちょっとゴルフさぼっていた。
あまりよろしくない傾向なので頑張らないと。

99 shinichiros of hamaji

アドレス入れるだけならわざわざchar型に入れなくてもいいじゃない、ということで型宣言を外した。
後はこのやり方でやるならon the wallの部分は埋め込んでしまった方が短くなったのでそのように。6B縮んで254B。
流石にそろそろやり方を変えようかな・・・

 *s = " shinichiroes of hamaji", *u = " shinichiro of hamaji";
i = 99;

main()
{
  for(; i;)
    printf("%d%s on the wall, %d%s.\n%s, %d%s on the wall.\n\n",
           i--, i > 1 ? s : u, i, i > 1 ? s : u,
           i > 1 ? "Take one down and pass it around" : "Go to the store and buy some more",
           i > 1 ? i - 1 : 99, i - 2 ? s : u);
}

invert case

小文字を大文字に、大文字を小文字に変換しろっていう問題。
まとめて変換する方法がわからなかったので、地道に1文字づつ変換した。
おかげで大分コードが長くなってしまって55B。
まとめて変換する方法を見つけないと、これ以上は大幅に縮められそうにないなあ。

main(i)
{
  for(; (i = getchar()) > 0;)
    putchar(i & 32 ? i - 32 : i + 32);
}

入力がfoobarの変形型しかないので答えを埋め込んだものも試した。
短くなるかと思ったけど逆に長くなるという結果に。記録は63B。

main(i)
{
  gets(&i);
  puts(i & 32 ? "FOOBAR" : i & 8192 ? "fOObar" : "foobar");
}

絶好調である

前回の発見(?)のおかげで前に書いたコードがどんどん縮む。
手元の環境ではputsの引数を省略できないけど、anarchy golfのサーバー側では省略できるのが分かったのも大きい。
これでより最短に近づけるようになった。ばんざーい。

echo

多分C言語だけでやったらこれが最短だと思うところまで持ってこれた。環境依存で31B。
ちなみに何でもありでの最短は、名前欄に書いてあるddコマンドを使うコードみたい。
もう1つ、違うやり方での31Bのコードがあるみたいなので、私のコードの方は公開しちゃいます。

main(i)
{
  for(; gets(&i); puts());
}

delete blank lines

putsの引数を省略しただけ。環境依存で37B。
これも最短はsystem関数を使うのかな?

main(i)
{
  for(; gets(&i);)
    i & 63 && puts();
}

even lines

これも引数を省略しただけ。37B。
でもgetsの方だから手元の環境でも動くよ!
それにしてもここまでやって37Bなのに、トップの35Bとかどんだけ・・・

main(i)
{
  for(; gets(&i); puts(gets()));
}

最短コード到達

swap linesで最短コードに到達できた。
割と簡単に到達できたコードだけど、やっぱり達成感があるなあ。
多分始めた当初では到達できなかっただろうから、ちょっとは成長しているんだと思いたい。

swap lines

2行読み込んでから入れ替えて表示しろっていう問題。
幸いテストコードが全部偶数行で構成されていたので、入力が奇数行だった場合なんかを気にしないでいいので楽できた。
なんの工夫もなく素直に書いても52B。
ちなみに最短コードは手元の環境では動かない環境依存のコードでした。


52B

t;

main(o)
{
  for(; gets(&o); puts(&t), puts(&o))
    gets(&t);
}

短縮する過程で気付いたことだけど、getsの引数を省略して書いたコードが動作した場合
直前に起動した関数に渡した引数が使用されて動くみたい。
多分スタックポインターに残ってるのを使用してるのかなと無責任に予想してみる。
コードはこんな感じ。

main(i)
{
  gets(&i);  //ここでhogeを入力
  gets();    //ここでpiyoを入力
  puts(&i);  //表示はpiyoになる
}

これだと落ちる。

main(i)
{
  gets();  //puts(gets())としても落ちる
}

stdioとかをincludeしてしまうとgetsの引数がないってエラーになるので、ゴルフにしか使えないけど。
そもそも他の環境でも同じ動作になるのかもわからないという。
うーん、C言語にはまだわからないことがたくさんあるなあ。