Source code for kyu_3.battleship_field_validator.validator

"""
Solution for -> Battleship field validator.

Created by Egor Kostan.
GitHub: https://github.com/ikostan
"""


[docs] def validate_battlefield(field: list) -> bool: """ Validate battlefield. A method that takes a field for well-known board game "Battleship" as an argument and returns true if it has a valid disposition of ships, false otherwise. Argument is guaranteed to be 10*10 two-dimension array. Elements in the array are numbers, 0 if the cell is free and 1 if occupied by ship. :param field: 2D list, board game "Battleship" :return: bool, true if there is a valid disposition of ships """ counters: list = [row.count(1) for row in field] if sum(counters) != 20: return False ships: dict = { 1: [], 2: [], 3: [], 4: []} ship_counter_by_row(field, ships) ship_counter_by_col(field, ships) return all([len(ships[1]) == 4, len(ships[2]) == 3, len(ships[3]) == 2, len(ships[4]) == 1])
[docs] def ship_counter_by_row(field: list, ships: dict): """ Ship counter by row. :param field: list :param ships: dict :return: """ for index_row, row in enumerate(field): ship: list = [] for cell in enumerate(row): if row[cell[0]] == 1: ship.append([index_row, cell[0]]) else: # Allowed ship sizes between 1 and 4 cells if (len(ship) == 1 and all_cells_valid(ships=ships, field=field, direction='submarine', ship=ship)): ships[len(ship)].append(ship) elif (1 < len(ship) <= 4 and all_cells_valid(ships=ships, field=field, direction='horizontal', ship=ship)): ships[len(ship)].append(ship) ship = [] # Allowed ship sizes between 1 and 4 cells if (len(ship) == 1 and all_cells_valid(ships=ships, field=field, direction='submarine', ship=ship)): ships[len(ship)].append(ship) elif (1 < len(ship) <= 4 and all_cells_valid(ships=ships, field=field, direction='horizontal', ship=ship)): ships[len(ship)].append(ship)
[docs] def ship_counter_by_col(field: list, ships: dict): """ Ship counter by col. :param field: list :param ships: dict :return: """ for index_col in range(len(field[0])): ship: list = [] for index_row, row in enumerate(field): if row[index_col] == 1: ship.append([index_row, index_col]) else: # Allowed ship sizes between 1 and 4 cells if (len(ship) == 1 and all_cells_valid(ships=ships, field=field, direction='submarine', ship=ship)): ships[len(ship)].append(ship) elif (1 < len(ship) <= 4 and all_cells_valid(ships=ships, field=field, direction='vertical', ship=ship)): ships[len(ship)].append(ship) ship = [] # Allowed ship sizes between 1 and 4 cells if (len(ship) == 1 and all_cells_valid(ships=ships, field=field, direction='submarine', ship=ship)): ships[len(ship)].append(ship) elif (1 < len(ship) <= 4 and all_cells_valid(ships=ships, field=field, direction='vertical', ship=ship)): ships[len(ship)].append(ship)
[docs] def all_cells_valid(**kwargs): """ Validate all cells. :param kwargs: :return: """ return all( is_valid_cell(ships=kwargs['ships'], field=kwargs['field'], cell=cell, direction=kwargs['direction']) for cell in kwargs['ship'])
[docs] def check_vertical(row, col, field) -> bool: """ Verify vertical direction. :param row: :param col: :param field: list, board game "Battleship" (list) :return: """ for row_id in range(row - 1, row + 2): for col_id in range(col - 1, col + 2): if 0 <= row_id < len(field) \ and 0 <= col_id < len(field) \ and col_id != col \ and field[row_id][col_id] == 1: return False return True
[docs] def check_horizontal(row, col, field) -> bool: """ Verify horizontal direction. :param row: :param col: :param field: list, board game "Battleship" (list) :return: """ for row_id in range(row - 1, row + 2): for col_id in range(col - 1, col + 2): if 0 <= row_id < len(field) \ and 0 <= col_id < len(field) \ and row_id != row \ and field[row_id][col_id] == 1: return False return True
[docs] def check_submarine(**kwargs) -> bool: """ Check submarine. Check if submarine already in list (avoid duplicates) Validates if submarine cell has contacts with other ships/cells ships: dict, collection of valid ships (dict) field: list, board game "Battleship" (list) cell: list, candidate for single ship/submarine :return: bool """ # check if submarine already in list (avoid duplicates) for submarine in kwargs['ships'][1]: if [kwargs['cell']] == submarine: return False # validates if submarine cell has contacts with other ships/cells for row_id in range(kwargs['row'] - 1, kwargs['row'] + 2): for col_id in range(kwargs['col'] - 1, kwargs['col'] + 2): if 0 <= row_id < len(kwargs['field']) \ and 0 <= col_id < len(kwargs['field']) \ and (col_id != kwargs['col'] or row_id != kwargs['row']) \ and kwargs['field'][row_id][col_id] == 1: return False return True
[docs] def is_valid_cell(**kwargs) -> bool: """ Check if cell is valid. Validates if single cell result is valid (valid submarine or single ship cell) :return: bool """ row, col = kwargs['cell'][0], kwargs['cell'][1] if kwargs['direction'] == 'submarine': if not check_submarine(row=row, col=col, ships=kwargs['ships'], field=kwargs['field'], cell=kwargs['cell']): return False elif kwargs['direction'] == 'horizontal': if not check_horizontal(row=row, col=col, field=kwargs['field']): return False elif kwargs['direction'] == 'vertical': if not check_vertical(row=row, col=col, field=kwargs['field']): return False return True