프로젝트

[정보융합탐구 과제] 파이썬을 이용한 끝말잇기 제작(6월)

callvin 2024. 6. 19. 15:04
반응형

1. 탐구 내용

먼저 지금까지 준비한 단어 목록과 알고리즘 순서도를 바탕으로 코드를 작성하고, 여러 번 직접 실행해보면 발생하는 버그를 수정하였다. 발생하는 모든 경우에 대해 예상을 벗어나는 경우에도 거기에 맞추어 에러 메시지가 출력되도록 하였다. 두음법칙을 적용시키는 코드는 함수로 나타내 구현을 쉽게 하였다.

2. 전체 소스 코드

import random
import os

# 특정 글자로 시작하는 리스트 추출
def filter_words_starting_with(lst, prefix):
    return [word for word in lst if word.startswith(prefix)]

#특정 글자로 끝나는 리스트 추출
def filter_words_end_with(lst, prefix):
    return [word for word in lst if word.endswith(prefix)]

#에러메시지
def errMessage(err_num):
    errList = {1 : "게임을 종료합니다",
               2 : "컴퓨터 승리\n게임을 종료합니다. \n이 게임에서 이미 사용된 단어입니다. ",
               3 : "컴퓨터 승리\n게임을 종료합니다. \n표준국어대사전에 등재된 단어 중 동사, 형용사, 속담, 관용구를 제외한 단어를 입력해주세요.",
               4 : "컴퓨터 승리 \n게임을 종료합니다. \n플레이어의 입력 값은 컴퓨터 입력 단어의 마지막 글자로 시작해야 합니다.",}
    return errList[err_num]

#두음법칙
def two_sound_law(last_lett, is_player_turn):
    if last_lett[0] in n_to_o:
        last_lett.append(chr(ord(last_lett[0])+5292))
        if is_player_turn == 1: print("컴퓨터: {0}({1})\n".format(computer_choice,last_lett[1])) 
    elif last_lett[0] in r_to_n:
        last_lett.append(chr(ord(last_lett[0])-1764))
        if is_player_turn == 1: print("컴퓨터: {0}({1})\n".format(computer_choice,last_lett[1]))
    elif last_lett[0] in r_to_o:
        last_lett.append(chr(ord(last_lett[0])+3528))
        if is_player_turn == 1: print("컴퓨터: {0}({1})\n".format(computer_choice,last_lett[1]))
    else:
        if is_player_turn == 1: print("컴퓨터 : {0}\n".format(computer_choice))

this_file_path = os.path.abspath(__file__)
rel_path = this_file_path[:-8]

#단어 목록 추출
word_list = []
word_list_file = rel_path + "\\wordlist.txt"

with open(word_list_file, "r",encoding="utf-8") as f:
    allstr = f.read()

f.close()

word_list = allstr.split('\n')
word_list.remove(word_list[-1])

used_word_list = []

#한방단어 목록 추출
hanbang_file = rel_path + "\\hanbang.txt"
with open(hanbang_file, "r", encoding="utf-8") as f:
    hanbang_list = f.read().split("\n")
    
hanbang_list.remove(hanbang_list[-1])

#두음법칙
n_to_o = ['녀','년','념','녕','뉴','닐','닉','뇨','뉵','니','녑','랫','뇰','녁']
r_to_n = ['론','로','라','락','란','랄','람','랑','륵','록','루','릉','래','뢰','랍','랭','름','롱','르','릅','랏','롯','릎','릇','른','룅']
r_to_o = ['례','류','리','료','률','력','량','려','련','렴','령','린','립','렬','략','룡','륜','륙','륭','렵','릿','랸','룔']

#main
print("\n끝말잇기\n\n끝말잇기의 단어 데이터는 2023.09.18의 표준국어대사전 등재 단어를 기준으로 합니다\n")

a = input("게임을 시작하려면 \"시작\"을 입력하세요\n")

if a == "시작":
    print("──────────────────────────────────────────")
    print("게임 시작")
    gamestate = 1
    
    #컴퓨터 첫단어 고르기
    computer_choice = random.choice(word_list)
    while computer_choice[-1] in hanbang_list:
        computer_choice = random.choice(word_list)
    used_word_list.append(computer_choice)
    word_list.remove(computer_choice)
    
    last_lett = [computer_choice[-1]]
    two_sound_law(last_lett, 1)
    
    #플레이어 단어 입력
    player_choice = input("플레이어 : ")
    
    if player_choice[0] in last_lett: # 플레이어 단어가 컴퓨터 마지막단어와 같을때
        
        if player_choice in word_list: # 단어가 사전에 있을 때

            while gamestate == 1: #게임 반복 부분
                last_lett = [player_choice[-1]]
                used_word_list.append(player_choice)
                word_list.remove(player_choice)
                    
                two_sound_law(last_lett,0)
                    
                prefix_to_search = last_lett[0]
                result_list = filter_words_starting_with(word_list, prefix_to_search) 
                if len(last_lett) != 1:
                    prefix_to_search = last_lett[1]
                    result_list=result_list+filter_words_starting_with(word_list, prefix_to_search) 
                    
                if result_list != []:
                    computer_choice = random.choice(result_list) #컴퓨터 단어 선택
                    used_word_list.append(computer_choice)
                    word_list.remove(computer_choice)
                    last_lett = [computer_choice[-1]]
                else:
                    print("플레이어 승리!")
                    gamestate = 2
                    break
                
                two_sound_law(last_lett, 1)
                player_choice = input("플레이어 : ")
                
                if player_choice[0] in last_lett:
                    if player_choice in word_list:
                        continue
                    else:
                        gamestate = 0
                        if player_choice in used_word_list:
                            print(errMessage(2))
                        else:
                            print(errMessage(3))
                else:
                    gamestate = 0
                    print(errMessage(4))
        else:
            gaemstate = 0
            print(errMessage(3))
    else:
        gamestate = 0
        print(errMessage(4))
else:
    print(errMessage(1))

게임을 시작하면 가장 먼저 시작을 입력받는다. 정상적으로 시작을 입력할 경우 게임이 시작되고, 아니면 게임을 종료합니다를 출력한다. 여기서 컴퓨터는 기본적으로 사용 가능한 단어가 존재하면 그 중 무작위 단어를 출력하게 된다. 사용 가능한 단어의 기준은 게임 시작 후 지금까지 사용되지 않았는가이고, 첫 단어 선택의 경우 한방 단어가 아닌가도 기준이 된다.

그렇게 첫 단어를 컴퓨터가 선택하면 해당 단어에 두음법칙이 적용되는지에 따라 두음법칙을 적용하고, 플레이어 입력을 받는다.

플레이어 입력을 받으면 단어가 사전에 있는지, 이미 사용된 단어가 아닌지를 판단한 후 컴퓨터 차례로 넘어간다. 컴퓨터는 다시 플레이어 단어에 두음법칙이 적용된다면 적용하고, 적용되지 않는다면 바로 단어를 선택해 출력한다. 이 과정을 반복한다.

컴퓨터가 선택할 수 있는 단어가 없는 경우 플레이어가 승리하고, 플레이어가 사전에 없는 단어를 사용하거나 이미 사용한 단어를 사용하면 컴퓨터가 승리한다.

3. 변수, 함수 설명

가. 함수

filter_words_starting_with : 특정 글자로 시작하는 단어 목록을 추출한다.

filter_words_end_with : 특정 글자로 끝나는 단어 목록을 추출한다.

errMessage : 상황에 맞는 에러 메시지를 출력한다.

two_sound_law : 단어에 두음법칙이 적용된다면 적용한다. 다음이 플레이어 차례라면 컴퓨터가 입력한 단어 끝에 괄호를 붙여 첫 글자로 사용할 수 있는 단어를 알려 준다.

ex) 달무리 -> 달무리()

만약 다음이 컴퓨터 차례라면 이를 출력하지 않는다. 두음법칙을 적용하는 것에는 한글 유니코드의 번호 차이를 이용한다. ㄴ부터 ㅇ까지 5292, ㄹ부터 ㄴ까지 1764, ㄹ부터 ㅇ까지 3528 간격이다.

나. 변수

rel_path : 현재 코드 파일의 상대 경로를 저장한다.

word_list : 사용 가능 여부에 관계없는 전체 단어를 저장한다.

word_list_file : 단어 목록이 저장된 txt 파일의 경로를 저장한다.

used_word_list : 사용된 단어를 저장한다.

hanbang_file : 한방 단어 목록이 저장된 txt 파일의 경로를 저장한다.

hanbang_list : 한방 단어의 목록을 저장한다.

n_to_o, r_to_n, r_to_o : 각각 ㄴ->, ->, ->ㅇ으로 바뀌는 두음법칙이 적용되는 글자를 저장한다.

gamestate : 현재 게임 상황을 저장한다. 0은 게임 종료, 1은 진행 중, 2는 플레이어 승리이다.

computer_choice : 컴퓨터가 선택한 단어를 저장한다.

last_lett : 컴퓨터 또는 플레이어가 선택한 단어의 마지막 글자를 저장한다. 리스트 형태로 플레이어의 단어 마지막 글자에 두음법칙이 적용된다면 적용한 글자도 여기에 추가된다.

player_choice : 플레이어가 선택한 단어를 저장한다.

result_list : 플레이어가 선택한 단어의 마지막 글자로 시작하는 단어의 목록을 저장한다.

4. 오류 개선

처음부터 체계적으로 순서도를 그리고 알고리즘을 충분히 생각한 후 코드를 짜서 오류가 거의 발생하지 않았다. 대신 코드를 다 짠 후 반복적으로 나타나는 두음법칙에 대한 부분을 따로 함수로 표현하였고, 플레이어에 의한 에러 상황을 에러 메시지를 튜플에 저장하여 사용했다.

5. 느낀 점, 시행착오

스스로 내가 만들어 보고 싶은 프로그램을 만들어 볼 수 있어서 좋았다. 두음법칙을 적용하기 위해 한글 유니코드에서 글자별로 초성이 같다면 유니코드 간격이 일정하다는 사실을 이용한 것이 인상 깊었다. 코드를 짠 후 시간이 지나 다시 볼 때 내가 짜 놓고도 잘 이해가 안 됐었는데, 그때 VSC의 파이썬 디버거의 유용성을 깨닫게 되었다. 이번에는 JSON 데이터로 잘 구조화되어 있는 데이터를 사용했는데, 다음에는 훨씬 어려운 데이터도 사용해 보고 싶다. 처음 계획을 세울 때 두음법칙을 어떻게 적용할 수 있을지 고민했었는데, 유튜브에서 개발자들이 한글을 처리하는 법을 다룬 영상을 보고 영감을 얻을 수 있었다. 처음에는 JSON 데이터를 처리하는 것조차도 어려워했었는데, 이 프로젝트를 진행하면서 코딩 실력이나 코드에 대한 이해도가 전에 비해 많이 높아진 것 같다.

6. 개선할 점

들여쓰기가 너무 많아 가독성이 좀 낮다. 이를 개선하기 위해 들여쓰기를 줄이고, 반복되는 부분을 더 함수로 정의하여 사용하는 것이 좋을 것 같다.

반응형