@xcpcio/core 0.3.3 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -27,6 +27,13 @@ const minMax__default = /*#__PURE__*/_interopDefaultLegacy(minMax);
27
27
  const relativeTime__default = /*#__PURE__*/_interopDefaultLegacy(relativeTime);
28
28
  const ___default = /*#__PURE__*/_interopDefaultLegacy(_);
29
29
 
30
+ function calcDict(attemptedNum, solvedNum) {
31
+ if (solvedNum === 0) {
32
+ return 0;
33
+ }
34
+ return Math.floor((attemptedNum - solvedNum) * 100 / attemptedNum);
35
+ }
36
+
30
37
  dayjs__default.extend(duration__default);
31
38
  dayjs__default.extend(utc__default);
32
39
  dayjs__default.extend(timezone__default);
@@ -52,8 +59,9 @@ function getTimestamp(time) {
52
59
  }
53
60
  function getTimeDiff(seconds) {
54
61
  const two = (a) => {
55
- if (a < 10)
56
- return "0" + a;
62
+ if (a < 10) {
63
+ return `0${a}`;
64
+ }
57
65
  return String(a);
58
66
  };
59
67
  const h = Math.floor(seconds / 3600);
@@ -62,17 +70,113 @@ function getTimeDiff(seconds) {
62
70
  return [two(h), two(m), two(s)].join(":");
63
71
  }
64
72
 
73
+ class ContestIndexConfig {
74
+ constructor() {
75
+ this.contestName = "";
76
+ this.startTime = createDayJS();
77
+ this.endTime = createDayJS();
78
+ this.freezeTime = createDayJS();
79
+ this.totalDurationTimestamp = 0;
80
+ this.freezeDurationTimestamp = 0;
81
+ this.unFreezeDurationTimestamp = 0;
82
+ }
83
+ }
84
+ class ContestIndex {
85
+ constructor() {
86
+ this.config = new ContestIndexConfig();
87
+ this.boardLink = "";
88
+ }
89
+ }
90
+ function createContestIndex(contestIndexJSON) {
91
+ const c = new ContestIndex();
92
+ const cc = c.config;
93
+ const cjc = contestIndexJSON.config;
94
+ cc.contestName = cjc.contest_name;
95
+ cc.startTime = createDayJS(cjc.start_time);
96
+ cc.endTime = createDayJS(cjc.end_time);
97
+ cc.totalDurationTimestamp = cc.endTime.unix() - cc.startTime.unix();
98
+ {
99
+ cc.freezeTime = cc.endTime;
100
+ cc.freezeDurationTimestamp = 0;
101
+ if (cjc.frozen_time !== void 0 && cjc.frozen_time != null) {
102
+ const frozenTime = Number(cjc.frozen_time);
103
+ cc.freezeTime = createDayJS(cc.endTime.unix() - frozenTime);
104
+ cc.freezeDurationTimestamp = frozenTime;
105
+ }
106
+ cc.unFreezeDurationTimestamp = cc.totalDurationTimestamp - cc.freezeDurationTimestamp;
107
+ }
108
+ cc.logo = cjc.logo;
109
+ c.boardLink = contestIndexJSON.board_link;
110
+ return c;
111
+ }
112
+ function createContestIndexList(contestListJSON) {
113
+ const contestIndexList = [];
114
+ const dfs = (contestList) => {
115
+ if (Object.prototype.hasOwnProperty.call(contestList, "config")) {
116
+ contestIndexList.push(createContestIndex(contestList));
117
+ } else {
118
+ for (const k in contestList) {
119
+ dfs(contestList[k]);
120
+ }
121
+ }
122
+ };
123
+ dfs(contestListJSON);
124
+ contestIndexList.sort((a, b) => {
125
+ if (a.config.startTime.isBefore(b.config.startTime)) {
126
+ return 1;
127
+ }
128
+ if (a.config.startTime.isAfter(b.config.startTime)) {
129
+ return -1;
130
+ }
131
+ if (a.config.endTime.isBefore(b.config.endTime)) {
132
+ return 1;
133
+ }
134
+ if (a.config.endTime.isAfter(b.config.endTime)) {
135
+ return -1;
136
+ }
137
+ if (a.config.contestName < b.config.contestName) {
138
+ return 1;
139
+ }
140
+ if (a.config.contestName > b.config.contestName) {
141
+ return -1;
142
+ }
143
+ return 0;
144
+ });
145
+ return contestIndexList;
146
+ }
147
+
148
+ class ProblemStatistics {
149
+ constructor() {
150
+ this.acceptedNum = 0;
151
+ this.rejectedNum = 0;
152
+ this.pendingNum = 0;
153
+ this.submittedNum = 0;
154
+ this.attemptedNum = 0;
155
+ this.ignoreNum = 0;
156
+ this.firstSolveSubmissions = [];
157
+ this.lastSolveSubmissions = [];
158
+ }
159
+ reset() {
160
+ this.acceptedNum = 0;
161
+ this.rejectedNum = 0;
162
+ this.pendingNum = 0;
163
+ this.submittedNum = 0;
164
+ this.firstSolveSubmissions = [];
165
+ this.lastSolveSubmissions = [];
166
+ }
167
+ get dict() {
168
+ if (this.acceptedNum === 0) {
169
+ return 0;
170
+ }
171
+ return calcDict(this.attemptedNum, this.acceptedNum);
172
+ }
173
+ }
65
174
  class Problem {
66
175
  constructor() {
67
176
  this.id = "";
68
177
  this.label = "";
69
178
  this.name = "";
70
- this.statistics = {
71
- acceptedNum: 0,
72
- rejectedNum: 0,
73
- pendingNum: 0,
74
- submittedNum: 0
75
- };
179
+ this.statistics = new ProblemStatistics();
76
180
  }
77
181
  }
78
182
  function createProblem(problemJSON) {
@@ -273,6 +377,15 @@ function getImageSource(image) {
273
377
  return "";
274
378
  }
275
379
 
380
+ class RankStatistics {
381
+ constructor() {
382
+ this.teamSolvedNum = [];
383
+ }
384
+ reset() {
385
+ this.teamSolvedNum = [];
386
+ }
387
+ }
388
+
276
389
  class Team {
277
390
  constructor() {
278
391
  this.id = "";
@@ -281,20 +394,29 @@ class Team {
281
394
  this.group = [];
282
395
  this.tag = [];
283
396
  this.rank = 0;
397
+ this.organizationRank = -1;
284
398
  this.solvedProblemNum = 0;
399
+ this.attemptedProblemNum = 0;
285
400
  this.penalty = 0;
286
401
  this.problemStatistics = [];
287
402
  this.problemStatisticsMap = /* @__PURE__ */ new Map();
288
403
  }
289
- penaltyToMinute() {
404
+ get penaltyToMinute() {
290
405
  return Math.floor(this.penalty / 60);
291
406
  }
407
+ get dict() {
408
+ const attemptedNum = this.attemptedProblemNum;
409
+ const solvedNum = this.solvedProblemNum;
410
+ return calcDict(attemptedNum, solvedNum);
411
+ }
292
412
  calcSolvedData() {
293
413
  this.solvedProblemNum = 0;
294
414
  this.penalty = 0;
415
+ this.attemptedProblemNum = 0;
295
416
  for (const p of this.problemStatistics) {
296
417
  if (p.isAccepted) {
297
418
  this.solvedProblemNum++;
419
+ this.attemptedProblemNum += p.failedCount + 1;
298
420
  this.penalty += p.penalty;
299
421
  }
300
422
  }
@@ -319,17 +441,18 @@ function createTeam(teamJSON) {
319
441
  t.id = teamJSON.id ?? teamJSON.team_id ?? "";
320
442
  t.name = teamJSON.name ?? teamJSON.team_name ?? "";
321
443
  t.organization = teamJSON.organization ?? "";
444
+ t.badge = teamJSON.badge;
322
445
  t.group = teamJSON.group ?? [];
323
446
  t.tag = teamJSON.group ?? [];
324
447
  t.coach = teamJSON.coach;
325
448
  t.members = teamJSON.members;
326
- if (teamJSON.official === true) {
449
+ if (Boolean(teamJSON.official) === true) {
327
450
  t.group.push("official");
328
451
  }
329
- if (teamJSON.unofficial === true) {
452
+ if (Boolean(teamJSON.unofficial) === true) {
330
453
  t.group.push("unofficial");
331
454
  }
332
- if (teamJSON.girl === true) {
455
+ if (Boolean(teamJSON.girl) === true) {
333
456
  t.group.push("girl");
334
457
  }
335
458
  t.group = [...new Set(t.group)];
@@ -528,7 +651,7 @@ class Rank {
528
651
  this.teamsMap = new Map(this.teams.map((t) => [t.id, t]));
529
652
  this.submissions = ___default.cloneDeep(submissions).sort(Submission.compare);
530
653
  this.submissionsMap = new Map(this.submissions.map((s) => [s.id, s]));
531
- this.firstSolvedSubmissions = new Map(this.contest.problems.map((p) => [p.id, []]));
654
+ this.rankStatistics = new RankStatistics();
532
655
  }
533
656
  buildRank(options) {
534
657
  (() => {
@@ -541,7 +664,9 @@ class Rank {
541
664
  });
542
665
  t.problemStatisticsMap = new Map(t.problemStatistics.map((ps) => [ps.problem.id, ps]));
543
666
  }
544
- this.firstSolvedSubmissions = new Map(this.contest.problems.map((p) => [p.id, []]));
667
+ this.contest.problems.forEach((p) => {
668
+ p.statistics.reset();
669
+ });
545
670
  for (const s of this.submissions) {
546
671
  const teamId = s.teamId;
547
672
  const problemId = s.problemId;
@@ -557,13 +682,13 @@ class Rank {
557
682
  }
558
683
  const problemStatistics = team.problemStatisticsMap.get(problemId);
559
684
  const submissions = problemStatistics.submissions;
560
- const firstSolvedSubmissions = this.firstSolvedSubmissions.get(problemId);
561
685
  submissions.push(s);
562
686
  problem.statistics.submittedNum++;
563
687
  if (problemStatistics.isSolved) {
564
688
  continue;
565
689
  }
566
690
  if (s.isIgnore || s.isNotCalculatedPenaltyStatus()) {
691
+ problem.statistics.ignoreNum++;
567
692
  problemStatistics.ignoreCount++;
568
693
  continue;
569
694
  }
@@ -574,10 +699,15 @@ class Rank {
574
699
  problemStatistics.isSolved = true;
575
700
  problemStatistics.solvedTimestamp = s.timestamp;
576
701
  problem.statistics.acceptedNum++;
577
- if (firstSolvedSubmissions.length === 0 || firstSolvedSubmissions[firstSolvedSubmissions.length - 1].timestamp === s.timestamp) {
702
+ problem.statistics.attemptedNum += problemStatistics.failedCount + 1;
703
+ if (problem.statistics.firstSolveSubmissions.length === 0 || problem.statistics.firstSolveSubmissions[problem.statistics.firstSolveSubmissions.length - 1].timestamp === s.timestamp) {
578
704
  problemStatistics.isFirstSolved = true;
579
- firstSolvedSubmissions.push(s);
705
+ problem.statistics.firstSolveSubmissions.push(s);
706
+ }
707
+ while (problem.statistics.lastSolveSubmissions.length > 0) {
708
+ problem.statistics.lastSolveSubmissions.pop();
580
709
  }
710
+ problem.statistics.lastSolveSubmissions.push(s);
581
711
  }
582
712
  if (s.isRejected()) {
583
713
  problemStatistics.failedCount++;
@@ -596,6 +726,24 @@ class Rank {
596
726
  t.rank = rank++;
597
727
  }
598
728
  }
729
+ if (this.contest.organization) {
730
+ let rank = 1;
731
+ const se = /* @__PURE__ */ new Set();
732
+ for (const t of this.teams) {
733
+ if (!se.has(t.organization)) {
734
+ se.add(t.organization);
735
+ t.organizationRank = rank;
736
+ rank++;
737
+ }
738
+ }
739
+ }
740
+ })();
741
+ (() => {
742
+ this.rankStatistics.reset();
743
+ this.rankStatistics.teamSolvedNum = Array(this.contest.problems.length + 1).fill(0);
744
+ for (const t of this.teams) {
745
+ this.rankStatistics.teamSolvedNum[t.solvedProblemNum]++;
746
+ }
599
747
  })();
600
748
  return this;
601
749
  }
@@ -691,13 +839,20 @@ class Resolver extends Rank {
691
839
 
692
840
  exports.dayjs = dayjs__default;
693
841
  exports.Contest = Contest;
842
+ exports.ContestIndex = ContestIndex;
843
+ exports.ContestIndexConfig = ContestIndexConfig;
694
844
  exports.Problem = Problem;
845
+ exports.ProblemStatistics = ProblemStatistics;
695
846
  exports.Rank = Rank;
847
+ exports.RankStatistics = RankStatistics;
696
848
  exports.Resolver = Resolver;
697
849
  exports.Submission = Submission;
698
850
  exports.Team = Team;
699
851
  exports.TeamProblemStatistics = TeamProblemStatistics;
852
+ exports.calcDict = calcDict;
700
853
  exports.createContest = createContest;
854
+ exports.createContestIndex = createContestIndex;
855
+ exports.createContestIndexList = createContestIndexList;
701
856
  exports.createDayJS = createDayJS;
702
857
  exports.createProblem = createProblem;
703
858
  exports.createProblems = createProblems;
package/dist/index.d.ts CHANGED
@@ -1,6 +1,28 @@
1
1
  import dayjs from 'dayjs';
2
2
  export { default as dayjs } from 'dayjs';
3
- import { SubmissionStatus, Submission as Submission$1, Submissions as Submissions$1, BalloonColor, Problem as Problem$1, Problems as Problems$1, StatusTimeDisplay, Image, ContestState, Contest as Contest$1, Team as Team$1, Teams as Teams$1 } from '@xcpcio/types';
3
+ import { Image, ContestIndex as ContestIndex$1, SubmissionStatus, Submission as Submission$1, Submissions as Submissions$1, BalloonColor, Problem as Problem$1, Problems as Problems$1, StatusTimeDisplay, ContestState, Contest as Contest$1, Team as Team$1, Teams as Teams$1 } from '@xcpcio/types';
4
+
5
+ declare class ContestIndexConfig {
6
+ contestName: string;
7
+ startTime: dayjs.Dayjs;
8
+ endTime: dayjs.Dayjs;
9
+ freezeTime: dayjs.Dayjs;
10
+ totalDurationTimestamp: number;
11
+ freezeDurationTimestamp: number;
12
+ unFreezeDurationTimestamp: number;
13
+ logo?: Image;
14
+ constructor();
15
+ }
16
+ declare class ContestIndex {
17
+ config: ContestIndexConfig;
18
+ boardLink: string;
19
+ constructor();
20
+ }
21
+ type ContestIndexList = Array<ContestIndex>;
22
+ declare function createContestIndex(contestIndexJSON: ContestIndex$1): ContestIndex;
23
+ declare function createContestIndexList(contestListJSON: any): ContestIndexList;
24
+
25
+ declare function calcDict(attemptedNum: number, solvedNum: number): number;
4
26
 
5
27
  declare function createDayJS(time?: Date | string | number | undefined): dayjs.Dayjs;
6
28
  declare function getTimestamp(time: number | dayjs.Dayjs): number;
@@ -24,11 +46,18 @@ type Submissions = Array<Submission>;
24
46
  declare function createSubmission(submissionJSON: Submission$1): Submission;
25
47
  declare function createSubmissions(submissionsJSON: Submissions$1): Submissions;
26
48
 
27
- interface ProblemStatistics {
49
+ declare class ProblemStatistics {
28
50
  acceptedNum: number;
29
51
  rejectedNum: number;
30
52
  pendingNum: number;
31
53
  submittedNum: number;
54
+ attemptedNum: number;
55
+ ignoreNum: number;
56
+ firstSolveSubmissions: Submissions;
57
+ lastSolveSubmissions: Submissions;
58
+ constructor();
59
+ reset(): void;
60
+ get dict(): number;
32
61
  }
33
62
  declare class Problem {
34
63
  id: string;
@@ -100,21 +129,31 @@ declare function createContest(contestJSON: Contest$1): Contest;
100
129
 
101
130
  declare function getImageSource(image: Image): string;
102
131
 
132
+ declare class RankStatistics {
133
+ teamSolvedNum: Array<number>;
134
+ constructor();
135
+ reset(): void;
136
+ }
137
+
103
138
  declare class Team {
104
139
  id: string;
105
140
  name: string;
106
141
  organization: string;
142
+ badge?: Image;
107
143
  group: Array<string>;
108
144
  tag: Array<string>;
109
145
  coach?: string | Array<string>;
110
146
  members?: string | Array<string>;
111
147
  rank: number;
148
+ organizationRank: number;
112
149
  solvedProblemNum: number;
150
+ attemptedProblemNum: number;
113
151
  penalty: number;
114
152
  problemStatistics: Array<TeamProblemStatistics>;
115
153
  problemStatisticsMap: Map<string, TeamProblemStatistics>;
116
154
  constructor();
117
- penaltyToMinute(): number;
155
+ get penaltyToMinute(): number;
156
+ get dict(): number;
118
157
  calcSolvedData(): void;
119
158
  static compare(lhs: Team, rhs: Team): number;
120
159
  }
@@ -128,7 +167,7 @@ declare class Rank {
128
167
  teamsMap: Map<string, Team>;
129
168
  submissions: Submissions;
130
169
  submissionsMap: Map<string, Submission>;
131
- firstSolvedSubmissions: Map<string, Submissions>;
170
+ rankStatistics: RankStatistics;
132
171
  constructor(contest: Contest, teams: Teams, submissions: Submissions);
133
172
  buildRank(options?: {
134
173
  timestamp?: number;
@@ -159,4 +198,4 @@ declare function isRejected(status: SubmissionStatus): boolean;
159
198
  declare function isPending(status: SubmissionStatus): boolean;
160
199
  declare function isNotCalculatedPenaltyStatus(status: SubmissionStatus): boolean;
161
200
 
162
- export { Contest, Problem, ProblemStatistics, Problems, Rank, Resolver, Submission, Submissions, Team, TeamProblemStatistics, Teams, createContest, createDayJS, createProblem, createProblems, createProblemsByProblemIds, createSubmission, createSubmissions, createTeam, createTeams, getImageSource, getTimeDiff, getTimestamp, isAccepted, isNotCalculatedPenaltyStatus, isPending, isRejected, stringToSubmissionStatus };
201
+ export { Contest, ContestIndex, ContestIndexConfig, ContestIndexList, Problem, ProblemStatistics, Problems, Rank, RankStatistics, Resolver, Submission, Submissions, Team, TeamProblemStatistics, Teams, calcDict, createContest, createContestIndex, createContestIndexList, createDayJS, createProblem, createProblems, createProblemsByProblemIds, createSubmission, createSubmissions, createTeam, createTeams, getImageSource, getTimeDiff, getTimestamp, isAccepted, isNotCalculatedPenaltyStatus, isPending, isRejected, stringToSubmissionStatus };
package/dist/index.mjs CHANGED
@@ -11,6 +11,13 @@ import relativeTime from 'dayjs/plugin/relativeTime';
11
11
  import { VERSION, ContestState, SubmissionStatus } from '@xcpcio/types';
12
12
  import _ from 'lodash';
13
13
 
14
+ function calcDict(attemptedNum, solvedNum) {
15
+ if (solvedNum === 0) {
16
+ return 0;
17
+ }
18
+ return Math.floor((attemptedNum - solvedNum) * 100 / attemptedNum);
19
+ }
20
+
14
21
  dayjs.extend(duration);
15
22
  dayjs.extend(utc);
16
23
  dayjs.extend(timezone);
@@ -36,8 +43,9 @@ function getTimestamp(time) {
36
43
  }
37
44
  function getTimeDiff(seconds) {
38
45
  const two = (a) => {
39
- if (a < 10)
40
- return "0" + a;
46
+ if (a < 10) {
47
+ return `0${a}`;
48
+ }
41
49
  return String(a);
42
50
  };
43
51
  const h = Math.floor(seconds / 3600);
@@ -46,17 +54,113 @@ function getTimeDiff(seconds) {
46
54
  return [two(h), two(m), two(s)].join(":");
47
55
  }
48
56
 
57
+ class ContestIndexConfig {
58
+ constructor() {
59
+ this.contestName = "";
60
+ this.startTime = createDayJS();
61
+ this.endTime = createDayJS();
62
+ this.freezeTime = createDayJS();
63
+ this.totalDurationTimestamp = 0;
64
+ this.freezeDurationTimestamp = 0;
65
+ this.unFreezeDurationTimestamp = 0;
66
+ }
67
+ }
68
+ class ContestIndex {
69
+ constructor() {
70
+ this.config = new ContestIndexConfig();
71
+ this.boardLink = "";
72
+ }
73
+ }
74
+ function createContestIndex(contestIndexJSON) {
75
+ const c = new ContestIndex();
76
+ const cc = c.config;
77
+ const cjc = contestIndexJSON.config;
78
+ cc.contestName = cjc.contest_name;
79
+ cc.startTime = createDayJS(cjc.start_time);
80
+ cc.endTime = createDayJS(cjc.end_time);
81
+ cc.totalDurationTimestamp = cc.endTime.unix() - cc.startTime.unix();
82
+ {
83
+ cc.freezeTime = cc.endTime;
84
+ cc.freezeDurationTimestamp = 0;
85
+ if (cjc.frozen_time !== void 0 && cjc.frozen_time != null) {
86
+ const frozenTime = Number(cjc.frozen_time);
87
+ cc.freezeTime = createDayJS(cc.endTime.unix() - frozenTime);
88
+ cc.freezeDurationTimestamp = frozenTime;
89
+ }
90
+ cc.unFreezeDurationTimestamp = cc.totalDurationTimestamp - cc.freezeDurationTimestamp;
91
+ }
92
+ cc.logo = cjc.logo;
93
+ c.boardLink = contestIndexJSON.board_link;
94
+ return c;
95
+ }
96
+ function createContestIndexList(contestListJSON) {
97
+ const contestIndexList = [];
98
+ const dfs = (contestList) => {
99
+ if (Object.prototype.hasOwnProperty.call(contestList, "config")) {
100
+ contestIndexList.push(createContestIndex(contestList));
101
+ } else {
102
+ for (const k in contestList) {
103
+ dfs(contestList[k]);
104
+ }
105
+ }
106
+ };
107
+ dfs(contestListJSON);
108
+ contestIndexList.sort((a, b) => {
109
+ if (a.config.startTime.isBefore(b.config.startTime)) {
110
+ return 1;
111
+ }
112
+ if (a.config.startTime.isAfter(b.config.startTime)) {
113
+ return -1;
114
+ }
115
+ if (a.config.endTime.isBefore(b.config.endTime)) {
116
+ return 1;
117
+ }
118
+ if (a.config.endTime.isAfter(b.config.endTime)) {
119
+ return -1;
120
+ }
121
+ if (a.config.contestName < b.config.contestName) {
122
+ return 1;
123
+ }
124
+ if (a.config.contestName > b.config.contestName) {
125
+ return -1;
126
+ }
127
+ return 0;
128
+ });
129
+ return contestIndexList;
130
+ }
131
+
132
+ class ProblemStatistics {
133
+ constructor() {
134
+ this.acceptedNum = 0;
135
+ this.rejectedNum = 0;
136
+ this.pendingNum = 0;
137
+ this.submittedNum = 0;
138
+ this.attemptedNum = 0;
139
+ this.ignoreNum = 0;
140
+ this.firstSolveSubmissions = [];
141
+ this.lastSolveSubmissions = [];
142
+ }
143
+ reset() {
144
+ this.acceptedNum = 0;
145
+ this.rejectedNum = 0;
146
+ this.pendingNum = 0;
147
+ this.submittedNum = 0;
148
+ this.firstSolveSubmissions = [];
149
+ this.lastSolveSubmissions = [];
150
+ }
151
+ get dict() {
152
+ if (this.acceptedNum === 0) {
153
+ return 0;
154
+ }
155
+ return calcDict(this.attemptedNum, this.acceptedNum);
156
+ }
157
+ }
49
158
  class Problem {
50
159
  constructor() {
51
160
  this.id = "";
52
161
  this.label = "";
53
162
  this.name = "";
54
- this.statistics = {
55
- acceptedNum: 0,
56
- rejectedNum: 0,
57
- pendingNum: 0,
58
- submittedNum: 0
59
- };
163
+ this.statistics = new ProblemStatistics();
60
164
  }
61
165
  }
62
166
  function createProblem(problemJSON) {
@@ -257,6 +361,15 @@ function getImageSource(image) {
257
361
  return "";
258
362
  }
259
363
 
364
+ class RankStatistics {
365
+ constructor() {
366
+ this.teamSolvedNum = [];
367
+ }
368
+ reset() {
369
+ this.teamSolvedNum = [];
370
+ }
371
+ }
372
+
260
373
  class Team {
261
374
  constructor() {
262
375
  this.id = "";
@@ -265,20 +378,29 @@ class Team {
265
378
  this.group = [];
266
379
  this.tag = [];
267
380
  this.rank = 0;
381
+ this.organizationRank = -1;
268
382
  this.solvedProblemNum = 0;
383
+ this.attemptedProblemNum = 0;
269
384
  this.penalty = 0;
270
385
  this.problemStatistics = [];
271
386
  this.problemStatisticsMap = /* @__PURE__ */ new Map();
272
387
  }
273
- penaltyToMinute() {
388
+ get penaltyToMinute() {
274
389
  return Math.floor(this.penalty / 60);
275
390
  }
391
+ get dict() {
392
+ const attemptedNum = this.attemptedProblemNum;
393
+ const solvedNum = this.solvedProblemNum;
394
+ return calcDict(attemptedNum, solvedNum);
395
+ }
276
396
  calcSolvedData() {
277
397
  this.solvedProblemNum = 0;
278
398
  this.penalty = 0;
399
+ this.attemptedProblemNum = 0;
279
400
  for (const p of this.problemStatistics) {
280
401
  if (p.isAccepted) {
281
402
  this.solvedProblemNum++;
403
+ this.attemptedProblemNum += p.failedCount + 1;
282
404
  this.penalty += p.penalty;
283
405
  }
284
406
  }
@@ -303,17 +425,18 @@ function createTeam(teamJSON) {
303
425
  t.id = teamJSON.id ?? teamJSON.team_id ?? "";
304
426
  t.name = teamJSON.name ?? teamJSON.team_name ?? "";
305
427
  t.organization = teamJSON.organization ?? "";
428
+ t.badge = teamJSON.badge;
306
429
  t.group = teamJSON.group ?? [];
307
430
  t.tag = teamJSON.group ?? [];
308
431
  t.coach = teamJSON.coach;
309
432
  t.members = teamJSON.members;
310
- if (teamJSON.official === true) {
433
+ if (Boolean(teamJSON.official) === true) {
311
434
  t.group.push("official");
312
435
  }
313
- if (teamJSON.unofficial === true) {
436
+ if (Boolean(teamJSON.unofficial) === true) {
314
437
  t.group.push("unofficial");
315
438
  }
316
- if (teamJSON.girl === true) {
439
+ if (Boolean(teamJSON.girl) === true) {
317
440
  t.group.push("girl");
318
441
  }
319
442
  t.group = [...new Set(t.group)];
@@ -512,7 +635,7 @@ class Rank {
512
635
  this.teamsMap = new Map(this.teams.map((t) => [t.id, t]));
513
636
  this.submissions = _.cloneDeep(submissions).sort(Submission.compare);
514
637
  this.submissionsMap = new Map(this.submissions.map((s) => [s.id, s]));
515
- this.firstSolvedSubmissions = new Map(this.contest.problems.map((p) => [p.id, []]));
638
+ this.rankStatistics = new RankStatistics();
516
639
  }
517
640
  buildRank(options) {
518
641
  (() => {
@@ -525,7 +648,9 @@ class Rank {
525
648
  });
526
649
  t.problemStatisticsMap = new Map(t.problemStatistics.map((ps) => [ps.problem.id, ps]));
527
650
  }
528
- this.firstSolvedSubmissions = new Map(this.contest.problems.map((p) => [p.id, []]));
651
+ this.contest.problems.forEach((p) => {
652
+ p.statistics.reset();
653
+ });
529
654
  for (const s of this.submissions) {
530
655
  const teamId = s.teamId;
531
656
  const problemId = s.problemId;
@@ -541,13 +666,13 @@ class Rank {
541
666
  }
542
667
  const problemStatistics = team.problemStatisticsMap.get(problemId);
543
668
  const submissions = problemStatistics.submissions;
544
- const firstSolvedSubmissions = this.firstSolvedSubmissions.get(problemId);
545
669
  submissions.push(s);
546
670
  problem.statistics.submittedNum++;
547
671
  if (problemStatistics.isSolved) {
548
672
  continue;
549
673
  }
550
674
  if (s.isIgnore || s.isNotCalculatedPenaltyStatus()) {
675
+ problem.statistics.ignoreNum++;
551
676
  problemStatistics.ignoreCount++;
552
677
  continue;
553
678
  }
@@ -558,10 +683,15 @@ class Rank {
558
683
  problemStatistics.isSolved = true;
559
684
  problemStatistics.solvedTimestamp = s.timestamp;
560
685
  problem.statistics.acceptedNum++;
561
- if (firstSolvedSubmissions.length === 0 || firstSolvedSubmissions[firstSolvedSubmissions.length - 1].timestamp === s.timestamp) {
686
+ problem.statistics.attemptedNum += problemStatistics.failedCount + 1;
687
+ if (problem.statistics.firstSolveSubmissions.length === 0 || problem.statistics.firstSolveSubmissions[problem.statistics.firstSolveSubmissions.length - 1].timestamp === s.timestamp) {
562
688
  problemStatistics.isFirstSolved = true;
563
- firstSolvedSubmissions.push(s);
689
+ problem.statistics.firstSolveSubmissions.push(s);
690
+ }
691
+ while (problem.statistics.lastSolveSubmissions.length > 0) {
692
+ problem.statistics.lastSolveSubmissions.pop();
564
693
  }
694
+ problem.statistics.lastSolveSubmissions.push(s);
565
695
  }
566
696
  if (s.isRejected()) {
567
697
  problemStatistics.failedCount++;
@@ -580,6 +710,24 @@ class Rank {
580
710
  t.rank = rank++;
581
711
  }
582
712
  }
713
+ if (this.contest.organization) {
714
+ let rank = 1;
715
+ const se = /* @__PURE__ */ new Set();
716
+ for (const t of this.teams) {
717
+ if (!se.has(t.organization)) {
718
+ se.add(t.organization);
719
+ t.organizationRank = rank;
720
+ rank++;
721
+ }
722
+ }
723
+ }
724
+ })();
725
+ (() => {
726
+ this.rankStatistics.reset();
727
+ this.rankStatistics.teamSolvedNum = Array(this.contest.problems.length + 1).fill(0);
728
+ for (const t of this.teams) {
729
+ this.rankStatistics.teamSolvedNum[t.solvedProblemNum]++;
730
+ }
583
731
  })();
584
732
  return this;
585
733
  }
@@ -673,4 +821,4 @@ class Resolver extends Rank {
673
821
  }
674
822
  }
675
823
 
676
- export { Contest, Problem, Rank, Resolver, Submission, Team, TeamProblemStatistics, createContest, createDayJS, createProblem, createProblems, createProblemsByProblemIds, createSubmission, createSubmissions, createTeam, createTeams, getImageSource, getTimeDiff, getTimestamp, isAccepted, isNotCalculatedPenaltyStatus, isPending, isRejected, stringToSubmissionStatus };
824
+ export { Contest, ContestIndex, ContestIndexConfig, Problem, ProblemStatistics, Rank, RankStatistics, Resolver, Submission, Team, TeamProblemStatistics, calcDict, createContest, createContestIndex, createContestIndexList, createDayJS, createProblem, createProblems, createProblemsByProblemIds, createSubmission, createSubmissions, createTeam, createTeams, getImageSource, getTimeDiff, getTimestamp, isAccepted, isNotCalculatedPenaltyStatus, isPending, isRejected, stringToSubmissionStatus };