function updatePassenger(index) { index = Number(index); let passenger = objects.passengers[index]; let row = Number(passenger.row); let col = Number(passenger.col); let state = passenger.state; let queueState = passenger.queueState; let chosenQueue = Number(passenger.chosenQueue); let hasArrived = Math.abs(Number(passenger.targetRow) - row) + Math.abs(Number(passenger.targetCol) - col) == 0; switch (state) { case "immigration": if (queueState == "immigrationQueue" && hasArrived) { if ( col == Number(objects[queueState][chosenQueue].col) + 50 && objects.immigration[chosenQueue].state == "IDLE" ) { passenger.queueState = "none"; passenger.targetRow = Number(objects.immigration[chosenQueue].row) + 3; passenger.targetCol = Number(objects.immigration[chosenQueue].col) - 1; let newStack = Number(objects.immigrationQueue[chosenQueue].stack) - 1; objects.immigrationQueue[chosenQueue].stack = newStack; objects.immigration[chosenQueue].state = "BUSY"; } else if (col < Number(objects[queueState][chosenQueue].col) + 50) { let filledCol = objects.passengers.filter(function (i) { return i.col == col + 1 && i.row == row; }); if (filledCol.length == 0) { passenger.targetCol = col + 1; } } } else if (queueState == "none" && hasArrived) { if (Math.random() < probImmigration) { // TODO Create function callable by the different states. // ? Could probably do a recursive function for this. objects.immigration[chosenQueue].state = "IDLE"; let station = Number(passenger.station); station += 1; let newState = getKeyByValue(positions, station); if (newState == undefined) { newState = getKeyByValue(positions, station + 1); station += 1; if (newState == undefined) { newState = getKeyByValue(positions, station + 1); station += 1; } // Final state if nothing else will be "out". } if (newState == "exiting") { passenger.state = "exiting"; passenger.targetCol = width; passenger.targetRow = height / 2; } else { passenger.state = newState; queueState = newState + "Queue"; passenger.queueState = queueState; function chosenQueueRectifier(chosenQueue) { //* Recursive function when having mismatched station lengths. if (chosenQueue >= objects[queueState].length) { chosenQueue -= 1; return chosenQueueRectifier(chosenQueue); } else { return chosenQueue; } } chosenQueue = chosenQueueRectifier(chosenQueue); for (let i in objects[queueState]) { if (i !== chosenQueue) { if ( Number(objects[queueState][i].stack) < Number(objects[queueState][chosenQueue].stack) ) { chosenQueue = Number(i); } } } if (Math.random() < randomChosenQueue) { chosenQueue = Math.floor( Math.random() * objects[queueState].length ); } let stackOverflow = 0; if (Number(objects[queueState][chosenQueue].stack) > 50) { stackOverflow = Number(objects[queueState][chosenQueue].stack) - 50; } passenger.targetRow = Number(objects[queueState][chosenQueue].row) + 3; passenger.targetCol = Number(objects[queueState][chosenQueue].col) - stackOverflow; passenger.station = station; passenger.chosenQueue = chosenQueue; let newStack = Number(objects[queueState][chosenQueue].stack) + 1; objects[queueState][chosenQueue].stack = newStack; } } } break; case "testing": if (queueState == "testingQueue" && hasArrived) { if ( col == Number(objects[queueState][chosenQueue].col) + 50 && objects.testing[chosenQueue].state == "IDLE" ) { passenger.queueState = "testBox"; passenger.targetRow = Number(objects.testing[chosenQueue].row) + 3; passenger.targetCol = Number(objects.testing[chosenQueue].col) - 1; let newStack = Number(objects.testingQueue[chosenQueue].stack) - 1; objects.testingQueue[chosenQueue].stack = newStack; objects.testing[chosenQueue].state = "BUSY"; } else if (col < Number(objects[queueState][chosenQueue].col) + 50) { let filledCol = objects.passengers.filter(function (i) { return i.col == col + 1 && i.row == row; }); if (filledCol.length == 0) { passenger.targetCol = col + 1; } } } else if (queueState == "testBox" && hasArrived) { if (Math.random() < probTesting) { passenger.queueState = "enteringTestBox"; passenger.targetCol = Number(objects.testingBox[0].col) - 1; objects.testing[chosenQueue].state = "IDLE"; } } else if (queueState == "enteringTestBox" && hasArrived) { let boxWidth = Number(objects.testingBox[0].width); let boxHeight = Number(objects.testingBox[0].height); let boxRow = Number(objects.testingBox[0].row); let boxCol = Number(objects.testingBox[0].col); let newCol; let newRow; let overlapping = true; while (overlapping) { let count = 1; let randRow = Math.floor(Math.random() * boxHeight); let randCol = Math.floor(Math.random() * boxWidth); newRow = boxRow + randRow; newCol = boxCol + randCol; let overlappedList = objects.passengers.filter(function (d) { return d.targetRow == newRow && d.targetCol == newCol; }); if (overlappedList.length == 0) { overlapping = false; } // ! DEFINE BEHAVIOUR WHEN BOX IS FULL (UNLIKELY, BUT STILL). // * FOR NOW, IF BOX IS FULL, ALLOW OVERLAP. if (count > boxWidth * boxHeight) { overlapping = false; } } passenger.targetCol = newCol; passenger.targetRow = newRow; passenger.queueState = "waiting"; } else if (queueState == "waiting" && hasArrived) { passenger.timeWaited = Number(passenger.timeWaited) + 1; if (passenger.timeWaited >= testingTime && passenger.covid) { passenger.state = "covid"; passenger.targetRow = 0; } else if (passenger.timeWaited >= testingTime) { let station = Number(passenger.station); station += 1; let newState = getKeyByValue(positions, station); if (newState == undefined) { newState = getKeyByValue(positions, station + 1); station += 1; if (newState == undefined) { newState = getKeyByValue(positions, station + 1); station += 1; } // Final state if nothing else will be "out". } if (newState == "exiting") { passenger.state = "exiting"; passenger.targetCol = width; passenger.targetRow = height / 2; } else { passenger.state = newState; queueState = newState + "Queue"; passenger.queueState = queueState; function chosenQueueRectifier(chosenQueue) { //* Recursive function when having mismatched station lengths. if (chosenQueue >= objects[queueState].length) { chosenQueue -= 1; return chosenQueueRectifier(chosenQueue); } else { return chosenQueue; } } chosenQueue = chosenQueueRectifier(chosenQueue); for (let i in objects[queueState]) { if (i !== chosenQueue) { if ( Number(objects[queueState][i].stack) < Number(objects[queueState][chosenQueue].stack) ) { chosenQueue = Number(i); } } } if (Math.random() < randomChosenQueue) { chosenQueue = Math.floor( Math.random() * objects[queueState].length ); } let stackOverflow = 0; if (Number(objects[queueState][chosenQueue].stack) > 50) { stackOverflow = Number(objects[queueState][chosenQueue].stack) - 50; } passenger.targetRow = Number(objects[queueState][chosenQueue].row) + 3; passenger.targetCol = Number(objects[queueState][chosenQueue].col) - stackOverflow; passenger.station = station; passenger.chosenQueue = chosenQueue; let newStack = Number(objects[queueState][chosenQueue].stack) + 1; objects[queueState][chosenQueue].stack = newStack; } } } break; case "baggage": if (queueState == "baggageQueue" && hasArrived) { if (col == Number(objects[queueState][chosenQueue].col) + 50) { let newStack = Number(objects.baggageQueue[chosenQueue].stack) - 1; objects.baggageQueue[chosenQueue].stack = newStack; passenger.queueState = "gettingBaggage"; passenger.targetCol = Number(objects.baggage[0].col) - 1; // objects.testing[chosenQueue].state = "BUSY"; } else if (col < Number(objects[queueState][chosenQueue].col) + 50) { let filledCol = objects.passengers.filter(function (i) { return i.col == col + 1 && i.row == row; }); if (filledCol.length == 0) { passenger.targetCol = col + 1; } } } else if (queueState == "gettingBaggage" && hasArrived) { let boxWidth = Number(objects.baggage[0].width); let boxHeight = Number(objects.baggage[0].height); let boxRow = Number(objects.baggage[0].row); let boxCol = Number(objects.baggage[0].col); let newCol; let newRow; let overlapping = true; while (overlapping) { let count = 1; let randRow = Math.floor(Math.random() * boxHeight); let randCol = Math.floor(Math.random() * boxWidth); newRow = boxRow + randRow; newCol = boxCol + randCol; let overlappedList = objects.passengers.filter(function (d) { return d.targetRow == newRow && d.targetCol == newCol; }); if (overlappedList.length == 0) { overlapping = false; } // ! DEFINE BEHAVIOUR WHEN BOX IS FULL (UNLIKELY, BUT STILL). // * FOR NOW, IF BOX IS FULL, ALLOW OVERLAP. if (count > boxWidth * boxHeight) { overlapping = false; } } passenger.targetCol = newCol; passenger.targetRow = newRow; passenger.queueState = "reachedBaggage"; } else if (queueState == "reachedBaggage" && hasArrived) { if (Math.random() < probFindBaggage) { let station = Number(passenger.station); station += 1; let newState = getKeyByValue(positions, station); if (newState == undefined) { newState = getKeyByValue(positions, station + 1); station += 1; if (newState == undefined) { newState = getKeyByValue(positions, station + 1); station += 1; } // Final state if nothing else will be "out". } if (newState == "exiting") { passenger.state = "exiting"; passenger.targetCol = width; passenger.targetRow = height / 2; } else { passenger.state = newState; queueState = newState + "Queue"; passenger.queueState = queueState; function chosenQueueRectifier(chosenQueue) { //* Recursive function when having mismatched station lengths. if (chosenQueue >= objects[queueState].length) { chosenQueue -= 1; return chosenQueueRectifier(chosenQueue); } else { return chosenQueue; } } chosenQueue = chosenQueueRectifier(chosenQueue); for (let i in objects[queueState]) { if (i !== chosenQueue) { if ( Number(objects[queueState][i].stack) < Number(objects[queueState][chosenQueue].stack) ) { chosenQueue = Number(i); } } } if (Math.random() < randomChosenQueue) { chosenQueue = Math.floor( Math.random() * objects[queueState].length ); } let stackOverflow = 0; if (Number(objects[queueState][chosenQueue].stack) > 50) { stackOverflow = Number(objects[queueState][chosenQueue].stack) - 50; } passenger.targetRow = Number(objects[queueState][chosenQueue].row) + 3; passenger.targetCol = Number(objects[queueState][chosenQueue].col) - stackOverflow; passenger.station = station; passenger.chosenQueue = chosenQueue; let newStack = Number(objects[queueState][chosenQueue].stack) + 1; objects[queueState][chosenQueue].stack = newStack; } } else { passenger.queueState = "gettingBaggage"; hasArrived = true; } } break; case "exiting": if (hasArrived) { passenger.state = "out"; exitedPassengers += 1; document.getElementById("numExited").innerHTML = "Number of exited passengers: " + exitedPassengers + "."; listTimeToClear.push(Number(passenger.timeTaken)); newAvg = listTimeToClear.reduce((a, b) => a + b) / listTimeToClear.length; listMeanTimeToClear.push(newAvg); let overallAvg; if (listSimulationMeanRunTime.length > 0) { overallAvg = listSimulationMeanRunTime[listSimulationMeanRunTime.length - 1]; var overallStdDev = Math.sqrt( listSimulationRunTime .map((x) => Math.pow(x - overallAvg, 2)) .reduce((a, b) => a + b) / listSimulationRunTime.length ); } else { overallAvg = 0; var overallStdDev = 0; } document.getElementById("aveAll").innerHTML = "Average time taken by a passenger: " + newAvg.toFixed(2) + ". Average time taken for " + passengerCount + " passenger(s): " + overallAvg.toFixed(2) + " over " + simulationsRan + " simulation(s) with a standard deviation of " + overallStdDev.toFixed(2) + "."; if (isRunning == true) { Plotly.extendTraces("tester", { y: [[getData1()]] }, [0]); cnt++; if (cnt > limit) { Plotly.relayout("tester", { xaxis: { range: [cnt - limit, cnt], }, }); } } } break; case "covid": if (hasArrived) { passenger.state = "exiting"; } } passenger.timeTaken = Number(passenger.timeTaken) + 1; let targetRow = Number(passenger.targetRow); let targetCol = Number(passenger.targetCol); // compute the distance to the target destination let rowsToGo = targetRow - row; let colsToGo = targetCol - col; // set the speed let cellsPerStep = 1; // compute the cell to move to let newRow = row + Math.min(Math.abs(rowsToGo), cellsPerStep) * Math.sign(rowsToGo); let newCol = col + Math.min(Math.abs(colsToGo), cellsPerStep) * Math.sign(colsToGo); // update the location of the passenger passenger.row = newRow; passenger.col = newCol; }