maplestory_dpm_calc icon indicating copy to clipboard operation
maplestory_dpm_calc copied to clipboard

팬텀 딜사이클 적용 (DO NOT MERGE)

Open icepeng opened this issue 4 years ago • 29 comments

정신 나간건 알고 있습니다.

Rule만으로는 스킬들 간에 쿨 기다려주고 맞춰서 쓰고 하는걸 구현하기가 너무 힘들어서... 일단 이렇게나마 딜사이클을 적용해 봤습니다.

15000, 30000 - 540 같은 수치들은 스킬 쿨타임과 그 스킬 사용 전에 붙는 다른 스킬들 딜레이를 고려해서 쿨이 정확히 돌아가게 한것입니다.

당연히 이대로 머지하기는 그렇고... 다른 방법으로 딜사이클 구현해보고, 여기서 나온 dpm과 비교해서 오차를 비교해볼 생각입니다.

icepeng avatar Aug 04 '20 21:08 icepeng

별개로, 조커 공격이 끝난 후가 아닌 매 공격마다 최종 버프를 발동하고 있는 오류 수정도 포함하고 있습니다.

icepeng avatar Aug 04 '20 21:08 icepeng

@oleneyl @cheese98 딜사이클 구현에 대한 의견 부탁드립니다

icepeng avatar Aug 06 '20 03:08 icepeng

제가 팬텀 딜사이클은 잘 몰라서 유의미한 피드백은 못 낼 것 같습니다.

그래도 읽어보긴 했는데 블랙잭은 쿨 돌아올때마다 쓰는게 맞나요?

jj9810 avatar Aug 06 '20 03:08 jj9810

팬텀 딜사이클은 피드백을 받아서 진행했고 ms단위로 맞췄기 때문에 거의 정확합니다.

다만 코드를 보시면 알겠지만 이렇게 직업 하나씩 구현하면 시간도 오래 걸리고 유지보수가 너무 어려워집니다.

A는 B를 x번 사용 할 때마다 맞춰 사용 같은 조건들을 어떻게 하면 더 간단하게 구현 가능할지에 대한 질문이였습니다.

icepeng avatar Aug 06 '20 03:08 icepeng

대충 구상해봤는데 StackSkillWrapper를 하나 만들고 B를 사용할때마다 1스택씩 증가시키게 한다음, A는 B의 스택이 x번 차면 사용하도록 OptionalElement를 거는게 어떨까요?

지금 컴퓨터를 사용할 수 없어서 직접 테스트는 불가능합니다.

jj9810 avatar Aug 06 '20 03:08 jj9810

제가 딜사이클을 구현하기 위해 먼저 했던 시도는

class CooltimeSyncActiveRule(AbstractRule):
    def __init__(self, state_element, checking_element):
        self._state_element_name = state_element
        self._checking_element_name = checking_element
    def get_related_elements(self, reference_graph):
        return [reference_graph.get_element(self._state_element_name)]
    def check(self, caller, reference_graph, context = None):
        checking_element = reference_graph.get_element(self._checking_element_name)
        state_element = reference_graph.get_element(self._state_element_name)
        if checking_element.is_active():
            return True
        if checking_element.is_cooltime_left(state_element.skill.cooltime, 1):
            return True
        return False
class CooltimeSyncUsableRule(AbstractRule):
    def __init__(self, state_element, checking_element):
        self._state_element_name = state_element
        self._checking_element_name = checking_element
    def get_related_elements(self, reference_graph):
        return [reference_graph.get_element(self._state_element_name)]
    def check(self, caller, reference_graph, context = None):
        checking_element = reference_graph.get_element(self._checking_element_name)
        state_element = reference_graph.get_element(self._state_element_name)
        if checking_element.is_usable():
            return True
        if checking_element.is_cooltime_left(state_element.skill.cooltime, 1):
            return True
        return False

이런 Rule을 만들어서 A, B가 있을 때 A의 쿨타임이 B의 남은 쿨타임보다 짧다면 사용, 그리고 B가 켜져 있으면 묶어서 사용

이런 느낌의 ConcurrentRule, ReservationRule을 확장한 것이였습니다.

하지만 이렇게 사용하면 스킬들 딜레이에 의해 미묘하게 어긋나서 특정 스킬 발동이 아예 안되는 경우가 있었습니다.

또한, 쿨타임이 돌더라도 Rule에 의해 사용되지 않고 기다리는 스킬이 있으면 해당 Rule은 사용하기 어려워집니다.

예를 들어 마오팬은 쿨이 30초, 엔버링크는 90초, 조커는 150초, 불스아이는 180초입니다.

여기서 조커와 불스아이를 맞춰 사용하는데, 이렇게 되면 조커의 남은 쿨타임으로 뭔가를 계산하는 것은 의미가 없어집니다.

그리고 마오팬 3번마다 엔버링크를 1번씩 맞춰줘야 하는데 이것도 미묘하게 밀리면서 마오팬 3번째보다 엔버링크 쿨이 먼저 와서 미리 켜버리고 마오팬은 안쓰고....

icepeng avatar Aug 06 '20 03:08 icepeng

StackSkillWrapper를 이용하는 방법은 가장 먼저 시도했습니다.

이게 지금 생각해보니 https://github.com/oleneyl/maplestory_dpm_calc/issues/144 이슈 때문이였던거 같기도 한데

마오팬 쿨인 30초마다 더미 홀더가 발동

스택이 0이면 불스아이+엔버링크+레투다+마오팬+조커 사이클 사용 스택이 3이면 엔버링크+레투다+마오팬 사이클 사용 스택이 1,2,4,5면 마오팬 사이클 사용

각 사이클은 끝난 후 스택 1 증가, 5면 0으로

이렇게 잡고 시작했었습니다

그런데 어째선지 조커 사이클이 끝나고 스택이 1이 되자마자 30초를 안기다리고 다음 마오팬을 시전해서...

그리고 이런 방식의 가장 큰 문제점은 쿨감뚝이나 메르쿨감 같은게 계산에 들어오는 순간 죄다 망가진다는 점입니다.

icepeng avatar Aug 06 '20 03:08 icepeng

흠 그러면 #144 가 변경하는 쪽으로 결론이 나면 다시 한번 StackSkillWrapper 기반으로 시도해봐주실수 있나요?

jj9810 avatar Aug 06 '20 05:08 jj9810

네 시도는 해보겠지만 이렇게 generate() 안에서 딜사이클을 정의하는거가 맞는지도 잘 모르겠습니다. Ruleset쪽에서 최대한 해결될 수 있으면 좋겠습니다.

icepeng avatar Aug 06 '20 05:08 icepeng

정해진 순서대로 스킬을 사용해야 하는것이 팬텀의 딜 사이클 인가요?

oleneyl avatar Aug 06 '20 06:08 oleneyl

180초마다 불스아이+엔버링크+레투다+마오팬+블랙잭+조커 90초마다 엔버링크+레투다+마오팬+블랙잭 나머지는 마오팬, 블랙잭 쿨마다

이정도로 요약 가능합니다.

icepeng avatar Aug 06 '20 06:08 icepeng

이때 사용 순서는

불스아이-레투다-블랙잭-엔버링크-마오팬-조커

레투다-엔버링크-블랙잭-마오팬

순이면 좋지만 이게 중요한 것은 아닙니다.

icepeng avatar Aug 06 '20 06:08 icepeng

그럼 사용 순서는

  1. 마오팬, 블랙잭은 쿨마다 사용
  2. 엔버링크가 돌아오면, 엔링->레투다->마오팬->블랙잭
  3. 불스아이가 돌아오면, 불스아이-레투다-블랙잭-엔버링크-마오팬-조커

이 셋만 구현되면 되는 문제인가요? 구현에서 어떤 어려움이 있었는지 궁금합니다.

oleneyl avatar Aug 06 '20 06:08 oleneyl

제가 가장 막혔던 포인트는 Rule만으로 쿨타임이 서로의 배수를 넘어갈 때 sync를 맞추는 문제였습니다.

icepeng avatar Aug 06 '20 06:08 icepeng

https://github.com/oleneyl/maplestory_dpm_calc/pull/145#issuecomment-669667054

해당 코멘트가 가장 큰 pain point였습니다.

icepeng avatar Aug 06 '20 06:08 icepeng

몰아서 쓰는 스킬이 6개씩이나 되고, 어떨땐 6개를 몰아쓰고 어떨땐 3개를 몰아쓰다 보면 Rule에서 서로의 관계를 정의하기가 굉장히 까다로워집니다.

또한, 몰아서 사용시 서로의 딜레이가 사용 시점에 영향을 주기 때문에 조커사이클 이후 다음 엔버링크+마오팬때 쿨이 미묘하게 다릅니다.

그러면 엔버링크+마오팬때의 서로를 맞춰주는 Rule을 넣다보면 조커때의 Rule이 또 꼬이기 시작하고... 텍스트만으로 설명하기가 좀 어렵습니다.

사용 순서는 다음 문제고, 스킬 6개,3개를 정확하게 몰아서 쓰게 하고 싶었습니다.

icepeng avatar Aug 06 '20 06:08 icepeng

그럼 A스킬의 n번째 사용을 B스킬 사용 이후에 맞추기<- 부분이 문제가 된다는 거군요

oleneyl avatar Aug 06 '20 06:08 oleneyl

네 그렇다고 볼 수 있겠네요.

그리고 딜사이클을 조금 더 최적화하자면

레투다-엔버링크-블랙잭-마오팬

이때 레투다와 엔버링크는 마오팬 쿨까지 기다리는게 아니라

레투다는 엔버링크+블랙잭의 딜레이 만큼 빼고, 엔버링크는 블랙잭 딜레이를 뺀 만큼 일때부터 시전 가능해야 합니다.

이렇게 안해주면 극딜버프 사용 시점이 점점 밀리면서 가동률이 악화됩니다.

icepeng avatar Aug 06 '20 07:08 icepeng

  • CooltimeSyncActiveRule에서 잔여 cooltime을 비교할 때, 스킬 시전 딜레이를 고려하도록 하고
  • 그리고 마오팬 3번마다 엔버링크를 1번씩 맞춰줘야 하는데 이것도 미묘하게 밀리면서 마오팬 3번째보다 엔버링크 쿨이 먼저 와서 미리 켜버리고 마오팬은 안쓰고....<- 이런 경우에 대해서는,
    • 마오팬은 엔버링크 사용 후에 쓸 수 있으면 쓰지 않고,
    • 엔버링크는 사용 후에 마오팬을 쓸 수 없으면 쓰지 않고 와 같이 두 Rule로 맞출 수 있을 것 같은데, 어떻게 생각하시나요?

oleneyl avatar Aug 06 '20 07:08 oleneyl

  • CooltimeSyncActiveRule에서 잔여 cooltime을 비교할 때, 스킬 시전 딜레이를 고려하도록 하고

이게 또 조커 사이클과 엔버레투다 사이클에서 중간에 끼어드는 스킬 개수가 다릅니다.

icepeng avatar Aug 06 '20 07:08 icepeng

  • 마오팬은 엔버링크 사용 후에 쓸 수 있으면 쓰지 않고,
  • 엔버링크는 사용 후에 마오팬을 쓸 수 없으면 쓰지 않고

분명히 이렇게 한다고 했는데 음... 제가 뭔가 놓쳤는지도 모르겠습니다.

icepeng avatar Aug 06 '20 07:08 icepeng

해당 pr 기준으로 6000에서 팬텀 dpm은 415957883326.8022가 나옵니다.

혹시 Rule만으로 이에 근접한 수치가 나오게 구현해주실 수 있을까요? 제가 하면 근처로 가기는 커녕 계속 내려가기만 해서... 한번 구현된 예제를 보고 배워서 다른 직업들에 잘 적용해보도록 하겠습니다.

icepeng avatar Aug 06 '20 07:08 icepeng

네 제가 한번 작업해 보아야 어떤 점이 난점인지를 좀 더 알 수 있을 것 같습니다. 구현을 위해서 딜 사이클을 제외한, 오류만 수정한 버전으로 PR주실 수 있으실까요?

oleneyl avatar Aug 06 '20 07:08 oleneyl

https://github.com/oleneyl/maplestory_dpm_calc/pull/169

올렸습니다.

icepeng avatar Aug 06 '20 07:08 icepeng

혹시, 위 방식으로 강제된 딜 사이클이 쿨타임이 다 돌지 않았는데도 사용하는 경우는 없나요? onAfter()로 체이닝된 그래프 요소는 어떠한 검사도 거치지 않고 강제로 실행됩니다.

oleneyl avatar Aug 06 '20 08:08 oleneyl

돌지 않아도 사용하긴 할텐데, 사이클 자체를 쿨타임에 정확히 맞춰서 쿨타임을 무시하진 않았을거라 생각합니다.

icepeng avatar Aug 06 '20 08:08 icepeng

image

적용했을 때의 가동 plot입니다.

icepeng avatar Aug 06 '20 08:08 icepeng

이거아주쉽지않네요..

oleneyl avatar Aug 19 '20 06:08 oleneyl

저도 손놓은지 좀 되었네요... 그나마 해결책으로는, ConditionRule의 마지막 인자를 배열로 받게 해서 복수의 GraphElement의 상태를 참조해서 사용 여부를 결정하게 하는 방법을 생각했습니다.

icepeng avatar Aug 19 '20 06:08 icepeng