본문 바로가기

Toy Project/오딘 딜 계산기

[Toy Project] 오딘:발할라 라이징(Odin) 공 계산기(진행중)

728x90
반응형

오딘 게임을 하면서 평소에 궁금했었던 내용이 있었다. 공격력과 모든 대미지 중에 어떤것이 더 좋을까? 경우에 따라서는 모든 대미지가 더 좋을 수도 있지 않을까 라는 생각을 했었다. 그러던 찰나에 유튜브 'Ho랑 게임채널' 채널에서 공격 스탯에 관한 의문들에 대한 정리 영상(하단 영상 첨부)을 보게 되었고, 토이프로젝트로 공계산기를 만들어보면 어떨까 라는 생각으로 첫 번째 토이프로젝트를 시작하게 되었다.


만들고자 하는 내용은 간단했다.

 

1. 계산에 필요한 값들을 일련의 공식에 대입해서 계산해서 알려준다.

예를 들어, 평딜 계산은 『공격력 * 공퍼댐 * (모든 대미지 + 직업 대미지 + 일반 몬스터 대미지) * 일반 대미지』 을 통해서 계산이 되는데, 공퍼댐의 경우 공격력이 증가함에 따라서 임의 값이 증가되는 형태인데, 

 

 

웹에서 사진을 Drag-and-drop 하여 해당 사진에 있는 텍스트를 파싱하려면 먼저 사용자가 사진을 서버에 업로드할 수 있어야 합니다. 이를 구현하기 위해 다음과 같은 과정을 거칩니다:

  1. 클라이언트 사이드에서 Drag-and-drop 이벤트를 처리할 수 있는 JavaScript 코드를 작성합니다. 이를 위해 HTML5의 Drag-and-drop API를 사용할 수 있습니다.
  2. 파일이 서버에 전송되려면 웹 애플리케이션 서버에서 파일을 받을 수 있는 API 엔드포인트를 생성해야 합니다. 이를 위해 Python의 Flask나 Django와 같은 백엔드 프레임워크를 사용할 수 있습니다.
  3. 사용자가 파일을 드래그 앤 드롭하면 클라이언트 사이드에서 파일을 서버로 전송하기 위한 HTTP 요청을 만들어야 합니다. 이를 위해 JavaScript의 Fetch API나 XMLHttpRequest를 사용할 수 있습니다.
  4. 서버에서 파일을 저장하려면 서버의 API 엔드포인트에서 전송된 파일을 받아 로컬 또는 클라우드 스토리지에 저장하는 코드를 작성해야 합니다.
  5. 텍스트를 파싱하려면 이미지에서 텍스트를 추출할 수 있는 Optical Character Recognition (OCR) 라이브러리를 사용해야 합니다. 이를 위해 Python에서 Tesseract와 pytesseract 라이브러리를 사용할 수 있습니다.
  6. 텍스트 추출이 완료되면 결과를 클라이언트에 반환하고, 클라이언트는 결과를 웹 페이지에 표시할 수 있습니다.

요약하면, 사용자가 사진을 Drag-and-drop 하면 웹 애플리케이션 서버에서 해당 파일을 저장하고 처리할 수 있습니다. 이를 위해 클라이언트와 서버 사이드에서 적절한 코드를 작성하고, 필요한 라이브러리를 사용해야 합니다.

 

[기능 구현]

(1) 평타 딜 계산기 (기능구현완료)

(2) 스킬 딜 계산기

(3) 치명타데미지 적용한 딜 계산기

(4) 직업별 자사 필드 추천

    1) 필드 몬스터 LV DB 구축

    2) 직업별 스킬 DB 구축

    3) 공퍼댐 자동 계산 DB 구축 (구축중)

 

(5) 캡처한 캐릭터 스탯창을 통한 자동 parameter 구축(OCR 개발)


1. 평타 딜 계산 기능 구현

from sqlalchemy import create_engine
import pandas as pd
import sqlite3

class DamageMeter:
    """
    attack      : 공격력
    per_dam     : 공퍼댐
    all_dam     : 모든 데미지
    job_dam     : 직업계열 데미지
    com_mon_dam : 일반 몬스터 데미지
    com_dam     : 일반 데미지
    """

    def __init__(self, job, *params):
        # 일반연결
        self.conn = sqlite3.connect("./odin/odin.db", isolation_level=None)
        self.cur = self.conn.cursor()

        # SQLAlchemy 연결
        engine = create_engine('sqlite:///./odin/odin.db')
        conn2 = engine.connect()

        self.job = job
        self.attack, self.all_dam, self.job_dam, self.com_mon_dam, self.com_dam = params
        self.per_dam = pd.read_sql(f"""
                            SELECT 공퍼댐 
                            FROM ODIN_ATTACK
                            WHERE 공격력 = {self.attack}
                        """, self.conn).values[0][0]
        self.res = 0

    def get_damage(self):
        """평타 데미지 계산"""
        self.res = self.attack * (self.per_dam/100) * (((self.all_dam + self.job_dam + self.com_mon_dam)/100) + 1) \
                    * ((self.com_dam/100)+1)
        return round(self.res)

    def skill_damage(self):
        """평타 데미지 * 스댐 * 일반몬스터스댐 * 스킬계수"""
        pass

    def show(self):
        """평타 데미지 계산 결과 출력
        - 예시화면\n
        '------------------------------\n
        |     Class     :    세인트    |\n
        |  평타데미지   :  628  ~  630 |\n
        '------------------------------ 
        """
        print("-"*30)
        print(f"|{'Class':^15}{':':^2}{self.job:^10}|")
        print(f"|{'평타데미지':^10}{':':^2}{self.get_damage()-1:^5} ~ {self.get_damage()+1:^5}|")
        print("-"*30)

 

5. 캡처한 캐릭터 스탯창을 통한 자동 parameter 구축(OCR 개발)

 5-1. 구글 테서렉트를 활용한 Text 추출

결론 : 구글 테서렉트는 한글 Text 추출은 정확하게 한글을 인식하지 못하는 문제가 있었음.

jpg는 인식 못해서 png로 변환하는 코드 추가해야함.

pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'

# a = Image.open("./pic/KakaoTalk_20230402_005309829.jpg")
a = Image.open("./pic/KakaoTalk_20230330_153325610.jpg")
result = pytesseract.image_to_string(a, lang='kor')
# print(result)

pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'
result = open("./output.txt","w")

path_dir = './png_pic/'
file_list = os.listdir(path_dir)

for file_name in file_list :
    if file_name == "output.txt":
        continue
    result.write(pytesseract.image_to_string(path_dir+file_name,lang='ENG+KOR',config='--psm 4 -c preserve_interword_spaces=1')+'\n')
result.close()
print("추출이 완료되었습니다. 확인부탁드립니다.")

info_dict = {}
with open('./output.txt', 'r', encoding='utf-8') as f:
    lines = f.readlines()
    for idx, line in enumerate(lines):
        line = line.strip()

        if '공격력' in line: # 11row
            info_dict['공격력'] = int(line.split()[-1])
        elif '공격속도' in line: #13row
            info_dict['공격 속도'] = float(line.split()[-1].rstrip('%')) / 100
        elif '스킬시전속도' in line:
            info_dict['스킬 시전 속도'] = float(line.split()[-1].rstrip('%')) / 100
        elif 'US' in line:
            info_dict['명중'] = int(line.split()[-1])
        elif '마범 명중' in line:
            info_dict['마법 명중'] = int(line.split()[-1].rstrip('!'))
        elif '치명타확률' in line:
            info_dict['치명타 확률'] = float(line.split()[-1].rstrip('%')) / 100
        elif '치명타 대미지' in line:
            info_dict['치명타 데미지'] = float(line.split()[-1].rstrip('%')) / 1000
        elif '모든 대미지절대중가' in line:
            info_dict['모든 데미지 절대 증가'] = int(line.split()[-1].rstrip('%'))
        elif '마범 대미지 절대중가' in line:
            info_dict['마법 데미지 절대 증가'] = int(line.split()[-1].rstrip('%'))
        elif '월반 cya]' in line:
            info_dict['일반 데미지'] = int(line.split()[-1].rstrip('%'))
        elif '스킬대미지' in line:
            info_dict['스킬 데미지'] = float(line.split()[-1].rstrip('%'))
        elif 'QE 대미지' in line:
            info_dict['모든 데미지'] = float(line.split()[-1].rstrip('%'))
        elif '마범 대미지' in line and 'Pvp' not in line:
            info_dict['마법 데미지'] = float(line.split()[-1].rstrip('%'))
        elif 'Pvp 2 대미지' in line:
            info_dict['pvp 모든 데미지'] = float(line.split()[-1].rstrip('%'))
        elif 'Pvp 마범 대미지' in line:
            info_dict['pvp 마법 데미지'] = float(line.split()[-1].rstrip('%'))
        elif '브루탈메게 주는 대미지' in line:
            info_dict['브루탈에게 주는 데미지'] = float(line.split()[-1].rstrip('%'))
        elif '보스 몬스터에게 주는 대미지' in line:
            info_dict['보스 몬스터에게 주는 데미지'] = float(line.split()[-1].rstrip('%'))
        elif '일반몬스터에게 주는 대미지' in line:
            info_dict['일반 몬스터에게 주는 데미지'] = float(line.split()[-1].rstrip('%'))
        elif '상태이상적중' in line:
            info_dict['상태이상적중'] = float(line.split()[-1].rstrip('%'))

print(info_dict)

 

5-2. 네이버 OCR 적용

# 구글 테서렉트 보다 한글인식은 잘하는 문제가 있지만 파싱의 어려움이 존재함.

# open cv로 이미지 로드시 path에 한글이 있으면 이미지 인식이 안되는 문제가 있음.

import numpy as np
import platform
from PIL import ImageFont, ImageDraw, Image
from matplotlib import pyplot as plt
 
import uuid
import json
import time
import cv2
import requests

def plt_imshow(title='image', img=None, figsize=(8 ,5)):
    plt.figure(figsize=figsize)
 
    if type(img) == list:
        if type(title) == list:
            titles = title
        else:
            titles = []
 
            for i in range(len(img)):
                titles.append(title)
 
        for i in range(len(img)):
            if len(img[i].shape) <= 2:
                rgbImg = cv2.cvtColor(img[i], cv2.COLOR_GRAY2RGB)
            else:
                rgbImg = cv2.cvtColor(img[i], cv2.COLOR_BGR2RGB)
 
            plt.subplot(1, len(img), i + 1), plt.imshow(rgbImg)
            plt.title(titles[i])
            plt.xticks([]), plt.yticks([])
 
        plt.show()
    else:
        if len(img.shape) < 3:
            rgbImg = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)
        else:
            rgbImg = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
 
        plt.imshow(rgbImg)
        plt.title(title)
        plt.xticks([]), plt.yticks([])
        plt.show()
        
def put_text(image, text, x, y, color=(0, 255, 0), font_size=22):
    if type(image) == np.ndarray:
        color_coverted = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        image = Image.fromarray(color_coverted)
 
    if platform.system() == 'Darwin':
        font = 'AppleGothic.ttf'
    elif platform.system() == 'Windows':
        font = 'malgun.ttf'
        
    image_font = ImageFont.truetype(font, font_size)
    font = ImageFont.load_default()
    draw = ImageDraw.Draw(image)
 
    draw.text((x, y), text, font=image_font, fill=color)
    
    numpy_image = np.array(image)
    opencv_image = cv2.cvtColor(numpy_image, cv2.COLOR_RGB2BGR)
 
    return opencv_image
    
# 네이버 OCR에서 받는 파라미터
api_url = 'Your api url'
secret_key = 'secret key'

path = './pic/KakaoTalk_20230330_153325610.jpg'
files = [('file', open(path,'rb'))]

request_json = {'images': [{'format': 'jpg',
                                'name': 'demo'
                               }],
                    'requestId': str(uuid.uuid4()),
                    'version': 'V2',
                    'timestamp': int(round(time.time() * 1000))
                   }
 
payload = {'message': json.dumps(request_json).encode('UTF-8')}
 
headers = {
  'X-OCR-SECRET': secret_key,
}
 
response = requests.request("POST", api_url, headers=headers, data=payload, files=files)
result = response.json()

list_info = [ field['inferText'] for field in result['images'][0]['fields'] ]
info_dict = {}

i = 0
while i < len(list_info) - 1:
    key = list_info[i]

    if key == '공격력':
        value = int(list_info[i + 1])
        info_dict[key] = value
        i += 2
    elif key == '공격 속도':
        value = float(list_info[i + 1].strip('%')) / 100
        info_dict[key] = round(value,3)
        i += 2
    elif key == '스킬' and list_info[i + 1] == '시전' and list_info[i + 2] == '속도':
        key = '스킬 시전 속도'
        value = float(list_info[i + 3].strip('%')) / 100
        info_dict[key] = round(value,3)
        i += 4
    elif key == '명중':
        value = int(list_info[i + 1])
        info_dict[key] = value
        i += 2
    elif key == '마법' and list_info[i+1] == '명중':
        key = '마법 명중'
        value = int(list_info[i + 2])
        info_dict[key] = value
        i += 2
    elif key == '치명타' and list_info[i+1] == '확률':
        key = '치명타 확률'
        value = float(list_info[i + 2].strip('%')) / 100
        info_dict[key] = round(value,3)
        i += 2
    elif key == '치명타' and list_info[i+1] == '대미지':
        key = '치명타 대미지'
        value = float(list_info[i + 2].strip('%')) / 100
        info_dict[key] = round(value,3)
        i += 2
    elif key == '모든' and list_info[i + 1] == '대미지' and list_info[i + 2] == '절대' and list_info[i + 3] == '증가':
        key = '모든 대미지 절대 증가'
        value = float(list_info[i + 4].strip('%')) / 100
        info_dict[key] = round(value,3)
        i += 5
    elif key == '마법' and list_info[i + 1] == '대미지' and list_info[i + 2] == '절대' and list_info[i + 3] == '증가':
        key = '마법 대미지 절대 증가'
        value = float(list_info[i + 4].strip('%')) / 100
        info_dict[key] = round(value,3)
        i += 5
    elif key == '일반' and list_info[i+1] == '대미지':
        key = '일반 대미지'
        value = float(list_info[i + 2].strip('%')) / 100
        info_dict[key] = round(value,3)
        i += 2
    elif key == '스킬' and list_info[i+1] == '대미지':
        key = '스킬 대미지'
        value = float(list_info[i + 2].strip('%')) / 100
        info_dict[key] = round(value,3)
        i += 2
    elif key == '모든' and list_info[i+1] == '대미지':
        key = '모든 대미지'
        value = float(list_info[i + 2].strip('%')) / 100
        info_dict[key] = round(value,3)
        i += 2
    elif key == '마법' and list_info[i+1] == '대미지':
        key = '마법 대미지'
        value = float(list_info[i + 2].strip('%')) / 100
        info_dict[key] = round(value,3)
        i += 2
    elif key == 'PvP' and list_info[i + 1] == '모든' and list_info[i + 2] == '대미지':
        key = 'PvP 모든 대미지'
        value = float(list_info[i + 3].strip('%')) / 100
        info_dict[key] = round(value,3)
        i += 4
    elif key == 'PvP' and list_info[i + 1] == '마법' and list_info[i + 2] == '대미지':
        key = 'PvP 마법 대미지'
        value = float(list_info[i + 3].strip('%')) / 100
        info_dict[key] = round(value,3)
        i += 4
    elif key == '브루탈에게' and list_info[i + 1] == '주는' and list_info[i + 2] == '대미지':
        key = '브루탈에게 주는 대미지'
        value = float(list_info[i + 3].strip('%')) / 100
        info_dict[key] = round(value,3)
        i += 4
    elif key == '보스' and list_info[i + 1] == '몬스터에게' and list_info[i + 2] == '주는' and list_info[i + 3] == '대미지':
        key = '보스 몬스터에게 주는 대미지'
        value = float(list_info[i + 4].strip('%')) / 100
        info_dict[key] = round(value,3)
        i += 5
    elif key == '일반' and list_info[i + 1] == '몬스터에게' and list_info[i + 2] == '주는' and list_info[i + 3] == '대미지':
        key = '일반 몬스터에게 주는 대미지'
        value = float(list_info[i + 4].strip('%')) / 100
        info_dict[key] = round(value,3)
        i += 5
    elif key == '상태이상' and list_info[i+1] == '적중':
        key = '상태이상 적중'
        value = float(list_info[i + 2].strip('%')) / 100
        info_dict[key] = round(value,3)
        i += 2
    elif key == '재사용' and list_info[i + 1] == '대기' and list_info[i + 2] == '시간' and list_info[i + 3] == '감소':
        key = '재사용 대기 시간 감소'
        value = float(list_info[i + 4].strip('%')) / 100
        info_dict[key] = round(value,3)
        i += 5
    else:
        i += 1

print(info_dict)

 

 

[참고할 사이트]

HTML, javascript 계산기 만들기 참고 블로그

https://velog.io/@sonaki0811/Toy-Project-%EA%B3%84%EC%82%B0%EA%B8%B0-%EB%A7%8C%EB%93%A4%EA%B8%B0class-%EC%83%9D%EC%84%B1

 

Toy Project - 계산기 만들기(class 생성)

ac버튼과 연산 버튼 등호 버튼과 넘버 버튼은 각각 따로 작동하므로 data-type을 각자 정해준다.Calculator class를 만들어주고 인스턴스를 만들어준다. displayElement는 displayElement, displayContent 빈값을 넣

velog.io

https://youtu.be/1lISFuuSYm4

 

728x90
반응형