import random
from typing import List


def simulate_roll(pins: List[int]) -> None:
    for _ in range(20):
        x = random.randint(0, 14)
        if x < len(pins):
            pins[x] = 1


def calculate_score(rolls: List[int]) -> int:
    score = 0
    frame = 1
    b = 1
    for index, pins in enumerate(rolls):
        score += pins
        if b == 1:
            if pins == 10:  # strike
                score += sum(rolls[index + 1 : index + 3])
                frame += 1
            else:
                b = 2
        else:
            if sum(rolls[index - 1 : index + 1]) == 10:  # spare
                score += rolls[index + 1]
            b = 1
            frame += 1
        if frame > 10:
            break

    return score


class Player:
    def __init__(self, name: str) -> None:
        self.name = name
        self.rolls: List[int] = []

    def play_frame(self, frame: int) -> None:
        extra = 0
        prev_score = 0
        pins = [0] * 10  # reset the pins
        for ball in range(2):
            simulate_roll(pins)
            score = sum(pins)
            self.show(pins)
            pin_count = score - prev_score
            self.rolls.append(pin_count)  # log the number of pins toppled this roll
            print(f"{pin_count} for {self.name}")
            if score - prev_score == 0:
                print("GUTTER!!!")
            if ball == 0:
                if score == 10:
                    print("STRIKE!!!")
                    extra = 2
                    break  # cannot roll more than once in a frame
                else:
                    print(f"next roll {self.name}")
            else:
                if score == 10:
                    print("SPARE!")
                    extra = 1

            prev_score = score  # remember previous pins to distinguish ...
        if frame == 9 and extra > 0:
            print(f"Extra rolls for {self.name}")
            pins = [0] * 10  # reset the pins
            score = 0
            for _ball in range(extra):
                if score == 10:
                    pins = [0] * 10
                simulate_roll(pins)
                score = sum(pins)
                self.rolls.append(score)

    def __str__(self) -> str:
        return f"{self.name}: {self.rolls}, total:{calculate_score(self.rolls)}"

    def show(self, pins: List[int]) -> None:
        pins_iter = iter(pins)
        print()
        for row in range(4):
            print(" " * row, end="")
            for _ in range(4 - row):
                p = next(pins_iter)
                print("O " if p else "+ ", end="")
            print()


def centre_text(text: str, width: int) -> str:
    t = len(text)
    return (" " * ((width - t) // 2)) + text


def main() -> None:
    print(centre_text("Bowl", 80))
    print(centre_text("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY", 80))
    print()
    print("WELCOME TO THE ALLEY.")
    print("BRING YOUR FRIENDS.")
    print("OKAY LET'S FIRST GET ACQUAINTED.")

    while True:
        print()
        if input("THE INSTRUCTIONS (Y/N)? ") in "yY":
            print("THE GAME OF BOWLING TAKES MIND AND SKILL. DURING THE GAME")
            print("THE COMPUTER WILL KEEP SCORE. YOU MAY COMPETE WITH")
            print("OTHER PLAYERS[UP TO FOUR]. YOU WILL BE PLAYING TEN FRAMES.")
            print("ON THE PIN DIAGRAM 'O' MEANS THE PIN IS DOWN...'+' MEANS THE")
            print("PIN IS STANDING. AFTER THE GAME THE COMPUTER WILL SHOW YOUR")
            print("SCORES.")

        total_players = int(input("FIRST OF ALL...HOW MANY ARE PLAYING? "))
        player_names = []
        print()
        print("VERY GOOD...")
        for index in range(total_players):
            player_names.append(Player(input(f"Enter name for player {index + 1}: ")))

        for frame in range(10):
            for player in player_names:
                player.play_frame(frame)

        for player in player_names:
            print(player)

        if input("DO YOU WANT ANOTHER GAME? ") not in "yY":
            break


if __name__ == "__main__":
    main()


############################################################################################
#
# This is a fairly straight conversion to python with some exceptions.
# I have kept most of the upper case text that the program prints.
# I have added the feature of giving names to players.
# I have added a Player class to store player data in.
# This last change works around the problems in the original storing data in a matrix.
# The original had bugs in calculating indexes which meant that the program
# would overwrite data in the matrix, so the results printed out contained errors.
# The last change is to do with the strict rules which allow extra rolls if the player
# scores a spare or strike in the last frame.
# This program allows these extra rolls and also calculates the proper score.
#
############################################################################################
