Python
Fortran
python3
情報処理

情報処理の問題をfortranとpythonで書く!#07 -ボーリングのスコア計算 (残っているピンの数から)

はじめに

大学の授業でfortranを使った情報処理の授業があり、その課題をpythonとfortranの両方で書いていきたいと思います。

プログラミングについては、初心者ですので誤りや改善点など多くあると思います。
気づいた方はコメントで指摘していただけると助かります。

以下にそれぞれの環境を記しておきます

fortran90

windows10
cygwin2.8.1

python

windows10
python3.6.2

課題内容 問5-18

ボウリングの得点を計算するプログラムを作れ.ただし,データとしては,各投球の直後に残っているピンの数を次の例のように与えるものとする.
5, 1, 0, 2, 0, 3, 1, 0, 8, 0, 0, 3, 0, 0, 1, 0, 1

fortran

fortranのプログラムについては10フレーム目のスコア計算の仕方を勘違いしていたため、正しいスコアを出力しない場合があります。pythonのコードについてはおそらく正しいと考えられるため、fortranのコードもpythonの分岐に倣って、後日修正します
修正しました(2018/02/28)

出力結果

$ ./a.exe 5-18.txt
 1     9
 2    29
 3    46
 4    55
 5    75
 6    95
 7   115
 8   135
 9   155
10   174
score=   174

1行目は出力結果ではなく、入力なのですが、getargをはじめて見る人がこのプログラムを動かせるようにのせておきました.出力結果は、ボーリングの最終スコアだけでよさそうですが、味気ないので1~9ターン目の点数も表示させるようにしました。

ウェブ上でボーリングのスコアを計算できるサイトで試してみましたが、あっているようです。http://www.eonet.ne.jp/~gon-chan/etc/bowling/bowling.html

入力ファイル

5-18.txt
5, 1, 0, 2, 0, 3, 1, 0, 8, 0, 0, 3, 0, 0, 1, 0, 1

今回の問題では数値を横並びで与えられているため、入力ファイルも横並びの数値が入ったテキストファイルを仮定しました。

toi5_18.f90
program main
implicit none
integer,parameter::UNIT_NUM=12
integer,parameter::boring_n=10
integer::num=1
integer::sum_score=0
character(100) arg
integer a(21),iost,i1

call getarg(1,arg)

open(UNIT_NUM,file=arg,status="old",iostat=iost)
if(iost/=0) stop '<Error> Input File Data'

read(UNIT_NUM,*,iostat=iost) a(:)

close(UNIT_NUM)

DO i1=1,boring_n
  if (a(num)==0 .and. a(num+1)==0) then !ダブルの場合
    sum_score=sum_score+30-a(num+2)
    num=num+1
  else if (a(num)==0) then ! ストライクの場合
    sum_score=sum_score+20-a(num+2)
    num=num+1
  else if (a(num+1)==0) then !スペアの場合
    sum_score=sum_score+20-a(num+2)
    num=num+2
  else
    sum_score=sum_score+10-a(num+1)
    num=num+2
  end if
  write(*,'(I2,I6)') i1,sum_score
end do

write(*,'(A,I4)') "score=  ", sum_score

stop
end program main

今回の構想は、ボーリングの投げる回数は最大21回?なので、それを基準に数値を配列に入れ込み、10ターン目はほかのターンと合算方法が異なるため、先にif文で処理して、あとはストライクとスペアの時の場合でif文で場合分けを行いました。

上の内容は修正前のコードに関してです。修正後のコードはpythonで書いたコードを元に作成しました。
今回の構想はボーリングの投げる回数は最大21回であることから、それを基準に配列に入れ込み、それぞれのフレームごとにif文で分岐してスコアを計算させました。

今回、一番問題があるのがread文です。read文の特徴として読み込みごとに改行してしまうため、横一列の入力データに対してread文1回で書く必要があって終端処理がうまくできませんでした。おそらく、知識不足で何らかの手はあると思います。
今回は、ボーリングスコアであるため配列上ではゴミが含まれていてもスコアのルールから終端がわかるためゴミを入れたまま、計算処理を行いました。

作っていて思ったのですが、なぜ入力が倒したピンの数ではなくて残ったピンの数なのか、残ったピンの数だとかなり簡潔にかける方法があるんですかね...実用的なのは倒したピンの数だと思いますが:frowning2:

改善点はスコア合算のところの書き方が、自分で見てもわかりずらいところです。分岐の数はこんなものかと思いますが、スコア計算のところはもう少し工夫できそうな気がします。

python

pythonでコードを書いていると、この問題が意外と良問な気がしてきました。ボーリングのスコア計算は10フレーム目は例外として考えないといけないと思い込んでいましたが、以下のようにコードを書いてみると10フレーム目も全く同じコードとなり、10フレーム目を分岐させずに書けました。
ボーリングの1フレームという単位は、かなりつかみづらい概念なのかもしれません。:spy:

出力結果

9
29
46
55
75
95
115
135
155
174
score= 174

プログラムコード

5-18.py
f=open("5-18.txt","r")
score0=f.read()
#print(score0)
score1=score0.split(",")
score2=list(map(int,score1))
#print(score2)

num=0
sum_score=0
for frame in range(1,11):
    if score2[num]==0 and score2[num+1]==0: !ダブルの場合(2フレーム連続ストレート)
        sum_score=sum_score+30-score2[num+2]
        num=num+1
    elif score2[num]==0: !ストライクの場合
        sum_score=sum_score+20-score2[num+2]
        num=num+1
    elif score2[num+1]==0: !スペアの場合
        sum_score=sum_score+20-score2[num+2]
        num=num+2
    else: !それ以外の場合
        sum_score=sum_score+10-score2[num+1]
        num=num+2
    print(sum_score)

print("score=",sum_score)

残っているピンの数が入ったテキストファイルはfortranと同様のものを用いました。

正直、これほど短く書けるとは思っていなかったので驚きです。