🔒 Level 3 — Random Values in JavaScript

Welcome to the final chamber!
In this level, we’ll explore how random values work in JavaScript.


🎲 Math.random()

  • Math.random() generates a decimal in [0, 1).
  • Multiply by a range, then floor it to get integers.

Example: roll a die

Math.floor(Math.random() * 6) + 1; // → 1 to 6 inclusive

Example 1: Random decimal in [0, 1)

%%javascript
// Roll a die: random integer between 1 and 6
let roll = Math.floor(Math.random() * 6) + 1;
console.log("You rolled:", roll);
<IPython.core.display.Javascript object>

Example 2: Random Choice from a List

%%javascript
// Pick a random fruit from a list
let fruits = ["apple", "banana", "cherry", "grape"];
let index = Math.floor(Math.random() * fruits.length);
console.log("Random fruit:", fruits[index]);
<IPython.core.display.Javascript object>

Example 3: Shuffle a List

%%javascript
// Shuffle numbers using a simple swap method
let numbers = [1, 2, 3, 4, 5];

// Fisher-Yates shuffle
for (let i = numbers.length - 1; i > 0; i--) {
  let j = Math.floor(Math.random() * (i + 1));
  let temp = numbers[i];
  numbers[i] = numbers[j];
  numbers[j] = temp;
}

console.log("Shuffled:", numbers);
<IPython.core.display.Javascript object>

🧱 Final Lock — Build the Spinner

Goal: Assemble the blocks to build a correct JavaScript algorithm for a weighted spinner with 8 equal slices:

  • Green: 3/8
  • Blue: 2/8
  • Red: 1/8
  • Orange: 1/8
  • Purple: 1/8

How it should work

  1. Generate an integer from 1 to 8 (inclusive) with JavaScript: ```js let spin = Math.floor(Math.random() * 8) + 1;
%%html 

<div id="l3-sim" style="margin:18px 0; padding:16px; border:1px solid rgba(255,255,255,.12); border-radius:14px; background:linear-gradient(180deg,#101635,#0b1026);">
  <h3 style="margin:0 0 12px;">🔒 Final Lock  Build the Spinner Algorithm (JS)</h3>
  <p style="margin:6px 0 12px; color:#cfe7ff">
    Drag the blocks into the <strong>Assembly Area</strong> (top  bottom). Then press <em>Check Structure</em>. If correct, you can run the simulation.
  </p>

  <div style="display:grid; gap:12px; grid-template-columns:1fr 1fr;">
    <!-- Palette -->
    <div>
      <h4 style="margin:0 0 8px;">🧰 Block Palette</h4>
      <div id="palette" style="min-height:240px; padding:10px; border-radius:10px; background:rgba(255,255,255,.04); display:grid; gap:8px;">
        <!-- Blocks injected by JS -->
      </div>
      <button id="reset" style="margin-top:10px;"> Reset Blocks</button>
    </div>

    <!-- Assembly -->
    <div>
      <h4 style="margin:0 0 8px;">🧱 Assembly Area (top executes first)</h4>
      <div id="assembly" style="min-height:240px; padding:10px; border-radius:10px; background:rgba(255,255,255,.06); outline:2px dashed rgba(255,255,255,.18); display:grid; gap:8px;"></div>

      <div style="display:flex; gap:8px; margin-top:10px;">
        <button id="check"> Check Structure</button>
        <button id="run" disabled> Run 10,000 Spins</button>
      </div>

      <div id="feedback" style="margin-top:8px; font-weight:600;"></div>
      <div id="results" style="margin-top:10px;"></div>
    </div>
  </div>

  <style>
  /* General text inside the activity */
  #l3-sim, 
  #l3-sim h3, 
  #l3-sim h4, 
  #l3-sim p, 
  #l3-sim code, 
  #l3-sim div, 
  #l3-sim span {
    color: #e9eefc; /* bright off-white */
  }

  /* Buttons */
  #l3-sim button {
    padding:6px 10px;
    border-radius:6px;
    border:1px solid rgba(255,255,255,.25);
    background:rgba(255,255,255,.06);
    color:#e9eefc;
    cursor:pointer;
  }
  #l3-sim button:hover {
    background: rgba(255,255,255,.12);
  }
  #l3-sim button[disabled] {
    opacity:.5; 
    cursor:not-allowed;
  }

  /* Blocks */
  #l3-sim .block {
    user-select:none; cursor:grab; padding:8px 10px; border-radius:8px;
    border:1px solid rgba(255,255,255,.18); 
    background:rgba(255,255,255,.08);
    color:#e9eefc;
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
    display:flex; align-items:center; gap:8px;
  }
  #l3-sim .block:active { cursor:grabbing; }

  /* Block tag badges */
  #l3-sim .tag {
    font-size:.75rem; 
    padding:2px 6px; 
    border-radius:999px; 
    background:rgba(255,255,255,.15);
    color:#cfe7ff;
  }

  /* Feedback */
  #l3-sim .ok { color:#7CFC00; }
  #l3-sim .bad { color:#FF6B6B; }

  /* Results pills */
  #l3-sim .pill { 
    display:inline-block; 
    padding:2px 8px; 
    border-radius:999px; 
    background:rgba(255,255,255,.12); 
    color:#e9eefc;
    margin-right:6px; 
  }

  /* Indentation for nested-style blocks */
  #l3-sim .indent-1 { margin-left:16px; }
  #l3-sim .indent-2 { margin-left:32px; }
  </style>

  <script>
  (function(){
    // ---- Target algorithm order (IDs must match):
    // 1) assign, 2) ifG, 3) elifB, 4) elifR, 5) elifO, 6) elseP
    const EXPECT = ["assign","ifG","elifB","elifR","elifO","elseP"];

    const BLOCKS = [
      // Core blocks (now shown in JavaScript syntax)
      { id:"assign", text:"let spin = Math.floor(Math.random() * 8) + 1;", tag:"SET", indent:0 },
      { id:"ifG",   text:"if (spin <= 3) { display('Green'); }",                tag:"IF",      indent:0 },
      { id:"elifB", text:"else if (spin === 4 || spin === 5) { display('Blue'); }", tag:"ELSE IF", indent:0 },
      { id:"elifR", text:"else if (spin === 6) { display('Red'); }",            tag:"ELSE IF", indent:0 },
      { id:"elifO", text:"else if (spin === 7) { display('Orange'); }",         tag:"ELSE IF", indent:0 },
      { id:"elseP", text:"else { display('Purple'); }",                         tag:"ELSE",    indent:0 },

      // Decoys (JS syntax but subtly wrong to prompt thinking)
      { id:"decoy1", text:"if (spin < 3) { display('Green'); }",                tag:"IF",      indent:0 },       // uses < instead of <=
      { id:"decoy2", text:"else if (spin >= 6) { display('Purple'); }",         tag:"ELSE IF", indent:0 }        // collapses too many cases
    ];

    const palette  = document.getElementById("palette");
    const assembly = document.getElementById("assembly");
    const feedback = document.getElementById("feedback");
    const results  = document.getElementById("results");
    const checkBtn = document.getElementById("check");
    const runBtn   = document.getElementById("run");
    const resetBtn = document.getElementById("reset");

    // Create a draggable block element
    function makeBlock(b){
      const el = document.createElement("div");
      el.className = "block " + (b.indent?("indent-"+b.indent):"");
      el.draggable = true;
      el.dataset.blockId = b.id;
      el.innerHTML = `<span class="tag">${b.tag}</span><span>${b.text}</span>`;
      el.addEventListener("dragstart", function(ev){
        ev.dataTransfer.setData("text/plain", b.id);
        ev.dataTransfer.setData("from", el.parentElement.id);
      });
      return el;
    }

    function renderPalette(){
      palette.innerHTML = "";
      BLOCKS.forEach(function(b){ palette.appendChild(makeBlock(b)); });
    }

    function renderAssemblyPlaceholders(){
      assembly.innerHTML = "";
      assembly.addEventListener("dragover", function(ev){ ev.preventDefault(); });
      assembly.addEventListener("drop", onDropAssembly);
    }

    function onDropAssembly(ev){
      ev.preventDefault();
      const id = ev.dataTransfer.getData("text/plain");
      const from = ev.dataTransfer.getData("from");
      if(!id) return;

      // Prevent duplicates of core blocks when dragged from palette
      if(from==="palette" && Array.from(assembly.querySelectorAll(".block")).some(function(el){ return el.dataset.blockId===id; })){
        return;
      }
      const def = BLOCKS.find(function(b){ return b.id===id; });
      if(!def) return;

      if(from==="assembly"){
        const node = Array.from(assembly.children).find(function(el){ return el.dataset.blockId===id; });
        if(node){ assembly.removeChild(node); assembly.appendChild(node); }
      } else {
        assembly.appendChild(makeBlock(def));
      }
      feedback.textContent = "";
      results.innerHTML = "";
      runBtn.disabled = true;
    }

    // Allow dropping back into palette to remove from assembly
    palette.addEventListener("dragover", function(ev){ ev.preventDefault(); });
    palette.addEventListener("drop", function(ev){
      ev.preventDefault();
      const id = ev.dataTransfer.getData("text/plain");
      const from = ev.dataTransfer.getData("from");
      if(from==="assembly"){
        const node = Array.from(assembly.children).find(function(el){ return el.dataset.blockId===id; });
        if(node) assembly.removeChild(node);
      }
      feedback.textContent = "";
      results.innerHTML = "";
      runBtn.disabled = true;
    });

    // Structure checker
    checkBtn.onclick = function(){
      const ids = Array.from(assembly.querySelectorAll(".block")).map(function(el){ return el.dataset.blockId; });
      const exact = JSON.stringify(ids) === JSON.stringify(EXPECT);
      if(ids.length === 0){
        feedback.innerHTML = `<span class="bad">Add blocks to the Assembly Area first.</span>`;
        runBtn.disabled = true;
        return;
      }
      if(exact){
        feedback.innerHTML = `<span class="ok">Perfect structure! You can now run the simulation.</span>`;
        runBtn.disabled = false;
      } else {
        // Targeted hints (JS wording)
        var hint = "";
        if(!ids.includes("assign")) hint = "Start with <code>let spin = Math.floor(Math.random() * 8) + 1;</code>";
        else if(ids[0] !== "assign") hint = "The <code>let spin = ...</code> assignment block must be first.";
        else if(!ids.includes("ifG")) hint = "Add the <code>if (spin <= 3)</code> block for Green right after the assignment.";
        else if(ids.indexOf("elifB") < ids.indexOf("ifG")) hint = "The Blue check must come after the Green check.";
        else hint = "Order should be: assign → if(Green) → else if(Blue) → else if(Red) → else if(Orange) → else(Purple).";
        feedback.innerHTML = `<span class="bad">Not quite.</span> ${hint}`;
        runBtn.disabled = true;
      }
      results.innerHTML = "";
    };

    // Simulation: 10k spins, show empirical percentages
    runBtn.onclick = function(){
      const trials = 10000;
      const counts = {Green:0, Blue:0, Red:0, Orange:0, Purple:0};
      for(var i=0;i<trials;i++){
        var spin = Math.floor(Math.random()*8)+1; // 1..8
        var color = "";
        if(spin <= 3) color = "Green";
        else if (spin===4 || spin===5) color = "Blue";
        else if (spin===6) color = "Red";
        else if (spin===7) color = "Orange";
        else color = "Purple";
        counts[color]++;
      }
      function pct(c){ return (c/trials*100).toFixed(1) + "%"; }
      results.innerHTML = `
        <div><span class="pill">Green</span> ${pct(counts.Green)} (target 37.5%)</div>
        <div><span class="pill">Blue</span> ${pct(counts.Blue)} (target 25.0%)</div>
        <div><span class="pill">Red</span> ${pct(counts.Red)} (target 12.5%)</div>
        <div><span class="pill">Orange</span> ${pct(counts.Orange)} (target 12.5%)</div>
        <div><span class="pill">Purple</span> ${pct(counts.Purple)} (target 12.5%)</div>
        <div style="margin-top:8px; opacity:.85;">(Targets are slices/8: 3/8, 2/8, 1/8, 1/8, 1/8)</div>
      `;
    };

    resetBtn.onclick = function(){
      renderPalette();
      renderAssemblyPlaceholders();
      feedback.textContent = "";
      results.innerHTML = "";
      runBtn.disabled = true;
    };

    // Initial render
    renderPalette();
    renderAssemblyPlaceholders();
  })();
  </script>
</div>


🔒 Final Lock — Build the Spinner Algorithm (JS)

Drag the blocks into the Assembly Area (top → bottom). Then press Check Structure. If correct, you can run the simulation.

🧰 Block Palette

🧱 Assembly Area (top executes first)


⬅️ Back to Lobby