Skip to content

feat: day 5 print queue 20241205 #25

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Dec 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ This repository contains solutions and a local development environment for the [
- Day 2: Red-Nosed Reports [[link]](/src/2024/2024-12-02/README.md)
- Day 3: Mull It Over [[link]](/src/2024/2024-12-03/README.md)
- Day 4: Ceres Search [[link]](/src/2024/2024-12-04/README.md)
- Day 5: Print Queue [[link]](/src/2024/2024-12-05/README.md)

</details>

Expand Down
14 changes: 14 additions & 0 deletions src/2024/2024-12-05/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
## Day 5: Print Queue

Visit the Advent of Code website for more information on this puzzle at:

**Source:** https://adventofcode.com/2024/day/5<br>
**Status:** Complete ⭐⭐

<br>

| Code | Description |
| --- | --- |
| **orderedUpdates.ts** | Has functions for finding correct "updates" according to defined "rules." It also counts the sum of middle page numbers from correctly-ordered "updates." |
| **fixOrderingUpdates.ts** | Has functions for correcting incorrectly placed "updates" before calculating the sum of the corrected middle page numbers. |
| **fileReader.ts** | Reads and transforms a text file for processing by the quiz scripts. |
28 changes: 28 additions & 0 deletions src/2024/2024-12-05/input.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
39|89
101|15
101|95
101|39
5|48
95|15
5|89
48|15
101|48
89|48
95|89
101|89
95|48
39|15
5|39
101|5
39|95
5|95
39|48
5|15
89|15

5,39,95,89,48
101,95,89,48,15
5,48,15
5,101,39,95,89
95,15,48
101,15,5,48,39
56 changes: 56 additions & 0 deletions src/2024/2024-12-05/lib/fileReader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import path from 'path'
import { currentDirectory, readFile } from '@/utils/file.js'
import { uniformArrayElements } from '@/utils/arrays.js'

export type Rules = Record<number, number[]>

export type QuizData = {
rules: Rules;
updates: number[][]
}

/**
* Reads and transforms quiz input text for processing.
* @param fileName {string} Filename of input text relative to the calling function
* @returns {QuizData} Formatted data
*/
export const fileReader = (fileName: string): QuizData => {
const directory = currentDirectory(import.meta.url)
const file = readFile(path.join(directory, '..', fileName))

const segments = file.split('\n\n')

if (
segments[0] === undefined || // rules
segments[1] === undefined // updates
) {
throw new Error('Undefined rules or updates data')
}

const rules: Rules = segments[0]
.split('\n')
.reduce((list: Rules, item) => {
const pair: number[] = item.split('|').map(Number)

if (!uniformArrayElements(pair, 'number')) {
throw new Error('Invalid rules value')
}

const key = pair[0] as number

if (list[key] === undefined) {
list[key] = [pair[1] as number]
} else {
list[key].push(pair[1] as number)
}

return list
}, {})

return {
rules,
updates: segments[1]
.split('\n')
.map(item => item.split(',').map(Number))
}
}
116 changes: 116 additions & 0 deletions src/2024/2024-12-05/lib/fixOrderingUpdates.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { isOrderedReport } from './orderedUpdates.js'
import { arrayMiddleIndex, uniformArrayElements } from '@/utils/arrays.js'

import type { Rules } from './fileReader.js'
import type { QuizData } from './fileReader.js'

type CurrentItem = {
value: number;
index: number;
}

/**
* Finds the next appropriate array index to swap places with the incorrectly placed current item accoring to the "rules"
* @param rules {Rules} Object containing parsed and formatted rules and updates data
* @param restItems {number[]} "updates" array items content
* @param currentItem {CurrentItem} Object containing the "current" item data in focus: value and array index
* @returns {number} Array index to swap positions with the current item
*/
const nextHotSwapIndex = (rules: Rules, restItems: number[], currentItem: CurrentItem): number => {
let indexToSwap = -1

for (let i = currentItem.index; i < restItems.length; i += 1) {
if (rules[restItems[i] as number]?.includes(currentItem.value)) {
indexToSwap = i
break
}
}

if (indexToSwap === -1) {
throw new Error('No array item to swap with')
}

return indexToSwap
}

/**
* Fixes the ordering of an incorrectly-ordered "update" (row) by swapping its elements with target items.
* @param rules {Rules} Object containing parsed and formatted rules and updates data
* @param unorderedItems {number[]} "updates" array items content
* @returns {number[]} Corrected "update" items
*/
export const fixOrdering = (rules: Rules, unorderedItems: number[]): number[] => {
if (!uniformArrayElements(unorderedItems, 'number')) {
throw new Error('Invalid item/s')
}

for (let i = 0; i < unorderedItems.length - 1; i += 1) {
let currentItem = unorderedItems[i] as number
const currentItemData = { value: currentItem, index: i }

// Swaps incorrectly placed items with target items in the array
const swapItems = () => {
const indexToSwap = nextHotSwapIndex(
rules,
unorderedItems,
currentItemData
)

const temp = unorderedItems[indexToSwap] as number
unorderedItems[indexToSwap] = currentItem
unorderedItems[i] = temp
currentItem = temp

fixOrdering(rules, unorderedItems)
}

// Correct "update" item should have en entry in the "rules" object
// Swap places with other items if its incorrect
if (rules[currentItem] === undefined) {
swapItems()
}

// Get the rest of items after the current item for comparison
const afterItems = unorderedItems.filter((_, index) => index > i)

// Correct item's "rule" should have the after-item entries
// Swap places with other items if its incorrect
if (!afterItems.every(item => rules[currentItem]?.includes(item))) {
swapItems()
}
}

return [...unorderedItems]
}

/**
* Fixes incorrectly-ordered "updates" and calculates their middle page numbers after fixing.
* @param data {QuizData} Object containing "rules" and "updates" input
* @param verbose {boolean} Flag to display processing log messages. Defaults to false.
* @returns {number} Total middle page numbers from corrected "updates"
*/
export const fixOrderingUpdates = (data: QuizData, verbose: boolean = false): number => {
let sum = 0

for (let i = 0; i < data.updates.length; i += 1) {
const isOrdered = isOrderedReport(data.rules, data.updates[i] ?? [])

if (!isOrdered) {
const corrected = fixOrdering(
data.rules,
[...data.updates[i] as number[]]
)

if (verbose) {
console.log('---incorrect', data.updates[i])
console.log('---corrected', corrected)
}

const middleIndex = arrayMiddleIndex(corrected)
const item = corrected[middleIndex - 1]
sum += item ?? 0
}
}

return sum
}
63 changes: 63 additions & 0 deletions src/2024/2024-12-05/lib/orderedUpdates.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import type { QuizData, Rules } from './fileReader.js'
import { uniformArrayElements, arrayMiddleIndex } from '@/utils/arrays.js'

/**
* Checks if an "update" list is correct according to defined "rules"
* @param rules {Rules} Object containing parsed and formatted rules and updates data
* @param updateItems {number[]} "updates" array items content
* @returns Flag indicating if the set of `updateItems` is correct
*/
export const isOrderedReport = (rules: Rules, updateItems: number[]): boolean => {
let isOrdered = true

if (!uniformArrayElements(updateItems, 'number')) {
throw new Error('Invalid updateItems item/s')
}

for (let i = 0; i < updateItems.length - 1; i += 1) {
const currentItem = updateItems[i] as number

// Current "update" item should have en entry in the "rules" object
if (rules[currentItem] === undefined) {
isOrdered = false
break
}

// Get the rest of items after the current item for comparison
const afterItems = updateItems.filter((_, index) => index > i)

// Current item's "rule" should have the after-item entries
if (!afterItems.every(item => rules[currentItem]?.includes(item))) {
isOrdered = false
break
}
}

return isOrdered
}

/**
* Counts the sum of middle page numbers from correctly-ordered "updates"
* @param data {QuizData} Object containing "rules" and "updates" input
* @param verbose {boolean} Flag to display processing log messages. Defaults to false.
* @returns {number} Total middle page numbers from correct "updates"
*/
export const sumOfCorrectUpdates = (data: QuizData, verbose: boolean = false): number => {
let sum = 0

for (let i = 0; i < data.updates.length; i += 1) {
const isOrdered = isOrderedReport(data.rules, data.updates[i] ?? [])

if (isOrdered) {
if (verbose) {
console.log('---correct', data.updates[i])
}

const mid = arrayMiddleIndex(data.updates[i] as number[])
const item = data.updates[i]?.[mid - 1]
sum += item ?? 0
}
}

return sum
}
29 changes: 29 additions & 0 deletions src/2024/2024-12-05/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { fileReader } from './lib/fileReader.js'
import type { QuizData } from './lib/fileReader.js'

import { sumOfCorrectUpdates } from './lib/orderedUpdates.js'
import { fixOrderingUpdates } from './lib/fixOrderingUpdates.js'

const data: QuizData = fileReader('input.txt')

/**
* Part 1/2 of the 2024-12-05 quiz
* Counts the sum of middle pages from correct updates
*/
const quiz20241205_01 = () => {
const sum = sumOfCorrectUpdates(data, true)
console.log('Sum of middle page numbers:', sum)
}

/**
* Part 2/2 of the 2024-12-05 quiz
* Fixes incorrectly-ordered updates and counts
* the sum of middle pages from corrected updates
*/
const quiz20241205_02 = () => {
const sum = fixOrderingUpdates(data, true)
console.log('Sum of corrected middle page numbers:', sum)
}

quiz20241205_01()
quiz20241205_02()
16 changes: 16 additions & 0 deletions src/2024/2024-12-05/sample.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { test, expect } from 'vitest'
import { fileReader } from './lib/fileReader.js'
import type { QuizData } from './lib/fileReader.js'

import { sumOfCorrectUpdates } from './lib/orderedUpdates.js'
import { fixOrderingUpdates } from './lib/fixOrderingUpdates.js'

const data: QuizData = fileReader('input.txt')

test('Count middle pages - demo', () => {
expect(sumOfCorrectUpdates(data)).toBe(232)
})

test('Fix ordering - demo', () => {
expect(fixOrderingUpdates(data)).toBe(126)
})
12 changes: 12 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,15 @@ export { similarityScore } from './2024/2024-12-01/lib/similarityScore.js'
export { countSafeReports } from './2024/2024-12-02/lib/countSafeReports.js'
export { extractMultiply } from './2024/2024-12-03/lib/extractMultiply.js'
export { extractMultiplyCondition } from './2024/2024-12-03/lib/extractMultiply.js'
export { wordCount } from './2024/2024-12-04/lib/wordCount.js'

export {
checkDiagonal,
checkHorizontal,
checkVertical
} from './2024/2024-12-04/lib/wordCheckerUtils.js'

export { isOrderedReport } from './2024/2024-12-05/lib/orderedUpdates.js'
export { sumOfCorrectUpdates } from './2024/2024-12-05/lib/orderedUpdates.js'
export { fixOrderingUpdates } from './2024/2024-12-05/lib/fixOrderingUpdates.js'
export { fixOrdering } from './2024/2024-12-05/lib/fixOrderingUpdates.js'
21 changes: 21 additions & 0 deletions src/utils/arrays.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,24 @@ export const arrangeArray = (order: ARRAY_ORDERING) =>
throw new Error('Invalid ordering')
}
}

/**
* Checks if array elements have the same type and has no null or undefined values
* @param items {S[]} array of elements
* @param type {T} type name of the elements inside the `items` array
* @returns {boolean} Flag indicating if all array elements have the same type
*/
export const uniformArrayElements = <S, T>(items: S[], type: T): boolean => {
return (
items.filter(value => typeof value === type).length === items.length
)
}

/**
* Retrieves the middle (center) index of an array
* @param list {T[]} 1-dimensional array
* @returns {number} Middle index of an array
*/
export const arrayMiddleIndex = <T>(list: T[]): number => {
return Math.floor(list.length / 2) + list.length % 2
}
Loading