@xcpcio/core 0.4.3 → 0.5.0

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
@@ -379,6 +379,13 @@ class RankStatistics {
379
379
  }
380
380
  }
381
381
 
382
+ class PlaceChartPointData {
383
+ constructor() {
384
+ this.timePoint = 0;
385
+ this.rank = 0;
386
+ this.lastSolvedProblem = null;
387
+ }
388
+ }
382
389
  class Team {
383
390
  constructor() {
384
391
  this.id = "";
@@ -390,20 +397,26 @@ class Team {
390
397
  this.organizationRank = -1;
391
398
  this.solvedProblemNum = 0;
392
399
  this.attemptedProblemNum = 0;
400
+ this.lastSolvedProblem = null;
393
401
  this.lastSolvedProblemTimestamp = 0;
394
402
  this.penalty = 0;
395
403
  this.problemStatistics = [];
396
404
  this.problemStatisticsMap = /* @__PURE__ */ new Map();
405
+ this.submissions = [];
406
+ this.placeChartPoints = [];
397
407
  }
398
408
  reset() {
399
409
  this.rank = 0;
400
410
  this.organizationRank = -1;
401
411
  this.solvedProblemNum = 0;
402
412
  this.attemptedProblemNum = 0;
413
+ this.lastSolvedProblem = null;
403
414
  this.lastSolvedProblemTimestamp = 0;
404
415
  this.penalty = 0;
405
416
  this.problemStatistics = [];
406
417
  this.problemStatisticsMap = /* @__PURE__ */ new Map();
418
+ this.submissions = [];
419
+ this.placeChartPoints = [];
407
420
  }
408
421
  get penaltyToMinute() {
409
422
  return Math.floor(this.penalty / 60);
@@ -415,8 +428,8 @@ class Team {
415
428
  }
416
429
  calcSolvedData() {
417
430
  this.solvedProblemNum = 0;
418
- this.penalty = 0;
419
431
  this.attemptedProblemNum = 0;
432
+ this.penalty = 0;
420
433
  for (const p of this.problemStatistics) {
421
434
  if (p.isAccepted) {
422
435
  this.solvedProblemNum++;
@@ -428,6 +441,24 @@ class Team {
428
441
  isEqualRank(otherTeam) {
429
442
  return this.solvedProblemNum === otherTeam.solvedProblemNum && this.penalty === otherTeam.penalty;
430
443
  }
444
+ postProcessPlaceChartPoints() {
445
+ if (this.placeChartPoints.length === 0) {
446
+ return;
447
+ }
448
+ const res = [];
449
+ res.push(this.placeChartPoints[0]);
450
+ for (let i = 1; i < this.placeChartPoints.length - 1; i++) {
451
+ const p = this.placeChartPoints[i];
452
+ const preP = res[res.length - 1];
453
+ if (p.rank !== preP.rank || p.lastSolvedProblem !== preP.lastSolvedProblem) {
454
+ res.push(p);
455
+ }
456
+ }
457
+ if (this.placeChartPoints.length > 1) {
458
+ res.push(this.placeChartPoints[this.placeChartPoints.length - 1]);
459
+ }
460
+ this.placeChartPoints = res;
461
+ }
431
462
  static compare(lhs, rhs) {
432
463
  if (lhs.solvedProblemNum !== rhs.solvedProblemNum) {
433
464
  return rhs.solvedProblemNum - lhs.solvedProblemNum;
@@ -618,6 +649,9 @@ class Submission {
618
649
  isNotCalculatedPenaltyStatus() {
619
650
  return isNotCalculatedPenaltyStatus(this.status);
620
651
  }
652
+ get timestampToMinute() {
653
+ return Math.floor(this.timestamp / 60);
654
+ }
621
655
  static compare(lhs, rhs) {
622
656
  if (lhs.timestamp !== rhs.timestamp) {
623
657
  return lhs.timestamp - rhs.timestamp;
@@ -694,87 +728,84 @@ class Rank {
694
728
  this.contest.problems.forEach((p) => {
695
729
  p.statistics.reset();
696
730
  });
697
- for (const s of this.getSubmissions()) {
731
+ let preSubmissionTimestampToMinute = 0;
732
+ const allSubmissions = this.getSubmissions();
733
+ this.teams.forEach(
734
+ (t) => t.placeChartPoints.push({
735
+ timePoint: 0,
736
+ rank: 1,
737
+ lastSolvedProblem: null
738
+ })
739
+ );
740
+ for (let ix = 0; ix < allSubmissions.length; ix++) {
741
+ const s = allSubmissions[ix];
698
742
  const teamId = s.teamId;
699
743
  const problemId = s.problemId;
700
744
  const team = this.teamsMap.get(teamId);
701
745
  const problem = this.contest.problemsMap.get(problemId);
702
- if (team === void 0 || problem === void 0) {
703
- continue;
704
- }
705
- const problemStatistics = team.problemStatisticsMap.get(problemId);
706
- const submissions = problemStatistics.submissions;
707
- submissions.push(s);
708
- problem.statistics.submittedNum++;
709
- if (problemStatistics.isSolved) {
710
- continue;
711
- }
712
- if (s.isIgnore || s.isNotCalculatedPenaltyStatus()) {
713
- problem.statistics.ignoreNum++;
714
- problemStatistics.ignoreCount++;
715
- continue;
716
- }
717
- problemStatistics.isSubmitted = true;
718
- problemStatistics.lastSubmitTimestamp = s.timestamp;
719
- problemStatistics.totalCount++;
720
- if (s.isAccepted()) {
721
- problemStatistics.isSolved = true;
722
- problemStatistics.solvedTimestamp = s.timestamp;
723
- problem.statistics.acceptedNum++;
724
- problem.statistics.attemptedNum += problemStatistics.failedCount + 1;
725
- if (problem.statistics.firstSolveSubmissions.length === 0 || problem.statistics.firstSolveSubmissions[problem.statistics.firstSolveSubmissions.length - 1].timestamp === s.timestamp) {
726
- problemStatistics.isFirstSolved = true;
727
- problem.statistics.firstSolveSubmissions.push(s);
746
+ (() => {
747
+ if (team === void 0 || problem === void 0) {
748
+ return;
728
749
  }
729
- while (problem.statistics.lastSolveSubmissions.length > 0) {
730
- problem.statistics.lastSolveSubmissions.pop();
750
+ const problemStatistics = team.problemStatisticsMap.get(problemId);
751
+ const submissions = problemStatistics.submissions;
752
+ submissions.push(s);
753
+ team.submissions.push(s);
754
+ problem.statistics.submittedNum++;
755
+ if (problemStatistics.isSolved) {
756
+ return;
731
757
  }
732
- problem.statistics.lastSolveSubmissions.push(s);
733
- team.lastSolvedProblemTimestamp = s.timestamp;
734
- }
735
- if (s.isRejected()) {
736
- problemStatistics.failedCount++;
737
- problem.statistics.rejectedNum++;
738
- }
739
- if (s.isPending()) {
740
- problemStatistics.pendingCount++;
741
- problem.statistics.pendingNum++;
742
- }
743
- }
744
- this.teams.forEach((t) => t.calcSolvedData());
745
- this.teams.sort(Team.compare);
746
- {
747
- let rank = 1;
748
- let preTeam = null;
749
- for (const t of this.teams) {
750
- t.rank = rank++;
751
- if (preTeam !== null) {
752
- if (t.isEqualRank(preTeam)) {
753
- t.rank = preTeam.rank;
758
+ if (s.isIgnore || s.isNotCalculatedPenaltyStatus()) {
759
+ problem.statistics.ignoreNum++;
760
+ problemStatistics.ignoreCount++;
761
+ return;
762
+ }
763
+ problemStatistics.isSubmitted = true;
764
+ problemStatistics.lastSubmitTimestamp = s.timestamp;
765
+ problemStatistics.totalCount++;
766
+ if (s.isAccepted()) {
767
+ problemStatistics.isSolved = true;
768
+ problemStatistics.solvedTimestamp = s.timestamp;
769
+ problem.statistics.acceptedNum++;
770
+ problem.statistics.attemptedNum += problemStatistics.failedCount + 1;
771
+ if (problem.statistics.firstSolveSubmissions.length === 0 || problem.statistics.firstSolveSubmissions[problem.statistics.firstSolveSubmissions.length - 1].timestamp === s.timestamp) {
772
+ problemStatistics.isFirstSolved = true;
773
+ problem.statistics.firstSolveSubmissions.push(s);
774
+ }
775
+ while (problem.statistics.lastSolveSubmissions.length > 0) {
776
+ problem.statistics.lastSolveSubmissions.pop();
754
777
  }
778
+ problem.statistics.lastSolveSubmissions.push(s);
779
+ team.lastSolvedProblem = problem;
780
+ team.lastSolvedProblemTimestamp = s.timestamp;
755
781
  }
756
- preTeam = t;
757
- }
758
- }
759
- if (this.contest.organization) {
760
- let rank = 1;
761
- let preTeam = null;
762
- const se = /* @__PURE__ */ new Set();
763
- for (const t of this.teams) {
764
- const org = t.organization;
765
- if (se.has(org)) {
766
- continue;
782
+ if (s.isRejected()) {
783
+ problemStatistics.failedCount++;
784
+ problem.statistics.rejectedNum++;
767
785
  }
768
- se.add(org);
769
- t.organizationRank = rank++;
770
- if (preTeam !== null) {
771
- if (t.isEqualRank(preTeam)) {
772
- t.organizationRank = preTeam.organizationRank;
773
- }
786
+ if (s.isPending()) {
787
+ problemStatistics.pendingCount++;
788
+ problem.statistics.pendingNum++;
774
789
  }
775
- preTeam = t;
790
+ })();
791
+ if (s.timestampToMinute > preSubmissionTimestampToMinute || ix === allSubmissions.length - 1) {
792
+ this.teams.forEach((t) => t.calcSolvedData());
793
+ this.teams.sort(Team.compare);
794
+ this.buildTeamRank();
795
+ this.teams.forEach(
796
+ (t) => t.placeChartPoints.push(
797
+ {
798
+ timePoint: s.timestampToMinute,
799
+ rank: t.rank,
800
+ lastSolvedProblem: t.lastSolvedProblem
801
+ }
802
+ )
803
+ );
776
804
  }
805
+ preSubmissionTimestampToMinute = s.timestampToMinute;
777
806
  }
807
+ this.teams.forEach((t) => t.postProcessPlaceChartPoints());
808
+ this.buildOrgRank();
778
809
  })();
779
810
  (() => {
780
811
  this.rankStatistics.reset();
@@ -785,6 +816,41 @@ class Rank {
785
816
  })();
786
817
  return this;
787
818
  }
819
+ buildTeamRank() {
820
+ let rank = 1;
821
+ let preTeam = null;
822
+ for (const t of this.teams) {
823
+ t.rank = rank++;
824
+ if (preTeam !== null) {
825
+ if (t.isEqualRank(preTeam)) {
826
+ t.rank = preTeam.rank;
827
+ }
828
+ }
829
+ preTeam = t;
830
+ }
831
+ }
832
+ buildOrgRank() {
833
+ if (!this.contest.organization) {
834
+ return;
835
+ }
836
+ let rank = 1;
837
+ let preTeam = null;
838
+ const se = /* @__PURE__ */ new Set();
839
+ for (const t of this.teams) {
840
+ const org = t.organization;
841
+ if (se.has(org)) {
842
+ continue;
843
+ }
844
+ se.add(org);
845
+ t.organizationRank = rank++;
846
+ if (preTeam !== null) {
847
+ if (t.isEqualRank(preTeam)) {
848
+ t.organizationRank = preTeam.organizationRank;
849
+ }
850
+ }
851
+ preTeam = t;
852
+ }
853
+ }
788
854
  getSubmissions() {
789
855
  if (this.options.enableFilterSubmissionsByTimestamp === false) {
790
856
  return this.submissions;
@@ -885,6 +951,7 @@ exports.dayjs = dayjs__default;
885
951
  exports.Contest = Contest;
886
952
  exports.ContestIndex = ContestIndex;
887
953
  exports.ContestIndexConfig = ContestIndexConfig;
954
+ exports.PlaceChartPointData = PlaceChartPointData;
888
955
  exports.Problem = Problem;
889
956
  exports.ProblemStatistics = ProblemStatistics;
890
957
  exports.Rank = Rank;
package/dist/index.d.ts CHANGED
@@ -14,6 +14,7 @@ declare class Submission {
14
14
  isRejected(): boolean;
15
15
  isPending(): boolean;
16
16
  isNotCalculatedPenaltyStatus(): boolean;
17
+ get timestampToMinute(): number;
17
18
  static compare(lhs: Submission, rhs: Submission): number;
18
19
  }
19
20
  type Submissions = Array<Submission>;
@@ -135,6 +136,12 @@ declare class RankStatistics {
135
136
  reset(): void;
136
137
  }
137
138
 
139
+ declare class PlaceChartPointData {
140
+ timePoint: number;
141
+ rank: number;
142
+ lastSolvedProblem: Problem | null;
143
+ constructor();
144
+ }
138
145
  declare class Team {
139
146
  id: string;
140
147
  name: string;
@@ -148,16 +155,20 @@ declare class Team {
148
155
  organizationRank: number;
149
156
  solvedProblemNum: number;
150
157
  attemptedProblemNum: number;
158
+ lastSolvedProblem: Problem | null;
151
159
  lastSolvedProblemTimestamp: number;
152
160
  penalty: number;
153
161
  problemStatistics: Array<TeamProblemStatistics>;
154
162
  problemStatisticsMap: Map<string, TeamProblemStatistics>;
163
+ submissions: Submissions;
164
+ placeChartPoints: Array<PlaceChartPointData>;
155
165
  constructor();
156
166
  reset(): void;
157
167
  get penaltyToMinute(): number;
158
168
  get dict(): number;
159
169
  calcSolvedData(): void;
160
170
  isEqualRank(otherTeam: Team): boolean;
171
+ postProcessPlaceChartPoints(): void;
161
172
  static compare(lhs: Team, rhs: Team): number;
162
173
  }
163
174
  type Teams = Array<Team>;
@@ -182,6 +193,8 @@ declare class Rank {
182
193
  options: RankOptions;
183
194
  constructor(contest: Contest, teams: Teams, submissions: Submissions);
184
195
  buildRank(): this;
196
+ buildTeamRank(): void;
197
+ buildOrgRank(): void;
185
198
  getSubmissions(): Submissions;
186
199
  }
187
200
 
@@ -209,4 +222,4 @@ declare function isRejected(status: SubmissionStatus): boolean;
209
222
  declare function isPending(status: SubmissionStatus): boolean;
210
223
  declare function isNotCalculatedPenaltyStatus(status: SubmissionStatus): boolean;
211
224
 
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 };
225
+ export { Contest, ContestIndex, ContestIndexConfig, ContestIndexList, PlaceChartPointData, 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
@@ -363,6 +363,13 @@ class RankStatistics {
363
363
  }
364
364
  }
365
365
 
366
+ class PlaceChartPointData {
367
+ constructor() {
368
+ this.timePoint = 0;
369
+ this.rank = 0;
370
+ this.lastSolvedProblem = null;
371
+ }
372
+ }
366
373
  class Team {
367
374
  constructor() {
368
375
  this.id = "";
@@ -374,20 +381,26 @@ class Team {
374
381
  this.organizationRank = -1;
375
382
  this.solvedProblemNum = 0;
376
383
  this.attemptedProblemNum = 0;
384
+ this.lastSolvedProblem = null;
377
385
  this.lastSolvedProblemTimestamp = 0;
378
386
  this.penalty = 0;
379
387
  this.problemStatistics = [];
380
388
  this.problemStatisticsMap = /* @__PURE__ */ new Map();
389
+ this.submissions = [];
390
+ this.placeChartPoints = [];
381
391
  }
382
392
  reset() {
383
393
  this.rank = 0;
384
394
  this.organizationRank = -1;
385
395
  this.solvedProblemNum = 0;
386
396
  this.attemptedProblemNum = 0;
397
+ this.lastSolvedProblem = null;
387
398
  this.lastSolvedProblemTimestamp = 0;
388
399
  this.penalty = 0;
389
400
  this.problemStatistics = [];
390
401
  this.problemStatisticsMap = /* @__PURE__ */ new Map();
402
+ this.submissions = [];
403
+ this.placeChartPoints = [];
391
404
  }
392
405
  get penaltyToMinute() {
393
406
  return Math.floor(this.penalty / 60);
@@ -399,8 +412,8 @@ class Team {
399
412
  }
400
413
  calcSolvedData() {
401
414
  this.solvedProblemNum = 0;
402
- this.penalty = 0;
403
415
  this.attemptedProblemNum = 0;
416
+ this.penalty = 0;
404
417
  for (const p of this.problemStatistics) {
405
418
  if (p.isAccepted) {
406
419
  this.solvedProblemNum++;
@@ -412,6 +425,24 @@ class Team {
412
425
  isEqualRank(otherTeam) {
413
426
  return this.solvedProblemNum === otherTeam.solvedProblemNum && this.penalty === otherTeam.penalty;
414
427
  }
428
+ postProcessPlaceChartPoints() {
429
+ if (this.placeChartPoints.length === 0) {
430
+ return;
431
+ }
432
+ const res = [];
433
+ res.push(this.placeChartPoints[0]);
434
+ for (let i = 1; i < this.placeChartPoints.length - 1; i++) {
435
+ const p = this.placeChartPoints[i];
436
+ const preP = res[res.length - 1];
437
+ if (p.rank !== preP.rank || p.lastSolvedProblem !== preP.lastSolvedProblem) {
438
+ res.push(p);
439
+ }
440
+ }
441
+ if (this.placeChartPoints.length > 1) {
442
+ res.push(this.placeChartPoints[this.placeChartPoints.length - 1]);
443
+ }
444
+ this.placeChartPoints = res;
445
+ }
415
446
  static compare(lhs, rhs) {
416
447
  if (lhs.solvedProblemNum !== rhs.solvedProblemNum) {
417
448
  return rhs.solvedProblemNum - lhs.solvedProblemNum;
@@ -602,6 +633,9 @@ class Submission {
602
633
  isNotCalculatedPenaltyStatus() {
603
634
  return isNotCalculatedPenaltyStatus(this.status);
604
635
  }
636
+ get timestampToMinute() {
637
+ return Math.floor(this.timestamp / 60);
638
+ }
605
639
  static compare(lhs, rhs) {
606
640
  if (lhs.timestamp !== rhs.timestamp) {
607
641
  return lhs.timestamp - rhs.timestamp;
@@ -678,87 +712,84 @@ class Rank {
678
712
  this.contest.problems.forEach((p) => {
679
713
  p.statistics.reset();
680
714
  });
681
- for (const s of this.getSubmissions()) {
715
+ let preSubmissionTimestampToMinute = 0;
716
+ const allSubmissions = this.getSubmissions();
717
+ this.teams.forEach(
718
+ (t) => t.placeChartPoints.push({
719
+ timePoint: 0,
720
+ rank: 1,
721
+ lastSolvedProblem: null
722
+ })
723
+ );
724
+ for (let ix = 0; ix < allSubmissions.length; ix++) {
725
+ const s = allSubmissions[ix];
682
726
  const teamId = s.teamId;
683
727
  const problemId = s.problemId;
684
728
  const team = this.teamsMap.get(teamId);
685
729
  const problem = this.contest.problemsMap.get(problemId);
686
- if (team === void 0 || problem === void 0) {
687
- continue;
688
- }
689
- const problemStatistics = team.problemStatisticsMap.get(problemId);
690
- const submissions = problemStatistics.submissions;
691
- submissions.push(s);
692
- problem.statistics.submittedNum++;
693
- if (problemStatistics.isSolved) {
694
- continue;
695
- }
696
- if (s.isIgnore || s.isNotCalculatedPenaltyStatus()) {
697
- problem.statistics.ignoreNum++;
698
- problemStatistics.ignoreCount++;
699
- continue;
700
- }
701
- problemStatistics.isSubmitted = true;
702
- problemStatistics.lastSubmitTimestamp = s.timestamp;
703
- problemStatistics.totalCount++;
704
- if (s.isAccepted()) {
705
- problemStatistics.isSolved = true;
706
- problemStatistics.solvedTimestamp = s.timestamp;
707
- problem.statistics.acceptedNum++;
708
- problem.statistics.attemptedNum += problemStatistics.failedCount + 1;
709
- if (problem.statistics.firstSolveSubmissions.length === 0 || problem.statistics.firstSolveSubmissions[problem.statistics.firstSolveSubmissions.length - 1].timestamp === s.timestamp) {
710
- problemStatistics.isFirstSolved = true;
711
- problem.statistics.firstSolveSubmissions.push(s);
730
+ (() => {
731
+ if (team === void 0 || problem === void 0) {
732
+ return;
712
733
  }
713
- while (problem.statistics.lastSolveSubmissions.length > 0) {
714
- problem.statistics.lastSolveSubmissions.pop();
734
+ const problemStatistics = team.problemStatisticsMap.get(problemId);
735
+ const submissions = problemStatistics.submissions;
736
+ submissions.push(s);
737
+ team.submissions.push(s);
738
+ problem.statistics.submittedNum++;
739
+ if (problemStatistics.isSolved) {
740
+ return;
715
741
  }
716
- problem.statistics.lastSolveSubmissions.push(s);
717
- team.lastSolvedProblemTimestamp = s.timestamp;
718
- }
719
- if (s.isRejected()) {
720
- problemStatistics.failedCount++;
721
- problem.statistics.rejectedNum++;
722
- }
723
- if (s.isPending()) {
724
- problemStatistics.pendingCount++;
725
- problem.statistics.pendingNum++;
726
- }
727
- }
728
- this.teams.forEach((t) => t.calcSolvedData());
729
- this.teams.sort(Team.compare);
730
- {
731
- let rank = 1;
732
- let preTeam = null;
733
- for (const t of this.teams) {
734
- t.rank = rank++;
735
- if (preTeam !== null) {
736
- if (t.isEqualRank(preTeam)) {
737
- t.rank = preTeam.rank;
742
+ if (s.isIgnore || s.isNotCalculatedPenaltyStatus()) {
743
+ problem.statistics.ignoreNum++;
744
+ problemStatistics.ignoreCount++;
745
+ return;
746
+ }
747
+ problemStatistics.isSubmitted = true;
748
+ problemStatistics.lastSubmitTimestamp = s.timestamp;
749
+ problemStatistics.totalCount++;
750
+ if (s.isAccepted()) {
751
+ problemStatistics.isSolved = true;
752
+ problemStatistics.solvedTimestamp = s.timestamp;
753
+ problem.statistics.acceptedNum++;
754
+ problem.statistics.attemptedNum += problemStatistics.failedCount + 1;
755
+ if (problem.statistics.firstSolveSubmissions.length === 0 || problem.statistics.firstSolveSubmissions[problem.statistics.firstSolveSubmissions.length - 1].timestamp === s.timestamp) {
756
+ problemStatistics.isFirstSolved = true;
757
+ problem.statistics.firstSolveSubmissions.push(s);
758
+ }
759
+ while (problem.statistics.lastSolveSubmissions.length > 0) {
760
+ problem.statistics.lastSolveSubmissions.pop();
738
761
  }
762
+ problem.statistics.lastSolveSubmissions.push(s);
763
+ team.lastSolvedProblem = problem;
764
+ team.lastSolvedProblemTimestamp = s.timestamp;
739
765
  }
740
- preTeam = t;
741
- }
742
- }
743
- if (this.contest.organization) {
744
- let rank = 1;
745
- let preTeam = null;
746
- const se = /* @__PURE__ */ new Set();
747
- for (const t of this.teams) {
748
- const org = t.organization;
749
- if (se.has(org)) {
750
- continue;
766
+ if (s.isRejected()) {
767
+ problemStatistics.failedCount++;
768
+ problem.statistics.rejectedNum++;
751
769
  }
752
- se.add(org);
753
- t.organizationRank = rank++;
754
- if (preTeam !== null) {
755
- if (t.isEqualRank(preTeam)) {
756
- t.organizationRank = preTeam.organizationRank;
757
- }
770
+ if (s.isPending()) {
771
+ problemStatistics.pendingCount++;
772
+ problem.statistics.pendingNum++;
758
773
  }
759
- preTeam = t;
774
+ })();
775
+ if (s.timestampToMinute > preSubmissionTimestampToMinute || ix === allSubmissions.length - 1) {
776
+ this.teams.forEach((t) => t.calcSolvedData());
777
+ this.teams.sort(Team.compare);
778
+ this.buildTeamRank();
779
+ this.teams.forEach(
780
+ (t) => t.placeChartPoints.push(
781
+ {
782
+ timePoint: s.timestampToMinute,
783
+ rank: t.rank,
784
+ lastSolvedProblem: t.lastSolvedProblem
785
+ }
786
+ )
787
+ );
760
788
  }
789
+ preSubmissionTimestampToMinute = s.timestampToMinute;
761
790
  }
791
+ this.teams.forEach((t) => t.postProcessPlaceChartPoints());
792
+ this.buildOrgRank();
762
793
  })();
763
794
  (() => {
764
795
  this.rankStatistics.reset();
@@ -769,6 +800,41 @@ class Rank {
769
800
  })();
770
801
  return this;
771
802
  }
803
+ buildTeamRank() {
804
+ let rank = 1;
805
+ let preTeam = null;
806
+ for (const t of this.teams) {
807
+ t.rank = rank++;
808
+ if (preTeam !== null) {
809
+ if (t.isEqualRank(preTeam)) {
810
+ t.rank = preTeam.rank;
811
+ }
812
+ }
813
+ preTeam = t;
814
+ }
815
+ }
816
+ buildOrgRank() {
817
+ if (!this.contest.organization) {
818
+ return;
819
+ }
820
+ let rank = 1;
821
+ let preTeam = null;
822
+ const se = /* @__PURE__ */ new Set();
823
+ for (const t of this.teams) {
824
+ const org = t.organization;
825
+ if (se.has(org)) {
826
+ continue;
827
+ }
828
+ se.add(org);
829
+ t.organizationRank = rank++;
830
+ if (preTeam !== null) {
831
+ if (t.isEqualRank(preTeam)) {
832
+ t.organizationRank = preTeam.organizationRank;
833
+ }
834
+ }
835
+ preTeam = t;
836
+ }
837
+ }
772
838
  getSubmissions() {
773
839
  if (this.options.enableFilterSubmissionsByTimestamp === false) {
774
840
  return this.submissions;
@@ -865,4 +931,4 @@ class Resolver extends Rank {
865
931
  }
866
932
  }
867
933
 
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 };
934
+ export { Contest, ContestIndex, ContestIndexConfig, PlaceChartPointData, 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",
3
+ "version": "0.5.0",
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.3"
45
+ "@xcpcio/types": "0.5.0"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@babel/types": "^7.22.4",
package/src/rank.ts CHANGED
@@ -77,113 +77,108 @@ export class Rank {
77
77
  p.statistics.reset();
78
78
  });
79
79
 
80
- for (const s of this.getSubmissions()) {
80
+ let preSubmissionTimestampToMinute = 0;
81
+ const allSubmissions = this.getSubmissions();
82
+
83
+ this.teams.forEach(t =>
84
+ t.placeChartPoints.push({
85
+ timePoint: 0,
86
+ rank: 1,
87
+ lastSolvedProblem: null,
88
+ }),
89
+ );
90
+
91
+ for (let ix = 0; ix < allSubmissions.length; ix++) {
92
+ const s = allSubmissions[ix];
93
+
81
94
  const teamId = s.teamId;
82
95
  const problemId = s.problemId;
83
96
  const team = this.teamsMap.get(teamId);
84
97
  const problem = this.contest.problemsMap.get(problemId);
85
98
 
86
- if (team === undefined || problem === undefined) {
87
- continue;
88
- }
89
-
90
- const problemStatistics = team.problemStatisticsMap.get(problemId) as TeamProblemStatistics;
91
- const submissions = problemStatistics.submissions;
92
-
93
- submissions.push(s);
94
- problem.statistics.submittedNum++;
95
-
96
- if (problemStatistics.isSolved) {
97
- continue;
98
- }
99
-
100
- if (s.isIgnore || s.isNotCalculatedPenaltyStatus()) {
101
- problem.statistics.ignoreNum++;
102
- problemStatistics.ignoreCount++;
103
- continue;
104
- }
105
-
106
- problemStatistics.isSubmitted = true;
107
- problemStatistics.lastSubmitTimestamp = s.timestamp;
108
- problemStatistics.totalCount++;
99
+ (() => {
100
+ if (team === undefined || problem === undefined) {
101
+ return;
102
+ }
109
103
 
110
- if (s.isAccepted()) {
111
- problemStatistics.isSolved = true;
112
- problemStatistics.solvedTimestamp = s.timestamp;
104
+ const problemStatistics = team.problemStatisticsMap.get(problemId) as TeamProblemStatistics;
105
+ const submissions = problemStatistics.submissions;
113
106
 
114
- problem.statistics.acceptedNum++;
115
- problem.statistics.attemptedNum += problemStatistics.failedCount + 1;
107
+ submissions.push(s);
108
+ team.submissions.push(s);
109
+ problem.statistics.submittedNum++;
116
110
 
117
- if (
118
- problem.statistics.firstSolveSubmissions.length === 0
119
- || problem.statistics.firstSolveSubmissions[problem.statistics.firstSolveSubmissions.length - 1].timestamp === s.timestamp
120
- ) {
121
- problemStatistics.isFirstSolved = true;
122
- problem.statistics.firstSolveSubmissions.push(s);
111
+ if (problemStatistics.isSolved) {
112
+ return;
123
113
  }
124
114
 
125
- while (problem.statistics.lastSolveSubmissions.length > 0) {
126
- problem.statistics.lastSolveSubmissions.pop();
115
+ if (s.isIgnore || s.isNotCalculatedPenaltyStatus()) {
116
+ problem.statistics.ignoreNum++;
117
+ problemStatistics.ignoreCount++;
118
+ return;
127
119
  }
128
120
 
129
- problem.statistics.lastSolveSubmissions.push(s);
130
- team.lastSolvedProblemTimestamp = s.timestamp;
131
- }
132
-
133
- if (s.isRejected()) {
134
- problemStatistics.failedCount++;
135
- problem.statistics.rejectedNum++;
136
- }
137
-
138
- if (s.isPending()) {
139
- problemStatistics.pendingCount++;
140
- problem.statistics.pendingNum++;
141
- }
142
- }
121
+ problemStatistics.isSubmitted = true;
122
+ problemStatistics.lastSubmitTimestamp = s.timestamp;
123
+ problemStatistics.totalCount++;
143
124
 
144
- this.teams.forEach(t => t.calcSolvedData());
145
- this.teams.sort(Team.compare);
125
+ if (s.isAccepted()) {
126
+ problemStatistics.isSolved = true;
127
+ problemStatistics.solvedTimestamp = s.timestamp;
146
128
 
147
- {
148
- let rank = 1;
149
- let preTeam = null;
150
- for (const t of this.teams) {
151
- t.rank = rank++;
129
+ problem.statistics.acceptedNum++;
130
+ problem.statistics.attemptedNum += problemStatistics.failedCount + 1;
152
131
 
153
- if (preTeam !== null) {
154
- if (t.isEqualRank(preTeam)) {
155
- t.rank = preTeam.rank;
132
+ if (
133
+ problem.statistics.firstSolveSubmissions.length === 0
134
+ || problem.statistics.firstSolveSubmissions[problem.statistics.firstSolveSubmissions.length - 1].timestamp === s.timestamp
135
+ ) {
136
+ problemStatistics.isFirstSolved = true;
137
+ problem.statistics.firstSolveSubmissions.push(s);
156
138
  }
157
- }
158
-
159
- preTeam = t;
160
- }
161
- }
162
139
 
163
- if (this.contest.organization) {
164
- let rank = 1;
165
- let preTeam = null;
140
+ while (problem.statistics.lastSolveSubmissions.length > 0) {
141
+ problem.statistics.lastSolveSubmissions.pop();
142
+ }
166
143
 
167
- const se = new Set<string>();
144
+ problem.statistics.lastSolveSubmissions.push(s);
168
145
 
169
- for (const t of this.teams) {
170
- const org = t.organization;
171
- if (se.has(org)) {
172
- continue;
146
+ team.lastSolvedProblem = problem;
147
+ team.lastSolvedProblemTimestamp = s.timestamp;
173
148
  }
174
149
 
175
- se.add(org);
176
- t.organizationRank = rank++;
177
-
178
- if (preTeam !== null) {
179
- if (t.isEqualRank(preTeam)) {
180
- t.organizationRank = preTeam.organizationRank;
181
- }
150
+ if (s.isRejected()) {
151
+ problemStatistics.failedCount++;
152
+ problem.statistics.rejectedNum++;
182
153
  }
183
154
 
184
- preTeam = t;
155
+ if (s.isPending()) {
156
+ problemStatistics.pendingCount++;
157
+ problem.statistics.pendingNum++;
158
+ }
159
+ })();
160
+
161
+ if (s.timestampToMinute > preSubmissionTimestampToMinute || ix === allSubmissions.length - 1) {
162
+ this.teams.forEach(t => t.calcSolvedData());
163
+ this.teams.sort(Team.compare);
164
+ this.buildTeamRank();
165
+
166
+ this.teams.forEach(t =>
167
+ t.placeChartPoints.push(
168
+ {
169
+ timePoint: s.timestampToMinute,
170
+ rank: t.rank,
171
+ lastSolvedProblem: t.lastSolvedProblem,
172
+ },
173
+ ),
174
+ );
185
175
  }
176
+
177
+ preSubmissionTimestampToMinute = s.timestampToMinute;
186
178
  }
179
+
180
+ this.teams.forEach(t => t.postProcessPlaceChartPoints());
181
+ this.buildOrgRank();
187
182
  })();
188
183
 
189
184
  (() => {
@@ -198,6 +193,51 @@ export class Rank {
198
193
  return this;
199
194
  }
200
195
 
196
+ buildTeamRank() {
197
+ let rank = 1;
198
+ let preTeam = null;
199
+ for (const t of this.teams) {
200
+ t.rank = rank++;
201
+
202
+ if (preTeam !== null) {
203
+ if (t.isEqualRank(preTeam)) {
204
+ t.rank = preTeam.rank;
205
+ }
206
+ }
207
+
208
+ preTeam = t;
209
+ }
210
+ }
211
+
212
+ buildOrgRank() {
213
+ if (!this.contest.organization) {
214
+ return;
215
+ }
216
+
217
+ let rank = 1;
218
+ let preTeam = null;
219
+
220
+ const se = new Set<string>();
221
+
222
+ for (const t of this.teams) {
223
+ const org = t.organization;
224
+ if (se.has(org)) {
225
+ continue;
226
+ }
227
+
228
+ se.add(org);
229
+ t.organizationRank = rank++;
230
+
231
+ if (preTeam !== null) {
232
+ if (t.isEqualRank(preTeam)) {
233
+ t.organizationRank = preTeam.organizationRank;
234
+ }
235
+ }
236
+
237
+ preTeam = t;
238
+ }
239
+ }
240
+
201
241
  getSubmissions() {
202
242
  if (this.options.enableFilterSubmissionsByTimestamp === false) {
203
243
  return this.submissions;
package/src/submission.ts CHANGED
@@ -41,6 +41,10 @@ export class Submission {
41
41
  return isNotCalculatedPenaltyStatus(this.status);
42
42
  }
43
43
 
44
+ get timestampToMinute() {
45
+ return Math.floor(this.timestamp / 60);
46
+ }
47
+
44
48
  static compare(lhs: Submission, rhs: Submission): number {
45
49
  if (lhs.timestamp !== rhs.timestamp) {
46
50
  return lhs.timestamp - rhs.timestamp;
package/src/team.ts CHANGED
@@ -1,7 +1,20 @@
1
1
  import type { Team as ITeam, Teams as ITeams, Image } from "@xcpcio/types";
2
2
 
3
- import type { TeamProblemStatistics } from "./problem";
3
+ import type { Problem, TeamProblemStatistics } from "./problem";
4
4
  import { calcDict } from "./utils";
5
+ import type { Submissions } from "./submission";
6
+
7
+ export class PlaceChartPointData {
8
+ timePoint: number;
9
+ rank: number;
10
+ lastSolvedProblem: Problem | null;
11
+
12
+ constructor() {
13
+ this.timePoint = 0;
14
+ this.rank = 0;
15
+ this.lastSolvedProblem = null;
16
+ }
17
+ }
5
18
 
6
19
  export class Team {
7
20
  id: string;
@@ -21,6 +34,8 @@ export class Team {
21
34
 
22
35
  solvedProblemNum: number;
23
36
  attemptedProblemNum: number;
37
+
38
+ lastSolvedProblem: Problem | null;
24
39
  lastSolvedProblemTimestamp: number;
25
40
 
26
41
  penalty: number;
@@ -28,6 +43,10 @@ export class Team {
28
43
  problemStatistics: Array<TeamProblemStatistics>;
29
44
  problemStatisticsMap: Map<string, TeamProblemStatistics>;
30
45
 
46
+ submissions: Submissions;
47
+
48
+ placeChartPoints: Array<PlaceChartPointData>;
49
+
31
50
  constructor() {
32
51
  this.id = "";
33
52
  this.name = "";
@@ -42,12 +61,18 @@ export class Team {
42
61
 
43
62
  this.solvedProblemNum = 0;
44
63
  this.attemptedProblemNum = 0;
64
+
65
+ this.lastSolvedProblem = null;
45
66
  this.lastSolvedProblemTimestamp = 0;
46
67
 
47
68
  this.penalty = 0;
48
69
 
49
70
  this.problemStatistics = [];
50
71
  this.problemStatisticsMap = new Map<string, TeamProblemStatistics>();
72
+
73
+ this.submissions = [];
74
+
75
+ this.placeChartPoints = [];
51
76
  }
52
77
 
53
78
  reset() {
@@ -56,12 +81,18 @@ export class Team {
56
81
 
57
82
  this.solvedProblemNum = 0;
58
83
  this.attemptedProblemNum = 0;
84
+
85
+ this.lastSolvedProblem = null;
59
86
  this.lastSolvedProblemTimestamp = 0;
60
87
 
61
88
  this.penalty = 0;
62
89
 
63
90
  this.problemStatistics = [];
64
91
  this.problemStatisticsMap = new Map<string, TeamProblemStatistics>();
92
+
93
+ this.submissions = [];
94
+
95
+ this.placeChartPoints = [];
65
96
  }
66
97
 
67
98
  get penaltyToMinute() {
@@ -77,9 +108,10 @@ export class Team {
77
108
 
78
109
  calcSolvedData() {
79
110
  this.solvedProblemNum = 0;
80
- this.penalty = 0;
81
111
  this.attemptedProblemNum = 0;
82
112
 
113
+ this.penalty = 0;
114
+
83
115
  for (const p of this.problemStatistics) {
84
116
  if (p.isAccepted) {
85
117
  this.solvedProblemNum++;
@@ -94,6 +126,30 @@ export class Team {
94
126
  return this.solvedProblemNum === otherTeam.solvedProblemNum && this.penalty === otherTeam.penalty;
95
127
  }
96
128
 
129
+ postProcessPlaceChartPoints() {
130
+ if (this.placeChartPoints.length === 0) {
131
+ return;
132
+ }
133
+
134
+ const res = [];
135
+ res.push(this.placeChartPoints[0]);
136
+
137
+ for (let i = 1; i < this.placeChartPoints.length - 1; i++) {
138
+ const p = this.placeChartPoints[i];
139
+ const preP = res[res.length - 1];
140
+
141
+ if (p.rank !== preP.rank || p.lastSolvedProblem !== preP.lastSolvedProblem) {
142
+ res.push(p);
143
+ }
144
+ }
145
+
146
+ if (this.placeChartPoints.length > 1) {
147
+ res.push(this.placeChartPoints[this.placeChartPoints.length - 1]);
148
+ }
149
+
150
+ this.placeChartPoints = res;
151
+ }
152
+
97
153
  static compare(lhs: Team, rhs: Team): number {
98
154
  if (lhs.solvedProblemNum !== rhs.solvedProblemNum) {
99
155
  return rhs.solvedProblemNum - lhs.solvedProblemNum;