Created
December 12, 2016 06:19
-
-
Save pseale/3eb2e5631f311670c0f252c323cffe29 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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