Claudeで書くオセロ

Claude 3がリリースされたので、その試用も兼ねて、かねてから気になっていた「AIはプログラムをどれだけうまく書けるのか」を試してみることにした。

会話履歴の全文はここ

オセロ書いてくれる?

まず無料版を使って、単刀直入に「I’d like to write a Python program that lets two human players play Othello. Can you help me with that?」と聞いてみた。すると、CUIベースの動くオセロプログラムがスルスルっと出てきた:

% python othello_game.py
  0  1  2  3  4  5  6  7
0                
1                
2                
3       W B      
4       B W      
5                
6                
7                
Player W's turn. Valid moves: [(2, 4), (3, 5), (4, 2), (5, 3)]
Enter your move (row col): 

次に単体テストを書いてもらうと、それっぽいコードは出てきたが、動かしてみると一つも通らない。

ゲームはプレイできるのだから、問題はテストにあるのは明らかだ。エラーメッセージをClaudeに渡してテストを修正してもらおうとしたが、新しいコードと称して実際には古いコードと同じものを返してくるだけだった。

しょうがない、僕がテストの問題を理解して手引してやらないといけないなと思い、テストを見てみると、こんな感じのとても読めないコードが書いてある:

tiles = self.game.check_direction(2, 3, 2, 1, 0, 1)
self.assertEqual(tiles, [(3, 4)])

2,3,2,1,0,1?? なんだそりゃ

ここで少し方向転換。まずはClaudeにこのコードをリファクタリングしてもらうようにする。僕ならこのコードをどう書くか、イメージはとても明確なので、その線に沿って具体的な指示を出して誘導していく。

これは辛い作業だった。Claudeは私の提案に快く応じ、正しい方向へ一歩を踏み出してくれるのだが、踏み出すのは一歩だけで、僕の期待するようなコードは出てこない。何度もやりとりを繰り返して、丁寧に指導しながら目指す状態に近づける必要がある。その上、モデルの反応は遅く、やりとりには時間がかかると来ている。会話が長くなると処理しないといけない情報が増えるのか、遅くなるように感じた。

熱心なインターンと一緒に仕事をしているかのようだ。愛すべき存在で、とてもやる気に満ちているのだが、手取り足取り指導が必要で、出来上がるコードはなんだか凡庸。 とはいえ、そのレベルでもコーディングできない人は山ほどいるので、基準も高すぎるのだろう。僕はプログラミングにはとても拘りを持っているのだし。

一回に一つずつ指摘をしていくのも良くなかったかもしれない。本能的に、一度にたくさん言ってはいけないような気がしたが、相手は人間じゃなくてAIなので、それでやり取りの回数を少なくしたほうがよかったかも?

この辺で無料枠が切れたので、課金して続ける。課金したことで、より優れたモデルも使えるようになった。

やり取りを重ねて、ようやく満足できる出来のプログラムになった。新しいコードと称して今と同じコードが返答されてくるという事態は頻発した。まるで電話越しに同僚とコードの話をしているかのようだ。「現在のコードの状態」が何であるか、僕とClaudeの認識が一致していない。チャットという形態も良くないと感じる。そして、チャットという形態がAIによるプログラミングに向いていないとしたら、他にもチャットに向いていない「AIによる〜」が無数にあるのだろうなと想像する。

テストを書いて

満足できるコードが出来た事に意を強くし、あらためて単体テストへと戻る。

最初から作り直してもらったテストがこれ。初見、非常に冗長だ。僕ならEMPTY, BLACK, WHITE位は定数にするし、Vector2Dという名前だって、こういう局所的な頻出するキーワードにつける名前にしてはだいぶ長い。そして、もっとも重要な事に、テストは依然として一つも通らない。

ここから、もうおなじみとなった辛い作業が始まる。エラーメッセージを渡す。分かった!と言われるが、戻ってくるコードは前と同じ。しょうがないので自分でデバッグする。

self.assertEqual(self.game.check_direction(Vector2D(2, 3), Player.BLACK, Othello.EAST), [Vector2D(3, 4)])
AssertionError: Lists differ: [] != [(3, 4)]

I’m pretty sure this is a bug in the test code. Checking direction from (2,3) to east wouldn’t produce (3,4). Maybe you are trying to check south from (2,3), or perhaps east from (3,2)

これに対する長文の返答が、問題の原因を明らかにした。Claudeは、テスト前の盤面が次のようなものだと思っているらしい:

  0 1 2 3 4 5 6 7
0
1
2     . B .
3   . W B .
4     . B W
5
6
7

一体この盤面はどこから来たのか。オセロではこんな状態は発生しないぞ。これに続けて色々言いながら、ようやっとパスするテストを送り返してきたが、出てくる値に合わせてassertionの方をいじっただけに見える。まあテストの問題って言っているのだからそれでもいいのだが、プログラマーとしてはそれはやっちゃ駄目だろ。

盤面の状態がおかしいと指摘したら、例のごとく喜んで新しいテストを書いてくれたが、やっぱり通らない。

この辺で潮時だと思い、匙を投げる事にした。自分で修正したほうがよっぽど早い。

講評

さて、これまでを振り返ろう。

この形式は、コーディング・インタビューを思い起こさせる。こっちは口は出すが手は出さない。そういう視点で見ると、Claudeは僕が今までインタビューしたどの人間とも違っていて異質だ。

ちゃんと動くオセロのプログラムを一発15秒で書き上げてくるプログラマなどいない。超人的な力だ。

一方で、Claudeは別に美しいプログラムには興味がないようで、ヒントを与えてもそういうプログラムを書くことは出来ない。この辺は、まあ平均的プログラマと言える。平均というのは悪いという意味ではなく、今までだって自分なりに美しいプログラムを書けない人はたくさん採用してきたし、みんな活躍してくれている。

そして、様々な局面で、Claudeは醜態も晒した。自分の間違いから脱出することが出来ない。ヒントを与えても。ジュニアな技術者でも、もっとうまくやるだろう。現実のインタビューでこういうところをこれだけ見せつけられたら、確実に落とすだろう。

不思議な能力の組み合わせ。やはり人間ではない。

一方、採用としてこれを考えるのは必ずしも適切ではない。僕の生産性を高める道具としてとしてはどうか。

LLMという構造を考えると、しでかした失敗から立ち直れないのは道具の癖だ。仕様上、Claudeは自分で言った事にも影響を受けてしまうから。間違いを指摘して直していくよりも、会話を途中からやり直して間違いが履歴に残らないようにしてあげたほうがいいかもしれない。

0→1には素晴らしい。最初に八割方のものを作るにはこれほどいい方法はない。特にオセロのように仕様がきっかり決まっているならなおさらだ。でも、そっから先は自分でいじった方が良さそうだ。自分でいじると時間はちょっと余分に掛かるかもしれないが、ストレスはずっと少ない。こういうコードにせよ、と明確に指示するのは難しいものだ。

対話形式はよくない。IDEに組み込まれていてほしい。多くのベンダーがこれに取り組んでいるので、いつか試す。

おかわり

Claudeのことがちょっとよく分かったので、もう一度最初からやり直してみることにした。二度目の共同作業は一度目よりうまくいくといいのだが。

会話履歴の全文はここ

今度は、最初からこういう感じにしてほしいというのをより明確に指示する。一度Claudeの出力を見ているので、それと比べてどうしてほしいのか指示しやすい。おまけとして、CUIじゃなくてGUIにしてみる。

第一回目の時のように、ほぼほぼ一発で動くプログラムが出てきた。本当にすごい。超人的だ。pygameなんて聞いたことがないので、自分でやろうとしたらもっとずっと時間が掛かるだろう。

コードのリファクタリングはもう諦めることにする。色々細かいところにはバグもある(例えばgame_overフラグ周り)それも気にしないことにする。自分で直すほうが簡単だから。

単体テストを書いてというと今度は一発で通るテストが出てきた。テストケースとして良いかというとそうでもないが、動かないテストが出てくるよりだいぶマシ。

うんうん、Claud君、仲良くやろうじゃないの。まだ一緒に仕事するのは二回目だけど、ちょっとは君とどう仕事したらいいのか分かってきたよ。

comments powered by Disqus