Created
December 21, 2016 08:15
-
-
Save pseale/18366d6ccc7df8213479c4550d7160a0 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
//------------------------------------------------ | |
// Node console runner | |
import fs = require("fs") | |
import Global = require("./global") | |
import parse = require("./parse") | |
import run = require("./run") | |
const input = fs.readFileSync("./input.txt", "utf8") | |
const instructions = parse(input) | |
console.log("Part A:") | |
run(instructions, Global.InitType.a) | |
console.log("\n\n\n") | |
console.log("Part B:") | |
run(instructions, Global.InitType.b) | |
//------------------------------------------------ | |
// run.ts - this is the interesting part | |
import _ = require("lodash") | |
import Global = require("./global") | |
type Registers = Global.RegisterState[] | |
interface State { | |
location: number, | |
registers: Registers | |
} | |
// for the purposes of Advent of Code, we'll assume registers are initialized to 0 | |
// Part B - AHA! I KNEW IT! Can't get fooled twice. | |
function init(type: Global.InitType): State { | |
return { | |
location: 0, | |
registers: [ | |
{ register: Global.Register.a, value: 0}, | |
{ register: Global.Register.b, value: 0}, | |
{ register: Global.Register.c, value: type === Global.InitType.b ? 1 : 0}, | |
{ register: Global.Register.d, value: 0}, | |
] | |
} | |
} | |
function replace(registers: Registers, registerState: Global.RegisterState): Registers { | |
const others = registers.filter(x => x.register !== registerState.register) | |
return others.concat(registerState) | |
} | |
function processCpyValue(state: State, cpyValue: Global.CpyValue): State { | |
const registerNewState = { | |
register: cpyValue.destination, | |
value: cpyValue.value | |
} | |
return { | |
location: state.location + 1, | |
registers: replace(state.registers, registerNewState) | |
} | |
} | |
function processCpyRegister(state: State, cpyRegister: Global.CpyRegister): State { | |
const copyFrom = state.registers.filter(x => x.register === cpyRegister.source)[0] | |
const registerNewState = { | |
register: cpyRegister.destination, | |
value: copyFrom.value | |
} | |
return { | |
location: state.location + 1, | |
registers: replace(state.registers, registerNewState) | |
} | |
} | |
function processIncOrDec(state: State, register: Global.Register, value: number): State { | |
const registerCurrentState = state.registers.filter(x => x.register === register)[0] | |
const registerNewState = { | |
register, | |
value: registerCurrentState.value + value | |
} | |
return { | |
location: state.location + 1, | |
registers: replace(state.registers, registerNewState) | |
} | |
} | |
function processInc(state: State, inc: Global.Inc): State { | |
return processIncOrDec(state, inc.register, +1) | |
} | |
function processDec(state: State, dec: Global.Dec): State { | |
return processIncOrDec(state, dec.register, -1) | |
} | |
function jumpIfNotZero(state: State, value: number, jump: number): State { | |
if (value !== 0) { | |
return { | |
location: state.location + jump, | |
registers: state.registers | |
} | |
} | |
return { | |
location: state.location + 1, | |
registers: state.registers | |
} | |
} | |
function processJnzRegister(state: State, jnz: Global.JnzRegister): State { | |
const register = state.registers.filter(x => x.register === jnz.register)[0] | |
// Jump if Not Zero (JNZ) | |
return jumpIfNotZero(state, register.value, jnz.jump) | |
} | |
function processJnzValue(state: State, jnz: Global.JnzValue): State { | |
// Jump if Not Zero (JNZ) | |
return jumpIfNotZero(state, jnz.value, jnz.jump) | |
} | |
function prettyPrint(state: State): void { | |
console.log(`Memory location: ${state.location}`) | |
_(state.registers) | |
.sortBy(x => x.register) | |
.each(x => console.log(`Register ${Global.Register[x.register]}: ${x.value}`)) | |
} | |
function process(state: State, instruction: Global.Instruction): State { | |
if (instruction instanceof Global.CpyValue) { | |
return processCpyValue(state, instruction) | |
} else if (instruction instanceof Global.CpyRegister) { | |
return processCpyRegister(state, instruction) | |
} else if (instruction instanceof Global.Inc) { | |
return processInc(state, instruction) | |
} else if (instruction instanceof Global.Dec) { | |
return processDec(state, instruction) | |
} else if (instruction instanceof Global.JnzRegister) { | |
return processJnzRegister(state, instruction) | |
} else if (instruction instanceof Global.JnzValue) { | |
return processJnzValue(state, instruction) | |
} else { | |
throw `Error: couldn't process instruction ${instruction} - type '${typeof(instruction)}'` | |
} | |
} | |
function run(instructions: Global.Instruction[], type: Global.InitType): void { | |
let state = init(type) | |
let locationIndicatingWeShouldTerminate = instructions.length | |
while (state.location < locationIndicatingWeShouldTerminate) { | |
state = process(state, instructions[state.location]) | |
} | |
prettyPrint(state) | |
} | |
export = run | |
//------------------------------------------------ | |
// parse.ts - parse text into Instruction objects | |
import Global = require("./global") | |
function parseCpy(tokens: string[]): Global.CpyRegister|Global.CpyValue { | |
const destination = Global.Register[tokens[2]] | |
const value = Number(tokens[1]) | |
if (!isNaN(value)) { | |
return new Global.CpyValue(value, destination) | |
} else { | |
return new Global.CpyRegister(Global.Register[tokens[1]], destination) | |
} | |
} | |
function parseInc(tokens: string[]): Global.Inc { | |
return new Global.Inc(Global.Register[tokens[1]]) | |
} | |
function parseDec(tokens: string[]): Global.Dec { | |
return new Global.Dec(Global.Register[tokens[1]]) | |
} | |
function parseJnz(tokens: string[]): Global.JnzRegister|Global.JnzValue { | |
const jump = Number(tokens[2]) | |
const value = Number(tokens[1]) | |
if (!isNaN(value)) { | |
return new Global.JnzValue(Number(tokens[1]), jump) | |
} else { | |
return new Global.JnzRegister(Global.Register[tokens[1]], jump) | |
} | |
} | |
function parseLine(line: string): Global.Instruction { | |
const tokens = line.split(" ") | |
if (tokens[0] === "cpy") { | |
return parseCpy(tokens) | |
} else if (tokens[0] === "inc") { | |
return parseInc(tokens) | |
} else if (tokens[0] === "dec") { | |
return parseDec(tokens) | |
} else if (tokens[0] === "jnz") { | |
return parseJnz(tokens) | |
} else { | |
throw `Error: couldn't parse line as its first token is unrecognized. Line: '${line}'` | |
} | |
} | |
function parse(text: string): Global.Instruction[] { | |
const lines = text.split("\n") | |
.map(x => x.trim()) | |
.filter(x => x !== "") | |
return lines.map(x => parseLine(x)) | |
} | |
export = parse | |
//------------------------------------------------ | |
// global.ts - shared types (only types, no shared functions) | |
// Part A or Part B | |
export enum InitType { | |
a, | |
b | |
} | |
export enum Register { | |
a, | |
b, | |
c, | |
d | |
} | |
export class CpyRegister { | |
private _source: Register | |
private _destination: Register | |
get source(): Register { return this._source } | |
get destination(): Register { return this._destination } | |
public constructor(source: Register, destination: Register) { | |
this._source = source | |
this._destination = destination | |
} | |
} | |
export class CpyValue { | |
private _value: number | |
private _destination: Register | |
get value(): number { return this._value } | |
get destination(): Register { return this._destination } | |
public constructor(value: number, destination: Register) { | |
this._value = value | |
this._destination = destination | |
} | |
} | |
export class Inc { | |
private _register: Register | |
get register(): Register { return this._register } | |
public constructor(register: Register) { | |
this._register = register | |
} | |
} | |
export class Dec { | |
private _register: Register | |
get register(): Register { return this._register } | |
public constructor(register: Register) { | |
this._register = register | |
} | |
} | |
export class JnzValue { | |
private _value: number | |
private _jump: number | |
get value(): Register { return this._value } | |
get jump(): Register { return this._jump } | |
public constructor(value: number, jump: number) { | |
this._value = value | |
this._jump = jump | |
} | |
} | |
export class JnzRegister { | |
private _register: Register | |
private _jump: number | |
get register(): Register { return this._register } | |
get jump(): Register { return this._jump } | |
public constructor(register: Register, jump: number) { | |
this._register = register | |
this._jump = jump | |
} | |
} | |
export type Instruction = CpyRegister|CpyValue|Inc|Dec|JnzRegister|JnzValue | |
export interface RegisterState { | |
register: Register, | |
value: number | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment