2022 Day 1

Posted on Dec 1, 2022

Part 1

Problem

Each line in the file contains an inventory item with the number of calories. Blank lines separate each group of items. Find the inventory group with the highest number of calories and print that number.

General solution

Create a nested data structure which is an array of arrays, where the first level is the list of inventory groups and the second level is the list of items within that group. Iterate over the groups and keep a record of the highest calories count.

Solution: Go

In Go we will use slices instead of arrays, as arrays are of a fixed size in Go whereas slices can grow (and shrink) dynamically (in other languages, such as PHP, an array can be a dynamic size).

For clarity, we will define structs for individual items and groups of items. These only contain one member and so may seem overkill, but they make the code clearer and allow for future expansion (which is often needed in part 2).

In this case, we could have simply used a slice of integers, since we do not actually need the details of each item - everything is aggregated at the group level.

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	type InventoryItem struct {
		calories int
	}

	type InventoryGroup struct {
		items []InventoryItem
	}

	inventoryGroups := []InventoryGroup{}
	currentGroupIndex := 0
	currentGroupInitialised := false
	highestCalories := 0

	scanner := bufio.NewScanner(os.Stdin)

	// Read in the data and convert into structure
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())

		if len(line) > 0 {
			if !currentGroupInitialised {
				inventoryGroups = append(inventoryGroups, InventoryGroup{})
				currentGroupInitialised = true
			}

			// ASSUMPTION: Every non-empty line contains a number which can be expressed as an integer
			var inventoryItem InventoryItem
			inventoryItem.calories, _ = strconv.Atoi(line)
			inventoryGroups[currentGroupIndex].items = append(inventoryGroups[currentGroupIndex].items, inventoryItem)
		} else {
			// Blank line separates groups
			currentGroupIndex++
			currentGroupInitialised = false
		}
	}

	// Now the data has been transformed into a structure, find the highest calorie group
	for groupIndex := range inventoryGroups {
		group := inventoryGroups[groupIndex]
		groupCalories := 0

		for itemIndex := range group.items {
			groupCalories += group.items[itemIndex].calories
		}

		if groupCalories > highestCalories {
			highestCalories = groupCalories
		}
	}

	fmt.Println(highestCalories)
}

Part 2

Problem

Find the sum of the calories of the three most calorific groups.

General solution

As previously, iterate over the groups, but this time find the three highest groups and sum their calories.

Solution: Go

The main change from part 1 is that we create an array of highest calories (array is fine as the size is fixed) and initialise this with zeroes. We then check the calorie count of each group and replace the lowest of the highest calories if the group calorie count is greater, always sorting the highest calorie array to ensure it is in ascending order (with a small array this is unlikely to have performance issues).

package main

import (
	"bufio"
	"fmt"
	"os"
	"sort"
	"strconv"
	"strings"
)

func main() {
	type InventoryItem struct {
		calories int
	}

	type InventoryGroup struct {
		items []InventoryItem
	}

	inventoryGroups := []InventoryGroup{}
	currentGroupIndex := 0
	currentGroupInitialised := false
	highestCalories := []int{0, 0, 0}

	scanner := bufio.NewScanner(os.Stdin)

	// Read in the data and convert into structure
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())

		if len(line) > 0 {
			if !currentGroupInitialised {
				inventoryGroups = append(inventoryGroups, InventoryGroup{})
				currentGroupInitialised = true
			}

			// ASSUMPTION: Every non-empty line contains a number which can be expressed as an integer
			var inventoryItem InventoryItem
			inventoryItem.calories, _ = strconv.Atoi(line)
			inventoryGroups[currentGroupIndex].items = append(inventoryGroups[currentGroupIndex].items, inventoryItem)
		} else {
			// Blank line separates groups
			currentGroupIndex++
			currentGroupInitialised = false
		}
	}

	// Now the data has been transformed into a structure, find the highest calorie group
	for groupIndex := range inventoryGroups {
		group := inventoryGroups[groupIndex]
		groupCalories := 0

		for itemIndex := range group.items {
			groupCalories += group.items[itemIndex].calories
		}

		// If this group has a higher calorie count than the lowest of the highest
		// calorie groups, replace the lowest with the current group
		if groupCalories > highestCalories[0] {
			highestCalories[0] = groupCalories

			// Resort the highest calories into ascending order, i.e. element 0
			// is the smallest
			sort.Slice(highestCalories, func(x, y int) bool {
				return highestCalories[x] < highestCalories[y]
			})
		}
	}

	// Finally, find the sum of the highest calories
	highestCaloriesSum := 0

	for hc := range highestCalories {
		highestCaloriesSum += highestCalories[hc]
	}

	fmt.Println(highestCaloriesSum)
}