battleship.py 6.05 KB
"""
CS1 24fa - Assignment 1
Main game loop of a battleship game implementation.
"""

from src.battleship_helpers import *
from src.battleship_visualizer import *
from src.task3_5 import ask_coordinates
from src.task6 import make_boolean_square_board
from src.task11 import check_win
from src.task12 import go_to_next_round

# Define constants and variables
# no ship of length 0 or 1, 2 ships of length 2, 2 of length 3, and 1 of 4
SHIPS = [0, 0, 2, 2, 1]
PURPLE = "\033[35m"
DEFAULT = "\033[0m"
N = 7


# Intro message to players
def intro(player, rounds):
    """
    Display an introduction message to the players and begin the game.

    Args:
        player (int): The current player (0 for Player 1, 1 for Player 2).
        rounds (int): The current round number.
    """
    clear()
    print("Welcome to CS1 Battleships, the 2 player version!")
    print(RULES)
    input("Player 1, when you are ready to start the game, \
hide the screen from your opponent and press Enter!")
    clear()
    print(f"{PURPLE}Player {player + 1} - Round {rounds}{DEFAULT}\n")


def place_ships(player, rounds, board_ships_p, ships_placed):
    """
    Allow a player to place their ships on the board.

    Args:
        player (int): current player (0 for Player 1, 1 for Player 2).
        rounds (int): current round number.
        board_ships_p (list[list[bool]]): player's ship board.
        ships_placed (list[list[tuple[int, int]]]): coords of placed ships.
    """
    # Players need to place their ships
    visualize_ship_board(board_ships_p)
    for length in range(len(SHIPS)-1, 0, -1):
        for i in range(SHIPS[length]):
            # TODO (Task 7): add a print statement below to display:
            # Place a ship of size LENGTH!
            # with LENGTH being the value of the current ship's size.
            # Hint: Our solution is only one line!

            invalid_placement = True
            rotate = False
            while invalid_placement:
                # Checks valid coordinate
                coord = ask_coordinates(board_ships_p)
                # Rotate ship for player if desired
                if input(f"Do you want to rotate the ship horizontal to vertical? ").upper() == 'Y':
                    rotate = True
                if not check_placement(board_ships_p, length, coord, rotate):
                    print("Invalid position for your ship, try again!")
                else:
                    invalid_placement = False
            # Place ship
            x, y = coord
            new_ship = []
            for j in range(length):
                if rotate:
                    board_ships_p[y + j][x] = True

                    # TODO (Task 8): modify the following line of code to append
                    # the tuple of coordinates that was just set to True on the
                    # board. Hint: remember that coordinate tuples are in the
                    # following format (x-coordinate, y-coordinate).
                    new_ship.append(())

                else:
                    board_ships_p[y][x + j] = True

                    # TODO (Task 9): modify the following line of code to append
                    # the tuple of coordinates that was just set to True on the
                    # board.
                    new_ship.append(())

            # TODO (Task 10): modify the following line of code to append the
            # list of coordinates we just populated to ships_placed of the
            # current player. Hint: ships placed is of the following format
            # [[],[]] with the first list corresponding to player 1 and the
            # second to player 2. Remember that you can use the player
            # variable.
            ships_placed

            clear()
            print(f"{PURPLE}Player {player + 1} - Round \
{rounds}{DEFAULT}\n")
            visualize_ship_board(board_ships_p)


def attack(player, rounds, board_ships, board_hits, ships_placed):
    """
    Allow a player to attack the opponent's ships.

    Args:
        player (int): current player (0 for Player 1, 1 for Player 2).
        rounds (int): current round number.
        board_ships (list[list[list[bool]]]): players' ship boards.
        board_hits (list[list[list[bool]]]): players' hit boards.
        ships_placed (list[list[tuple[int, int]]]): coords of placed ships.

    Returns:
        bool: True if the attack was a miss, False otherwise.
    """
    print("You're on the offensive, choose your next spot carefully!")
    x, y = ask_coordinates(board_hits[player])
    board_hits[player][y][x] = True
    if board_ships[get_opponent(player)][y][x]:
        # Check if player won!
        if check_win(board_ships, board_hits, player, N):
            clear()
            print(f"Congratulations Player {player + 1}! You sank all of the \
enemy ships in {rounds} rounds!")
            exit()
        clear()
        print(f"{PURPLE}Player {player + 1} - Round {rounds}{DEFAULT}\n")
        visualize_boards(board_ships, board_hits, player,
                         ships_placed[get_opponent(player)])
        print("And that's a hit! Woohoo! Your turn again!")
    else:
        print("You missed, better luck next time...")
        return True


def game_loop():
    """
    Main loop of the Battleship game, managing the flow between players and 
    rounds.
    """
    # Initialize boards
    board_ships = [make_boolean_square_board(N), make_boolean_square_board(N)]
    board_hits = [make_boolean_square_board(N), make_boolean_square_board(N)]
    ships_placed = [[], []]
    player = 0
    rounds = 0
    intro(player, rounds)
    while True:
        if rounds == 0:
            place_ships(player, rounds, board_ships[player], ships_placed)
        else:
            visualize_boards(board_ships, board_hits, player,
                            ships_placed[get_opponent(player)])
            missed = False
            while not missed:
                missed = attack(player, rounds, board_ships, board_hits,
                                ships_placed)
        rounds = go_to_next_round(player, rounds)
        player = switch_player(player, rounds)


if __name__ == "__main__":
    game_loop()