BattleBot: Redux

Battle Data Analysis

Every robot on record has fought real battles, and this time you'll do the analysis for real. Download the actual battle log file, open it in IDLE, and write genuine Python file-processing code on your own computer — no simulation, no browser sandbox. Then visualise your findings and design the ultimate robot.

54
Logged battles
6
Robots on record
3
Sections to forge through
Section 1

Reading and Processing the Battle Log

Mission Brief

The Forge Command Centre has handed you battle_data.csv — a real file, a log of every battle fought by six robots. Your job is to write real Python file-processing code in IDLE: open the file, read it, and turn raw rows into useful statistics.

This saves an actual battle_data.csv file to your computer — the same file your Python program will open.

Getting Set Up in IDLE

  1. Click the download button above and make a new folder for this project — call it something like battlebot_redux.
  2. Move the downloaded battle_data.csv into that folder.
  3. Open IDLE. Go to File → New File.
  4. Save this new file into the same folder as your CSV — name it battle_analysis.py. This matters: open("battle_data.csv") only works if the CSV is in the same folder as the script you're running.
  5. Copy each starter code block below into your file as you go, complete the TODOs, then run your program with Run → Run Module (or press F5). Check your output in the IDLE Shell window.
1.1Open the file and read it back
import csv

# Open the file that's sitting in the same folder as this script
with open("battle_data.csv", "r") as f:
    lines = f.readlines()

print(f"The file contains {len(lines)} lines (including the header).")
print("First 5 lines:")
for line in lines[:5]:
    print(line.strip())
This is exactly how you'd process a file a teammate emails you: open() gives you a file object, .readlines() gives you every line as a string in a list. The with block automatically closes the file when you're done — no need to call f.close() yourself.
1.2Turn rows into dictionaries with csv.DictReader
import csv

with open("battle_data.csv", "r") as f:
    reader = csv.DictReader(f)
    battles = list(reader)   # each row becomes a dictionary

print(f"Loaded {len(battles)} battle records.")
print("\nExample record:")
print(battles[0])

# TODO: print the robot_name and result for the first 5 battles
# Your code here:
Solution
for battle in battles[:5]:
    print(battle["robot_name"], "-", battle["result"])
1.3Write a win_rate() function
import csv

with open("battle_data.csv", "r") as f:
    battles = list(csv.DictReader(f))

def win_rate(robot_name, battles):
    """Return the win rate (0-100) for a given robot, rounded to 1 decimal place."""
    # TODO:
    # 1. Filter 'battles' down to only rows where robot_name matches
    # 2. Count how many of those rows have result == "Win"
    # 3. Return (wins / total) * 100, rounded to 1 decimal place
    pass

print("Titanium Fang win rate:", win_rate("Titanium Fang", battles), "%")
print("Nova Striker win rate:", win_rate("Nova Striker", battles), "%")
Solution
def win_rate(robot_name, battles):
    robot_battles = [b for b in battles if b["robot_name"] == robot_name]
    wins = [b for b in robot_battles if b["result"] == "Win"]
    return round((len(wins) / len(robot_battles)) * 100, 1)
1.4Average damage dealt per robot
import csv

with open("battle_data.csv", "r") as f:
    battles = list(csv.DictReader(f))

def average_damage_dealt(robot_name, battles):
    """Return the average damage_dealt for a robot, rounded to 1 decimal place."""
    # Remember: values from a CSV file are always strings!
    # You'll need to convert damage_dealt to an int before doing maths on it.
    # TODO: complete this function
    pass

robots = ["Titanium Fang", "Volt Reaper", "Iron Wraith",
          "Crimson Gearhead", "Nova Striker", "Obsidian Juggernaut"]

for robot in robots:
    avg = average_damage_dealt(robot, battles)
    print(f"{robot:<20} avg damage dealt: {avg}")
Solution
def average_damage_dealt(robot_name, battles):
    robot_battles = [b for b in battles if b["robot_name"] == robot_name]
    damages = [int(b["damage_dealt"]) for b in robot_battles]
    return round(sum(damages) / len(damages), 1)
1.5Build the leaderboard
import csv

with open("battle_data.csv", "r") as f:
    battles = list(csv.DictReader(f))

def win_rate(robot_name, battles):
    robot_battles = [b for b in battles if b["robot_name"] == robot_name]
    wins = [b for b in robot_battles if b["result"] == "Win"]
    return round((len(wins) / len(robot_battles)) * 100, 1)

robots = ["Titanium Fang", "Volt Reaper", "Iron Wraith",
          "Crimson Gearhead", "Nova Striker", "Obsidian Juggernaut"]

# TODO: build a list of (robot_name, win_rate) tuples,
# then sort it so the highest win rate comes first,
# then print a numbered leaderboard, e.g.
# 1. Crimson Gearhead - 66.7%

leaderboard = []
# your code here
Solution
leaderboard = [(robot, win_rate(robot, battles)) for robot in robots]
leaderboard.sort(key=lambda pair: pair[1], reverse=True)

for i, (robot, rate) in enumerate(leaderboard, start=1):
    print(f"{i}. {robot} - {rate}%")
Section 2

Data Visualisation

Mission Brief

Numbers in a table are hard to compare at a glance — graphs make patterns jump out. Here you'll use Python's matplotlib library to turn the same battle log into charts, still running in IDLE against your real battle_data.csv.

Before you start

  1. Make sure matplotlib is installed. In IDLE, go to Run → Python Shell and type import matplotlib — if you get an error, ask your teacher to help you install it (usually pip install matplotlib from a command prompt).
  2. Keep working in the same folder as battle_data.csv. You can add these charts to battle_analysis.py or start a new file — either way, plt.show() will pop up a chart window when you run the program.
2.1Bar chart — wins per robot
import csv
import matplotlib.pyplot as plt

with open("battle_data.csv", "r") as f:
    battles = list(csv.DictReader(f))

robots = ["Titanium Fang", "Volt Reaper", "Iron Wraith",
          "Crimson Gearhead", "Nova Striker", "Obsidian Juggernaut"]

# TODO: build a list "win_counts" with the number of wins for each robot,
# in the same order as "robots"
win_counts = []
# your code here


plt.figure(figsize=(8, 4.5))
plt.bar(robots, win_counts, color="#2e8f6d")
plt.title("Wins per Robot")
plt.ylabel("Wins")
plt.xticks(rotation=25, ha="right")
plt.tight_layout()
plt.show()
Solution
win_counts = []
for robot in robots:
    wins = [b for b in battles if b["robot_name"] == robot and b["result"] == "Win"]
    win_counts.append(len(wins))
plt.show() opens a window with your chart in it. Close that window to let the rest of your program (or the Shell) continue.
2.2Scatter plot — damage dealt vs damage taken
import csv
import matplotlib.pyplot as plt

with open("battle_data.csv", "r") as f:
    battles = list(csv.DictReader(f))

# TODO: build three lists from 'battles':
#  - dealt: damage_dealt as an int, for every battle
#  - taken: damage_taken as an int, for every battle
#  - colors: "#2e8f6d" (green) if result == "Win" else "#c94f3d" (red)
dealt, taken, colors = [], [], []
# your code here


plt.figure(figsize=(6, 6))
plt.scatter(dealt, taken, c=colors, alpha=0.75, edgecolor="#101826")
plt.title("Damage Dealt vs Damage Taken")
plt.xlabel("Damage Dealt")
plt.ylabel("Damage Taken")
plt.plot([0, 100], [0, 100], linestyle="--", color="#5b6b85")  # reference line
plt.tight_layout()
plt.show()
Solution
for b in battles:
    dealt.append(int(b["damage_dealt"]))
    taken.append(int(b["damage_taken"]))
    colors.append("#2e8f6d" if b["result"] == "Win" else "#c94f3d")
2.3Your own chart — average energy used per robot
import csv
import matplotlib.pyplot as plt

with open("battle_data.csv", "r") as f:
    battles = list(csv.DictReader(f))

robots = ["Titanium Fang", "Volt Reaper", "Iron Wraith",
          "Crimson Gearhead", "Nova Striker", "Obsidian Juggernaut"]

# Challenge: no scaffolding this time!
# Calculate the average energy_used for each robot and plot it as a
# horizontal bar chart (hint: plt.barh(...)). Which robot is most
# energy efficient?

# your code here


plt.show()
Solution
avg_energy = []
for robot in robots:
    energies = [int(b["energy_used"]) for b in battles if b["robot_name"] == robot]
    avg_energy.append(sum(energies) / len(energies))

plt.figure(figsize=(8, 4.5))
plt.barh(robots, avg_energy, color="#f4b731")
plt.xlabel("Average Energy Used")
plt.title("Energy Efficiency per Robot")
plt.tight_layout()
Section 3

Build the Ultimate Robot

Mission Brief

Use everything the data taught you. You have 100 points to distribute across five stats. Adjust the sliders, watch the radar chart update live, then hit Simulate Battle to see how your build stacks up against the Forge's toughest boss bot, Obsidian Juggernaut Mk.II.

Points remaining 100
The outcome is modelled using the same formula shown in the Python function below — sliders decide your design, the maths decides your odds.
Extension: implement the battle simulator yourself in IDLE
def battle_sim(stats, boss):
    """
    stats/boss: dicts with keys armor, speed, weapon, energy, targeting (0-60 each)
    Returns a dict with win_probability and whether the player won.
    """
    import random

    player_power = (stats["weapon"] * 1.1 + stats["targeting"] * 0.6
                     + stats["speed"] * 0.4 + stats["armor"] * 0.3
                     + stats["energy"] * 0.2)
    boss_power = (boss["weapon"] * 1.1 + boss["targeting"] * 0.6
                   + boss["speed"] * 0.4 + boss["armor"] * 0.3
                   + boss["energy"] * 0.2)

    win_probability = player_power / (player_power + boss_power)
    won = random.random() < win_probability

    return {
        "win_probability": round(win_probability * 100, 1),
        "won": won
    }

# Try it yourself: build a "stats" dictionary matching the sliders above,
# a "boss" dictionary with armor=22, speed=18, weapon=28, energy=16, targeting=16,
# and call battle_sim(stats, boss). Run it a few times -- since it uses
# random.random(), does a robot with a higher win_probability always win?