From 635eb650e8f86523c0cc9b15418e670bd6f98452 Mon Sep 17 00:00:00 2001 From: David Benepe Date: Fri, 4 Jun 2021 00:27:41 -0500 Subject: [PATCH] Updated score script with game status. --- tools/python/score.py | 55 +++-- tools/python/score_display.py | 157 +++++++++++++++ tools/python/score_progress.json | 335 +++++++++++++++++++++++++++++++ 3 files changed, 517 insertions(+), 30 deletions(-) create mode 100644 tools/python/score_display.py create mode 100644 tools/python/score_progress.json diff --git a/tools/python/score.py b/tools/python/score.py index 61e7053e..b9b52ac2 100644 --- a/tools/python/score.py +++ b/tools/python/score.py @@ -1,6 +1,8 @@ import re import sys +import argparse from file_util import FileUtil +from score_display import ScoreDisplay ASM_FOLDERS = [ './asm/unknown_0251F0', @@ -13,7 +15,7 @@ ASM_FOLDERS = [ ] # These will automatically be added to the adventure one percentage. -ASM_FUNCTIONS = [ 'entrypoint' ] +ASM_LABELS = [ 'entrypoint' ] for folder in ASM_FOLDERS: GLABEL_REGEX = r'glabel ([0-9A-Za-z_]+)' filenames = FileUtil.get_filenames_from_directory(folder, extensions=('.s',)) @@ -22,12 +24,12 @@ for folder in ASM_FOLDERS: text = asmFile.read() matches = re.finditer(GLABEL_REGEX, text, re.MULTILINE) for matchNum, match in enumerate(matches, start=1): - ASM_FUNCTIONS.append(match.groups()[0]) + ASM_LABELS.append(match.groups()[0]) BUILD_DIRECTORY = './build/us_1.0' SRC_DIRECTORY = './src' LIB_SRC_DIRECTORY = './lib/src' -FUNCTION_REGEX = r'([/][*][*]([^*]|([*][^/]))*[*][/][\n]\s*)?(void|s64|s32|s16|s8|u64|u32|u16|u8|f32|f64)(\s|[*])*([0-9A-Za-z_]+)\s*[(][^)]*[)]\s*{' +FUNCTION_REGEX = r'((?:[\/][*][*]+[\n])(?:[^\n]*\n)*?(?:.*[*][\/]))?(void|s64|s32|s16|s8|u64|u32|u16|u8|f32|f64)(?:\s|[*])*([0-9A-Za-z_]+)\s*[(][^)]*[)]\s*{' GLOBAL_ASM_REGEX = r'GLOBAL_ASM[(]".*(?=\/)\/([^.]+).s"[)]' WIP_REGEX = r'#if(.|\n)*?(GLOBAL_ASM[(]([^)]*)[)])(.|\n)*?#else(.|\n)*?#endif' @@ -87,7 +89,7 @@ class ScoreFile: matches = re.finditer(FUNCTION_REGEX, self.text, re.MULTILINE) for matchNum, match in enumerate(matches, start=1): groups = match.groups() - self.functions.append(ScoreFileMatch(groups[0], groups[5])) + self.functions.append(ScoreFileMatch(groups[0], groups[2])) matches = re.finditer(GLOBAL_ASM_REGEX, self.text, re.MULTILINE) for matchNum, match in enumerate(matches, start=1): @@ -120,17 +122,16 @@ class ScoreFile: def main(): showTopFiles = 0 - - numArgs = len(sys.argv) - for i in range(1, numArgs): - arg = sys.argv[i].lower() - print('Argument:', sys.argv[i]) - if arg.startswith("--top"): - num = arg[5:] - if num == "": - showTopFiles = 10 # 10 is the default - else: - showTopFiles = int(num) + + parser = argparse.ArgumentParser(description="") + parser.add_argument("-t", "--top", help="(Optional) Shows the top N files remaining.") + parser.add_argument("-a", "--adventure", help="(Optional) Only shows adventure 1 or 2 based on passed in value.", choices=['1', '2']) + args = parser.parse_args() + adventureSelect = 3 # Show both adventures by default + if args.adventure != None: + adventureSelect = int(args.adventure) + if args.top != None: + showTopFiles = int(args.top) scoreFiles = [] totalNumberOfDecompiledFunctions = 0 @@ -158,25 +159,17 @@ def main(): totalSizeOfDocumentedFunctions += scoreFile.get_size_of_documented_functions() scoreFiles.append(scoreFile) - totalNumberOfFunctions = MAP_FILE.numFunctions - for asm_function in ASM_FUNCTIONS: - totalSizeOfDecompiledFunctions += MAP_FILE.functionSizes[asm_function] + totalNumberOfFunctions = MAP_FILE.numFunctions + for asm_function in ASM_LABELS: + if asm_function in MAP_FILE.functionSizes: + totalSizeOfDecompiledFunctions += MAP_FILE.functionSizes[asm_function] adventureOnePercentage = (totalSizeOfDecompiledFunctions / CODE_SIZE) * 100 adventureTwoPercentage = (totalSizeOfDocumentedFunctions / CODE_SIZE) * 100 - print('=========================================') - print(' ADVENTURE ONE (ASM -> C Decompilation)') - print(' ----------- {:5.2f}% Complete -----------'.format(adventureOnePercentage)) - print(' # Decompiled functions: ' + str(totalNumberOfDecompiledFunctions)) - print(' # GLOBAL_ASM remaining: ' + str(totalNumberOfGlobalAsms)) - print('=========================================') - print(' ADVENTURE TWO (Cleanup & Documentation)') - print(' ----------- {:5.2f}% Complete -----------'.format(adventureTwoPercentage)) - print(' # Documented functions: ' + str(totalNumberOfDocumentedFunctions)) - print(' # Undocumented remaining: ' + str(totalNumberOfFunctions - totalNumberOfDocumentedFunctions)) - print('=========================================') + scoreDisplay = ScoreDisplay() + print(scoreDisplay.getDisplay(adventureOnePercentage, adventureTwoPercentage, adventureSelect, totalNumberOfDecompiledFunctions, totalNumberOfGlobalAsms, totalNumberOfDocumentedFunctions, totalNumberOfFunctions - totalNumberOfDocumentedFunctions)) if showTopFiles > 0: if showTopFiles > len(scoreFiles): @@ -189,7 +182,9 @@ def main(): for i in range(0, showTopFiles): percentageRemaining = (files[i][1] / CODE_SIZE) * 100 percentageDone = (files[i][2] / CODE_SIZE) * 100 - funcName = files[i][0][12:] + funcName = files[i][0] + if '/' in funcName: + funcName = funcName[funcName.rindex('/') + 1:] print("", funcName, (" " * (24 - len(funcName))), "| {:5.2f}% | {:5.2f}% |".format(percentageRemaining, percentageDone)) diff --git a/tools/python/score_display.py b/tools/python/score_display.py new file mode 100644 index 00000000..5b3a35ba --- /dev/null +++ b/tools/python/score_display.py @@ -0,0 +1,157 @@ +import json +import argparse + +def readScoreDisplayJson(filename='./tools/python/score_progress.json'): + with open(filename, 'r') as inFile: + return json.loads(inFile.read()) + +TOTAL_NUMBER_OF_BALLOONS = 47 +TOTAL_NUMBER_OF_KEYS = 4 +TOTAL_NUMBER_OF_TT_AMULETS = 4 +TOTAL_NUMBER_OF_WIZPIG_AMULETS = 4 +TOTAL_NUMBER_OF_TROPHIES = 5 + +DEFAULT_MAX_LENGTH = 42 + +class ScoreDisplay: + def __init__(self): + self.progressNodes = readScoreDisplayJson() + + def getStatus(self, percentage): + if percentage >= 100.0: + return { + "Balloon": TOTAL_NUMBER_OF_BALLOONS, + "Key": TOTAL_NUMBER_OF_KEYS, + "TTAmulet": TOTAL_NUMBER_OF_TT_AMULETS, + "WizpigAmulet": TOTAL_NUMBER_OF_WIZPIG_AMULETS, + "Trophy": TOTAL_NUMBER_OF_TROPHIES, + "Msg": "COMPLETED!" + } + out = { + "Balloon": 0, + "Key": 0, + "TTAmulet": 0, + "WizpigAmulet": 0, + "Trophy": 0, + "Msg": 'Not Started' + } + if percentage <= 0.0: + return out + numberOfCompletedNodes = int((percentage / 100.0) * len(self.progressNodes)) + currentNodeProgress = ((percentage / 100.0) * len(self.progressNodes)) - numberOfCompletedNodes + for i in range(0, numberOfCompletedNodes): + rewards = self.progressNodes[i]["rewards"] + for reward in rewards: + out[reward] += rewards[reward] + out['Msg'] = self.progressNodes[numberOfCompletedNodes]['msg'] + nodeType = self.progressNodes[numberOfCompletedNodes]['type'] + #if nodeType != 'Task': + # out['Msg'] += '\n' + if nodeType == 'Race': + out['Msg'] += ' (Lap ' + str(int(currentNodeProgress*3)+1) + '/3)' + elif nodeType == 'Collecting': + collectingName = self.progressNodes[numberOfCompletedNodes]['collecting']['name'] + collectingMax = self.progressNodes[numberOfCompletedNodes]['collecting']['max'] + out['Msg'] += ' (' + str(int(currentNodeProgress * collectingMax)) + '/' + str(collectingMax) + ' ' + collectingName + 's)' + elif nodeType == 'SilverCoinsRace': + out['Msg'] += ' (' + str(int(currentNodeProgress*9)) + '/8 silver coins)' + elif nodeType == 'TrophyRace': + if currentNodeProgress < 0.25: + out['Msg'] += ' (Round One)' + elif currentNodeProgress < 0.50: + out['Msg'] += ' (Round Two)' + elif currentNodeProgress < 0.75: + out['Msg'] += ' (Round Three)' + elif currentNodeProgress < 1.00: + out['Msg'] += ' (Round Four)' + elif nodeType == 'Battle': + if currentNodeProgress < 0.34: + out['Msg'] += ' (3 opponents remain)' + elif currentNodeProgress < 0.67: + out['Msg'] += ' (2 opponents remain)' + elif currentNodeProgress < 1.00: + out['Msg'] += ' (1 opponent remains)' + return out + + def makeLine(self, char, length, title=None): + if title == None: + return ' ' + (char * length) + ' \n' + else: + lineSideLength = (length - len(title) - 2) // 2 + leftLength = lineSideLength + rightLength = lineSideLength + if (leftLength + rightLength + len(title) + 2) < length: + rightLength += 1 + if leftLength + rightLength <= 0: + if leftLength + rightLength == 0: + return ' ' + title + ' \n' + else: + return ' ' + title + ' \n' + else: + return ' ' + (char * leftLength) + ' ' + title + ' ' + (char * rightLength) + ' \n' + + def getGameStatusDisplay(self, status, dashLen): + out = '' + out += self.makeLine('-', dashLen, 'Game Status') + firstStatusLine = '' + secondStatusLine = '' + firstStatusLine += 'Balloons: {:}/{:},'.format(status['Balloon'], TOTAL_NUMBER_OF_BALLOONS) + firstStatusLine += ' Keys: {:}/{:},'.format(status['Key'], TOTAL_NUMBER_OF_KEYS) + firstStatusLine += ' Trophies: {:}/{:}'.format(status['Trophy'], TOTAL_NUMBER_OF_TROPHIES) + secondStatusLine += 'T.T. Amulets: {:}/{:},'.format(status['TTAmulet'], TOTAL_NUMBER_OF_TT_AMULETS) + secondStatusLine += ' Wizpig Amulets: {:}/{:}'.format(status['WizpigAmulet'], TOTAL_NUMBER_OF_WIZPIG_AMULETS) + out += self.makeLine(' ', dashLen, firstStatusLine) + out += self.makeLine(' ', dashLen, secondStatusLine) + out += self.makeLine('-', dashLen) + out += self.makeLine(' ', dashLen, status['Msg']) + return [out, dashLen] + + def getDisplay(self, advOnePer, advTwoPer, showFlags=3, totalDecompFunctions=0, totalGlobalAsm=0, totalDocumented=0, totalUndocumented=0): + advOneStatus = self.getStatus(advOnePer) + advTwoStatus = self.getStatus(advTwoPer) + if showFlags == 3: + dashLen = max(len(advOneStatus['Msg']), len(advTwoStatus['Msg']), DEFAULT_MAX_LENGTH) + elif showFlags == 1: + dashLen = max(len(advOneStatus['Msg']), DEFAULT_MAX_LENGTH) + elif showFlags == 2: + dashLen = max(len(advTwoStatus['Msg']), DEFAULT_MAX_LENGTH) + else: + dashLen = DEFAULT_MAX_LENGTH + advOneGameStatusDisplay = self.getGameStatusDisplay(advOneStatus, dashLen) + advTwoGameStatusDisplay = self.getGameStatusDisplay(advTwoStatus, dashLen) + out = '' + if showFlags & 1: + out += self.makeLine('=', dashLen) + out += self.makeLine(' ', dashLen, 'ADVENTURE ONE (ASM -> C Decompilation)') + if advOnePer >= 100.0: + out += self.makeLine('-', dashLen, '{:5.1f}% Complete'.format(advOnePer)) + else: + out += self.makeLine('-', dashLen, '{:5.2f}% Complete'.format(advOnePer)) + out += self.makeLine(' ', dashLen, '# Decompiled functions: ' + str(totalDecompFunctions)) + out += self.makeLine(' ', dashLen, '# GLOBAL_ASM remaining: ' + str(totalGlobalAsm)) + out += advOneGameStatusDisplay[0] + if showFlags & 2: + out += self.makeLine('=', dashLen) + out += self.makeLine(' ', dashLen, ' ADVENTURE TWO (Cleanup & Documentation)') + if advTwoPer >= 100.0: + out += self.makeLine('-', dashLen, '{:5.1f}% Complete'.format(advTwoPer)) + else: + out += self.makeLine('-', dashLen, '{:5.2f}% Complete'.format(advTwoPer)) + out += self.makeLine(' ', dashLen, '# Documented functions: ' + str(totalDocumented)) + out += self.makeLine(' ', dashLen, '# Undocumented remaining: ' + str(totalUndocumented)) + out += advTwoGameStatusDisplay[0] + out += self.makeLine('=', dashLen)[:-1] + return out + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="") + parser.add_argument("adventureOnePercentage", help="Value within [0.0, 100.0] that describes how complete the decomp is.") + parser.add_argument("adventureTwoPercentage", help="Value within [0.0, 100.0] that describes how complete the documentation is.") + parser.add_argument("-a", "--adventure", help="(Optional) Only shows adventure 1 or 2 based on passed in value.", choices=['1', '2']) + args = parser.parse_args() + adventureSelect = 3 # Show both adventures by default + if args.adventure != None: + adventureSelect = int(args.adventure) + scoreDisplay = ScoreDisplay() + print(scoreDisplay.getDisplay(float(args.adventureOnePercentage), float(args.adventureTwoPercentage), adventureSelect)) + \ No newline at end of file diff --git a/tools/python/score_progress.json b/tools/python/score_progress.json new file mode 100644 index 00000000..314d61cf --- /dev/null +++ b/tools/python/score_progress.json @@ -0,0 +1,335 @@ +[ + { + "msg": "We are collecting the first balloon on Timber's Island.", + "rewards": { "Balloon": 1 }, + "type": "Task" + }, + { + "msg": "We are collecting the second balloon on Timber's Island.", + "rewards": { "Balloon": 1 }, + "type": "Task" + }, + { + "msg": "We are collecting the third balloon on Timber's Island.", + "rewards": { "Balloon": 1 }, + "type": "Task" + }, + { + "msg": "We are collecting the fourth balloon on Timber's Island.", + "rewards": { "Balloon": 1 }, + "type": "Task" + }, + { + "msg": "We are racing in Ancient Lake.", + "rewards": { "Balloon": 1, "Key": 1 }, + "type": "Race" + }, + { + "msg": "We are racing in Fossil Canyon.", + "rewards": { "Balloon": 1 }, + "type": "Race" + }, + { + "msg": "We are racing in Jungle Falls.", + "rewards": { "Balloon": 1 }, + "type": "Race" + }, + { + "msg": "We are racing in Hot Top Volcano.", + "rewards": { "Balloon": 1 }, + "type": "Race" + }, + { + "msg": "We are racing the dinosaur boss Trickytops", + "rewards": {}, + "type": "Task" + }, + { + "msg": "We are collecting eggs in Fire Mountain.", + "rewards": { "TTAmulet": 1 }, + "type": "Collecting", + "collecting": { "name": "Egg", "max": 3 } + }, + { + "msg": "We are collecting silver coins in Ancient Lake.", + "rewards": { "Balloon": 1 }, + "type": "SilverCoinsRace" + }, + { + "msg": "We are collecting silver coins in Fossil Canyon.", + "rewards": { "Balloon": 1 }, + "type": "SilverCoinsRace" + }, + { + "msg": "We are collecting silver coins in Jungle Falls.", + "rewards": { "Balloon": 1 }, + "type": "SilverCoinsRace" + }, + { + "msg": "We are collecting silver coins in Hot Top Volcano.", + "rewards": { "Balloon": 1 }, + "type": "SilverCoinsRace" + }, + { + "msg": "We are racing in the rematch against Trickytops.", + "rewards": { "WizpigAmulet": 1 }, + "type": "Task" + }, + { + "msg": "We are participating in the Trophy Race of Dino Domain.", + "rewards": { "Trophy": 1 }, + "type": "TrophyRace" + }, + { + "msg": "We are racing Taj in the Car Challenge.", + "rewards": { "Balloon": 1 }, + "type": "Race" + }, + { + "msg": "We are racing in Everfrost Peak.", + "rewards": { "Balloon": 1 }, + "type": "Race" + }, + { + "msg": "We are racing in Walrus Cove.", + "rewards": { "Balloon": 1 }, + "type": "Race" + }, + { + "msg": "We are racing in Snowball Valley.", + "rewards": { "Balloon": 1, "Key": 1 }, + "type": "Race" + }, + { + "msg": "We are racing in Frosty Village.", + "rewards": { "Balloon": 1 }, + "type": "Race" + }, + { + "msg": "We are racing the walrus boss Bluey.", + "rewards": {}, + "type": "Task" + }, + { + "msg": "We are battling in Icicle Pyramid.", + "rewards": { "TTAmulet": 1 }, + "type": "Battle" + }, + { + "msg": "We are collecting silver coins in Everfrost Peak.", + "rewards": { "Balloon": 1 }, + "type": "SilverCoinsRace" + }, + { + "msg": "We are collecting silver coins in Walrus Cove.", + "rewards": { "Balloon": 1 }, + "type": "SilverCoinsRace" + }, + { + "msg": "We are collecting silver coins in Snowball Valley.", + "rewards": { "Balloon": 1 }, + "type": "SilverCoinsRace" + }, + { + "msg": "We are collecting silver coins in Frosty Village.", + "rewards": { "Balloon": 1 }, + "type": "SilverCoinsRace" + }, + { + "msg": "We are racing in the rematch against Bluey.", + "rewards": { "WizpigAmulet": 1 }, + "type": "Task" + }, + { + "msg": "We are participating in the Trophy Race of Snowflake Mountain.", + "rewards": { "Trophy": 1 }, + "type": "TrophyRace" + }, + { + "msg": "We are racing Taj in the Hover Challenge.", + "rewards": { "Balloon": 1 }, + "type": "Race" + }, + { + "msg": "We are racing in Whale Bay.", + "rewards": { "Balloon": 1 }, + "type": "Race" + }, + { + "msg": "We are racing in Crescent Island.", + "rewards": { "Balloon": 1, "Key": 1 }, + "type": "Race" + }, + { + "msg": "We are racing in Pirate Lagoon.", + "rewards": { "Balloon": 1 }, + "type": "Race" + }, + { + "msg": "We are racing in Treasure Caves.", + "rewards": { "Balloon": 1 }, + "type": "Race" + }, + { + "msg": "We are racing the octopus boss Bubbler.", + "rewards": {}, + "type": "Race" + }, + { + "msg": "We are battling in Darkwater Beach.", + "rewards": { "TTAmulet": 1 }, + "type": "Battle" + }, + { + "msg": "We are collecting silver coins in Whale Bay.", + "rewards": { "Balloon": 1 }, + "type": "SilverCoinsRace" + }, + { + "msg": "We are collecting silver coins in Crescent Island.", + "rewards": { "Balloon": 1 }, + "type": "SilverCoinsRace" + }, + { + "msg": "We are collecting silver coins in Pirate Lagoon.", + "rewards": { "Balloon": 1 }, + "type": "SilverCoinsRace" + }, + { + "msg": "We are collecting silver coins in Treasure Caves.", + "rewards": { "Balloon": 1 }, + "type": "SilverCoinsRace" + }, + { + "msg": "We are racing in the rematch against Bubbler.", + "rewards": { "WizpigAmulet": 1 }, + "type": "Race" + }, + { + "msg": "We are participating in the Trophy Race of Sherbet Island.", + "rewards": { "Trophy": 1 }, + "type": "TrophyRace" + }, + { + "msg": "We are racing Taj in the Plane Challenge.", + "rewards": { "Balloon": 1 }, + "type": "Race" + }, + { + "msg": "We are racing in Windmill Plains.", + "rewards": { "Balloon": 1 }, + "type": "Race" + }, + { + "msg": "We are racing in Greenwood Village.", + "rewards": { "Balloon": 1 }, + "type": "Race" + }, + { + "msg": "We are racing in Boulder Canyon.", + "rewards": { "Balloon": 1, "Key": 1 }, + "type": "Race" + }, + { + "msg": "We are racing in Haunted Woods.", + "rewards": { "Balloon": 1 }, + "type": "Race" + }, + { + "msg": "We are racing the dragon boss Smokey.", + "rewards": {}, + "type": "Race" + }, + { + "msg": "We are collecting bananas in Smokey Castle.", + "rewards": { "TTAmulet": 1 }, + "type": "Collecting", + "collecting": { "name": "Banana", "max": 10 } + }, + { + "msg": "We are collecting silver coins in Windmill Plains.", + "rewards": { "Balloon": 1 }, + "type": "SilverCoinsRace" + }, + { + "msg": "We are collecting silver coins in Greenwood Village.", + "rewards": { "Balloon": 1 }, + "type": "SilverCoinsRace" + }, + { + "msg": "We are collecting silver coins in Boulder Canyon.", + "rewards": { "Balloon": 1 }, + "type": "SilverCoinsRace" + }, + { + "msg": "We are collecting silver coins in Haunted Woods.", + "rewards": { "Balloon": 1 }, + "type": "SilverCoinsRace" + }, + { + "msg": "We are racing in the rematch against Smokey.", + "rewards": { "WizpigAmulet": 1 }, + "type": "Race" + }, + { + "msg": "We are participating in the Trophy Race of Dragon Forest.", + "rewards": { "Trophy": 1 }, + "type": "TrophyRace" + }, + { + "msg": "We are racing the wizard pig boss Wizpig.", + "rewards": {}, + "type": "Race" + }, + { + "msg": "We are racing in Spacedust Alley.", + "rewards": { "Balloon": 1 }, + "type": "Race" + }, + { + "msg": "We are racing in Darkmoon Caverns.", + "rewards": { "Balloon": 1 }, + "type": "Race" + }, + { + "msg": "We are racing in Spaceport Alpha.", + "rewards": { "Balloon": 1 }, + "type": "Race" + }, + { + "msg": "We are racing in Star City.", + "rewards": { "Balloon": 1 }, + "type": "Race" + }, + { + "msg": "We are collecting silver coins in Spacedust Alley.", + "rewards": { "Balloon": 1 }, + "type": "SilverCoinsRace" + }, + { + "msg": "We are collecting silver coins in Darkmoon Caverns.", + "rewards": { "Balloon": 1 }, + "type": "SilverCoinsRace" + }, + { + "msg": "We are collecting silver coins in Spaceport Alpha.", + "rewards": { "Balloon": 1 }, + "type": "SilverCoinsRace" + }, + { + "msg": "We are collecting silver coins in Star City.", + "rewards": { "Balloon": 1 }, + "type": "SilverCoinsRace" + }, + { + "msg": "We are racing in the rematch against Wizpig.", + "rewards": {}, + "type": "Race" + }, + { + "msg": "We are participating in the Trophy Race of Future Fun Land.", + "rewards": {}, + "type": "TrophyRace" + } +] +