Jack of all Trades

Program is capable of using information gathered from a web service to obtain past prices of stocks, calculate some metrics summarizing those prices, and ultimately report on potentially opportune times to buy or sell the stock, based on one of a few automated buying-and-selling strategies.

'''MODULE 1:''' import IndicatorsClasses import readingURL import urllib.request import json def analyzeHeader(symbol: str) -> None: '''accesses the readingURL module to connect a company to the given symbol. Passes this information in a list to get it correctly formatted.''' access = readingURL.read() company, shares = access.readURL(symbol) info = [str(symbol), str(company), str(shares)] print(symbol) print(company) print(shares) table_Header = [['Date', 'Open', 'High', 'Low', 'Close', 'Volume' , 'Indicator', 'Buy?', 'Sell?']] tabFormat(table_Header) def tabFormat(info: list) -> None: '''takes a list and correctly applies tab formatting.''' for eachList in range(len(info)): tempStr = "" for component in info[eachList][:-1]: tempStr += str(component) + "\t" tempStr += str(info[eachList][-1]) print(tempStr) if __name__ == "__main__": symbol = input().upper() while True: try: n = int(input()) if n < 0 or n > 999: print("Invalid trading days. Please try again.") n = int(input()) else: break except ValueError: print("Invalid input. Please submit an integer.") combination = input() if "TR" in combination: analyzeHeader(symbol) callClass = IndicatorsClasses.calculateRange(combination) listDetails = callClass.calculate(symbol, n) tabFormat(listDetails) elif "MP" in combination or "MV" in combination: analyzeHeader(symbol) callClass = IndicatorsClasses.calculateAverage(combination) if "MP" in combination: listDetails = callClass.calculate(symbol, n) else: listDetails = callClass.calculateVolume(symbol, n) tabFormat(listDetails) elif "DP" in combination or "DV" in combination: analyzeHeader(symbol) callClass = IndicatorsClasses.calculateDirectional(combination) if "DP" in combination: listDetails = callClass.calculate(symbol, n) else: listDetails = callClass.calculateVolume(symbol, n) tabFormat(listDetails) else: print("Input Error (Line 3): Must specify a legal indicator and signal strategy on this line") print("Data provided for free by IEX") print("View IEX's Terms of Use") print("https://iextrading.com/api-exhibit-a/") '''MODULE 2:''' import urllib.request import json class read: def readURL(self, symbol: str) -> (str, int): '''looks through the list of available companies participating in the stock exchange by matching their symbol to the uses''' response = urllib.request.urlopen("https://api.iextrading.com/1.0/stock/market/previous") data = response.read() response.close() text = json.loads(data.decode(encoding = 'utf-8')) symbolsList = [] for entity in text: symbolsList.append(entity) if symbol in symbolsList: company = self.readSymbols(symbol) outShares = self.analyzeShares(symbol) return company, outShares else: print("Not a valid symbol.") def readSymbols(self, symbol: str) -> str: '''matches the symbol to a company name''' postOpening = urllib.request.urlopen("https://api.iextrading.com/1.0/ref-data/symbols") valueData = postOpening.read() postOpening.close() matchList = json.loads(valueData.decode(encoding = 'utf-8')) for eachDict in matchList: for key, value in eachDict.items(): if symbol == value: name = eachDict["name"] return name def analyzeShares(self, symbol: str) -> int: '''replaces the URL's query with the search symbol and analyzes the data from this URL''' newURL = "https://api.iextrading.com/1.0/stock/" + symbol + "/stats" openThis = urllib.request.urlopen(newURL) readInfo = openThis.read() openThis.close() compValues = json.loads(readInfo.decode(encoding = 'utf-8')) for component in compValues: outstanding = compValues["sharesOutstanding"] return outstanding def additionalInfo(self, symbol: str, backNDays: int) -> [[]]: '''finds information for the "date, opening price, highest price, lowest price, closing price, and volume''' newURL = "https://api.iextrading.com/1.0/stock/" + symbol + "/chart/6m" response = urllib.request.urlopen(newURL) read = response.read() response.close() data = json.loads(read.decode(encoding = 'utf-8')) infoNestList = [] for component in data[-backNDays:]: date = component["date"] openP = ('%.4f' %(component["open"])) high = ('%.4f' %(component["high"])) low = ('%.4f' %(component["low"])) closeP = ('%.4f' %(component["close"])) volume = ('%d' %(component["volume"])) infoNestList.append([date, openP, high, low, closeP, volume]) return infoNestList '''MODULE 3:''' class true_range: '''after called on from calcRange in "IndicatorsClass" module, iterates through nested list containing TR information, and determines if should buy (TR/ previous closing value> buyThreshold) or should sell (TR/ previous closing value < sellThreshold), or neither.''' def signal(self, nestDetails: [[]], buyThreshold: str, sellThreshold: str, calcN: int) -> [[]]: strategy = [] nestDetails[0].append("") nestDetails[0].append("") buyList = [] sellList = [] for eachDay in range(1, len(nestDetails)): prevClosePrice = nestDetails[eachDay-1][4] trueRange = nestDetails[eachDay][-1] if ">" in buyThreshold: if float(trueRange) <= float(buyThreshold[1:]): nestDetails[eachDay].append("") else: nestDetails[eachDay].append("BUY") else: if float(trueRange) < float(buyThreshold[1:]): nestDetails[eachDay].append("BUY") else: nestDetails[eachDay].append("") if ">" in sellThreshold: if float(trueRange) >= float(sellThreshold[1:]): nestDetails[eachDay].append("SELL") else: nestDetails[eachDay].append("") else: if float(trueRange) < float(sellThreshold[1:]): nestDetails[eachDay].append("SELL") else: nestDetails[eachDay].append("") return nestDetails class simple_moving: '''after called on from calcAvg in "IndicatorsClass" module, iterates through nested list containing SMA information, and determines if should buy (if prev day's closing price < its SMA AND opposite for today) or should sell (if prev day's closing price > its SMA AND opposite for today), or neither.''' def signal(self, nestDetails: [[]], buyThreshold: str, sellThreshold: str, calcN: int) -> [[]]: nestDetails[0].append("") nestDetails[0].append("") for eachDay in range(1,len(nestDetails)): SMA = nestDetails[eachDay][-1] closingPrice = nestDetails[eachDay][4] prevDaySMA = nestDetails[eachDay-1][6] prevDayClosing = nestDetails[eachDay-1][4] if closingPrice > SMA: if prevDayClosing < prevDaySMA: nestDetails[eachDay].append("BUY") else: nestDetails[eachDay].append('') else: nestDetails[eachDay].append('') if closingPrice < SMA: if prevDayClosing > prevDaySMA: nestDetails[eachDay].append("SELL") else: nestDetails[eachDay].append('') else: nestDetails[eachDay].append('') return nestDetails def simple_moving_volume(self, nestDetails: [[]], buyThreshold: str, sellThreshold: str, calcN: int) -> [[]]: '''after called on from calcAvg in "IndicatorsClass" module, iterates through nested list containing SMA information, and determines if should buy (if prev day's closing price < its SMA AND opposite for today) or should sell (if prev day's closing price > its SMA AND opposite for today), or neither.''' nestDetails[0].append("") nestDetails[0].append("") for eachDay in range(1,len(nestDetails)): SMA = nestDetails[eachDay][-1] volume = nestDetails[eachDay][5] prevDaySMA = nestDetails[eachDay-1][-1] prevDayVolume = nestDetails[eachDay-1][5] if volume > SMA: if prevDayVolume < prevDaySMA: nestDetails[eachDay].append("BUY") else: nestDetails[eachDay].append('') else: nestDetails[eachDay].append('') if volume < SMA: if prevDayVolume > prevDaySMA: nestDetails[eachDay].append("SELL") else: nestDetails[eachDay].append('') else: nestDetails[eachDay].append('') return nestDetails class directional: '''after called on from calcDirec in "IndicatorsClass" module, iterates through nested list containing indicator information, and determines if should buy (daily indicator > buy threshold) or should sell (daily indicator < sell threshold)''' def signal(self, nestDetails: [[]], buyThreshold: str, sellThreshold: str, calcN: int) -> [[]]: buyThreshold = float(buyThreshold) sellThreshold = float(sellThreshold) for eachDay in range(len(nestDetails)): todayIndicator = nestDetails[eachDay][6] prevIndicator = nestDetails[eachDay-1][6] if "+" in todayIndicator: todayIndicator = todayIndicator[1:] todayIndicator = float(todayIndicator) if "+" in prevIndicator: prevIndicator = prevIndicator[1:] prevIndicator = float(prevIndicator) if todayIndicator > buyThreshold: if prevIndicator <= buyThreshold: nestDetails[eachDay].append("BUY") else: nestDetails[eachDay].append("") else: nestDetails[eachDay].append("") if todayIndicator < sellThreshold: if prevIndicator >= sellThreshold: nestDetails[eachDay].append("SELL") else: nestDetails[eachDay].append("") else: nestDetails[eachDay].append("") return nestDetails '''MODULE 4:''' import readingURL import StrategiesClasses class calculateRange: def __init__(self, combination) -> [[]]: '''initializes the basic variables used throughout the calculate function.''' split = combination.split() self._indicator = split[0] self._buyThreshold = split[1] self._sellThreshold = split[2] def calculate(self, symbol, N) -> [[]]: '''calculates the indicator value (based on comparing closing prices) and appends to the end of each list in a nested list.''' accessAPI = readingURL.read() nestDetails = accessAPI.additionalInfo(symbol, N) TRList = [""] for eachDay in range(1,N): highest = float(nestDetails[eachDay][2]) lowest = float(nestDetails[eachDay][3]) prevClosing = float(nestDetails[eachDay-1][4]) if prevClosing > highest: highest = prevClosing if prevClosing < lowest: lowest = prevClosing TRange = "%.4f"%(100*((highest - lowest)/prevClosing)) TRList.append(TRange) for each in range(len(nestDetails)): nestDetails[each].append(TRList[each]) calcN = 0 buyOrSell = StrategiesClasses.true_range() nestDetails = buyOrSell.signal(nestDetails, self._buyThreshold, self._sellThreshold, calcN) return nestDetails class calculateAverage: def __init__(self, combination) -> [[]]: '''initializes the basic variables used throughout the calculate function.''' split = combination.split() self._indicator = split[0] self._calcN = int(split[1]) def calculate(self, symbol, N) -> [[]]: '''calculates the indicator value (based on comparing closing prices) and appends to the end of each list in a nested list.''' printN = N calcN = self._calcN accessAPI = readingURL.read() nestDetails = accessAPI.additionalInfo(symbol, printN) SMAList = [] closePriceList = [] for eachDay in range(len(nestDetails)): closePrice = float(nestDetails[eachDay][4]) closePriceList.append(closePrice) if eachDay < calcN-1: nestDetails[eachDay].append("") if eachDay == calcN-1: SMA = '%.4f'%(sum(closePriceList)/calcN) SMAList.append(SMA) elif eachDay > calcN-1: tot = (sum(closePriceList[eachDay-calcN:eachDay])) SMA = "%.4f"%(float(tot)/float(calcN)) SMAList.append(SMA) nestDetails[eachDay-1].append(SMA) if printN >= calcN: lastSMA = "%.4f" %(sum(closePriceList[-calcN:])/calcN) nestDetails[printN-1].append(lastSMA) self._buyThreshold = 0 self._sellThreshold = 0 buyOrSell = StrategiesClasses.simple_moving() nestDetails = buyOrSell.signal(nestDetails, self._buyThreshold, self._sellThreshold, calcN) return nestDetails def calculateVolume(self, symbol, N) -> [[]]: '''calculates the indicator value (based on comparing volumes) and appends to the end of each list in a nested list.''' printN = N calcN = self._calcN accessAPI = readingURL.read() nestDetails = accessAPI.additionalInfo(symbol, printN) SMAList = [] volumeList = [] for eachDay in range(len(nestDetails)): volume = float(nestDetails[eachDay][5]) volumeList.append(volume) if eachDay < calcN-1: nestDetails[eachDay].append("") if eachDay == calcN-1: SMA = '%.4f'%(sum(volumeList)/calcN) SMAList.append(SMA) elif eachDay > calcN-1: total = (sum(volumeList[eachDay-calcN:eachDay])) SMA = "%.4f"%(float(total)/float(calcN)) SMAList.append(SMA) nestDetails[eachDay-1].append(SMA) if printN >= calcN: lastSMA = "%.4f" %(sum(volumeList[-calcN:])/calcN) nestDetails[printN-1].append(lastSMA) self._buyThreshold = 0 self._sellThreshold = 0 buyOrSell = StrategiesClasses.simple_moving() nestDetails = buyOrSell.simple_moving_volume(nestDetails, self._buyThreshold, self._sellThreshold, calcN) return nestDetails class calculateDirectional: def __init__(self, combination): split = combination.split() self._indicator = split[0] self._calcN = int(split[1]) self._buyThreshold = split[2] self._sellThreshold = split[3] def calculate(self, symbol, N) -> [[]]: '''calculates the indicator value (based on comparing closing prices) and appends to the end of each list in a nested list.''' calcN = self._calcN printN = N accessAPI = readingURL.read() nestDetails = accessAPI.additionalInfo(symbol, printN) permList = [] closing = [] for eachDay in range(printN): tempList = [] closing.append(nestDetails[eachDay][4]) for n in range(eachDay-calcN, eachDay+1): if n >= 0: tempList.append(closing[n]) indicator = 0 for loopDay in range(len(tempList)): if loopDay-1 >=0: if float(tempList[loopDay]) > float(tempList[loopDay-1]): indicator+=1 elif float(tempList[loopDay]) < float(tempList[loopDay-1]): indicator-=1 if indicator > 0: sign = "+"+str(indicator) permList.append(sign) elif indicator < 0: permList.append(str(indicator)) else: permList.append("0") for eachDay in range(len(permList)): nestDetails[eachDay].append(permList[eachDay]) calcN = 0 buyOrSell = StrategiesClasses.directional() nestDetails = buyOrSell.signal(nestDetails, self._buyThreshold, self._sellThreshold, calcN) return nestDetails def calculateVolume(self, symbol, N) -> [[]]: '''calculates the indicator value (based on comparing volumes) and appends to the end of each list in a nested list.''' calcN = self._calcN printN = N accessAPI = readingURL.read() nestDetails = accessAPI.additionalInfo(symbol, printN) permList = [] volume = [] for eachDay in range(printN): tempList = [] volume.append(nestDetails[eachDay][5]) for n in range(eachDay-calcN, eachDay+1): if n >= 0: tempList.append(volume[n]) indicator = 0 for loopDay in range(len(tempList)): if loopDay-1 >=0: if float(tempList[loopDay]) > float(tempList[loopDay-1]): indicator+=1 elif float(tempList[loopDay]) < float(tempList[loopDay-1]): indicator-=1 if indicator > 0: sign = "+"+str(indicator) permList.append(sign) elif indicator < 0: permList.append(str(indicator)) else: permList.append("0") for eachDay in range(len(permList)): nestDetails[eachDay].append(permList[eachDay]) calcN = 0 buyOrSell = StrategiesClasses.directional() nestDetails = buyOrSell.signal(nestDetails, self._buyThreshold, self._sellThreshold, calcN) return nestDetails

Send me on my Way

Implement a console-based game (Connect 4) that the user will initially be able to play on their own computer, but will extend so that they can play via the Internet by connecting to a central, shared server.

'''MODULE 1:''' import connectfour import sharedfunctions def play_game() -> None: '''Runs the main game loop in which two players alternate taking turns.''' game_state = connectfour.new_game() while True: sharedfunctions.display_board(game_state) if sharedfunctions.color_turn(game_state) == connectfour.RED: print("It is Red player's turn.") elif sharedfunctions.color_turn(game_state) == connectfour.YELLOW: print("It is Yellow player's turn.") t = sharedfunctions.user_input() column_number = sharedfunctions.choose_column(t[1]) move_type = sharedfunctions.choose_move_type(t[0]) game_state = sharedfunctions.move(game_state,column_number,move_type) winning_player = connectfour.winner(game_state) if winning_player != connectfour.NONE: sharedfunctions.game_over(winning_player) sharedfunctions.display_board(game_state) break def display_game() -> None: ''' Displays the user interface which asks the user if they want to start a game.''' sharedfunctions.welcome_message() while True: choice = input("Would you like to start a new game? (Y/N)\n") if choice.lower() == 'y': play_game() elif choice.lower() == 'n': break else: print("Invalid choice, please enter 'Y' or 'N'") if __name__ == '__main__': display_game() '''MODULE 2:''' import socket import collections Connection = collections.namedtuple("Connection", "socket socket_input socket_output") def read_host() -> str: '''Asks user for valid host name, and if blank, asks again''' while True: host = input('Host: ').strip() if host == '': print('Please specify a host (either a name or an IP address)') else: return host def read_port() -> int: '''Asks user for valid port number, and if blank or not within range, asks again''' while True: try: port = int(input('Port: ').strip()) if port < 0 or port > 65535: print('Ports must be an integer between 0 and 65535') else: return port except ValueError: print('Ports must be an integer between 0 and 65535') def enter_username() -> str: '''Asks user for a username, and if blank, asks again''' while True: username = input("Choose a username. Please avoid whitespaces: ") if " " in username: enter_username() else: return username def connect(host: str, port: int) -> Connection: '''Connects to the server (while running on the inputted host and listening on the given port); if successful, a connection object is returned, and if unsuccessful, an exception is raised''' new_socket = socket.socket() new_socket.connect((host,port)) new_socket_in = new_socket.makefile('r') new_socket_out = new_socket.makefile('w') return Connection(new_socket, new_socket_in, new_socket_out) def close(connection: Connection) -> None: '''Closes a connection''' connection.socket_input.close() connection.socket_output.close() connection.socket.close() def send_message(connection: Connection, message: str) -> None: '''Sends a message to the server via a connection that is already assumed to have been opened (and not yet closed).''' connection.socket_output.write(message + '\r\n') connection.socket_output.flush() def read_message(connection: Connection) -> str: '''Receives a response from the server via a connection that is already assumed to have been opened (and not yet closed).''' return connection.socket_input.readline()[:-1] def start_game(connection: Connection) -> None: '''Sends a message to the server in order to start a new game.''' playermove = "AI_GAME" send_message(connection, playermove) def login(connection: Connection, username: str) -> None: '''Sends a message to the server in order to login with a username.''' send_message(connection, "I32CFSP_HELLO "+username) '''MODULE 3:''' import connectfour class RangeError(Exception): '''Raised whenever an invalid column number is called''' pass class MoveError(Exception): '''Raised whenever a word other than drop or pop is called''' pass def welcome_message() -> None: '''Displays a welcome message to the player(s)''' print("Welcome to Connect Four!") def display_board(game_state: connectfour.GameState) -> str: '''Displays the board through the game state in the console''' x = 1 for num in range(connectfour.BOARD_COLUMNS): print(x, end=' ') x += 1 print() for i in range(connectfour.BOARD_ROWS): for j in range(connectfour.BOARD_COLUMNS): if game_state.board[j][i] == connectfour.NONE: print(". ", end = "") else: if (game_state.board[j][i]) == 1: print("R ", end = "") else: print("Y ", end = "") print() def user_input() -> str: '''Asks the player to choose their move and column number to drop their coin''' while True: move = '' colNum = '' try: user_input = input("Enter if you would like to drop or pop your coin. Then, select a number between 1 and %d for your turn:\n" % (connectfour.BOARD_COLUMNS)) split = user_input.split() move = split[0] colNum = int(split[1]) if (colNum > 0 and colNum <= connectfour.BOARD_COLUMNS) == False: raise RangeError if ((move == 'drop') or (move == 'pop')) == False: raise MoveError return move, colNum except MoveError: print("Invalid input: Not drop or pop") except RangeError: print("Invalid input: Number not within range") pass except IndexError: print("Invalid input: Please enter 'drop' or 'pop' followed by a number") pass except ValueError: print("Invalid input: No number inputted") pass except OSError: print("Invalid input") pass except (move != 'drop') or (move != 'pop'): print("Invalid input: not pop or drop") pass def choose_column(number) -> int: '''Translates user input of a number to a specific column number''' colNum = number return colNum def choose_move_type(move) -> str: '''Determines if the value entered for move (drop or pop) is valid or not if (move == 'drop') or (move == 'pop'):''' return move def color_turn(game_state: connectfour.GameState) -> str: '''Changes players back and forth''' return game_state.turn def move(game_state: connectfour.GameState, column_number: int, move_type: str) -> None: '''Applies a move to the game state.''' try: if move_type == 'drop': return connectfour.drop(game_state,column_number-1) elif move_type == 'pop': return connectfour.pop(game_state,column_number-1) except connectfour.InvalidMoveError: print("Invalid move.") return game_state except connectfour.GameOverError: print("The game is already over!") return game_state def game_over(player: str) -> None: '''Checks if a player has won.''' if player == connectfour.RED: print("Red has won, the game is now over.") elif player == connectfour.YELLOW: print("Yellow has won, the game is now over.") '''MODULE 4:''' # connectfour.py # # ICS 32 Winter 2019 # Project #2: Send Me On My Way ''' This module contains the game logic that underlies a Connect Four game, implementing such functionality as tracking the state of a game, updating that state as players make moves, and determining if there is a winner. No user interface or network functionality is included; this is strictly a collection of tools for implementing the game logic. ''' import collections # These constants specify the concepts of "no player", the "red player" # and the "yellow player". Your code should use these constants in # place of their hard-coded values. Note that these values are not # intended to be displayed to a user; they're simply intended as a # universal way to distinguish which player we're talking about (e.g., # which player's turn it is, or which player (if any) has a piece in a # particular cell on the board). NONE = 0 RED = 1 YELLOW = 2 # These constants specify the size of the game board (i.e., the number # of rows and columns it has). It should be possible to change these # constants and re-run your program so that the board will have a # different size; be sure that you're not hard-coding these values # anywhere in your own code, because this is something we'll be # attempting when we test your program. BOARD_COLUMNS = 7 BOARD_ROWS = 6 # GameState is a namedtuple that tracks everything important about # the state of a Connect Four game as it progresses. It contains # two fields: # # 'board', which is a two-dimensional list of values describing # the game board. Each value represents one cell on the board # and is either NONE, RED, or YELLOW, depending on whether the # cell contains a red piece, a yellow piece, or is empty # # 'turn', which specifies which player will make the next move; # its value will always be either RED or YELLOW GameState = collections.namedtuple('GameState', ['board', 'turn']) # This is the simplest example of how you create new kinds of exceptions # that are specific to your own program. We'll see later in the quarter # in more detail what this notation means; in short, we're introducing # new types into our program and stating that they have strong similarities # to the existing type called Exception. class InvalidMoveError(Exception): '''Raised whenever an invalid move is made''' pass class GameOverError(Exception): ''' Raised whenever an attempt is made to make a move after the game is already over ''' pass # The next four functions are the "public" functions that are provided # by this module. You'll need to call all four of these functions, but # will not need (or want) to call any of the others. def new_game() -> GameState: ''' Returns a GameState representing a brand new game in which no moves have been made yet. ''' return GameState(board = _new_game_board(), turn = RED) def drop(game_state: GameState, column_number: int) -> GameState: ''' Given a game state and a column number, returns the game state that results when the current player (whose turn it is) drops a piece into the given column. If the column number is invalid, a ValueError is raised. If the game is over, a GameOverError is raised. If a move cannot be made in the given column because the column is filled already, an InvalidMoveError is raised. ''' _require_valid_column_number(column_number) _require_game_not_over(game_state) empty_row = _find_bottom_empty_row_in_column(game_state.board, column_number) if empty_row == -1: raise InvalidMoveError() else: new_board = _copy_game_board(game_state.board) new_board[column_number][empty_row] = game_state.turn new_turn = _opposite_turn(game_state.turn) return GameState(board = new_board, turn = new_turn) def pop(game_state: GameState, column_number: int) -> GameState: ''' Given a game state and a column number, returns the game state that results when the current player (whose turn it is) pops a piece from the bottom of the given column. If the column number is invalid, a ValueError is raised. If the game is over, a GameOverError is raised. If a piece cannot be popped from the bottom of the given column because the column is empty or because the piece at the bottom of the column belongs to the other player, an InvalidMoveError is raised. ''' _require_valid_column_number(column_number) _require_game_not_over(game_state) if game_state.turn == game_state.board[column_number][BOARD_ROWS - 1]: new_board = _copy_game_board(game_state.board) for row in range(BOARD_ROWS - 1, -1, -1): new_board[column_number][row] = new_board[column_number][row - 1] new_board[column_number][row] = NONE new_turn = _opposite_turn(game_state.turn) return GameState(board = new_board, turn = new_turn) else: raise InvalidMoveError() def winner(game_state: GameState) -> int: ''' Determines the winning player in the given game state, if any. If the red player has won, RED is returned; if the yellow player has won, YELLOW is returned; if no player has won yet, NONE is returned. ''' winner = NONE for col in range(BOARD_COLUMNS): for row in range(BOARD_ROWS): if _winning_sequence_begins_at(game_state.board, col, row): if winner == NONE: winner = game_state.board[col][row] elif winner != game_state.board[col][row]: # This handles the rare case of popping a piece # causing both players to have four in a row; # in that case, the last player to make a move # is the winner. return _opposite_turn(game_state.turn) return winner # Modules often contain functions, variables, or classes whose names begin # with underscores. This is no accident; in Python, this is the agreed-upon # standard for specifying functions, variables, or classes that should be # treated as "private" or "hidden". The rest of your program should not # need to call any of these functions directly; they are utility functions # called by the functions above, so that those functions can be shorter, # simpler, and more readable. def _new_game_board() -> [[int]]: ''' Creates a new game board. Initially, a game board has the size BOARD_COLUMNS x BOARD_ROWS and is comprised only of integers with the value NONE ''' board = [] for col in range(BOARD_COLUMNS): board.append([]) for row in range(BOARD_ROWS): board[-1].append(NONE) return board def _copy_game_board(board: [[int]]) -> [[int]]: '''Copies the given game board''' board_copy = [] for col in range(BOARD_COLUMNS): board_copy.append([]) for row in range(BOARD_ROWS): board_copy[-1].append(board[col][row]) return board_copy def _find_bottom_empty_row_in_column(board: [[int]], column_number: int) -> int: ''' Determines the bottommost empty row within a given column, useful when dropping a piece; if the entire column in filled with pieces, this function returns -1 ''' for i in range(BOARD_ROWS - 1, -1, -1): if board[column_number][i] == NONE: return i return -1 def _opposite_turn(turn: str) -> str: '''Given the player whose turn it is now, returns the opposite player''' if turn == RED: return YELLOW else: return RED def _winning_sequence_begins_at(board: [[int]], col: int, row: int) -> bool: ''' Returns True if a winning sequence of pieces appears on the board beginning in the given column and row and extending in any of the eight possible directions; returns False otherwise ''' return _four_in_a_row(board, col, row, 0, 1) \ or _four_in_a_row(board, col, row, 1, 1) \ or _four_in_a_row(board, col, row, 1, 0) \ or _four_in_a_row(board, col, row, 1, -1) \ or _four_in_a_row(board, col, row, 0, -1) \ or _four_in_a_row(board, col, row, -1, -1) \ or _four_in_a_row(board, col, row, -1, 0) \ or _four_in_a_row(board, col, row, -1, 1) def _four_in_a_row(board: [[int]], col: int, row: int, coldelta: int, rowdelta: int) -> bool: ''' Returns True if a winning sequence of pieces appears on the board beginning in the given column and row and extending in a direction specified by the coldelta and rowdelta ''' start_cell = board[col][row] if start_cell == NONE: return False else: for i in range(1, 4): if not _is_valid_column_number(col + coldelta * i) \ or not _is_valid_row_number(row + rowdelta * i) \ or board[col + coldelta *i][row + rowdelta * i] != start_cell: return False return True def _require_valid_column_number(column_number: int) -> None: '''Raises a ValueError if its parameter is not a valid column number''' if type(column_number) != int or not _is_valid_column_number(column_number): raise ValueError('column_number must be int between 0 and {}'.format(BOARD_COLUMNS - 1)) def _require_game_not_over(game_state: GameState) -> None: ''' Raises a GameOverError if the given game state represents a situation where the game is over (i.e., there is a winning player) ''' if winner(game_state) != NONE: raise GameOverError() def _is_valid_column_number(column_number: int) -> bool: '''Returns True if the given column number is valid; returns False otherwise''' return 0 <= column_number < BOARD_COLUMNS def _is_valid_row_number(row_number: int) -> bool: '''Returns True if the given row number is valid; returns False otherwise''' return 0 <= row_number < BOARD_ROWS

Digging in the Dirt

Find and display the paths of all of the files in a directory (and, potentially, all of its subdirectories, and their subdirectories, and so on), and then take action (such as reading, modifying, calculating their bytes, duplicating, touching, etc.) on some of the files that have interesting characteristics.

import os import shutil from pathlib import Path def subdirectoryCheck(subdirectory: Path, listPaths: [Path]) -> Path: '''Checks and returns the list of files inside a subdirectory and repeats if necessary.''' for subPath in sorted(Path(subdirectory).()): if subPath.is_file() == True: listPaths.append(subPath) for subelement in subdirectory.iterdir(): if subelement.is_dir() == True: subdirectoryCheck(subelement, listPaths) return listPaths def userInputSpecifier(userInput: str) -> (str, str): '''Splits the user's input if multi-part and returns the user's input in an organized manner.''' checkLetter = '' searchStatement = '' while True: if userInput == "A" or userInput == "F" or userInput == "D" or userInput == "T": checkLetter = userInput[0] searchStatement = '' return checkLetter, searchStatement elif " " in userInput: splitInput = userInput.split(" ", 1) checkLetter = splitInput[0] searchStatement = splitInput[-1] if checkLetter == "N" or checkLetter == "E" or checkLetter == "T" or checkLetter == "<" or checkLetter == ">": if searchStatement != "": return checkLetter, searchStatement else: print("ERROR") userInput = input() continue else: print("ERROR") userInput = input() continue else: print("ERROR") userInput = input() continue def letterA(listPaths: [Path], interesting: [], checkStatement: bool, missingCheck: bool) -> (bool, [Path], bool): '''Adds all the files to the 'interesting' list if user's input is just 'A'. ''' if listPaths != []: for element in listPaths: print(element) interesting.append(element) missingCheck = True checkStatement = False return checkStatement, interesting, missingCheck def letterN(searchStatement: str, checkStatement: bool, listPaths: [Path], interesting: [], missingCheck: bool) -> (bool, [Path], bool): '''Splits the path to see if matches part of user's input. If so, considered interesting.''' if listPaths != []: for element in listPaths: splitPath = os.path.split(element) if searchStatement == splitPath[-1]: print(element) interesting.append(element) missingCheck = True checkStatement = False return checkStatement, interesting, missingCheck def letterE(searchStatement: str, checkStatement: bool, listPaths: [Path], interesting: [], missingCheck: bool) -> (bool, [Path], bool): '''split each individual line to find their extension. see if extension matches searchStatement. If so, considered interesting.''' if listPaths != []: for element in listPaths: pathSuffix = element.suffix if searchStatement[0] != ".": searchStatement = "." + searchStatement if searchStatement == pathSuffix and element.exists(): print(element) interesting.append(element) missingCheck = True checkStatement = False return checkStatement, interesting, missingCheck def letterT(searchStatement: str, checkStatement: bool, listPaths: [Path], interesting: [], missingCheck: bool) -> (bool, [Path], bool): '''if opened file's readlines contains the specified phrase, is considered interesting.''' for element in listPaths: openFile = None try: openFile = open(element, 'r') for eachLine in openFile.readlines(): if searchStatement in eachLine: print(element) interesting.append(element) missingCheck = True break except ValueError or PermissionError: pass finally: if openFile != None: openFile.close() checkStatement = False return checkStatement, interesting, missingCheck def lessBytes(searchStatement: str, checkStatement: bool, listPaths: [Path], interesting: [], missingCheck: bool) -> (bool, [Path], bool): '''checks the amount of bytes the file contains and compares to the amount of bytes user specifies. If less than, considered interesting.''' try: if "," in searchStatement: searchStatement = searchStatement.replace(",", "") threshold = int(searchStatement) if type(threshold) == int and threshold >= 0: if listPaths != []: for element in listPaths: if element.is_file() == True: elementInfo = os.stat(element) fileSize = elementInfo.st_size if fileSize < threshold: print(element) interesting.append(element) missingCheck = True except ValueError: print("ERROR") userInput = input() checkLetter, searchStatement = userInputSpecifier(userInput) checkStatement, interesting, missingCheck = lessBytes(searchStatement, checkStatement, listPaths, interesting, missingCheck) finally: checkStatement = False return checkStatement, interesting, missingCheck def greaterBytes(searchStatement: str, checkStatement: bool, listPaths: [Path], interesting: [], missingCheck: bool) -> (bool, [Path], bool): '''checks the amount of bytes the file contains and compares to the amount of bytes user specifies. If greater than, considered interesting.''' try: if "," in searchStatement: searchStatement = searchStatement.replace(",", "") threshold = int(searchStatement) if type(threshold) == int and threshold >= 0: if listPaths != []: for element in listPaths: if element.is_file() == True: elementInfo = os.stat(element) fileSize = elementInfo.st_size if fileSize > threshold: print(element) interesting.append(element) missingCheck = True except ValueError: print("ERROR") userInput = input() checkLetter, searchStatement = userInputSpecifier(userInput) checkStatement, interesting, missingCheck = lessBytes(searchStatement, checkStatement, listPaths, interesting, missingCheck) finally: checkStatement = False return checkStatement, interesting, missingCheck def interestingF(interesting: [Path], loopControl: bool) -> (bool): '''If this line of input contains the letter F by itself, print the first line of text from the file if it's a text file; print NOT TEXT if it is not.''' for element in interesting: textCheck = None try: textCheck = open(element, 'r') read_the_file = textCheck.readline() if '\n' in read_the_file: print(read_the_file, end='') else: print(read_the_file) loopControl = False except: print('NOT TEXT') loopControl = True finally: if textCheck != None: textCheck.close() loopControl = False return loopControl def interestingD(interesting: [Path], loopControl: bool) -> (bool): '''Duplicates the file specified, stores in same directory as found, but renames the extension.''' for element in interesting: dupTitle = str(element) + '.dup' duplicate = shutil.copy(element, dupTitle) loopControl = False return loopControl def interestingT(interesting: [Path], loopControl: bool) -> (bool): '''Touches the file - modifies its last timestamp to be the current date/time.''' for element in interesting: element.touch() loopControl = False return loopControl def fileOpen(): '''Calls functions based on the user's input''' listPaths = [] interesting = [] while True: file_path = input() if file_path != '' and len(file_path) > 2: splitStatement = file_path.split(' ') if splitStatement[1] != '': location = Path(splitStatement[1]) if location.exists(): beginningLetter = splitStatement[0] if beginningLetter != 'D' and beginningLetter != 'R': print('ERROR') else: break else: print('ERROR') else: print('ERROR') else: print('ERROR') if beginningLetter == 'D': for element in location.iterdir(): if element.is_file(): listPaths.append(element) for file in listPaths: print(file) elif beginningLetter == 'R': listPaths = subdirectoryCheck(location, listPaths) sortedList = [] for element in listPaths: if element not in sortedList: sortedList.append(element) print(element) userInput = input() checkLetter, searchStatement = userInputSpecifier(userInput) checkStatement = True '''False means break while loop''' missingCheck = False while checkStatement == True: if checkLetter == "A": checkStatement, interesting, missingCheck = letterA(listPaths, interesting, checkStatement, missingCheck) elif checkLetter == "N": checkStatement, interesting, missingCheck = letterN(searchStatement, checkStatement, listPaths, interesting, missingCheck) elif checkLetter == "E": checkStatement, interesting, missingCheck = letterE(searchStatement, checkStatement, listPaths, interesting, missingCheck) elif checkLetter == "T": checkStatement, interesting, missingCheck = letterT(searchStatement, checkStatement, listPaths, interesting, missingCheck) elif checkLetter == "<": checkStatement, interesting, missingCheck = lessBytes(searchStatement, checkStatement, listPaths, interesting, missingCheck) elif checkLetter == ">": checkStatement, interesting, missingCheck = greaterBytes(searchStatement, checkStatement, listPaths, interesting,missingCheck ) else: print("ERROR") userInput = input() userInputSpecifier(userInput) if missingCheck: loopControl = True while loopControl == True: checkLetter = input() if checkLetter == 'F': loopControl = interestingF(interesting, loopControl) elif checkLetter == 'D': loopControl = interestingD(interesting, loopControl) elif checkLetter == 'T': loopControl = interestingT(interesting, loopControl) else: print("ERROR") if __name__ == "__main__": fileOpen()
1