-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathdevice.js
185 lines (146 loc) · 4.83 KB
/
device.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/**
* `cpy x y` _copies_ `x` (either an integer or the _value_ of a register) into register `y`.
* `inc x` _increases_ the value of register `x` by one.
* `dec x` _decreases_ the value of register `x` by one.
* `jnz x y` _jumps_ to an instruction `y` away (positive means forward; negative means backward), but only if `x` is _not zero_.
*
* `tgl x` _toggles_ the instruction `x` away (pointing at instructions like `jnz` does: positive means forward; negative means backward):
* - For _one-argument_ instructions, `inc` becomes `dec`, and all other one-argument instructions become `inc`.
* - For _two-argument_ instructions, `jnz` becomes `cpy`, and all other two-instructions become `jnz`.
* - The arguments of a toggled instruction are _not affected_.
* - If an attempt is made to toggle an instruction outside the program, _nothing happens_.
* - If toggling produces an _invalid instruction_ (like `cpy 1 2`) and an attempt is later made to execute that instruction, `skip it instead`.
* - If `tgl` toggles _itself_ (for example, if `a` is `0`, `tgl a` would target itself and become `inc a`), the resulting instruction is not executed until the next time it is reached.
*
* `out x` _transmits_ `x` (either an integer or the _value_ of a register) as the next value for the clock signal.
*/
const TOGGLE_TRANSFORMS = {
inc: 'dec',
dec: 'inc',
tgl: 'inc',
jnz: 'cpy',
cpy: 'jnz',
out: 'inc',
};
class Device {
constructor(
program,
starting_registers = {},
// Arbitrary 10 chars, of course this doesn't mean this will go on forever necessarily...
exit_on = '0101010101',
starting_instruction = 0
) {
// Defaults all registers to 0. Allows you to pass in just one register than you want to change
const default_registers = { a: 0, b: 0, c: 0, d: 0 };
starting_registers = Object.assign({}, default_registers, starting_registers);
// Clone the arrays we pass in
this.program = JSON.parse(JSON.stringify(program));
this.registers = JSON.parse(JSON.stringify(starting_registers));
this.instruction = starting_instruction;
this.exit_on = exit_on;
this.signal = '';
this.run = this.run.bind(this);
this.step = this.step.bind(this);
}
printRegisters() {
console.log(this.registers);
}
run(register_to_print = 'a') {
let line = this.program[this.instruction];
while (line) {
let { op, args } = line;
// Run the opcode
this[op].apply(this, args);
line = this.program[this.instruction];
}
return this.registers[register_to_print];
}
step(n = 1) {
for (let i = 0; i < n; i++) {
const { op, args } = this.program[this.instruction];;
// Run the opcode
this[op].apply(this, args);
}
}
/**
* @returns {Boolean} - Returns true if we reached our exit condition, returns false if we can't reach exit condition
*/
runUntilExitCondition() {
while (true) {
const { op } = this.program[this.instruction];
this.step();
// If we just ran `out`, check our signal and see if it matches the exit condition
if (op === 'out') {
if (this.signal === this.exit_on) {
return true;
} else if (this.signal.length > this.exit_on.length) {
return false;
} else {
// The signal is less than our exit string, so check that exit starts with the signla so far
// If it doesn't we can exit early.
if (this.exit_on.indexOf(this.signal) !== 0) {
return false;
}
}
}
}
}
getValueOf(x) {
return typeof x === 'string' ? this.registers[x] : x;
}
/**
* Opcodes
*/
// Copy - copies `x` (either an integer or the value of a register) into register `y`.
cpy(x, y) {
this.instruction++;
// Invalid instruction, skip
if (typeof y !== 'string') {
return;
}
this.registers[y] = this.getValueOf(x);
return this.registers;
}
// Increase - increases the value of register `x` by one.
inc(x) {
this.registers[x] += 1;
this.instruction++;
return this.registers;
}
// Decrease - decreases the value of register `x` by one.
dec(x) {
this.registers[x] -= 1;
this.instruction++;
return this.registers;
}
// Jump - jumps to an instruction `y` away (positive means forward; negative means backward), but only if `x` is not zero.
jnz(x, y) {
x = this.getValueOf(x);
y = this.getValueOf(y);
if (x === 0) {
// Then just move forward as normal
this.instruction++;
} else {
// Otherwise, jump our instruction
this.instruction += y;
}
return this.registers;
}
// Toggle
tgl(x) {
x = this.getValueOf(x);
let instruction_to_modify = this.instruction + x;
if (this.program[instruction_to_modify]) {
let current_op = this.program[instruction_to_modify].op;
this.program[instruction_to_modify].op = TOGGLE_TRANSFORMS[current_op];
}
this.instruction++;
}
// Transmit out to our device's "signal"
out(x) {
x = this.getValueOf(x);
this.signal += x;
this.instruction++;
}
}
module.exports = Device;