687 lines
17 KiB
TypeScript
687 lines
17 KiB
TypeScript
namespace AdventOfCode2019_17_2
|
|
{
|
|
const DAY = 17;
|
|
const PROBLEM = 2;
|
|
|
|
export async function run()
|
|
{
|
|
let input = await AdventOfCode.getInput(DAY);
|
|
if (input == null) return;
|
|
|
|
const code = input.trim().split(",").map(p => parseInt(p.trim()));
|
|
|
|
let [map, maxx, maxy] = await getMap(code);
|
|
|
|
const fullpath = getFullPath(map, maxx, maxy);
|
|
|
|
let proginput = await splitPath(fullpath, "y");
|
|
|
|
code[0]=2;
|
|
let rnr = new Interpreter(code, proginput);
|
|
while(!rnr.is_halted && (rnr.output.length < 0 || rnr.output[0] !== "?".charCodeAt(0))) { rnr.output=[]; rnr.singleStep(); }
|
|
while(!rnr.is_halted && (rnr.output.length < 0 || rnr.output[0] !== ".".charCodeAt(0))) { rnr.output=[]; rnr.singleStep(); }
|
|
while(!rnr.is_halted)
|
|
{
|
|
rnr.singleStep();
|
|
|
|
if (rnr.output.length === (maxx+1)*(maxy+1)+1)
|
|
{
|
|
let str = rnr.output.map(p => String.fromCharCode(p)).reduce((a,b)=>a+b);
|
|
await AdventOfCode.outputIntermed(str);
|
|
await AdventOfCode.sleepIfIntermed(10);
|
|
rnr.output = [];
|
|
|
|
AdventOfCode.outputConsole(str);
|
|
AdventOfCode.outputConsole("---------------NEXT------------");
|
|
}
|
|
}
|
|
|
|
AdventOfCode.outputConsole(rnr.output.map(p => String.fromCharCode(p)).reduce((a,b)=>a+b));
|
|
|
|
|
|
AdventOfCode.output(DAY, PROBLEM, rnr.output[0].toString());
|
|
}
|
|
|
|
async function splitPath(fullpath: RoboCommand[], live: string): Promise<number[]>
|
|
{
|
|
let allsplits: { [pos:number]: [ number, RoboCommand[], number[] ][] } = {};
|
|
for(let i=0; i<fullpath.length; i++) allsplits[i] = [];
|
|
|
|
let splitmap: { [pos:number]: [ RoboCommand[], number[] ] } = {};
|
|
|
|
let splitid = 10000;
|
|
for(let splitlen = 10; splitlen>0;splitlen--)
|
|
{
|
|
let localfounds: Set<number> = new Set<number>();
|
|
for (let start=0; start <= fullpath.length-splitlen; start++)
|
|
{
|
|
if (localfounds.has(start)) continue;
|
|
|
|
let occurences = findInPath(fullpath, start, splitlen);
|
|
if (occurences.length === 1) continue;
|
|
|
|
for(const occ of occurences) localfounds.add(occ);
|
|
|
|
let localsplit = fullpath.slice(start, start+splitlen);
|
|
|
|
let localsplitstr = localsplit.map(p=>p.toString()).reduce((a,b)=>a+","+b);
|
|
if (localsplitstr.length>20) continue;
|
|
|
|
let r: [ number, RoboCommand[], number[] ] = [ splitid++, localsplit, occurences ];
|
|
|
|
splitmap[r[0]] = [ r[1], r[2] ];
|
|
|
|
AdventOfCode.outputConsole(`||(${r[0]}) ${occurences.length} x [${localsplitstr}] --> [${occurences.map(p=>p.toString()).reduce((a,b)=>a+";"+b)}]`);
|
|
|
|
for(const occ of occurences) allsplits[occ].push(r);
|
|
}
|
|
}
|
|
|
|
// ------------
|
|
|
|
let sf = new Splitfinder();
|
|
sf.allsplits = allsplits;
|
|
sf.fulllength = fullpath.length;
|
|
|
|
sf.run();
|
|
|
|
let result = "";
|
|
let intresult = [];
|
|
|
|
let progmap: {[_:number]: string} = {};
|
|
let first = true;
|
|
let rc0: RoboCommand[] = [];
|
|
let rc1: RoboCommand[] = [];
|
|
let rc2: RoboCommand[] = [];
|
|
for(const comp of sf.results[0])
|
|
{
|
|
let id = "";
|
|
if (comp in progmap) id = progmap[comp];
|
|
else
|
|
{
|
|
if (Object.entries(progmap).length==0) { id="A"; rc0 = splitmap[comp][0]; }
|
|
else if (Object.entries(progmap).length==1) { id="B"; rc1 = splitmap[comp][0]; }
|
|
else if (Object.entries(progmap).length==2) { id="C"; rc2 = splitmap[comp][0]; }
|
|
progmap[comp] = id;
|
|
}
|
|
|
|
if (!first) { result += ","; intresult.push(44); }
|
|
result += id;
|
|
intresult.push(id.charCodeAt(0));
|
|
first = false;
|
|
}
|
|
|
|
result += "\n";
|
|
intresult.push(10);
|
|
|
|
let str0 = rc0.map(p => p.toString()).reduce((a,b)=>a+","+b);
|
|
result += str0+"\n";
|
|
str0.split('').forEach(p => intresult.push(p.charCodeAt(0)));
|
|
intresult.push(10);
|
|
|
|
let str1 = rc1.map(p => p.toString()).reduce((a,b)=>a+","+b);
|
|
result += str1+"\n";
|
|
str1.split('').forEach(p => intresult.push(p.charCodeAt(0)));
|
|
intresult.push(10);
|
|
|
|
let str2 = rc2.map(p => p.toString()).reduce((a,b)=>a+","+b);
|
|
result += str2+"\n";
|
|
str2.split('').forEach(p => intresult.push(p.charCodeAt(0)));
|
|
intresult.push(10);
|
|
|
|
result += live + "\n";
|
|
intresult.push(live.charCodeAt(0));
|
|
intresult.push(10);
|
|
|
|
AdventOfCode.outputConsole(result);
|
|
|
|
return intresult;
|
|
}
|
|
|
|
class Splitfinder
|
|
{
|
|
allsplits: { [pos:number]: [ number, RoboCommand[], number[] ][] } = {}; // pos => [ id, path, starts ]
|
|
fulllength: number = 0;
|
|
|
|
results: number[][] = [];
|
|
|
|
path: number[] = [];
|
|
used_segments: [number, number][] = [];
|
|
position = 0;
|
|
|
|
run()
|
|
{
|
|
// abort if we finished
|
|
if (this.position === this.fulllength)
|
|
{
|
|
this.results.push(Object.assign([], this.path));
|
|
AdventOfCode.outputConsole(">>> " + this.path.map(p => "["+p.toString()+"]").reduce((a,b)=>a+","+b) )
|
|
return;
|
|
}
|
|
|
|
for(const seg of this.allsplits[this.position])
|
|
{
|
|
// ======== add to used_segments ========
|
|
let added = false;
|
|
for(let i=0; i<this.used_segments.length; i++)
|
|
{
|
|
if (this.used_segments[i][0] === seg[0])
|
|
{
|
|
this.used_segments[i][1]++;
|
|
added = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!added)
|
|
{
|
|
if (this.used_segments.length === 3) continue;
|
|
|
|
this.used_segments.push([seg[0], 1]);
|
|
}
|
|
|
|
// ======== add to path ========
|
|
this.path.push(seg[0]);
|
|
|
|
// ======== inc pos ========
|
|
this.position += seg[1].length;
|
|
|
|
// ======== recursion ========
|
|
this.run();
|
|
|
|
// ======== dec pos ========
|
|
this.position -= seg[1].length;
|
|
|
|
// ======== rem from path ========
|
|
this.path.pop();
|
|
|
|
// ======== rem from used_segments ========
|
|
let rem_us = false;
|
|
for(let i=0; i<this.used_segments.length; i++)
|
|
{
|
|
if (this.used_segments[i][0] === seg[0])
|
|
{
|
|
this.used_segments[i][1]--;
|
|
if (this.used_segments[i][1] === 0) rem_us = true;
|
|
break;
|
|
}
|
|
}
|
|
if (rem_us)
|
|
{
|
|
this.used_segments = this.used_segments.filter(p => p[1] > 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function findInPath(fullpath: RoboCommand[], start: number, len: number): number[]
|
|
{
|
|
let r = [start];
|
|
for (let s = start+len; s<=fullpath.length-len;s++)
|
|
{
|
|
if (isPathEqual(fullpath, start, s, len)) r.push(s);
|
|
}
|
|
return r;
|
|
}
|
|
|
|
function isPathEqual(path: RoboCommand[], start1: number, start2: number, len: number)
|
|
{
|
|
for (let i=0; i<len; i++)
|
|
{
|
|
if (!(path[start1+i].eq(path[start2+i]))) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function getFullPath(map: {[_:number]: number}, maxx: number, maxy: number)
|
|
{
|
|
let robox = -1;
|
|
let roboy = -1;
|
|
let robod = Direction.North;
|
|
|
|
for(let yy=0;yy<=maxy;yy++)
|
|
for(let xx=0;xx<=maxx;xx++)
|
|
{
|
|
let id = (yy*1_0000 + xx);
|
|
if (map[id] === 94) { robox=xx; roboy=yy; robod=Direction.North; break; }
|
|
if (map[id] === 62) { robox=xx; roboy=yy; robod=Direction.East; break; }
|
|
if (map[id] === 60) { robox=xx; roboy=yy; robod=Direction.West; break; }
|
|
if (map[id] === 118) { robox=xx; roboy=yy; robod=Direction.South; break; }
|
|
}
|
|
|
|
let result: RoboCommand[] = [];
|
|
let pos: [number, number] = [robox, roboy];
|
|
let dir = robod;
|
|
|
|
let moves = 0;
|
|
for(;;)
|
|
{
|
|
if (isScaffolding(map, moveDir(pos, dir)))
|
|
{
|
|
moves++;
|
|
pos = moveDir(pos, dir);
|
|
}
|
|
else if (isScaffolding(map, moveDir(pos, turnLeft(dir))))
|
|
{
|
|
if (moves > 0) result.push(new RoboCommand(RoboCommandType.Move, moves));
|
|
moves = 0;
|
|
result.push(new RoboCommand(RoboCommandType.Left, 1));
|
|
|
|
dir = turnLeft(dir);
|
|
}
|
|
else if (isScaffolding(map, moveDir(pos, turnRight(dir))))
|
|
{
|
|
if (moves > 0) result.push(new RoboCommand(RoboCommandType.Move, moves));
|
|
moves = 0;
|
|
result.push(new RoboCommand(RoboCommandType.Right, 1));
|
|
|
|
dir = turnRight(dir);
|
|
}
|
|
else
|
|
{
|
|
if (moves > 0) result.push(new RoboCommand(RoboCommandType.Move, moves));
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
AdventOfCode.outputConsole(result.map(p => p.toString()).reduce((a,b)=> a+","+b));
|
|
|
|
return result;
|
|
}
|
|
|
|
function moveDir(pos: [number, number], dir: Direction): [number, number]
|
|
{
|
|
return move(pos, dirToVec(dir))
|
|
}
|
|
|
|
function move(pos: [number, number], vec: [number, number]): [number, number]
|
|
{
|
|
return [ pos[0]+vec[0], pos[1]+vec[1] ];
|
|
}
|
|
|
|
function isScaffolding(map: {[_:number]: number}, pos: [number, number])
|
|
{
|
|
let id = (pos[1]*1_0000 + pos[0]);
|
|
return map[id] === 35;
|
|
}
|
|
|
|
class RoboCommand
|
|
{
|
|
cmd: RoboCommandType = RoboCommandType.Move;
|
|
len: number = 0;
|
|
|
|
constructor(c: RoboCommandType, l: number)
|
|
{
|
|
this.cmd = c;
|
|
this.len = l;
|
|
}
|
|
|
|
eq(o: RoboCommand)
|
|
{
|
|
if (this.cmd !== o.cmd) return false;
|
|
if (this.len !== o.len) return false;
|
|
return true;
|
|
}
|
|
|
|
toString(): string
|
|
{
|
|
if (this.cmd === RoboCommandType.Left) return "L";
|
|
if (this.cmd === RoboCommandType.Right) return "R";
|
|
if (this.cmd === RoboCommandType.Move) return ""+this.len;
|
|
|
|
throw "??";
|
|
}
|
|
}
|
|
|
|
enum RoboCommandType { Left, Right, Move }
|
|
|
|
enum Direction
|
|
{
|
|
North = 1,
|
|
South = 2,
|
|
West = 3,
|
|
East = 4,
|
|
}
|
|
|
|
function dirToVec(d: Direction): [number, number]
|
|
{
|
|
if (d === Direction.North) return [0, -1];
|
|
if (d === Direction.East) return [+1, 0];
|
|
if (d === Direction.South) return [0, +1];
|
|
if (d === Direction.West) return [-1, 0];
|
|
|
|
throw "-.-";
|
|
}
|
|
|
|
function turnRight(d: Direction): Direction
|
|
{
|
|
if (d === Direction.North) return Direction.East;
|
|
if (d === Direction.East) return Direction.South;
|
|
if (d === Direction.South) return Direction.West;
|
|
if (d === Direction.West) return Direction.North;
|
|
|
|
throw "dr";
|
|
}
|
|
|
|
function turnLeft(d: Direction): Direction
|
|
{
|
|
if (d === Direction.North) return Direction.West;
|
|
if (d === Direction.East) return Direction.North;
|
|
if (d === Direction.South) return Direction.East;
|
|
if (d === Direction.West) return Direction.South;
|
|
|
|
throw "dl";
|
|
}
|
|
|
|
async function getMap(code: number[]): Promise<[{[_:number]: number}, number, number]>
|
|
{
|
|
let rnr = new Interpreter(code, []);
|
|
|
|
let map: {[_:number]: number} = {};
|
|
let maxx = 0;
|
|
let maxy = 0;
|
|
let out = "";
|
|
let ipx=0;
|
|
let ipy=0
|
|
while (!rnr.is_halted)
|
|
{
|
|
rnr.singleStep();
|
|
if (rnr.output.length>0)
|
|
{
|
|
const v = rnr.output.pop()!;
|
|
out += String.fromCharCode(v);
|
|
//await AdventOfCode.outputIntermed(out);
|
|
|
|
if (v === 10)
|
|
{
|
|
ipy++;
|
|
ipx=0;
|
|
}
|
|
else
|
|
{
|
|
// AdventOfCode.outputConsole(`# [${ipx}|${ipy}] = ${v}`);
|
|
|
|
map[ipy*1_0000 + ipx] = v;
|
|
ipx++;
|
|
|
|
maxx = Math.max(maxx, ipx);
|
|
maxy = Math.max(maxy, ipy);
|
|
}
|
|
}
|
|
}
|
|
|
|
await AdventOfCode.outputIntermed(out);
|
|
|
|
for(let yy=-1;yy<=maxy+1;yy++)
|
|
for(let xx=-1;xx<=maxx+1;xx++)
|
|
{
|
|
if (!((yy*1_0000 + xx) in map)) map[yy*1_0000 + xx] = 46;
|
|
}
|
|
|
|
return [map, maxx, maxy];
|
|
}
|
|
|
|
class Interpreter
|
|
{
|
|
program: InfMem;
|
|
inputqueue: number[];
|
|
instructionpointer: number;
|
|
output: number[];
|
|
relative_base: number;
|
|
|
|
is_halted: boolean = false;
|
|
|
|
constructor(prog: number[], input: number[])
|
|
{
|
|
this.program = new InfMem(prog);
|
|
this.inputqueue = input;
|
|
this.instructionpointer = 0;
|
|
this.output = [];
|
|
this.relative_base = 0;
|
|
}
|
|
|
|
fullRun() : number[]
|
|
{
|
|
while(!this.is_halted)
|
|
{
|
|
const r = this.singleStep();
|
|
|
|
if (r === StepResult.EXECUTED) continue;
|
|
if (r === StepResult.HALTED) return this.output;
|
|
if (r === StepResult.WAITING_FOR_IN) throw "not enough input";
|
|
|
|
throw "unknown output of singleStep";
|
|
}
|
|
|
|
return this.output;
|
|
}
|
|
|
|
autoRun() : StepResult
|
|
{
|
|
while(!this.is_halted)
|
|
{
|
|
const r = this.singleStep();
|
|
|
|
if (r === StepResult.EXECUTED) continue;
|
|
if (r === StepResult.HALTED) return StepResult.HALTED;
|
|
if (r === StepResult.WAITING_FOR_IN) return StepResult.WAITING_FOR_IN;
|
|
|
|
throw "unknown output of singleStep";
|
|
}
|
|
|
|
return StepResult.HALTED;
|
|
}
|
|
|
|
singleStep() : StepResult
|
|
{
|
|
const cmd = new Op(this.program.r(this.instructionpointer));
|
|
|
|
if (cmd.opcode == OpCode.ADD)
|
|
{
|
|
const p0 = cmd.getParameter(this, 0);
|
|
const p1 = cmd.getParameter(this, 1);
|
|
const pv = p0 + p1;
|
|
cmd.setParameter(this, 2, pv);
|
|
|
|
this.incInstrPtr(cmd);
|
|
|
|
return StepResult.EXECUTED;
|
|
}
|
|
else if (cmd.opcode == OpCode.MUL)
|
|
{
|
|
const p0 = cmd.getParameter(this, 0);
|
|
const p1 = cmd.getParameter(this, 1);
|
|
const pv = p0 * p1;
|
|
cmd.setParameter(this, 2, pv);
|
|
|
|
this.incInstrPtr(cmd);
|
|
|
|
return StepResult.EXECUTED;
|
|
}
|
|
else if (cmd.opcode == OpCode.HALT)
|
|
{
|
|
this.is_halted = true;
|
|
return StepResult.HALTED;
|
|
}
|
|
else if (cmd.opcode == OpCode.IN)
|
|
{
|
|
if (this.inputqueue.length == 0) return StepResult.WAITING_FOR_IN;
|
|
|
|
const pv = this.inputqueue[0];
|
|
cmd.setParameter(this, 0, pv);
|
|
this.inputqueue = this.inputqueue.slice(1);
|
|
|
|
this.incInstrPtr(cmd);
|
|
return StepResult.EXECUTED;
|
|
}
|
|
else if (cmd.opcode == OpCode.OUT)
|
|
{
|
|
const p0 = cmd.getParameter(this, 0);
|
|
this.output.push(p0);
|
|
//AdventOfCode.outputConsole("# " + p0);
|
|
|
|
this.incInstrPtr(cmd);
|
|
|
|
return StepResult.EXECUTED;
|
|
}
|
|
else if (cmd.opcode == OpCode.TJMP)
|
|
{
|
|
const p0 = cmd.getParameter(this, 0);
|
|
if (p0 != 0) this.instructionpointer = cmd.getParameter(this, 1);
|
|
else this.incInstrPtr(cmd);
|
|
|
|
return StepResult.EXECUTED;
|
|
}
|
|
else if (cmd.opcode == OpCode.FJMP)
|
|
{
|
|
const p0 = cmd.getParameter(this, 0);
|
|
if (p0 == 0) this.instructionpointer = cmd.getParameter(this, 1);
|
|
else this.incInstrPtr(cmd);
|
|
|
|
return StepResult.EXECUTED;
|
|
}
|
|
else if (cmd.opcode == OpCode.LT)
|
|
{
|
|
const p0 = cmd.getParameter(this, 0);
|
|
const p1 = cmd.getParameter(this, 1);
|
|
const pv = p0 < p1 ? 1 : 0;
|
|
cmd.setParameter(this, 2, pv);
|
|
|
|
this.incInstrPtr(cmd);
|
|
return StepResult.EXECUTED;
|
|
}
|
|
else if (cmd.opcode == OpCode.EQ)
|
|
{
|
|
const p0 = cmd.getParameter(this, 0);
|
|
const p1 = cmd.getParameter(this, 1);
|
|
const pv = p0 == p1 ? 1 : 0;
|
|
cmd.setParameter(this, 2, pv);
|
|
|
|
this.incInstrPtr(cmd);
|
|
return StepResult.EXECUTED;
|
|
}
|
|
else if (cmd.opcode == OpCode.ARB)
|
|
{
|
|
const p0 = cmd.getParameter(this, 0);
|
|
this.relative_base = this.relative_base+p0;
|
|
|
|
this.incInstrPtr(cmd);
|
|
return StepResult.EXECUTED;
|
|
}
|
|
else throw "Unknown Op: " + cmd.opcode + " @ " + this.instructionpointer;
|
|
}
|
|
|
|
private incInstrPtr(cmd: Op)
|
|
{
|
|
this.instructionpointer += 1 + cmd.parametercount;
|
|
}
|
|
}
|
|
|
|
enum StepResult { EXECUTED, HALTED, WAITING_FOR_IN }
|
|
|
|
enum OpCode
|
|
{
|
|
ADD = 1,
|
|
MUL = 2,
|
|
IN = 3,
|
|
OUT = 4,
|
|
TJMP = 5,
|
|
FJMP = 6,
|
|
LT = 7,
|
|
EQ = 8,
|
|
ARB = 9,
|
|
HALT = 99,
|
|
}
|
|
|
|
enum ParamMode
|
|
{
|
|
POSITION_MODE = 0,
|
|
IMMEDIATE_MODE = 1,
|
|
RELATIVE_MODE = 2,
|
|
}
|
|
|
|
class Op
|
|
{
|
|
opcode: OpCode;
|
|
modes: ParamMode[];
|
|
|
|
name: string;
|
|
parametercount: number;
|
|
|
|
constructor(v: number)
|
|
{
|
|
this.opcode = v%100;
|
|
v = Math.floor(v/100);
|
|
this.modes = [];
|
|
for(let i=0; i<4; i++)
|
|
{
|
|
this.modes.push(v%10);
|
|
v = Math.floor(v/10);
|
|
}
|
|
|
|
if (this.opcode == OpCode.ADD) { this.name="ADD"; this.parametercount=3; }
|
|
else if (this.opcode == OpCode.MUL) { this.name="MUL"; this.parametercount=3; }
|
|
else if (this.opcode == OpCode.HALT) { this.name="HALT"; this.parametercount=0; }
|
|
else if (this.opcode == OpCode.IN) { this.name="IN"; this.parametercount=1; }
|
|
else if (this.opcode == OpCode.OUT) { this.name="OUT"; this.parametercount=1; }
|
|
else if (this.opcode == OpCode.TJMP) { this.name="TJMP"; this.parametercount=2; }
|
|
else if (this.opcode == OpCode.FJMP) { this.name="FJMP"; this.parametercount=2; }
|
|
else if (this.opcode == OpCode.LT) { this.name="LT"; this.parametercount=3; }
|
|
else if (this.opcode == OpCode.EQ) { this.name="EQ"; this.parametercount=3; }
|
|
else if (this.opcode == OpCode.ARB) { this.name="ARB"; this.parametercount=1; }
|
|
else throw "Unknown opcode: "+this.opcode;
|
|
}
|
|
|
|
getParameter(proc: Interpreter, index: number): number
|
|
{
|
|
const prog = proc.program;
|
|
const ip = proc.instructionpointer;
|
|
|
|
let p = prog.r(ip+1+index);
|
|
|
|
if (this.modes[index] == ParamMode.POSITION_MODE) p = prog.r(p);
|
|
else if (this.modes[index] == ParamMode.IMMEDIATE_MODE) p = p;
|
|
else if (this.modes[index] == ParamMode.RELATIVE_MODE) p = prog.r(proc.relative_base+p);
|
|
else throw "Unknown ParamMode: "+this.modes[index];
|
|
|
|
return p;
|
|
}
|
|
|
|
setParameter(proc: Interpreter, index: number, value: number): void
|
|
{
|
|
const prog = proc.program;
|
|
const ip = proc.instructionpointer;
|
|
|
|
let p = prog.r(ip+1+index);
|
|
|
|
if (this.modes[index] == ParamMode.POSITION_MODE) prog.w(p, value);
|
|
else if (this.modes[index] == ParamMode.IMMEDIATE_MODE) throw "Immediate mode not allowed in write";
|
|
else if (this.modes[index] == ParamMode.RELATIVE_MODE) prog.w(proc.relative_base+p, value);
|
|
else throw "Unknown ParamMode: "+this.modes[index];
|
|
}
|
|
}
|
|
|
|
class InfMem
|
|
{
|
|
private data: { [_:number]:number } = {};
|
|
|
|
constructor(v: number[])
|
|
{
|
|
for(let i=0; i<v.length;i++) this.data[i]=v[i];
|
|
}
|
|
|
|
r(pos: number): number
|
|
{
|
|
if (!(pos in this.data)) this.data[pos] = 0;
|
|
return this.data[pos];
|
|
}
|
|
|
|
w(pos: number, val: number): number
|
|
{
|
|
return this.data[pos] = val;
|
|
}
|
|
}
|
|
}
|
|
|
|
|