36439


Pythonでボーリングのスコア計算アルゴリズムの実装をしたいです。  
ストライクかスペアの場合は、  
・ストライクの場合は現在の位置から2球分の合計数を足す  
・スペアの場合は現在の位置から1球分の合計数を足す  
という普通のボーリングの計算方法で計算をします。ピンは全部で10本で、10フレームを計算します。

[code]

    # coding: utf-8
    
    score_lines = [2, 5, 0, 1, 8, 2, 6, 2, 10, 2, 7, 0, 10, 10, 10, 9, 1, 3]
    
    delete_array =[]
    ans_sum =[]
    
    #スペア
    for i in range(len(score_lines)):
     if i % 2 ==0:
      if (score_lines[i] + score_lines[i+1]) == pin_num:
         ans_sum.append(score_lines[i] + score_lines[i+1] + score_lines[i+2])
         del score_lines[i]
         print(score_lines)
         del score_lines[i+1]
         print(score_lines)
    
    #ストライク
    for i in range(len(score_lines)):
     if i % 2 == 0:
      if score_lines[i] == pin_num:
         ans_sum.append(score_lines[i] + score_lines[i+1] + score_lines[i+2])
         del score_lines[i]
         print(1111111111)
         print(score_lines)
         print(33333333333)
    
    #スペア・ストライク以外
    for i in range(10):
        ans_sum.append(score_lines[i])
    
[/code]

今、「スペア」「ストライク」「スペアとストライク以外」でスコアを計算しています。score_linesが一投ごとに倒れたピンの数です。スペアとストライクで計算したscore_linesの要素（例えば8,
2
や10）などをdelメソッドを使い配列から削除し、スペアとストライク以外の計算の時score_linesの要素の合計を求めスペア・ストライクを計算させようと思っていました。しかし、

[code]

    #スペア
    for i in range(len(score_lines)):
    
[/code]

の`len(score_lines)`でdelメソッドを使ったため、IndexError: list index out of range  
とエラーが出ました。そのエラーがなぜおこったのかはわかるのですが、なら、どのようにスペア・ストライク・スペアとストライク以外のボーリングスコアの合計を求められるのか、わかりません。  
どのように直せばボーリングのスコア計算のアルゴリズムを実装できますか？


CC BY-SA 3.0
2017-07-17T02:56:49.937
36436
2017-07-17T05:55:18.650
2017-07-17T04:49:37.870
3060
24451
question
python
ボーリングのスコア計算アルゴリズムの実装　IndexError: list index out of range


この行で示される形式で正しい入力データが与えられているものとします。

[code]

    score_lines = [2, 5, 0, 1, 8, 2, 6, 2, 10, 2, 7, 0, 10, 10, 10, 9, 1, 3]
    
[/code]

この場合、あなたのコードは、ご自身で見つけられた「delのせいでインデックスがずれてしまう」と言う点以外にも、いろいろな点で破綻しています。

  * `i % 2 == 0`と言う条件で「`i`が一投目を指している」ことの判定に用いていますが、データから見る限り一投目が`10`であれば二投目のデータは存在しません。従って`i % 2 == 0`と言う条件では一投目かどうかの判定はできません。

  * あなたのコードではスペアの処理が全て終わった後に、ストライクの処理を行っています。そのためストライクの後にスペアがあったとしても、ストライクの処理の際にはそのデータが`score_lines`から削除されてしまっているため、ストライクのスコアを正しく処理することができません。

  * `for i in range(len(score_lines))`で繰り返しの範囲が決定されているため、`i`は`score_lines`の最後の要素を指している可能性もあるのですが、なんのチェックも行わないまま`score_lines[i+1]`や`score_lines[i+2]`にアクセスしています。従って、まったく`del`が無いコードに書き換えてもやはりindex out of rangeが発生します。

と言うわけで以下のような修正を行うことになるでしょう。

  * `i`の値から一投目かどうかの判定をするのではなく、「`i`が必ず一投目を指すように`i`の値を制御する。」

  * 上記に合わせて`i`の指すデータより前のデータは処理済みと考えて、処理済みのデータをdelで削除してしまうと言う処理は行わない。

  * リストのインデックスを指定する場合には、必ず範囲チェックを行う。

コードとしては、こんな感じになるでしょうか。

[code]

    score_lines = [2, 5, 0, 1, 8, 2, 6, 2, 10, 2, 7, 0, 10, 10, 10, 9, 1, 3]
    
    ans_sum =[]
    
    pin_num = 10
    
    i = 0
    while len(ans_sum) < 10:
      first_throw_pins = score_lines[i]
      second_throw_pins = score_lines[i+1] if i+1 < len(score_lines) else 0
      third_throw_pins = score_lines[i+2] if i+2 < len(score_lines) else 0
      if first_throw_pins == pin_num:
        # strike
        ans_sum.append(first_throw_pins + second_throw_pins + third_throw_pins)
        i += 1
      elif first_throw_pins + second_throw_pins == pin_num:
        # spare
        ans_sum.append(first_throw_pins + second_throw_pins + third_throw_pins)
        i += 2
      else:
        ans_sum.append(first_throw_pins + second_throw_pins)
        i += 2
    print(ans_sum)
    
[/code]

`while`の条件では`i`をみていませんから、`score_lines`には決して不整合なデータは入っていないことを前提にしないと動きませんが、正しい10フレーム分のデータからのスコア計算(実際には各フレームの得点をリスト`ans_sum`に入れるまで)は出来るはずです。お試しください。

一般的な話ですが、元のコードのように「処理済みのデータを削除してしまう」と言う形で処理を進めてしまうと、インデックスがずれてしまうだけではなく、長大なリストを扱う場合の処理速度も遅くなってしまいます。可能な限り、「処理済みデータは削除してしまう」は避けるべきだと思っておいた方が良いでしょう。


CC BY-SA 3.0
2017-07-17T05:55:18.650
36439
2017-07-17T05:55:18.650
13972
36436
answer