Skip to content

Commit dc3494c

Browse files
committed
[+] 谱面统计Chart.Statistics
1 parent 893d315 commit dc3494c

3 files changed

Lines changed: 136 additions & 72 deletions

File tree

chart/Chart.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,6 @@ public void Shift(Rational offset, decimal? bpm = null)
7070
public bool IsDxChart => Notes.Any(note => // 判定DX谱的标准:存在
7171
note is Touch || note.IsEx || (note.IsBreak && note is not Tap) || // Touch 或者 保护套 或者 非Tap/Star的绝赞
7272
note is Slide { segments.Count: > 1 }); // 星星段数大于1(fes星星)
73-
74-
// TODO 把谱面统计搬到Chart类下面来
75-
76-
73+
74+
public Statistics Statistics => new(this);
7775
}

chart/Statistics.cs

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
using MuConvert.utils;
2+
3+
namespace MuConvert.chart;
4+
5+
public class Statistics
6+
{
7+
private readonly Dictionary<string, int> _data = [];
8+
public IReadOnlyDictionary<string, int> Data => _data;
9+
10+
// 烟花数量
11+
public int Firework { get; private set; } = 0;
12+
13+
private void AddNote(Note note)
14+
{
15+
string prefix = "NM";
16+
if (note.IsBreak && note.IsEx) prefix = "BX";
17+
else if (note.IsBreak) prefix = "BR";
18+
else if (note.IsEx) prefix = "EX";
19+
string type;
20+
if (note is Hold) type = "HLD";
21+
else if (note is Star) type = "STR";
22+
else if (note is Tap) type = "TAP";
23+
else if (note is Touch touch)
24+
{
25+
type = "TTP";
26+
if (note is TouchHold) type = "THO";
27+
if (touch.IsFirework) Firework++;
28+
}
29+
else if (note is Slide slide)
30+
{
31+
type = "SLD";
32+
if (slide.OwnHead != null) AddNote(slide.OwnHead);
33+
}
34+
else throw Utils.Fail();
35+
36+
var res = prefix + type;
37+
_data[res] = _data.GetValueOrDefault(res) + 1;
38+
}
39+
40+
internal Statistics(Chart chart)
41+
{
42+
foreach (var note in chart.Notes) AddNote(note);
43+
}
44+
45+
// 音符总数(总物量)
46+
public int Total => _data.Values.Sum();
47+
48+
// 返回按音符类型分组的数量。所返回的字典中会包含的key:TAP,STR,HLD,SLD,TTP,THO
49+
public Dictionary<string, int> ByNoteType =>
50+
_data.GroupBy(x => x.Key[2..5]).ToDictionary(x => x.Key, x => x.Sum(v => v.Value));
51+
52+
// 返回按音符的修饰符分组的数量。所返回的字典中会包含的key:NM,BR,EX,BX
53+
public Dictionary<string, int> ByModifiers =>
54+
_data.GroupBy(x => x.Key[..2]).ToDictionary(x => x.Key, x => x.Sum(v => v.Value));
55+
56+
// 返回按游戏结算画面上屏判定表分类的数量。所返回的字典中会包含的key:TAP,HOLD,SLIDE,TOUCH,BREAK
57+
public Dictionary<string, int> ByScoring =>
58+
_data.GroupBy(x =>
59+
{
60+
if (x.Key[0] == 'B') return "BREAK";
61+
var type = x.Key[2..5];
62+
if (type is "HLD" or "THO") return "HOLD";
63+
else if (type == "SLD") return "SLIDE";
64+
else if (type == "TTP") return "TOUCH";
65+
else if (type is "TAP" or "STR") return "TAP";
66+
else throw Utils.Fail();
67+
}).ToDictionary(x => x.Key, x => x.Sum(v => v.Value));
68+
69+
// 绝赞总数
70+
public int Break => _data.Where(x=>x.Key[0] == 'B').Sum(x=>x.Value);
71+
72+
// 保护套总数
73+
public int EX => _data.Where(x=>x.Key[1] == 'X').Sum(x=>x.Value);
74+
75+
// 修正物量(Slide算3个,Hold算2个,Break算5个)
76+
public int WeightedNoteCount {
77+
get
78+
{
79+
var d = ByScoring;
80+
return d["TAP"] + d["TOUCH"] + d["HOLD"] * 2 + d["SLIDE"] * 3 + d["BREAK"] * 5;
81+
}
82+
}
83+
84+
// 旧框计算规则下的分数
85+
public int OldScore => WeightedNoteCount * 500 + Break * 100;
86+
87+
// 粉1个tap的损失
88+
public double Great1Loss => 100.0d / WeightedNoteCount;
89+
90+
public override string ToString()
91+
{
92+
var t = ByNoteType;
93+
var m = ByModifiers;
94+
List<string> r = [$"Tap: {t["TAP"]}", $"Hold: {t["HLD"]}", $"Star: {t["STR"]}",
95+
$"Slide: {t["SLD"]}", $"Touch: {t["TTP"]}", $"Touch Hold: {t["THO"]}",
96+
$"Total: {Total}",
97+
$"Break: {ByModifiers["BR"] + ByModifiers["BX"]}", $"Ex: {ByModifiers["EX"] + ByModifiers["BX"]}",
98+
$"Firework: {Firework}"];
99+
return string.Join(", ", r);
100+
}
101+
}

generator/MA2Generator.cs

Lines changed: 33 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -203,92 +203,57 @@ private void AddTap(Tap tap, int bar, int tick)
203203
result.AppendLine();
204204

205205
// 统计段
206-
var stats_num = new Dictionary<string, int>
206+
var stats = chart.Statistics;
207+
foreach (var (k, v) in statsNameConversion())
207208
{
208-
["TAP"] = 0, ["BRK"] = 0, ["HLD"] = 0, ["SLD"] = 0,
209-
};
210-
var stats_rec = new Dictionary<string, int>();
211-
var stNamesCvt = statsNameConversion();
212-
foreach (var key in stNamesCvt.Values.Select(x=>x?.Item1).Where(x=>x!=null).Distinct())
213-
{
214-
stats_rec[key!] = 0;
215-
}
216-
foreach (var l in lines)
217-
{
218-
var r = stNamesCvt[l.Name];
219-
if (r == null) continue;
220-
var (rec, num) = r.Value;
221-
stats_rec[rec]++;
222-
stats_num[num]++;
209+
result.AppendLine($"T_REC_{k}\t{stats.Data[v]}");
223210
}
211+
var totalNum = stats.Total;
212+
result.AppendLine($"T_REC_ALL\t{totalNum}");
224213

225-
var rec_all = 0;
226-
foreach (var (k, v) in stats_rec)
227-
{
228-
result.AppendLine($"T_REC_{k}\t{v}");
229-
rec_all += v;
230-
}
231-
result.AppendLine($"T_REC_ALL\t{rec_all}");
232-
foreach (var (k, v) in stats_num)
233-
{
234-
result.AppendLine($"T_NUM_{k}\t{v}");
235-
}
236-
result.AppendLine($"T_NUM_ALL\t{rec_all}");
214+
var statsScoring = stats.ByScoring;
215+
result.AppendLine($"T_NUM_TAP\t{statsScoring["TAP"] + statsScoring["TOUCH"]}");
216+
result.AppendLine($"T_NUM_BRK\t{statsScoring["BREAK"]}");
217+
result.AppendLine($"T_NUM_HLD\t{statsScoring["HOLD"]}");
218+
result.AppendLine($"T_NUM_SLD\t{statsScoring["SLIDE"]}");
219+
result.AppendLine($"T_NUM_ALL\t{totalNum}");
237220

221+
var statsNoteType = stats.ByNoteType;
238222
var stats_judge = new Dictionary<string, int>
239223
{
240-
["TAP"] = stats_num["TAP"] + stats_rec["BRK"] + stats_rec["BXX"] + stats_rec["BST"] + stats_rec["XBS"],
224+
["TAP"] = statsNoteType["TAP"] + statsNoteType["STR"] + statsNoteType["TTP"],
241225
["HLD"] = 0, // TODO 还在研究中
242-
["SLD"] = stats_rec["SLD"] + stats_rec["BSL"],
226+
["SLD"] = statsNoteType["SLD"],
243227
};
244-
var judge_all = 0;
245228
foreach (var (k, v) in stats_judge)
246229
{
247230
result.AppendLine($"T_JUDGE_{k}\t{v}");
248-
judge_all += v;
249231
}
250-
result.AppendLine($"T_JUDGE_ALL\t{judge_all}");
232+
result.AppendLine($"T_JUDGE_ALL\t{stats_judge.Sum(x=>x.Value)}");
251233

252234
result.AppendLine($"TTM_EACHPAIRS\t{0}"); // TODO 还在研究中
253235

254-
var stats_score = new Dictionary<string, int>
255-
{
256-
["TAP"] = stats_num["TAP"] * 500,
257-
["BRK"] = stats_num["BRK"] * 2600,
258-
["HLD"] = stats_num["HLD"] * 1000,
259-
["SLD"] = stats_num["SLD"] * 1500,
260-
};
261-
var score_all = 0;
262-
foreach (var (k, v) in stats_score)
263-
{
264-
result.AppendLine($"TTM_SCR_{k}\t{v}");
265-
score_all += v;
266-
}
267-
result.AppendLine($"TTM_SCR_ALL\t{score_all}");
268-
var score_sss = score_all - stats_num["BRK"] * 100; // 旧框扣除额外分
269-
result.AppendLine($"TTM_SCR_S\t{score_sss * 0.97 / 50 * 50}");
236+
result.AppendLine($"TTM_SCR_TAP\t{(statsScoring["TAP"] + statsScoring["TOUCH"]) * 500}");
237+
result.AppendLine($"TTM_SCR_BRK\t{statsScoring["BREAK"] * 2600}");
238+
result.AppendLine($"TTM_SCR_HLD\t{statsScoring["HOLD"] * 1000}");
239+
result.AppendLine($"TTM_SCR_SLD\t{statsScoring["SLIDE"] * 1500}");
240+
var theoryScore = stats.OldScore;
241+
result.AppendLine($"TTM_SCR_ALL\t{theoryScore}");
242+
243+
var score_sss = stats.WeightedNoteCount * 500; // 旧框扣除额外分
244+
result.AppendLine($"TTM_SCR_S\t{Math.Ceiling(score_sss * 0.97 / 50) * 50}");
270245
result.AppendLine($"TTM_SCR_SS\t{score_sss}");
271-
result.AppendLine($"TTM_RAT_ACV\t{(long)score_all * 10000 / score_sss }"); // 用long避免溢出
246+
result.AppendLine($"TTM_RAT_ACV\t{(long)theoryScore * 10000 / score_sss }"); // 用long避免溢出
272247

273248
return (result.ToString(), alerts);
274249
}
275-
276-
/* 从音符名字到statistics部分的项目名的转换表 */
277-
private Dictionary<string, (string, string)?> statsNameConversion()
250+
251+
private Dictionary<string, string> statsNameConversion() => new()
278252
{
279-
var result = new Dictionary<string, (string, string)?>
280-
{
281-
["NMTAP"] = ("TAP", "TAP"), ["BRTAP"] = ("BRK", "BRK"), ["EXTAP"] = ("XTP", "TAP"), ["BXTAP"] = ("BXX", "BRK"),
282-
["NMHLD"] = ("HLD", "HLD"), ["EXHLD"] = ("XHO", "HLD"), ["BRHLD"] = ("BHO", "BRK"), ["BXHLD"] = ("BXH", "BRK"),
283-
["NMSTR"] = ("STR", "TAP"), ["BRSTR"] = ("BST", "BRK"), ["EXSTR"] = ("XST", "TAP"), ["BXSTR"] = ("XBS", "BRK"),
284-
["NMTTP"] = ("TTP", "TAP"), ["NMTHO"] = ("THO", "HLD"),
285-
};
286-
foreach (var name in Enum.GetNames<SlideType>())
287-
{
288-
result["NM" + name] = ("SLD", "SLD");
289-
result["BR" + name] = ("BSL", "BRK");
290-
result["CN" + name] = null;
291-
}
292-
return result;
293-
}
253+
["TAP"] = "NMTAP", ["BRK"] = "BRTAP", ["XTP"] = "EXTAP", ["BXX"] = "BXTAP",
254+
["HLD"] = "NMHLD", ["XHO"] = "EXHLD", ["BHO"] = "BRHLD", ["BXH"] = "BXHLD",
255+
["STR"] = "NMSTR", ["BST"] = "BRSTR", ["XST"] = "EXSTR", ["XBS"] = "BXSTR",
256+
["TTP"] = "NMTTP", ["THO"] = "NMTHO",
257+
["SLD"] = "NMSLD", ["BSL"] = "BRSLD",
258+
};
294259
}

0 commit comments

Comments
 (0)