temu what beats rock

This sketch creates an interactive game where players type words that beat the current item in a logic-based chain. Starting with 'rock,' each valid answer becomes the new current item and is added to a scrolling history, until the player's logic fails or runs out of creative beats—then the game ends.

🧪 Try This!

Experiment with the code by making these changes:

  1. Add a new rule — Create a new thing and what beats it—players can now chain through your new logic path
  2. Make the game harder — Reduce the creativity fallback chance—now only the hardcoded rules work, no random acceptance
  3. Show more history — Change the history limit from 10 to 20 to display twice as many past victories on screen
  4. Celebrate victories — Change the acceptance message to something more exciting—updates instantly every time a move succeeds
Prefer the full editor? Open it there →

📖 About This Sketch

This sketch builds an interactive word-chain game on a dark canvas where you type answers to 'what beats rock?' and watch your chain grow. Each successful answer updates the current item and adds to a scrolling history of victories. The game ends when your logic fails—when you propose something that doesn't convincingly beat the current item. It's a playful exploration of how creative coding can validate player input and manage simple game state.

The code is organized around three core ideas: a setup() that creates the input interface, a draw() loop that displays the game state and history on the canvas, and a handleSubmit() function that validates moves using a rules-based logic system. By studying it, you'll learn how to connect HTML inputs to p5.js variables, manage arrays of game history, and write rule-checking logic that accepts both hardcoded answers and creative randomness.

⚙️ How It Works

  1. When the sketch loads, setup() creates a 600x400 canvas and builds an HTML input box and submit button positioned at the bottom of the screen.
  2. Every frame, draw() clears the canvas with a dark background and displays the current item at the top, instructions below it, and a scrolling list of the last 10 history entries.
  3. When you type a word and click Submit, handleSubmit() reads your input, checks if it's a valid move using isValidMove(), and either accepts it (adding to history and updating currentThing) or rejects it (ending the game with noLoop()).
  4. The isValidMove() function has two levels of logic: first it checks hardcoded rules (rock loses to paper, water, lava; paper loses to scissors, fire), then it gives a 60% random chance to creative answers that don't match any rule.
  5. If your move is accepted, the message shows 'Accepted!' and the loop continues; if rejected, the message explains why the game ended and noLoop() freezes the canvas until you refresh.

🎓 Concepts You'll Learn

HTML input integrationGame state managementArray manipulation and iterationConditional logic and rule validationText display and layout on canvas

📝 Code Breakdown

setup()

setup() runs once when the sketch starts. It prepares the canvas and creates the HTML input and button elements that bridge user input into the p5.js environment. Notice how createInput() and createButton() are p5.js functions that create real HTML elements—p5.js can create and control DOM elements, not just draw shapes.

function setup() {
  createCanvas(600, 400);
  textFont("Arial");
  
  inputBox = createInput();
  inputBox.position(20, height - 60);
  inputBox.size(200);

  submitButton = createButton("Submit");
  submitButton.position(inputBox.x + inputBox.width + 10, height - 60);
  submitButton.mousePressed(handleSubmit);
}
Line-by-line explanation (8 lines)

🔧 Subcomponents:

variable-assignment Input box creation inputBox = createInput();

Creates an HTML text input field that players type into

variable-assignment Submit button creation submitButton = createButton("Submit");

Creates an HTML button that triggers handleSubmit when clicked

createCanvas(600, 400);
Creates a 600-pixel-wide, 400-pixel-tall canvas where the game board displays
textFont("Arial");
Sets the default font for all text() calls to Arial, making the display consistent
inputBox = createInput();
Creates an HTML text input field and stores a reference to it so we can read its value later
inputBox.position(20, height - 60);
Positions the input box 20 pixels from the left and 60 pixels from the bottom of the canvas
inputBox.size(200);
Sets the input box width to 200 pixels
submitButton = createButton("Submit");
Creates an HTML button labeled 'Submit' and stores a reference to it
submitButton.position(inputBox.x + inputBox.width + 10, height - 60);
Positions the button 10 pixels to the right of the input box, aligned at the same vertical level
submitButton.mousePressed(handleSubmit);
Tells p5.js to call the handleSubmit() function whenever the button is clicked

draw()

draw() runs 60 times per second and is responsible for displaying the entire game state on the canvas. It shows three key pieces of information: the current item, an instruction prompt, feedback from the last move, and the scrolling history of victories. Notice how the y variable is used to stack history entries vertically—this pattern is fundamental to laying out multiple lines of text.

🔬 This block displays the current item. What happens if you change textSize(20) to textSize(40)? Does the text stay in the same place or does it move?

  textSize(20);
  textAlign(LEFT);
  text("Current: " + currentThing, 20, 40);

🔬 These two lines draw each history entry and move the y position down. What happens if you change y += 18 to y += 8? What if you change it to y += 30?

    text("- " + history[i], 20, y);
    y += 18;
function draw() {
  background(30);
  fill(255);

  textSize(20);
  textAlign(LEFT);
  text("Current: " + currentThing, 20, 40);

  textSize(14);
  text("Enter something that beats it:", 20, height - 80);

  fill(200);
  text(message, 20, height - 20);

  // Show history
  let y = 80;
  fill(180);
  text("History:", 20, y);
  y += 20;

  for (let i = max(0, history.length - 10); i < history.length; i++) {
    text("- " + history[i], 20, y);
    y += 18;
  }
}
Line-by-line explanation (16 lines)

🔧 Subcomponents:

for-loop History display loop for (let i = max(0, history.length - 10); i < history.length; i++) {

Iterates through the last 10 items in the history array and displays each one on a new line

background(30);
Clears the canvas each frame with a very dark gray (30, 30, 30 RGB), creating the game board background
fill(255);
Sets the fill color to white (255, 255, 255 RGB) for subsequent text and shapes
textSize(20);
Sets the size of the next text() calls to 20 pixels tall
textAlign(LEFT);
Aligns text to the left edge starting from the x coordinate you provide
text("Current: " + currentThing, 20, 40);
Displays the current item (starting as 'rock') at pixel position (20, 40)
textSize(14);
Reduces text size to 14 pixels for smaller, secondary text
text("Enter something that beats it:", 20, height - 80);
Shows instruction text 80 pixels from the bottom, telling the player what to do
fill(200);
Changes fill color to a lighter gray for the message text
text(message, 20, height - 20);
Displays the feedback message ('Accepted!' or the game-over message) 20 pixels from the bottom
let y = 80;
Initializes a variable to track the vertical position where history entries will be drawn
fill(180);
Changes fill color to a medium gray for history text
text("History:", 20, y);
Displays the 'History:' label at y-position 80
y += 20;
Moves the y position down 20 pixels so the next entry will appear below the label
for (let i = max(0, history.length - 10); i < history.length; i++) {
Loops through the last 10 history items (or fewer if the history is shorter), starting from max(0, length-10) to avoid negative indices
text("- " + history[i], 20, y);
Displays each history entry with a dash bullet point
y += 18;
Increments y by 18 pixels so each new entry appears below the previous one

handleSubmit()

handleSubmit() is the callback function triggered every time the Submit button is clicked. It bridges the HTML input into p5.js variables: reading playerInput, validating it with isValidMove(), and updating game state (history, currentThing, message, or noLoop). This function demonstrates the core event-driven programming pattern: user action → read input → validate → update state.

🔬 This if-else is the heart of the game logic. Notice that on success, we change currentThing, but on failure we call noLoop(). What happens if you comment out the noLoop() line (add // at the start)? Does the game let you keep playing after an invalid move?

  if (isValidMove(currentThing, playerInput)) {
    history.push(playerInput + " beats " + currentThing);
    currentThing = playerInput;
    message = "Accepted!";
  } else {
    message = "That doesn't convincingly beat " + currentThing + ". Game over!";
    noLoop();
  }
function handleSubmit() {
  let playerInput = inputBox.value().toLowerCase().trim();
  inputBox.value("");

  if (playerInput === "") return;

  if (isValidMove(currentThing, playerInput)) {
    history.push(playerInput + " beats " + currentThing);
    currentThing = playerInput;
    message = "Accepted!";
  } else {
    message = "That doesn't convincingly beat " + currentThing + ". Game over!";
    noLoop();
  }
}
Line-by-line explanation (10 lines)

🔧 Subcomponents:

conditional Empty input check if (playerInput === "") return;

Prevents processing empty submissions—the function exits early if the player submitted nothing

conditional Valid move check if (isValidMove(currentThing, playerInput)) {

Branches into acceptance or rejection logic based on whether the move passes the game's rules

let playerInput = inputBox.value().toLowerCase().trim();
Reads the text the player typed, converts it to lowercase (so 'Rock' and 'rock' are the same), and removes leading/trailing spaces
inputBox.value("");
Clears the input box by setting its value to an empty string, ready for the next submission
if (playerInput === "") return;
If the player submitted nothing, the function exits immediately—no game state changes
if (isValidMove(currentThing, playerInput)) {
Calls isValidMove() to check if playerInput logically beats currentThing
history.push(playerInput + " beats " + currentThing);
If valid, adds a new entry to the history array describing the victory (e.g., 'paper beats rock')
currentThing = playerInput;
Updates currentThing to the player's answer, making it the thing the next player must beat
message = "Accepted!";
Sets the feedback message to 'Accepted!' so draw() displays it on the next frame
} else {
If the move is invalid, this block executes instead
message = "That doesn't convincingly beat " + currentThing + ". Game over!";
Sets a game-over message explaining why the move failed
noLoop();
Stops the draw loop, freezing the canvas and ending the game

isValidMove()

isValidMove() is the logic engine of the game. It implements a two-tier validation system: first, a strict rule-based check against hardcoded beats-logic; second, a permissive 60% fallback for creative answers. This teaches a valuable pattern—rules ensure game consistency, but randomness keeps the game surprising and rewards player creativity. The .some() array method is key here: it returns true if any element in the array passes a test, letting you check 'does the answer match any known win condition?'

🔬 These rules define the game logic. What happens if you add a new rule like ["lava", ["ice", "volcano"]]? Can you now chain 'rock' → 'lava' → 'ice'?

  const rules = [
    ["rock", ["paper", "water", "lava"]],
    ["paper", ["scissors", "fire"]],
    ["scissors", ["rock", "hammer"]],
    ["fire", ["water", "extinguisher"]],
    ["water", ["electricity", "freeze"]],
  ];
function isValidMove(prev, next) {
  // Prevent repeats
  if (history.some(h => h.includes(next))) return false;

  // Basic keyword logic
  const rules = [
    ["rock", ["paper", "water", "lava"]],
    ["paper", ["scissors", "fire"]],
    ["scissors", ["rock", "hammer"]],
    ["fire", ["water", "extinguisher"]],
    ["water", ["electricity", "freeze"]],
  ];

  for (let [thing, beats] of rules) {
    if (prev.includes(thing)) {
      if (beats.some(b => next.includes(b))) return true;
    }
  }

  // Fallback: random chance for "creative" answers
  return random() < 0.6;
}
Line-by-line explanation (8 lines)

🔧 Subcomponents:

conditional Repeat prevention if (history.some(h => h.includes(next))) return false;

Prevents the player from reusing words they've already played in the history

for-loop Rules checking loop for (let [thing, beats] of rules) {

Iterates through the hardcoded rules to see if the player's answer matches a known winning condition

calculation Creative answer fallback return random() < 0.6;

Gives any answer not matching the rules a 60% chance to be accepted, rewarding creative thinking

function isValidMove(prev, next) {
Defines a function that takes two parameters: prev (the current item) and next (the player's proposed answer)
if (history.some(h => h.includes(next))) return false;
Uses array.some() to check if any history entry contains the player's answer—if so, the word was already used, so return false immediately
const rules = [
Declares a constant array containing hardcoded rules of what beats what
["rock", ["paper", "water", "lava"]],
One rule: 'rock' is beaten by 'paper', 'water', or 'lava'
for (let [thing, beats] of rules) {
Loops through each rule, destructuring each array into thing (the item) and beats (array of things that beat it)
if (prev.includes(thing)) {
Checks if the current item contains the keyword from this rule (e.g., does 'rock' include 'rock')
if (beats.some(b => next.includes(b))) return true;
If the current item matches, checks if the player's answer contains any of the words that beat it—if yes, return true immediately
return random() < 0.6;
If no hardcoded rule applied, give a 60% chance that the answer is accepted anyway (rewarding creativity)

📦 Key Variables

currentThing string

Stores the thing the player must beat in the current round—updates with each successful move

let currentThing = "rock";
inputBox object

A reference to the HTML input field created by createInput(), allowing you to read and clear its value

let inputBox;
submitButton object

A reference to the HTML submit button created by createButton(), allowing you to attach click handlers

let submitButton;
message string

Stores the feedback text displayed at the bottom of the canvas ('Accepted!' or the game-over message)

let message = "";
history array

An array of strings recording all past victories (e.g., ['paper beats rock', 'scissors beats paper']), displayed in draw() and checked by isValidMove() to prevent repeats

let history = [];

🔧 Potential Improvements (4)

Here are some ways this code could be enhanced:

BUG isValidMove() - repeat prevention

The repeat check uses history.some(h => h.includes(next)), which only looks for exact word matches in history strings. If a player has played 'fire' before, they can reuse it as long as it appears in a different history entry (e.g., 'fire' in 'fire beats paper' vs. 'water beats fire')—this could allow sneaky reuse.

💡 Track used words in a separate Set for exact matching: const usedWords = new Set(history.map(h => h.split(' ')[0])); if (usedWords.has(next)) return false;

FEATURE handleSubmit()

The game ends when a move is invalid, but there's no way to restart without refreshing the page.

💡 Add a restart button that resets currentThing = 'rock', history = [], and message = '', then calls loop() to resume the draw loop.

STYLE isValidMove() - rules array

The hardcoded rules are embedded in the function, making it hard to edit or expand the game logic without modifying code. The structure is clear but inflexible.

💡 Move the rules array to a global constant at the top of the sketch, or store it as a JSON file and load it with loadJSON() for easier customization.

PERFORMANCE draw() - text rendering

Text properties (textSize, textAlign, fill) are reset multiple times per frame even when they don't change, wasting CPU cycles.

💡 Set text properties once in setup() or group related text calls together to minimize state changes.

🔄 Code Flow

Code flow showing setup, draw, handlesubmit, isvalidmove

💡 Click on function names in the diagram to jump to their code

graph TD start[Start] --> setup[setup] setup --> input-creation[Input Creation] setup --> button-creation[Button Creation] setup --> draw[draw loop] click setup href "#fn-setup" click input-creation href "#sub-input-creation" click button-creation href "#sub-button-creation" draw --> input-validation[Input Validation] draw --> history-loop[History Display Loop] click draw href "#fn-draw" click input-validation href "#sub-input-validation" click history-loop href "#sub-history-loop" input-validation -->|Empty Input Check| move-validation[Move Validation] move-validation -->|Valid Move Check| repeat-check[Repeat Prevention] repeat-check -->|If Valid| handlesubmit[handleSubmit] repeat-check -->|If Invalid| draw handlesubmit --> isvalidmove[isValidMove] click handlesubmit href "#fn-handlesubmit" click isvalidmove href "#fn-isvalidmove" isvalidmove --> rules-loop[Rules Checking Loop] isvalidmove --> random-fallback[Random Fallback] rules-loop -->|If Match| updateState[Update State] random-fallback -->|If Accepted| updateState updateState --> draw history-loop -->|For Each History Item| draw history-loop -->|Display History| draw rules-loop -->|If No Match| draw random-fallback -->|If Rejected| draw

❓ Frequently Asked Questions

What visual experience does the temu what beats rock sketch provide?

The sketch presents a dark canvas where users see the current item, input prompts, and a growing history of victories displayed as text.

How can users engage with the temu what beats rock interactive game?

Users interact by typing in items they believe can beat the current item and submitting their answers, which updates the game state until an invalid move is made.

What creative coding concepts are showcased in the temu what beats rock sketch?

The sketch demonstrates basic game logic through conditionals, user input handling, and dynamic text updates, encouraging creative thinking in generating valid responses.

Preview

temu what beats rock - p5.js creative coding sketch preview
Sketch Preview
Code flow diagram showing the structure of temu what beats rock - Code flow showing setup, draw, handlesubmit, isvalidmove
Code Flow Diagram