Skip to content

Instantly share code, notes, and snippets.

@pseale
Created December 19, 2016 01:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pseale/f363425f08fa230236cdfa990768f220 to your computer and use it in GitHub Desktop.
Save pseale/f363425f08fa230236cdfa990768f220 to your computer and use it in GitHub Desktop.
// DISCLAIMER:
//
// I'm getting lazier as I power through more and more of these Advent of Code challenges.
// Notably, I couldn't muster the energy to:
// * fix naming (e.g. part A is named "parseA" and part B is named "count")
// * Write part B tests
// * Write fine-grained unit tests
// * I probably left dead code in here
//
// Just know that I'm probably ashamed of some of this, but my laziness exceeds my fear of shame.
//
// Enjoy.
//---------------------------------------
// tests - for Part A only - no Part B tests because motivation was very low
import parseA = require("../parseA")
describe("Acceptance tests", () => {
describe("Part A", () => {
test("Text with no markers should be kept verbatim", () => {
const result = parseA("ADVENT")
expect(result).toBe("ADVENT")
})
test("Text with any markers should repeat the following text", () => {
const result = parseA("A(1x5)BC")
expect(result).toBe("ABBBBBC")
})
test("Text within the data section of a marker is not parsed for markers", () => {
const result = parseA("X(8x2)(3x3)ABCY")
expect(result).toBe("X(3x3)ABC(3x3)ABCY")
})
})
})
//---------------------------------------
// Part A
interface ParseCompressionMarkerResult {
output: string[],
tokens: string[]
}
function parseCompressionMarker(tokens: string[]): ParseCompressionMarkerResult {
let tokenString = tokens.join("")
const match = /^\((\d+)x(\d+)\)/.exec(tokenString)
const characters = Number(match[1])
const repeat = Number(match[2])
const dataSectionPlusRemainingLine = tokenString.substr(match[0].length)
const dataSection = dataSectionPlusRemainingLine.substr(0, characters)
const remainingLine = dataSectionPlusRemainingLine.substr(characters)
return {
output: Array(repeat + 1).join(dataSection).split(""),
tokens: remainingLine.split("")
}
}
function parse(line: string): string {
let output = []
let tokens = line.split("")
let numTokens = []
let subsequentCharacters = 0
while (_(tokens).some()) {
if (tokens[0] === "(") {
const result = parseCompressionMarker(tokens)
output = output.concat(result.output)
tokens = result.tokens
} else {
output.push(tokens.shift())
}
}
return output.join("")
}
export = parse
//---------------------------------------
// Part B (a copy+paste+rewrite of Part A)
interface StandardToken {
length: number,
text: string
}
interface CompressionMarkerToken {
length: number,
dataSection: Array<StandardToken|CompressionMarkerToken>
}
interface ParseCompressionMarkerResult {
remaining: string,
token: CompressionMarkerToken
}
function count(tokens: Array<StandardToken|CompressionMarkerToken>): number {
if (tokens.length === 0) {
return 0
}
return tokens
.map(token => token.length)
.reduce((previousValue, currentValue) => previousValue + currentValue, 0)
}
function parseCompressionMarker(text: string): ParseCompressionMarkerResult {
const match = /^\((\d+)x(\d+)\)/.exec(text)
const characters = Number(match[1])
const repeat = Number(match[2])
const dataSectionPlusRemainingLine = text.substr(match[0].length)
const dataSection = dataSectionPlusRemainingLine.substr(0, characters)
const dataSectionTokens = parse(dataSection)
const remaining = dataSectionPlusRemainingLine.substr(characters)
const token = {
length: repeat * count(dataSectionTokens),
dataSection: dataSectionTokens
}
return {
remaining,
token
}
}
function parse(text: string): Array<StandardToken|CompressionMarkerToken> {
if (!text || text.length === 0) {
return []
}
const tokens = []
let remaining = text
while (remaining.length > 0) {
if (remaining[0] !== "(") {
const matches = /^(.*?)(\(|$)/.exec(remaining)
const token = matches[1]
remaining = remaining.substr(token.length)
tokens.push({
length: token.length,
text: token
})
} else {
const result = parseCompressionMarker(remaining)
remaining = result.remaining
tokens.push(result.token)
}
}
return tokens
}
function parseAndCount(text: string): number {
const tokens = parse(text)
return count(tokens)
}
export = parseAndCount
//---------------------------------------
// Main (Node console runner)
import fs = require("fs")
import parseA = require("./parseA")
import count = require("./count")
const input = fs.readFileSync("./input.txt", "utf8")
.split("\n")
.map(x => x.trim())
.filter(x => x !== "")
const partACount = input
.map(line => parseA(line))
.reduce((previousValue, currentValue) => previousValue + currentValue.length, 0)
const partBCount = input
.map(line => count(line))
.reduce((previousValue, currentValue) => previousValue + currentValue, 0)
console.log(`Part A: total count of uncompressed data is ${partACount} for ${input.length} lines`)
console.log(`Part B: total count of uncompressed data is ${partBCount} for ${input.length} lines`)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment