text: #!/usr/bin/env python3
  from random import randrange
  from typing import List, Tuple
  
  PointType = Tuple[int, int]
  VectorType = PointType
  SeaType = Tuple[List[int], ...]
  
  SEA_WIDTH = 6
  DESTROYER_LENGTH = 2
  CRUISER_LENGTH = 3
  AIRCRAFT_CARRIER_LENGTH = 4
  
  
  def random_vector() -> Tuple[int, int]:
      while True:
          vector = (randrange(-1, 2), randrange(-1, 2))
  
          if vector == (0, 0):
              # We can't have a zero vector, so try again
              continue
  
          return vector
  
  
  def add_vector(point: PointType, vector: VectorType) -> PointType:
      return (point[0] + vector[0], point[1] + vector[1])
  
  
  def place_ship(sea: SeaType, size: int, code: int) -> None:
      while True:
          start = (randrange(1, SEA_WIDTH + 1), randrange(1, SEA_WIDTH + 1))
          vector = random_vector()
  
          # Get potential ship points
          point = start
          points = []
  
          for _ in range(size):
              point = add_vector(point, vector)
              points.append(point)
  
          if not all([is_within_sea(point, sea) for point in points]) or any(
              [value_at(point, sea) for point in points]
          ):
              # ship out of bounds or crosses other ship, trying again
              continue
  
          # We found a valid spot, so actually place it now
          for point in points:
              set_value_at(code, point, sea)
  
          break
  
  
  def print_encoded_sea(sea: SeaType) -> None:
      for x in range(len(sea)):
          print(" ".join([str(sea[y][x]) for y in range(len(sea) - 1, -1, -1)]))
  
  
  def is_within_sea(point: PointType, sea: SeaType) -> bool:
      return (1 <= point[0] <= len(sea)) and (1 <= point[1] <= len(sea))
  
  
  def has_ship(sea: SeaType, code: int) -> bool:
      return any(code in row for row in sea)
  
  
  def count_sunk(sea: SeaType, *codes: int) -> int:
      return sum(not has_ship(sea, code) for code in codes)
  
  
  def value_at(point: PointType, sea: SeaType) -> int:
      return sea[point[1] - 1][point[0] - 1]
  
  
  def set_value_at(value: int, point: PointType, sea: SeaType) -> None:
      sea[point[1] - 1][point[0] - 1] = value
  
  
  def get_next_target(sea: SeaType) -> PointType:
      while True:
          try:
              guess = input("? ")
              point_str_list = guess.split(",")
  
              if len(point_str_list) != 2:
                  raise ValueError()
  
              point = (int(point_str_list[0]), int(point_str_list[1]))
  
              if not is_within_sea(point, sea):
                  raise ValueError()
  
              return point
          except ValueError:
              print(
                  f"INVALID. SPECIFY TWO NUMBERS FROM 1 TO {len(sea)}, SEPARATED BY A COMMA."
              )
  
  
  def setup_ships(sea: SeaType) -> None:
      place_ship(sea, DESTROYER_LENGTH, 1)
      place_ship(sea, DESTROYER_LENGTH, 2)
      place_ship(sea, CRUISER_LENGTH, 3)
      place_ship(sea, CRUISER_LENGTH, 4)
      place_ship(sea, AIRCRAFT_CARRIER_LENGTH, 5)
      place_ship(sea, AIRCRAFT_CARRIER_LENGTH, 6)
  
  
  def main() -> None:
      sea = tuple([0 for _ in range(SEA_WIDTH)] for _ in range(SEA_WIDTH))
      setup_ships(sea)
      print(
          """
                  BATTLE
  CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY
  
  THE FOLLOWING CODE OF THE BAD GUYS' FLEET DISPOSITION
  HAS BEEN CAPTURED BUT NOT DECODED:
  
  """
      )
      print_encoded_sea(sea)
      print(
          """
  
  DE-CODE IT AND USE IT IF YOU CAN
  BUT KEEP THE DE-CODING METHOD A SECRET.
  
  START GAME"""
      )
      splashes = 0
      hits = 0
  
      while True:
          target = get_next_target(sea)
          target_value = value_at(target, sea)
  
          if target_value < 0:
              print(
                  f"YOU ALREADY PUT A HOLE IN SHIP NUMBER {abs(target_value)} AT THAT POINT."
              )
  
          if target_value <= 0:
              print("SPLASH! TRY AGAIN.")
              splashes += 1
              continue
  
          print(f"A DIRECT HIT ON SHIP NUMBER {target_value}")
          hits += 1
          set_value_at(-target_value, target, sea)
  
          if not has_ship(sea, target_value):
              print("AND YOU SUNK IT. HURRAH FOR THE GOOD GUYS.")
              print("SO FAR, THE BAD GUYS HAVE LOST")
              print(
                  f"{count_sunk(sea, 1, 2)} DESTROYER(S),",
                  f"{count_sunk(sea, 3, 4)} CRUISER(S),",
                  f"AND {count_sunk(sea, 5, 6)} AIRCRAFT CARRIER(S).",
              )
  
          if any(has_ship(sea, code) for code in range(1, 7)):
              print(f"YOUR CURRENT SPLASH/HIT RATIO IS {splashes}/{hits}")
              continue
  
          print(
              "YOU HAVE TOTALLY WIPED OUT THE BAD GUYS' FLEET "
              f"WITH A FINAL SPLASH/HIT RATIO OF {splashes}/{hits}"
          )
  
          if not splashes:
              print("CONGRATULATIONS -- A DIRECT HIT EVERY TIME.")
  
          print("\n****************************")
          break
  
  
  if __name__ == "__main__":
      main()
  