🎮ArcadeLab

Tron Imitator Reet

by HyperFlare25
477 lines13.2 KB
▶ Play
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tron Imitator Reet</title>
<style>
  * { margin: 0; padding: 0; box-sizing: border-box; }
  html, body {
    width: 100%; height: 100%;
    background: #000;
    overflow: hidden;
    font-family: 'Courier New', monospace;
    color: #0ff;
  }
  #gameCanvas {
    display: block;
    width: 100vw;
    height: 100vh;
    background: #000814;
  }
  #ui {
    position: fixed;
    top: 10px; left: 10px; right: 10px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    pointer-events: none;
    z-index: 10;
    text-shadow: 0 0 8px #0ff;
    font-size: 14px;
  }
  #ui > div { pointer-events: auto; }
  #title {
    font-size: 20px;
    letter-spacing: 3px;
    color: #0ff;
  }
  #langBtn {
    background: transparent;
    border: 1px solid #0ff;
    color: #0ff;
    padding: 6px 12px;
    cursor: pointer;
    font-family: inherit;
    letter-spacing: 2px;
    transition: all 0.2s;
  }
  #langBtn:hover {
    background: #0ff;
    color: #000;
    box-shadow: 0 0 15px #0ff;
  }
  #score {
    color: #ff0;
    text-shadow: 0 0 8px #ff0;
  }
  /* Overlay menu */
  .overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 8, 20, 0.92);
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    z-index: 100;
    backdrop-filter: blur(4px);
  }
  .overlay h1 {
    font-size: 54px;
    letter-spacing: 8px;
    color: #0ff;
    text-shadow: 0 0 20px #0ff, 0 0 40px #0ff;
    margin-bottom: 10px;
    animation: glow 2s ease-in-out infinite alternate;
  }
  .overlay h2 {
    font-size: 18px;
    color: #ff0;
    letter-spacing: 4px;
    margin-bottom: 40px;
    text-shadow: 0 0 10px #ff0;
  }
  @keyframes glow {
    from { text-shadow: 0 0 15px #0ff, 0 0 30px #0ff; }
    to   { text-shadow: 0 0 25px #0ff, 0 0 50px #0ff, 0 0 70px #08f; }
  }
  .btn {
    background: transparent;
    border: 2px solid #0ff;
    color: #0ff;
    padding: 14px 40px;
    margin: 10px;
    font-family: inherit;
    font-size: 18px;
    letter-spacing: 3px;
    cursor: pointer;
    transition: all 0.25s;
    text-shadow: 0 0 5px #0ff;
  }
  .btn:hover {
    background: #0ff;
    color: #000;
    box-shadow: 0 0 25px #0ff;
    text-shadow: none;
  }
  .btn.red { border-color: #f06; color: #f06; text-shadow: 0 0 5px #f06; }
  .btn.red:hover { background: #f06; color: #000; box-shadow: 0 0 25px #f06; }
  .info {
    margin-top: 30px;
    font-size: 13px;
    color: #888;
    letter-spacing: 2px;
    text-align: center;
    line-height: 1.8;
  }
  .hidden { display: none !important; }
  .gameover-title { color: #f06; text-shadow: 0 0 20px #f06, 0 0 40px #f06; }
  .win-title { color: #0f0; text-shadow: 0 0 20px #0f0, 0 0 40px #0f0; }
</style>
</head>
<body>

<canvas id="gameCanvas"></canvas>

<div id="ui">
  <div id="title">TRON IMITATOR REET</div>
  <div id="score">SCORE: 0</div>
  <button id="langBtn">FR</button>
</div>

<!-- Start menu -->
<div id="startMenu" class="overlay">
  <h1>TRON</h1>
  <h2>IMITATOR REET</h2>
  <button class="btn" id="startBtn">START</button>
  <div class="info" id="infoText">
    Use ARROW KEYS to turn<br>
    Avoid walls and trails<br>
    Beat the AI to win
  </div>
</div>

<!-- Game over menu -->
<div id="endMenu" class="overlay hidden">
  <h1 id="endTitle" class="gameover-title">GAME OVER</h1>
  <h2 id="endSub">Score: 0</h2>
  <button class="btn" id="restartBtn">RESTART</button>
  <button class="btn red" id="menuBtn">MAIN MENU</button>
</div>

<script>
/* =========================================================
   TRON IMITATOR REET
   Full-screen Tron game vs AI, retro-modern neon design
   ========================================================= */

// ---------- Language ----------
const LANG = {
  en: {
    start: "START",
    restart: "RESTART",
    menu: "MAIN MENU",
    gameOver: "GAME OVER",
    youWin: "YOU WIN",
    score: "Score",
    info: "Use ARROW KEYS to turn<br>Avoid walls and trails<br>Beat the AI to win",
    langBtn: "FR"
  },
  fr: {
    start: "JOUER",
    restart: "REJOUER",
    menu: "MENU PRINCIPAL",
    gameOver: "PERDU",
    youWin: "VICTOIRE",
    score: "Score",
    info: "Utilisez les FLÈCHES pour tourner<br>Évitez les murs et les traînées<br>Battez l'IA pour gagner",
    langBtn: "EN"
  }
};
let currentLang = 'en';

function applyLang() {
  const L = LANG[currentLang];
  document.getElementById('startBtn').textContent = L.start;
  document.getElementById('restartBtn').textContent = L.restart;
  document.getElementById('menuBtn').textContent = L.menu;
  document.getElementById('infoText').innerHTML = L.info;
  document.getElementById('langBtn').textContent = L.langBtn;
}

document.getElementById('langBtn').addEventListener('click', () => {
  currentLang = currentLang === 'en' ? 'fr' : 'en';
  applyLang();
});

// ---------- Canvas setup ----------
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');

let W = 0, H = 0;
const CELL = 10; // tile size in pixels
let COLS = 0, ROWS = 0;

function resize() {
  W = canvas.width  = window.innerWidth;
  H = canvas.height = window.innerHeight;
  COLS = Math.floor(W / CELL);
  ROWS = Math.floor(H / CELL);
}
window.addEventListener('resize', resize);
resize();

// ---------- Game state ----------
const STATE = { MENU: 0, PLAYING: 1, END: 2 };
let state = STATE.MENU;

// Directions: 0=up, 1=right, 2=down, 3=left
const DX = [0, 1, 0, -1];
const DY = [-1, 0, 1, 0];

let player, ai, grid, score, tickInterval, tickTimer;
const TICK_MS = 55; // movement speed (lower = faster)

function initGame() {
  // Grid: 0 = empty, 1 = player trail, 2 = AI trail
  grid = new Uint8Array(COLS * ROWS);

  // Place player on left side, facing right
  const px = Math.floor(COLS * 0.25);
  const py = Math.floor(ROWS * 0.5);
  player = { x: px, y: py, dir: 1, nextDir: 1, alive: true, color: '#0ff' };

  // Place AI on right side, facing left
  const ax = Math.floor(COLS * 0.75);
  const ay = Math.floor(ROWS * 0.5);
  ai = { x: ax, y: ay, dir: 3, nextDir: 3, alive: true, color: '#f06' };

  // Mark starting cells
  grid[py * COLS + px] = 1;
  grid[ay * COLS + ax] = 2;

  score = 0;
  document.getElementById('score').textContent = 'SCORE: 0';
}

// ---------- Input ----------
const keyMap = {
  'ArrowUp': 0, 'ArrowDown': 2, 'ArrowLeft': 3, 'ArrowRight': 1,
  'w': 0, 's': 2, 'a': 3, 'd': 1,
  'W': 0, 'S': 2, 'A': 3, 'D': 1
};

window.addEventListener('keydown', (e) => {
  if (state !== STATE.PLAYING) return;
  const d = keyMap[e.key];
  if (d === undefined) return;
  // Prevent 180° reversal
  const opposite = (player.dir + 2) % 4;
  if (d !== opposite) player.nextDir = d;
  e.preventDefault();
});

// ---------- AI logic ----------
// Simple but smart AI: look ahead, avoid collisions, prefer direction toward player
function chooseAIDir() {
  const options = [];
  for (let d = 0; d < 4; d++) {
    // no 180° reversal
    if (d === (ai.dir + 2) % 4) continue;
    const nx = ai.x + DX[d];
    const ny = ai.y + DY[d];
    if (nx < 0 || ny < 0 || nx >= COLS || ny >= ROWS) continue;
    if (grid[ny * COLS + nx] !== 0) continue;
    options.push(d);
  }
  if (options.length === 0) return ai.dir; // dead anyway

  // Score each option: prefer direction that moves toward player,
  // and also look 2 steps ahead for safety
  let best = options[0];
  let bestScore = -Infinity;
  for (const d of options) {
    const nx = ai.x + DX[d];
    const ny = ai.y + DY[d];
    // distance to player (lower = better)
    const dist = Math.abs(nx - player.x) + Math.abs(ny - player.y);
    let s = -dist;

    // safety: count free cells in the direction 2 steps ahead
    let freeAhead = 0;
    for (let step = 1; step <= 4; step++) {
      const fx = nx + DX[d] * step;
      const fy = ny + DY[d] * step;
      if (fx < 0 || fy < 0 || fx >= COLS || fy >= ROWS) break;
      if (grid[fy * COLS + fx] !== 0) break;
      freeAhead++;
    }
    s += freeAhead * 5;

    // slight randomness to avoid predictability
    s += Math.random() * 2;

    if (s > bestScore) { bestScore = s; best = d; }
  }
  return best;
}

// ---------- Game tick ----------
function tick() {
  if (state !== STATE.PLAYING) return;

  // Apply queued directions
  player.dir = player.nextDir;
  ai.dir = chooseAIDir();
  ai.nextDir = ai.dir;

  // Compute new positions
  const npx = player.x + DX[player.dir];
  const npy = player.y + DY[player.dir];
  const nax = ai.x + DX[ai.dir];
  const nay = ai.y + DY[ai.dir];

  // Collision checks
  const playerHitsWall = npx < 0 || npy < 0 || npx >= COLS || npy >= ROWS;
  const aiHitsWall     = nax < 0 || nay < 0 || nax >= COLS || nay >= ROWS;
  const playerHitsTrail = !playerHitsWall && grid[npy * COLS + npx] !== 0;
  const aiHitsTrail     = !aiHitsWall && grid[nay * COLS + nax] !== 0;

  // Head-on collision
  const headOn = (npx === nax && npy === nay);

  const playerDead = playerHitsWall || playerHitsTrail || headOn;
  const aiDead     = aiHitsWall || aiHitsTrail || headOn;

  if (playerDead || aiDead) {
    endGame(playerDead && !aiDead ? false : (aiDead && !playerDead ? true : false));
    return;
  }

  // Move and paint trails
  player.x = npx; player.y = npy;
  ai.x = nax;     ai.y = nay;
  grid[npy * COLS + npx] = 1;
  grid[nay * COLS + nax] = 2;

  score++;
  document.getElementById('score').textContent = 'SCORE: ' + score;
}

// ---------- Rendering ----------
function draw() {
  // Background
  ctx.fillStyle = '#000814';
  ctx.fillRect(0, 0, W, H);

  // Grid (tiling)
  ctx.strokeStyle = 'rgba(0, 255, 255, 0.08)';
  ctx.lineWidth = 1;
  ctx.beginPath();
  for (let x = 0; x <= COLS; x++) {
    ctx.moveTo(x * CELL + 0.5, 0);
    ctx.lineTo(x * CELL + 0.5, ROWS * CELL);
  }
  for (let y = 0; y <= ROWS; y++) {
    ctx.moveTo(0, y * CELL + 0.5);
    ctx.lineTo(COLS * CELL, y * CELL + 0.5);
  }
  ctx.stroke();

  if (state === STATE.PLAYING || state === STATE.END) {
    // Draw trails with glow
    // Player trail (cyan)
    ctx.fillStyle = '#0ff';
    ctx.shadowColor = '#0ff';
    ctx.shadowBlur = 8;
    for (let i = 0; i < grid.length; i++) {
      if (grid[i] === 1) {
        const x = (i % COLS) * CELL;
        const y = Math.floor(i / COLS) * CELL;
        ctx.fillRect(x + 1, y + 1, CELL - 2, CELL - 2);
      }
    }
    // AI trail (pink/red)
    ctx.fillStyle = '#f06';
    ctx.shadowColor = '#f06';
    for (let i = 0; i < grid.length; i++) {
      if (grid[i] === 2) {
        const x = (i % COLS) * CELL;
        const y = Math.floor(i / COLS) * CELL;
        ctx.fillRect(x + 1, y + 1, CELL - 2, CELL - 2);
      }
    }
    ctx.shadowBlur = 0;

    // Heads (brighter)
    if (player.alive) {
      ctx.fillStyle = '#fff';
      ctx.shadowColor = '#0ff';
      ctx.shadowBlur = 15;
      ctx.fillRect(player.x * CELL, player.y * CELL, CELL, CELL);
    }
    if (ai.alive) {
      ctx.fillStyle = '#fff';
      ctx.shadowColor = '#f06';
      ctx.shadowBlur = 15;
      ctx.fillRect(ai.x * CELL, ai.y * CELL, CELL, CELL);
    }
    ctx.shadowBlur = 0;
  }

  requestAnimationFrame(draw);
}

// ---------- Game flow ----------
function startGame() {
  initGame();
  state = STATE.PLAYING;
  document.getElementById('startMenu').classList.add('hidden');
  document.getElementById('endMenu').classList.add('hidden');
  clearInterval(tickTimer);
  tickTimer = setInterval(tick, TICK_MS);
}

function endGame(playerWon) {
  state = STATE.END;
  clearInterval(tickTimer);
  player.alive = playerWon;
  ai.alive = !playerWon;

  const L = LANG[currentLang];
  const title = document.getElementById('endTitle');
  const sub = document.getElementById('endSub');
  if (playerWon) {
    title.textContent = L.youWin;
    title.className = 'win-title';
  } else {
    title.textContent = L.gameOver;
    title.className = 'gameover-title';
  }
  sub.textContent = L.score + ': ' + score;
  document.getElementById('endMenu').classList.remove('hidden');

  // Popunder: open a hidden tab behind the current one (non-blocking)
  tryPopunder();
}

function tryPopunder() {
  // Attempts a popunder; modern browsers may block it.
  // We use a tiny hidden window blurred behind the current one.
  try {
    const w = window.open('', '_blank',
      'width=1,height=1,left=0,top=9999,toolbar=0,location=0,menubar=0,status=0');
    if (w) {
      w.document.write('<title>Tron Imitator Reet</title>');
      w.blur();
      window.focus();
    }
  } catch (e) { /* ignored */ }
}

// ---------- Buttons ----------
document.getElementById('startBtn').addEventListener('click', startGame);
document.getElementById('restartBtn').addEventListener('click', startGame);
document.getElementById('menuBtn').addEventListener('click', () => {
  state = STATE.MENU;
  document.getElementById('endMenu').classList.add('hidden');
  document.getElementById('startMenu').classList.remove('hidden');
});

// ---------- Boot ----------
applyLang();
draw();
</script>
</body>
</html>

Game Source: Tron Imitator Reet

Creator: HyperFlare25

Libraries: none

Complexity: complex (477 lines, 13.2 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: tron-imitator-reet-hyperflare25" to link back to the original. Then publish at arcadelab.ai/publish.