@xcpcio/core 0.4.2 → 0.4.3
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 +75 -15
- package/dist/index.d.ts +16 -5
- package/dist/index.mjs +75 -16
- package/package.json +2 -2
- package/src/contest.ts +12 -4
- package/src/problem.ts +2 -0
- package/src/rank.ts +64 -13
- package/src/team.ts +24 -0
package/dist/index.cjs
CHANGED
|
@@ -86,6 +86,8 @@ class ProblemStatistics {
|
|
|
86
86
|
this.rejectedNum = 0;
|
|
87
87
|
this.pendingNum = 0;
|
|
88
88
|
this.submittedNum = 0;
|
|
89
|
+
this.attemptedNum = 0;
|
|
90
|
+
this.ignoreNum = 0;
|
|
89
91
|
this.firstSolveSubmissions = [];
|
|
90
92
|
this.lastSolveSubmissions = [];
|
|
91
93
|
}
|
|
@@ -217,19 +219,25 @@ class Contest {
|
|
|
217
219
|
}
|
|
218
220
|
return getTimeDiff(Math.floor(dayjs__default.duration(this.startTime.diff(baseTime)).asSeconds()));
|
|
219
221
|
}
|
|
220
|
-
|
|
222
|
+
getContestElapsedTime(nowTime) {
|
|
221
223
|
let baseTime = createDayJS(nowTime);
|
|
222
224
|
if (baseTime.isAfter(this.endTime)) {
|
|
223
225
|
baseTime = this.endTime;
|
|
224
226
|
}
|
|
225
|
-
|
|
227
|
+
if (baseTime.isBefore(this.startTime)) {
|
|
228
|
+
baseTime = this.startTime;
|
|
229
|
+
}
|
|
230
|
+
return getTimeDiff(Math.floor(dayjs__default.duration(baseTime.diff(this.startTime)).asSeconds()));
|
|
226
231
|
}
|
|
227
|
-
|
|
232
|
+
getContestRemainingTime(nowTime) {
|
|
228
233
|
let baseTime = createDayJS(nowTime);
|
|
229
234
|
if (baseTime.isAfter(this.endTime)) {
|
|
230
235
|
baseTime = this.endTime;
|
|
231
236
|
}
|
|
232
|
-
|
|
237
|
+
if (baseTime.isBefore(this.startTime)) {
|
|
238
|
+
baseTime = this.startTime;
|
|
239
|
+
}
|
|
240
|
+
return getTimeDiff(Math.floor(dayjs__default.duration(this.endTime.diff(baseTime)).asSeconds()));
|
|
233
241
|
}
|
|
234
242
|
getContestProgressRatio(nowTime) {
|
|
235
243
|
const baseTime = createDayJS(nowTime);
|
|
@@ -382,6 +390,17 @@ class Team {
|
|
|
382
390
|
this.organizationRank = -1;
|
|
383
391
|
this.solvedProblemNum = 0;
|
|
384
392
|
this.attemptedProblemNum = 0;
|
|
393
|
+
this.lastSolvedProblemTimestamp = 0;
|
|
394
|
+
this.penalty = 0;
|
|
395
|
+
this.problemStatistics = [];
|
|
396
|
+
this.problemStatisticsMap = /* @__PURE__ */ new Map();
|
|
397
|
+
}
|
|
398
|
+
reset() {
|
|
399
|
+
this.rank = 0;
|
|
400
|
+
this.organizationRank = -1;
|
|
401
|
+
this.solvedProblemNum = 0;
|
|
402
|
+
this.attemptedProblemNum = 0;
|
|
403
|
+
this.lastSolvedProblemTimestamp = 0;
|
|
385
404
|
this.penalty = 0;
|
|
386
405
|
this.problemStatistics = [];
|
|
387
406
|
this.problemStatisticsMap = /* @__PURE__ */ new Map();
|
|
@@ -406,6 +425,9 @@ class Team {
|
|
|
406
425
|
}
|
|
407
426
|
}
|
|
408
427
|
}
|
|
428
|
+
isEqualRank(otherTeam) {
|
|
429
|
+
return this.solvedProblemNum === otherTeam.solvedProblemNum && this.penalty === otherTeam.penalty;
|
|
430
|
+
}
|
|
409
431
|
static compare(lhs, rhs) {
|
|
410
432
|
if (lhs.solvedProblemNum !== rhs.solvedProblemNum) {
|
|
411
433
|
return rhs.solvedProblemNum - lhs.solvedProblemNum;
|
|
@@ -413,6 +435,9 @@ class Team {
|
|
|
413
435
|
if (lhs.penalty !== rhs.penalty) {
|
|
414
436
|
return lhs.penalty - rhs.penalty;
|
|
415
437
|
}
|
|
438
|
+
if (lhs.lastSolvedProblemTimestamp !== rhs.lastSolvedProblemTimestamp) {
|
|
439
|
+
return lhs.lastSolvedProblemTimestamp - rhs.lastSolvedProblemTimestamp;
|
|
440
|
+
}
|
|
416
441
|
if (lhs.name < rhs.name) {
|
|
417
442
|
return -1;
|
|
418
443
|
} else if (lhs.name > rhs.name) {
|
|
@@ -629,6 +654,21 @@ function createSubmissions(submissionsJSON) {
|
|
|
629
654
|
}
|
|
630
655
|
}
|
|
631
656
|
|
|
657
|
+
class RankOptions {
|
|
658
|
+
constructor() {
|
|
659
|
+
this.enableFilterSubmissionsByTimestamp = false;
|
|
660
|
+
this.width = 0;
|
|
661
|
+
this.timestamp = 0;
|
|
662
|
+
}
|
|
663
|
+
setWidth(width, contest) {
|
|
664
|
+
this.width = width;
|
|
665
|
+
this.timestamp = Math.floor((contest.endTime.unix() - contest.startTime.unix()) * this.width * 1e-4);
|
|
666
|
+
this.enableFilterSubmissionsByTimestamp = true;
|
|
667
|
+
}
|
|
668
|
+
disableFilterSubmissionByTimestamp() {
|
|
669
|
+
this.enableFilterSubmissionsByTimestamp = false;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
632
672
|
class Rank {
|
|
633
673
|
constructor(contest, teams, submissions) {
|
|
634
674
|
this.contest = contest;
|
|
@@ -637,10 +677,12 @@ class Rank {
|
|
|
637
677
|
this.submissions = ___default.cloneDeep(submissions).sort(Submission.compare);
|
|
638
678
|
this.submissionsMap = new Map(this.submissions.map((s) => [s.id, s]));
|
|
639
679
|
this.rankStatistics = new RankStatistics();
|
|
680
|
+
this.options = new RankOptions();
|
|
640
681
|
}
|
|
641
|
-
buildRank(
|
|
682
|
+
buildRank() {
|
|
642
683
|
(() => {
|
|
643
684
|
for (const t of this.teams) {
|
|
685
|
+
t.reset();
|
|
644
686
|
t.problemStatistics = this.contest.problems.map((p) => {
|
|
645
687
|
const ps = new TeamProblemStatistics();
|
|
646
688
|
ps.problem = p;
|
|
@@ -652,7 +694,7 @@ class Rank {
|
|
|
652
694
|
this.contest.problems.forEach((p) => {
|
|
653
695
|
p.statistics.reset();
|
|
654
696
|
});
|
|
655
|
-
for (const s of this.
|
|
697
|
+
for (const s of this.getSubmissions()) {
|
|
656
698
|
const teamId = s.teamId;
|
|
657
699
|
const problemId = s.problemId;
|
|
658
700
|
const team = this.teamsMap.get(teamId);
|
|
@@ -660,11 +702,6 @@ class Rank {
|
|
|
660
702
|
if (team === void 0 || problem === void 0) {
|
|
661
703
|
continue;
|
|
662
704
|
}
|
|
663
|
-
if (options?.timestamp !== void 0 && options?.timestamp !== null) {
|
|
664
|
-
if (s.timestamp > options.timestamp) {
|
|
665
|
-
break;
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
705
|
const problemStatistics = team.problemStatisticsMap.get(problemId);
|
|
669
706
|
const submissions = problemStatistics.submissions;
|
|
670
707
|
submissions.push(s);
|
|
@@ -693,6 +730,7 @@ class Rank {
|
|
|
693
730
|
problem.statistics.lastSolveSubmissions.pop();
|
|
694
731
|
}
|
|
695
732
|
problem.statistics.lastSolveSubmissions.push(s);
|
|
733
|
+
team.lastSolvedProblemTimestamp = s.timestamp;
|
|
696
734
|
}
|
|
697
735
|
if (s.isRejected()) {
|
|
698
736
|
problemStatistics.failedCount++;
|
|
@@ -707,19 +745,34 @@ class Rank {
|
|
|
707
745
|
this.teams.sort(Team.compare);
|
|
708
746
|
{
|
|
709
747
|
let rank = 1;
|
|
748
|
+
let preTeam = null;
|
|
710
749
|
for (const t of this.teams) {
|
|
711
750
|
t.rank = rank++;
|
|
751
|
+
if (preTeam !== null) {
|
|
752
|
+
if (t.isEqualRank(preTeam)) {
|
|
753
|
+
t.rank = preTeam.rank;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
preTeam = t;
|
|
712
757
|
}
|
|
713
758
|
}
|
|
714
759
|
if (this.contest.organization) {
|
|
715
760
|
let rank = 1;
|
|
761
|
+
let preTeam = null;
|
|
716
762
|
const se = /* @__PURE__ */ new Set();
|
|
717
763
|
for (const t of this.teams) {
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
764
|
+
const org = t.organization;
|
|
765
|
+
if (se.has(org)) {
|
|
766
|
+
continue;
|
|
767
|
+
}
|
|
768
|
+
se.add(org);
|
|
769
|
+
t.organizationRank = rank++;
|
|
770
|
+
if (preTeam !== null) {
|
|
771
|
+
if (t.isEqualRank(preTeam)) {
|
|
772
|
+
t.organizationRank = preTeam.organizationRank;
|
|
773
|
+
}
|
|
722
774
|
}
|
|
775
|
+
preTeam = t;
|
|
723
776
|
}
|
|
724
777
|
}
|
|
725
778
|
})();
|
|
@@ -732,6 +785,12 @@ class Rank {
|
|
|
732
785
|
})();
|
|
733
786
|
return this;
|
|
734
787
|
}
|
|
788
|
+
getSubmissions() {
|
|
789
|
+
if (this.options.enableFilterSubmissionsByTimestamp === false) {
|
|
790
|
+
return this.submissions;
|
|
791
|
+
}
|
|
792
|
+
return this.submissions.filter((s) => s.timestamp <= this.options.timestamp).sort(Submission.compare);
|
|
793
|
+
}
|
|
735
794
|
}
|
|
736
795
|
|
|
737
796
|
class ResolverOperation {
|
|
@@ -829,6 +888,7 @@ exports.ContestIndexConfig = ContestIndexConfig;
|
|
|
829
888
|
exports.Problem = Problem;
|
|
830
889
|
exports.ProblemStatistics = ProblemStatistics;
|
|
831
890
|
exports.Rank = Rank;
|
|
891
|
+
exports.RankOptions = RankOptions;
|
|
832
892
|
exports.RankStatistics = RankStatistics;
|
|
833
893
|
exports.Resolver = Resolver;
|
|
834
894
|
exports.Submission = Submission;
|
package/dist/index.d.ts
CHANGED
|
@@ -101,8 +101,8 @@ declare class Contest {
|
|
|
101
101
|
getContestDuration(timeFormat?: string): string;
|
|
102
102
|
getContestState(nowTime?: Date): ContestState;
|
|
103
103
|
getContestPendingTime(nowTime?: Date): string;
|
|
104
|
-
getContestRemainingTime(nowTime?: Date): string;
|
|
105
104
|
getContestElapsedTime(nowTime?: Date): string;
|
|
105
|
+
getContestRemainingTime(nowTime?: Date): string;
|
|
106
106
|
getContestProgressRatio(nowTime?: Date): number;
|
|
107
107
|
}
|
|
108
108
|
declare function createContest(contestJSON: Contest$1): Contest;
|
|
@@ -148,19 +148,30 @@ declare class Team {
|
|
|
148
148
|
organizationRank: number;
|
|
149
149
|
solvedProblemNum: number;
|
|
150
150
|
attemptedProblemNum: number;
|
|
151
|
+
lastSolvedProblemTimestamp: number;
|
|
151
152
|
penalty: number;
|
|
152
153
|
problemStatistics: Array<TeamProblemStatistics>;
|
|
153
154
|
problemStatisticsMap: Map<string, TeamProblemStatistics>;
|
|
154
155
|
constructor();
|
|
156
|
+
reset(): void;
|
|
155
157
|
get penaltyToMinute(): number;
|
|
156
158
|
get dict(): number;
|
|
157
159
|
calcSolvedData(): void;
|
|
160
|
+
isEqualRank(otherTeam: Team): boolean;
|
|
158
161
|
static compare(lhs: Team, rhs: Team): number;
|
|
159
162
|
}
|
|
160
163
|
type Teams = Array<Team>;
|
|
161
164
|
declare function createTeam(teamJSON: Team$1): Team;
|
|
162
165
|
declare function createTeams(teamsJSON: Teams$1): Teams;
|
|
163
166
|
|
|
167
|
+
declare class RankOptions {
|
|
168
|
+
enableFilterSubmissionsByTimestamp: boolean;
|
|
169
|
+
width: number;
|
|
170
|
+
timestamp: number;
|
|
171
|
+
constructor();
|
|
172
|
+
setWidth(width: number, contest: Contest): void;
|
|
173
|
+
disableFilterSubmissionByTimestamp(): void;
|
|
174
|
+
}
|
|
164
175
|
declare class Rank {
|
|
165
176
|
readonly contest: Contest;
|
|
166
177
|
teams: Teams;
|
|
@@ -168,10 +179,10 @@ declare class Rank {
|
|
|
168
179
|
submissions: Submissions;
|
|
169
180
|
submissionsMap: Map<string, Submission>;
|
|
170
181
|
rankStatistics: RankStatistics;
|
|
182
|
+
options: RankOptions;
|
|
171
183
|
constructor(contest: Contest, teams: Teams, submissions: Submissions);
|
|
172
|
-
buildRank(
|
|
173
|
-
|
|
174
|
-
}): this;
|
|
184
|
+
buildRank(): this;
|
|
185
|
+
getSubmissions(): Submissions;
|
|
175
186
|
}
|
|
176
187
|
|
|
177
188
|
declare class ResolverOperation {
|
|
@@ -198,4 +209,4 @@ declare function isRejected(status: SubmissionStatus): boolean;
|
|
|
198
209
|
declare function isPending(status: SubmissionStatus): boolean;
|
|
199
210
|
declare function isNotCalculatedPenaltyStatus(status: SubmissionStatus): boolean;
|
|
200
211
|
|
|
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 };
|
|
212
|
+
export { Contest, ContestIndex, ContestIndexConfig, ContestIndexList, Problem, ProblemStatistics, Problems, Rank, RankOptions, 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
|
@@ -70,6 +70,8 @@ class ProblemStatistics {
|
|
|
70
70
|
this.rejectedNum = 0;
|
|
71
71
|
this.pendingNum = 0;
|
|
72
72
|
this.submittedNum = 0;
|
|
73
|
+
this.attemptedNum = 0;
|
|
74
|
+
this.ignoreNum = 0;
|
|
73
75
|
this.firstSolveSubmissions = [];
|
|
74
76
|
this.lastSolveSubmissions = [];
|
|
75
77
|
}
|
|
@@ -201,19 +203,25 @@ class Contest {
|
|
|
201
203
|
}
|
|
202
204
|
return getTimeDiff(Math.floor(dayjs.duration(this.startTime.diff(baseTime)).asSeconds()));
|
|
203
205
|
}
|
|
204
|
-
|
|
206
|
+
getContestElapsedTime(nowTime) {
|
|
205
207
|
let baseTime = createDayJS(nowTime);
|
|
206
208
|
if (baseTime.isAfter(this.endTime)) {
|
|
207
209
|
baseTime = this.endTime;
|
|
208
210
|
}
|
|
209
|
-
|
|
211
|
+
if (baseTime.isBefore(this.startTime)) {
|
|
212
|
+
baseTime = this.startTime;
|
|
213
|
+
}
|
|
214
|
+
return getTimeDiff(Math.floor(dayjs.duration(baseTime.diff(this.startTime)).asSeconds()));
|
|
210
215
|
}
|
|
211
|
-
|
|
216
|
+
getContestRemainingTime(nowTime) {
|
|
212
217
|
let baseTime = createDayJS(nowTime);
|
|
213
218
|
if (baseTime.isAfter(this.endTime)) {
|
|
214
219
|
baseTime = this.endTime;
|
|
215
220
|
}
|
|
216
|
-
|
|
221
|
+
if (baseTime.isBefore(this.startTime)) {
|
|
222
|
+
baseTime = this.startTime;
|
|
223
|
+
}
|
|
224
|
+
return getTimeDiff(Math.floor(dayjs.duration(this.endTime.diff(baseTime)).asSeconds()));
|
|
217
225
|
}
|
|
218
226
|
getContestProgressRatio(nowTime) {
|
|
219
227
|
const baseTime = createDayJS(nowTime);
|
|
@@ -366,6 +374,17 @@ class Team {
|
|
|
366
374
|
this.organizationRank = -1;
|
|
367
375
|
this.solvedProblemNum = 0;
|
|
368
376
|
this.attemptedProblemNum = 0;
|
|
377
|
+
this.lastSolvedProblemTimestamp = 0;
|
|
378
|
+
this.penalty = 0;
|
|
379
|
+
this.problemStatistics = [];
|
|
380
|
+
this.problemStatisticsMap = /* @__PURE__ */ new Map();
|
|
381
|
+
}
|
|
382
|
+
reset() {
|
|
383
|
+
this.rank = 0;
|
|
384
|
+
this.organizationRank = -1;
|
|
385
|
+
this.solvedProblemNum = 0;
|
|
386
|
+
this.attemptedProblemNum = 0;
|
|
387
|
+
this.lastSolvedProblemTimestamp = 0;
|
|
369
388
|
this.penalty = 0;
|
|
370
389
|
this.problemStatistics = [];
|
|
371
390
|
this.problemStatisticsMap = /* @__PURE__ */ new Map();
|
|
@@ -390,6 +409,9 @@ class Team {
|
|
|
390
409
|
}
|
|
391
410
|
}
|
|
392
411
|
}
|
|
412
|
+
isEqualRank(otherTeam) {
|
|
413
|
+
return this.solvedProblemNum === otherTeam.solvedProblemNum && this.penalty === otherTeam.penalty;
|
|
414
|
+
}
|
|
393
415
|
static compare(lhs, rhs) {
|
|
394
416
|
if (lhs.solvedProblemNum !== rhs.solvedProblemNum) {
|
|
395
417
|
return rhs.solvedProblemNum - lhs.solvedProblemNum;
|
|
@@ -397,6 +419,9 @@ class Team {
|
|
|
397
419
|
if (lhs.penalty !== rhs.penalty) {
|
|
398
420
|
return lhs.penalty - rhs.penalty;
|
|
399
421
|
}
|
|
422
|
+
if (lhs.lastSolvedProblemTimestamp !== rhs.lastSolvedProblemTimestamp) {
|
|
423
|
+
return lhs.lastSolvedProblemTimestamp - rhs.lastSolvedProblemTimestamp;
|
|
424
|
+
}
|
|
400
425
|
if (lhs.name < rhs.name) {
|
|
401
426
|
return -1;
|
|
402
427
|
} else if (lhs.name > rhs.name) {
|
|
@@ -613,6 +638,21 @@ function createSubmissions(submissionsJSON) {
|
|
|
613
638
|
}
|
|
614
639
|
}
|
|
615
640
|
|
|
641
|
+
class RankOptions {
|
|
642
|
+
constructor() {
|
|
643
|
+
this.enableFilterSubmissionsByTimestamp = false;
|
|
644
|
+
this.width = 0;
|
|
645
|
+
this.timestamp = 0;
|
|
646
|
+
}
|
|
647
|
+
setWidth(width, contest) {
|
|
648
|
+
this.width = width;
|
|
649
|
+
this.timestamp = Math.floor((contest.endTime.unix() - contest.startTime.unix()) * this.width * 1e-4);
|
|
650
|
+
this.enableFilterSubmissionsByTimestamp = true;
|
|
651
|
+
}
|
|
652
|
+
disableFilterSubmissionByTimestamp() {
|
|
653
|
+
this.enableFilterSubmissionsByTimestamp = false;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
616
656
|
class Rank {
|
|
617
657
|
constructor(contest, teams, submissions) {
|
|
618
658
|
this.contest = contest;
|
|
@@ -621,10 +661,12 @@ class Rank {
|
|
|
621
661
|
this.submissions = _.cloneDeep(submissions).sort(Submission.compare);
|
|
622
662
|
this.submissionsMap = new Map(this.submissions.map((s) => [s.id, s]));
|
|
623
663
|
this.rankStatistics = new RankStatistics();
|
|
664
|
+
this.options = new RankOptions();
|
|
624
665
|
}
|
|
625
|
-
buildRank(
|
|
666
|
+
buildRank() {
|
|
626
667
|
(() => {
|
|
627
668
|
for (const t of this.teams) {
|
|
669
|
+
t.reset();
|
|
628
670
|
t.problemStatistics = this.contest.problems.map((p) => {
|
|
629
671
|
const ps = new TeamProblemStatistics();
|
|
630
672
|
ps.problem = p;
|
|
@@ -636,7 +678,7 @@ class Rank {
|
|
|
636
678
|
this.contest.problems.forEach((p) => {
|
|
637
679
|
p.statistics.reset();
|
|
638
680
|
});
|
|
639
|
-
for (const s of this.
|
|
681
|
+
for (const s of this.getSubmissions()) {
|
|
640
682
|
const teamId = s.teamId;
|
|
641
683
|
const problemId = s.problemId;
|
|
642
684
|
const team = this.teamsMap.get(teamId);
|
|
@@ -644,11 +686,6 @@ class Rank {
|
|
|
644
686
|
if (team === void 0 || problem === void 0) {
|
|
645
687
|
continue;
|
|
646
688
|
}
|
|
647
|
-
if (options?.timestamp !== void 0 && options?.timestamp !== null) {
|
|
648
|
-
if (s.timestamp > options.timestamp) {
|
|
649
|
-
break;
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
689
|
const problemStatistics = team.problemStatisticsMap.get(problemId);
|
|
653
690
|
const submissions = problemStatistics.submissions;
|
|
654
691
|
submissions.push(s);
|
|
@@ -677,6 +714,7 @@ class Rank {
|
|
|
677
714
|
problem.statistics.lastSolveSubmissions.pop();
|
|
678
715
|
}
|
|
679
716
|
problem.statistics.lastSolveSubmissions.push(s);
|
|
717
|
+
team.lastSolvedProblemTimestamp = s.timestamp;
|
|
680
718
|
}
|
|
681
719
|
if (s.isRejected()) {
|
|
682
720
|
problemStatistics.failedCount++;
|
|
@@ -691,19 +729,34 @@ class Rank {
|
|
|
691
729
|
this.teams.sort(Team.compare);
|
|
692
730
|
{
|
|
693
731
|
let rank = 1;
|
|
732
|
+
let preTeam = null;
|
|
694
733
|
for (const t of this.teams) {
|
|
695
734
|
t.rank = rank++;
|
|
735
|
+
if (preTeam !== null) {
|
|
736
|
+
if (t.isEqualRank(preTeam)) {
|
|
737
|
+
t.rank = preTeam.rank;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
preTeam = t;
|
|
696
741
|
}
|
|
697
742
|
}
|
|
698
743
|
if (this.contest.organization) {
|
|
699
744
|
let rank = 1;
|
|
745
|
+
let preTeam = null;
|
|
700
746
|
const se = /* @__PURE__ */ new Set();
|
|
701
747
|
for (const t of this.teams) {
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
748
|
+
const org = t.organization;
|
|
749
|
+
if (se.has(org)) {
|
|
750
|
+
continue;
|
|
751
|
+
}
|
|
752
|
+
se.add(org);
|
|
753
|
+
t.organizationRank = rank++;
|
|
754
|
+
if (preTeam !== null) {
|
|
755
|
+
if (t.isEqualRank(preTeam)) {
|
|
756
|
+
t.organizationRank = preTeam.organizationRank;
|
|
757
|
+
}
|
|
706
758
|
}
|
|
759
|
+
preTeam = t;
|
|
707
760
|
}
|
|
708
761
|
}
|
|
709
762
|
})();
|
|
@@ -716,6 +769,12 @@ class Rank {
|
|
|
716
769
|
})();
|
|
717
770
|
return this;
|
|
718
771
|
}
|
|
772
|
+
getSubmissions() {
|
|
773
|
+
if (this.options.enableFilterSubmissionsByTimestamp === false) {
|
|
774
|
+
return this.submissions;
|
|
775
|
+
}
|
|
776
|
+
return this.submissions.filter((s) => s.timestamp <= this.options.timestamp).sort(Submission.compare);
|
|
777
|
+
}
|
|
719
778
|
}
|
|
720
779
|
|
|
721
780
|
class ResolverOperation {
|
|
@@ -806,4 +865,4 @@ class Resolver extends Rank {
|
|
|
806
865
|
}
|
|
807
866
|
}
|
|
808
867
|
|
|
809
|
-
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 };
|
|
868
|
+
export { Contest, ContestIndex, ContestIndexConfig, Problem, ProblemStatistics, Rank, RankOptions, 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 };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xcpcio/core",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"description": "XCPCIO Core",
|
|
5
5
|
"author": "Dup4 <lyuzhi.pan@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"dayjs": "^1.11.8",
|
|
44
44
|
"lodash": "^4.17.21",
|
|
45
|
-
"@xcpcio/types": "0.4.
|
|
45
|
+
"@xcpcio/types": "0.4.3"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@babel/types": "^7.22.4",
|
package/src/contest.ts
CHANGED
|
@@ -89,22 +89,30 @@ export class Contest {
|
|
|
89
89
|
return getTimeDiff(Math.floor(dayjs.duration(this.startTime.diff(baseTime)).asSeconds()));
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
|
|
92
|
+
getContestElapsedTime(nowTime?: Date): string {
|
|
93
93
|
let baseTime = createDayJS(nowTime);
|
|
94
94
|
if (baseTime.isAfter(this.endTime)) {
|
|
95
95
|
baseTime = this.endTime;
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
|
|
98
|
+
if (baseTime.isBefore(this.startTime)) {
|
|
99
|
+
baseTime = this.startTime;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return getTimeDiff(Math.floor(dayjs.duration(baseTime.diff(this.startTime)).asSeconds()));
|
|
99
103
|
}
|
|
100
104
|
|
|
101
|
-
|
|
105
|
+
getContestRemainingTime(nowTime?: Date): string {
|
|
102
106
|
let baseTime = createDayJS(nowTime);
|
|
103
107
|
if (baseTime.isAfter(this.endTime)) {
|
|
104
108
|
baseTime = this.endTime;
|
|
105
109
|
}
|
|
106
110
|
|
|
107
|
-
|
|
111
|
+
if (baseTime.isBefore(this.startTime)) {
|
|
112
|
+
baseTime = this.startTime;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return getTimeDiff(Math.floor(dayjs.duration(this.endTime.diff(baseTime)).asSeconds()));
|
|
108
116
|
}
|
|
109
117
|
|
|
110
118
|
getContestProgressRatio(nowTime?: Date): number {
|
package/src/problem.ts
CHANGED
package/src/rank.ts
CHANGED
|
@@ -8,6 +8,28 @@ import { Submission } from "./submission";
|
|
|
8
8
|
import { TeamProblemStatistics } from "./problem";
|
|
9
9
|
import { RankStatistics } from "./rank-statistics";
|
|
10
10
|
|
|
11
|
+
export class RankOptions {
|
|
12
|
+
enableFilterSubmissionsByTimestamp: boolean;
|
|
13
|
+
width: number;
|
|
14
|
+
timestamp: number;
|
|
15
|
+
|
|
16
|
+
constructor() {
|
|
17
|
+
this.enableFilterSubmissionsByTimestamp = false;
|
|
18
|
+
this.width = 0;
|
|
19
|
+
this.timestamp = 0;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
setWidth(width: number, contest: Contest) {
|
|
23
|
+
this.width = width;
|
|
24
|
+
this.timestamp = Math.floor((contest.endTime.unix() - contest.startTime.unix()) * this.width * 0.0001);
|
|
25
|
+
this.enableFilterSubmissionsByTimestamp = true;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
disableFilterSubmissionByTimestamp() {
|
|
29
|
+
this.enableFilterSubmissionsByTimestamp = false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
11
33
|
export class Rank {
|
|
12
34
|
readonly contest: Contest;
|
|
13
35
|
|
|
@@ -19,6 +41,8 @@ export class Rank {
|
|
|
19
41
|
|
|
20
42
|
rankStatistics: RankStatistics;
|
|
21
43
|
|
|
44
|
+
options: RankOptions;
|
|
45
|
+
|
|
22
46
|
constructor(contest: Contest, teams: Teams, submissions: Submissions) {
|
|
23
47
|
this.contest = contest;
|
|
24
48
|
|
|
@@ -29,11 +53,15 @@ export class Rank {
|
|
|
29
53
|
this.submissionsMap = new Map(this.submissions.map(s => [s.id, s]));
|
|
30
54
|
|
|
31
55
|
this.rankStatistics = new RankStatistics();
|
|
56
|
+
|
|
57
|
+
this.options = new RankOptions();
|
|
32
58
|
}
|
|
33
59
|
|
|
34
|
-
buildRank(
|
|
60
|
+
buildRank() {
|
|
35
61
|
(() => {
|
|
36
62
|
for (const t of this.teams) {
|
|
63
|
+
t.reset();
|
|
64
|
+
|
|
37
65
|
t.problemStatistics = this.contest.problems.map((p) => {
|
|
38
66
|
const ps = new TeamProblemStatistics();
|
|
39
67
|
ps.problem = p;
|
|
@@ -49,7 +77,7 @@ export class Rank {
|
|
|
49
77
|
p.statistics.reset();
|
|
50
78
|
});
|
|
51
79
|
|
|
52
|
-
for (const s of this.
|
|
80
|
+
for (const s of this.getSubmissions()) {
|
|
53
81
|
const teamId = s.teamId;
|
|
54
82
|
const problemId = s.problemId;
|
|
55
83
|
const team = this.teamsMap.get(teamId);
|
|
@@ -59,12 +87,6 @@ export class Rank {
|
|
|
59
87
|
continue;
|
|
60
88
|
}
|
|
61
89
|
|
|
62
|
-
if (options?.timestamp !== undefined && options?.timestamp !== null) {
|
|
63
|
-
if (s.timestamp > options.timestamp) {
|
|
64
|
-
break;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
90
|
const problemStatistics = team.problemStatisticsMap.get(problemId) as TeamProblemStatistics;
|
|
69
91
|
const submissions = problemStatistics.submissions;
|
|
70
92
|
|
|
@@ -105,6 +127,7 @@ export class Rank {
|
|
|
105
127
|
}
|
|
106
128
|
|
|
107
129
|
problem.statistics.lastSolveSubmissions.push(s);
|
|
130
|
+
team.lastSolvedProblemTimestamp = s.timestamp;
|
|
108
131
|
}
|
|
109
132
|
|
|
110
133
|
if (s.isRejected()) {
|
|
@@ -123,21 +146,42 @@ export class Rank {
|
|
|
123
146
|
|
|
124
147
|
{
|
|
125
148
|
let rank = 1;
|
|
149
|
+
let preTeam = null;
|
|
126
150
|
for (const t of this.teams) {
|
|
127
151
|
t.rank = rank++;
|
|
152
|
+
|
|
153
|
+
if (preTeam !== null) {
|
|
154
|
+
if (t.isEqualRank(preTeam)) {
|
|
155
|
+
t.rank = preTeam.rank;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
preTeam = t;
|
|
128
160
|
}
|
|
129
161
|
}
|
|
130
162
|
|
|
131
163
|
if (this.contest.organization) {
|
|
132
164
|
let rank = 1;
|
|
165
|
+
let preTeam = null;
|
|
166
|
+
|
|
133
167
|
const se = new Set<string>();
|
|
134
168
|
|
|
135
169
|
for (const t of this.teams) {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
170
|
+
const org = t.organization;
|
|
171
|
+
if (se.has(org)) {
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
se.add(org);
|
|
176
|
+
t.organizationRank = rank++;
|
|
177
|
+
|
|
178
|
+
if (preTeam !== null) {
|
|
179
|
+
if (t.isEqualRank(preTeam)) {
|
|
180
|
+
t.organizationRank = preTeam.organizationRank;
|
|
181
|
+
}
|
|
140
182
|
}
|
|
183
|
+
|
|
184
|
+
preTeam = t;
|
|
141
185
|
}
|
|
142
186
|
}
|
|
143
187
|
})();
|
|
@@ -146,7 +190,6 @@ export class Rank {
|
|
|
146
190
|
this.rankStatistics.reset();
|
|
147
191
|
|
|
148
192
|
this.rankStatistics.teamSolvedNum = Array(this.contest.problems.length + 1).fill(0);
|
|
149
|
-
|
|
150
193
|
for (const t of this.teams) {
|
|
151
194
|
this.rankStatistics.teamSolvedNum[t.solvedProblemNum]++;
|
|
152
195
|
}
|
|
@@ -154,4 +197,12 @@ export class Rank {
|
|
|
154
197
|
|
|
155
198
|
return this;
|
|
156
199
|
}
|
|
200
|
+
|
|
201
|
+
getSubmissions() {
|
|
202
|
+
if (this.options.enableFilterSubmissionsByTimestamp === false) {
|
|
203
|
+
return this.submissions;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return this.submissions.filter(s => s.timestamp <= this.options.timestamp).sort(Submission.compare);
|
|
207
|
+
}
|
|
157
208
|
}
|
package/src/team.ts
CHANGED
|
@@ -21,6 +21,7 @@ export class Team {
|
|
|
21
21
|
|
|
22
22
|
solvedProblemNum: number;
|
|
23
23
|
attemptedProblemNum: number;
|
|
24
|
+
lastSolvedProblemTimestamp: number;
|
|
24
25
|
|
|
25
26
|
penalty: number;
|
|
26
27
|
|
|
@@ -41,6 +42,21 @@ export class Team {
|
|
|
41
42
|
|
|
42
43
|
this.solvedProblemNum = 0;
|
|
43
44
|
this.attemptedProblemNum = 0;
|
|
45
|
+
this.lastSolvedProblemTimestamp = 0;
|
|
46
|
+
|
|
47
|
+
this.penalty = 0;
|
|
48
|
+
|
|
49
|
+
this.problemStatistics = [];
|
|
50
|
+
this.problemStatisticsMap = new Map<string, TeamProblemStatistics>();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
reset() {
|
|
54
|
+
this.rank = 0;
|
|
55
|
+
this.organizationRank = -1;
|
|
56
|
+
|
|
57
|
+
this.solvedProblemNum = 0;
|
|
58
|
+
this.attemptedProblemNum = 0;
|
|
59
|
+
this.lastSolvedProblemTimestamp = 0;
|
|
44
60
|
|
|
45
61
|
this.penalty = 0;
|
|
46
62
|
|
|
@@ -74,6 +90,10 @@ export class Team {
|
|
|
74
90
|
}
|
|
75
91
|
}
|
|
76
92
|
|
|
93
|
+
isEqualRank(otherTeam: Team) {
|
|
94
|
+
return this.solvedProblemNum === otherTeam.solvedProblemNum && this.penalty === otherTeam.penalty;
|
|
95
|
+
}
|
|
96
|
+
|
|
77
97
|
static compare(lhs: Team, rhs: Team): number {
|
|
78
98
|
if (lhs.solvedProblemNum !== rhs.solvedProblemNum) {
|
|
79
99
|
return rhs.solvedProblemNum - lhs.solvedProblemNum;
|
|
@@ -83,6 +103,10 @@ export class Team {
|
|
|
83
103
|
return lhs.penalty - rhs.penalty;
|
|
84
104
|
}
|
|
85
105
|
|
|
106
|
+
if (lhs.lastSolvedProblemTimestamp !== rhs.lastSolvedProblemTimestamp) {
|
|
107
|
+
return lhs.lastSolvedProblemTimestamp - rhs.lastSolvedProblemTimestamp;
|
|
108
|
+
}
|
|
109
|
+
|
|
86
110
|
if (lhs.name < rhs.name) {
|
|
87
111
|
return -1;
|
|
88
112
|
} else if (lhs.name > rhs.name) {
|