1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ClaudeCodeへのダ命令 ~バイブコーディングアンチパターン~

Last updated at Posted at 2025-08-12

はじめに

はっきり言って、AIへの命令がうまい訳ではないです。
が、逆にそれを才能と思ってダメな命令=ダ命令を皆さんに見てもらうことで反面教師にしてもらおうかと思って記事にした次第です。

今回の教材

5年以上前に購入したアルゴリズムを鍛えるためのドリルです。
↑のリンクでも紹介されていてるボウリングのスコア計算についてバイブコーディングで実装していきたいと思います。

今回の相棒

Claude Code(claude-sonnet-4-20250514モデル)

1回目:無邪気につくる

pythonでボウリングのスコア計算関数を作成してください
  - スコアは配列で10フレームまで与えられます。
  - 返却値として合計スコアを返してください。
  - これはscore_1.pyとして作成してください

アンチパターンにするために、具体的な命令は何もしてません。
ボウリングとは何ぞやすら教えずに作らせてみました。

結果

def calculate_bowling_score(scores):
    total_score = 0
    roll_index = 0
    
    for frame in range(10):
        if is_strike(scores[roll_index]):
            total_score += 10 + strike_bonus(scores, roll_index)
            roll_index += 1
        elif is_spare(scores, roll_index):
            total_score += 10 + spare_bonus(scores, roll_index)
            roll_index += 2
        else:
            total_score += sum_of_balls_in_frame(scores, roll_index)
            roll_index += 2
    
    return total_score

def is_strike(roll):
    return roll == 10

def is_spare(scores, roll_index):
    return scores[roll_index] + scores[roll_index + 1] == 10

def strike_bonus(scores, roll_index):
    return scores[roll_index + 1] + scores[roll_index + 2]

def spare_bonus(scores, roll_index):
    return scores[roll_index + 2]

def sum_of_balls_in_frame(scores, roll_index):
    return scores[roll_index] + scores[roll_index + 1]


if __name__ == "__main__":
    # テストケース
    frames = [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 2]
    print(calculate_bowling_score(frames))  # 出力: 292
    frames = [9, 1, 8, 2, 10, 5, 0, 3, 6, 4, 2, 7, 3, 6, 3, 10, 9, 1, 9]
    print(calculate_bowling_score(frames))  # 出力: 137

ちゃんと計算出来てしまう、という。
ボウリングのスコア計算がメジャーだった為に計算できてしまいました。

ただし教本だとフレーム毎の二次元配列を渡すので、本来は↓のような形で渡されます。

    frames = [[10], [10], [10], [10], [10], [10], [10], [10], [10], [10, 10, 2]]
    frames = [[9,1], [8,2], [10], [5,0], [3,6], [4,2], [7,3], [6,3], [10], [9,1,9]]

なので、実際は不具合で動きません。残念。ということにしましょう。

2回目:定義を書いてみる

命令を変えます。一から作成しなおしてください。
# ボウリングのスコア計算関数を作成するための命令
  - pythonでボウリングのスコア計算関数を作成してください
  - フレーム:1ゲームを構成する単位。2投目、または10本のピン全てを倒すまでを1フレームと呼ぶ。1ゲーム10フレーム。最後の10フレームはストライクまたはスペアを取ると三投目まで投げられる。
  - 10フレームの特別なルール:
    - 10フレーム目でストライクを取ると、3投目が投げられる。
    - 10フレーム目でスペアを取ると、3投目が投げられる。
    - 10フレーム目でノーマルの場合は2投目まで。
  - ストライク:1投目で10本のピンを倒すこと
  - スペア:2投目で10本のピンを倒すこと
  - スコア:各フレームの得点。ストライク、スペア、またはノーマルの得点を計算する。
  - 最高得点は300点。ストライクを10フレーム連続で取ると最高得点となる。
# 作成する関数について
  - スコアは2次元配列で10フレームまで引数で与えられます。1フレームごとに倒したピンの数の配列になります。
    - 例:[[1,0],[8,2],[10],[0,0],[5,5],[10],[0,2],[8,1],[9,0],[1,2]]
  - スコアを返却すること
  • ストライクの時、2投目までの点数が加算される
  • スペアの時、1投目までの点数が加算される

という得点に関わる大切な定義を書き忘れてます

結果

def calculate_bowling_score(frames):
    total_score = 0
    
    for frame_idx in range(10):
        frame = frames[frame_idx]
        
        if frame_idx < 9:  # 1-9フレーム
            if is_strike(frame):
                total_score += 10 + get_next_two_rolls(frames, frame_idx)
            elif is_spare(frame):
                total_score += 10 + get_next_roll(frames, frame_idx)
            else:
                total_score += sum(frame)
        else:  # 10フレーム
            total_score += sum(frame)
    
    return total_score

def is_strike(frame):
    return len(frame) == 1 and frame[0] == 10

def is_spare(frame):
    return len(frame) == 2 and sum(frame) == 10

def get_next_two_rolls(frames, frame_idx):
    next_frame = frames[frame_idx + 1]
    
    if is_strike(next_frame):
        if frame_idx + 1 == 9:  # 次が10フレームの場合
            return next_frame[0] + next_frame[1]
        else:
            return next_frame[0] + frames[frame_idx + 2][0]
    else:
        return next_frame[0] + next_frame[1]

def get_next_roll(frames, frame_idx):
    next_frame = frames[frame_idx + 1]
    return next_frame[0]

if __name__ == "__main__":
    # テストケース
    frames = [[10], [10], [10], [10], [10], [10], [10], [10], [10], [10, 10, 2]]
    print(calculate_bowling_score(frames))  # 出力: 292
    frames = [[9,1], [8,2], [10], [5,0], [3,6], [4,2], [7,3], [6,3], [10], [9,1,9]]
    print(calculate_bowling_score(frames))  # 出力: 137

ストライク、スペアの時のボーナス計算が何も言わなくても書かれてますね。
「仕様書にないことをするな」と怒ってはいけません。漏れたのは仕様書を書いた人間です。

1フレームから順に計算していくのもあり、ちょっと読みづらいですね。

  • 次のフレームがストライクだったら、さらに次のフレームの投球を見に行く
  • ただし、次が10フレーム目なら次のフレームがないのでそのまま二投目を足し合わせる
    という内容になってます。

3-5回目:10フレームから逆順で計算する

これは教本にもあるんですけど、別に1フレーム目から計算する必要は全くないんですね。
10フレーム目から計算し、その時、1投目、2投目のスコアを保存しておく。
9フレーム目でストライクなら先ほど保存した1,2投目を足し合わせる。
1,2投目を更新して、8フレーム目へ。
という流れで計算をするとシンプルになります。

という訳で逆順で計算するように命令します。
が、ここで思うように命令が実行されませんでした。
皆さんだったらどう命令してましたか。

10フレーム目から計算するようにし、10,9,8フレームの順でスコア計算してください。途中のスコアは表示しないので最終結果のみ、効率よく計算することを目指したものを作成してください
  - score_3.pyで作成してください

3回目結果

def calculate_bowling_score(frames):
    total_score = sum(frames[9])  # 10フレーム目は単純合計
    
    for frame_idx in range(8, -1, -1):  # 9フレームから1フレームまで逆順
        frame = frames[frame_idx]
        
        if len(frame) == 1 and frame[0] == 10:  # ストライク
            total_score += 10 + get_next_two_rolls(frames, frame_idx)
        elif len(frame) == 2 and sum(frame) == 10:  # スペア
            total_score += 10 + frames[frame_idx + 1][0]
        else:  # ノーマル
            total_score += sum(frame)
    
    return total_score

def get_next_two_rolls(frames, frame_idx):
    next_frame = frames[frame_idx + 1]
    
    if frame_idx + 1 == 9:  # 次が10フレーム
        return next_frame[0] + next_frame[1]
    elif len(next_frame) == 1 and next_frame[0] == 10:  # 次もストライク
        return next_frame[0] + frames[frame_idx + 2][0]
    else:
        return next_frame[0] + next_frame[1]

いやー、違うんだよ。次のフレームを見に行くのをやめてくれって話をしたいんだ。

get_next_two_rolls()を使わずにnext_roll_1,next_roll_2と取れるのではないか
  - score_4.pyとして作成してください

ここの命令は焦って、熟考せずに命令を書いてしまった(反省)

4回目結果

def calculate_bowling_score(frames):
    total_score = sum(frames[9])  # 10フレーム目は単純合計
    
    for frame_idx in range(8, -1, -1):  # 9フレームから1フレームまで逆順
        frame = frames[frame_idx]
        next_frame = frames[frame_idx + 1]
        
        if len(frame) == 1 and frame[0] == 10:  # ストライク
            if frame_idx + 1 == 9:  # 次が10フレーム
                next_roll_1 = next_frame[0]
                next_roll_2 = next_frame[1]
            elif len(next_frame) == 1 and next_frame[0] == 10:  # 次もストライク
                next_roll_1 = next_frame[0]
                next_roll_2 = frames[frame_idx + 2][0]
            else:
                next_roll_1 = next_frame[0]
                next_roll_2 = next_frame[1]
            total_score += 10 + next_roll_1 + next_roll_2
        elif len(frame) == 2 and sum(frame) == 10:  # スペア
            next_roll_1 = next_frame[0]
            total_score += 10 + next_roll_1
        else:  # ノーマル
            total_score += sum(frame)
    
    return total_score


if __name__ == "__main__":
    # テストケース
    frames = [[10], [10], [10], [10], [10], [10], [10], [10], [10], [10, 10, 2]]
    print(calculate_bowling_score(frames))  # 出力: 292
    frames = [[9,1], [8,2], [10], [5,0], [3,6], [4,2], [7,3], [6,3], [10], [9,1,9]]
    print(calculate_bowling_score(frames))  # 出力: 137

いや、出来てるんだけど、if/elseが多すぎる。
ちゃんとやりたいことを書きます

- 10フレーム目を取得時
  - next_rolle_1 = frame[0], next_rolle_2 = frame[1]として保存する
- 9フレーム目を取得時
  - ストライクなら「next_rolle_1+next_rolle_2」
  - next_roll_1 = 10, next_roll_2 = next_roll_1
  - ストライク以外:next_roll_1 = frame[0], next_roll_2 = frame[1]
- このようにしてscore_5.pyを作成してみてください

見返すとちゃんと書いてないのは目をつぶります。

5回目結果

def calculate_bowling_score(frames):
    # 10フレーム目を処理して初期値設定
    frame_10 = frames[9]
    total_score = sum(frame_10)
    next_roll_1 = frame_10[0]
    next_roll_2 = frame_10[1]
    
    # 9フレームから1フレームまで逆順処理
    for frame_idx in range(8, -1, -1):
        frame = frames[frame_idx]
        
        if len(frame) == 1 and frame[0] == 10:  # ストライク
            total_score += 10 + next_roll_1 + next_roll_2
            # 次のフレーム用の値更新
            temp_next_roll_1 = next_roll_1
            next_roll_1 = 10
            next_roll_2 = temp_next_roll_1
        elif len(frame) == 2 and sum(frame) == 10:  # スペア
            total_score += 10 + next_roll_1
            # 次のフレーム用の値更新
            next_roll_1 = frame[0]
            next_roll_2 = frame[1]
        else:  # ノーマル
            total_score += sum(frame)
            # 次のフレーム用の値更新
            next_roll_1 = frame[0]
            next_roll_2 = frame[1]
    
    return total_score

if __name__ == "__main__":
    # テストケース
    frames = [[10], [10], [10], [10], [10], [10], [10], [10], [10], [10, 10, 2]]
    print(calculate_bowling_score(frames))  # 出力: 292
    frames = [[9,1], [8,2], [10], [5,0], [3,6], [4,2], [7,3], [6,3], [10], [9,1,9]]
    print(calculate_bowling_score(frames))  # 出力: 137

やっと、やりたい事が出来ました。
1,2回目の結果と見比べて、かなりシンプルな処理になったし分かりやすいものになったのではないかと思います。

まとめ

  • 何も考えずに書いても、ある程度やりたい事は書いてくれる
  • シンプルさ、可読性を考えたものを書かせるとなるとそれなりに書く必要がある
    • これはAI的には読みにくさというものが存在しないのかも

一撃で5回目に書いたようなコードを書かせることができる!という人、募集しております。

1
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?