Skip to content

Instantly share code, notes, and snippets.

@pseale
Created December 12, 2016 06:19
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/3eb2e5631f311670c0f252c323cffe29 to your computer and use it in GitHub Desktop.
Save pseale/3eb2e5631f311670c0f252c323cffe29 to your computer and use it in GitHub Desktop.
/* test results:
PASS __tests__\day4.test.js
Acceptance tests
√ aaaaa-bbb-z-y-x-123[abxyz] is a real room (15ms)
√ totally-real-room-200[decoy] is not a real room
parsing
√ aaaaa-bbb-z-y-x-123[abxyz]
generating a checksum
√ aaaaa-bbb-z-y-x-123[abxyz]
decrypt
√ 'qzmt-zixmtkozy-ivhz-343' is 'very encrypted name'
*/
import parse = require("../parser")
import checker = require("../checker")
import decrypt = require("../decrypt")
describe("Acceptance tests", () => {
test("aaaaa-bbb-z-y-x-123[abxyz] is a real room", () => {
var rooms = parse("aaaaa-bbb-z-y-x-123[abxyz]")
var room = rooms[0]
var result = checker.check(room)
expect(result.sectorId).toBe(123)
expect(result.encryptedName).toBe("aaaaa-bbb-z-y-x")
expect(result.legal).toBe(true)
})
test("totally-real-room-200[decoy] is not a real room", () => {
var rooms = parse("totally-real-room-200[decoy]")
var room = rooms[0]
var result = checker.check(room)
expect(result.sectorId).toBe(200)
expect(result.encryptedName).toBe("totally-real-room")
expect(result.legal).toBe(false)
})
})
describe("parsing", () => {
test("aaaaa-bbb-z-y-x-123[abxyz]", () => {
var result = parse("aaaaa-bbb-z-y-x-123[abxyz]")
expect(result.length).toBe(1)
var room = result[0]
expect(room.encryptedName).toBe("aaaaa-bbb-z-y-x")
expect(room.sectorId).toBe(123)
expect(room.checksum).toBe("abxyz")
})
})
describe("generating a checksum", () => {
test("aaaaa-bbb-z-y-x-123[abxyz]", () => {
var result = checker.generateChecksum("aaaaa-bbb-z-y-x")
expect(result).toBe("abxyz")
})
})
describe("decrypt", () => {
test("'qzmt-zixmtkozy-ivhz-343' is 'very encrypted name'", () => {
var r = {
encryptedName: "qzmt-zixmtkozy-ivhz",
sectorId: 343,
legal: true
}
var result = decrypt(r)
expect(result.sectorId).toBe(343)
expect(result.name).toBe("very encrypted name")
})
})
//--------------------------------------
// main file
import fs = require("fs");
import _ = require("lodash")
import parse = require("./parser")
import checker = require("./checker")
import decrypt = require("./decrypt")
var input = fs.readFileSync("./input.txt", "utf8")
var rooms = parse(input)
var results = _(rooms).map(x => checker.check(x)).value()
var legalRooms = _(results).filter(x => x.legal === true).value().length
var sum = _(results).filter(x => x.legal === true).map(x => x.sectorId).sum()
var partBResults = _(results)
.filter(x => x.legal === true)
.map(x => decrypt(x))
.value()
var possibleNorthPoleObjectStorageLocations = _(partBResults).filter(x => /north/.exec(x.name) && /pole/.exec(x.name) ).value()
console.log(`Part A: Sum: ${sum} of ${legalRooms} legal rooms (of ${rooms.length} total)`)
console.log(`Part B: all possible north pole locations: `, possibleNorthPoleObjectStorageLocations)
//--------------------------------
// input parser
import _ = require("lodash")
interface Room {
encryptedName : string,
sectorId : number,
checksum : string
}
function parse(input : string) : Room[] {
var lines = _(input.split("\n"))
.map(x => x.trim())
.filter(x => x !== "")
.value()
return _(lines)
.map(line => {
var regexGroups = /^(.+)-(\d+)\[(.+)\]$/.exec(line)
if (!regexGroups || !regexGroups.length || regexGroups.length !== 4) {
throw `Unable to parse room: ${line}`
}
var encryptedName = regexGroups[1]
var sectorIdString = regexGroups[2]
var checksum = regexGroups[3]
if (!encryptedName || !sectorIdString || !checksum) {
throw `Invalid room - expected values for encrypted name:'${encryptedName}' sector ID:'${sectorIdString}' checksum:'${checksum}' for room ${line}`
}
if (encryptedName !== encryptedName.toLowerCase()) {
throw `Expected room name '${encryptedName}' to be in all lower-case for room ${line}`
}
var sectorId = Number(sectorIdString)
if (isNaN(sectorId)) {
throw `Invalid sector ID: ${regexGroups[2]} for room ${line}`
}
return {
encryptedName: encryptedName,
sectorId: sectorId,
checksum: checksum
}
})
.value()
}
export = parse
//---------------------------------------------------------------
// checksum checker
import _ = require("lodash")
interface RoomInput {
encryptedName : string,
sectorId : number,
checksum : string
}
interface Room {
sectorId : number,
encryptedName : string,
legal : boolean
}
export function generateChecksum(encryptedName : string) : string {
var roomNameWithoutDashes = encryptedName.replace(/-/g, "")
var lodashGroups = _(roomNameWithoutDashes)
.groupBy(x => x)
.value()
var letters = []
_.forOwn(lodashGroups, (value, key) => {
letters.push({
letter: key,
occurrencesSortableAscending: -value.length //for convenience sorting by both fields in ascending order, change it so that the letter with the most "occurrences" sorts as first in ascending order. To do so, negate the occurrences.
})
})
var expectedChecksumArray = _(_.sortBy(letters, ["occurrencesSortableAscending", "letter"]))
.map(x => x.letter)
.take(5)
.value()
var expectedChecksum = expectedChecksumArray.join("")
return expectedChecksum
}
export function check(room : RoomInput) : Room {
var legal = room.checksum === generateChecksum(room.encryptedName)
return {
sectorId: room.sectorId,
encryptedName: room.encryptedName,
legal: legal
}
}
//------------------------------------------------------
// decrypt-er
import _ = require("lodash")
import checker = require("./checker")
interface EncryptedRoom {
sectorId : number,
encryptedName : string
}
interface DecryptedRoom {
sectorId : number,
name : string
}
function decryptAlphabeticalLetter(letter: string, rotate : number) : string {
var charCodeForA = "a".charCodeAt(0)
var offset = letter.charCodeAt(0) - charCodeForA
var newCharCode = ((offset + rotate) % 26) + charCodeForA
return String.fromCharCode(newCharCode)
}
function decrypt(encryptedRoom : EncryptedRoom) : DecryptedRoom {
var name = _(encryptedRoom.encryptedName.split(""))
.map(x => {
if (x === "-") {
return " "
}
return decryptAlphabeticalLetter(x, encryptedRoom.sectorId)
})
.value()
.join("")
return {
sectorId: encryptedRoom.sectorId,
name: name
}
}
export = decrypt
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment