Function crashing node-red - Fatal JavaScript invalid size error

I have a function node containing the following code:

// set number of boxes/prisoners
const MaxPrisoners = 20;

// create PrisonerBoxes array
let PrisonerBoxes = [];

// set which box/prisoner to start from
const startingNumber = msg.payload - 1;

// create Loop array
const Loop = [];

// fill PrisonerBoxes with values
for (let i = 1; i <= MaxPrisoners; i++) {
    PrisonerBoxes.push(i);
};

// shuffle values in array of PrisonerBoxes
let res = PrisonerBoxes.slice(0);
for (let j, x, i = res.length; i; j = Math.floor(Math.random() * i), x = res[--i], res[i] = res[j], res[j] = x);
PrisonerBoxes = res;

// get value in the currently open box
let boxValue = PrisonerBoxes[startingNumber];

// set value of nextBox to open
let nextBoxValue = boxValue;

// iterate through boxes until starting value is found
for (let i = startingNumber; i != boxValue; i = nextBoxValue - 1) {
    if (PrisonerBoxes[i]) {
        Loop.push(PrisonerBoxes[i]);
        nextBoxValue = PrisonerBoxes[i];
        boxValue = PrisonerBoxes[nextBoxValue];
    };  
};

// display the resulting Loop as msg.loop
msg.loop = Loop;

// display the resulting PrisonerBoxes as msg.boxes
msg.boxes = PrisonerBoxes;

// return the msg object
return msg;

Sometimes when it runs I get a return like this:

{
"_msgid":"e03a58c68912c40b",
"payload":5,
"loop":[17,7,2,3,16,20,14,18,6,11,9,15,10],
"boxes":[13,3,16,5,17,11,2,4,15,1,9,12,8,18,10,20,7,6,19,14]
}

But other times I get this in the console output

# Fatal error in , line 0
# Fatal JavaScript invalid size error 169220804
#
#
#
#FailureMessage Object: 00000080594FD2E0
 1: 00007FF747E11B4F v8::internal::CodeObjectRegistry::~CodeObjectRegistry+115135
 2: 00007FF747D2EA2F std::basic_ostream<char,std::char_traits<char> >::operator<<+64591
 3: 00007FF748AF2432 V8_Fatal+162
 4: 00007FF748664D85 v8::internal::FactoryBase<v8::internal::Factory>::NewFixedArray+101
 5: 00007FF74852206E v8::debug::Script::GetIsolate+16350
 6: 00007FF7483937CD v8::internal::CompilationCache::IsEnabledScriptAndEval+27293
 7: 00007FF74883F6A1 v8::internal::SetupIsolateDelegate::SetupHeap+513649
 8: 0000019DF2F6D8B3

I'm still somewhat new to JS and Node-RED, normally errors give me something to go off of, but I'm stumped here. Am I doing something to an array wrong? or is it something else?

Happens on a Raspberry Pi 4, Node-RED 2.2.2 node v16.15.1 npm v8.11.0
and on Windows 10 build 21H2, Node-RED 2.1.4 node v17.6.0 npm v8.3.0

My money is on this part of the code. I believe if PrisonerBoxes[i] is empty or zero, you will have an infinite loop.

I would have thought that an Array filter would have been a lot more efficient here.

Also it is dangerous to test for equality for the end of a loop, just in case you start off the wrong side of the end value. It is safer always to use, for example, <=

Sorry, I should have specified that my inspiration for this exercise is a Veritasium video about the 100 Prisoners Riddle: The Riddle That Seems Impossible Even If You Know The Answer - YouTube

So in trying to recreate the conditions I believe I need to form a loop through iteration.

I see, this sounds like good advice, I will try to implement this from now on.

Here is a low(ish) code version (i'm certain given a little more time I could remove more function code)

image

[{"id":"243a9ac0bbe5e22f","type":"inject","z":"10ece3ab98e93683","name":"Start (shuffle and go)","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":1750,"y":120,"wires":[["637f709011eba368"]]},{"id":"637f709011eba368","type":"function","z":"10ece3ab98e93683","name":"Shuffle Boxes","func":"function shuffle(array) {\n    let currentIndex = array.length, randomIndex;\n    while (currentIndex != 0) {\n        // Pick a remaining element.\n        randomIndex = Math.floor(Math.random() * currentIndex);\n        currentIndex--;\n        [array[currentIndex], array[randomIndex]] = [\n            array[randomIndex], array[currentIndex]];\n    }\n    return array;\n}\n\nconst boxes = []\nfor (let index = 0; index < 100; index++) {\n    boxes[index] = (index+1);\n}\n\nmsg.payload = [null, ...shuffle(boxes)]\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1950,"y":120,"wires":[["cacc208e1ee151c9"]]},{"id":"ce9926bcb0cd73ec","type":"split","z":"10ece3ab98e93683","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":1990,"y":180,"wires":[["ff5fb633b49f7224"]]},{"id":"199b437c3249eff8","type":"join","z":"10ece3ab98e93683","name":"","mode":"auto","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":"false","timeout":"","count":"","reduceRight":false,"x":1810,"y":240,"wires":[["435a284bd18dfd3f"]]},{"id":"ff5fb633b49f7224","type":"function","z":"10ece3ab98e93683","name":"Look for box","func":"\nconst boxes = flow.get(\"boxes\");\nconst prisonerNo = msg.payload;\nlet boxToCheck = prisonerNo;\n\nmsg.payload = false; //assume failure\n\nfor (let index = 0; index < 50; index++) {\n    const inBox = boxes[boxToCheck]\n    if (inBox === prisonerNo) {\n        msg.payload = true\n        break\n    }\n    boxToCheck = inBox;\n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":2150,"y":180,"wires":[["199b437c3249eff8"]]},{"id":"435a284bd18dfd3f","type":"function","z":"10ece3ab98e93683","name":"calc result","func":"const results = msg.payload\nmsg.payload = {}\n\nmsg.payload.results = results.map((e, i) => {\n    return {\n        prisonerNo: i+1,\n        result: e\n    }\n})\n\nmsg.payload.losers = msg.payload.results.filter(e => e.result == false)\nmsg.payload.winners = msg.payload.results.filter(e => e.result == true)\nmsg.payload.prisonerCount = msg.payload.results.length\nmsg.payload.loserCount = msg.payload.losers.length\nmsg.payload.winnerCount = msg.payload.winners.length\nmsg.payload.result = msg.payload.winnerCount === msg.payload.prisonerCount ? \"freedom\" : \"death\"\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1980,"y":240,"wires":[["8533858cd09afb66"]]},{"id":"8533858cd09afb66","type":"debug","z":"10ece3ab98e93683","name":"Life or death","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":2150,"y":240,"wires":[]},{"id":"1186a8b946b1d714","type":"change","z":"10ece3ab98e93683","name":"Lineup prisoners","rules":[{"t":"set","p":"payload","pt":"msg","to":"[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]","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":1810,"y":180,"wires":[["ce9926bcb0cd73ec"]]},{"id":"cacc208e1ee151c9","type":"change","z":"10ece3ab98e93683","name":"","rules":[{"t":"set","p":"boxes","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":2140,"y":120,"wires":[["1186a8b946b1d714"]]}]

Thank you so much!

msg.payload = false; //assume failure

for (let index = 0; index < 50; index++) {
    const inBox = boxes[boxToCheck]
    if (inBox === prisonerNo) {
        msg.payload = true
        break
    }
    boxToCheck = inBox;
}

This part of your code was what I needed to figure out.

I really like knowing this from your code too:

msg.payload = [null, ...shuffle(emptyBoxes)];

Seems like it might make dealing with arrays and off by one math less of a headache.

Excellent advice all around, I truly appreciate the help from you all.

Is there a way to set say msg.payload.loops to an array of each unique loop?

i dont follow. What are you trying to achieve? Do you want to keep multiple set of results? Or do you just want the final result in a certain format?

My goal is in analyzing probabilities of each the loop sizes over multiple runs. As it is currently any time the flow results in freedom then we can conclude no loops exceed 50 in length.

So you want to gather the min/max number of box openings over multiple runs? Something else. Please be specific.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.