Skip to content

🎮 Code Runner — Debug Python Code!

Bạn là Junior Data Analyst mới vào công ty DataMart Việt Nam. Ngày đầu tiên, senior giao cho bạn 7 đoạn Python code xử lý dữ liệu bán hàng — nhưng tất cả đều có bug! Mission: tìm và sửa lỗi trước khi CEO cần báo cáo chiều nay! 🐛


🎯 Mục tiêu học tập

Sau khi hoàn thành game, bạn sẽ:

  1. Nhận diện TypeError — lỗi kiểu dữ liệu khi tính toán
  2. Phát hiện NameError — biến chưa định nghĩa hoặc sai tên
  3. Sửa IndexError — truy cập phần tử ngoài phạm vi list
  4. Tránh off-by-one error — lệch chỉ số khi duyệt list/range
  5. Xử lý logic error — code chạy nhưng ra kết quả sai
  6. Debug file handling error — đọc/ghi file CSV, JSON sai cách
  7. Fix function error — return sai chỗ, thiếu parameter, scope sai

📜 Luật chơi

┌──────────────────────────────────────────────────────┐
│  BẠN = Bug Hunter 🐛🔍                               │
│  CODE = 7 đoạn Python xử lý dữ liệu bán hàng        │
│  MỖI VÒNG = 1 đoạn code có bug → chọn cách sửa đúng │
│  3 LỰA CHỌN mỗi vòng — chỉ 1 đáp án đúng            │
│  MỤC TIÊU = Thu thập ≥ 80 XP để đạt hạng Gold 🥇    │
└──────────────────────────────────────────────────────┘

Cách tính điểm mỗi vòng:

Thành phầnXP
Trả lời đúng+10 XP (Vòng 1–2), +12 XP (Vòng 3–4), +15 XP (Vòng 5–6), +18 XP (Vòng 7)
Trả lời sai+0 XP
Không dùng hint+2 XP bonus ⚡
Giải thích đúng loại bug+3 XP bonus 🧠

Tổng XP tối đa: 10+10+12+12+15+15+18 = 92 XP (chưa tính bonus)


🏆 Bảng xếp hạng & Huy hiệu

Ranks

HạngXPMô tả
🥇 Gold — Senior Debugger≥ 80 XPBạn là debug master! Sẵn sàng code Pandas
🥈 Silver — Bug Spotter≥ 55 XPTốt! Cần luyện thêm logic & file handling
🥉 Bronze — Code Newbie≥ 35 XPỔn cho ngày đầu — ôn lại data types & loops
💀 Game Over< 35 XPQuay lại đọc Buổi 7 rồi thử lại nhé!

Huy hiệu đặc biệt

BadgeĐiều kiệnMô tả
🎯 Perfect RoundĐúng 1 vòng + không dùng hintHoàn hảo!
🔥 Hot Streak3 vòng liên tiếp đúngChuỗi lửa!
🧠 Bug IdentifierGọi đúng tên loại bug ≥ 5 vòngBạn biết bug từ xa!
Speed DemonHoàn thành 7 vòng trong < 10 phútNhanh như chớp!
🏆 Full ClearĐúng tất cả 7 vòngHuyền thoại!
🐛 First BugĐúng vòng đầu tiênBước đầu tốt lành!
💡 No Hints NeededKhông dùng hint cả gameTự lực cánh sinh!
🛡️ Comeback KingSai 2 vòng đầu, đúng 5 vòng sauKhông bỏ cuộc!

🎲 Chỉ số theo dõi

Chỉ sốIconMô tảMục tiêu
Debug Accuracy🎯Số vòng trả lời đúng / tổng 7 vòng≥ 6/7
Speed⏱️Tổng thời gian hoàn thành game< 15 phút
Bug Types Found🐛Số loại bug khác nhau bạn nhận diện đúng≥ 5 loại

📋 Kịch bản chi tiết


🐛 Vòng 1: TypeError — "Cộng số với chuỗi" ⭐ Dễ

Đoạn code đọc doanh thu từ CSV và tính tổng — nhưng bị lỗi!

💬 Slack từ Senior DA:

"Em ơi, code tính tổng doanh thu bị crash — chạy được vài dòng thì báo lỗi. Em xem sửa giúp anh nhé!"

Code có lỗi:

python
import csv

def calculate_total_revenue(filename):
    total = 0
    with open(filename, "r") as file:
        reader = csv.DictReader(file)
        for row in reader:
            total = total + row["revenue"]  # 💥 Bug ở đây!
    return total

result = calculate_total_revenue("sales.csv")
print(f"Tổng doanh thu: {result:,} VNĐ")

Lỗi hiển thị: TypeError: unsupported operand type(s) for +: 'int' and 'str'

Lựa chọn:

Cách sửaDòng sửa
Atotal = total + int(row["revenue"])Dòng 8
Btotal = str(total) + row["revenue"]Dòng 8
Ctotal = total + float(row["revenue"])Dòng 8
💡 Hint (−2 XP)

csv.DictReader đọc tất cả giá trị dưới dạng str — kể cả số. Cần chuyển đổi kiểu dữ liệu trước khi cộng. Doanh thu có thể có số thập phân (VD: 1500000.50).

Đáp án & Giải thích

✅ Đáp án đúng: Ctotal = total + float(row["revenue"])

Loại bug: TypeError — cộng int với str

Giải thích: csv.DictReader trả về tất cả values dưới dạng str. Dòng row["revenue"] trả về "1500000" (string), không phải 1500000 (number). Dùng float() thay vì int() vì doanh thu có thể có số thập phân. Đáp án B sai vì nối chuỗi, không phải cộng số!

XP: +10 | Bonus: +2 (no hint), +3 (đúng loại bug)


🐛 Vòng 2: NameError — "Biến chưa tồn tại" ⭐ Dễ

Code phân loại đơn hàng theo giá trị — nhưng biến bị sai tên!

💬 Slack từ Marketing Manager:

"Em chạy script phân loại đơn hàng thì báo lỗi NameError — em check lại code giùm chị nhé!"

Code có lỗi:

python
def classify_order(amount):
    if amount >= 5000000:
        category = "VIP"
    elif amount >= 1000000:
        category = "Standard"
    else:
        categori = "Basic"  # 💥 Bug ở đây!
    return category

orders = [500000, 1200000, 8000000, 300000]
for order_amount in orders:
    print(f"{order_amount:,} VNĐ → {classify_order(order_amount)}")

Lỗi hiển thị: NameError: name 'category' is not defined (khi amount < 1000000)

Lựa chọn:

Cách sửaDòng sửa
ASửa categori = "Basic" thành category = "Basic"Dòng 7
BThêm category = "" ở đầu functionDòng 2
CSửa return category thành return categoriDòng 8
💡 Hint (−2 XP)

Xem kỹ cách viết tên biến — Python phân biệt chữ hoa/thường và từng ký tự. So sánh tên biến ở nhánh else với các nhánh khác.

Đáp án & Giải thích

✅ Đáp án đúng: A — Sửa typo categoricategory

Loại bug: NameError — biến category chưa được gán trong nhánh else (vì gán vào categori)

Giải thích: Ở nhánh else, typo categori (thiếu chữ y) tạo ra biến mới thay vì gán vào category. Khi return category, biến này chưa được gán → NameError. Đáp án B "chữa cháy" nhưng không sửa gốc bug. Đáp án C return sai biến.

XP: +10 | Bonus: +2 (no hint), +3 (đúng loại bug)


🐛 Vòng 3: IndexError — "Vượt phạm vi list" ⭐⭐ Trung bình

Code lấy top 3 sản phẩm bán chạy — nhưng list chỉ có 2 phần tử!

💬 Slack từ Product Manager:

"Script lấy top 3 bị crash — anh nghĩ có category nào chỉ có ít sản phẩm thôi."

Code có lỗi:

python
def get_top3_products(sales_data):
    sorted_products = sorted(sales_data, key=lambda x: x["revenue"], reverse=True)
    top3 = [
        sorted_products[0]["name"],
        sorted_products[1]["name"],
        sorted_products[2]["name"],  # 💥 Bug khi list < 3 phần tử!
    ]
    return top3

# Category "Sách" chỉ có 2 sản phẩm
books = [
    {"name": "Python Data Science", "revenue": 5000000},
    {"name": "SQL Cookbook", "revenue": 3200000},
]
print(get_top3_products(books))

Lỗi hiển thị: IndexError: list index out of range

Lựa chọn:

Cách sửa
Atop3 = [p["name"] for p in sorted_products[:3]] — dùng slicing
BThêm if len(sorted_products) >= 3: trước khi truy cập index
Ctop3 = sorted_products[0:3]["name"] — slice rồi lấy name
💡 Hint (−2 XP)

Python list slicing (list[:3]) không bao giờ gây IndexError — nếu list có ít hơn 3 phần tử, nó trả về tất cả phần tử có. Cách nào dùng slicing đúng cú pháp?

Đáp án & Giải thích

✅ Đáp án đúng: A — Dùng list comprehension + slicing sorted_products[:3]

Loại bug: IndexError — truy cập index cứng khi list không đủ phần tử

Giải thích: sorted_products[2] crash khi list chỉ có 2 phần tử (index 0, 1). Slicing [:3] an toàn — trả về tối đa 3, không crash nếu ít hơn. Đáp án B hoạt động nhưng không xử lý trường hợp < 3 (bỏ qua luôn). Đáp án C sai cú pháp — không thể ["name"] trên list.

XP: +12 | Bonus: +2 (no hint), +3 (đúng loại bug)


🐛 Vòng 4: Off-by-one Error — "Thiếu 1 tháng" ⭐⭐ Trung bình

Code tính doanh thu Q4 (tháng 10, 11, 12) — nhưng thiếu tháng 12!

💬 Slack từ CFO:

"Tổng doanh thu Q4 sao thấp quá — em check lại xem có đúng 3 tháng không?"

Code có lỗi:

python
monthly_revenue = {
    1: 50000000, 2: 45000000, 3: 62000000,
    4: 58000000, 5: 71000000, 6: 68000000,
    7: 75000000, 8: 82000000, 9: 79000000,
    10: 95000000, 11: 110000000, 12: 135000000,
}

def calculate_quarter_revenue(monthly_data, quarter):
    """Tính doanh thu theo quý. Q1=1-3, Q2=4-6, Q3=7-9, Q4=10-12."""
    start_month = (quarter - 1) * 3 + 1
    end_month = quarter * 3
    total = 0
    for month in range(start_month, end_month):  # 💥 Bug ở đây!
        total += monthly_data[month]
    return total

q4_revenue = calculate_quarter_revenue(monthly_revenue, 4)
print(f"Doanh thu Q4: {q4_revenue:,} VNĐ")
# Kết quả: 205,000,000 — THIẾU tháng 12 (135,000,000)!

Lựa chọn:

Cách sửaDòng sửa
Afor month in range(start_month, end_month + 1):Dòng 13
Bend_month = quarter * 3 + 1Dòng 11
Cfor month in range(start_month, end_month): và sửa start_month = (quarter - 1) * 3Dòng 10 + 13
💡 Hint (−2 XP)

range(10, 12) trong Python tạo ra [10, 11]không bao gồm số cuối! Q4 cần tháng 10, 11, 12.

Đáp án & Giải thích

✅ Đáp án đúng: Arange(start_month, end_month + 1)

Loại bug: Off-by-one error — range() trong Python là exclusive ở cận trên

Giải thích: range(10, 12) chỉ tạo [10, 11], bỏ sót tháng 12. Thêm + 1 vào end: range(10, 13)[10, 11, 12]. Đáp án B thay đổi end_month tại chỗ khai báo — hoạt động nhưng làm sai ngữ nghĩa biến. Đáp án C sửa sai start_month (Q4 phải bắt đầu từ 10, không phải 9).

XP: +12 | Bonus: +2 (no hint), +3 (đúng loại bug)


🐛 Vòng 5: Logic Error — "Tính sai trung bình" ⭐⭐⭐ Khó

Code tính average order value — chạy không báo lỗi, nhưng kết quả SAI!

💬 Slack từ Senior DA:

"Em ơi, average order value = 2.5 triệu? Cao quá — gần bằng doanh thu cao nhất rồi. Check lại logic nhé."

Code có lỗi:

python
def avg_order_value(orders):
    """Tính giá trị đơn hàng trung bình (chỉ đơn completed)."""
    total = 0
    count = 0
    for order in orders:
        if order["status"] == "completed":
            total += order["amount"]
            count += 1
        avg = total / count  # 💥 Bug: tính avg TRONG vòng lặp!
    return avg

sample_orders = [
    {"id": 1, "amount": 500000, "status": "completed"},
    {"id": 2, "amount": 1200000, "status": "cancelled"},
    {"id": 3, "amount": 3000000, "status": "completed"},
    {"id": 4, "amount": 800000, "status": "completed"},
]
print(f"Avg Order Value: {avg_order_value(sample_orders):,.0f} VNĐ")
# Output: 2,150,000 — SAI! Đúng phải là (500000+3000000+800000)/3 = 1,433,333

Lựa chọn:

Cách sửa
ADi chuyển avg = total / count ra ngoài vòng for, thụt lề ngang hàng với for
BThêm if count > 0: trước dòng avg = total / count
CSửa thành avg = total / len(orders) ở ngoài vòng lặp
💡 Hint (−2 XP)

Xem indentation (thụt lề) của dòng avg = total / count — nó nằm bên trong hay bên ngoài vòng for? Kết quả avg phải tính sau khi duyệt xong tất cả đơn hàng.

Đáp án & Giải thích

✅ Đáp án đúng: A — Di chuyển avg = total / count ra ngoài vòng for

Loại bug: Logic error — indentation sai khiến avg tính lại mỗi iteration

Giải thích: Dòng avg = total / count bị thụt vào trong for → tính avg sau MỖI dòng, kết quả cuối = avg của lần cuối (sai). Cần kéo ra ngoài for để tính trung bình SAU KHI duyệt hết. Đáp án B thêm guard nhưng vẫn trong loop. Đáp án C chia cho len(orders) = 4 (bao gồm cả cancelled) thay vì 3 (chỉ completed).

XP: +15 | Bonus: +2 (no hint), +3 (đúng loại bug)


🐛 Vòng 6: File Handling Error — "File ghi đè thay vì nối thêm" ⭐⭐⭐ Khó

Code ghi báo cáo hàng ngày vào file — nhưng mỗi ngày chạy lại mất dữ liệu cũ!

💬 Slack từ BI Manager:

"Anh chạy script ghi báo cáo mỗi ngày, nhưng file chỉ có dữ liệu ngày cuối — 29 ngày trước biến mất hết!"

Code có lỗi:

python
import csv

def save_daily_report(date, revenue, order_count, filename):
    """Ghi báo cáo hàng ngày vào file CSV."""
    with open(filename, "w", newline="") as file:  # 💥 Bug ở đây!
        writer = csv.writer(file)
        writer.writerow(["date", "revenue", "order_count"])
        writer.writerow([date, revenue, order_count])

# Chạy mỗi ngày
save_daily_report("2026-02-16", 15000000, 42, "monthly_report.csv")
save_daily_report("2026-02-17", 18000000, 55, "monthly_report.csv")
save_daily_report("2026-02-18", 12000000, 38, "monthly_report.csv")
# File chỉ chứa dữ liệu ngày 18/02 — 2 ngày trước bị ghi đè!

Lựa chọn:

Cách sửa
AĐổi "w" thành "a" — nhưng bỏ dòng header writerow(["date", ...]) hoặc check file rỗng
BĐổi "w" thành "r+" — đọc trước rồi ghi thêm
CThêm file.seek(0, 2) trước khi ghi — nhảy đến cuối file
💡 Hint (−2 XP)

File mode trong Python: "w" = write (tạo mới / ghi đè), "a" = append (nối thêm cuối file), "r" = read only. Bạn muốn thêm dòng mới vào file đã có.

Đáp án & Giải thích

✅ Đáp án đúng: A — Đổi "w" thành "a" và xử lý header

Loại bug: File handling error — "w" mode ghi đè toàn bộ file mỗi lần mở

Giải thích: Mode "w" xóa sạch nội dung cũ và ghi mới. Mode "a" (append) nối thêm vào cuối file. Tuy nhiên, cần bỏ header khi append (hoặc check os.path.getsize(filename) == 0 để chỉ ghi header lần đầu). Đáp án B "r+" cần file đã tồn tại + phải seek thủ công. Đáp án C không hoạt động với mode "w" — file đã bị xóa sạch khi mở.

XP: +15 | Bonus: +2 (no hint), +3 (đúng loại bug)


🐛 Vòng 7: Function Error — "Return sai chỗ" ⭐⭐⭐⭐ BOSS

Code tìm sản phẩm bán chạy nhất mỗi danh mục — chỉ trả về 1 danh mục!

💬 Slack từ CEO:

"Em ơi, anh cần top product MỖI danh mục — 5 category mà chỉ thấy 1? Báo cáo board meeting 30 phút nữa!"

Code có lỗi:

python
def top_product_per_category(products):
    """Tìm sản phẩm có doanh thu cao nhất mỗi danh mục."""
    categories = {}

    for product in products:
        cat = product["category"]
        rev = product["revenue"]

        if cat not in categories:
            categories[cat] = {"name": product["name"], "revenue": rev}
        elif rev > categories[cat]["revenue"]:
            categories[cat] = {"name": product["name"], "revenue": rev}

        return categories  # 💥 Bug: return TRONG vòng lặp!

all_products = [
    {"name": "iPhone 16", "category": "Điện Tử", "revenue": 85000000},
    {"name": "Áo thun Uniqlo", "category": "Thời Trang", "revenue": 32000000},
    {"name": "Galaxy S25", "category": "Điện Tử", "revenue": 72000000},
    {"name": "Nồi cơm điện", "category": "Gia Dụng", "revenue": 18000000},
    {"name": "Quần jean Levi's", "category": "Thời Trang", "revenue": 41000000},
    {"name": "Máy xay sinh tố", "category": "Gia Dụng", "revenue": 25000000},
]

result = top_product_per_category(all_products)
for cat, info in result.items():
    print(f"{cat}: {info['name']} ({info['revenue']:,} VNĐ)")
# Chỉ in ra: Điện Tử: iPhone 16 — thiếu Thời Trang và Gia Dụng!

Lựa chọn:

Cách sửa
ADi chuyển return categories ra ngoài vòng forbỏ thụt lề 1 cấp
BThêm if len(categories) == 3: return categories — return khi đủ 3 category
CĐổi return categories thành yield categories — dùng generator
💡 Hint (−2 XP)

Xem vị trí của return — nó nằm bên trong vòng for. Function sẽ trả về ngay sau khi xử lý phần tử đầu tiên — không bao giờ duyệt hết list! Đây là bug cực phổ biến mà DA hay gặp.

Đáp án & Giải thích

✅ Đáp án đúng: A — Di chuyển return categories ra ngoài vòng for

Loại bug: Function error — return bên trong loop khiến function dừng sớm

Giải thích: return ở indentation level 2 (trong for) → function return ngay sau item đầu tiên! Kéo return ra ngoài for (indentation level 1) → duyệt hết tất cả sản phẩm rồi mới return. Đáp án B hardcode số category = 3, không linh hoạt. Đáp án C dùng yield thay đổi hẳn cách dùng function — phải dùng list() hoặc for ở ngoài.

XP: +18 | Bonus: +2 (no hint), +3 (đúng loại bug)


📊 Tổng kết

Bảng tổng hợp bug

VòngLoại bugĐộ khóXPBài học
1TypeError⭐ Dễ10CSV đọc mọi thứ thành str — luôn convert type
2NameError⭐ Dễ10Typo tên biến → Python tạo biến mới thay vì gán
3IndexError⭐⭐ TB12Dùng slicing [:n] thay vì index cứng [n]
4Off-by-one⭐⭐ TB12range() exclusive cận trên — luôn nhớ +1
5Logic Error⭐⭐⭐ Khó15Indentation = logic trong Python — 1 space sai = kết quả sai
6File Handling⭐⭐⭐ Khó15"w" ghi đè, "a" nối thêm — chọn đúng mode
7Function Error⭐⭐⭐⭐ Boss18return trong loop = dừng sớm — bug phổ biến nhất

Công thức tính điểm

XP tổng = Σ (XP mỗi vòng đúng) + Σ (No-hint bonus) + Σ (Bug ID bonus)
         = (max 92) + (max 14) + (max 21) = max 127 XP

🎯 Mẹo debug cho DA

Ghi nhớ 3 bước debug:

  1. Đọc error message — Python nói rõ loại lỗi (TypeError, NameError...) và dòng nào
  2. Check data typeprint(type(variable)) để xem kiểu dữ liệu
  3. Check indentation — logic error thường do thụt lề sai 1 cấp