🗝️ Level 2 — Randomness in Python

The second chamber glows faintly, with glowing serpents (🐍) carved into the walls.
You’ll learn how Python generates random values and use it to simulate coins, dice, and more.


📘 Python’s random Module

Python provides randomness with the random module.
We’ll mainly use:

  • random.randint(a, b) → random integer between a and b, inclusive
  • random.choice(list) → pick a random element from a list
  • random.shuffle(list) → shuffle a list in place

🔑 Note: random.randint(a,b) behaves exactly like AP CSP’s RANDOM(a,b).

Demo: random.randint

Generates a random integer between two inclusive bounds a and b.

import random

# roll a die
roll = random.randint(1, 6)
print("Die roll:", roll)

# simulate 5 random numbers between -3 and 3
nums = [random.randint(-3, 3) for _ in range(5)]
print("Random sample:", nums)
Die roll: 2
Random sample: [2, -2, 3, 2, 1]

Demo: random.choice

Selects a random element from a sequence (list, string, etc.).

import random

colors = ["red", "blue", "green", "yellow"]
pick = random.choice(colors)
print("Picked color:", pick)

# random character from a string
char = random.choice("ABCDE")
print("Random letter:", char)

10 coin flips: ['Heads', 'Tails', 'Heads', 'Heads', 'Tails', 'Heads', 'Tails', 'Tails', 'Heads', 'Tails']

Demo: random.shuffle

Randomizes the order of elements in a list (in place).

import random

deck = ["A♠", "2♠", "3♠", "4♠", "5♠"]
print("Original deck:", deck)

random.shuffle(deck)
print("Shuffled deck:", deck)

Original deck: ['A♠', '2♠', '3♠', '4♠', '5♠']
Shuffled deck: ['2♠', '4♠', '5♠', '3♠', 'A♠']
%%html 

<div id="py-quiz" 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 10px;">🧩 Python Random Mini-Quiz</h3>
  <p style="margin:6px 0 12px; color:#cfe7ff">Answer correctly to proceed.</p>

  <!-- Progress -->
  <div id="pyq-progress" style="margin:10px 0 12px;">
    <div style="display:flex; align-items:center; gap:10px;">
      <div id="pyq-counter" style="min-width:110px; font-weight:600;">Q 1 / 12</div>
      <div style="flex:1; height:8px; background:rgba(255,255,255,.12); border-radius:999px; overflow:hidden;">
        <div id="pyq-bar" style="height:100%; width:0%; background:#7CFC00;"></div>
      </div>
    </div>
  </div>

  <!-- Question mount -->
  <div id="pyq-stage"></div>

  <!-- Nav -->
  <div style="display:flex; gap:8px; margin-top:12px;">
    <button id="pyq-prev" disabled> Back</button>
    <button id="pyq-next" disabled>Next </button>
  </div>

  <!-- Status -->
  <div id="pyq-status" style="margin-top:14px; font-weight:600;"></div>

  <style>
    /* Force readable light text in dark theme */
    #py-quiz, #py-quiz *, #py-quiz code { color:#e9eefc; }
    #py-quiz code { background:rgba(255,255,255,.08); padding:1px 4px; border-radius:4px; }

    /* Buttons */
    #py-quiz 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;
    }
    #py-quiz button:hover{ background:rgba(255,255,255,.12); }
    #py-quiz button[disabled]{ opacity:.5; cursor:not-allowed; }

    /* Card */
    #py-quiz .card{
      margin-bottom:14px; padding:12px; border-radius:10px;
      background:rgba(255,255,255,.05); border:1px solid rgba(255,255,255,.08);
    }

    /* Choices */
    #py-quiz .choices{ display:grid; gap:6px; margin:8px 0; }
    #py-quiz .choices label{ cursor:pointer; }
    #py-quiz input[type="radio"]{ accent-color:#8ab4ff; }

    /* Feedback */
    #py-quiz .feedback{ margin-top:8px; font-size:.95rem; }
    #py-quiz .ok{ color:#7CFC00; }
    #py-quiz .bad{ color:#FF6B6B; }
  </style>

  <script>
  (function(){
    const qs = [
      // 1 Basics  inclusivity & behavior
      { stem: "What values can <code>random.randint(1, 3)</code> produce?",
        choices:["{1,2}","{1,2,3}","{0,1,2,3}"], answer:1,
        explain:"<code>randint</code> is inclusive → 1, 2, or 3." },
      { stem: "<code>random.randint(5,5)</code> always returns…",
        choices:["5","a random number between 1–5","Error"], answer:0,
        explain:"Inclusive endpoints; only 5 exists in the range." },

      // 2 Probability reasoning
      { stem: "What is the probability that <code>random.randint(1, 10) == 7</code>?",
        choices:["1/7","1/9","1/10","10%"], answer:2,
        explain:"10 equally likely outcomes → 1/10 = 10%." },
      { stem: "<code>random.randint(2, 6) &lt;= 4</code> is true what percent of the time?",
        choices:["20%","40%","60%","80%"], answer:2,
        explain:"Outcomes {2,3,4,5,6}. ≤4 gives {2,3,4} → 3/5 = 60%." },

      // 3 random.choice
      { stem: "<code>random.choice(['red','blue','green'])</code> returns…",
        choices:["Always 'red'","A random element from the list","An index number"], answer:1,
        explain:"<code>choice</code> returns one random element, not an index." },
      { stem: "Which call gives a fair coin flip?",
        choices:[
          "<code>random.randint(0,1)</code>",
          "<code>random.randint(1,2)</code>",
          "<code>random.choice(['Heads','Tails'])</code>",
          "All of these"
        ], answer:3,
        explain:"All three produce two equally likely outcomes." },

      // 4 Shuffle & lists
      { stem: "After <code>random.shuffle(deck)</code>, what happens?",
        choices:[
          "deck is unchanged",
          "deck is shuffled in place",
          "shuffle returns a new shuffled copy",
          "Error"
        ], answer:1,
        explain:"<code>shuffle</code> shuffles the original list in place and returns None." },

      // 5 Code tracing
      { stem: "What can this print?<br><pre style='margin:6px 0;'>import random\na = random.randint(1,2)\nif a == 1:\n    print('Hi')\nelse:\n    print('Bye')</pre>",
        choices:["Always 'Hi'","Always 'Bye'","'Hi' or 'Bye' randomly"], answer:2,
        explain:"a is 1 or 2 → branch chosen at random." },
      { stem: "Given:<br><pre style='margin:6px 0;'>rolls = [random.randint(1,6) for _ in range(1000)]</pre>What is <code>rolls</code>?",
        choices:["A single integer","A list of 1000 random integers (1–6)","An error"], answer:1,
        explain:"List comprehension builds a 1000-element list of die rolls." },

      // 6 Trickier probability
      { stem: "What percent of runs display TRUE?<br><code>random.randint(1,4) == 5</code>",
        choices:["0%","20%","25%","100%"], answer:0,
        explain:"Impossible; 1–4 never equals 5." },
      { stem: "What percent of runs display TRUE?<br><code>random.randint(1,4) &lt;= 2</code>",
        choices:["25%","40%","50%","100%"], answer:2,
        explain:"Outcomes {1,2,3,4}; ≤2 is {1,2} → 2/4 = 50%." },

      // 7 Combo / parity
      { stem: "If <code>x = random.randint(1,6)</code>, what is <code>P(x % 2 == 0)</code>?",
        choices:["1/6","1/2","2/3","100%"], answer:1,
        explain:"Even faces {2,4,6} are 3 of 6 → 1/2." }
    ];

    const root   = document.getElementById('py-quiz');
    const stage  = document.getElementById('pyq-stage');
    const status = document.getElementById('pyq-status');
    const prevBtn= document.getElementById('pyq-prev');
    const nextBtn= document.getElementById('pyq-next');
    const counter= document.getElementById('pyq-counter');
    const bar    = document.getElementById('pyq-bar');

    const solved = new Set();
    let idx = 0;
    let lastCorrect = false;

    function render(){
      stage.innerHTML = '';
      const q = qs[idx];
      const card = document.createElement('div');
      card.className = 'card';
      card.innerHTML = `
        <div style="font-weight:600; margin-bottom:6px;">Q${idx+1}.</div>
        <div>${q.stem}</div>
        <div class="choices">
          ${q.choices.map((c,i)=>`
            <label><input type="radio" name="q-${idx}" value="${i}"> ${String.fromCharCode(65+i)}. ${c}</label>
          `).join('')}
        </div>
        <button id="check">Check</button>
        <div id="fb" class="feedback"></div>
      `;
      stage.appendChild(card);

      const fb = card.querySelector('#fb');
      card.querySelector('#check').onclick = ()=>{
        const sel = card.querySelector('input[type="radio"]:checked');
        if(!sel){
          fb.textContent = "Pick an option.";
          fb.className = 'feedback bad';
          lastCorrect = false;
          update();
          return;
        }
        const ok = Number(sel.value) === q.answer;
        if(ok){
          fb.innerHTML = `✅ <span class="ok">Correct.</span> ${q.explain || ''}`;
          fb.className = 'feedback ok';
          solved.add(idx);
          lastCorrect = true;
        }else{
          fb.innerHTML = `❌ <span class="bad">Not quite.</span>`;
          fb.className = 'feedback bad';
          solved.delete(idx);
          lastCorrect = false;
        }
        update();
      };

      // if previously solved, allow next
      lastCorrect = solved.has(idx);
      update();
    }

    function update(){
      counter.textContent = `Q ${idx+1} / ${qs.length}`;
      const pct = (solved.size / qs.length) * 100;
      bar.style.width = pct.toFixed(1) + '%';
      status.textContent = solved.size === qs.length
        ? "🎉 All tumblers solved! You’ve cleared the Python chamber."
        : `🔐 ${solved.size}/${qs.length} solved.`;
      prevBtn.disabled = (idx===0);
      nextBtn.disabled = !lastCorrect || (idx===qs.length-1);
      nextBtn.textContent = (idx===qs.length-1) ? "Finish" : "Next ▶";
    }

    prevBtn.onclick = ()=>{ if(idx>0){ idx--; render(); } };
    nextBtn.onclick = ()=>{
      if(idx < qs.length-1){ idx++; render(); }
      else { status.textContent = "🏆 You mastered the Python lock — onward!"; }
    };

    render();
  })();
  </script>
</div>

🧩 Python Random Mini-Quiz

Answer correctly to proceed.

Q 1 / 12

⬅️ Back to Lobby