diff --git a/Expedience.Web/Expedience.Web.csproj b/Expedience.Web/Expedience.Web.csproj
index 5abeb4c..ae59c19 100644
--- a/Expedience.Web/Expedience.Web.csproj
+++ b/Expedience.Web/Expedience.Web.csproj
@@ -8,6 +8,11 @@
Linux
+
+
+
+
+
diff --git a/Expedience.Web/wwwroot/images/vuln-stack.png b/Expedience.Web/wwwroot/images/vuln-stack.png
new file mode 100644
index 0000000..b3c35bb
Binary files /dev/null and b/Expedience.Web/wwwroot/images/vuln-stack.png differ
diff --git a/Expedience.Web/wwwroot/js/time-bomb.js b/Expedience.Web/wwwroot/js/time-bomb.js
new file mode 100644
index 0000000..83b5a66
--- /dev/null
+++ b/Expedience.Web/wwwroot/js/time-bomb.js
@@ -0,0 +1,389 @@
+const canvas = document.getElementById('gameCanvas');
+const ctx = canvas.getContext('2d');
+
+const arenaSize = 500;
+const subSquareSize = arenaSize / 3;
+const clockRadius = subSquareSize / 9;
+const playerSize = subSquareSize / 10;
+const handLength = clockRadius;
+const triangleOpacity = 0.5;
+
+let rotationStep = 0;
+const maxRotationSteps = 6;
+let rotationStarted = false;
+let rotationComplete = false;
+let randomLayout = Math.floor(Math.random() * 5);
+let lastRotationTime = performance.now();
+let gameStopped = false;
+
+let player = null;
+let clocks = null;
+
+let deathImage = new Image();
+
+// Set the source of the image
+deathImage.src = "../images/vuln-stack.png";
+
+
+function rotateClocks(elapsedTime) {
+ if (rotationStep < maxRotationSteps && elapsedTime >= 1000) {
+ clocks.forEach(clock => clock.rotateHand());
+ rotationStep++;
+ lastRotationTime = performance.now();
+ } else if (rotationStep === maxRotationSteps) {
+ rotationComplete = true;
+ }
+}
+
+function resetRotation() {
+ rotationStep = 0;
+ rotationComplete = false;
+}
+
+class Clock {
+ constructor(x, y, handDirection) {
+ this.x = x;
+ this.y = y;
+ this.handDirection = handDirection;
+ }
+
+ drawArrow() {
+ ctx.lineWidth = 1;
+ ctx.strokeStyle = "black";
+ // Draw the line
+ ctx.beginPath();
+ ctx.moveTo(this.x, this.y);
+ ctx.lineTo(
+ this.x + handLength * Math.cos(this.handDirection * Math.PI / 180),
+ this.y + handLength * Math.sin(this.handDirection * Math.PI / 180)
+ );
+ ctx.stroke();
+
+ // Draw the arrowhead
+ var arrowSize = 7; // Change the value to adjust the size of the arrowhead
+ var arrowAngle = Math.PI / 9; // Change the value to adjust the angle of the arrowhead
+ var angle = Math.atan2(handLength * Math.sin(this.handDirection * Math.PI / 180), handLength * Math.cos(this.handDirection * Math.PI / 180));
+ ctx.beginPath();
+ ctx.moveTo(
+ this.x + handLength * Math.cos(this.handDirection * Math.PI / 180),
+ this.y + handLength * Math.sin(this.handDirection * Math.PI / 180)
+ );
+ ctx.lineTo(
+ this.x + (handLength - arrowSize) * Math.cos(angle - arrowAngle),
+ this.y + (handLength - arrowSize) * Math.sin(angle - arrowAngle)
+ );
+ ctx.lineTo(
+ this.x + (handLength - arrowSize) * Math.cos(angle + arrowAngle),
+ this.y + (handLength - arrowSize) * Math.sin(angle + arrowAngle)
+ );
+ ctx.closePath();
+ ctx.fillStyle = "red"; // Set the fill color to red
+ ctx.fill();
+ }
+
+ draw() {
+ ctx.beginPath();
+ ctx.arc(this.x, this.y, clockRadius, 0, 2 * Math.PI);
+ ctx.fillStyle = "gold";
+ ctx.fill();
+ ctx.strokeStyle = "orange";
+ ctx.lineWidth = 3;
+ ctx.stroke();
+
+ this.drawArrow();
+ ctx.stroke();
+ }
+
+ rotateHand() {
+ this.handDirection = (this.handDirection + 90) % 360;
+ }
+
+drawConal() {
+ const angle = 45;
+ const sideLength = 50;
+
+ ctx.beginPath();
+ ctx.moveTo(this.x, this.y);
+ ctx.lineTo(
+ this.x + sideLength * handLength * Math.cos((this.handDirection - angle) * Math.PI / 180),
+ this.y + sideLength * handLength * Math.sin((this.handDirection - angle) * Math.PI / 180)
+ );
+ ctx.lineTo(
+ this.x + sideLength * handLength * Math.cos((this.handDirection + angle) * Math.PI / 180),
+ this.y + sideLength * handLength * Math.sin((this.handDirection + angle) * Math.PI / 180)
+ );
+ ctx.closePath();
+ ctx.fillStyle = `rgba(255, 255, 0, ${triangleOpacity})`;
+ ctx.fill();
+
+
+ // Draw red lines
+ ctx.beginPath();
+ ctx.moveTo(this.x, this.y);
+ ctx.lineTo(
+ this.x + sideLength * handLength * Math.cos((this.handDirection - angle) * Math.PI / 180),
+ this.y + sideLength * handLength * Math.sin((this.handDirection - angle) * Math.PI / 180)
+ );
+ ctx.strokeStyle = 'red';
+ ctx.stroke();
+
+ ctx.beginPath();
+ ctx.moveTo(this.x, this.y);
+ ctx.lineTo(
+ this.x + sideLength * handLength * Math.cos((this.handDirection + angle) * Math.PI / 180),
+ this.y + sideLength * handLength * Math.sin((this.handDirection + angle) * Math.PI / 180)
+ );
+ ctx.strokeStyle = 'red';
+ ctx.stroke();
+ ctx.strokeStyle = 'black';
+ }
+}
+
+const keysPressed = {
+ ArrowUp: false, W: false, w: false,
+ ArrowLeft: false, A: false, a: false,
+ ArrowDown: false, S: false, s: false,
+ ArrowRight: false, D: false, d: false
+};
+
+class Player {
+ constructor(x, y) {
+ this.x = x;
+ this.y = y;
+ this.hasMoved = false;
+ }
+
+ draw() {
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(this.x - playerSize / 2, this.y - playerSize / 2, playerSize, playerSize);
+ }
+
+ move(dx, dy) {
+ const newX = this.x + dx;
+ const newY = this.y + dy;
+
+ if (newX >= playerSize / 2 && newX <= arenaSize - playerSize / 2) {
+ this.x = newX;
+ this.hasMoved = true;
+ }
+ if (newY >= playerSize / 2 && newY <= arenaSize - playerSize / 2) {
+ this.y = newY;
+ this.hasMoved = true;
+ }
+ }
+}
+
+function getClockPositions(layout) {
+ switch (layout) {
+ case 0:
+ return [
+ new Clock(subSquareSize, subSquareSize, 180),
+ new Clock(arenaSize - subSquareSize, subSquareSize * 2, 0)
+ ];
+
+ case 1:
+ return [
+ new Clock(subSquareSize, subSquareSize, 180),
+ new Clock(subSquareSize, subSquareSize * 2, 180),
+ new Clock(arenaSize - subSquareSize, subSquareSize + (subSquareSize / 2), 0)
+ ];
+
+ case 2:
+ return [
+ new Clock(subSquareSize, subSquareSize, 270),
+ new Clock(subSquareSize, subSquareSize * 2, 90),
+ new Clock(arenaSize - subSquareSize, subSquareSize + (subSquareSize / 2), 180)
+ ];
+
+ case 3:
+ return [
+ new Clock(subSquareSize + (subSquareSize / 2), subSquareSize, 270),
+ new Clock(subSquareSize, subSquareSize * 2, 90),
+ new Clock(arenaSize - subSquareSize, subSquareSize + (subSquareSize / 2), 180)
+ ];
+
+ case 4:
+ return [
+ new Clock(subSquareSize + (subSquareSize / 2), subSquareSize, 0),
+ new Clock(subSquareSize, subSquareSize * 2, 180),
+ new Clock(arenaSize - subSquareSize, subSquareSize + (subSquareSize / 2), 90)
+ ]
+ }
+}
+
+
+function pointInTriangle(px, py, ax, ay, bx, by, cx, cy) {
+ const v0x = cx - ax;
+ const v0y = cy - ay;
+ const v1x = bx - ax;
+ const v1y = by - ay;
+ const v2x = px - ax;
+ const v2y = py - ay;
+
+ const d00 = v0x * v0x + v0y * v0y;
+ const d01 = v0x * v1x + v0y * v1y;
+ const d11 = v1x * v1x + v1y * v1y;
+ const d20 = v2x * v0x + v2y * v0y;
+ const d21 = v2x * v1x + v2y * v1y;
+
+ const denominator = d00 * d11 - d01 * d01;
+ const a = (d11 * d20 - d01 * d21) / denominator;
+ const b = (d00 * d21 - d01 * d20) / denominator;
+
+ return a >= 0 && b >= 0 && a + b <= 1;
+}
+
+function checkCollisions() {
+ const px = player.x;
+ const py = player.y;
+ let collision = false;
+
+ for (const clock of clocks) {
+ const ax = clock.x;
+ const ay = clock.y;
+ const angle = 45;
+ const sideLength = 50;
+
+ const bx = clock.x + sideLength * handLength * Math.cos((clock.handDirection - angle) * Math.PI / 180);
+ const by = clock.y + sideLength * handLength * Math.sin((clock.handDirection - angle) * Math.PI / 180);
+ const cx = clock.x + sideLength * handLength * Math.cos((clock.handDirection + angle) * Math.PI / 180);
+ const cy = clock.y + sideLength * handLength * Math.sin((clock.handDirection + angle) * Math.PI / 180);
+
+ if (pointInTriangle(px, py, ax, ay, bx, by, cx, cy)) {
+ collision = true;
+ break;
+ }
+ }
+
+ return collision;
+}
+
+function displaySafeStatus(isSafe) {
+ if (isSafe)
+ succeeded();
+ else
+ failed();
+}
+
+
+function succeeded() {
+ drawText("SUCCESS!", 'green');
+}
+
+function failed() {
+ ctx.drawImage(deathImage, player.x - playerSize, player.y - playerSize, playerSize * 2, playerSize * 2);
+ drawText("FAILED!", 'red');
+}
+
+function drawText(text, colour) {
+ ctx.font = '35px Verdana'; // Set the font size and type
+ ctx.fillStyle = colour; // Set the fill color
+ ctx.textAlign = 'center'; // Set the horizontal alignment
+ ctx.textBaseline = 'middle'; // Set the vertical alignment
+
+ const x = canvas.width / 2; // Set the x-coordinate of the text
+ const y = 80; // Set the y-coordinate of the text
+
+ ctx.fillText(text, x, y); // Draw the text using fillText()
+}
+
+function gameLoop(currentTime) {
+
+ if (gameStopped) {
+ requestAnimationFrame(gameLoop);
+ return;
+ }
+
+ ctx.fillStyle = "gray";
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+
+ if (rotationStarted === false && player.hasMoved) {
+ rotationStarted = true;
+ }
+
+ const elapsedTime = currentTime - lastRotationTime;
+
+ if (rotationStarted && !rotationComplete) {
+ rotateClocks(elapsedTime);
+ }
+
+ const moveAmount = 4;
+ if (movingUp()) player.move(0, -moveAmount);
+ if (movingDown()) player.move(0, moveAmount);
+ if (movingLeft()) player.move(-moveAmount, 0);
+ if (movingRight()) player.move(moveAmount, 0);
+
+ player.draw();
+ clocks.forEach(clock => {
+ clock.draw();
+ if (rotationComplete) {
+ clock.drawConal();
+ }
+ });
+
+ if (rotationComplete) {
+ const isSafe = !checkCollisions();
+ displaySafeStatus(isSafe);
+ stopGame();
+ }
+
+ requestAnimationFrame(gameLoop);
+}
+
+function movingUp() { return keysPressed.ArrowUp || keysPressed.W || keysPressed.w; }
+function movingDown() { return keysPressed.ArrowDown || keysPressed.S || keysPressed.s; }
+function movingLeft() { return keysPressed.ArrowLeft || keysPressed.A || keysPressed.a; }
+function movingRight() { return keysPressed.ArrowRight || keysPressed.D || keysPressed.d; }
+
+function startGame() {
+ gameStopped = false;
+ rotationStarted = false;
+ rotationComplete = false;
+
+ player = new Player(arenaSize / 2, arenaSize / 2);
+ clocks = getClockPositions(randomLayout);
+}
+
+function stopGame() {
+ gameStopped = true;
+}
+
+function generateNewLayout() {
+ let newLayout;
+ do {
+ newLayout = Math.floor(Math.random() * 5);
+ } while (newLayout === randomLayout);
+ randomLayout = newLayout;
+}
+
+
+function keydownHandler(event) {
+ if (keysPressed.hasOwnProperty(event.key)) {
+ keysPressed[event.key] = true;
+ }
+}
+
+function keyupHandler(event) {
+ if (keysPressed.hasOwnProperty(event.key)) {
+ keysPressed[event.key] = false;
+ }
+}
+
+document.addEventListener('keydown', keydownHandler);
+document.addEventListener('keyup', keyupHandler);
+
+document.getElementById("resetButton").addEventListener("click", function() {
+ stopGame();
+ resetRotation();
+ generateNewLayout();
+ startGame();
+});
+
+document.getElementById("retryButton").addEventListener("click", function() {
+ stopGame();
+ resetRotation();
+ startGame();
+});
+
+startGame();
+gameLoop();
diff --git a/Expedience.Web/wwwroot/time-bomb.html b/Expedience.Web/wwwroot/time-bomb.html
new file mode 100644
index 0000000..63436b1
--- /dev/null
+++ b/Expedience.Web/wwwroot/time-bomb.html
@@ -0,0 +1,53 @@
+
+
+
+
+
+ Time Bomb
+
+
+
+ AVOID THE TIME BOMBS
+
+
Control the player with WSAD or Arrow keys. The clocks will start ticking as soon as the player moves.
+
+
+
+
+
+
+
+
+