25  Nested Loops: Parzival’s Complex Quests

In our previous lessons, we mastered the for loop and the while loop. Today, we embark on an epic adventure to combine these powerful tools: nested loops. Just as Parzival must navigate complex mazes and multi-layered challenges in his quest for the Holy Grail, nested loops allow us to create more intricate and sophisticated programs.

25.1 What are Nested Loops?

Nested loops are simply loops within loops. Imagine Parzival exploring a multi-level dungeon, where he must search every room on every floor. The outer loop could represent the floors, while the inner loop represents the rooms on each floor.

25.2 Nested For Loops: Exploring the Dungeon

Let’s start with a simple example of nested for loops. We’ll help Parzival explore a dungeon with 3 floors, each containing 4 rooms:

for floor in range(1, 4):  # 3 floors
    print(f"Parzival enters floor {floor}")
    for room in range(1, 5):  # 4 rooms per floor
        print(f"  Searching room {room} on floor {floor}")
    print(f"Parzival finishes exploring floor {floor}\n")

print("Dungeon fully explored!")

This will output:

Parzival enters floor 1
  Searching room 1 on floor 1
  Searching room 2 on floor 1
  Searching room 3 on floor 1
  Searching room 4 on floor 1
Parzival finishes exploring floor 1

Parzival enters floor 2
  Searching room 1 on floor 2
  Searching room 2 on floor 2
  Searching room 3 on floor 2
  Searching room 4 on floor 2
Parzival finishes exploring floor 2

Parzival enters floor 3
  Searching room 1 on floor 3
  Searching room 2 on floor 3
  Searching room 3 on floor 3
  Searching room 4 on floor 3
Parzival finishes exploring floor 3

Dungeon fully explored!

In this example, the outer loop iterates over the floors, while the inner loop iterates over the rooms on each floor.

25.3 Nested While Loops: The Training Montage

Now, let’s use nested while loops to create a training montage for Parzival. He’ll train for a number of days, and each day he’ll practice until he’s too tired to continue:

import random

days_of_training = 0
total_skills_improved = 0

while days_of_training < 7:  # Train for a week
    days_of_training += 1
    print(f"\nDay {days_of_training} of training:")
    
    energy = 100
    daily_skills_improved = 0
    
    while energy > 0:
        skill_improvement = random.randint(1, 10)
        energy_cost = random.randint(10, 25)
        
        if energy >= energy_cost:
            energy -= energy_cost
            daily_skills_improved += skill_improvement
            print(f"  Parzival practiced and improved his skills by {skill_improvement} points. Energy left: {energy}")
        else:
            print("  Parzival is too tired to continue training today.")
            break
    
    total_skills_improved += daily_skills_improved
    print(f"Skills improved today: {daily_skills_improved}")

print(f"\nTraining complete! Total skills improved over the week: {total_skills_improved}")

This script simulates a week of training, where each day Parzival practices until he runs out of energy. The outer loop tracks the days, while the inner loop simulates the practice sessions each day.

25.4 Combining For and While Loops: The Gauntlet Challenge

Let’s create a more complex challenge for Parzival using a combination of for and while loops. He must face a gauntlet of enemies on each floor of a tower:

import random

tower_floors = 5
parzival_health = 100

for floor in range(1, tower_floors + 1):
    print(f"\nParzival enters floor {floor} of the tower.")
    enemies_defeated = 0
    
    while parzival_health > 0:
        enemy_strength = random.randint(10, 20)
        print(f"  Parzival encounters an enemy with {enemy_strength} strength!")
        
        if random.random() < 0.7:  # 70% chance to defeat the enemy
            print("  Parzival defeats the enemy!")
            enemies_defeated += 1
            if enemies_defeated == 3:
                print(f"Parzival has cleared floor {floor}!")
                break
        else:
            damage = random.randint(5, 15)
            parzival_health -= damage
            print(f"  Parzival takes {damage} damage. Health remaining: {parzival_health}")
    
    if parzival_health <= 0:
        print("Parzival has fallen! The tower challenge is over.")
        break

if parzival_health > 0:
    print("\nCongratulations! Parzival has conquered the tower!")
else:
    print(f"\nParzival made it to floor {floor} before falling.")

In this challenge, the for loop represents the floors of the tower, while the while loop simulates the battles on each floor. Parzival must defeat three enemies to clear a floor, but if his health reaches zero, the challenge ends.

25.5 Practice Time: Your Nested Loop Quests

Now it’s your turn to create complex challenges using nested loops. Complete these quests to prove your mastery:

  1. Create a nested loop structure that represents Parzival searching a 3x3 grid for the Holy Grail. Each cell should have a random chance of containing the Grail.

  2. Simulate a tournament where Parzival must win a best-of-3 match against 4 opponents. Use an outer loop for the opponents and an inner loop for the individual matches.

  3. Create a “potion brewing” game where Parzival must correctly guess the ingredients for 3 potions. Use nested loops to allow multiple guesses for each potion.

  4. Design a “dungeon crawler” where Parzival explores a 5x5 grid. Use nested loops to move through the grid, and randomly place treasures and monsters in some cells.

Here’s a starting point for your quests:

import random

# Quest 1: Searching for the Holy Grail
grid_size = 3
# Your code here

# Quest 2: Tournament Challenge
opponents = ["Sir Lancelot", "Merlin", "Morgan le Fay", "The Green Knight"]
# Your code here

# Quest 3: Potion Brewing Game
potions = ["Health", "Strength", "Invisibility"]
ingredients = ["Dragon scale", "Unicorn hair", "Phoenix feather", "Troll tooth", "Fairy dust"]
# Your code here

# Quest 4: Dungeon Crawler
dungeon_size = 5
# Your code here

25.6 Common Bugs to Watch Out For

As you work with nested loops, be wary of these common pitfalls:

  1. Infinite loops: Be extra careful with your loop conditions in nested structures. It’s easier to create infinite loops when nesting.

  2. Incorrect indentation: With nested loops, proper indentation is crucial. Make sure each loop and its contents are correctly indented.

  3. Confusion between loop variables: When nesting loops, make sure you use different variable names for each loop to avoid confusion.

  4. Unnecessary nesting: Sometimes, complex nested structures can be simplified. Always look for ways to make your code more efficient and readable.

  5. Off-by-one errors: Be especially careful with your loop ranges in nested structures. It’s easy to miss the first or last iteration.

25.7 Conclusion and Further Resources

Congratulations, master code weavers! You’ve now unlocked the power of nested loops, allowing you to create complex, multi-layered programs. Just as Parzival must navigate intricate challenges in his quests, you can now craft sophisticated algorithms to solve complex problems.

To further enhance your nested loop skills, check out these excellent resources:

  1. Python’s official documentation on compound statements
  2. Real Python’s Python Nested Loops
  3. GeeksforGeeks Python Nested Loops

Remember, mastering nested loops is like becoming a grandmaster chess player - it takes practice to see all the moves and their consequences. Keep coding, keep experimenting, and soon you’ll be creating intricate programs with the elegance of a true coding knight!