Dev

[Python] 11. 뱀게임 스코어 파일에 기록 (feat. 파일 시스템 )

mlslly 2024. 5. 10. 12:23

* Udemy PythonBootcamp 수업 내용을 참고하여 작성

 

오늘은 프로그램을 확장하거나 자동화 하기 위해, 파일을 읽고 기록하는 방법들을 배우도록 한다. 

파일을 커스터마이즈해서 대량으로 발송하거나 활용하는 방법, 일을 자동화하는 방법 등의 원리이다.

 

뱀 게임의 점수 기록판 개발하기 

 

뱀 게임을 이미 구현한 바 있었는데, 오늘은 뱀게임을 더 발전시켜서, 뱀게임의 점수들이 기록되고 전체 기록을 보면서 유저가 더 잘 플레이하도록 하는 기능을 만들어볼 것이다. 외부 파일 및 디렉토리를 활용한다.

기본 뱀 게임의 설명 및 구현 코드는 아래 포스팅 참고 : https://ysryuu.tistory.com/46

 

[Python] 5. 뱀 게임 만들기 (1)

* Udemy의 PythonBootcamp 수업 내용을 참고하여 작성 이번에는 뱀게임을 구현해보자.  뱀게임은 간단한 규칙을 가진 게임이다.화면에 랜덤으로 사과가 생성되고, 뱀이 사과를 먹으면 성장 (길이가

ysryuu.tistory.com

 

1. reset 기능 및 High score 기능추가 

이전 뱀게임 코드에서는, 게임이 오버되면 그 상태로 최종 스코어가 확인 되기만 했었는데, 이럴 경우 매판 어던 점수를 기록했는지 확인할 수가 없다. 

따라서 매번 게임이 끝나는것이 아니라, 게임 오버가 되어도 스코어가 기록되고나면,

그 스코어가 최고 점수 high score인지를 확인하고, 계속 최고점을 갱신해 나가도록 구현할 것이다. 또한 다시 게임이 시작되면서 연속으로 게임을 플레이할 수 있도록 바꿔볼 것이다.

 

<scoreboard.py 파일 내용>

기존에 game over 메소드를 지우고 reset 메소드, update scoreboard 메소드에 high score를 추가해서 최고점을 기록할 수 있도록 한다  

from turtle import Turtle

ALIGNMENT = 'center'
FONT = ('Courier', 24, 'normal')

class Scoreboard(Turtle):

    def __init__(self):
        super().__init__()
        self.score = 0 
        self.high_score = 0
        self.color('white') # write이전에
        self.penup()
        self.goto(0, 270)
        self.hideturtle()
        self.update_scoreboard()
        
    def update_scoreboard(self):
        self.clear()
        self.write(f"Score: {self.score} High Score : {self.high_score}", align= ALIGNMENT, font = FONT)

    def reset(self):
        if self.score > self.high_score : 
            self.high_score = self.score
        self.score = 0 
        self.update_scoreboard()            
 

    def increase_score(self):
        self.score += 1
        self.update_scoreboard()

 

<snake.py 내용>

뱀 개체에 대해서도 reset 메소드를 추가해준다.

reset 메소드의 기능에는 현재 플레이되던 segments들을 모두 지워줘야 하는데, 각각의 segment들을 모두 화면 바깥으로 치워줘야 완전히 시야에서 사라지게 된다. 임의의 화면밖 좌표로 모든 세그먼트를 보냄으로써 기존 snake를 지우고, 

새롭게 create_snake를 해주면 된다.

from turtle import Turtle

STARTING_POSITIONS = [(0,0),(-20,0),(-40,0)] # 상수
MOVE_DISTANCE = 20
# turtle의 heading 메소드를 활용
UP = 90
DOWN = 270
LEFT = 180
RIGHT = 0

class Snake : 
    def __init__(self) : 
        self.segments = []
        self.create_snake() # 객체 생성시 자동 몸체 생성
        self.head = self.segments[0]  

    def create_snake(self) : # 몸체 만들기
        for position in STARTING_POSITIONS : 
            self.add_segment(position)

    def add_segment(self,position):
        new_segment = Turtle('square')
        new_segment.color('white')
        new_segment.penup()
        new_segment.goto(position)
        self.segments.append(new_segment)

    def reset(self):
        # 터틀을 아예 없애주기 (다른데로 보내기)
        for seg in self.segments :
            seg.goto(1000,1000)
        self.segments.clear()
        self.create_snake()
        self.head = self.segments[0]  

    def extend(self) : 
        self.add_segment(self.segments[-1].position())

    def move(self) :  # 움직이기
        for seg_num in range(len(self.segments)-1,0,-1):
            new_x = self.segments[seg_num-1].xcor()
            new_y = self.segments[seg_num-1].ycor()
            self.segments[seg_num].goto(new_x, new_y)
        self.head.forward(MOVE_DISTANCE)

    def up(self):
        if self.head.heading() != DOWN : # 반대방향으로 움직이지 못하도록
            self.head.setheading(UP)

    def down(self):
        if self.head.heading() != UP : 
            self.head.setheading(DOWN)

    def left(self):
        if self.head.heading() != RIGHT : 
            self.head.setheading(LEFT)

    def right(self):
        if self.head.heading() != LEFT :  
            self.head.setheading(RIGHT)

 

<main.py 내용>

이제 게임이 끝나지 않고 계속 재시작 될 수 있게 만들어야 하므로 game over 메소드 코드를 지우고

게임 종료 상황 시(벽과 충돌, 머리꼬리 충돌) 스코어보드와 터틀(snake)에 대한 reset 메소드를 취한다.

from turtle import Turtle, Screen
from snake_24 import Snake #클래스 임포트
from food_24 import Food
from scoreboard_24 import Scoreboard
import time

screen = Screen()
screen.setup(width=600, height=600)
screen.bgcolor('black')
screen.title('My Snake Game')
screen.tracer(0) 

snake = Snake()
food = Food()
scoreboard = Scoreboard()

screen.update()

screen.listen()
screen.onkey(snake.up, 'Up')
screen.onkey(snake.down, 'Down')
screen.onkey(snake.left, 'Left')
screen.onkey(snake.right, 'Right')

game_is_on = True
while game_is_on :
    screen.update()
    time.sleep(0.1)
    snake.move() #화면이 갱신될때마다 움직임

    # 먹이를 먹을 시
    if snake.head.distance(food) < 15 : 
        food.refresh() # 먹이 이동
        snake.extend() # 뱀이 늘어남 
        scoreboard.increase_score()

    # 벽과 충돌감지시 게임오버
    if snake.head.xcor() > 280 or snake.head.xcor() < -280 or snake.head.ycor() > 280 or snake.head.ycor() < -280 :
        scoreboard.reset()
        snake.reset()

    # 머리와 꼬리 충돌감지시 게임오버 
    for segment in snake.segments[1:] :
        if snake.head.distance(segment) < 10 : 
            scoreboard.reset()
            snake.reset()

screen.exitonclick()

 


뱀게임 점수를 파일에 읽고 쓰기

위 코드대로 뱀게임을 실행할 경우, 원하는대로 High Score가 갱신되면서 게임을 플레이할 수 있지만, 아예 게임을 꺼버리면 모든 기록들이 리셋되고, 다시 score 0 부터 시작하게 된다. 

High Score의 기록을 유지하기 위해서는 외부적인 점수 저장소가 필요하다는 뜻. 

뱀게임에서 최고 점수를 텍스트 파일에 쓰면서 점수를 갱신해보자. 

 

파일 시스템의 기본 읽기 쓰기 방법은 아래 지난 포스팅을 참고하면 된다. https://ysryuu.tistory.com/56

 

[Python] 10. 파이썬에서 파일 읽기, 쓰기

파일 시스템 사용하기  기본  1. 파이썬에서 텍스트 파일 열고 읽기 : open 파이썬에서 텍스트 파일을 열고 읽을 수 있다. 아래처럼 open함수에 파일 경로를 적어주고, file.read() 함수를 이용하면

ysryuu.tistory.com

 

1. data.txt 파일 생성

폴더에 최고 점수를 기록할 data.txt 파일을 생성한다 

 

2. 스코어 불러오기 

scoreboard의 init 메소드에서 high.score 초기값에 data 파일에서 불러온 값을 적어준다.

기존에 self.high_score = 0 이었던 부분을, 파일을 열어서 데이터를 가져오도록 수정했다. 

<scoreboard.py>

...
class Scoreboard(Turtle):

    def __init__(self):
        super().__init__()
        self.score = 0 
        # data 파일을 읽어와 최고 점으로 세팅하기 
        with open('/filepath/data.txt') as data :
            self.high_score = int(data.read())
        self.color('white') # write이전에
        self.penup()
        self.goto(0, 270)
        self.hideturtle()
        self.update_scoreboard()
        
    ...

 

3. 스코어 쓰기 

scoreboard의 reset 메소드에서 high_score가 판단되고 나면, data 파일에 해당 게임 라운드의 최고 점수를 적어준다. 

(최고 점수는 해당 플레이어의 성과에 의해 변동될 수도, 그대로일 수도 있다)

아래와 같이 갱신하면 data 파일의 score가 계속 바뀌면서 갱신될 것이다. 이런식으로 최고 점수는 계속 축적된다.

<scoreboard.py>

...
    def reset(self):
        if self.score > self.high_score : 
            self.high_score = self.score
   			# high_score가 갱신될 때마다 data 파일에 스코어를 덮어쓰기 
            with open('/filepath/data.txt', mode='w') as data:
                data.write(f'{self.high_score}')   
        self.score = 0 
        self.update_scoreboard()  

...