Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Added combo display for StaticMuriChecker and JudgeManager & fixed a random bug #4

Merged
merged 7 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 59 additions & 42 deletions judge.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
from core import JUDGE_TPF, JUDGE_TPS, DISTANCE_MERGE_SLIDE, DELTA_TANGENT_MERGE_SLIDE
from core import OVERLAY_THRESHOLD, COLLIDE_EXTRA_DELTA, TAP_ON_SLIDE_THRESHOLD, COLLIDE_THRESHOLD
from action import ActionExtraPadDown
from simai import SimaiNote, SimaiSlideChain, SimaiWifi, SimaiTap, SimaiHold, SimaiTouch, SimaiTouchHold, SimaiTouchGroup
from simai import SimaiNote, SimaiSlideChain, SimaiWifi, SimaiTap, SimaiHold, SimaiTouch, SimaiTouchHold, \
SimaiTouchGroup
from action import Action


Expand Down Expand Up @@ -48,37 +49,50 @@ def _flatten_touch_group(note_sequence: Sequence[SimaiNote]):
def _overlap_record(note: SimaiNote, note2: SimaiNote) -> dict:
return {
"type": "Overlap",
"affected": {"line": note.cursor[0], "col": note.cursor[1], "note": note.cursor[2]},
"cause": {"line": note2.cursor[0], "col": note2.cursor[1], "note": note2.cursor[2]},
"affected": {"line": note.cursor[0], "col": note.cursor[1], "note": note.cursor[2], "combo": note.combo},
"cause": {"line": note2.cursor[0], "col": note2.cursor[1], "note": note2.cursor[2], "combo": note2.combo},
}

@staticmethod
def _tap_on_slide_record(note: SimaiNote, slide: SimaiNote, delta: float) -> dict:
return {
"type": "TapOnSlide",
"affected": {"line": note.cursor[0], "col": note.cursor[1], "note": note.cursor[2]},
"cause": {"line": slide.cursor[0], "col": slide.cursor[1], "note": slide.cursor[2]},
"affected": {"line": note.cursor[0], "col": note.cursor[1], "note": note.cursor[2], "combo": note.combo},
"cause": {"line": slide.cursor[0], "col": slide.cursor[1], "note": slide.cursor[2], "combo": slide.combo},
"delta": delta,
}

@staticmethod
def _slide_head_tap_record(note: SimaiNote, slide: SimaiNote, delta: float) -> dict:
return {
"type": "SlideHeadTap",
"affected": {"line": note.cursor[0], "col": note.cursor[1], "note": note.cursor[2]},
"cause": {"line": slide.cursor[0], "col": slide.cursor[1], "note": slide.cursor[2]},
"affected": {"line": note.cursor[0], "col": note.cursor[1], "note": note.cursor[2], "combo": note.combo},
"cause": {"line": slide.cursor[0], "col": slide.cursor[1], "note": slide.cursor[2], "combo": slide.combo},
"delta": delta,
}

@classmethod
def check(cls, note_sequence: Sequence[SimaiNote]):
muri_records = []
flatten = list(cls._flatten_touch_group(note_sequence))

def _(_):
if getattr(_, "critical_moment", None):
return _.critical_moment
elif getattr(_, "end_moment", None):
return _.end_moment
else:
return _.moment

sorted_sequence = sorted(flatten,
key=_)
for index, it in enumerate(sorted_sequence, start=1):
it.combo = index
# 拆分note序列
slides = []
wifis = []
non_slides = []
for note in cls._flatten_touch_group(note_sequence):
for note in flatten:
if isinstance(note, SimaiSlideChain):
slides.append(note)
elif isinstance(note, SimaiWifi):
Expand All @@ -104,10 +118,10 @@ def check(cls, note_sequence: Sequence[SimaiNote]):

for note in non_slides:
if not isinstance(note, SimaiTap | SimaiHold):
continue # 筛掉 touch
continue # 筛掉 touch

if note.moment < slide.shoot_moment or note.moment > slide.end_moment + COLLIDE_THRESHOLD:
continue # 筛掉打击时刻不在 slide 范围内的
continue # 筛掉打击时刻不在 slide 范围内的

# 先查第一个区的外无
if note.idx == slide.start and TAP_ON_SLIDE_THRESHOLD <= note.moment - slide.shoot_moment <= COLLIDE_THRESHOLD:
Expand All @@ -126,10 +140,10 @@ def check(cls, note_sequence: Sequence[SimaiNote]):
for wifi in wifis:
for note in non_slides:
if not isinstance(note, SimaiTap | SimaiHold):
continue # 筛掉 touch
continue # 筛掉 touch

if note.moment < wifi.shoot_moment or note.moment > wifi.end_moment + COLLIDE_THRESHOLD:
continue # 筛掉打击时刻不在 slide 范围内的
continue # 筛掉打击时刻不在 slide 范围内的

# 偷个懒,wifi其实只需要查头尾
if wifi.start == note.idx and TAP_ON_SLIDE_THRESHOLD <= note.moment - wifi.shoot_moment <= COLLIDE_THRESHOLD:
Expand Down Expand Up @@ -167,17 +181,17 @@ def check(cls, note_sequence: Sequence[SimaiNote]):

for record in sorted(muri_records, key=(lambda x: (x["affected"]["line"], x["affected"]["col"]))):
if record["type"] == "Overlap":
msg = "叠键无理:\"{note}\"(L{line},C{col}) 与 ".format_map(record["affected"])
msg += "\"{note}\"(L{line},C{col}) 重叠".format_map(record["cause"])
msg = "叠键无理:{combo}cb处\"{note}\"(L{line},C{col}) 与 ".format_map(record["affected"])
msg += "{combo}cb处\"{note}\"(L{line},C{col}) 重叠".format_map(record["cause"])
REPORT_WRITER.writeln(msg)
elif record["type"] == "SlideHeadTap":
msg = "外键无理:\"{note}\"(L{line},C{col}) 可能被 ".format_map(record["affected"])
msg += "\"{note}\"(L{line},C{col}) 蹭到 ".format_map(record["cause"])
msg = "外键无理:{combo}cb处\"{note}\"(L{line},C{col}) 可能被 ".format_map(record["affected"])
msg += "{combo}cb处\"{note}\"(L{line},C{col}) 蹭到 ".format_map(record["cause"])
msg += "(%+.0f ms)" % (record["delta"] * 1000 / JUDGE_TPS)
REPORT_WRITER.writeln(msg)
elif record["type"] == "TapOnSlide":
msg = "撞尾无理:\"{note}\"(L{line},C{col}) 可能被 ".format_map(record["affected"])
msg += "\"{note}\"(L{line},C{col}) 蹭到 ".format_map(record["cause"])
msg = "撞尾无理:{combo}cb处\"{note}\"(L{line},C{col}) 可能被 ".format_map(record["affected"])
msg += "{combo}cb处\"{note}\"(L{line},C{col}) 蹭到 ".format_map(record["cause"])
msg += "(%+.0f ms)" % (record["delta"] * 1000 / JUDGE_TPS)
REPORT_WRITER.writeln(msg)

Expand All @@ -186,6 +200,7 @@ def check(cls, note_sequence: Sequence[SimaiNote]):

class MultiTouchMuri:
__slots__ = ("_cursors",)

def __init__(self, cursors: Iterable[tuple[int, int, str]]):
self._cursors = tuple(sorted(cursors))

Expand Down Expand Up @@ -238,11 +253,11 @@ def tick(self, elapsed_time: float) -> tuple[list[TouchPoint], int, list[SimaiNo
break

# ===== 更新触摸状态 =====
next_pad_state = 0 # 记录下一tick初始时的触摸板状态
finished_actions: list[Action] = [] # 记录已经完成的action
this_frame_touch_points: list[TouchPoint] = [] # 记录本tick中的触点
next_pad_state = 0 # 记录下一tick初始时的触摸板状态
finished_actions: list[Action] = [] # 记录已经完成的action
this_frame_touch_points: list[TouchPoint] = [] # 记录本tick中的触点

pad_down_source_dict: dict[Pad, Action] = {} # 记录本tick触发的pad down事件及其来源
pad_down_source_dict: dict[Pad, Action] = {} # 记录本tick触发的pad down事件及其来源
# 记录本tick触发的pad down事件及其来源
pad_up_source_dict: dict[Pad, Action | None] = {p: None for p in Pad}
# 记录下一tick初始时各激活触摸板的来源
Expand Down Expand Up @@ -308,7 +323,7 @@ def tick(self, elapsed_time: float) -> tuple[list[TouchPoint], int, list[SimaiNo
affected_cursors = set(a.source.cursor for _0, _1, _2, a in this_frame_touch_points)
muri = MultiTouchMuri(affected_cursors)
if muri not in self.multi_touch_muri:
self.multi_touch_muri.add(muri) # 记录下来避免重复报告
self.multi_touch_muri.add(muri) # 记录下来避免重复报告
self.muri_record_list.append(
{
"time": self.timer / JUDGE_TPS,
Expand Down Expand Up @@ -367,14 +382,15 @@ def tick(self, elapsed_time: float) -> tuple[list[TouchPoint], int, list[SimaiNo
record = {
"time": self.timer / JUDGE_TPS,
"type": "SlideTooFast",
"affected": {"line": note.cursor[0], "col": note.cursor[1], "note": note.cursor[2]},
"affected": {"line": note.cursor[0], "col": note.cursor[1], "note": note.cursor[2],
"combo": note.combo},
"judge_areas": [],
}

s, f = divmod(self.timer / JUDGE_TPF, 60)
m, s = divmod(int(s), 60)
msg = "[%02d:%02dF%05.2f] 内屏无理:" % (m, s, f)
msg += "\"{2}\"(L{0},C{1}) 被提前蹭掉,".format(*note.cursor)
msg += "{3}cb处\"{2}\"(L{0},C{1}) 被提前蹭掉,".format(*note.cursor, note.combo)
msg += "CP区间±%.0f ms,相关判定区如下" % (note.critical_delta * 1000 / JUDGE_TPS)

for i in range(note.total_area_num):
Expand All @@ -396,7 +412,8 @@ def tick(self, elapsed_time: float) -> tuple[list[TouchPoint], int, list[SimaiNo
"area": "/".join(p.name for p in area),
"cause": {"line": act.source.cursor[0],
"col": act.source.cursor[1],
"note": act.source.cursor[2]},
"note": act.source.cursor[2],
"combo": act.source.combo},
"time": t / JUDGE_TPS,
}
)
Expand All @@ -418,19 +435,20 @@ def tick(self, elapsed_time: float) -> tuple[list[TouchPoint], int, list[SimaiNo
record = {
"time": self.timer / JUDGE_TPS,
"type": "SlideTooFast",
"affected": {"line": note.cursor[0], "col": note.cursor[1], "note": note.cursor[2]},
"affected": {"line": note.cursor[0], "col": note.cursor[1], "note": note.cursor[2],
"combo": note.combo},
"judge_areas": [],
}
s, f = divmod(self.timer / JUDGE_TPF, 60)
m, s = divmod(int(s), 60)
msg = "[%02d:%02dF%05.2f] 内屏无理:" % (m, s, f)
msg += "\"{2}\"(L{0},C{1}) 被提前蹭掉,".format(*note.cursor)
msg += "{3}cb处\"{2}\"(L{0},C{1}) 被提前蹭掉,".format(*note.cursor, note.combo)
msg += "CP区间±%.0f ms,相关判定区如下" % (note.critical_delta * 1000 / JUDGE_TPS)

for i in range(note.total_area_num):
for j in range(3):
if i == 0 and j == 1:
break # 避免起始A区反复打印3次
break # 避免起始A区反复打印3次
entry = note.area_judge_actions[j][i]
area = note.info.tri_judge_sequence[j][i]
if entry is None:
Expand All @@ -449,7 +467,8 @@ def tick(self, elapsed_time: float) -> tuple[list[TouchPoint], int, list[SimaiNo
"area": "/".join(p.name for p in area),
"cause": {"line": act.source.cursor[0],
"col": act.source.cursor[1],
"note": act.source.cursor[2]},
"note": act.source.cursor[2],
"combo": act.source.combo},
"time": t / JUDGE_TPS,
}
)
Expand All @@ -473,45 +492,43 @@ def tick(self, elapsed_time: float) -> tuple[list[TouchPoint], int, list[SimaiNo
self.muri_record_list.append(
{
"time": self.timer / JUDGE_TPS,
"type": "SlideHeadTap" if isinstance(note.judge_action, ActionExtraPadDown) else "TapOnSlide",
"affected": {"line": note.cursor[0], "col": note.cursor[1], "note": note.cursor[2]},
"type": "SlideHeadTap" if isinstance(note.judge_action,
ActionExtraPadDown) else "TapOnSlide",
"affected": {"line": note.cursor[0], "col": note.cursor[1], "note": note.cursor[2],
"combo": note.combo},
"cause": {"line": note.judge_action.source.cursor[0],
"col": note.judge_action.source.cursor[1],
"note": note.judge_action.source.cursor[2]},
"note": note.judge_action.source.cursor[2],
"combo": note.judge_action.source.combo},
}
)

s, f = divmod(self.timer / JUDGE_TPF, 60)
m, s = divmod(int(s), 60)
msg = "[%02d:%02dF%05.2f] " % (m, s, f)
msg += "外键无理:" if isinstance(note.judge_action, ActionExtraPadDown) else "撞尾无理:"
msg += "\"{2}\"(L{0},C{1}) 被 ".format(*note.cursor)
msg += "\"{2}\"(L{0},C{1}) 蹭到 ".format(*note.judge_action.source.cursor)
msg += "{3}cb处\"{2}\"(L{0},C{1}) 被 ".format(*note.cursor, note.combo)
msg += "{3}cb处\"{2}\"(L{0},C{1}) 蹭到 ".format(*note.judge_action.source.cursor,note.judge_action.source.combo)
msg += "(%+.0f ms)" % ((note.judge_moment - note.moment) * 1000 / JUDGE_TPS)

else:
self.muri_record_list.append(
{
"time": self.timer / JUDGE_TPS,
"type": "Overlap",
"affected": {"line": note.cursor[0], "col": note.cursor[1], "note": note.cursor[2]},
"affected": {"line": note.cursor[0], "col": note.cursor[1], "note": note.cursor[2], "combo": note.combo},
}
)

s, f = divmod(self.timer / JUDGE_TPF, 60)
m, s = divmod(int(s), 60)
msg = "[%02d:%02dF%05.2f] 叠键无理:" % (m, s, f)
msg += "\"{2}\"(L{0},C{1}) 似乎与另一个note重叠".format(*note.cursor)
msg += "{3}cb处\"{2}\"(L{0},C{1}) 似乎与另一个note重叠".format(*note.cursor, note.combo)
msg += " (%+.0f ms)" % ((note.judge_moment - note.moment) * 1000 / JUDGE_TPS)
REPORT_WRITER.writeln(msg)

return this_frame_touch_points, hand_count, finished_notes






if __name__ == "__main__":
pass

3 changes: 2 additions & 1 deletion majparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,8 @@ def _parse_slide_note(cls, cursor: tuple[int, int, str], slide_str: str, now: fl
end = next(char_iter)
shapes.append(last_target + ch + end)
last_target = end

if not SlideInfo._entries:
SlideInfo.generate_all()
# validate all shape
for shape in shapes:
try:
Expand Down
4 changes: 4 additions & 0 deletions simai.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,8 @@ def __init__(self, cursor: tuple[int, int, str], moment: float, shapes: Iterable
raise TypeError("either `durations` or `total_duration` need to be specified")

super().__init__(cursor, moment)
if not SlideInfo._entries:
SlideInfo.generate_all()
self.shapes = tuple(shapes) # 存储了slidechain里每一段的形状,比如1-3-5这里就是("1-3", "3-5")
self.segment_infos: tuple[SlideInfo, ...] = tuple(SlideInfo.get(k) for k in shapes) # 每一段的信息
self.start = self.segment_infos[0].start # 整个slidechain的起点
Expand Down Expand Up @@ -470,6 +472,8 @@ def __init__(self, cursor: tuple[int, int, str], moment: float, shape: str, wait
@param wait: the waiting time before slide-star shoots (in ticks)
@param duration: the duration of the wifi slide (in ticks)
"""
if not WifiInfo._entries:
WifiInfo.generate_all()
super().__init__(cursor, moment)
self.shape = shape
self.info = WifiInfo.get(shape)
Expand Down