- games = open('bowlarama.txt').read().splitlines() # load bowling data
- def score(data): # this function calculates the score of the given data
- score = 0 # initialize the score to 0
- val = {'x': 10, '/': 10, '-': 0, '': 0} # strikes and spares will be
- # counted as 10, whereas empty
- # strings (such as when bonus could
- # go out ouf bounds) and - (which
- # indicates a third frame 10 roll
- # that never occurs) count as 0
- for i in range(10): # then we add the literal values
- val[str(i)] = i # of 0-9 to the dict
- for i in range(len(data) - (data[-3] == 'x' and 2 or 0)):
- # this iterator is where a lot of the magic happens; whenever we see a
- # strike, we have to add the next two rolls to obtain the bonus; however,
- # we don't want to add bonuses for strikes that occur as bonuses in the
- # final frame; to that end, we count right up to the end of the data
- # string unless the first roll in frame 10 is a strike, in which case
- # we only count up to that strike
- if data[i] == 'x': # STRIKE!
- score += i < len(data) - 1 and 10 or 0 # we add 10 to score unless
- # the strike in question is
- # part of a frame-10 bonus
- bonus = i < len(data) - 2 and data[i+1:i+3] or ''
- # bonus contains the data for the next two rolls (as a joined
- # string) unless we are handling a frame-10 bonus, in which case
- # it is empty, thus adding no additional points to score
- if '/' in bonus: # here we have a spare in the bonus! regardless
- # of what the other piece of data is...
- score += 10 # we add 10 to the score
- else: # if there is no spare in the bonus...
- for j in bonus: # we iterate over the bonus and add the
- score += val[j] # numerical value to score; this is not the
- # same as val[bonus[0]] + val[bonus[1]],
- # because we must also consider that there
- # will only be 1 piece of bonus data if the
- # strike in question is in frame 10
- elif data[i] == '/': # SPARE!
- score += 10 # we add 10 regardless
- score += val[data[i+1:i+2]] # then we obtain the roll that occured
- # after the spare and add it to the
- # total; again, taking into account the
- # various ways that frame 10 can be
- # tricky, this can't just be
- # + val[data[i+1]], otherwise it would
- # go out of bounds if, for instance
- # the tenth frame is x4/, where the
- # last bowl is a strike, and then the
- # player gets to roll two more bonus
- # balls and ends up sparing. Bless
- # the Python gods who decided it would
- # be a good idea to allow range slices
- # to go out of bounds.
- else: # neither STRIKE nor SPARE
- if i < len(data) - 1 and data[i+1] == '/': # check the next roll
- score += 0 # if the user ends up sparing, we don't want to
- # add this value to the score, because we're
- # already adding 10 for every spare we see
- else: # no strike, no spare, and no spare in the next roll
- if i < len(data) - 1: # as long as this isn't the ver last
- score += val[data[i]] # roll, add its numerical value to the
- # total; this is where val[] really
- # shines, because this line accounts
- # for every possible final roll
- # x, /, -, or 0-9
- return score # cause the function to return the tallied score
- total = 0.0 # initalized total as float(0) because we want the average
- # rounded to the nearest hundredth
- for g in games: # iterate over each of the sequences of rolls in the game data
- total += score(g) # add the result of the scory tally to the total
- print total / len(games) # calculate the average by dividing the number of
- # games into the accumulated total; we will receive
- # a decimal number (91.462895) that doesn't require
- # rounding, so we're done. Answer: 91.46