2026, Jan 03 21:03

Исправляем сохранение рекорда в Python: читаем перед записью

Почему рекорд в игре на Python перезаписывается? Объясняем ошибку сравнения и режимы файлов, даем код: читаем CSV, сравниваем и записываем лучший результат.

Сохранение рекорда кажется пустяком, пока не обнаружишь, что программа перезаписывает сохранённое значение даже при худшем результате. Причина не в «магии» ввода‑вывода, а в том, с чем вы сравниваете и в какой момент записываете данные. Ниже — краткий разбор проблемы и прямое решение, которое сохраняет исходную логику и обновляет файл только тогда, когда это действительно нужно.

Постановка задачи

Цель — удерживать максимальный счёт между запусками игры. Если новый результат выше, записать его в CSV‑файл; если нет — оставить текущее значение. Однако текущая реализация перезаписывает файл даже тогда, когда новый счёт ниже.

Проблемная реализация

Следующий фрагмент демонстрирует поведение, из‑за которого файл обновляется без необходимости. Смысл имён сохранён, формулировки слегка упрощены ради ясности.

from turtle import Turtle
import csv
class ScorePanel:
    def __init__(self):
        self.tally = 0
        self.temp_peak = 0
        self.marker = Turtle()
        self.marker.hideturtle()
        self.marker.penup()
        self.marker.goto(200, 260)
        self.marker.color("white")
        self.render_tally()
    def render_tally(self):
        self.marker.clear()
        self.marker.write(f"Score: {self.tally}", align="center", font=("Arial", 20, 'normal'))        
    def add_point(self):
        self.tally += 1
        self.marker.clear()
        self.render_tally()
    def end_game(self):
        self.sync_highscore()
        self.marker.goto(0, 0)
        self.marker.color("red")            
        self.marker.write(f"Game over!\nYour highest score is: {self.tally}", align="center", font=("Arial", 20, "normal"))
    def sync_highscore(self):
        if self.tally > self.temp_peak:
            self.temp_peak = self.tally
            try:
                with open("score.csv", "w") as fh:
                    written = fh.write(str(self.temp_peak))
                    self.marker.write(f"Score: {written}", align="center", font=("Arial", 20, 'normal'))    
            except FileNotFoundError:
                with open("score.csv", 'w') as fh:
                    fh.write(self.tally)
                    self.marker.write(f"Score: {self.tally}", align="center", font=("Arial", 20, 'normal'))
        self.marker.write(f"Score: {self.tally}", align="center", font=("Arial", 20, 'normal')) 

Почему это не работает

Логика опирается на сравнение текущего счёта со значением в оперативной памяти, а не с тем, что уже сохранено на диске. При новом запуске это значение в памяти равно нулю, поэтому даже небольшой результат кажется улучшением и вызывает запись. Кроме того, запись в режиме "w" в срабатывающих ветках безусловна, из‑за чего содержимое файла перезаписывается вне зависимости от того, является ли сохранённое значение реальным рекордом. Здесь всплывают и другие подводные камни. Код ни разу не читает существующий счёт перед тем, как решать, перезаписывать ли его, — нет опорной точки для корректного сравнения. Открытие файла в режиме "w" создаёт или обнуляет его, поэтому обработка ситуации «файл отсутствует» в этой же ветке бессмысленна. Аргумент метода write должен быть строкой: полагаться на неявное преобразование или пытаться записать число напрямую нельзя. И, наконец, файл называется CSV лишь номинально — фактически в нём хранится и читается одно единственное значение.

Минимальное исправление

Рабочий подход прост. Сначала прочитайте текущее значение из файла. Сравните его с текущим счётом. Если он выше — запишите новое значение; иначе оставьте сохранённый рекорд без изменений. Если файла нет, инициализируйте его текущим счётом.

def sync_highscore(self):
    try:
        with open("score.csv", "r") as fh:
            line = fh.readlines()[0]
            stored_peak = int(line)
            if self.tally > stored_peak:                
                with open("score.csv", 'w') as fhw:
                    fhw.write(str(self.tally))
                self.marker.write(f"Game over!\nYour highest score is: {self.tally}", align="center", font=("Arial", 20, "normal"))
            else:
                with open("score.csv", 'w') as fhw:
                    fhw.write(str(stored_peak))
                self.marker.write(f"Game over!\nYour highest score is: {stored_peak}", align="center", font=("Arial", 20, "normal"))
    except FileNotFoundError:
        with open("score.csv", 'w') as fhw:
            fhw.write(str(self.tally))
            self.marker.write(f"Score: {self.tally}", align="center", font=("Arial", 20, 'normal'))

Эта последовательность жёстко соблюдает требуемое правило: повышать рекорд только тогда, когда новое значение превосходит сохранённое. Кроме того, она гарантирует, что на диск записываются строки, и задаёт понятный путь инициализации, когда файла ещё нет.

Почему это важно понимать

Условительное сохранение — распространённый приём далеко за пределами игр. Чтение перед записью помогает поддерживать согласованное состояние и избегать тихих откатов. Это также подчёркивает, как режимы файлов и обработка исключений взаимодействуют при создании и обнулении файлов, и почему сравнение с сохранённым на диске состоянием, а не с временными значениями в памяти, критично, когда данные должны переживать перезапуски процесса.

Итоги

Опирайтесь на значение, уже записанное на диск, сравните с ним текущий счёт — и только после этого решайте, перезаписывать ли файл. Передавайте в write строки и держите пути ввода‑вывода простыми и явными. Даже если файл называется CSV, обращайтесь с ним в соответствии с тем, что вы в нём храните. Соблюдение такого порядка действий исключает случайное понижение рекорда и делает поведение предсказуемым между запусками.