github.com/frodejac/aoc-2022@v0.0.0-20221213081734-037c741b1c89/internal/aoc/day10/day10.go (about)

     1  package day10
     2  
     3  import (
     4  	"sort"
     5  	"strconv"
     6  	"strings"
     7  
     8  	"github.com/frodejac/aoc-2022/pkg/datastructures/stack"
     9  )
    10  
    11  type monkey struct {
    12  	inventory  *stack.Stack[int]
    13  	operation  func(int) int
    14  	mod        int
    15  	dstTrue    int
    16  	dstFalse   int
    17  	opsCounter *int
    18  }
    19  
    20  func parseInventory(inventoryRaw string) *stack.Stack[int] {
    21  	inventory := stack.New([]int{})
    22  	itemsRaw := strings.Split(inventoryRaw, ": ")[1]
    23  	items := strings.Split(itemsRaw, ", ")
    24  	for _, item := range items {
    25  		worryLevel, _ := strconv.Atoi(item)
    26  		inventory.Push(worryLevel)
    27  	}
    28  	return inventory
    29  }
    30  
    31  func parseOperation(operationRaw string) func(int) int {
    32  	operationRaw = strings.Split(operationRaw, "new = ")[1]
    33  	parts := strings.Split(operationRaw, " ")
    34  
    35  	operation := func(old int) int {
    36  		var op2 int
    37  		switch parts[2] {
    38  		case "old":
    39  			op2 = old
    40  		default:
    41  			op2, _ = strconv.Atoi(parts[2])
    42  		}
    43  		switch parts[1] {
    44  		case "+":
    45  			return old + op2
    46  		case "*":
    47  			return old * op2
    48  		default:
    49  			panic("Unknown operation")
    50  		}
    51  	}
    52  	return operation
    53  }
    54  
    55  func parseMod(modRaw string) int {
    56  	modRaw = strings.TrimSpace(modRaw)
    57  	parts := strings.Split(modRaw, " ")
    58  	mod, _ := strconv.Atoi(parts[3])
    59  	return mod
    60  }
    61  
    62  func parseDestination(destinationRaw string) int {
    63  	destinationRaw = strings.TrimSpace(destinationRaw)
    64  	parts := strings.Split(destinationRaw, " ")
    65  	dst, _ := strconv.Atoi(parts[5])
    66  	return dst
    67  }
    68  
    69  func parseMonkey(monkeyRaw string) monkey {
    70  	monkey := monkey{}
    71  	lines := strings.Split(monkeyRaw, "\n")
    72  	monkey.inventory = parseInventory(lines[1])
    73  	monkey.operation = parseOperation(lines[2])
    74  	monkey.mod = parseMod(lines[3])
    75  	monkey.dstTrue = parseDestination(lines[4])
    76  	monkey.dstFalse = parseDestination(lines[5])
    77  	monkey.opsCounter = new(int)
    78  	return monkey
    79  }
    80  
    81  func parseInput(input string) []monkey {
    82  	monkeysRaw := strings.Split(input, "\n\n")
    83  	monkeys := []monkey{}
    84  	for _, monkeyRaw := range monkeysRaw {
    85  		monkeys = append(monkeys, parseMonkey(monkeyRaw))
    86  	}
    87  	return monkeys
    88  }
    89  
    90  type Day10 struct {
    91  	input []byte
    92  }
    93  
    94  func Solver(input []byte) *Day10 {
    95  	return &Day10{input: input}
    96  }
    97  
    98  func (d *Day10) SolvePart1() string {
    99  	monkeys := parseInput(string(d.input))
   100  
   101  	for i := 0; i < 20; i++ {
   102  		for _, monkey := range monkeys {
   103  			itemCount := monkey.inventory.Size()
   104  			for i := 0; i < itemCount; i++ {
   105  				*monkey.opsCounter++
   106  				item := monkey.inventory.Pop()
   107  				newItem := monkey.operation(item)
   108  				newItem = newItem / 3
   109  				if (newItem % monkey.mod) == 0 {
   110  					monkeys[monkey.dstTrue].inventory.Push(newItem)
   111  				} else {
   112  					monkeys[monkey.dstFalse].inventory.Push(newItem)
   113  				}
   114  			}
   115  		}
   116  	}
   117  	opsCounters := []int{}
   118  	for _, monkey := range monkeys {
   119  		opsCounters = append(opsCounters, *monkey.opsCounter)
   120  	}
   121  	sort.Slice(opsCounters[:], func(i, j int) bool {
   122  		return opsCounters[i] > opsCounters[j]
   123  	})
   124  	monkeyBusiness := opsCounters[0] * opsCounters[1]
   125  	return strconv.Itoa(monkeyBusiness)
   126  }
   127  
   128  func (d *Day10) SolvePart2() string {
   129  	monkeys := parseInput(string(d.input))
   130  
   131  	commonDivisor := 1
   132  	for _, monkey := range monkeys {
   133  		commonDivisor *= monkey.mod
   134  	}
   135  
   136  	for i := 0; i < 10000; i++ {
   137  		for _, monkey := range monkeys {
   138  			itemCount := monkey.inventory.Size()
   139  			for i := 0; i < itemCount; i++ {
   140  				*monkey.opsCounter++
   141  				item := monkey.inventory.Pop()
   142  				newItem := monkey.operation(item)
   143  				newItem = newItem % commonDivisor
   144  				if (newItem % monkey.mod) == 0 {
   145  					monkeys[monkey.dstTrue].inventory.Push(newItem)
   146  				} else {
   147  					monkeys[monkey.dstFalse].inventory.Push(newItem)
   148  				}
   149  			}
   150  		}
   151  	}
   152  	opsCounters := []int{}
   153  	for _, monkey := range monkeys {
   154  		opsCounters = append(opsCounters, *monkey.opsCounter)
   155  	}
   156  	sort.Slice(opsCounters[:], func(i, j int) bool {
   157  		return opsCounters[i] > opsCounters[j]
   158  	})
   159  	monkeyBusiness := opsCounters[0] * opsCounters[1]
   160  	return strconv.Itoa(monkeyBusiness)
   161  }