Skip to content

feat: day 6 guard gallivant 20241206 #31

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 6 commits into from
Dec 20, 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
40 changes: 21 additions & 19 deletions src/2024/2024-12-05/lib/fixOrderingUpdates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ type CurrentItem = {
}

/**
* Finds the next appropriate array index to swap places with the incorrectly placed current item accoring to the "rules"
* Finds the next appropriate array index to swap places with the incorrectly placed current item according 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
Expand Down Expand Up @@ -44,30 +44,32 @@ export const fixOrdering = (rules: Rules, unorderedItems: number[]): 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 }
let currentItem: number = -2

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

const temp = unorderedItems[indexToSwap] as number
unorderedItems[indexToSwap] = currentItem
unorderedItems[i] = temp
currentItem = temp
const temp = unorderedItems[indexToSwap] as number
unorderedItems[indexToSwap] = activeItem.value
unorderedItems[activeIndex] = temp
currentItem = temp

fixOrdering(rules, unorderedItems)
}
fixOrdering(rules, unorderedItems)
}

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

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

// Get the rest of items after the current item for comparison
Expand All @@ -76,7 +78,7 @@ export const fixOrdering = (rules: Rules, unorderedItems: number[]): number[] =>
// 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()
swapItems(currentItemData, i)
}
}

Expand Down
25 changes: 25 additions & 0 deletions src/2024/2024-12-06/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
## Day 6: Guard Gallivant

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

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

## Code

1. **grid.ts**
- `Grid` - class that has a 2D array object containing paths and obstacles in which a `Guard` runs.
- Has functions and methods for managing a `Grid` object

2. **guard.ts**
- `Guard` - class representing an object that can walk in the `Grid` map.
- Have methods for moving around in the `Grid`.

3. **guardController.ts**
- `guardController()` - Runs the `Guard` on the `Grid`, counting distinct positions/steps.

5. **guardControllerLoop.ts**
- `gridHasInfiniteLoop()`
- Runs the `Guard` on a `Grid` with obstacles, checking the placement of paths and obstacles that will make the `Guard` walk in an infinite loop
- `findObstructionPositions()` - Counts the number of positions in the `Grid` in which inserting one (1) obstruction symbol # will cause the `Guard` to walk in an infinite loop.
- > _**WARNING:** This is a very slow, unoptimized program execution that takes about ~3 mins to complete using the actual AoC input text._
10 changes: 10 additions & 0 deletions src/2024/2024-12-06/input.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
....#.....
.........#
..........
.#........
..........
.......#..
.#..^.....
.#......#.
....#.....
......#...
126 changes: 126 additions & 0 deletions src/2024/2024-12-06/lib/grid.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { GuardStatus, GuardDirection } from './guard.types.js'
import type { GuardState } from './guard.types.js'


/**
* @class Grid
* @description Object containing a 2D array of strings and other data in which a `Guard` object runs. Each item represents an open path or obstacle.
*/
export class Grid {
/** 2D board string array */
board: string[][] = []

/** Board length */
length: number = 0

/** Board width */
width: number = 0

/** Number of distinct positions that a `Guard` can traverse on the board */
positionCount: number = 0

/** Initial (x,y) coordinate starting position of a `Guard` */
start: { x: number; y: number; }

/** Obstruction symbol. Guards turn clockwise if this symbol blocks their next step. */
obstruction = '#'

/** Path symbol. Guards proceed to the next (x,y) coordinate after on this symbol. */
pathSymbol = '.'

/**
* @constructor
* @param {string[][]} data 2D string array containing paths `"."`, obstacles `"#"` and the `Guard` object
* @param obstructionSymbol
*/
constructor (data: string[][], obstructionSymbol: string = '#') {
this.board = data
this.length = data.length
this.width = (data[0])?.length as number
this.obstruction = obstructionSymbol
this.start = { x: -1, y: -1 }

this.findGuardStartPosition()
}

/**
* Finds the initial position and state of the guard in a 2D board array
* @param data {string[][]} 2D string array of the guard board
* @returns {GuardState} Initial position and state of the guard
*/
findGuardStartPosition (): GuardState {
const GuardDirections: string[] = Object.values(GuardDirection)

const initialState: GuardState = {
direction: null,
xPos: -1,
yPos: -1,
status: GuardStatus.IDLE
}

for (let y = 0; y < this.board.length; y += 1) {
const row = this.board[y]

if (row === undefined) {
throw new Error('Undefined row')
}

for (let i = 0; i < GuardDirections.length; i += 1) {
const indexOfDirection = row.indexOf(GuardDirections[i] as string)

if (indexOfDirection >= 0) {
initialState.direction = GuardDirections[i] as GuardDirection
initialState.xPos = indexOfDirection
initialState.yPos = y
initialState.status = GuardStatus.ACTIVE
break
}
}

if (initialState.status === GuardStatus.ACTIVE) break
}

this.start.x = initialState.xPos
this.start.y = initialState.yPos

return initialState
}

/**
* Checks if an (x,y) coordinate is outside the grid area
* @param {number} x array x coordinate
* @param {number} y array y coordinate
* @returns {boolean} Flag indicating if the guard is outside the grid
*/
isOutOfBounds (x: number, y: number): boolean {
const row = this.board[0] as string[]

return x < 0 ||
x >= row.length ||
y < 0 ||
y >= this.board.length
}

/**
* Marks an (x,y) coordinate in the grid
* @param {number} x array x coordinate
* @param {number} y array y coordinate
* @returns {void}
*/
mark (x: number, y: number): void {
const row = this.board[y]

if (row !== undefined && row[x] !== 'X') {
row[x] = 'X'
this.positionCount += 1
}
}

/**
* Displays the guard's distinct positions in the grid
*/
print () {
console.clear()
console.log(this.board.map(row => row.join(' ')))
}
}
10 changes: 10 additions & 0 deletions src/2024/2024-12-06/lib/grid.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* `Grid` dimensions properties
*
* @property {number} length - Length of the grid
* @property {number} wwidth - Width of the grid
*/
export type GridDimensions = {
length: number;
width: number;
}
65 changes: 65 additions & 0 deletions src/2024/2024-12-06/lib/guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import type { GuardState } from './guard.types.js'
import { GuardDirection, GuardStatus } from './guard.types.js'

/**
* @class Guard
* @description Game object that can move around in a 2D grid array
*/
export class Guard {
direction: GuardDirection | null = null
xPos: number = 0
yPos: number = 0
status: GuardStatus = GuardStatus.IDLE
steps: number = 0

/**
* Creates an instance of the `Guard` class
* @constructor
* @param {GuardState} state - Initial state of the guard
*/
constructor (state: GuardState) {
this.direction = state.direction
this.xPos = state.xPos || 0
this.yPos = state.yPos || 0
this.status = state.status
}

/**
* Turn the guard's direction clockwise by 90 degrees.
* If the direction is `null`, does nothing.
* @returns {void}
*/
turn (): void {
if (this.direction === null) return

const directions = Object.values(GuardDirection)
const currentDirectionIndex = directions.indexOf(this.direction)

const nextIndex = (currentDirectionIndex + 1) < directions.length
? currentDirectionIndex + 1
: 0

this.direction = directions[nextIndex] as GuardDirection
}

/**
* Moves the guard by one (1) coordinate [x,y] position in the `Grid`
* @param xDirection {number} x coordinate
* @param yDirection {number} y coordinate
* @returns {void}
*/
walk (xDirection: number, yDirection: number): void {
if (xDirection === undefined || yDirection === undefined) return

this.xPos += xDirection
this.yPos += yDirection
this.steps += 1
}

/**
* Set's the guard's status to "exited"
*/
exit (): void {
this.status = GuardStatus.EXIT
}
}
62 changes: 62 additions & 0 deletions src/2024/2024-12-06/lib/guard.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/**
* Directions that a `Guard` can face in a `Grid`
*
* @enum {string}
* @property {string} UP - Upward direction `"^"`
* @property {string} RIGHT - Upward direction `">"`
* @property {string} DOWN - Upward direction `"v"`
* @property {string} LEFT - Upward direction `"<"`
*/
export enum GuardDirection {
UP = '^',
RIGHT = '>',
DOWN = 'v',
LEFT = '<'
}

/**
* Mapping of `GuardDirections` to their corresponding numeric direction vectors.
* The vector indicates the direction of movement in the `Grid`
*
* @constant
* @type {Record<string, number>}
* @property {string} [GuardDirection.UP] - Upwards movement direction along the y-axis
* @property {string} [GuardDirection.RIGHT] - Right-ise movement direction along the x-axis
* @property {string} [GuardDirection.DOWN] - Downwards movement direction along the y-axis
* @property {string} [GuardDirection.left] - Left-wise movement direction along the x-axis
*/
export const GuardDirectionVector: Record<string, number> = {
[GuardDirection.UP]: -1,
[GuardDirection.RIGHT]: 1,
[GuardDirection.DOWN]: 1,
[GuardDirection.LEFT]: -1
}

/**
* `Guard` activity status in the `Grid` board
*
* @enum {string}
* @property {string} IDLE - Initial status
* @property {string} ACTIVE - Indicates active placement on the `Grid`
* @property {string} EXIT - Indicates off-Grid placement
*/
export enum GuardStatus {
IDLE = 'idle',
ACTIVE = 'active',
EXIT = 'exit'
}

/**
* Properties identifying the current state/status of a `Guard`
*
* @property {GuardDirection | null} direction Current direction of a `Guard` in the `Grid`
* @property {number} xPos x-coordinate of the `Guard` in the `Grid`
* @property {number} yPos y-coordinate of the `Guard` in the `Grid`
* @property {GuardStatus} status `Guard` activity status
*/
export type GuardState = {
direction: GuardDirection | null;
xPos: number;
yPos: number;
status: GuardStatus;
}
Loading
Loading