Loops
for loops, foreach loops, and the very important reason while loops are banned from robot code :D
For Loops
ok so imagine your teacher gives you a punishment: write the sentence "I will not throw a gear across the shop" 10 times. you COULD write it out 10 separate times — copy paste it, tab over, repeat. or you could just say "do this 10 times" and let something handle the repetition automatically. that's literally what a loop is. it takes a piece of code and runs it over and over without you writing it out each time.
the for loop is the workhorse. it's what you'll reach for the most in Java, and especially in FRC code. it's built for situations where you know (or can figure out) exactly how many times you want to loop — like "go through all 4 drive motors" or "check each of the 8 sensor readings" or "process every module in my swerve drive (swerve is a drivetrain where each wheel can spin and rotate independently, letting the robot move in any direction without turning first)." when the count is known, for is your guy.
the for loop anatomy
picture a factory assembly line. a conveyor belt carries items past a worker, one at a time. the worker does the exact same job on each item — inspect it, stamp it, move it on. there's a counter keeping track of how many items have been processed. when the counter hits the target number, the belt stops. that's a for loop: a counter, a job to do, and a stopping condition.
in code, the "item on the belt" is whatever index you're currently at. the "job" is the code inside the curly braces. the "stopping condition" is the check that decides whether to keep going or quit. you write all three of those pieces right there in the loop header, and Java takes care of the rest.
here's why this matters in real robot code: your swerve drive has 4 modules. you'll need to configure each one. you COULD write 4 separate blocks of code — one for each module. but if you ever need to change how configuration works, you'd have to change it in 4 places. with a loop, you change it once. modularity is everything in a codebase that 15 people are maintaining.
the three parts of a for loop
a for loop has three pieces crammed into one line, separated by semicolons. here's what each one does and when it runs:
i++ adds 1, i-- subtracts 1, i += 2 skips every other.// the anatomy of a for loop for (int i = 0; i < 5; i++) { // everything in here runs 5 times System.out.println("Iteration: " + i); } // ^--- init ^----- condition ^-- step // after the loop, i is gone — it only exists inside the loop header + body
step-by-step execution trace
the order of what runs when is not obvious just from reading the syntax, so let's trace through it manually. this is something you should do in your head whenever a loop is confusing you.
for (int i = 0; i < 4; i++) { System.out.println("i is: " + i); } // Here's exactly what Java does, in order: // Step 1: init runs once → i = 0 // Step 2: check condition → is 0 < 4? YES → run body → prints "i is: 0" // Step 3: step runs → i++ → i is now 1 // Step 4: check condition → is 1 < 4? YES → run body → prints "i is: 1" // Step 5: step runs → i++ → i is now 2 // Step 6: check condition → is 2 < 4? YES → run body → prints "i is: 2" // Step 7: step runs → i++ → i is now 3 // Step 8: check condition → is 3 < 4? YES → run body → prints "i is: 3" // Step 9: step runs → i++ → i is now 4 // Step 10: check condition → is 4 < 4? NO → EXIT LOOP // Final output: "i is: 0", "i is: 1", "i is: 2", "i is: 3"
notice the step ALWAYS runs before the condition check — not after. and the condition check ALWAYS happens before the body runs. this order matters. if you forget it, loops with tricky conditions will break in ways you won't expect.
wait — why does it start at 0?
computers count from 0, not 1. this is called zero-indexing and it shows up everywhere in programming. arrays, lists, loops — they all start at 0. if you want to run a loop 5 times, you go 0, 1, 2, 3, 4 (not 1, 2, 3, 4, 5). once you get used to it, it feels natural. for now just memorize the pattern:
the pattern to memorize: for (int i = 0; i < N; i++) runs exactly N times. i goes from 0 to N-1. this is the most common loop you'll ever write. if you want to run something 10 times, use i < 10. want 4 times? i < 4. start at 0, use strictly-less-than.
FRC example — iterating over swerve modules
here's where this actually matters in robot code. a swerve drivetrain has 4 modules (each of the 4 corners of the robot; each module has a drive wheel and a steering mechanism): front-left, front-right, back-left, back-right. when you initialize the drive system, you need to configure each module. instead of copying the same code 4 times:
int[] driveMotorIDs = {1, 2, 3, 4}; // front-left, front-right, back-left, back-right int[] steerMotorIDs = {5, 6, 7, 8}; // configure all 4 modules in one loop instead of 4 separate blocks for (int i = 0; i < driveMotorIDs.length; i++) { System.out.println("Configuring module " + i + ": drive=" + driveMotorIDs[i] + ", steer=" + steerMotorIDs[i]); // in real code: swerveModules[i].configure(driveMotorIDs[i], steerMotorIDs[i]); } // driveMotorIDs.length is 4, so i goes 0, 1, 2, 3 // if you add a 5th module later, the loop automatically adjusts — zero maintenance
array.length gives you the number of elements in an array. using it in your loop condition instead of a hardcoded number is good practice — if you add or remove elements later, the loop automatically adjusts. hardcoded numbers in loops are a maintenance trap.
loops that count down or skip
the three-part format is flexible. you can count backwards, skip every other number, count by 5s — whatever you need. the key insight is that init, condition, and step are completely independent. you define the behavior you want:
// count DOWN from 5 to 1 (like a launch countdown) for (int i = 5; i > 0; i--) { System.out.println(i); // prints 5, 4, 3, 2, 1 } // step by 2 — only even numbers for (int i = 0; i < 10; i += 2) { System.out.println(i); // prints 0, 2, 4, 6, 8 } // backwards through an array — last element first int[] ids = {1, 2, 3, 4}; for (int i = ids.length - 1; i >= 0; i--) { System.out.println(ids[i]); // prints 4, 3, 2, 1 // start at index 3 (length-1), go DOWN to 0 inclusive }
that backwards loop is worth understanding. ids.length - 1 gives the index of the last element (since arrays are zero-indexed, a 4-element array has valid indices 0, 1, 2, 3 — the last is 3 = length-1). and we use >= 0 as the condition because we still want to process index 0.
the off-by-one gotcha (this WILL bite you)
story time: the single most common bug in any kind of loop-based code is the off-by-one error. it's exactly what it sounds like — you're off by one iteration. either the loop runs once too many times, or once too few. and the worst part is that it often doesn't crash — it just produces slightly wrong output that's hard to notice until something breaks at the worst possible moment.
int[] swerveModules = {0, 1, 2, 3}; // 4 elements, indices 0-3 // BAD: <= length instead of < length for (int i = 0; i <= swerveModules.length; i++) { System.out.println(swerveModules[i]); // CRASH when i = 4! } // when i = 4, swerveModules[4] doesn't exist // Java throws ArrayIndexOutOfBoundsException — match over
int[] swerveModules = {0, 1, 2, 3}; // CORRECT: strictly less than, not less-than-or-equal for (int i = 0; i < swerveModules.length; i++) { System.out.println(swerveModules[i]); // i goes 0, 1, 2, 3 — all valid } // the rule: i < length (not <=) when accessing array indices from 0
the rule to remember: i < array.length is almost always what you want. i <= array.length goes one past the end and crashes. one character difference. super easy to typo. when a loop crashes with ArrayIndexOutOfBoundsException, this is the first thing you check.
infinite loop: if the condition NEVER becomes false, the loop runs forever and your program freezes. example: for (int i = 0; i >= 0; i++) — i starts at 0 and only goes up, so i >= 0 is always true. no exit. in a desktop program this just locks up your app. in robot code this is worse — keep reading the while loop section for why.
Topic 1 — Coding Prompt
Start with: int[] motorIDs = {10, 20, 30, 40, 50};
Write a for loop that goes through every element. For each motor:
• print a line like "Motor 0: ID 10" — the number is the index, the ID is the array value
• if the ID is greater than 35, also print: "Motor X: ID Y — HIGH ID, check wiring"
Which motors in this array should trigger the warning?
Topic 1 — Quick Check
Foreach Loops
the regular for loop says "go through positions 0, 1, 2, 3 and give me the thing at each spot." it needs you to manage an index variable, check the length, all of that. the foreach loop says something simpler: "give me each thing in this collection, one at a time. i don't need to know what position it's at." when all you're doing is reading each value, foreach is cleaner and harder to mess up.
you'll use foreach constantly in FRC code for things like reading sensor arrays, logging subsystem states, or checking a list of conditions. any time you're going front-to-back through a collection and don't need the index, foreach is the move.
foreach — cleaner iteration
think of a teacher taking attendance. she doesn't call out "student number 0, student number 1" — she just goes "for each student in this room, call their name." she doesn't care about the position. she just wants to hit every person, one at a time, from start to finish. that's a foreach loop.
in Java, the foreach is written with a colon instead of semicolons. you name the type and a variable that will hold the current element, then put the collection after the colon. each pass through the loop, that variable gets the next element automatically. no counter, no index, no length check.
// syntax: for (type variableName : collectionToLoopThrough) double[] speeds = {0.5, 0.75, 1.0, -0.5}; for (double speed : speeds) { // 'speed' is automatically each element, one per iteration System.out.println("Speed: " + speed); } // reads as: "for each double called 'speed' in the 'speeds' array, do this" // prints: Speed: 0.5 / Speed: 0.75 / Speed: 1.0 / Speed: -0.5
read it out loud: "for each double called speed in speeds." that's exactly what it does. no counting, no indexing, no off-by-one risk. Java handles the iteration behind the scenes.
foreach vs regular for — same task, two ways
both of these do the same thing. use this comparison to see exactly what foreach is saving you from writing:
double[] sensorReadings = {12.4, 13.1, 11.8, 14.0}; // regular for loop — need to manage index, length, and access manually for (int i = 0; i < sensorReadings.length; i++) { System.out.println(sensorReadings[i]); } // foreach — cleaner when you only need the value, not the index for (double reading : sensorReadings) { System.out.println(reading); }
both do the same thing. the foreach version has fewer things that can go wrong — no off-by-one, no length check mistake, no accidental wrong index. when you're reviewing code at 11pm before a competition, that matters.
the copy gotcha — this WILL confuse you
here's the catch with foreach, and it's important enough to dedicate a whole section to. the foreach variable is a copy of each element, not the actual slot in the array. if you change it, you're changing the copy. the original array stays exactly the same.
double[] speeds = {0.5, 0.75, 1.0}; // WRONG — looks like it scales everything by 0.8, but it doesn't for (double speed : speeds) { speed = speed * 0.8; // modifies the LOCAL copy of 'speed' — NOT speeds[i] } System.out.println(speeds[0]); // still 0.5 — nothing changed in the array! // CORRECT — use a regular for loop to write back to the array for (int i = 0; i < speeds.length; i++) { speeds[i] = speeds[i] * 0.8; // writes to the actual array slot } System.out.println(speeds[0]); // now 0.4 — correctly changed
foreach gotcha: the foreach variable is a copy of each primitive value, not a reference to the original slot. modifying it does nothing to the array. use a regular for loop whenever you need to write back to the array.
this trips people up even after years of experience. the mental model that helps: foreach is "read mode." you're visiting each element to look at it. if you want to edit it, you need to use a regular for loop so you have the index to write back with.
when to use which loop type
arr[i] = ...). foreach gives you a copy — you need the index to actually write back to the array.FRC example — reading all sensor values
this is exactly what foreach is made for. you want to scan every sensor in an array and react to what you see. no need for the index — you just need each value:
double[] distanceSensors_in = {14.2, 9.8, 22.1, 11.5}; double total = 0.0; int warningCount = 0; // foreach is perfect here — just reading, no writes, no index needed for (double dist : distanceSensors_in) { total += dist; // accumulate for average if (dist < 12.0) { System.out.println("Warning: obstacle close at " + dist + " in"); warningCount++; } } double avg = total / distanceSensors_in.length; System.out.println("Average distance: " + avg + " in"); System.out.println("Warnings triggered: " + warningCount);
Topic 2 — Coding Prompt
Start with: double[] voltages = {11.8, 12.4, 9.1, 12.7, 10.5, 12.1};
Use a foreach loop (not a regular for loop) to go through every voltage. Inside the loop:
• add each voltage to a running total
• if the voltage is below 11.0, increment a warning counter
After the loop, print the total sum and how many low-battery warnings there were.
Hint: declare double total = 0; and int warnings = 0; before the loop.
Topic 2 — Quick Check
While Loops & Why They're Banned
ok, we need to have a talk. while loops are a perfectly normal, fundamental part of Java. you'll use them in regular desktop and console programs. they're not bad. but in FRC robot code, they are effectively banned from periodic methods — and it's not an arbitrary rule somebody made up. it comes from a real, documented failure mode that has actually ended robots' matches at real competitions. let's understand what a while loop is, why it's useful normally, and why it's so dangerous in robot code specifically.
while loops — and why they're banned
imagine doing dishes. you stand at the sink and keep washing while there are still dirty dishes. you don't know in advance how many dishes there are — you just keep going until there aren't any left. that's a while loop: you don't know the count up front, so you keep looping as long as some condition is true.
for loops are for "do this exactly N times." while loops are for "do this until something changes." that distinction is what makes while loops useful in certain contexts — specifically, when you genuinely don't know how many iterations you'll need.
// basic while loop — same structure as a for loop, just more explicit int count = 0; while (count < 5) { System.out.println(count); count++; // YOU are responsible for moving the counter — easy to forget! } // prints 0, 1, 2, 3, 4 — same result as a for loop
notice: unlike a for loop, the counter variable lives OUTSIDE the loop. the step is inside the body — YOU have to remember to write it. a for loop's structure forces you to handle all three pieces (init, condition, step) right there in the header. a while loop doesn't. this is why forgetting the step in a while loop is one of the most common bugs beginners write.
when while loops are fine (and actually good)
in a console program running on a normal computer, while loops are super natural. here are real use cases where they're the right tool:
// keep asking until valid input — you don't know how many tries it takes while (!userHasEnteredValidInput) { promptUserForInput(); } // game loop — keep playing until the player quits while (!playerHasQuit) { updateGame(); renderFrame(); } // read lines from a file until we hit the end while (scanner.hasNextLine()) { String line = scanner.nextLine(); processLine(line); }
all of these are totally fine on a regular computer. the program just... waits. nobody cares. your desktop app isn't going to explode if it blocks for a second. but this exact pattern is what kills robots at competition.
the FRC problem — the 20ms loop
here's what you need to understand about how FRC robots actually run. your robot's control loop runs on a 20 millisecond cycle. every 20ms, WPILib (the FRC framework) calls your code to say "hey, update yourself." it reads the latest joystick input from the driver station (the laptop at the driver's station running the FRC Driver Station software -- it connects wirelessly to the robot), updates motor commands, reads sensors, updates dashboards — all of that runs 50 times per second, like clockwork.
the framework EXPECTS your code to run fast and return control within that 20ms window. run, return. run, return. over and over. every piece of your robot's responsiveness — joystick response, motor updates, safety checks — depends on your code completing quickly every single cycle and handing control back.
here's what a while loop does to this. your periodic method gets called. inside it, there's a while (!atTarget) { move(); }. Java starts running the while loop. the target isn't reached yet, so it loops. again. again. still looping. 20ms pass. 40ms. 100ms. the framework can't get control back because your while loop has it. motors stop being commanded because nobody is calling them. the driver pushes the joystick and nothing happens. the system logs a warning. then the watchdog fires.
the watchdog — the robot's dead-man's switch
the watchdog is a safety timer built into WPILib. if your code doesn't return control to the framework within a few hundred milliseconds, the watchdog assumes something is catastrophically wrong and automatically disables the robot. it cuts motor output. everything stops. the robot goes limp.
this is actually the RIGHT call — a frozen, unresponsive robot on a field with other robots and field elements is genuinely dangerous. but it means your robot just became a paperweight in the middle of a match because your while loop was waiting for a sensor that took too long to settle.
// DO NOT DO THIS inside any periodic method in robot code public void teleopPeriodic() { // this blocks the 20ms cycle until the encoder reaches target // if the encoder is slow or stuck, this runs forever // the watchdog fires, the robot disables mid-match while (!arm.isAtTarget()) { arm.driveToTarget(); // BAD: blocks the entire framework } } // the right pattern for "keep doing this until something happens" is // a state machine that checks once per cycle and returns immediately // you'll learn state machines in Phase 2 — for now, just know: no while loops
a real story — while loops at competition
this isn't hypothetical. during a regional qualification match, a team's autonomous (the 15-second period at the start of a match where the robot runs without driver input) routine had a while loop waiting for an arm encoder to reach a target position. the encoder was slow to settle due to mechanical slop in the gearbox. the while loop kept running. 50ms. 100ms. 200ms. the watchdog fired. the robot disabled for the rest of the auto period.
when teleop (teleop = the 2-minute driver-controlled period of the match) started, the robot was oriented the wrong direction because auto never finished. it drove forward and immediately hit a field element. the team got a foul. they missed playoffs by one match. one while loop. one competition. don't be that team.
to be fair: while loops are completely fine in the console programs this course has you build. the ban is specific to FRC robot periodic methods where the 20ms cycle is sacred. you'll write plenty of while loops this course — just never inside teleopPeriodic(), autonomousPeriodic(), or any method that runs on the robot's loop.
what to use instead in robot code: when you need "keep doing this until something happens" behavior, the answer is state machines and the WPILib Command-Based framework. your state machine checks conditions once per 20ms cycle and decides what to do next — it never blocks. you'll learn Command-Based programming in Phase 2 and it will make total sense once you've seen why blocking loops are a problem.
the infinite loop gotcha
the most classic while loop mistake is forgetting to update the condition variable, creating an infinite loop that never exits. with a for loop, the step is right there in the header — you have to write it. with a while loop, nothing reminds you:
// BAD — forgot to increment count, loops forever int count = 0; while (count < 5) { System.out.println(count); // oops — forgot count++ here // count stays 0 forever, 0 < 5 is always true, program freezes } // FIXED int count = 0; while (count < 5) { System.out.println(count); count++; // now count moves toward the exit condition }
if your console program hangs and won't respond, 9 times out of 10 you have an infinite loop. check your while loop conditions and make sure something inside the loop is actually moving toward making the condition false.
Topic 3 — Coding Prompt
Write a while loop that counts up from 1 and keeps going until it finds the first number divisible by both 3 and 7.
• start with int n = 1;
• each iteration: print the current value of n, then add 1
• the loop condition should keep running while n is NOT divisible by both 3 and 7
• after the loop, print "Found it: " + n
What number should it stop on? Verify it manually before running.
Topic 3 — Quick Check
Loop Visualizer
adjust the values and watch the loop execute.
Fill in the Blanks
for (int i = 0; i ; ) { }
for (double : readings) { }
for (int i = 5; i > 0; ) { }
Knowledge Check
Coding Challenge
Start with: double[] readings = {12.4, 13.1, 11.8, 14.0, 12.7};
Use a regular for loop (with an index variable) to:
• add up all readings, then after the loop print the average (sum ÷ number of readings)
• track the highest value seen — after the loop print it
• for any reading below 12.0, print a warning like "Reading 2: 11.8 — LOW"
Create SensorProcessor.java in your minibot-project folder. This class processes a batch of sensor readings — simulating how a robot analyzes data from multiple distance sensors at once.
- Declare a
static double[] processSensorReadings(double[] rawReadings)method - Use a regular for loop to iterate through all readings
- Filter out any readings below 0.0 (replace with 0.0 — sensor noise)
- Cap any readings above 120.0 inches at 120.0 (max sensor range)
- Return the cleaned array
- Also write
static double findAverage(double[] readings)using a foreach loop - Also write
static double findMax(double[] readings)using a regular for loop - In a
mainmethod, test with:{-2.0, 14.5, 150.0, 12.1, 0.5, 88.3}
Weekly Test
covers everything from week 3. your score gets sent to the leads :) try without looking back first!!