Created
December 19, 2016 07:35
-
-
Save pseale/85ea4ea9a697c385a900b4361a311e22 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
//============================================= | |
// Main - Node console runner | |
import fs = require("fs") | |
import Factory = require("./factory") | |
import parse = require("./parse") | |
const input = fs.readFileSync("./input.txt", "utf8") | |
const events = parse(input) | |
const factory = new Factory() | |
events.forEach(event => factory.process(event)) | |
factory.solvePartB() | |
//============================================= | |
// Factory.ts - meat of the "domain" | |
// the only thing Factory.ts exposes to the rest of the world is via its public methods. | |
import _ = require("lodash") | |
import Global = require("./global") | |
interface Chips { | |
low: number, | |
high: number | |
} | |
class Instructions { | |
public giveLowTo: Global.Destination | |
public giveHighTo: Global.Destination | |
constructor(giveLowTo: Global.Destination, giveHighTo: Global.Destination) { | |
this.giveLowTo = giveLowTo | |
this.giveHighTo = giveHighTo | |
} | |
} | |
class Bot { | |
public id: number | |
private chips: number[] | |
private instructions: Instructions | |
constructor(id: number) { | |
this.id = id | |
this.chips = [] | |
} | |
public recordInstructions(giveLowTo: Global.Destination, giveHighTo: Global.Destination) { | |
this.instructions = new Instructions(giveLowTo, giveHighTo) | |
} | |
public canDistributeChips(): boolean { | |
return this.chips.length === 2 | |
} | |
public receive(chip: number): Instructions { | |
if (this.chips.length >= 2) { | |
throw `Error: bots can't hold more than 2 chips. Bot ${this.id} ` | |
+ `with chips ${this.chips.join(",")} attempting to receive chip ${chip}` | |
} | |
this.chips.push(chip) | |
const chips = _(this.chips).sortBy(x => x).value() | |
if (chips.length === 2 && chips[0] === 17 && chips[1] === 61) { | |
console.log("========================================================") | |
console.log(`Part A solution: bot ${this.id} dealt chips 17 and 61.`) | |
console.log("========================================================") | |
} | |
return this.instructions | |
} | |
public takeChips(): Chips { | |
const sortedChips = _(this.chips).sortBy(x => x).value() | |
this.chips = [] | |
return { | |
low: sortedChips[0], | |
high: sortedChips[1] | |
} | |
} | |
} | |
class Bin { | |
public id: number | |
public chips: number[] | |
constructor(id: number) { | |
this.id = id | |
this.chips = [] | |
} | |
public receive(chip: number): void { | |
this.chips.push(chip) | |
} | |
public toString(): string { | |
return `Bin[${this.id}]: ${this.chips.join(",")}` | |
} | |
} | |
class Factory { | |
private bots: Bot[] | |
private bins: Bin[] | |
constructor() { | |
this.bots = [] | |
this.bins = [] | |
} | |
public process(event: Global.BotReceivesChip|Global.BotInstructionsCreated): void { | |
if (event instanceof Global.BotReceivesChip) { | |
this.processReceiveEvent(event) | |
} else if (event instanceof Global.BotInstructionsCreated) { | |
this.processBotInstructionsCreatedEvent(event) | |
} else { | |
throw `Error: expected event ${event} to be a legal event, but it reached this impossible place in the code.` | |
} | |
} | |
// this method makes no sense, but alas, ADVENT OF CODE DEMANDS IT | |
public solvePartB() { | |
const bin0 = this.getBin(0) | |
const bin1 = this.getBin(1) | |
const bin2 = this.getBin(2) | |
console.log("========================================================") | |
console.log(`Part B solution: ${bin0.chips[0] * bin1.chips[0] * bin2.chips[0]} ` | |
+ `| bins: ${bin0.toString()} ${bin1.toString()} ${bin2.toString()}`) | |
console.log("========================================================") | |
} | |
private getBot(id: number): Bot { | |
const matches = this.bots.filter(x => x.id === id) | |
if (matches.length > 0) { | |
return matches[0] | |
} | |
const bot = new Bot(id) | |
this.bots.push(bot) | |
return bot | |
} | |
private getBin(id: number): Bin { | |
const matches = this.bins.filter(x => x.id === id) | |
if (matches.length > 0) { | |
return matches[0] | |
} | |
const bin = new Bin(id) | |
this.bins.push(bin) | |
return bin | |
} | |
private processReceiveEvent(event: Global.BotReceivesChip): void { | |
const bot = this.getBot(event.botId) | |
bot.receive(event.chipId) | |
return | |
} | |
private getDestination(destination: Global.Destination): Bot|Bin { | |
if (destination.destinationType === Global.DestinationType.Bot) { | |
return this.getBot(destination.id) | |
} else if (destination.destinationType === Global.DestinationType.Bin) { | |
return this.getBin(destination.id) | |
} | |
} | |
private processBotInstructionsCreatedEvent(event: Global.BotInstructionsCreated): void { | |
const from = this.getBot(event.givenFromBotId) | |
from.recordInstructions(event.giveLowTo, event.giveHighTo) | |
this.processInstructions(from, {giveLowTo: event.giveLowTo, giveHighTo: event.giveHighTo}) | |
} | |
private processInstructions(from: Bot, instructions: Instructions) { | |
if (!from.canDistributeChips()) { | |
return | |
} | |
const lowGivenTo = this.getDestination(instructions.giveLowTo) | |
const highGivenTo = this.getDestination(instructions.giveHighTo) | |
const chips = from.takeChips() | |
console.log(`Bot ${from.id} giving low:` | |
+ `[chip:${chips.low} to ` | |
+ `${Global.DestinationType[instructions.giveLowTo.destinationType]}|${instructions.giveLowTo.id}] ` | |
+ `high:[chip:${chips.high} to ` | |
+ `${Global.DestinationType[instructions.giveHighTo.destinationType]}|${instructions.giveHighTo.id}]`) | |
const instructions1 = lowGivenTo.receive(chips.low) | |
const instructions2 = highGivenTo.receive(chips.high) | |
if (instructions1 instanceof Instructions && lowGivenTo instanceof Bot) { | |
this.processInstructions(lowGivenTo, instructions1) | |
} | |
if (instructions2 instanceof Instructions && highGivenTo instanceof Bot) { | |
this.processInstructions(highGivenTo, instructions2) | |
} | |
} | |
} | |
export = Factory | |
//============================================= | |
// parse.ts - turn text into event objects | |
import Global = require("./global") | |
function parseReceiveEvent(line: string): Global.BotReceivesChip { | |
const matches = /^value (\d+) goes to bot (\d+)$/.exec(line) | |
return new Global.BotReceivesChip(Number(matches[1]), Number(matches[2])) | |
} | |
function parseBotInstructionsCreatedEvent(line: string): Global.BotInstructionsCreated { | |
const matches = /^bot (\d+) gives low to (\w+) (\d+) and high to (\w+) (\d+)$/.exec(line) | |
const lowDestination = matches[2] === "bot" ? Global.DestinationType.Bot : Global.DestinationType.Bin | |
const highDestination = matches[4] === "bot" ? Global.DestinationType.Bot : Global.DestinationType.Bin | |
const givenFromBotId = Number(matches[1]) | |
const lowGivenTo = { | |
id: Number(matches[3]), | |
destinationType: lowDestination | |
} | |
const highGivenTo = { | |
id: Number(matches[5]), | |
destinationType: highDestination | |
} | |
return new Global.BotInstructionsCreated(givenFromBotId, lowGivenTo, highGivenTo) | |
} | |
function parse(text: string): Array<Global.BotReceivesChip|Global.BotInstructionsCreated> { | |
const lines = text | |
.split("\n") | |
.map(x => x.trim()) | |
.filter(x => x !== "") | |
const receiveEvents = lines.filter(x => x[0] === "v") // value 5 goes to bot 189 | |
.map(x => parseReceiveEvent(x)) | |
const giveEvents = lines.filter(x => x[0] === "b") // bot 138 gives low to bot 9 and high to bot 47 | |
.map(x => parseBotInstructionsCreatedEvent(x)) | |
let typeSafeArray: Array<Global.BotReceivesChip|Global.BotInstructionsCreated> = [] | |
typeSafeArray = typeSafeArray | |
.concat(receiveEvents).concat(giveEvents) | |
return typeSafeArray | |
} | |
export = parse | |
//============================================= | |
// Global.ts - because I didn't know what else to name it. Stores DTOs shared by | |
// Factory.ts and parse.ts | |
export class BotReceivesChip { | |
public chipId: number | |
public botId: number | |
constructor(chipId: number, botId: number) { | |
this.chipId = chipId | |
this.botId = botId | |
} | |
} | |
export enum DestinationType { | |
Bot, | |
Bin | |
} | |
export interface Destination { | |
id: number, | |
destinationType: DestinationType | |
} | |
export class BotInstructionsCreated { | |
public givenFromBotId: number | |
public giveLowTo: Destination | |
public giveHighTo: Destination | |
constructor(givenFromBotId: number, giveLowTo: Destination, giveHighTo: Destination) { | |
this.givenFromBotId = givenFromBotId | |
this.giveLowTo = giveLowTo | |
this.giveHighTo = giveHighTo | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment