Computer Science Essentials · Unit 1 Project · Build guides

Build Guides

One guide per game. Do the Designer part first, then the Blocks steps in order — each one has a test. If the test fails, fix it before moving on. Your must-haves and rubric are on the project page.

Before you start (every game)

  • Go to ai2.appinventor.mit.edu, sign in, Projects → Start new project. Name it with no spaces: MoleMash, CatchIt, RockPaperScissors, GuessMyNumber, TicTacToe.
  • Rename every component the moment you drop it in (select it → Rename, bottom of the Components panel). The guide uses the names below. If yours are different, your blocks will have different names — that's fine, just be consistent.
  • Connect the phone with Connect → AI Companion the same way you did for Digital Doodle. Test after every step. Not at the end.
  • Blocks are found two ways: Built-in drawers (Control, Logic, Math, Text, Variables, Procedures) on the left, or by clicking a component's name to open its own drawer (that's where when Mole.Touched lives).
  • To add else if / else to an if block, click the little blue gear on it and drag the pieces in. To give a procedure an input, same gear on the procedure block.
eventset propertyget propertycall methodcontrollogicmathtextvariableprocedure

🐹 Mole Mash

Intermediate · about 2 class periods
Part ADesigner — what the app has

Drag these in, in this order. Rename each one. Set the properties listed; leave everything else alone.

Rename toComponent (palette)Properties to set
ScoreLabelLabel (User Interface)Text: Score: 0 · FontSize 20
MissLabelLabel (User Interface)Text: Misses: 0 · FontSize 20
Canvas1Canvas (Drawing and Animation)Width: Fill parent · Height: 320 pixels · BackgroundColor: any light color
MoleImageSprite (Drawing and Animation) — drop it inside the CanvasPicture: upload any small PNG (Media → Upload File; a 60×60 image is good; happy.png from Happy Accelerometer works to start)
ResetButtonButton (User Interface)Text: Reset · Width: Fill parent
Clock1Clock (Sensors) — it goes below the phone as a "non-visible component"TimerInterval: 1000 · TimerEnabled: ✓ checked
Check: the Components tree on the right shows Mole indented under Canvas1. If it's beside Canvas1, drag it onto the canvas again.
Part BBlocks — what the app does
1
Make the two counters
Find it: Variables drawer → initialize global name to. Click "name" to rename. Plug a Math number block (0) into each.
initialize global score to 0 initialize global misses to 0
Initialize before you update — no box, nothing to add to. Same rule as Happy Accelerometer.
2
The procedure that moves the mole
Find it: Procedures → to procedure do. Rename to moveMole. Click Mole → call Mole.MoveTo. Math → random integer from … to … (twice) and the − block. Click Canvas1 → Canvas1.Width / Canvas1.Height getters; click Mole → Mole.Width / Mole.Height.
to moveMole do call Mole.MoveTo x random integer from 0 to ( Canvas1.Width − Mole.Width ) y random integer from 0 to ( Canvas1.Height − Mole.Height )
Why subtract the mole's width? If x could be all the way at Canvas1.Width, the mole would start at the right edge and hang off it. The biggest x where the whole mole still fits is Width − Mole.Width.
3
Make the Clock call it
Find it: click Clock1 → when Clock1.Timer. Procedures → call moveMole (it appears there once you made the procedure).
when Clock1.Timer do call moveMole
Test: on the phone, the mole should hop to a new spot every second and never hang off the edge.
Doesn't move? Clock1's TimerEnabled is unchecked in the Designer, or the call block isn't snapped inside the Timer.
4
Hitting the mole
Find it: click Mole → when Mole.Touched. Variables → set global score to and get global score. Math → +. Click ScoreLabel → set ScoreLabel.Text to. Text → join.
when Mole.Touched (x, y) do set global score to ( get global score + 1 ) set ScoreLabel.Text to join "Score: " get global score call moveMole
Read → change → write. Then the Label is updated separately — changing the variable never changes the screen by itself.
Test: tap the mole. Score goes up by exactly 1 and the mole jumps away.
5
Missing the mole (the tricky one)
Find it: click Canvas1 → when Canvas1.Touched. It gives you three values: x, y, and touchedAnySprite. Hover over touchedAnySprite on the event block and drag out its get. Logic → not. Control → if then.
when Canvas1.Touched (x, y, touchedAnySprite) do if not get touchedAnySprite then set global misses to ( get global misses + 1 ) set MissLabel.Text to join "Misses: " get global misses
Tapping the mole fires both Mole.Touched and Canvas1.Touched. touchedAnySprite is true when the tap landed on a sprite — so "if not" means "only count it when they hit grass."
Test: tap grass → Misses +1. Tap the mole → Score +1 and Misses stays the same.
Every hit also counts a miss? The not block is missing, or the set blocks are outside the if.
6
Reset
Find it: Procedures → new procedure, rename reset. Click ResetButton → when ResetButton.Click.
to reset do set global score to 0 set global misses to 0 set ScoreLabel.Text to "Score: 0" set MissLabel.Text to "Misses: 0" when ResetButton.Click do call reset
Test: play a bit, tap Reset, both labels read 0, and the next hit reads 1 (not 8).
That's all five must-haves. Now go break it: tap the very edge of the mole, tap fast twice, rotate the phone. Write down what happened for your bug log. Then stretch goals: speed up every 5 hits with set Clock1.TimerInterval to (Clock1.TimerInterval − 100) inside an if score mod 5 = 0 — but don't go below 300.

🧺 Catch It!

Intermediate · about 2–3 class periods
Part ADesigner — what the app has
Rename toComponent (palette)Properties to set
CaughtLabelLabelText: Caught: 0 · FontSize 18
LostLabelLabelText: Lost: 0 · FontSize 18
Canvas1Canvas (Drawing and Animation)Width: Fill parent · Height: 400 pixels · light BackgroundColor
BasketImageSprite — inside the CanvasPicture: a basket/bowl PNG · Y: 340 (near the bottom) · X: 120
GiftImageSprite — inside the CanvasPicture: a gift/star PNG · X: 100 · Y: 0 (top)
ResetButtonButtonText: Reset
Clock1Clock (Sensors)TimerInterval: 50 · TimerEnabled ✓
AccelerometerSensor1AccelerometerSensor (Sensors)Enabled ✓ — skip this row if you'll move the basket by dragging instead
Check: both sprites are indented under Canvas1. Gift is near the top, Basket near the bottom.
Part BBlocks
1
Variables
Find it: Variables → three initialize global blocks.
initialize global caught to 0 initialize global lost to 0 initialize global speed to 4
speed lives in one place so you can tune the game later with one change — that's the whole point of a variable instead of typing 4 everywhere.
2
Move the basket (pick ONE way)
Tilt: click AccelerometerSensor1 → when AccelerometerSensor1.AccelerationChanged, hover xAccel for its get. Drag: click Canvas1 → when Canvas1.Dragged, hover currentX.
TILT: when AccelerometerSensor1.AccelerationChanged (xAccel, yAccel, zAccel) do set Basket.X to ( Basket.X + ( get xAccel × 4 ) ) — or — DRAG: when Canvas1.Dragged (startX, startY, prevX, prevY, currentX, currentY, draggedAnySprite) do set Basket.X to ( get currentX − ( Basket.Width / 2 ) )
Test: basket slides left/right. It will fly off the edge for now — that's step 3.
Tilt goes the wrong way? Change the × 4 to × −4. Too slow? Make the 4 bigger.
3
Keep the basket on screen
Find it: click Basket → when Basket.EdgeReached and call Basket.MoveTo. Control → if then, click its gear and add an else. Math → compare block, change the dropdown to <.
when Basket.EdgeReached (edge) do if Basket.X < 1 then call Basket.MoveTo x: 1 y: Basket.Y else call Basket.MoveTo x: ( Canvas1.Width − Basket.Width − 1 ) y: Basket.Y
EdgeReached fires the instant the sprite touches any edge. The if asks "which side?" — tiny X means the left edge, so pin it there; otherwise pin it to the right.
Test: push the basket hard left and right. It stops at the walls.
4
The procedure that drops a new gift
Find it: Procedures → rename dropNewGift. Click Gift → call Gift.MoveTo. Math → random integer.
to dropNewGift do call Gift.MoveTo x random integer from 0 to ( Canvas1.Width − Gift.Width ) y 0
Y = 0 is the top of the canvas. Screen Y grows downward — the trap from Happy Accelerometer.
5
Make it fall, and catch the miss
Find it: click Clock1 → when Clock1.Timer. Click Gift → set Gift.Y to, Gift.Y, Gift.Height. Control → if then. Math → >.
when Clock1.Timer do set Gift.Y to ( Gift.Y + get global speed ) if Gift.Y > ( Canvas1.Height − Gift.Height ) then set global lost to ( get global lost + 1 ) set LostLabel.Text to join "Lost: " get global lost call dropNewGift
Test: the gift falls. When it reaches the bottom, Lost goes up by 1 and a new gift appears at the top somewhere else.
Gift falls up? You subtracted speed. Lost jumps by 5 at once? Clock fires 20× a second — the gift sat at the bottom through several ticks. Make sure call dropNewGift is inside the then.
6
Catch it
Find it: click Gift → when Gift.CollidedWith.
when Gift.CollidedWith (other) do set global caught to ( get global caught + 1 ) set CaughtLabel.Text to join "Caught: " get global caught call dropNewGift
Calling dropNewGift right away moves the gift out of the basket, so CollidedWith can't keep firing while they overlap.
Test: catch one. Caught +1, exactly once. Miss one. Lost +1.
7
Reset
to reset do set global caught to 0 set global lost to 0 set CaughtLabel.Text to "Caught: 0" set LostLabel.Text to "Lost: 0" call dropNewGift when ResetButton.Click do call reset
Test: Reset zeroes both labels and restarts the gift from the top.
All five must-haves done. Bug log ideas: what happens if you hold the phone flat? What if two gifts overlap the basket? Stretch: every 5 catches, set global speed to (speed + 1) — one line, because speed is a variable.

✊ Rock, Paper, Scissors

Beginner · about 2 class periods · needs if / else if (1.1.3)
Part ADesigner — what the app has
Rename toComponent (palette)Properties to set
ScoreLabelLabelText: You 0 — Phone 0 · FontSize 22 · TextAlignment center · Width Fill parent
PhoneLabelLabelText: Phone picks… · FontSize 40 · TextAlignment center · Width Fill parent
ResultLabelLabelText: Pick one! · FontSize 18 · TextAlignment center · Width Fill parent
RowHorizontalArrangement (Layout)Width: Fill parent · AlignHorizontal: Center
RockButtonButton — inside RowText: ✊ Rock · FontSize 18
PaperButtonButton — inside RowText: ✋ Paper · FontSize 18
ScissorsButtonButton — inside RowText: ✌️ Scissors · FontSize 18
NewMatchButtonButtonText: New match · Width Fill parent
Part BBlocks
1
Scores
initialize global you to 0 initialize global phone to 0
2
A procedure that shows the phone's pick
Find it: Procedures → to procedure do, rename showPick. Click its blue gear, drag input: x into the inputs slot, rename x to n. Control → if, gear → add two else if. Math → = compare.
to showPick (n) do if get n = 1 then set PhoneLabel.Text to "✊" else if get n = 2 then set PhoneLabel.Text to "✋" else set PhoneLabel.Text to "✌️"
1 = rock, 2 = paper, 3 = scissors everywhere in this app. Write that on your plan sheet so you don't flip it later.
3
The main procedure: play(mine)
Find it: new procedure play with input mine. Variables → initialize local name to … in (the one with a "do" slot), rename to phonePick. Math → random integer from 1 to 3. Logic → and, or.
to play (mine) do initialize local phonePick to random integer from 1 to 3 in call showPick n: get phonePick if get mine = get phonePick then set ResultLabel.Text to "Tie!" else if ( get mine = 1 and get phonePick = 3 ) or ( get mine = 2 and get phonePick = 1 ) or ( get mine = 3 and get phonePick = 2 ) then set ResultLabel.Text to "You win the round!" set global you to ( get global you + 1 ) else set ResultLabel.Text to "Phone wins the round." set global phone to ( get global phone + 1 ) set ScoreLabel.Text to join "You " get global you " — Phone " get global phone
Three ways to win, so three and pairs joined by or. Rock(1) beats scissors(3). Paper(2) beats rock(1). Scissors(3) beats paper(2). Everything else that isn't a tie is a loss — that's what else is for.
The or block only has two slots? Click its gear and add a third, or nest one or inside another.
4
Wire the three buttons
Find it: click each button → its .Click event. Procedures → call play.
when RockButton.Click do call play mine: 1 when PaperButton.Click do call play mine: 2 when ScissorsButton.Click do call play mine: 3
Test — all nine: tap Rock until the phone shows each of ✊ ✋ ✌️ and check the result each time. Then Paper, then Scissors. Write down any combo that's wrong. That's your bug log.
Phone always shows the same thing? The random block is outside play, so it ran once. Scores jump by 2? A + 1 is in two branches.
5
First to five, and New Match
Add this at the bottom of play, after the score label line. Click each button → set Button.Enabled to; Logic → false / true.
(inside play, last thing in the "in" slot) if get global you = 5 or get global phone = 5 then set ResultLabel.Text to "MATCH OVER — tap New match" set RockButton.Enabled to false set PaperButton.Enabled to false set ScissorsButton.Enabled to false to reset do set global you to 0 set global phone to 0 set ScoreLabel.Text to "You 0 — Phone 0" set ResultLabel.Text to "Pick one!" set PhoneLabel.Text to "Phone picks…" set RockButton.Enabled to true set PaperButton.Enabled to true set ScissorsButton.Enabled to true when NewMatchButton.Click do call reset
Test: play to 5. The three buttons grey out. New match brings them back at 0–0.
All five must-haves done. Stretch: a streak variable that goes +1 on a win and back to 0 on a loss, shown in a fourth label. Or swap the emoji for Image components.

🔢 Guess My Number

Beginner · about 1–2 class periods · needs if / else if (1.1.3)
Part ADesigner — what the app has
Rename toComponent (palette)Properties to set
TitleLabelLabelText: I'm thinking of a number from 1 to 100 · FontSize 18 · Width Fill parent
GuessBoxTextBox (User Interface)NumbersOnly ✓ · Hint: your guess · Width Fill parent
GuessButtonButtonText: Guess · Width Fill parent
FeedbackLabelLabelText: (blank) · FontSize 22 · TextColor: a dark green
CountLabelLabelText: Guesses: 0
NewGameButtonButtonText: New game
Notifier1Notifier (User Interface) — non-visiblenothing to set
Part BBlocks
1
Variables
initialize global secret to 0 initialize global guesses to 0
secret starts at 0 only so the box exists. The real number gets picked in step 2.
2
newGame — and call it at startup
Find it: Procedures → rename newGame. Click Screen1 (top of the component list) → when Screen1.Initialize. Click GuessButton → set GuessButton.Enabled to.
to newGame do set global secret to random integer from 1 to 100 set global guesses to 0 set FeedbackLabel.Text to "" set CountLabel.Text to "Guesses: 0" set GuessBox.Text to "" set GuessButton.Enabled to true when Screen1.Initialize do call newGame when NewGameButton.Click do call newGame
Screen1.Initialize runs once when the app opens — same block Happy Accelerometer used. Two buttons, one procedure: define once, call twice.
Test (sneaky): temporarily add set TitleLabel.Text to get global secret at the end of newGame so you can see the number. Open the app — a number shows. Tap New game — a different number. Remove that line before you demo.
3
Reject a bad guess first
Find it: click GuessButton → when GuessButton.Click. Control → if, gear → add else. Math → is number?. Logic → not. Click GuessBox → GuessBox.Text.
when GuessButton.Click do if not is number? GuessBox.Text then set FeedbackLabel.Text to "Type a number first." else … step 4 goes here …
Everything that counts as a real guess goes in the else. If the counter were above the if, blank taps would count — that's must-have M4.
Test: tap Guess with the box empty. "Type a number first." Guesses stays 0.
4
Too low, too high, got it
Find it: inside the else from step 3. Control → if, gear → one else if and one else. Math → compare with < and >. Click Notifier1 → call Notifier1.ShowAlert.
else set global guesses to ( get global guesses + 1 ) set CountLabel.Text to join "Guesses: " get global guesses if GuessBox.Text < get global secret then set FeedbackLabel.Text to "Too low ⬆" else if GuessBox.Text > get global secret then set FeedbackLabel.Text to "Too high ⬇" else set FeedbackLabel.Text to join "You got it in " get global guesses " guesses!" call Notifier1.ShowAlert notice: FeedbackLabel.Text set GuessButton.Enabled to false
Use the Math compare blocks (blue), not the Text compare (pink). The text version compares "9" and "10" letter by letter and says 9 is bigger.
Test: with the sneaky title still on, guess below, above, then exactly. Three different messages, counter goes 1 → 2 → 3, the Guess button greys out, New game wakes it up.
Always "Too high"? secret is still 0 — newGame isn't called from Initialize. Counter counts blanks? The + 1 is above the first if.
All five must-haves done. Remove the sneaky title line. Stretch: a best variable — after a win, if guesses < best (start best at 999) then set best, and show it in TitleLabel. Or "warmer / colder" with a lastDistance variable.

❌ Tic-Tac-Toe

Beginner+ · about 2 class periods · needs if / else (1.1.3)

This is what you're building — play it

X's turn
X: 0 · O: 0 · Draws: 0
Part ADesigner — what the app has
Rename toComponent (palette)Properties to set
StatusLabelLabelText: X's turn · FontSize 22 · TextAlignment center · Width Fill parent
GridTableArrangement (Layout)Columns 3 · Rows 3 · Width Fill parent
B1 … B9Button × 9 — drop each one into a cell of Grid, left to right, top to bottomText: blank (delete the word "Text for Button1") · Width 100 pixels · Height 100 pixels · FontSize 40 · FontBold ✓
ScoreLabelLabelText: X: 0 · O: 0 · TextAlignment center · Width Fill parent
NewGameButtonButtonText: New game · Width Fill parent
Check: the Components tree shows B1–B9 indented under Grid, in order. Numbering matters for the win lines: B1 B2 B3 is the top row, B1 B5 B9 is a diagonal.
Part BBlocks
1
Variables
Find it: Variables → four initialize global blocks. For turn, plug in a Text block and type X.
initialize global turn to "X" initialize global moves to 0 initialize global xWins to 0 initialize global oWins to 0
2
One square, done right — then copy it
Find it: click B5 → when B5.Click, B5.Text, set B5.Text to. Control → if then. Math → = (it compares text too). Text → the empty " " block. Make a Procedures block named afterMove now (leave it empty) so you can call it.
when B5.Click do if B5.Text = "" then set B5.Text to get global turn call afterMove
The if is must-have M1: a square that already has a letter ignores the tap. Without it, O could overwrite X.
Test: tap the middle square → "X" appears. Tap it again → nothing changes.
Now copy it 8 times: right-click the when B5.Click block → Duplicate. On the copy, use the dropdown on the block to change B5 → B1, and change all three B5's inside it to B1. Repeat for B2, B3, B4, B6, B7, B8, B9. Nine handlers, each one identical except its number.
A copy still says B5 inside? Then tapping B3 writes into B5. Every block inside a handler must match the handler's number — check all three.
3
A procedure that answers a question: three(a, b, c)
Find it: Procedures → the block that says to procedure result (the second one in the drawer — it has a socket on the right, not a "do" slot). Rename three. Blue gear → add three inputs, rename them a, b, c. Logic → and (gear → 3 slots). Math → = twice and ≠ once (change the dropdown).
to three (a, b, c) result ( get a = get b ) and ( get b = get c ) and ( get a ≠ "" )
This procedure doesn't do anything — it answers true or false. "Are all three the same, and not blank?" The ≠ "" part is the one everyone forgets, and without it three empty squares count as a win.
4
checkWin — ask the question eight times
Find it: another to procedure result, rename checkWin. Logic → or, gear → 8 slots. Procedures → call three (the one with a plug on the left, since it returns a value) × 8. Click each button → its .Text getter.
to checkWin result three( B1.Text, B2.Text, B3.Text ) or ← top row three( B4.Text, B5.Text, B6.Text ) or ← middle row three( B7.Text, B8.Text, B9.Text ) or ← bottom row three( B1.Text, B4.Text, B7.Text ) or ← left column three( B2.Text, B5.Text, B8.Text ) or ← middle column three( B3.Text, B6.Text, B9.Text ) or ← right column three( B1.Text, B5.Text, B9.Text ) or ← diagonal ↘ three( B3.Text, B5.Text, B7.Text ) ← diagonal ↙
Eight lines, one question each. Because three exists, this is eight short calls instead of 24 comparisons. Draw the 3×3 grid on paper, number it 1–9, and check your eight triples against it before you build them.
5
lockBoard and reset
Find it: two ordinary to procedure do blocks. Click each button → set B1.Enabled to and set B1.Text to. Logic → false / true.
to lockBoard do set B1.Enabled to false set B2.Enabled to false … all nine … to reset do set B1.Text to "" set B1.Enabled to true set B2.Text to "" set B2.Enabled to true … all nine … set global moves to 0 set global turn to "X" set StatusLabel.Text to "X's turn" when NewGameButton.Click do call reset
Yes, eighteen set blocks in reset. That's the honest cost of nine separate buttons — and exactly why the "any Button" stretch goal exists. Scores are not zeroed here on purpose: New game starts a new round, not a new match.
6
afterMove — the brain
Find it: fill in the empty afterMove from step 2. Control → if, gear → one else if and one else. Inside the win branch you need a second if / else to see who won. Procedures → call checkWin (the one with a plug), call lockBoard. Text → join.
to afterMove do set global moves to ( get global moves + 1 ) if checkWin then set StatusLabel.Text to join get global turn " wins!" call lockBoard if get global turn = "X" then set global xWins to ( get global xWins + 1 ) else set global oWins to ( get global oWins + 1 ) set ScoreLabel.Text to join "X: " get global xWins " · O: " get global oWins else if get global moves = 9 then set StatusLabel.Text to "Draw!" else if get global turn = "X" then set global turn to "O" else set global turn to "X" set StatusLabel.Text to join get global turn "'s turn"
Order matters. Win is checked first — a ninth move can win, and if you checked "moves = 9" first you'd call it a draw. The turn only swaps in the else, so the winner's letter is still in turn when you name them.
Test: X top row → "X wins!", board locks, score X: 1. New game → board clears, score stays. Fill a board with no line (X O X / X O O / O X X) → "Draw!". Tap a taken square mid-game → nothing.
"Draw" after a win on move 9? Your checks are in the wrong order. Winner named wrong? You swapped turn before naming them. Three blanks win at the start? Step 3 is missing ≠ "".
All five must-haves done. Bug log: find the draw board above and play it — did your app call it? Stretch: open the Any component drawer at the bottom of the Blocks list, use when any Button.Click with set Button.Text of component, and delete eight of your nine handlers. Same game, a third of the blocks.