A tournament scoreboard disappears when its program stops. Your challenge is to save scores in a file, load them into useful Python values, and find a player in a saved roster.
Every example writes its own known data before reading it, so you can run the notebook again safely.
You will learn
- Save text in a file and read it back later.
- Use
with open(...)so a file closes when the work is done. - Read saved lines one at a time, clean them, and turn numbers into integers.
- Save and load records, then search a saved roster.
Lesson 1: Save Scores, Then Load Them Back
The tournament director wants the scoreboard to survive after the program stops. We will write one score per line, then read the whole saved file back as text.
Write one line to a file
open(path, "w") opens a fresh file for writing. A file stores text, so f.write("840\n") saves the digits and a newline. The with statement closes the file when its indented block ends.
with open("lesson_l1_one.txt", "w") as f:
f.write("840\n")
Notice
"w" creates a fresh file, and with closes it after the indented write.
Read the whole file
Open the same known file in "r" mode and use f.read() to get all its text. For this first lesson, f.read() loads the whole file as one text string. Line-by-line reading begins in Lesson 2.
with open("lesson_l1_one.txt", "r") as f:
print(f.read())
Output
840
Notice
The saved 840 prints with an extra blank line because the text already ends in \n and print adds its own newline.
Write several scores
\n is the newline character inside a string. A loop writes one line per score, and the f-string changes each number to text.
with open("lesson_l1_scores.txt", "w") as f:
for score in [840, 920, 775]:
f.write(f"{score}\n")
Notice
The file now has three separate score lines. A later "w" write replaces all three.
Put the save and load steps in functions
These functions accept a list and a path, save each score, and return the file's complete text. A second call uses "w" on the same path to replace its earlier contents.
# Save every score on its own line.
def save_scores(scores, path):
with open(path, "w") as f:
for score in scores:
f.write(f"{score}\n")
return path
def load_score_text(path):
with open(path, "r") as f:
text = f.read()
return text
def score_text_round_trip(scores, path):
save_scores(scores, path)
return load_score_text(path)
print(score_text_round_trip([840, 920, 775], "lesson_l1_scores.txt"))
print(score_text_round_trip([610, 990], "lesson_l1_scores.txt"))
Output
840
920
775
610
990
Notice
The first function call saves three lines and returns 840\n920\n775\n. The second replaces that file with two known lines before reading it again. Mode "w" means write a fresh file. Mode "r" means read an existing file.
Why use with?
Here is the old way in function form. The programmer has to remember a separate close() call.
def save_one_score_old_way(score, path):
f = open(path, "w")
f.write(f"{score}\n")
f.close()
return path
print(save_one_score_old_way(730, "lesson_l1_old_way.txt"))
Output
lesson_l1_old_way.txtThe worked call prints lesson_l1_old_way.txt because that path is the function's result, and it leaves 730 on one line in the file. The better form is with open(path, "w") as f:. with closes the file for you automatically, even if your code crashes or you forget close(). We will use with for every remaining file operation.
Add to the end with "a"
Start a known file with "w", then reopen it in "a" mode to append a later score.
with open("lesson_l1_append.txt", "w") as f:
f.write("575\n")
with open("lesson_l1_append.txt", "a") as f:
f.write("905\n")
with open("lesson_l1_append.txt", "r") as f:
print(f.read(), end="")
Output
575
905Notice
"w" starts a fresh file; "a" keeps what is there and adds at the end. The file prints 575 and then 905.
Try real score input
This version reads a count and that many scores from the keyboard, saves them, then prints the reloaded text.
Try it yourself
n = int(input("How many scores? "))
with open("lesson_l1_input.txt", "w") as f:
for i in range(n):
score = int(input("Score: "))
f.write(f"{score}\n")
with open("lesson_l1_input.txt", "r") as f:
print(f.read(), end="")
Notice
Entering scores saves them in a file before the final print reads them back.
Lesson 2: Turn Saved Lines into Score Statistics
The scoreboard file is readable, but a statistics tool cannot add text such as "840". It needs to clean each saved line and turn it into an integer. Then it can update a statistic immediately or rebuild a list for Python's built-in tools.
Read one line at a time
for line in f: visits each line in order. line.strip() removes its newline, and int(...) changes the cleaned text into an integer. The append step transforms each file line into one list item.
def save_number_lines(scores, path):
with open(path, "w") as f:
for score in scores:
f.write(f"{score}\n")
return path
def load_number_lines(path):
loaded_scores = []
with open(path, "r") as f:
for line in f:
score = int(line.strip())
loaded_scores.append(score)
return loaded_scores
def number_line_round_trip(scores, path):
save_number_lines(scores, path)
return load_number_lines(path)
print(number_line_round_trip([14, 21, 18], "lesson_l2_numbers.txt"))
print(number_line_round_trip([32, 27], "lesson_l2_numbers.txt"))
Output
[14, 21, 18]
[32, 27]Keep a running total
Save the known scores, turn each saved line into an integer, and add it to total as it is read.
def running_total_from_file(scores, path):
save_number_lines(scores, path)
total = 0
with open(path, "r") as f:
for line in f:
score = int(line.strip())
total = total + score
return total
print(running_total_from_file([420, 680, 510, 735], "lesson_l2_total.txt"))
Output
2345Notice
running_total_from_file reports 2345 after adding the four saved scores.
Count scores that reach a target
Keep count at zero until a saved score meets the target. Increase it only for qualifying scores.
def count_from_file(scores, target, path):
save_number_lines(scores, path)
count = 0
with open(path, "r") as f:
for line in f:
score = int(line.strip())
if score >= target:
count = count + 1
return count
print(count_from_file([420, 680, 510, 735], 600, "lesson_l2_count.txt"))
Output
2Notice
count_from_file reports 2 scores at least 600. It does not need a separate list.
Keep the best score so far
Start best with the first known score, then replace it only when a larger saved score appears.
def best_score_from_file(scores, path):
save_number_lines(scores, path)
best = scores[0]
with open(path, "r") as f:
for line in f:
score = int(line.strip())
if score > best:
best = score
return best
print(best_score_from_file([420, 680, 510, 735], "lesson_l2_best.txt"))
Output
735Notice
best_score_from_file reports 735 with a direct file loop rather than max.
Compare built-in statistics
Another approach rebuilds an integer list from the file and then calls min, max, sum, and len.
def built_in_stats_from_file(scores, path):
save_number_lines(scores, path)
loaded_scores = load_number_lines(path)
return [min(loaded_scores), max(loaded_scores), sum(loaded_scores), len(loaded_scores)]
print(built_in_stats_from_file([420, 680, 510, 735], "lesson_l2_builtins.txt"))
Output
[420, 735, 2345, 4]Notice
built_in_stats_from_file reports [420, 735, 2345, 4] after rebuilding the list.
Try real score input
This version reads a target and a count, saves that many entered scores, then counts qualifying saved lines.
Try it yourself
target = int(input("Target: "))
n = int(input("How many scores? "))
with open("lesson_l2_input.txt", "w") as f:
for i in range(n):
score = int(input("Score: "))
f.write(f"{score}\n")
count = 0
with open("lesson_l2_input.txt", "r") as f:
for line in f:
if int(line.strip()) >= target:
count = count + 1
print(count)
Notice
The target is compared with numbers reloaded from the file, not just the entered text.
Lesson 3: Save Records and Search a Roster
A coach needs more than a bare score. One saved player record must keep a name, a level, and points together. The coach also needs a search tool that checks roster lines in order and returns as soon as it finds the requested player.
One record across several lines
This simple format uses three lines for one record: name first, then level, then points. The loader cleans every line. It keeps the name as text and transforms the remaining fields into integers before appending them to one record list.
f.readline() reads just the NEXT line and moves on, so a following for line in f: continues from the line after it.
def save_player_record(name, level, points, path):
with open(path, "w") as f:
f.write(f"{name}\n")
f.write(f"{level}\n")
f.write(f"{points}\n")
return path
def load_player_record(path):
record = []
with open(path, "r") as f:
name = f.readline().strip()
record.append(name)
for line in f:
number = int(line.strip())
record.append(number)
return record
def player_record_round_trip(name, level, points, path):
save_player_record(name, level, points, path)
return load_player_record(path)
print(player_record_round_trip("Mina", 4, 860, "lesson_l3_record.txt"))
print(player_record_round_trip("Omar", 6, 1040, "lesson_l3_record.txt"))
Output
['Mina', 4, 860]
['Omar', 6, 1040]One record per line, fields split by commas
When a whole record fits on one line, split(",") separates its fields at the commas.
line = "Mina,4,860"
parts = line.split(",")
print(parts)
Output
['Mina', '4', '860']Notice
The three fields are still strings: ['Mina', '4', '860'].
Turn a field into a number
Use its position in parts and int when the points field needs arithmetic.
print(int(parts[2]))
Output
860Notice
int(parts[2]) prints the number 860, not the text "860".
Walk saved comma records
Save three known records first. Then split each reloaded line and print its name field.
with open("lesson_l3_commas.txt", "w") as f:
f.write("Mina,4,860\n")
f.write("Omar,6,1040\n")
f.write("Nia,3,720\n")
with open("lesson_l3_commas.txt", "r") as f:
for line in f:
parts = line.strip().split(",")
print(parts[0])
Output
Mina
Omar
NiaNotice
The saved records print Mina, Omar, and Nia in file order.
Search for the first matching line
A linear search checks one line, then the next. Equality with == asks whether the cleaned line matches the target exactly. A matching line returns immediately. The not-found return belongs after the loop because the function must check every line before giving up.
def save_roster(names, path):
with open(path, "w") as f:
for name in names:
f.write(f"{name}\n")
return path
def find_player(target, path):
with open(path, "r") as f:
for line in f:
if line.strip() == target:
return f"Found {target}."
return f"{target} was not found."
def roster_search(names, target, path):
save_roster(names, path)
return find_player(target, path)
print(roster_search(["Ari", "Bo", "Chen"], "Bo", "lesson_l3_roster.txt"))
print(roster_search(["Ari", "Bo", "Chen"], "Devi", "lesson_l3_roster.txt"))
Output
Found Bo.
Devi was not found.The first search returns Found Bo. from inside the loop. The second checks all three lines, then reaches the fallback after the loop and returns Devi was not found.
Try real roster input
This version reads a target name and a count, saves that many entered names, and searches the reloaded roster.
Try it yourself
target = input("Name to find: ")
n = int(input("How many names? "))
with open("lesson_l3_input.txt", "w") as f:
for i in range(n):
name = input("Name: ")
f.write(f"{name}\n")
result = f"{target} was not found."
with open("lesson_l3_input.txt", "r") as f:
for line in f:
if line.strip() == target:
result = f"Found {target}."
break
print(result)
Notice
The search checks saved names in order and prints the first match or the not-found message.
Recap
open(path, 'w')starts a fresh file;'a'adds to its end.with open(path, 'r') as f:opens a file for reading and closes it afterward.for line in f:visits saved lines;int(line.strip())turns a number line into an integer.line.split(',')separates the fields of a comma record.- A search checks saved names in order and reports a match or a not-found result.
You can now save a tournament scoreboard and search a saved roster.