@@ -114,8 +114,8 @@ static IEnumerable<Entry> Canon(IReadOnlyList<Entry> e) =>
114114
115115 private static void AssertNoteEqual ( string expected , string actual , int noteIdx , Rational time , MaiChart chart )
116116 {
117- var expArr = RearrangeNote ( expected ) . Split ( '/' , '`' ) ;
118- var actArr = RearrangeNote ( actual ) . Split ( '/' , '`' ) ;
117+ var expArr = RearrangeNote ( expected ) . Split ( '/' , '`' , '*' ) ;
118+ var actArr = RearrangeNote ( actual ) . Split ( '/' , '`' , '*' ) ;
119119 var max = Math . Max ( expArr . Length , actArr . Length ) ;
120120
121121 for ( var i = 0 ; i < max ; i ++ )
@@ -130,36 +130,7 @@ private static void AssertNoteEqual(string expected, string actual, int noteIdx,
130130 result = exp == act ;
131131 }
132132
133- if ( ! result )
134- {
135- // 尝试是否是只有时间不匹配,如果是的话,允许一定的阈值
136- var expTime = DurationStrRegex ( ) . Match ( exp ) ;
137- var actTime = DurationStrRegex ( ) . Match ( act ) ;
138- var bpm = chart . BpmList . Find ( time ) . Bpm ;
139- if ( expTime . Groups [ 2 ] . Success && actTime . Groups [ 4 ] . Success )
140- { // exp中是分数时间、act中是小数时间的情况
141- // 小数时间化为分数时间,看看是否对的上
142- var numer = decimal . Parse ( actTime . Groups [ 4 ] . Value ) / ( 240 / bpm ) * int . Parse ( expTime . Groups [ 2 ] . Value ) ;
143- if ( Math . Round ( numer ) == int . Parse ( expTime . Groups [ 3 ] . Value ) ) result = true ; // 如果对的上,则不判定为比较失败
144- }
145- else if ( actTime . Groups [ 2 ] . Success && expTime . Groups [ 4 ] . Success )
146- { // exp中是小数时间、act中是分数时间的情况
147- // 分数时间化为小数时间,看是否对的上(差距<1ms)
148- var sec = new Rational ( int . Parse ( actTime . Groups [ 3 ] . Value ) , int . Parse ( actTime . Groups [ 2 ] . Value ) ) * ( 240 / ( Rational ) bpm ) ;
149- if ( Near ( ( double ) sec , double . Parse ( expTime . Groups [ 4 ] . Value ) ) ) result = true ; // 如果对的上,则不判定为比较失败
150- }
151- else if ( actTime . Groups [ 4 ] . Success && expTime . Groups [ 4 ] . Success )
152- { // exp中是小数时间、act中是小数时间的情况
153- var expSec = double . Parse ( expTime . Groups [ 4 ] . Value ) ;
154- var actSec = double . Parse ( actTime . Groups [ 4 ] . Value ) ;
155- if ( Near ( expSec , actSec ) ) result = true ; // 如果对的上,则不判定为比较失败
156- }
157-
158- // 比较等待时间是否相等(没显式写出的就是1拍)
159- var expWait = expTime . Groups [ 1 ] . Success ? double . Parse ( expTime . Groups [ 1 ] . Value ) : 60 / ( double ) bpm ;
160- var actWait = actTime . Groups [ 1 ] . Success ? double . Parse ( actTime . Groups [ 1 ] . Value ) : 60 / ( double ) bpm ;
161- if ( ! Near ( expWait , actWait ) ) result = false ; // 如果等待时间对不上,则仍判定为比较失败
162- }
133+ if ( ! result ) result = CompareDurationStr ( exp , act , time , chart ) ;
163134
164135 if ( ! result ) Assert . Fail (
165136 $ "First difference at Notation { noteIdx + 1 } (time { time } ):{ Environment . NewLine } " +
@@ -169,6 +140,61 @@ private static void AssertNoteEqual(string expected, string actual, int noteIdx,
169140 }
170141 }
171142
143+ private static bool CompareDurationStr ( string exp , string act , Rational time , MaiChart chart )
144+ {
145+ bool result = false ;
146+ // 尝试是否是只有时间不匹配,如果是的话,允许一定的阈值
147+ var expTime = DurationStrRegex ( ) . Match ( exp ) ;
148+ var actTime = DurationStrRegex ( ) . Match ( act ) ;
149+ if ( ! expTime . Success || ! actTime . Success ) return result ;
150+ var expRemain = exp [ ..expTime . Index ] + exp [ ( expTime . Index + expTime . Length ) ..] ;
151+ var actRemain = act [ ..expTime . Index ] + act [ ( actTime . Index + actTime . Length ) ..] ;
152+ if ( actRemain != expRemain ) return result ; // 如果除了时间以外还有其他不一样的,那么直接返回false
153+
154+ // 对act产生的时间标记,做规范性检查。对齐到标准中的每一条
155+ if ( actRemain . Contains ( 'h' ) )
156+ { // Hold / TouchHold
157+ Assert . False ( actTime . Groups [ 1 ] . Success , $ "Hold/TouchHold不应该有等待时间!{ act } ") ;
158+ if ( actTime . Groups [ 4 ] . Success )
159+ { // 绝对时长的情况
160+ Assert . True ( act [ actTime . Groups [ 4 ] . Index - 1 ] == '#' , $ "Hold/TouchHold格式不正确,绝对时长的前面必须带一个井号!{ act } ") ;
161+ }
162+ }
163+ else
164+ {
165+ if ( actTime . Groups [ 4 ] . Success )
166+ { // 绝对时长的情况,前面必须是'bpm#'或'等待时间##'。我们不考虑前面一种情况,则应该断言一定是第二种情况出现了
167+ Assert . True ( actTime . Groups [ 1 ] . Success && actTime . Groups [ 1 ] . Index + actTime . Groups [ 1 ] . Length == actTime . Groups [ 4 ] . Index , $ "星星持续时长使用了非标准语法!{ act } ") ;
168+ }
169+ }
170+
171+ var bpm = chart . BpmList . Find ( time ) . Bpm ;
172+ if ( expTime . Groups [ 2 ] . Success && actTime . Groups [ 4 ] . Success )
173+ { // exp中是分数时间、act中是小数时间的情况
174+ // 小数时间化为分数时间,看看是否对的上
175+ var numer = decimal . Parse ( actTime . Groups [ 4 ] . Value ) / ( 240 / bpm ) * int . Parse ( expTime . Groups [ 2 ] . Value ) ;
176+ if ( Math . Round ( numer ) == int . Parse ( expTime . Groups [ 3 ] . Value ) ) result = true ; // 如果对的上,则不判定为比较失败
177+ }
178+ else if ( actTime . Groups [ 2 ] . Success && expTime . Groups [ 4 ] . Success )
179+ { // exp中是小数时间、act中是分数时间的情况
180+ // 分数时间化为小数时间,看是否对的上(差距<1ms)
181+ var sec = new Rational ( int . Parse ( actTime . Groups [ 3 ] . Value ) , int . Parse ( actTime . Groups [ 2 ] . Value ) ) * ( 240 / ( Rational ) bpm ) ;
182+ if ( Near ( ( double ) sec , double . Parse ( expTime . Groups [ 4 ] . Value ) ) ) result = true ; // 如果对的上,则不判定为比较失败
183+ }
184+ else if ( actTime . Groups [ 4 ] . Success && expTime . Groups [ 4 ] . Success )
185+ { // exp中是小数时间、act中是小数时间的情况
186+ var expSec = double . Parse ( expTime . Groups [ 4 ] . Value ) ;
187+ var actSec = double . Parse ( actTime . Groups [ 4 ] . Value ) ;
188+ if ( Near ( expSec , actSec ) ) result = true ; // 如果对的上,则不判定为比较失败
189+ }
190+
191+ // 比较等待时间是否相等(没显式写出的就是1拍)
192+ var expWait = expTime . Groups [ 1 ] . Success ? double . Parse ( expTime . Groups [ 1 ] . Value ) : 60 / ( double ) bpm ;
193+ var actWait = actTime . Groups [ 1 ] . Success ? double . Parse ( actTime . Groups [ 1 ] . Value ) : 60 / ( double ) bpm ;
194+ if ( ! Near ( expWait , actWait ) ) result = false ; // 如果等待时间对不上,则仍判定为比较失败
195+ return result ;
196+ }
197+
172198 private static string RearrangeNote ( string s )
173199 {
174200 return string . Join ( '`' , s . Split ( '`' ) . Select ( x =>
0 commit comments