summaryrefslogtreecommitdiff
path: root/2026/games/gbjk/index.html
diff options
context:
space:
mode:
authorLLLL Colonq <llll@colonq>2026-08-26 23:47:59 -0400
committerLLLL Colonq <llll@colonq>2026-08-26 23:47:59 -0400
commit0d2d8cd897e94bd94e0b3ae7c6ce9aa580a44a0a (patch)
treee9039d1c3aedca515d6cb499451d4b73aca4f5fd /2026/games/gbjk/index.html
parent242943e5b023ce40eb3be826c418a478d4672f78 (diff)
Add a_tension_span
Diffstat (limited to '2026/games/gbjk/index.html')
-rw-r--r--2026/games/gbjk/index.html456
1 files changed, 0 insertions, 456 deletions
diff --git a/2026/games/gbjk/index.html b/2026/games/gbjk/index.html
deleted file mode 100644
index 0e43bab..0000000
--- a/2026/games/gbjk/index.html
+++ /dev/null
@@ -1,456 +0,0 @@
-<!doctype html>
-<html lang="en">
- <head>
- <meta charset="UTF-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
- <title>tortis</title>
- <script type="module" crossorigin>//#region \0vite/modulepreload-polyfill.js
-(function polyfill() {
- const relList = document.createElement("link").relList;
- if (relList && relList.supports && relList.supports("modulepreload")) return;
- for (const link of document.querySelectorAll("link[rel=\"modulepreload\"]")) processPreload(link);
- new MutationObserver((mutations) => {
- for (const mutation of mutations) {
- if (mutation.type !== "childList") continue;
- for (const node of mutation.addedNodes) if (node.tagName === "LINK" && node.rel === "modulepreload") processPreload(node);
- }
- }).observe(document, {
- childList: true,
- subtree: true
- });
- function getFetchOpts(link) {
- const fetchOpts = {};
- if (link.integrity) fetchOpts.integrity = link.integrity;
- if (link.referrerPolicy) fetchOpts.referrerPolicy = link.referrerPolicy;
- if (link.crossOrigin === "use-credentials") fetchOpts.credentials = "include";
- else if (link.crossOrigin === "anonymous") fetchOpts.credentials = "omit";
- else fetchOpts.credentials = "same-origin";
- return fetchOpts;
- }
- function processPreload(link) {
- if (link.ep) return;
- link.ep = true;
- const fetchOpts = getFetchOpts(link);
- fetch(link.href, fetchOpts);
- }
-})();
-//#endregion
-//#region src/setup.ts
-var WIDTH = 160;
-var HEIGHT = 240;
-var GRID_HEIGHT = HEIGHT - 10;
-var ROWS = 15;
-var COLS = 10;
-var GRID_WIDTH = GRID_HEIGHT / ROWS * 10;
-var GRID_X = 5;
-var GRID_Y = 5;
-var MARGIN = 2;
-var CELL_WIDTH = GRID_HEIGHT / ROWS - MARGIN;
-var FALLTIME = 600;
-var MIN_FALLTIME = 300;
-var rotation = 0;
-var board = [];
-function resetBoard() {
- board.length = 0;
- for (let height = 0; height < ROWS; height++) {
- const row = [];
- for (let width = 0; width < COLS; width++) row.push("void");
- board.push(row);
- }
-}
-resetBoard();
-function random(elements) {
- return elements[Math.floor(Math.random() * elements.length)];
-}
-var addedShapeNumber = 1;
-function addSquares() {
- const color = random([
- "fallingRed",
- "fallingGreen",
- "fallingBlue"
- ]);
- let w = COLS / 2;
- let h = 1;
- for (let i = 0; i < 4; i++) {
- board[h][w] = color;
- if (i === 3) break;
- let move = null;
- let j = 0;
- for (; j < 20; j++) {
- move = addedShapeNumber % 6 === 0 ? "down" : random([
- "up",
- "down",
- "left",
- "right"
- ]);
- if (move === "up" && (h === 0 || board[h - 1][w] !== "void")) continue;
- if (move === "down" && (h === ROWS - 1 || board[h + 1][w] !== "void")) continue;
- if (move === "left" && (w === 0 || board[h][w - 1] !== "void")) continue;
- if (move === "right" && (w === COLS - 1 || board[h][w + 1] !== "void")) continue;
- break;
- }
- if (j === 20) return false;
- switch (move) {
- case "up":
- h--;
- break;
- case "down":
- h++;
- break;
- case "left":
- w--;
- break;
- case "right":
- w++;
- break;
- }
- }
- addedShapeNumber++;
- return true;
-}
-function clearFilledRows() {
- let lastClearedRow = ROWS;
- let clearedRows = 0;
- for (let row = ROWS - 1; row >= 0; row--) if (!board[row].some((cell) => cell === "void" || cell === "fallingBlue" || cell === "fallingGreen" || cell === "fallingRed")) {
- for (let col = 0; col < COLS; col++) board[row][col] = "void";
- lastClearedRow = row;
- clearedRows++;
- } else if (lastClearedRow !== ROWS) for (let col = 0; col < COLS; col++) switch (board[row][col]) {
- case "red":
- board[row][col] = "fallingRed";
- break;
- case "green":
- board[row][col] = "fallingGreen";
- break;
- case "blue":
- board[row][col] = "fallingBlue";
- break;
- }
- return clearedRows;
-}
-function setup(elements) {
- const ctx = elements.main.getContext("2d");
- let gameRunning = false;
- let animationFrameId = null;
- let lastFrame = 0;
- let fallTime = FALLTIME;
- let speedUpPressed = false;
- elements.left.addEventListener("click", () => moveFalling("left"));
- elements.right.addEventListener("click", () => moveFalling("right"));
- elements.rotateLeft.addEventListener("click", () => {
- if (!gameRunning) return;
- rotation -= 30 * Math.random();
- elements.main.style.transform = `rotate(${rotation}deg)`;
- });
- elements.rotateRight.addEventListener("click", () => {
- if (!gameRunning) return;
- rotation += 30 * Math.random();
- elements.main.style.transform = `rotate(${rotation}deg)`;
- });
- elements.speedUp.addEventListener("mousedown", () => speedUpPressed = gameRunning);
- elements.speedUp.addEventListener("mouseup", () => speedUpPressed = false);
- window.addEventListener("message", (ev) => {
- const message = ev.data;
- if (!isStartMessage(message)) return;
- startGame(message.difficulty);
- });
- window.parent.postMessage({ op: "ready" }, "*");
- draw();
- function isStartMessage(message) {
- if (typeof message !== "object" || message === null) return false;
- const candidate = message;
- return candidate.op === "start" && typeof candidate.difficulty === "number" && Number.isFinite(candidate.difficulty);
- }
- function resetGame(difficulty) {
- resetBoard();
- addedShapeNumber = 1;
- fallTime = Math.max(MIN_FALLTIME, FALLTIME - Math.max(0, difficulty) * 5);
- rotation = 0;
- elements.main.style.transform = "";
- speedUpPressed = false;
- lastFrame = 0;
- }
- function startGame(difficulty) {
- if (animationFrameId !== null) {
- cancelAnimationFrame(animationFrameId);
- animationFrameId = null;
- }
- resetGame(difficulty);
- gameRunning = true;
- window.parent.postMessage({
- op: "started",
- verb: "catch!"
- }, "*");
- animationFrameId = requestAnimationFrame(gameLoop);
- }
- function finishGame(win) {
- if (!gameRunning) return;
- gameRunning = false;
- if (animationFrameId !== null) {
- cancelAnimationFrame(animationFrameId);
- animationFrameId = null;
- }
- window.parent.postMessage({
- op: "done",
- win
- }, "*");
- resetGame(0);
- draw();
- }
- function moveFalling(move) {
- if (!gameRunning) return;
- switch (move) {
- case "left":
- for (let row = 0; row < ROWS; row++) for (let col = 0; col < COLS - 1; col++) if (board[row][col] === "void") switch (board[row][col + 1]) {
- case "fallingRed":
- case "fallingGreen":
- case "fallingBlue":
- board[row][col] = board[row][col + 1];
- board[row][col + 1] = "void";
- }
- break;
- case "right":
- for (let row = 0; row < ROWS; row++) for (let col = COLS - 1; col > 0; col--) if (board[row][col] === "void") switch (board[row][col - 1]) {
- case "fallingRed":
- case "fallingGreen":
- case "fallingBlue":
- board[row][col] = board[row][col - 1];
- board[row][col - 1] = "void";
- }
- break;
- }
- }
- function physics() {
- let somethingFell = false;
- for (let row = ROWS - 1; row >= 1; row--) for (let col = 0; col < COLS; col++) {
- let space = board[row][col];
- let above = board[row - 1][col];
- switch (above) {
- case "fallingRed":
- case "fallingGreen":
- case "fallingBlue": if (space === "void") {
- somethingFell = true;
- board[row][col] = board[row - 1][col];
- board[row - 1][col] = "void";
- } else switch (above) {
- case "fallingRed":
- board[row - 1][col] = "red";
- break;
- case "fallingGreen":
- board[row - 1][col] = "green";
- break;
- case "fallingBlue":
- board[row - 1][col] = "blue";
- break;
- }
- }
- }
- for (let col = 0; col < COLS; col++) switch (board[ROWS - 1][col]) {
- case "fallingRed":
- board[ROWS - 1][col] = "red";
- break;
- case "fallingGreen":
- board[ROWS - 1][col] = "green";
- break;
- case "fallingBlue":
- board[ROWS - 1][col] = "blue";
- break;
- }
- return somethingFell;
- }
- function draw() {
- ctx.clearRect(0, 0, WIDTH, HEIGHT);
- ctx.fillStyle = "black";
- ctx.fillRect(GRID_X, GRID_Y, GRID_WIDTH, GRID_HEIGHT);
- for (let h = 0; h < ROWS; h++) for (let w = 0; w < COLS; w++) {
- switch (board[h][w]) {
- case "void":
- ctx.fillStyle = "#2e2e2e";
- break;
- case "red":
- ctx.fillStyle = "#994444";
- break;
- case "green":
- ctx.fillStyle = "#449944";
- break;
- case "blue":
- ctx.fillStyle = "#444499";
- break;
- case "fallingRed":
- ctx.fillStyle = "#bb3333";
- break;
- case "fallingGreen":
- ctx.fillStyle = "#33bb33";
- break;
- case "fallingBlue":
- ctx.fillStyle = "#3333bb";
- break;
- }
- const x = 8 + w * CELL_WIDTH + (w - 1) * MARGIN;
- const y = 8 + h * CELL_WIDTH + (h - 1) * MARGIN;
- ctx.fillRect(x, y, CELL_WIDTH, CELL_WIDTH);
- }
- }
- function gameLoop(time) {
- animationFrameId = null;
- if (!gameRunning) return;
- if (time - lastFrame > (speedUpPressed ? fallTime / 10 : fallTime)) {
- lastFrame = time;
- if (!physics()) {
- const clearedRows = clearFilledRows();
- if (clearedRows >= 4) {
- finishGame(true);
- return;
- } else if (clearedRows === 0) {
- if (!addSquares()) {
- finishGame(false);
- return;
- }
- }
- }
- }
- draw();
- animationFrameId = requestAnimationFrame(gameLoop);
- }
-}
-//#endregion
-//#region src/main.ts
-document.querySelector("#app").innerHTML = `
- <div class="layout">
-
- <div class="game">
- <div class="title-bar">
- <h1>Tortis</h1>
- <p>(4row clear to win)</p>
- </div>
-
- <canvas height="240" width="160" id="main"></canvas>
- </div>
-
- <ul>
- <button id="move-left">move left</button>
- <button id="rotate-left">rotate left</button>
- <button id="move-right">move right</button>
- <button id="rotate-right">rotate right</button>
- <button id="speed-up">speed up</button>
- </ul>
- </div>
-`;
-setup({
- main: document.querySelector("#main"),
- left: document.querySelector("#move-left"),
- right: document.querySelector("#move-right"),
- rotateLeft: document.querySelector("#rotate-left"),
- rotateRight: document.querySelector("#rotate-right"),
- speedUp: document.querySelector("#speed-up")
-});
-//#endregion</script>
- <style rel="stylesheet" crossorigin>* {
- box-sizing: border-box;
-}
-
-html,
-body,
-#app {
- min-height: 100%;
-}
-
-body {
- margin: 0;
- color: darksalmon;
- background-color: #2e2e2e;
- font-family: monospace;
- overflow-x: hidden;
-}
-
-button {
- min-width: 0;
- font: inherit;
- font-size: .4rem;
- line-height: 1.1;
- padding: .15rem;
- color: darksalmon;
- background-color: #2e2e2e;
- border-color: salmon;
- cursor: pointer;
- white-space: normal;
- overflow-wrap: anywhere;
-
- z-index: 9999;
-}
-
-button:hover {
- background-color: darkslategray;
-}
-
-button:active {
- background-color: slategray;
-}
-
-.layout {
- margin: auto;
- display: flex;
- min-height: 100dvh;
- width: 100%;
- padding: .15rem;
- gap: .2rem;
- align-items: center;
- justify-content: center;
- overflow: hidden;
-}
-
-.game {
- min-width: 0;
-}
-
-.title-bar {
- display: flex;
- align-items: baseline;
- gap: .25rem;
- margin-bottom: .2rem;
-}
-
-h1,
-p {
- margin: 0;
-}
-
-h1 {
- font-size: .6rem;
- line-height: 1;
-}
-
-p {
- font-size: .4rem;
- line-height: 1.1;
-}
-
-canvas {
- display: block;
- aspect-ratio: 2 / 3;
- border-radius: 6px;
- border: 1px solid darksalmon;
- box-shadow: 0px 0px 4px darksalmon;
- height: 80vmin;
- width: auto;
-
- transition: transform 100ms;
- margin: auto;
-}
-
-ul {
- display: flex;
- justify-content: center;
- flex-direction: column;
- gap: .15rem;
- width: min(6rem, 42vw);
- margin: 0;
- padding: 0;
- list-style: none;
-}
-/*$vite$:1*/</style>
- </head>
- <body>
- <div id="app"></div>
- </body>
-</html>