From 2cfb142e7634a0e2989b71362df92bebae7d1c2b Mon Sep 17 00:00:00 2001 From: Orso <30684972+OrsoEric@users.noreply.github.com> Date: Tue, 3 Dec 2024 16:39:12 +0100 Subject: [PATCH] day 3 part 2. Result of valid mul(X,Y) = 92082041 --- solutions_orso/day03/day_3.py | 493 ++++++++++++++---- .../day03/day_3_example_conditional.txt | 1 + 2 files changed, 397 insertions(+), 97 deletions(-) create mode 100644 solutions_orso/day03/day_3_example_conditional.txt diff --git a/solutions_orso/day03/day_3.py b/solutions_orso/day03/day_3.py index da24d94..6234495 100644 --- a/solutions_orso/day03/day_3.py +++ b/solutions_orso/day03/day_3.py @@ -29,6 +29,29 @@ Scan the corrupted memory for uncorrupted mul instructions. What do you get if you add up all of the results of the multiplications? """ + +""" +--- Part Two --- +As you scan through the corrupted memory, you notice that some of the conditional statements are also still intact. +If you handle some of the uncorrupted conditional statements in the program, you might be able to get an even more accurate result. + +There are two new instructions you'll need to handle: + +The do() instruction enables future mul instructions. +The don't() instruction disables future mul instructions. +Only the most recent do() or don't() instruction applies. +At the beginning of the program, mul instructions are enabled. + +For example: + +xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5)) +This corrupted memory is similar to the example from before, but this time the mul(5,5) and mul(11,8) +instructions are disabled because there is a don't() instruction before them. +The other mul instructions function normally, including the one at the end that gets re-enabled by a do() instruction. + +This time, the sum of the results is 48 (2*4 + 8*5). +""" + """ ALGORITHM do a finite state machine that matches @@ -38,10 +61,20 @@ 4) search an int 5) search ) 6) return two operands X, Y +I also match +"do()" +"don't()" +that enable a flag +that false won't append the result """ +import logging + from typing import Tuple, List +#enumeration support +from enum import Enum, auto + def load_string(is_filename: str) -> str: """ From file, load the full string @@ -66,124 +99,379 @@ def parse_sequence( s_sequence : str ) -> List[Tuple[int, int]]: ltn_result = list() ls_state = ["m", "u", "l", "(", "X", ",", "Y", ")"] + ls_state_do = ["d", "o", "(", ")"] + ls_state_dont = ["d", "o", "n", "'", "t", "(", ")"] + + + #detect special command, start from execute + b_execute_mul = True #start from searching m - s_state = ls_state[0] - s_number = "" + n_arg_x = int(0) n_arg_y = int(0) n_index = 0 n_max_index = len(s_sequence) + while n_index < n_max_index: #fetch character s_char = s_sequence[n_index] #next character n_index += 1 - print(f"{s_char}") - if (s_state == ls_state[0]): - if (s_char == s_state): - #match - s_state = ls_state[1] - print(f"match {s_char}") + logging.debug(f"Char: {s_char} | State: {cl_state}") + + #not parsing a token + if (state.e_state == state.E_TOKEN.IDLE.value): + #match mul + if (s_char == "m"): + s_state = "u" + e_state = E_TOKEN.PARSE_MUL.value + logging.debug(f"match mul(X,Y) {s_char}") + elif (s_char == "d"): + s_state = "o" + e_state = E_TOKEN.PARSE_DO.value + logging.debug(f"match do()/don't() {s_char}") else: - #reset - s_state = ls_state[0] - print(f"Char: {s_char} | state: {s_state} | FSM error, reset.") - elif (s_state == ls_state[1]): - if (s_char == s_state): - #match - s_state = ls_state[2] - print(f"match {s_char}") + reset_fsm() + + elif (e_state == E_TOKEN.PARSE_MUL.value): + if (s_state == "u"): + if (s_char == s_state): + #match + s_state = "l" + logging.debug(f"match {s_char}") + else: + logging.debug(f"Char: {s_char} | state: {s_state} | FSM error, reset.") + reset_fsm() + elif (s_state == "l"): + if (s_char == s_state): + s_state = "(" + logging.debug(f"match {s_char}") + else: + #reset + s_state = ls_state[0] + logging.debug(f"Char: {s_char} | state: {s_state} | FSM error, reset.") + elif (s_state == ls_state[3]): + if (s_char == s_state): + #detect number + s_state = ls_state[4] + s_number = "" + logging.debug(f"match {s_char}") + else: + #reset + s_state = ls_state[0] + logging.debug(f"Char: {s_char} | state: {s_state} | FSM error, reset.") + #match number, I eat all numbers and parse them into int when I get a non number + elif (s_state == ls_state[4]): + #number + if (s_char >= "0") and (s_char <= "9"): + s_number += s_char + logging.debug(f"detecting number {s_char} ->{s_number}") + #no numbers were eaten + elif len(s_number) <= 0: + #reset + s_state = ls_state[0] + logging.debug(f"Char: {s_char} | state: {s_state} | FSM error, no number detected.") + #parse number to int + else: + n_arg_x = int(s_number) + logging.debug(f"Detected X {n_arg_x}") + #I need to reprocess this character + n_index -= 1 + #next state + s_state = ls_state[5] + elif (s_state == ls_state[5]): + if (s_char == s_state): + #detect number + s_state = ls_state[6] + s_number = "" + logging.debug(f"match {s_char}") + else: + #reset + logging.debug(f"Char: {s_char} | state: {s_state} | FSM error, reset.") + s_state = ls_state[0] + #match number, I eat all numbers and parse them into int when I get a non number + elif (s_state == ls_state[6]): + #number + if (s_char >= "0") and (s_char <= "9"): + s_number += s_char + logging.debug(f"detecting number {s_char} ->{s_number}") + #no numbers were eaten + elif len(s_number) <= 0: + #reset + s_state = ls_state[0] + logging.debug(f"Char: {s_char} | state: {s_state} | FSM error, no number detected.") + #parse number to int + else: + n_arg_y = int(s_number) + logging.debug(f"Detected Y {n_arg_y}") + #I need to reprocess this character + n_index -= 1 + #next state + s_state = ls_state[7] + elif (s_state == ls_state[7]): + if (s_char == s_state): + #detect number + logging.debug(f"FINAL MATCH, VALID X: {n_arg_x} Y: {n_arg_y}") + tn_mul = (n_arg_x,n_arg_y) + ltn_result.append(tn_mul) + #reset + s_state = ls_state[0] + else: + #reset + logging.debug(f"Char: {s_char} | state: {s_state} | FSM error, reset.") + s_state = ls_state[0] else: - #reset s_state = ls_state[0] - print(f"Char: {s_char} | state: {s_state} | FSM error, reset.") - elif (s_state == ls_state[2]): - if (s_char == s_state): - #match - s_state = ls_state[3] - print(f"match {s_char}") - else: - #reset - s_state = ls_state[0] - print(f"Char: {s_char} | state: {s_state} | FSM error, reset.") - elif (s_state == ls_state[3]): - if (s_char == s_state): - #detect number - s_state = ls_state[4] - s_number = "" - print(f"match {s_char}") + logging.debug("ERR:ALGORITHM: ask the dev to finish his job") + + elif (e_state == E_TOKEN.PARSE_MUL.value): + if (s_state == "o"): + if (s_char == "o"): + s_state = "n" + s_number = "" + logging.debug(f"match {s_char}") + else: + #reset + logging.debug(f"Char: {s_char} | state: {s_state} | FSM error, reset.") + s_state = ls_state[0] else: - #reset - s_state = ls_state[0] - print(f"Char: {s_char} | state: {s_state} | FSM error, reset.") - #match number, I eat all numbers and parse them into int when I get a non number - elif (s_state == ls_state[4]): - #number - if (s_char >= "0") and (s_char <= "9"): - s_number += s_char - print(f"detecting number {s_char} ->{s_number}") - #no numbers were eaten - elif len(s_number) <= 0: - #reset - s_state = ls_state[0] - print(f"Char: {s_char} | state: {s_state} | FSM error, no number detected.") - #parse number to int + reset_fsm() + else: + logging.debug("ERR:ALGORITHM: ask the dev to finish his job") + logging.debug(f"Char: {s_char} | state: {s_state} {e_state} | FSM error, reset.") + s_state = "" + e_state = E_TOKEN.PARSE_IDLE + + return ltn_result + + +class Parser(): + class E_TOKEN( Enum ): + IDLE = 0 + PARSE_MUL = auto() + PARSE_DO = auto() + PARSE_DONT = auto() + + def __init__(self): + self.n_index = 0 + self.n_index_max = 0 + self.gltn_results = list() + self.e_state = self.E_TOKEN.IDLE + self.s_state = "" + self.s_number = "" + #"do()" and "don't()" can blank MUL + self.b_allow_mul = True + return + + def reset_fsm(self): + self.e_state = self.E_TOKEN.IDLE + self.s_state = "" + self.s_number = "" + return + + def next(self): + self.n_index += 1 + + def prev(self): + self.n_index -= 1 + + def parse_sequence( self, s_sequence : str ) -> List[Tuple[int, int]]: + self.gltn_results = list() + self.n_index = 0 + self.n_max_index = len(s_sequence) + while self.n_index < self.n_max_index: + #fetch character + s_char = s_sequence[self.n_index] + #next character + self.next() + logging.debug(f"Char: {s_char} | State: {self.e_state} - >{self.s_state}<") + #parse the character + b_decoded = self.parse_char( s_char ) + + return self.gltn_results + + def parse_char( self, s_char: str ) -> bool: + + if (self.e_state == self.E_TOKEN.IDLE): + if (s_char == "m"): + self.s_state = "u" + self.e_state = self.E_TOKEN.PARSE_MUL + logging.debug(f"match mul(X,Y) {s_char}") + elif (s_char == "d"): + self.s_state = "o" + self.e_state = self.E_TOKEN.PARSE_DO + logging.debug(f"match do() {s_char}") else: - n_arg_x = int(s_number) - print(f"Detected X {n_arg_x}") - #I need to reprocess this character - n_index -= 1 - #next state - s_state = ls_state[5] - elif (s_state == ls_state[5]): - if (s_char == s_state): - #detect number - s_state = ls_state[6] - s_number = "" - print(f"match {s_char}") + self.reset_fsm() + + elif (self.e_state == self.E_TOKEN.PARSE_MUL): + #MATCH LETTER + if (self.s_state == "u"): + if (s_char == self.s_state): + self.s_state = "l" + logging.debug(f"matched character{s_char} , advance") + else: + self.reset_fsm() + #MATCH LETTER + elif (self.s_state == "l"): + if (s_char == self.s_state): + self.s_state = "(" + logging.debug(f"matched character {s_char}, advance") + else: + self.reset_fsm() + #MATCH LETTER + elif (self.s_state == "("): + if (s_char == self.s_state): + #match number next + self.s_state = "X" + self.s_number = "" + logging.debug(f"matched character {s_char}, advance") + else: + self.reset_fsm() + #MATCH NUMBER + elif (self.s_state == "X"): + if (s_char >= "0") and (s_char <= "9"): + #append the value + self.s_number += s_char + logging.debug(f"detecting number {s_char} ->{self.s_number}") + #no numbers were eaten + elif len(self.s_number) <= 0: + logging.debug(f"Zero length number") + self.reset_fsm() + #number is too long + elif len(self.s_number) >= 4: + logging.debug(f"Number is too long") + self.reset_fsm() + #parse number to int + else: + self.n_arg_x = int(self.s_number) + logging.debug(f"Detected X {self.n_arg_x}") + #I need to reprocess this character + self.prev() + #next state + self.s_state = "," + #MATCH LETTER + elif (self.s_state == ","): + if (s_char == self.s_state): + #match Y number next + self.s_state = "Y" + self.s_number = "" + logging.debug(f"matched character {s_char}, advance") + else: + self.reset_fsm() + #MATCH NUMBER + elif (self.s_state == "Y"): + if (s_char >= "0") and (s_char <= "9"): + #append the value + self.s_number += s_char + logging.debug(f"detecting number {s_char} ->{self.s_number}") + #no numbers were eaten + elif len(self.s_number) <= 0: + logging.debug(f"Zero length number") + self.reset_fsm() + #number is too long + elif len(self.s_number) >= 4: + logging.debug(f"Number is too long") + self.reset_fsm() + #parse number to int + else: + self.n_arg_y = int(self.s_number) + logging.debug(f"Detected Y {self.n_arg_y}") + #I need to reprocess this character + self.prev() + #next state + self.s_state = ")" + #MATCH LETTER + elif (self.s_state == ")"): + if (s_char == self.s_state): + logging.debug(f"FINAL MATCH, VALID X: {self.n_arg_x} Y: {self.n_arg_y}") + if (self.b_allow_mul == False): + print("MUL DISALLOWED!!!") + else: + tn_mul = (self.n_arg_x,self.n_arg_y) + self.gltn_results.append(tn_mul) + self.reset_fsm() + else: + self.reset_fsm() else: - #reset - print(f"Char: {s_char} | state: {s_state} | FSM error, reset.") - s_state = ls_state[0] - #match number, I eat all numbers and parse them into int when I get a non number - elif (s_state == ls_state[6]): - #number - if (s_char >= "0") and (s_char <= "9"): - s_number += s_char - print(f"detecting number {s_char} ->{s_number}") - #no numbers were eaten - elif len(s_number) <= 0: - #reset - s_state = ls_state[0] - print(f"Char: {s_char} | state: {s_state} | FSM error, no number detected.") - #parse number to int + logging.debug(f"ERR: Ask developer to finish the algorithm") + self.reset_fsm() + elif (self.e_state == self.E_TOKEN.PARSE_DO): + #MATCH LETTER + if (self.s_state == "o"): + if (s_char == self.s_state): + self.s_state = "(" + logging.debug(f"matched character{s_char} , advance") + else: + self.reset_fsm() + #MATCH LETTER + elif (self.s_state == "("): + if (s_char == self.s_state): + self.s_state = ")" + logging.debug(f"matched character{s_char} , advance") + #I might match the don't + elif (s_char == "n"): + self.s_state = "'" + self.e_state = self.E_TOKEN.PARSE_DONT + logging.debug(f"matched character{s_char} , advance") + else: + self.reset_fsm() + #MATCH LETTER + elif (self.s_state == ")"): + if (s_char == self.s_state): + self.b_allow_mul = True + logging.debug(f"DO {self.b_allow_mul}") + self.reset_fsm() + else: + self.reset_fsm() else: - n_arg_y = int(s_number) - print(f"Detected Y {n_arg_y}") - #I need to reprocess this character - n_index -= 1 - #next state - s_state = ls_state[7] - elif (s_state == ls_state[7]): - if (s_char == s_state): - #detect number - print(f"FINAL MATCH, VALID X: {n_arg_x} Y: {n_arg_y}") - tn_mul = (n_arg_x,n_arg_y) - ltn_result.append(tn_mul) - #reset - s_state = ls_state[0] + logging.debug(f"ERR: Ask developer to finish the algorithm") + self.reset_fsm() + elif (self.e_state == self.E_TOKEN.PARSE_DONT): + #MATCH LETTER + if (self.s_state == "'"): + if (s_char == self.s_state): + self.s_state = "t" + logging.debug(f"matched character{s_char} , advance") + else: + self.reset_fsm() + #MATCH LETTER + elif (self.s_state == "t"): + if (s_char == self.s_state): + self.s_state = "(" + logging.debug(f"matched character{s_char} , advance") + else: + self.reset_fsm() + #MATCH LETTER + elif (self.s_state == "("): + if (s_char == self.s_state): + self.b_allow_mul = False + logging.debug(f"DONT {self.b_allow_mul}") + self.reset_fsm() + else: + self.reset_fsm() else: - #reset - print(f"Char: {s_char} | state: {s_state} | FSM error, reset.") - s_state = ls_state[0] + logging.debug(f"ERR: Ask developer to finish the algorithm") + self.reset_fsm() else: - s_state = ls_state[0] - print("ERR:ALGORITHM: ask the dev to finish his job") - + logging.debug(f"ERR: Ask developer to finish the algorithm") + self.reset_fsm() + + return False + +def parse_sequence_b( s_sequence : str ) -> List[Tuple[int, int]]: + + cl_parser = Parser() + + ltn_result = cl_parser.parse_sequence( s_sequence ) + + print(ltn_result) return ltn_result def process_mul( iltn_args : List[Tuple[int,int]] ) -> int: + if (len(iltn_args) <=0): + return -1 n_result = 0 for tn_arg in iltn_args: n_result += tn_arg[0] * tn_arg[1] @@ -193,7 +481,8 @@ def day_3(is_filename: str) -> bool: #from file load the sequence s_sequence = load_string( is_filename ) print(s_sequence) - ltn_args = parse_sequence( s_sequence ) + #ltn_args = parse_sequence( s_sequence ) + ltn_args = parse_sequence_b( s_sequence ) print(ltn_args) n_result = process_mul( ltn_args ) print(f"Result of valid mul(X,Y) = {n_result}") @@ -204,8 +493,18 @@ def day_3(is_filename: str) -> bool: # Example usage gs_filename_example = 'advent_of_code\day_3_example.txt' +gs_filename_example_conditional = 'advent_of_code\day_3_example_conditional.txt' gs_filename = 'advent_of_code\day_3.txt' # if interpreter has the intent of executing this file if __name__ == "__main__": - day_3( gs_filename_example ) + logging.basicConfig( + filename='debug.log', + # Specify the log file name + level=logging.DEBUG, + # Set the level of debug to show + format='[%(asctime)s] %(levelname)s %(module)s:%(lineno)d > %(message)s ', + filemode='w' + ) + logging.debug("Begin") + #day_3( gs_filename_example_conditional ) day_3( gs_filename ) \ No newline at end of file diff --git a/solutions_orso/day03/day_3_example_conditional.txt b/solutions_orso/day03/day_3_example_conditional.txt new file mode 100644 index 0000000..b774ec9 --- /dev/null +++ b/solutions_orso/day03/day_3_example_conditional.txt @@ -0,0 +1 @@ +xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5)) \ No newline at end of file