From 857dd899a761f6d8a449705522d0f125c7737490 Mon Sep 17 00:00:00 2001 From: Stepland <16676308+Stepland@users.noreply.github.com> Date: Tue, 4 May 2021 11:27:03 +0200 Subject: [PATCH] [#mono-column] #circlefree mode accepts non-16ths notes and falls back to normal symbols when needed --- CHANGELOG.md | 2 ++ .../jubeat_analyser/mono_column/dump.py | 32 ++++--------------- .../jubeat_analyser/mono_column/load.py | 28 ++++++---------- .../tests/mono_column/test_mono_column.py | 3 +- .../test_mono_column_hypothesis.py | 12 +++---- 5 files changed, 25 insertions(+), 52 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bae8928..1218733 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ # Unreleased +## Added +- [#mono-column] #circlefree mode accepts non-16ths notes and falls back to normal symbols when needed ## Fixed - [jubeat-analyser] - Raise exception earlier when a mono-column file is detected by the other #memo parsers (based on "--" separator lines) diff --git a/jubeatools/formats/jubeat_analyser/mono_column/dump.py b/jubeatools/formats/jubeat_analyser/mono_column/dump.py index 773c991..0ef471c 100644 --- a/jubeatools/formats/jubeat_analyser/mono_column/dump.py +++ b/jubeatools/formats/jubeat_analyser/mono_column/dump.py @@ -31,6 +31,7 @@ from ..dump_tools import ( DEFAULT_EXTRA_SYMBOLS, DIRECTION_TO_ARROW, DIRECTION_TO_LINE, + NOTE_TO_CIRCLE_FREE_SYMBOL, JubeatAnalyserDumpedSection, LongNoteEnd, SortedDefaultDict, @@ -38,7 +39,6 @@ from ..dump_tools import ( fraction_to_decimal, jubeat_analyser_file_dumper, ) -from ..symbols import CIRCLE_FREE_SYMBOLS, NOTE_SYMBOLS class MonoColumnDumpedSection(JubeatAnalyserDumpedSection): @@ -62,19 +62,18 @@ class MonoColumnDumpedSection(JubeatAnalyserDumpedSection): frames: List[Dict[NotePosition, str]] = [] frame: Dict[NotePosition, str] = {} for note in self.notes: + time_in_section = note.time - self.current_beat + symbol = self.symbols[time_in_section] if isinstance(note, LongNote): needed_positions = set(note.positions_covered()) if needed_positions & frame.keys(): frames.append(frame) frame = {} - direction = note.tail_direction() arrow = DIRECTION_TO_ARROW[direction] line = DIRECTION_TO_LINE[direction] for is_first, is_last, pos in mark_ends(note.positions_covered()): if is_first: - time_in_section = note.time - self.current_beat - symbol = self.symbols[time_in_section] frame[pos] = symbol elif is_last: frame[pos] = arrow @@ -84,18 +83,13 @@ class MonoColumnDumpedSection(JubeatAnalyserDumpedSection): if note.position in frame: frames.append(frame) frame = {} - time_in_section = note.time - self.current_beat - symbol = self.symbols[time_in_section] frame[note.position] = symbol elif isinstance(note, LongNoteEnd): if note.position in frame: frames.append(frame) frame = {} - time_in_section = note.time - self.current_beat - if circle_free: - symbol = CIRCLE_FREE_SYMBOLS[int(time_in_section)] - else: - symbol = self.symbols[time_in_section] + if circle_free and symbol in NOTE_TO_CIRCLE_FREE_SYMBOL: + symbol = NOTE_TO_CIRCLE_FREE_SYMBOL[symbol] frame[note.position] = symbol frames.append(frame) @@ -108,9 +102,7 @@ class MonoColumnDumpedSection(JubeatAnalyserDumpedSection): yield "".join(frame.get(NotePosition(x, y), "□") for x in range(4)) -def _raise_if_unfit_for_mono_column( - chart: Chart, timing: Timing, circle_free: bool = False -) -> None: +def _raise_if_unfit_for_mono_column(chart: Chart, timing: Timing) -> None: if len(timing.events) < 1: raise ValueError("No BPM found in file") from None @@ -128,16 +120,6 @@ def _raise_if_unfit_for_mono_column( " mono_column format is not supported by jubeatools" ) - if circle_free and any( - (note.time + note.duration) % BeatsTime(1, 4) != 0 - for note in chart.notes - if isinstance(note, LongNote) - ): - raise ValueError( - "Chart contains long notes whose ending timing aren't" - " representable in #circlefree mode" - ) - def _section_factory(b: BeatsTime) -> MonoColumnDumpedSection: return MonoColumnDumpedSection(current_beat=b) @@ -151,7 +133,7 @@ def _dump_mono_column_chart( circle_free: bool = False, ) -> StringIO: - _raise_if_unfit_for_mono_column(chart, timing, circle_free) + _raise_if_unfit_for_mono_column(chart, timing) sections = create_sections_from_chart( _section_factory, chart, difficulty, timing, metadata, circle_free diff --git a/jubeatools/formats/jubeat_analyser/mono_column/load.py b/jubeatools/formats/jubeat_analyser/mono_column/load.py index 8ea61fc..ae60f6f 100644 --- a/jubeatools/formats/jubeat_analyser/mono_column/load.py +++ b/jubeatools/formats/jubeat_analyser/mono_column/load.py @@ -188,28 +188,20 @@ class MonoColumnParser(JubeatAnalyserParser): unfinished_longs: Dict[NotePosition, UnfinishedLongNote] = {} for section_starting_beat, section, bloc in self._iter_blocs(): should_skip: Set[NotePosition] = set() - # 1/3 : look for ends to unfinished long notes for pos, unfinished_long in unfinished_longs.items(): x, y = astuple(pos) symbol = bloc[y][x] - if self.circle_free: - if symbol in CIRCLE_FREE_SYMBOLS: - should_skip.add(pos) - symbol_time = CIRCLE_FREE_TO_BEATS_TIME[symbol] - note_time = section_starting_beat + symbol_time - yield unfinished_long.ends_at(note_time) - elif symbol in section.symbols: - raise SyntaxError( - "Can't have a note symbol on the holding square of" - " an unfinished long note when #circlefree is on" - ) - else: - if symbol in section.symbols: - should_skip.add(pos) - symbol_time = section.symbols[symbol] - note_time = section_starting_beat + symbol_time - yield unfinished_long.ends_at(note_time) + if self.circle_free and symbol in CIRCLE_FREE_SYMBOLS: + should_skip.add(pos) + symbol_time = CIRCLE_FREE_TO_BEATS_TIME[symbol] + note_time = section_starting_beat + symbol_time + yield unfinished_long.ends_at(note_time) + elif symbol in section.symbols: + should_skip.add(pos) + symbol_time = section.symbols[symbol] + note_time = section_starting_beat + symbol_time + yield unfinished_long.ends_at(note_time) unfinished_longs = { k: unfinished_longs[k] for k in unfinished_longs.keys() - should_skip diff --git a/jubeatools/formats/jubeat_analyser/tests/mono_column/test_mono_column.py b/jubeatools/formats/jubeat_analyser/tests/mono_column/test_mono_column.py index 86aaf76..d46bda4 100644 --- a/jubeatools/formats/jubeat_analyser/tests/mono_column/test_mono_column.py +++ b/jubeatools/formats/jubeat_analyser/tests/mono_column/test_mono_column.py @@ -2,9 +2,8 @@ from typing import Iterable, Union import pytest -from jubeatools.song import BeatsTime, LongNote, NotePosition, TapNote - from jubeatools.formats.jubeat_analyser.mono_column.load import MonoColumnParser +from jubeatools.song import BeatsTime, LongNote, NotePosition, TapNote def compare_chart_notes( diff --git a/jubeatools/formats/jubeat_analyser/tests/mono_column/test_mono_column_hypothesis.py b/jubeatools/formats/jubeat_analyser/tests/mono_column/test_mono_column_hypothesis.py index 2af61ef..bd9aa80 100644 --- a/jubeatools/formats/jubeat_analyser/tests/mono_column/test_mono_column_hypothesis.py +++ b/jubeatools/formats/jubeat_analyser/tests/mono_column/test_mono_column_hypothesis.py @@ -5,6 +5,9 @@ from typing import List, Set, Union import hypothesis.strategies as st from hypothesis import given +from jubeatools.formats import Format +from jubeatools.formats.jubeat_analyser.mono_column.dump import _dump_mono_column_chart +from jubeatools.formats.jubeat_analyser.mono_column.load import MonoColumnParser from jubeatools.song import ( BeatsTime, BPMEvent, @@ -12,19 +15,14 @@ from jubeatools.song import ( LongNote, Metadata, SecondsTime, + Song, TapNote, Timing, - Song ) from jubeatools.testutils.strategies import NoteOption, long_note from jubeatools.testutils.strategies import notes as notes_strat from jubeatools.testutils.strategies import tap_note -from jubeatools.formats import Format -from jubeatools.formats.jubeat_analyser.mono_column.dump import _dump_mono_column_chart -from jubeatools.formats.jubeat_analyser.mono_column.load import MonoColumnParser - - from ..test_utils import load_and_dump_then_check, memo_compatible_song @@ -86,4 +84,4 @@ def test_that_many_notes_roundtrip(notes: List[Union[TapNote, LongNote]]) -> Non @given(memo_compatible_song(), st.booleans()) def test_that_full_chart_roundtrips(song: Song, circle_free: bool) -> None: - load_and_dump_then_check(Format.MONO_COLUMN, song, circle_free) \ No newline at end of file + load_and_dump_then_check(Format.MONO_COLUMN, song, circle_free)