Unit 4 · Big Idea 3
The Turn Model
Student Lab · Turn Any Angle, in Either Direction
Student PIN:
Overview
Your turns right now are stuck at 90°. But missions need all kinds of angles — 45°, 180°, whatever the field demands. Today you’ll build one flexible function, Turn, that takes a direction and an angle and handles them all. Along the way you’ll meet four new tools: a new type for letters, a new kind of loop, functions that take more than one input, and functions that hand a value back. And you’ll discover something real engineers live with every day: a model is never perfect — it’s the best fit you can find.
Core Insight
A model like “ per degree” lets one function turn any angle. But the real world fights back — and friction mean no single number is perfect. You find the one that fits best.
By the end of this activity you will be able to:
- Use a
charto store a single letter like'R'or'L'. - Use a
forloop to repeat an action a set number of times. - Write a function that takes two parameters and returns a value.
- Build a
ticks_per_degreemodel and a flexibleTurnfunction.
Phase 1 — New Tool: The char Type
You’ve used int and double for numbers. A char holds a single character — one letter, written in single quotes:
char direction = 'R'; // one character, in SINGLE quotes
This is perfect for telling the robot which way to turn: 'R' for right, 'L' for left.
Single quotes, not double
'R' (single quotes) is a char — one character. "R" (double quotes) is a — a whole different, more complicated type. For one letter, always use single quotes. And remember: 'R' and 'r' are different characters to the computer.
What is a char, and how is 'R' different from "R"? Why is a char a good fit for a turn direction?
Phase 2 — New Tool: The for Loop
You’ve used while loops that run until something changes. A for loop is for when you know exactly how many times to repeat. It counts for you:
for (int i = 0; i < 4; i++) // run 4 times: i = 0, 1, 2, 3
{
// ...do this each time...
}Three parts in the parentheses: start (int i = 0), keep going while (i < 4), and each time, do this (i++, which adds 1 to i). When i reaches 4, it stops — so the body ran exactly 4 times.
How is a for loop different from the while loops you’ve used? When would you reach for a for loop instead?
Phase 3 — Calibrate: Find ticks_per_degree
Just like ticks_per_inch told you ticks-to-inches, you now need ticks_per_degree: how many ticks make one degree of turning. The clever way to measure it: make the robot spin all the way around — a full 360° — counting ticks, then divide.
The turning model
Here’s where the for loop shines. A full 360° spin can be built three different ways — and they should all equal 360°:
| The for loop | Total turn | |
|---|---|---|
| A | for (i=0; i<4; i++) → 90° each | 4 × 90° = 360° |
| B | for (i=0; i<8; i++) → 45° each | 8 × 45° = 360° |
| C | for (i=0; i<2; i++) → 180° each | 2 × 180° = 360° |
This example pivots in chunks using a for loop. Notice it uses mav, not motor — control is smoother for turning. Use a slow speed so the robot doesn’t from its own momentum.
cmpc(0); // clear the counter once, before the spin
for (int i = 0; i < 4; i++) // four chunks = one full 360 degree spin
{
long target = (i + 1) * CHUNK_TICKS; // how far we should be after this chunk
while (gmpc(0) < target)
{
mav(0, 300); // SLOW velocity: left wheel forward
mav(1, -300); // right wheel backward (pivot right)
}
}
motor(0,0); motor(3,0); msleep(50); // brake-settle
printf("total ticks = %d\n", gmpc(0));Run all three versions (A, B, C). After each full spin, read the total ticks and compute ticks_per_degree. Mark the robot’s start so you can see how close it lands to a true 360°.
Data
| Version | Total ticks for 360° | ticks ÷ 360 = ticks_per_degree |
|---|---|---|
| A — four 90° turns | ||
| B — eight 45° turns | ||
| C — two 180° turns |
They won't perfectly agree --- and that's the lesson
You’ll find it’s incredibly hard to make all three land on a perfect 360°. Every time the robot starts and stops a chunk, inertia carries it a little extra, and friction varies. More chunks (eight 45s) means more start-stops and more error pile-up. There is no single perfect ticks_per_degree — your job is to find the value that fits your robot best across the cases you care about.
Did your three ticks_per_degree values come out the same? Why might the eight-turn version (B) drift more than the two-turn version (C)?
Which ticks_per_degree value will you use as your model, and why did you pick it?
Phase 4 — Build: The Turn Function
Now build Turn — and it introduces two more new ideas at once: it takes two parameters (a char and a double), and it returns a value to report whether it worked.
Until now your functions took one input (or none). Turn takes two, separated by a comma — a direction and an angle:
Turn('R', 90.0); // turn right 90 degrees
Turn('L', 45.0); // turn left 45 degrees
Every function you’ve built has been void — it did something but handed nothing back. Turn is an int function: it returns a number that reports what happened. We’ll use 1 for success and 0 for failure (a bad direction). return also immediately exits the function — so a bad input never reaches the turning code.
A good function is easy to use and hard to break. Instead of demanding a capital 'R', accept either case with the OR operator || — true if either side is true. That way a user who types 'r' still succeeds — one less thing to remember.
if (direction == 'R' || direction == 'r') // either capital or lowercase
{
}// Unit 4, Big Idea 3: The Turn Model
// Name: _______________________ Date: ___________
#include <kipr/wombat.h>
#include <yourname.h>
double ticks_per_degree = ____; // YOUR best value from Phase 3
int Turn(char direction, double angle); // PROTOTYPE (note: returns an int)
int main()
{
Turn('R', 90.0); // right 90
Turn('l', 45.0); // left 45: lowercase works too!
return 0;
}
int Turn(char direction, double angle)
{
int ticks = angle * ticks_per_degree; // PREDICT ticks from the model
if (direction == 'R' || direction == 'r') // RIGHT (either case)
{
cmpc(0); // right pivot watches left wheel
while (gmpc(0) < ticks)
{
mav(0, 300); // slow velocity, left forward
mav(1, -300); // right backward
}
}
else if (direction == 'L' || direction == 'l') // LEFT (either case)
{
cmpc(1); // left pivot watches right wheel
while (gmpc(1) < ticks)
{
mav(0, -300);
mav(1, 300);
}
}
else // not R/r or L/l: bad input!
{
printf("Invalid direction! Use 'R' or 'L'.\n");
return 0; // report FAILURE and stop here
}
motor(0, 0); motor(3, 0); msleep(50); // brake-settle (your usual stop)
return 1; // report SUCCESS
}Test Turn('R', 90.0) and Turn('l', 90.0). Did both work, even with the lowercase L? Why does accepting both cases make your function easier for someone else to use?
Now try a bad input like Turn('X', 90.0). What did the robot do, what got printed, and what did the function return?
Phase 5 — Test Your Model on Real Angles
Your model should now turn any angle. Test a range, both directions, and measure how close each lands. Remember: it’s a best-fit, so expect small errors — especially on bigger angles.
| Try | Turn call | Actual angle turned (degrees) |
|---|---|---|
| 1 | Turn(‘R’, 90.0) | |
| 2 | Turn(‘L’, 45.0) | |
| 3 | ||
| 4 |
How close were your turns to the angles you asked for? Were small angles or big angles more accurate? Why might that be?
Phase 6 — Add to & Connect
Add ticks_per_degree and your Turn function to your library. Now any mission can turn any angle, either direction, with one readable call — and you can retire the old fixed 90° turns.
AI Literacy Thread
Models are best-fit approximations — never perfect, but good enough to act on.
Your three calibration runs disagreed, and no single ticks_per_degree was perfect. That’s not failure — that’s how models work everywhere in AI. A weather model, a self-driving car’s physics, a language model’s predictions: none are exactly right. They’re the best fit to messy real-world data, good enough to act on while never being flawless. The skill isn’t finding a perfect model — it’s finding one that fits well enough and knowing its limits.
Read each scenario. Think it through, then write your answer.
Why is it impossible to find one ticks_per_degree that turns every angle perfectly? Connect this to why real AI models are never 100% accurate.
Your Turn function returns 1 for success and 0 for failure. Why is it useful for a function to report back whether it worked?
Phase 7 — Individual Reflection
Complete this section on your own.
1. What is a char, and why must 'R' use single quotes?
2. Explain the three parts of a for loop, using your calibration spin as the example.
3. Your Turn takes two parameters and returns a value. What are the two inputs, and what does the return value tell you?
4. Complete in 2–3 sentences: “Models are best-fit approximations, never perfect. This means that when my robot turns, I should expect…”
Extension Challenges
Finished early? Try one or more of these.
Extension A — Slow vs. Fast
- Recalibrate at a faster
mavspeed. Does the robot overshoot more from inertia? How does that change your bestticks_per_degree?
Extension B — A Full Circle Test
- Use a
forloop to callTurn('R', 90.0)four times. Does the robot return to its start? Compare to your old fixed turns.
Extension C — Check the Return Value
- Store the return:
int ok = Turn('X', 90.0);thenprintfwhether it succeeded. How could a mission use that to react to a failed turn?
Extension D — Retire the Old Turns
- Find an old program that used
turn_left()/turn_right()and replace them withTurn. Is the new version easier to read and change?
Extension E — The Recursive Version
- Extension B used a
forloop to callTurn('R', 90.0)four times. A recursive function could do the same thing by calling itself: a function that turns once, then calls itself again with one fewer turn remaining, until it hits zero. - Sketch (in words or ) what that recursive version would look like. Why might a loop be the more natural choice than recursion for this particular task?
When you are finished, press the button to turn in your work and save a copy.
KIPR · Botball Explorer · Unit 4 Big Idea 3 — Student Lab