diff --git a/README.md b/README.md index bfbdde8..bfff89b 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,8 @@ |@..@'. ~ " ' ~ | |>O>o<@< \____ .'| 4 ** |_.~._@'.. ~ ~ *| | _| |_ ..\_\_ ..'* | 5 ** | ||| @@ '''...| |... .' '.'''../..| 6 ** - + |#~~~@@@@ @ | |/\ ''. | | -/ :| 7 ** + ## 2023 . * ~~~~ /\ ' . 14 diff --git a/fred.py b/fred.py index 7d71c87..e544805 100644 --- a/fred.py +++ b/fred.py @@ -1,144 +1,347 @@ -import re,sys +import re +import sys def loadFile(input_f): + """ + Loads a file and returns its lines as a list. + + Args: + input_f (str): The file path to read from. + + Returns: + list: A list of lines from the file, with each line stripped of trailing newlines. + + Raises: + FileNotFoundError: If the file cannot be found. + IOError: If there is an error reading the file. + """ lines = [] - with open(input_f) as file: - for line in file: - lines.append(line.rstrip()) + try: + with open(input_f) as file: + for line in file: + lines.append(line.rstrip()) # Removes trailing newline from each line + except FileNotFoundError: + raise FileNotFoundError(f"The file '{input_f}' was not found.") + except IOError as e: + raise IOError(f"Error reading file '{input_f}': {e}") return lines def convert_list(input_list): """ - Convert a list of strings to integers where possible, leaving others as strings. + Converts a list of strings to integers where possible, leaving others as strings. Args: input_list (list): A list of strings to be converted. Returns: list: A list with integers or strings based on the input. + + Raises: + TypeError: If the input is not a list. """ + if not isinstance(input_list, list): + raise TypeError("Input must be a list.") + converted_list = [] for item in input_list: try: - converted_list.append(int(item)) + converted_list.append(int(item)) # Attempt to convert string to integer except ValueError: - converted_list.append(item) + converted_list.append(item) # Leave as string if conversion fails return converted_list +def toGrid(input, parser=None): + """ + Converts input (file or data) into a grid (list of lists). + + Args: + input (str): The file path or data to be converted into a grid. + parser (function, optional): A parser function to process each line before adding to the grid. + Defaults to None. + + Returns: + list: A 2D list (grid) representation of the input. -def toGrid(input,parser=None): + Raises: + FileNotFoundError: If the file cannot be found (if input is a file path). + ValueError: If the parser function is invalid. + """ + if parser is not None and not callable(parser): + raise ValueError("The parser must be a callable function.") + grid = [] - if parser: + try: with open(input) as file: for line in file: - grid.append(list2int(line.rstrip())) - else: - with open(input) as file: - for line in file: - grid.append(list(line.rstrip())) + grid.append(list2int(line.rstrip()) if parser else list(line.rstrip())) # Use parser or default processing + except FileNotFoundError: + raise FileNotFoundError(f"The file '{input}' was not found.") + except IOError as e: + raise IOError(f"Error reading file '{input}': {e}") + return grid -def swap(a:int,b:int,lst:list): +def swap(a: int, b: int, lst: list): """ - Swaps two numbers in a list based on their indices. - - Parameters: - lst (list): The list of numbers. - index1 (int): The index of the first number. - index2 (int): The index of the second number. + Swaps two elements in a list based on their indices. + + Args: + a (int): Index of the first element. + b (int): Index of the second element. + lst (list): The list in which to swap elements. Returns: - list: The list after swapping the two numbers. - """ - - if a < 0 or b < 0 or a >= len(lst) or b >= len(lst): - raise IndexError("Index out of range.") + list: The list with swapped elements. - lst[a], lst[b] = lst[b], lst[a] + Raises: + IndexError: If any index is out of range. + TypeError: If lst is not a list. + """ + if not isinstance(lst, list): + raise TypeError("The provided object is not a list.") + if a < 0 or b < 0 or a >= len(lst) or b >= len(lst): + raise IndexError("Index out of range.") # Ensure indices are valid + lst[a], lst[b] = lst[b], lst[a] # Swap the elements return lst -def addTuples(x:tuple,y:tuple): - return (x[0]+y[0],x[1]+y[1]) +def addTuples(x: tuple, y: tuple): + """ + Adds two tuples element-wise. + + Args: + x (tuple): The first tuple. + y (tuple): The second tuple. + + Returns: + tuple: A tuple with the sum of corresponding elements of x and y. -def findDupes(input:list): - # Returns the indicies of duplicate values in list + Raises: + TypeError: If x or y are not tuples. + """ + if not isinstance(x, tuple) or not isinstance(y, tuple): + raise TypeError("Both inputs must be tuples.") + return (x[0] + y[0], x[1] + y[1]) + +def findDupes(input: list): + """ + Finds duplicate elements in a list and returns their values. + + Args: + input (list): The list to check for duplicates. + + Returns: + list: A list of duplicate values in the input. + + Raises: + TypeError: If the input is not a list. + """ + if not isinstance(input, list): + raise TypeError("Input must be a list.") return [item for item in set(input) if input.count(item) > 1] -def lprint(x:str,log:bool): +def lprint(x: str, log: bool): + """ + Prints a string if logging is enabled. + + Args: + x (str): The string to print. + log (bool): A flag to control logging. + + Returns: + None + """ if log: print(x) def expand_grid(grid): + """ + Expands the grid by adding a border of '.' characters around it. + + Args: + grid (list): A 2D grid to expand. + + Returns: + list: A new 2D grid with a border added. + + Raises: + TypeError: If grid is not a list of lists. + """ + if not all(isinstance(row, list) for row in grid): + raise TypeError("Grid must be a list of lists.") + num_rows = len(grid) num_cols = len(grid[0]) - expanded_grid = [] + # Add top and bottom borders expanded_grid.append(['.'] * (num_cols + 2)) + # Add left and right borders for each row for row in grid: expanded_grid.append(['.'] + row + ['.']) - expanded_grid.append(['.'] * (num_cols + 2)) + expanded_grid.append(['.'] * (num_cols + 2)) # Bottom border return expanded_grid def getCenter(grid): - return (int(len(grid)/2),int(len(grid[0])/2)) + """ + Gets the center position of a grid (middle of the grid). -def nprint(grid,cur:set=None,sign:str=None): - for idx,i in enumerate(grid): - for jdx,j in enumerate(i): - if (idx,jdx) == cur: + Args: + grid (list): A 2D grid. + + Returns: + tuple: A tuple containing the row and column index of the center. + + Raises: + TypeError: If grid is not a list of lists. + """ + if not all(isinstance(row, list) for row in grid): + raise TypeError("Grid must be a list of lists.") + + return (int(len(grid) / 2), int(len(grid[0]) / 2)) + +def nprint(grid, cur: set = None, sign: str = None): + """ + Prints a grid, highlighting the current position if specified. + + Args: + grid (list): A 2D grid to print. + cur (set, optional): A set containing the (row, col) indices of the current position. + Defaults to None. + sign (str, optional): The sign to highlight the current position with. Defaults to None. + + Returns: + None + + Raises: + TypeError: If cur is not a set or sign is not a string. + """ + if cur is not None and not isinstance(cur, tuple): + raise TypeError("Cur must be a tuple with (row, column) indices.") + if sign is not None and not isinstance(sign, str): + raise TypeError("Sign must be a string.") + + for idx, i in enumerate(grid): + for jdx, j in enumerate(i): + if (idx, jdx) == cur: if len(sign) > 1: - print(sign[0]+grid[idx][jdx]+sign[1],end='') + print(sign[0] + grid[idx][jdx] + sign[1], end='') # Print with sign else: - print(sign,end=' ') - else: - print(grid[idx][jdx],end=' ') + print(sign, end=' ') # Print sign + else: + print(grid[idx][jdx], end=' ') # Regular grid element print() def list2int(x): + """ + Converts a list of strings to a list of integers. + + Args: + x (list): A list of strings to convert. + + Returns: + list: A list of integers. + + Raises: + TypeError: If x is not a list. + """ + if not isinstance(x, list): + raise TypeError("Input must be a list.") return list(map(int, x)) -def grid_valid(x,y,grid): +def grid_valid(x, y, grid): + """ + Checks if a grid position is valid (within bounds). + + Args: + x (int): The row index. + y (int): The column index. + grid (list): The 2D grid to check against. + + Returns: + bool: True if the position is within bounds, False otherwise. + + Raises: + TypeError: If grid is not a list of lists. + """ + if not all(isinstance(row, list) for row in grid): + raise TypeError("Grid must be a list of lists.") + rows = len(grid) cols = len(grid[0]) return 0 <= x < rows and 0 <= y < cols -def get_re(pattern,str): +def get_re(pattern, str): + """ + Returns a match object if the pattern matches the string, else None. + + Args: + pattern (str): The regular expression pattern to match. + str (str): The string to match against. + + Returns: + match or None: A match object if a match is found, otherwise None. + + Raises: + TypeError: If str is not a string. + """ + if not isinstance(str, str): + raise TypeError("Input string must be of type str.") + match = re.match(pattern, str) if match: return match return None def ppprint(x): - for idx,i in enumerate(x): - for jdx,j in enumerate(i): - print(x[idx][jdx],end='') + """ + Pretty prints a 2D grid or matrix. + + Args: + x (list): A 2D grid to print. + + Returns: + None + + Raises: + TypeError: If x is not a list of lists. + """ + if not all(isinstance(row, list) for row in x): + raise TypeError("Input must be a list of lists.") + + for idx, i in enumerate(x): + for jdx, j in enumerate(i): + print(x[idx][jdx], end='') print() -def get_value_in_direction(grid, position, direction=None,length=1,type:str=None): +def get_value_in_direction(grid, position, direction=None, length=1, type: str = None): """ Get the value(s) in a specified direction from a given position in a grid. If no direction is provided, returns the value at the current position. - Parameters: + Args: grid (list of list of int/float/str): The 2D grid. position (set): A set containing x (row index) and y (column index) as integers. - direction (str, optional): The direction to check. - Options: 'up', 'down', 'left', 'right', - 'up-left', 'up-right', 'down-left', 'down-right'. + direction (str, optional): The direction to check. Defaults to None. length (int, optional): The number of steps to check in the given direction. Default is 1. + type (str, optional): The type of result to return ('list' or 'str'). Defaults to None. Returns: - list: A list of values in the grid for the specified direction and length. - Returns an empty list if any position is out of bounds. + list or str: A list or string of values in the specified direction, or a single value. + + Raises: + ValueError: If direction is invalid or position is not a set of two integers. + TypeError: If grid is not a list of lists. """ - # Ensure the position is a set of two integers + if not all(isinstance(row, list) for row in grid): + raise TypeError("Grid must be a list of lists.") + + # Ensure position is a set of two integers if len(position) != 2 or not all(isinstance(coord, int) for coord in position): raise ValueError("Position must be a set containing two integers (x, y).") - + x, y = position offsets = { 'up': (-1, 0), @@ -168,16 +371,15 @@ def get_value_in_direction(grid, position, direction=None,length=1,type:str=None values = [] if length == 1: - # Check for out-of-bounds, considering varying row lengths + # Check for out-of-bounds if 0 <= new_x < len(grid) and 0 <= new_y < len(grid[new_x]): return grid[new_x][new_y] else: return None else: for step in range(length): - print(step) new_x, new_y = x + step * dx, y + step * dy - # Check for out-of-bounds, considering varying row lengths + # Check for out-of-bounds if 0 <= new_x < len(grid) and 0 <= new_y < len(grid[new_x]): values.append(grid[new_x][new_y]) else: @@ -188,4 +390,3 @@ def get_value_in_direction(grid, position, direction=None,length=1,type:str=None return ''.join(values) else: return values - \ No newline at end of file