tods-competition-factory 1.6.22 → 1.6.23

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.
@@ -8053,25 +8053,25 @@ function targetByRoundOutcome({
8053
8053
  matchUp,
8054
8054
  targetLinks: { loserTargetLink, winnerTargetLink, byeTargetLink },
8055
8055
  targetMatchUps: {
8056
- byeMatchUp,
8057
- loserMatchUp,
8058
- winnerMatchUp,
8059
- byeTargetDrawPosition,
8060
- loserTargetDrawPosition,
8061
- winnerTargetDrawPosition,
8062
- byeMatchUpDrawPositionIndex,
8056
+ winnerMatchUpDrawPositionIndex,
8063
8057
  loserMatchUpDrawPositionIndex,
8064
- winnerMatchUpDrawPositionIndex
8058
+ byeMatchUpDrawPositionIndex,
8059
+ winnerTargetDrawPosition,
8060
+ loserTargetDrawPosition,
8061
+ byeTargetDrawPosition,
8062
+ winnerMatchUp,
8063
+ loserMatchUp,
8064
+ byeMatchUp
8065
8065
  },
8066
8066
  targetMatchUpIds: !!(winnerMatchUpId || loserMatchUpId)
8067
8067
  });
8068
8068
  }
8069
8069
  function targetByWinRatio({ matchUp }) {
8070
8070
  return {
8071
- matchUp,
8072
8071
  targetLinks: { loserTargetLink: void 0, winnerTargetLink: void 0 },
8073
8072
  // returned for testing
8074
- targetMatchUps: { loserMatchUp: void 0, winnerMatchUp: void 0 }
8073
+ targetMatchUps: { loserMatchUp: void 0, winnerMatchUp: void 0 },
8074
+ matchUp
8075
8075
  };
8076
8076
  }
8077
8077
 
@@ -17488,6 +17488,36 @@ function generateAndPopulatePlayoffStructures(params) {
17488
17488
  if (result.error)
17489
17489
  console.log(result.error);
17490
17490
  });
17491
+ const byeMatchUps = inContextDrawMatchUps?.filter(
17492
+ (matchUp) => matchUp.matchUpStatus === BYE && matchUp.structureId === sourceStructureId
17493
+ );
17494
+ byeMatchUps?.forEach((matchUp) => {
17495
+ const { matchUpId } = matchUp;
17496
+ const targetData = positionTargets({
17497
+ inContextDrawMatchUps,
17498
+ drawDefinition,
17499
+ matchUpId
17500
+ });
17501
+ const {
17502
+ targetLinks: { loserTargetLink },
17503
+ targetMatchUps: {
17504
+ loserMatchUpDrawPositionIndex,
17505
+ // only present when positionTargets found without loserMatchUpId
17506
+ loserMatchUp
17507
+ }
17508
+ } = targetData;
17509
+ const targetStructureId = loserTargetLink.target.structureId;
17510
+ const targetDrawPosition = loserMatchUp.drawPositions[loserMatchUpDrawPositionIndex];
17511
+ const result = assignDrawPositionBye({
17512
+ drawPosition: targetDrawPosition,
17513
+ structureId: targetStructureId,
17514
+ tournamentRecord,
17515
+ drawDefinition,
17516
+ event
17517
+ });
17518
+ if (result.error)
17519
+ console.log(result.error);
17520
+ });
17491
17521
  const matchUpModifications = [];
17492
17522
  const goesToMap = addGoesTo({
17493
17523
  inContextDrawMatchUps,
@@ -19545,188 +19575,600 @@ function generateVoluntaryConsolation(params) {
19545
19575
  return generateVoluntaryConsolation$1({ participants: tournamentParticipants, ...params });
19546
19576
  }
19547
19577
 
19548
- function generateQualifyingLink({
19549
- targetEntryRound = 1,
19550
- finishingPositions,
19551
- sourceRoundNumber,
19552
- sourceStructureId,
19553
- targetStructureId,
19554
- linkType = LinkTypeEnum.Winner
19578
+ function generateCandidate({
19579
+ maxIterations = 5e3,
19580
+ // cap the processing intensity of the candidate generator
19581
+ valueSortedPairings,
19582
+ // pairings sorted by value from low to high
19583
+ pairingValues,
19584
+ deltaObjects
19555
19585
  }) {
19556
- if (!sourceStructureId || !targetStructureId)
19557
- return { error: MISSING_STRUCTURE_ID };
19558
- const link = definedAttributes({
19559
- linkType,
19560
- source: {
19561
- roundNumber: sourceRoundNumber,
19562
- structureId: sourceStructureId,
19563
- finishingPositions
19564
- },
19565
- target: {
19566
- feedProfile: DRAW,
19567
- // positions are not automatically placed
19568
- roundNumber: targetEntryRound,
19569
- structureId: targetStructureId
19570
- }
19586
+ const rankedMatchUpValues = Object.assign(
19587
+ {},
19588
+ ...valueSortedPairings.map((rm) => ({ [rm.pairing]: rm.value }))
19589
+ );
19590
+ let candidate = roundCandidate({
19591
+ rankedMatchUpValues,
19592
+ valueSortedPairings,
19593
+ deltaObjects
19571
19594
  });
19572
- return { link };
19573
- }
19574
-
19575
- function generateQualifyingStructures({
19576
- qualifyingProfiles,
19577
- appliedPolicies,
19578
- idPrefix,
19579
- isMock,
19580
- uuids
19581
- }) {
19582
- const stack = "generateQualifyingSTructures";
19583
- const qualifyingDetails = [];
19584
- const structures = [];
19585
- const links = [];
19586
- const sequenceSort = (a, b) => a.stageSequence - b.stageSequence;
19587
- const roundTargetSort = (a, b) => a.roundTarget - b.roundTarget;
19588
- let qualifyingDrawPositionsCount = 0, totalQualifiersCount = 0, finishingPositions, roundTarget = 1;
19589
- for (const roundTargetProfile of qualifyingProfiles.sort(roundTargetSort)) {
19590
- const structureProfiles = roundTargetProfile.structureProfiles || [];
19591
- roundTarget = roundTargetProfile.roundTarget || roundTarget;
19592
- let stageSequence = 1, targetRoundQualifiersCount = 0, finalQualifyingRoundNumber, finalQualifyingStructureId, linkType;
19593
- for (const structureProfile of (structureProfiles || []).sort(
19594
- sequenceSort
19595
- )) {
19596
- let drawSize = structureProfile.drawSize || coerceEven(structureProfile.participantsCount);
19597
- const {
19598
- qualifyingRoundNumber,
19599
- qualifyingPositions,
19600
- structureOptions,
19601
- matchUpFormat,
19602
- structureName,
19603
- structureId,
19604
- drawType
19605
- } = structureProfile;
19606
- const matchUpType = structureProfile.matchUpType;
19607
- let roundLimit, structure, matchUps;
19608
- if (!isConvertableInteger(drawSize)) {
19609
- return decorateResult({
19610
- result: { error: MISSING_DRAW_SIZE },
19611
- stack
19612
- });
19613
- }
19614
- const roundTargetName = qualifyingProfiles.length > 1 ? `${roundTarget}-` : "";
19615
- const stageSequenceName = structureProfiles.length > 1 || roundTargetName ? stageSequence : "";
19616
- const qualifyingStructureName = structureName || (roundTargetName || stageSequenceName ? `${QUALIFYING} ${roundTargetName}${stageSequenceName}` : QUALIFYING);
19617
- if (drawType === ROUND_ROBIN) {
19618
- const {
19619
- structures: structures2,
19620
- groupCount,
19621
- maxRoundNumber
19622
- /*, groupSize*/
19623
- } = generateRoundRobin({
19624
- structureName: structureProfile.structureName || qualifyingStructureName,
19625
- structureId: structureId || uuids?.pop(),
19626
- // qualifyingPositions,
19627
- stage: QUALIFYING,
19628
- structureOptions,
19629
- appliedPolicies,
19630
- stageSequence,
19631
- matchUpType,
19632
- roundTarget,
19633
- drawSize,
19634
- idPrefix,
19635
- isMock,
19636
- uuids
19637
- });
19638
- targetRoundQualifiersCount = groupCount;
19639
- roundLimit = maxRoundNumber;
19640
- structure = structures2[0];
19641
- finishingPositions = [1];
19642
- } else {
19643
- ({ drawSize, matchUps, roundLimit } = treeMatchUps({
19644
- qualifyingRoundNumber,
19645
- qualifyingPositions,
19646
- matchUpType,
19647
- drawSize,
19648
- idPrefix,
19649
- isMock,
19650
- uuids
19651
- }));
19652
- structure = structureTemplate({
19653
- structureName: structureProfile.structureName || qualifyingStructureName,
19654
- structureId: structureId || uuids?.pop(),
19655
- qualifyingRoundNumber: roundLimit,
19656
- stage: QUALIFYING,
19657
- matchUpFormat,
19658
- stageSequence,
19659
- matchUpType,
19660
- roundLimit,
19661
- // redundant
19662
- matchUps
19663
- });
19664
- if (roundTarget) {
19665
- addExtension({
19666
- element: structure,
19667
- extension: { name: ROUND_TARGET, value: roundTarget }
19668
- });
19669
- }
19670
- targetRoundQualifiersCount = matchUps?.filter((matchUp) => matchUp.roundNumber === roundLimit)?.length || 0;
19671
- }
19672
- if (stageSequence > 1) {
19673
- const { link } = generateQualifyingLink({
19674
- sourceStructureId: finalQualifyingStructureId,
19675
- sourceRoundNumber: finalQualifyingRoundNumber,
19676
- targetStructureId: structure.structureId,
19677
- finishingPositions: linkType === POSITION ? [1] : void 0,
19678
- linkType
19595
+ let deltaCandidate = candidate;
19596
+ const actors = Object.keys(pairingValues);
19597
+ let candidatesCount = 0;
19598
+ let iterations;
19599
+ let opponentCount = actors.length;
19600
+ do {
19601
+ opponentCount -= 1;
19602
+ iterations = actors.length * opponentCount * valueSortedPairings.length / 2;
19603
+ } while (iterations > maxIterations && opponentCount > 2);
19604
+ const stipulatedPairs = [];
19605
+ actors.forEach((actor) => {
19606
+ const participantIdPairings = pairingValues[actor];
19607
+ participantIdPairings.slice(0, opponentCount).forEach((pairing) => {
19608
+ const stipulatedPair = pairingHash(actor, pairing.opponent);
19609
+ if (!stipulatedPairs.includes(stipulatedPair)) {
19610
+ const proposed = roundCandidate({
19611
+ // each roundCandidate starts with stipulated pairings
19612
+ stipulated: [[actor, pairing.opponent]],
19613
+ rankedMatchUpValues,
19614
+ valueSortedPairings,
19615
+ deltaObjects
19679
19616
  });
19680
- links.push(link);
19681
- qualifyingDrawPositionsCount += (drawSize || 0) - targetRoundQualifiersCount;
19682
- } else {
19683
- qualifyingDrawPositionsCount += drawSize || 0;
19617
+ if (proposed.maxDelta < deltaCandidate.maxDelta)
19618
+ deltaCandidate = proposed;
19619
+ if (proposed.value < candidate.value)
19620
+ candidate = proposed;
19621
+ stipulatedPairs.push(stipulatedPair);
19622
+ candidatesCount += 1;
19684
19623
  }
19685
- linkType = drawType === ROUND_ROBIN ? POSITION : WINNER;
19686
- finalQualifyingStructureId = structure.structureId;
19687
- finalQualifyingRoundNumber = roundLimit;
19688
- structures.push(structure);
19689
- stageSequence += 1;
19690
- }
19691
- totalQualifiersCount += targetRoundQualifiersCount;
19692
- qualifyingDetails.push({
19693
- qualifiersCount: targetRoundQualifiersCount,
19694
- finalQualifyingRoundNumber,
19695
- finalQualifyingStructureId,
19696
- finishingPositions,
19697
- roundTarget,
19698
- linkType
19699
19624
  });
19700
- targetRoundQualifiersCount = 0;
19701
- roundTarget += 1;
19702
- }
19703
- return {
19704
- qualifiersCount: totalQualifiersCount,
19705
- qualifyingDrawPositionsCount,
19706
- qualifyingDetails,
19707
- structures,
19708
- ...SUCCESS,
19709
- links
19710
- };
19625
+ });
19626
+ return { candidate, deltaCandidate, candidatesCount, iterations };
19711
19627
  }
19712
-
19713
- function generateDrawStructuresAndLinks(params) {
19714
- const {
19715
- enforceMinimumDrawSize = true,
19716
- overwriteExisting,
19717
- drawTypeCoercion,
19718
- // coerce to SINGLE_ELIMINATION for drawSize: 2
19719
- appliedPolicies,
19720
- staggeredEntry,
19721
- // optional - specifies main structure FEED_IN for drawTypes CURTIS_CONSOLATION, FEED_IN_CHAMPIONSHIPS, FMLC
19722
- drawDefinition,
19723
- tieFormat,
19724
- drawSize,
19725
- isMock,
19726
- uuids
19727
- } = params || {};
19728
- const stack = "generateDrawStructuresAndLinks";
19729
- let drawType = params.drawType ?? SINGLE_ELIMINATION;
19628
+ function roundCandidate({
19629
+ rankedMatchUpValues,
19630
+ valueSortedPairings,
19631
+ stipulated = [],
19632
+ deltaObjects
19633
+ }) {
19634
+ const roundPlayers = [].concat(...stipulated);
19635
+ const participantIdPairings = [];
19636
+ let candidateValue = 0;
19637
+ stipulated.filter(Boolean).forEach((participantIds) => {
19638
+ const [p1, p2] = participantIds;
19639
+ const pairing = pairingHash(p1, p2);
19640
+ const value = rankedMatchUpValues[pairing];
19641
+ participantIdPairings.push({ participantIds, value });
19642
+ candidateValue += rankedMatchUpValues[pairing];
19643
+ });
19644
+ valueSortedPairings.forEach((rankedPairing) => {
19645
+ const participantIds = rankedPairing.pairing.split("|");
19646
+ const opponentExists = participantIds.reduce(
19647
+ (p, c) => roundPlayers.includes(c) || p,
19648
+ false
19649
+ );
19650
+ if (!opponentExists) {
19651
+ roundPlayers.push(...participantIds);
19652
+ const value = rankedPairing.value;
19653
+ candidateValue += value;
19654
+ participantIdPairings.push({ participantIds, value });
19655
+ }
19656
+ });
19657
+ participantIdPairings.sort((a, b) => a.value - b.value);
19658
+ const maxDelta = participantIdPairings.reduce((p, c) => {
19659
+ const [p1, p2] = c.participantIds;
19660
+ const hash = pairingHash(p1, p2);
19661
+ const delta = deltaObjects[hash];
19662
+ return delta > p ? delta : p;
19663
+ }, 0);
19664
+ return { value: candidateValue, participantIdPairings, maxDelta };
19665
+ }
19666
+ function pairingHash(id1, id2) {
19667
+ return [id1, id2].sort().join("|");
19668
+ }
19669
+
19670
+ function generateAdHocMatchUps({
19671
+ participantIdPairings,
19672
+ addToStructure = true,
19673
+ tournamentRecord,
19674
+ matchUpIds = [],
19675
+ drawDefinition,
19676
+ matchUpsCount,
19677
+ roundNumber,
19678
+ structureId,
19679
+ newRound
19680
+ }) {
19681
+ if (typeof drawDefinition !== "object")
19682
+ return { error: MISSING_DRAW_DEFINITION };
19683
+ if (!structureId && drawDefinition.structures?.length === 1)
19684
+ structureId = drawDefinition.structures?.[0]?.structureId;
19685
+ if (typeof structureId !== "string")
19686
+ return { error: MISSING_STRUCTURE_ID };
19687
+ if (participantIdPairings && !Array.isArray(participantIdPairings) || matchUpsCount && !isConvertableInteger(matchUpsCount) || matchUpIds && !Array.isArray(matchUpIds) || !participantIdPairings && !matchUpsCount) {
19688
+ return { error: INVALID_VALUES, info: "matchUpsCount or pairings error" };
19689
+ }
19690
+ const structure = drawDefinition?.structures?.find(
19691
+ (structure2) => structure2.structureId === structureId
19692
+ );
19693
+ let structureHasRoundPositions;
19694
+ const existingMatchUps = structure?.matchUps;
19695
+ const lastRoundNumber = existingMatchUps?.reduce(
19696
+ (roundNumber2, matchUp) => {
19697
+ if (matchUp.roundPosition)
19698
+ structureHasRoundPositions = true;
19699
+ return (matchUp?.roundNumber || 0) > roundNumber2 ? matchUp.roundNumber : roundNumber2;
19700
+ },
19701
+ 0
19702
+ );
19703
+ if (structure?.structures || structureHasRoundPositions || structure?.finishingPosition === ROUND_OUTCOME) {
19704
+ return { error: INVALID_STRUCTURE };
19705
+ }
19706
+ if (roundNumber && roundNumber - 1 > (lastRoundNumber || 0))
19707
+ return { error: INVALID_VALUES, info: "roundNumber error" };
19708
+ const nextRoundNumber = roundNumber ?? (newRound ? (lastRoundNumber ?? 0) + 1 : lastRoundNumber ?? 1);
19709
+ participantIdPairings = participantIdPairings ?? generateRange(0, matchUpsCount).map(() => ({
19710
+ participantIds: [void 0, void 0]
19711
+ }));
19712
+ const matchUps = participantIdPairings?.map((pairing) => {
19713
+ const idStack = pairing?.participantIds ?? [void 0, void 0];
19714
+ idStack.push(...[void 0, void 0]);
19715
+ const participantIds = idStack.slice(0, 2);
19716
+ const sides = participantIds.map(
19717
+ (participantId, i) => definedAttributes({
19718
+ sideNumber: i + 1,
19719
+ participantId
19720
+ })
19721
+ );
19722
+ return {
19723
+ matchUpStatus: MatchUpStatusEnum.ToBePlayed,
19724
+ matchUpId: matchUpIds.pop() ?? UUID(),
19725
+ roundNumber: nextRoundNumber,
19726
+ sides
19727
+ };
19728
+ });
19729
+ if (addToStructure) {
19730
+ const result = addAdHocMatchUps({
19731
+ tournamentRecord,
19732
+ drawDefinition,
19733
+ structureId,
19734
+ matchUps
19735
+ });
19736
+ if (result.error)
19737
+ return result;
19738
+ }
19739
+ return { matchUpsCount: matchUps.length, matchUps, ...SUCCESS };
19740
+ }
19741
+ function addAdHocMatchUps({
19742
+ tournamentRecord,
19743
+ drawDefinition,
19744
+ structureId,
19745
+ matchUps
19746
+ }) {
19747
+ if (typeof drawDefinition !== "object")
19748
+ return { error: MISSING_DRAW_DEFINITION };
19749
+ if (!structureId && drawDefinition.structures?.length === 1)
19750
+ structureId = drawDefinition.structures?.[0]?.structureId;
19751
+ if (typeof structureId !== "string")
19752
+ return { error: MISSING_STRUCTURE_ID };
19753
+ if (!validMatchUps(matchUps))
19754
+ return { error: INVALID_VALUES, info: mustBeAnArray("matchUps") };
19755
+ const structure = drawDefinition.structures?.find(
19756
+ (structure2) => structure2.structureId === structureId
19757
+ );
19758
+ if (!structure)
19759
+ return { error: STRUCTURE_NOT_FOUND };
19760
+ const existingMatchUps = structure?.matchUps;
19761
+ const structureHasRoundPositions = existingMatchUps.find(
19762
+ (matchUp) => !!matchUp.roundPosition
19763
+ );
19764
+ if (structure.structures || structureHasRoundPositions || structure.finishingPosition === ROUND_OUTCOME) {
19765
+ return { error: INVALID_STRUCTURE };
19766
+ }
19767
+ const existingMatchUpIds = allTournamentMatchUps({
19768
+ tournamentRecord,
19769
+ inContext: false
19770
+ })?.matchUps?.map(getMatchUpId) ?? [];
19771
+ const newMatchUpIds = matchUps.map(getMatchUpId);
19772
+ if (overlap(existingMatchUpIds, newMatchUpIds)) {
19773
+ return {
19774
+ error: EXISTING_MATCHUP_ID,
19775
+ info: "One or more matchUpIds already present in tournamentRecord"
19776
+ };
19777
+ }
19778
+ structure.matchUps.push(...matchUps);
19779
+ addMatchUpsNotice({
19780
+ tournamentId: tournamentRecord?.tournamentId,
19781
+ drawDefinition,
19782
+ matchUps
19783
+ });
19784
+ modifyDrawNotice({ drawDefinition, structureIds: [structureId] });
19785
+ return { ...SUCCESS };
19786
+ }
19787
+
19788
+ const ENCOUNTER_VALUE = 50;
19789
+ const SAME_TEAM_VALUE = 60;
19790
+ const DEFAULT_RATING = 0;
19791
+ const MAX_ITERATIONS = 5e3;
19792
+ function generateDrawMaticRound({
19793
+ maxIterations = MAX_ITERATIONS,
19794
+ generateMatchUps = true,
19795
+ tournamentParticipants,
19796
+ tournamentRecord,
19797
+ participantIds,
19798
+ addToStructure,
19799
+ drawDefinition,
19800
+ adHocRatings,
19801
+ structureId,
19802
+ matchUpIds,
19803
+ eventType,
19804
+ structure
19805
+ }) {
19806
+ if (!drawDefinition)
19807
+ return { error: MISSING_DRAW_DEFINITION };
19808
+ if (!structure && !structureId)
19809
+ return { error: STRUCTURE_NOT_FOUND };
19810
+ if (!structure) {
19811
+ structure = findStructure({ drawDefinition, structureId }).structure;
19812
+ }
19813
+ if (!isObject(structure))
19814
+ return { error: MISSING_STRUCTURE };
19815
+ if (!participantIds?.length) {
19816
+ return { error: MISSING_PARTICIPANT_IDS };
19817
+ }
19818
+ const { encounters } = getEncounters({ matchUps: structure?.matchUps ?? [] });
19819
+ const valueObjects = {};
19820
+ for (const pairing of encounters) {
19821
+ if (!valueObjects[pairing])
19822
+ valueObjects[pairing] = 0;
19823
+ valueObjects[pairing] += ENCOUNTER_VALUE;
19824
+ }
19825
+ const teamParticipants = tournamentParticipants?.filter(
19826
+ ({ participantType }) => participantType === TEAM
19827
+ );
19828
+ if (teamParticipants) {
19829
+ for (const teamParticipant of teamParticipants) {
19830
+ const participantIds2 = teamParticipant.individualParticipantIds || [];
19831
+ const { uniquePairings: uniquePairings2 } = getPairingsData({ participantIds: participantIds2 });
19832
+ for (const pairing of uniquePairings2) {
19833
+ if (!valueObjects[pairing])
19834
+ valueObjects[pairing] = 0;
19835
+ valueObjects[pairing] += SAME_TEAM_VALUE;
19836
+ }
19837
+ }
19838
+ }
19839
+ const { uniquePairings, possiblePairings, deltaObjects } = getPairingsData({
19840
+ participantIds
19841
+ });
19842
+ const params = {
19843
+ tournamentParticipants,
19844
+ possiblePairings,
19845
+ drawDefinition,
19846
+ participantIds,
19847
+ uniquePairings,
19848
+ maxIterations,
19849
+ adHocRatings,
19850
+ deltaObjects,
19851
+ valueObjects,
19852
+ eventType,
19853
+ structure
19854
+ };
19855
+ const { candidatesCount, participantIdPairings, iterations } = getPairings(params);
19856
+ if (!candidatesCount)
19857
+ return { error: NO_CANDIDATES };
19858
+ let matchUps;
19859
+ if (generateMatchUps) {
19860
+ const result = generateAdHocMatchUps({
19861
+ structureId: structure?.structureId,
19862
+ participantIdPairings,
19863
+ tournamentRecord,
19864
+ addToStructure,
19865
+ newRound: true,
19866
+ drawDefinition,
19867
+ matchUpIds
19868
+ });
19869
+ if (result.error)
19870
+ return result;
19871
+ matchUps = result.matchUps;
19872
+ }
19873
+ return {
19874
+ ...SUCCESS,
19875
+ participantIdPairings,
19876
+ candidatesCount,
19877
+ iterations,
19878
+ matchUps
19879
+ };
19880
+ }
19881
+ function getSideRatings({
19882
+ tournamentParticipants,
19883
+ adHocRatings,
19884
+ eventType,
19885
+ pairing
19886
+ }) {
19887
+ return pairing.split("|").map((participantId) => {
19888
+ if (eventType === DOUBLES) {
19889
+ const individualParticipantIds = tournamentParticipants?.find(
19890
+ (participant) => participant.participantId === participantId
19891
+ )?.individualParticipantIds;
19892
+ return !individualParticipantIds ? DEFAULT_RATING * 2 : individualParticipantIds?.map(
19893
+ (participantId2) => adHocRatings[participantId2 || DEFAULT_RATING]
19894
+ );
19895
+ } else {
19896
+ return adHocRatings[participantId] || DEFAULT_RATING;
19897
+ }
19898
+ });
19899
+ }
19900
+ function getPairings({
19901
+ tournamentParticipants,
19902
+ adHocRatings = {},
19903
+ possiblePairings,
19904
+ // participant keyed; provides array of possible opponents
19905
+ uniquePairings,
19906
+ // hashes of all possible participantId pairings
19907
+ maxIterations,
19908
+ deltaObjects,
19909
+ // difference in rating between paired participants
19910
+ valueObjects,
19911
+ // calculated value of a pairing of participants, used for sorting pairings
19912
+ eventType
19913
+ }) {
19914
+ uniquePairings.forEach((pairing) => {
19915
+ const ratings = getSideRatings({
19916
+ tournamentParticipants,
19917
+ adHocRatings,
19918
+ eventType,
19919
+ pairing
19920
+ });
19921
+ const ratingsDifference = Math.abs(ratings[0] - ratings[1]) + 1;
19922
+ deltaObjects[pairing] = Math.abs(ratings[0] - ratings[1]);
19923
+ if (!valueObjects[pairing])
19924
+ valueObjects[pairing] = 0;
19925
+ valueObjects[pairing] += Math.pow(ratingsDifference, 2);
19926
+ });
19927
+ const valueSortedPairings = uniquePairings.map((pairing) => ({ pairing, value: valueObjects[pairing] })).sort((a, b) => a.value - b.value);
19928
+ const { pairingValues } = getParticipantPairingValues({
19929
+ possiblePairings,
19930
+ valueObjects
19931
+ });
19932
+ const { candidate, candidatesCount, iterations } = generateCandidate({
19933
+ valueSortedPairings,
19934
+ maxIterations,
19935
+ pairingValues,
19936
+ deltaObjects
19937
+ });
19938
+ const { participantIdPairings } = candidate;
19939
+ return { candidatesCount, participantIdPairings, iterations };
19940
+ }
19941
+ function getPairingsData({ participantIds }) {
19942
+ const possiblePairings = {};
19943
+ const uniquePairings = [];
19944
+ participantIds.forEach((participantId) => {
19945
+ possiblePairings[participantId] = participantIds.filter(
19946
+ (id) => id !== participantId
19947
+ );
19948
+ possiblePairings[participantId].forEach((id) => {
19949
+ const pairing = pairingHash(id, participantId);
19950
+ if (!uniquePairings.includes(pairing))
19951
+ uniquePairings.push(pairing);
19952
+ });
19953
+ });
19954
+ const deltaObjects = Object.assign(
19955
+ {},
19956
+ ...uniquePairings.map((pairing) => ({ [pairing]: 0 }))
19957
+ );
19958
+ return { uniquePairings, possiblePairings, deltaObjects };
19959
+ }
19960
+ function getEncounters({ matchUps }) {
19961
+ const encounters = [];
19962
+ for (const matchUp of matchUps) {
19963
+ const participantIds = matchUp.sides.map(getParticipantId);
19964
+ if (participantIds.length === 2) {
19965
+ const [p1, p2] = participantIds;
19966
+ const pairing = pairingHash(p1, p2);
19967
+ if (!encounters.includes(pairing))
19968
+ encounters.push(pairing);
19969
+ }
19970
+ }
19971
+ return { encounters };
19972
+ }
19973
+ function getParticipantPairingValues({ possiblePairings, valueObjects }) {
19974
+ const pairingValues = {};
19975
+ for (const participantId of Object.keys(possiblePairings)) {
19976
+ const participantValues = possiblePairings[participantId].map(
19977
+ (opponent) => pairingValue(participantId, opponent)
19978
+ );
19979
+ pairingValues[participantId] = participantValues.sort(
19980
+ (a, b) => a.value - b.value
19981
+ );
19982
+ }
19983
+ function pairingValue(participantId, opponent) {
19984
+ const key = pairingHash(participantId, opponent);
19985
+ return { opponent, value: valueObjects[key] };
19986
+ }
19987
+ return { pairingValues };
19988
+ }
19989
+
19990
+ function generateQualifyingLink({
19991
+ targetEntryRound = 1,
19992
+ finishingPositions,
19993
+ sourceRoundNumber,
19994
+ sourceStructureId,
19995
+ targetStructureId,
19996
+ linkType = LinkTypeEnum.Winner
19997
+ }) {
19998
+ if (!sourceStructureId || !targetStructureId)
19999
+ return { error: MISSING_STRUCTURE_ID };
20000
+ const link = definedAttributes({
20001
+ linkType,
20002
+ source: {
20003
+ roundNumber: sourceRoundNumber,
20004
+ structureId: sourceStructureId,
20005
+ finishingPositions
20006
+ },
20007
+ target: {
20008
+ feedProfile: DRAW,
20009
+ // positions are not automatically placed
20010
+ roundNumber: targetEntryRound,
20011
+ structureId: targetStructureId
20012
+ }
20013
+ });
20014
+ return { link };
20015
+ }
20016
+
20017
+ function generateQualifyingStructures({
20018
+ qualifyingProfiles,
20019
+ appliedPolicies,
20020
+ idPrefix,
20021
+ isMock,
20022
+ uuids
20023
+ }) {
20024
+ const stack = "generateQualifyingSTructures";
20025
+ const qualifyingDetails = [];
20026
+ const structures = [];
20027
+ const links = [];
20028
+ const sequenceSort = (a, b) => a.stageSequence - b.stageSequence;
20029
+ const roundTargetSort = (a, b) => a.roundTarget - b.roundTarget;
20030
+ let qualifyingDrawPositionsCount = 0, totalQualifiersCount = 0, finishingPositions, roundTarget = 1;
20031
+ for (const roundTargetProfile of qualifyingProfiles.sort(roundTargetSort)) {
20032
+ const structureProfiles = roundTargetProfile.structureProfiles || [];
20033
+ roundTarget = roundTargetProfile.roundTarget || roundTarget;
20034
+ let stageSequence = 1, targetRoundQualifiersCount = 0, finalQualifyingRoundNumber, finalQualifyingStructureId, linkType;
20035
+ for (const structureProfile of (structureProfiles || []).sort(
20036
+ sequenceSort
20037
+ )) {
20038
+ let drawSize = structureProfile.drawSize || coerceEven(structureProfile.participantsCount);
20039
+ const {
20040
+ qualifyingRoundNumber,
20041
+ qualifyingPositions,
20042
+ structureOptions,
20043
+ matchUpFormat,
20044
+ structureName,
20045
+ structureId,
20046
+ drawType
20047
+ } = structureProfile;
20048
+ const matchUpType = structureProfile.matchUpType;
20049
+ let roundLimit, structure, matchUps;
20050
+ if (!isConvertableInteger(drawSize)) {
20051
+ return decorateResult({
20052
+ result: { error: MISSING_DRAW_SIZE },
20053
+ stack
20054
+ });
20055
+ }
20056
+ const roundTargetName = qualifyingProfiles.length > 1 ? `${roundTarget}-` : "";
20057
+ const stageSequenceName = structureProfiles.length > 1 || roundTargetName ? stageSequence : "";
20058
+ const qualifyingStructureName = structureName || (roundTargetName || stageSequenceName ? `${QUALIFYING} ${roundTargetName}${stageSequenceName}` : QUALIFYING);
20059
+ if (drawType === ROUND_ROBIN) {
20060
+ const {
20061
+ structures: structures2,
20062
+ groupCount,
20063
+ maxRoundNumber
20064
+ /*, groupSize*/
20065
+ } = generateRoundRobin({
20066
+ structureName: structureProfile.structureName || qualifyingStructureName,
20067
+ structureId: structureId || uuids?.pop(),
20068
+ // qualifyingPositions,
20069
+ stage: QUALIFYING,
20070
+ structureOptions,
20071
+ appliedPolicies,
20072
+ stageSequence,
20073
+ matchUpType,
20074
+ roundTarget,
20075
+ drawSize,
20076
+ idPrefix,
20077
+ isMock,
20078
+ uuids
20079
+ });
20080
+ targetRoundQualifiersCount = groupCount;
20081
+ roundLimit = maxRoundNumber;
20082
+ structure = structures2[0];
20083
+ finishingPositions = [1];
20084
+ } else {
20085
+ ({ drawSize, matchUps, roundLimit } = treeMatchUps({
20086
+ qualifyingRoundNumber,
20087
+ qualifyingPositions,
20088
+ matchUpType,
20089
+ drawSize,
20090
+ idPrefix,
20091
+ isMock,
20092
+ uuids
20093
+ }));
20094
+ structure = structureTemplate({
20095
+ structureName: structureProfile.structureName || qualifyingStructureName,
20096
+ structureId: structureId || uuids?.pop(),
20097
+ qualifyingRoundNumber: roundLimit,
20098
+ stage: QUALIFYING,
20099
+ matchUpFormat,
20100
+ stageSequence,
20101
+ matchUpType,
20102
+ roundLimit,
20103
+ // redundant
20104
+ matchUps
20105
+ });
20106
+ if (roundTarget) {
20107
+ addExtension({
20108
+ element: structure,
20109
+ extension: { name: ROUND_TARGET, value: roundTarget }
20110
+ });
20111
+ }
20112
+ targetRoundQualifiersCount = matchUps?.filter((matchUp) => matchUp.roundNumber === roundLimit)?.length || 0;
20113
+ }
20114
+ if (stageSequence > 1) {
20115
+ const { link } = generateQualifyingLink({
20116
+ sourceStructureId: finalQualifyingStructureId,
20117
+ sourceRoundNumber: finalQualifyingRoundNumber,
20118
+ targetStructureId: structure.structureId,
20119
+ finishingPositions: linkType === POSITION ? [1] : void 0,
20120
+ linkType
20121
+ });
20122
+ links.push(link);
20123
+ qualifyingDrawPositionsCount += (drawSize || 0) - targetRoundQualifiersCount;
20124
+ } else {
20125
+ qualifyingDrawPositionsCount += drawSize || 0;
20126
+ }
20127
+ linkType = drawType === ROUND_ROBIN ? POSITION : WINNER;
20128
+ finalQualifyingStructureId = structure.structureId;
20129
+ finalQualifyingRoundNumber = roundLimit;
20130
+ structures.push(structure);
20131
+ stageSequence += 1;
20132
+ }
20133
+ totalQualifiersCount += targetRoundQualifiersCount;
20134
+ qualifyingDetails.push({
20135
+ qualifiersCount: targetRoundQualifiersCount,
20136
+ finalQualifyingRoundNumber,
20137
+ finalQualifyingStructureId,
20138
+ finishingPositions,
20139
+ roundTarget,
20140
+ linkType
20141
+ });
20142
+ targetRoundQualifiersCount = 0;
20143
+ roundTarget += 1;
20144
+ }
20145
+ return {
20146
+ qualifiersCount: totalQualifiersCount,
20147
+ qualifyingDrawPositionsCount,
20148
+ qualifyingDetails,
20149
+ structures,
20150
+ ...SUCCESS,
20151
+ links
20152
+ };
20153
+ }
20154
+
20155
+ function generateDrawStructuresAndLinks(params) {
20156
+ const {
20157
+ enforceMinimumDrawSize = true,
20158
+ overwriteExisting,
20159
+ drawTypeCoercion,
20160
+ // coerce to SINGLE_ELIMINATION for drawSize: 2
20161
+ appliedPolicies,
20162
+ staggeredEntry,
20163
+ // optional - specifies main structure FEED_IN for drawTypes CURTIS_CONSOLATION, FEED_IN_CHAMPIONSHIPS, FMLC
20164
+ drawDefinition,
20165
+ tieFormat,
20166
+ drawSize,
20167
+ isMock,
20168
+ uuids
20169
+ } = params || {};
20170
+ const stack = "generateDrawStructuresAndLinks";
20171
+ let drawType = params.drawType ?? SINGLE_ELIMINATION;
19730
20172
  const structures = [], links = [];
19731
20173
  const matchUpType = params?.matchUpType ?? SINGLES$1;
19732
20174
  const existingQualifyingStructures = drawDefinition?.structures?.filter(
@@ -20351,149 +20793,31 @@ function addDrawDefinition(params) {
20351
20793
  }
20352
20794
  if (incomingMatchUps?.length) {
20353
20795
  addMatchUpsNotice({
20354
- matchUps: incomingMatchUps,
20355
- tournamentId,
20356
- eventId
20357
- });
20358
- }
20359
- event.drawDefinitions = event.drawDefinitions.map(
20360
- (d) => d.drawId === drawId ? drawDefinition : d
20361
- );
20362
- const structureIds = drawDefinition.structures?.map(
20363
- ({ structureId }) => structureId
20364
- );
20365
- modifyDrawNotice({ drawDefinition, tournamentId, structureIds, eventId });
20366
- }
20367
- } else {
20368
- event.drawDefinitions.push(drawDefinition);
20369
- if (!suppressNotifications) {
20370
- const { matchUps } = allDrawMatchUps({ drawDefinition, event });
20371
- matchUps && addMatchUpsNotice({
20372
- tournamentId: tournamentRecord?.tournamentId,
20373
- matchUps
20374
- });
20375
- addDrawNotice({ drawDefinition, tournamentId, eventId });
20376
- }
20377
- }
20378
- return { ...SUCCESS, modifiedEventEntryStatusCount };
20379
- }
20380
-
20381
- function generateAdHocMatchUps({
20382
- participantIdPairings,
20383
- addToStructure = true,
20384
- tournamentRecord,
20385
- matchUpIds = [],
20386
- drawDefinition,
20387
- matchUpsCount,
20388
- roundNumber,
20389
- structureId,
20390
- newRound
20391
- }) {
20392
- if (typeof drawDefinition !== "object")
20393
- return { error: MISSING_DRAW_DEFINITION };
20394
- if (!structureId && drawDefinition.structures?.length === 1)
20395
- structureId = drawDefinition.structures?.[0]?.structureId;
20396
- if (typeof structureId !== "string")
20397
- return { error: MISSING_STRUCTURE_ID };
20398
- if (participantIdPairings && !Array.isArray(participantIdPairings) || matchUpsCount && !isConvertableInteger(matchUpsCount) || matchUpIds && !Array.isArray(matchUpIds) || !participantIdPairings && !matchUpsCount) {
20399
- return { error: INVALID_VALUES, info: "matchUpsCount or pairings error" };
20400
- }
20401
- const structure = drawDefinition?.structures?.find(
20402
- (structure2) => structure2.structureId === structureId
20403
- );
20404
- let structureHasRoundPositions;
20405
- const existingMatchUps = structure?.matchUps;
20406
- const lastRoundNumber = existingMatchUps?.reduce(
20407
- (roundNumber2, matchUp) => {
20408
- if (matchUp.roundPosition)
20409
- structureHasRoundPositions = true;
20410
- return (matchUp?.roundNumber || 0) > roundNumber2 ? matchUp.roundNumber : roundNumber2;
20411
- },
20412
- 0
20413
- );
20414
- if (structure?.structures || structureHasRoundPositions || structure?.finishingPosition === ROUND_OUTCOME) {
20415
- return { error: INVALID_STRUCTURE };
20416
- }
20417
- if (roundNumber && roundNumber - 1 > (lastRoundNumber || 0))
20418
- return { error: INVALID_VALUES, info: "roundNumber error" };
20419
- const nextRoundNumber = roundNumber ?? (newRound ? (lastRoundNumber ?? 0) + 1 : lastRoundNumber ?? 1);
20420
- participantIdPairings = participantIdPairings ?? generateRange(0, matchUpsCount).map(() => ({
20421
- participantIds: [void 0, void 0]
20422
- }));
20423
- const matchUps = participantIdPairings?.map((pairing) => {
20424
- const idStack = pairing?.participantIds ?? [void 0, void 0];
20425
- idStack.push(...[void 0, void 0]);
20426
- const participantIds = idStack.slice(0, 2);
20427
- const sides = participantIds.map(
20428
- (participantId, i) => definedAttributes({
20429
- sideNumber: i + 1,
20430
- participantId
20431
- })
20432
- );
20433
- return {
20434
- matchUpStatus: MatchUpStatusEnum.ToBePlayed,
20435
- matchUpId: matchUpIds.pop() ?? UUID(),
20436
- roundNumber: nextRoundNumber,
20437
- sides
20438
- };
20439
- });
20440
- if (addToStructure) {
20441
- const result = addAdHocMatchUps({
20442
- tournamentRecord,
20443
- drawDefinition,
20444
- structureId,
20445
- matchUps
20446
- });
20447
- if (result.error)
20448
- return result;
20449
- }
20450
- return { matchUpsCount: matchUps.length, matchUps, ...SUCCESS };
20451
- }
20452
- function addAdHocMatchUps({
20453
- tournamentRecord,
20454
- drawDefinition,
20455
- structureId,
20456
- matchUps
20457
- }) {
20458
- if (typeof drawDefinition !== "object")
20459
- return { error: MISSING_DRAW_DEFINITION };
20460
- if (!structureId && drawDefinition.structures?.length === 1)
20461
- structureId = drawDefinition.structures?.[0]?.structureId;
20462
- if (typeof structureId !== "string")
20463
- return { error: MISSING_STRUCTURE_ID };
20464
- if (!validMatchUps(matchUps))
20465
- return { error: INVALID_VALUES, info: mustBeAnArray("matchUps") };
20466
- const structure = drawDefinition.structures?.find(
20467
- (structure2) => structure2.structureId === structureId
20468
- );
20469
- if (!structure)
20470
- return { error: STRUCTURE_NOT_FOUND };
20471
- const existingMatchUps = structure?.matchUps;
20472
- const structureHasRoundPositions = existingMatchUps.find(
20473
- (matchUp) => !!matchUp.roundPosition
20474
- );
20475
- if (structure.structures || structureHasRoundPositions || structure.finishingPosition === ROUND_OUTCOME) {
20476
- return { error: INVALID_STRUCTURE };
20477
- }
20478
- const existingMatchUpIds = allTournamentMatchUps({
20479
- tournamentRecord,
20480
- inContext: false
20481
- })?.matchUps?.map(getMatchUpId) ?? [];
20482
- const newMatchUpIds = matchUps.map(getMatchUpId);
20483
- if (overlap(existingMatchUpIds, newMatchUpIds)) {
20484
- return {
20485
- error: EXISTING_MATCHUP_ID,
20486
- info: "One or more matchUpIds already present in tournamentRecord"
20487
- };
20796
+ matchUps: incomingMatchUps,
20797
+ tournamentId,
20798
+ eventId
20799
+ });
20800
+ }
20801
+ event.drawDefinitions = event.drawDefinitions.map(
20802
+ (d) => d.drawId === drawId ? drawDefinition : d
20803
+ );
20804
+ const structureIds = drawDefinition.structures?.map(
20805
+ ({ structureId }) => structureId
20806
+ );
20807
+ modifyDrawNotice({ drawDefinition, tournamentId, structureIds, eventId });
20808
+ }
20809
+ } else {
20810
+ event.drawDefinitions.push(drawDefinition);
20811
+ if (!suppressNotifications) {
20812
+ const { matchUps } = allDrawMatchUps({ drawDefinition, event });
20813
+ matchUps && addMatchUpsNotice({
20814
+ tournamentId: tournamentRecord?.tournamentId,
20815
+ matchUps
20816
+ });
20817
+ addDrawNotice({ drawDefinition, tournamentId, eventId });
20818
+ }
20488
20819
  }
20489
- structure.matchUps.push(...matchUps);
20490
- addMatchUpsNotice({
20491
- tournamentId: tournamentRecord?.tournamentId,
20492
- drawDefinition,
20493
- matchUps
20494
- });
20495
- modifyDrawNotice({ drawDefinition, structureIds: [structureId] });
20496
- return { ...SUCCESS };
20820
+ return { ...SUCCESS, modifiedEventEntryStatusCount };
20497
20821
  }
20498
20822
 
20499
20823
  function attachPolicies({ drawDefinition, policyDefinitions }) {
@@ -20558,543 +20882,163 @@ function checkValidEntries({
20558
20882
  const entryStatus = entryStatusMap[participant.participantId];
20559
20883
  const ungroupedParticipant = eventType && [TypeEnum.Doubles, TypeEnum.Team].includes(eventType) && participant.participantType === INDIVIDUAL && (isUngrouped(entryStatus) || entryStatus === WITHDRAWN);
20560
20884
  const mismatch = participant.participantType !== participantType && !ungroupedParticipant;
20561
- const personGender = participant?.person?.sex;
20562
- const wrongGender = !ignoreGender && eventGender && eventType === TypeEnum.Singles && [GenderEnum.Male, GenderEnum.Female].includes(eventGender) && personGender !== eventGender;
20563
- return mismatch || wrongGender;
20564
- });
20565
- if (invalidEntries.length) {
20566
- const invalidParticipantIds = invalidEntries.map(
20567
- (participant) => participant.participantId
20568
- );
20569
- return { error: INVALID_ENTRIES, invalidParticipantIds };
20570
- }
20571
- return { ...SUCCESS };
20572
- }
20573
-
20574
- function getValidStage({ stage, drawDefinition }) {
20575
- return Boolean(
20576
- stage === VOLUNTARY_CONSOLATION || stageExists({ stage, drawDefinition }) && getStageDrawPositionsCount({ stage, drawDefinition })
20577
- );
20578
- }
20579
-
20580
- function getStageSpace({
20581
- entryStatus = DIRECT_ACCEPTANCE,
20582
- drawDefinition,
20583
- stageSequence,
20584
- stage
20585
- }) {
20586
- if (entryStatus === ALTERNATE) {
20587
- if (stageAlternatesCount({ stage, drawDefinition })) {
20588
- return { positionsAvailable: Infinity, ...SUCCESS };
20589
- } else {
20590
- return { error: ENTRY_STATUS_NOT_ALLOWED_IN_STAGE };
20591
- }
20592
- }
20593
- const stageDrawPositionsAvailable = getStageDrawPositionsAvailable({
20594
- drawDefinition,
20595
- stageSequence,
20596
- stage
20597
- });
20598
- const wildcardPositions = getStageWildcardsCount({
20599
- drawDefinition,
20600
- stage
20601
- });
20602
- const wildcardEntriesCount = getStageEntryTypeCount({
20603
- entryStatus: WILDCARD,
20604
- drawDefinition,
20605
- stage
20606
- });
20607
- const directEntriesCount = getStageEntryTypeCount({
20608
- entryStatus: DIRECT_ACCEPTANCE,
20609
- drawDefinition,
20610
- stage
20611
- });
20612
- const totalEntriesCount = wildcardEntriesCount + directEntriesCount;
20613
- const stageFull = totalEntriesCount >= stageDrawPositionsAvailable;
20614
- const positionsAvailable = stageDrawPositionsAvailable - totalEntriesCount;
20615
- if (stage !== VOLUNTARY_CONSOLATION && stageFull) {
20616
- return { error: NO_STAGE_SPACE_AVAILABLE_FOR_ENTRY_STATUS };
20617
- }
20618
- if (entryStatus === WILDCARD) {
20619
- if (wildcardEntriesCount < wildcardPositions)
20620
- return { ...SUCCESS };
20621
- return { error: NO_STAGE_SPACE_AVAILABLE_FOR_ENTRY_STATUS };
20622
- }
20623
- return { positionsAvailable, ...SUCCESS };
20624
- }
20625
-
20626
- function addDrawEntry(params) {
20627
- const {
20628
- entryStatus = EntryStatusEnum.DirectAcceptance,
20629
- entryStageSequence,
20630
- entryStage = MAIN,
20631
- ignoreStageSpace,
20632
- drawDefinition,
20633
- entryPosition,
20634
- participant,
20635
- roundTarget,
20636
- extensions,
20637
- extension,
20638
- drawType
20639
- } = params;
20640
- const stack = "addDrawEntry";
20641
- if (!drawDefinition)
20642
- return { error: MISSING_DRAW_DEFINITION };
20643
- if (!entryStage)
20644
- return { error: MISSING_STAGE };
20645
- if (drawType !== AD_HOC && !getValidStage({ stage: entryStage, drawDefinition })) {
20646
- return decorateResult({ result: { error: INVALID_STAGE }, stack });
20647
- }
20648
- const spaceAvailable = getStageSpace({
20649
- stageSequence: entryStageSequence,
20650
- stage: entryStage,
20651
- drawDefinition,
20652
- entryStatus
20653
- });
20654
- if (!ignoreStageSpace && !spaceAvailable.success) {
20655
- return { error: spaceAvailable.error };
20656
- }
20657
- if (extension && !isValidExtension({ extension }))
20658
- return decorateResult({
20659
- result: { error: INVALID_VALUES },
20660
- info: "Invalid extension",
20661
- context: { extension },
20662
- stack
20663
- });
20664
- const participantId = params.participantId || participant?.participantId;
20665
- if (!participantId)
20666
- return decorateResult({ result: { error: MISSING_PARTICIPANT_ID }, stack });
20667
- const invalidLuckyLoser = entryStatus === EntryStatusEnum.LuckyLoser && participantInEntries({ participantId, drawDefinition, entryStatus });
20668
- const invalidVoluntaryConsolation = entryStage === VOLUNTARY_CONSOLATION && participantInEntries({
20669
- participantId,
20670
- drawDefinition,
20671
- entryStage
20672
- });
20673
- const invalidEntry = entryStatus !== EntryStatusEnum.LuckyLoser && entryStage !== VOLUNTARY_CONSOLATION && participantInEntries({ drawDefinition, participantId });
20674
- if (invalidEntry || invalidLuckyLoser || invalidVoluntaryConsolation) {
20675
- return decorateResult({
20676
- context: { invalidEntry, invalidLuckyLoser, invalidVoluntaryConsolation },
20677
- result: { error: EXISTING_PARTICIPANT },
20678
- stack
20679
- });
20680
- }
20681
- const entry = definedAttributes({
20682
- entryStageSequence,
20683
- participantId,
20684
- entryPosition,
20685
- entryStatus,
20686
- entryStage,
20687
- extensions
20688
- });
20689
- if (extension) {
20690
- addExtension({ element: entry, extension });
20691
- }
20692
- if (roundTarget) {
20693
- addExtension({
20694
- extension: { name: "roundEntry", value: roundTarget },
20695
- element: entry
20696
- });
20697
- }
20698
- if (!drawDefinition.entries)
20699
- drawDefinition.entries = [];
20700
- drawDefinition.entries.push(entry);
20701
- modifyDrawNotice({ drawDefinition });
20702
- return { ...SUCCESS };
20703
- }
20704
-
20705
- function getAllowedDrawTypes({
20706
- tournamentRecord,
20707
- categoryName,
20708
- categoryType
20709
- }) {
20710
- if (!tournamentRecord)
20711
- return { error: MISSING_TOURNAMENT_RECORD };
20712
- const { appliedPolicies } = getAppliedPolicies({ tournamentRecord });
20713
- const drawTypesPolicy = appliedPolicies?.[POLICY_TYPE_DRAWS];
20714
- const drawTypes = drawTypesPolicy?.allowedDrawTypes || [];
20715
- return drawTypes.filter(
20716
- ({ categoryNames, categoryTypes }) => !categoryName && !categoryTypes || categoryName && categoryNames?.includes(categoryName) || categoryType && categoryTypes?.includes(categoryType)
20717
- );
20718
- }
20719
-
20720
- function generateCandidate({
20721
- maxIterations = 5e3,
20722
- // cap the processing intensity of the candidate generator
20723
- valueSortedPairings,
20724
- // pairings sorted by value from low to high
20725
- pairingValues,
20726
- deltaObjects
20727
- }) {
20728
- const rankedMatchUpValues = Object.assign(
20729
- {},
20730
- ...valueSortedPairings.map((rm) => ({ [rm.pairing]: rm.value }))
20731
- );
20732
- let candidate = roundCandidate({
20733
- rankedMatchUpValues,
20734
- valueSortedPairings,
20735
- deltaObjects
20736
- });
20737
- let deltaCandidate = candidate;
20738
- const actors = Object.keys(pairingValues);
20739
- let candidatesCount = 0;
20740
- let iterations;
20741
- let opponentCount = actors.length;
20742
- do {
20743
- opponentCount -= 1;
20744
- iterations = actors.length * opponentCount * valueSortedPairings.length / 2;
20745
- } while (iterations > maxIterations && opponentCount > 2);
20746
- const stipulatedPairs = [];
20747
- actors.forEach((actor) => {
20748
- const participantIdPairings = pairingValues[actor];
20749
- participantIdPairings.slice(0, opponentCount).forEach((pairing) => {
20750
- const stipulatedPair = pairingHash(actor, pairing.opponent);
20751
- if (!stipulatedPairs.includes(stipulatedPair)) {
20752
- const proposed = roundCandidate({
20753
- // each roundCandidate starts with stipulated pairings
20754
- stipulated: [[actor, pairing.opponent]],
20755
- rankedMatchUpValues,
20756
- valueSortedPairings,
20757
- deltaObjects
20758
- });
20759
- if (proposed.maxDelta < deltaCandidate.maxDelta)
20760
- deltaCandidate = proposed;
20761
- if (proposed.value < candidate.value)
20762
- candidate = proposed;
20763
- stipulatedPairs.push(stipulatedPair);
20764
- candidatesCount += 1;
20765
- }
20766
- });
20767
- });
20768
- return { candidate, deltaCandidate, candidatesCount, iterations };
20769
- }
20770
- function roundCandidate({
20771
- rankedMatchUpValues,
20772
- valueSortedPairings,
20773
- stipulated = [],
20774
- deltaObjects
20775
- }) {
20776
- const roundPlayers = [].concat(...stipulated);
20777
- const participantIdPairings = [];
20778
- let candidateValue = 0;
20779
- stipulated.filter(Boolean).forEach((participantIds) => {
20780
- const [p1, p2] = participantIds;
20781
- const pairing = pairingHash(p1, p2);
20782
- const value = rankedMatchUpValues[pairing];
20783
- participantIdPairings.push({ participantIds, value });
20784
- candidateValue += rankedMatchUpValues[pairing];
20885
+ const personGender = participant?.person?.sex;
20886
+ const wrongGender = !ignoreGender && eventGender && eventType === TypeEnum.Singles && [GenderEnum.Male, GenderEnum.Female].includes(eventGender) && personGender !== eventGender;
20887
+ return mismatch || wrongGender;
20785
20888
  });
20786
- valueSortedPairings.forEach((rankedPairing) => {
20787
- const participantIds = rankedPairing.pairing.split("|");
20788
- const opponentExists = participantIds.reduce(
20789
- (p, c) => roundPlayers.includes(c) || p,
20790
- false
20889
+ if (invalidEntries.length) {
20890
+ const invalidParticipantIds = invalidEntries.map(
20891
+ (participant) => participant.participantId
20791
20892
  );
20792
- if (!opponentExists) {
20793
- roundPlayers.push(...participantIds);
20794
- const value = rankedPairing.value;
20795
- candidateValue += value;
20796
- participantIdPairings.push({ participantIds, value });
20797
- }
20798
- });
20799
- participantIdPairings.sort((a, b) => a.value - b.value);
20800
- const maxDelta = participantIdPairings.reduce((p, c) => {
20801
- const [p1, p2] = c.participantIds;
20802
- const hash = pairingHash(p1, p2);
20803
- const delta = deltaObjects[hash];
20804
- return delta > p ? delta : p;
20805
- }, 0);
20806
- return { value: candidateValue, participantIdPairings, maxDelta };
20893
+ return { error: INVALID_ENTRIES, invalidParticipantIds };
20894
+ }
20895
+ return { ...SUCCESS };
20807
20896
  }
20808
- function pairingHash(id1, id2) {
20809
- return [id1, id2].sort().join("|");
20897
+
20898
+ function getValidStage({ stage, drawDefinition }) {
20899
+ return Boolean(
20900
+ stage === VOLUNTARY_CONSOLATION || stageExists({ stage, drawDefinition }) && getStageDrawPositionsCount({ stage, drawDefinition })
20901
+ );
20810
20902
  }
20811
20903
 
20812
- const ENCOUNTER_VALUE = 50;
20813
- const SAME_TEAM_VALUE = 60;
20814
- const DEFAULT_RATING = 0;
20815
- const MAX_ITERATIONS = 5e3;
20816
- function generateDrawMaticRound({
20817
- maxIterations = MAX_ITERATIONS,
20818
- generateMatchUps = true,
20819
- tournamentParticipants,
20820
- tournamentRecord,
20821
- participantIds,
20822
- addToStructure,
20904
+ function getStageSpace({
20905
+ entryStatus = DIRECT_ACCEPTANCE,
20823
20906
  drawDefinition,
20824
- adHocRatings,
20825
- structureId,
20826
- matchUpIds,
20827
- eventType,
20828
- structure
20907
+ stageSequence,
20908
+ stage
20829
20909
  }) {
20830
- if (!participantIds?.length) {
20831
- return { error: MISSING_PARTICIPANT_IDS };
20832
- }
20833
- const { encounters } = getEncounters({ matchUps: structure.matchUps });
20834
- const valueObjects = {};
20835
- for (const pairing of encounters) {
20836
- if (!valueObjects[pairing])
20837
- valueObjects[pairing] = 0;
20838
- valueObjects[pairing] += ENCOUNTER_VALUE;
20839
- }
20840
- const teamParticipants = tournamentParticipants?.filter(
20841
- ({ participantType }) => participantType === TEAM
20842
- );
20843
- if (teamParticipants) {
20844
- for (const teamParticipant of teamParticipants) {
20845
- const participantIds2 = teamParticipant.individualParticipantIds || [];
20846
- const { uniquePairings: uniquePairings2 } = getPairingsData({ participantIds: participantIds2 });
20847
- for (const pairing of uniquePairings2) {
20848
- if (!valueObjects[pairing])
20849
- valueObjects[pairing] = 0;
20850
- valueObjects[pairing] += SAME_TEAM_VALUE;
20851
- }
20910
+ if (entryStatus === ALTERNATE) {
20911
+ if (stageAlternatesCount({ stage, drawDefinition })) {
20912
+ return { positionsAvailable: Infinity, ...SUCCESS };
20913
+ } else {
20914
+ return { error: ENTRY_STATUS_NOT_ALLOWED_IN_STAGE };
20852
20915
  }
20853
20916
  }
20854
- const { uniquePairings, possiblePairings, deltaObjects } = getPairingsData({
20855
- participantIds
20856
- });
20857
- const params = {
20858
- tournamentParticipants,
20859
- possiblePairings,
20917
+ const stageDrawPositionsAvailable = getStageDrawPositionsAvailable({
20860
20918
  drawDefinition,
20861
- participantIds,
20862
- uniquePairings,
20863
- maxIterations,
20864
- adHocRatings,
20865
- deltaObjects,
20866
- valueObjects,
20867
- eventType,
20868
- structure
20869
- };
20870
- const { candidatesCount, participantIdPairings, iterations } = getPairings(params);
20871
- if (!candidatesCount)
20872
- return { error: NO_CANDIDATES };
20873
- let matchUps;
20874
- if (generateMatchUps) {
20875
- const result = generateAdHocMatchUps({
20876
- participantIdPairings,
20877
- tournamentRecord,
20878
- addToStructure,
20879
- newRound: true,
20880
- drawDefinition,
20881
- structureId,
20882
- matchUpIds
20883
- });
20884
- if (result.error)
20885
- return result;
20886
- matchUps = result.matchUps;
20887
- }
20888
- return {
20889
- ...SUCCESS,
20890
- participantIdPairings,
20891
- candidatesCount,
20892
- iterations,
20893
- matchUps
20894
- };
20895
- }
20896
- function getSideRatings({
20897
- tournamentParticipants,
20898
- adHocRatings,
20899
- eventType,
20900
- pairing
20901
- }) {
20902
- return pairing.split("|").map((participantId) => {
20903
- if (eventType === DOUBLES) {
20904
- const individualParticipantIds = tournamentParticipants?.find(
20905
- (participant) => participant.participantId === participantId
20906
- )?.individualParticipantIds;
20907
- return !individualParticipantIds ? DEFAULT_RATING * 2 : individualParticipantIds?.map(
20908
- (participantId2) => adHocRatings[participantId2 || DEFAULT_RATING]
20909
- );
20910
- } else {
20911
- return adHocRatings[participantId] || DEFAULT_RATING;
20912
- }
20919
+ stageSequence,
20920
+ stage
20913
20921
  });
20914
- }
20915
- function getPairings({
20916
- tournamentParticipants,
20917
- adHocRatings = {},
20918
- possiblePairings,
20919
- // participant keyed; provides array of possible opponents
20920
- uniquePairings,
20921
- // hashes of all possible participantId pairings
20922
- maxIterations,
20923
- deltaObjects,
20924
- // difference in rating between paired participants
20925
- valueObjects,
20926
- // calculated value of a pairing of participants, used for sorting pairings
20927
- eventType
20928
- }) {
20929
- uniquePairings.forEach((pairing) => {
20930
- const ratings = getSideRatings({
20931
- tournamentParticipants,
20932
- adHocRatings,
20933
- eventType,
20934
- pairing
20935
- });
20936
- const ratingsDifference = Math.abs(ratings[0] - ratings[1]) + 1;
20937
- deltaObjects[pairing] = Math.abs(ratings[0] - ratings[1]);
20938
- if (!valueObjects[pairing])
20939
- valueObjects[pairing] = 0;
20940
- valueObjects[pairing] += Math.pow(ratingsDifference, 2);
20922
+ const wildcardPositions = getStageWildcardsCount({
20923
+ drawDefinition,
20924
+ stage
20941
20925
  });
20942
- const valueSortedPairings = uniquePairings.map((pairing) => ({ pairing, value: valueObjects[pairing] })).sort((a, b) => a.value - b.value);
20943
- const { pairingValues } = getParticipantPairingValues({
20944
- possiblePairings,
20945
- valueObjects
20926
+ const wildcardEntriesCount = getStageEntryTypeCount({
20927
+ entryStatus: WILDCARD,
20928
+ drawDefinition,
20929
+ stage
20946
20930
  });
20947
- const { candidate, candidatesCount, iterations } = generateCandidate({
20948
- valueSortedPairings,
20949
- maxIterations,
20950
- pairingValues,
20951
- deltaObjects
20931
+ const directEntriesCount = getStageEntryTypeCount({
20932
+ entryStatus: DIRECT_ACCEPTANCE,
20933
+ drawDefinition,
20934
+ stage
20952
20935
  });
20953
- const { participantIdPairings } = candidate;
20954
- return { candidatesCount, participantIdPairings, iterations };
20936
+ const totalEntriesCount = wildcardEntriesCount + directEntriesCount;
20937
+ const stageFull = totalEntriesCount >= stageDrawPositionsAvailable;
20938
+ const positionsAvailable = stageDrawPositionsAvailable - totalEntriesCount;
20939
+ if (stage !== VOLUNTARY_CONSOLATION && stageFull) {
20940
+ return { error: NO_STAGE_SPACE_AVAILABLE_FOR_ENTRY_STATUS };
20941
+ }
20942
+ if (entryStatus === WILDCARD) {
20943
+ if (wildcardEntriesCount < wildcardPositions)
20944
+ return { ...SUCCESS };
20945
+ return { error: NO_STAGE_SPACE_AVAILABLE_FOR_ENTRY_STATUS };
20946
+ }
20947
+ return { positionsAvailable, ...SUCCESS };
20955
20948
  }
20956
- function getPairingsData({ participantIds }) {
20957
- const possiblePairings = {};
20958
- const uniquePairings = [];
20959
- participantIds.forEach((participantId) => {
20960
- possiblePairings[participantId] = participantIds.filter(
20961
- (id) => id !== participantId
20962
- );
20963
- possiblePairings[participantId].forEach((id) => {
20964
- const pairing = pairingHash(id, participantId);
20965
- if (!uniquePairings.includes(pairing))
20966
- uniquePairings.push(pairing);
20949
+
20950
+ function addDrawEntry(params) {
20951
+ const {
20952
+ entryStatus = EntryStatusEnum.DirectAcceptance,
20953
+ entryStageSequence,
20954
+ entryStage = MAIN,
20955
+ ignoreStageSpace,
20956
+ drawDefinition,
20957
+ entryPosition,
20958
+ participant,
20959
+ roundTarget,
20960
+ extensions,
20961
+ extension,
20962
+ drawType
20963
+ } = params;
20964
+ const stack = "addDrawEntry";
20965
+ if (!drawDefinition)
20966
+ return { error: MISSING_DRAW_DEFINITION };
20967
+ if (!entryStage)
20968
+ return { error: MISSING_STAGE };
20969
+ if (drawType !== AD_HOC && !getValidStage({ stage: entryStage, drawDefinition })) {
20970
+ return decorateResult({ result: { error: INVALID_STAGE }, stack });
20971
+ }
20972
+ const spaceAvailable = getStageSpace({
20973
+ stageSequence: entryStageSequence,
20974
+ stage: entryStage,
20975
+ drawDefinition,
20976
+ entryStatus
20977
+ });
20978
+ if (!ignoreStageSpace && !spaceAvailable.success) {
20979
+ return { error: spaceAvailable.error };
20980
+ }
20981
+ if (extension && !isValidExtension({ extension }))
20982
+ return decorateResult({
20983
+ result: { error: INVALID_VALUES },
20984
+ info: "Invalid extension",
20985
+ context: { extension },
20986
+ stack
20967
20987
  });
20988
+ const participantId = params.participantId || participant?.participantId;
20989
+ if (!participantId)
20990
+ return decorateResult({ result: { error: MISSING_PARTICIPANT_ID }, stack });
20991
+ const invalidLuckyLoser = entryStatus === EntryStatusEnum.LuckyLoser && participantInEntries({ participantId, drawDefinition, entryStatus });
20992
+ const invalidVoluntaryConsolation = entryStage === VOLUNTARY_CONSOLATION && participantInEntries({
20993
+ participantId,
20994
+ drawDefinition,
20995
+ entryStage
20968
20996
  });
20969
- const deltaObjects = Object.assign(
20970
- {},
20971
- ...uniquePairings.map((pairing) => ({ [pairing]: 0 }))
20972
- );
20973
- return { uniquePairings, possiblePairings, deltaObjects };
20974
- }
20975
- function getEncounters({ matchUps }) {
20976
- const encounters = [];
20977
- for (const matchUp of matchUps) {
20978
- const participantIds = matchUp.sides.map(getParticipantId);
20979
- if (participantIds.length === 2) {
20980
- const [p1, p2] = participantIds;
20981
- const pairing = pairingHash(p1, p2);
20982
- if (!encounters.includes(pairing))
20983
- encounters.push(pairing);
20984
- }
20997
+ const invalidEntry = entryStatus !== EntryStatusEnum.LuckyLoser && entryStage !== VOLUNTARY_CONSOLATION && participantInEntries({ drawDefinition, participantId });
20998
+ if (invalidEntry || invalidLuckyLoser || invalidVoluntaryConsolation) {
20999
+ return decorateResult({
21000
+ context: { invalidEntry, invalidLuckyLoser, invalidVoluntaryConsolation },
21001
+ result: { error: EXISTING_PARTICIPANT },
21002
+ stack
21003
+ });
20985
21004
  }
20986
- return { encounters };
20987
- }
20988
- function getParticipantPairingValues({ possiblePairings, valueObjects }) {
20989
- const pairingValues = {};
20990
- for (const participantId of Object.keys(possiblePairings)) {
20991
- const participantValues = possiblePairings[participantId].map(
20992
- (opponent) => pairingValue(participantId, opponent)
20993
- );
20994
- pairingValues[participantId] = participantValues.sort(
20995
- (a, b) => a.value - b.value
20996
- );
21005
+ const entry = definedAttributes({
21006
+ entryStageSequence,
21007
+ participantId,
21008
+ entryPosition,
21009
+ entryStatus,
21010
+ entryStage,
21011
+ extensions
21012
+ });
21013
+ if (extension) {
21014
+ addExtension({ element: entry, extension });
20997
21015
  }
20998
- function pairingValue(participantId, opponent) {
20999
- const key = pairingHash(participantId, opponent);
21000
- return { opponent, value: valueObjects[key] };
21016
+ if (roundTarget) {
21017
+ addExtension({
21018
+ extension: { name: "roundEntry", value: roundTarget },
21019
+ element: entry
21020
+ });
21001
21021
  }
21002
- return { pairingValues };
21022
+ if (!drawDefinition.entries)
21023
+ drawDefinition.entries = [];
21024
+ drawDefinition.entries.push(entry);
21025
+ modifyDrawNotice({ drawDefinition });
21026
+ return { ...SUCCESS };
21003
21027
  }
21004
21028
 
21005
- function drawMatic({
21006
- tournamentParticipants,
21007
- restrictEntryStatus,
21029
+ function getAllowedDrawTypes({
21008
21030
  tournamentRecord,
21009
- generateMatchUps,
21010
- addToStructure,
21011
- participantIds,
21012
- drawDefinition,
21013
- maxIterations,
21014
- structureId,
21015
- matchUpIds,
21016
- scaleName,
21017
- // custom rating name to seed dynamic ratings
21018
- eventType,
21019
- event
21031
+ categoryName,
21032
+ categoryType
21020
21033
  }) {
21021
- if (typeof drawDefinition !== "object" || drawDefinition.drawType && drawDefinition.drawType !== AD_HOC) {
21022
- return { error: INVALID_DRAW_DEFINITION };
21023
- }
21024
- if (!Array.isArray(drawDefinition?.entries) && participantIds && !Array.isArray(participantIds)) {
21025
- return { error: INVALID_VALUES, info: "Missing Entries" };
21026
- }
21027
- eventType = eventType ?? event?.eventType;
21028
- const enteredParticipantIds = drawDefinition?.entries?.filter((entry) => {
21029
- const entryStatus = entry.entryStatus;
21030
- return !restrictEntryStatus || STRUCTURE_SELECTED_STATUSES.includes(entryStatus);
21031
- }).map(getParticipantId);
21032
- if (participantIds) {
21033
- const invalidParticipantIds = participantIds.filter(
21034
- (participantId) => !enteredParticipantIds?.includes(participantId)
21035
- );
21036
- if (invalidParticipantIds?.length)
21037
- return {
21038
- error: INVALID_PARTICIPANT_ID,
21039
- invalidParticipantIds
21040
- };
21041
- } else {
21042
- participantIds = enteredParticipantIds;
21043
- }
21044
- if (!structureId) {
21045
- const targetStructure = drawDefinition?.structures?.filter((structure2) => structure2.stageSequence === 1)?.reduce((targetStructure2, structure2) => {
21046
- const orderNumber = structure2.stage && stageOrder[structure2.stage];
21047
- const structureIsAdHoc2 = isAdHoc({ drawDefinition, structure: structure2 });
21048
- return structureIsAdHoc2 && orderNumber > (stageOrder[targetStructure2?.stage] || 1) ? structure2 : targetStructure2;
21049
- }, void 0);
21050
- structureId = targetStructure?.structureId;
21051
- }
21052
- const structure = drawDefinition?.structures?.find(
21053
- (structure2) => structure2.structureId === structureId
21034
+ if (!tournamentRecord)
21035
+ return { error: MISSING_TOURNAMENT_RECORD };
21036
+ const { appliedPolicies } = getAppliedPolicies({ tournamentRecord });
21037
+ const drawTypesPolicy = appliedPolicies?.[POLICY_TYPE_DRAWS];
21038
+ const drawTypes = drawTypesPolicy?.allowedDrawTypes || [];
21039
+ return drawTypes.filter(
21040
+ ({ categoryNames, categoryTypes }) => !categoryName && !categoryTypes || categoryName && categoryNames?.includes(categoryName) || categoryType && categoryTypes?.includes(categoryType)
21054
21041
  );
21055
- if (!structure)
21056
- return { error: STRUCTURE_NOT_FOUND };
21057
- const structureIsAdHoc = isAdHoc({ drawDefinition, structure });
21058
- if (!structureIsAdHoc)
21059
- return { error: INVALID_DRAW_DEFINITION };
21060
- const adHocRatings = {};
21061
- for (const participantId of participantIds ?? []) {
21062
- const participant = tournamentParticipants?.find(
21063
- (participant2) => participant2.participantId === participantId
21064
- );
21065
- let scaleValue = getScaleValue({ eventType, participant });
21066
- if (!scaleValue && scaleName) {
21067
- scaleValue = getScaleValue({ scaleName, eventType, participant });
21068
- }
21069
- if (scaleValue)
21070
- adHocRatings[participantId] = scaleValue;
21071
- }
21072
- return generateDrawMaticRound({
21073
- tournamentParticipants,
21074
- tournamentRecord,
21075
- generateMatchUps,
21076
- participantIds,
21077
- addToStructure,
21078
- drawDefinition,
21079
- maxIterations,
21080
- adHocRatings,
21081
- structureId,
21082
- matchUpIds,
21083
- structure,
21084
- eventType
21085
- });
21086
- }
21087
- function getScaleValue({ scaleName = "dynamic", eventType, participant }) {
21088
- const scaleAttributes = {
21089
- eventType: eventType || TypeEnum.Singles,
21090
- scaleType: RATING,
21091
- scaleName
21092
- };
21093
- const result = participant && participantScaleItem({
21094
- scaleAttributes,
21095
- participant
21096
- });
21097
- return result?.scaleItem?.scaleValue;
21098
21042
  }
21099
21043
 
21100
21044
  const definitionTemplate = () => ({
@@ -22572,6 +22516,113 @@ function prepareStage(params) {
22572
22516
  };
22573
22517
  }
22574
22518
 
22519
+ function drawMatic({
22520
+ tournamentParticipants,
22521
+ restrictEntryStatus,
22522
+ tournamentRecord,
22523
+ generateMatchUps,
22524
+ addToStructure,
22525
+ participantIds,
22526
+ drawDefinition,
22527
+ scaleAccessor,
22528
+ maxIterations,
22529
+ structureId,
22530
+ matchUpIds,
22531
+ scaleName,
22532
+ // custom rating name to seed dynamic ratings
22533
+ eventType,
22534
+ event
22535
+ }) {
22536
+ if (typeof drawDefinition !== "object" || drawDefinition.drawType && drawDefinition.drawType !== AD_HOC) {
22537
+ return { error: INVALID_DRAW_DEFINITION };
22538
+ }
22539
+ if (!Array.isArray(drawDefinition?.entries) && participantIds && !Array.isArray(participantIds)) {
22540
+ return { error: INVALID_VALUES, info: "Missing Entries" };
22541
+ }
22542
+ eventType = eventType ?? event?.eventType;
22543
+ const enteredParticipantIds = drawDefinition?.entries?.filter((entry) => {
22544
+ const entryStatus = entry.entryStatus;
22545
+ return !restrictEntryStatus || STRUCTURE_SELECTED_STATUSES.includes(entryStatus);
22546
+ }).map(getParticipantId);
22547
+ if (participantIds) {
22548
+ const invalidParticipantIds = participantIds.filter(
22549
+ (participantId) => !enteredParticipantIds?.includes(participantId)
22550
+ );
22551
+ if (invalidParticipantIds?.length)
22552
+ return {
22553
+ error: INVALID_PARTICIPANT_ID,
22554
+ invalidParticipantIds
22555
+ };
22556
+ } else {
22557
+ participantIds = enteredParticipantIds;
22558
+ }
22559
+ if (!structureId) {
22560
+ const targetStructure = drawDefinition?.structures?.filter((structure2) => structure2.stageSequence === 1)?.reduce((targetStructure2, structure2) => {
22561
+ const orderNumber = structure2.stage && stageOrder[structure2.stage];
22562
+ const structureIsAdHoc2 = isAdHoc({ drawDefinition, structure: structure2 });
22563
+ return structureIsAdHoc2 && orderNumber > (stageOrder[targetStructure2?.stage] || 1) ? structure2 : targetStructure2;
22564
+ }, void 0);
22565
+ structureId = targetStructure?.structureId;
22566
+ }
22567
+ const structure = drawDefinition?.structures?.find(
22568
+ (structure2) => structure2.structureId === structureId
22569
+ );
22570
+ if (!structure)
22571
+ return { error: STRUCTURE_NOT_FOUND };
22572
+ const structureIsAdHoc = isAdHoc({ drawDefinition, structure });
22573
+ if (!structureIsAdHoc)
22574
+ return { error: INVALID_DRAW_DEFINITION };
22575
+ tournamentParticipants = tournamentParticipants ?? tournamentRecord.participants ?? [];
22576
+ const adHocRatings = {};
22577
+ for (const participantId of participantIds ?? []) {
22578
+ const participant = tournamentParticipants?.find(
22579
+ (participant2) => participant2.participantId === participantId
22580
+ );
22581
+ let scaleValue = getScaleValue({ eventType, participant });
22582
+ if (!scaleValue && scaleName) {
22583
+ scaleValue = getScaleValue({
22584
+ scaleAccessor,
22585
+ participant,
22586
+ scaleName,
22587
+ eventType
22588
+ });
22589
+ }
22590
+ if (scaleValue)
22591
+ adHocRatings[participantId] = scaleValue;
22592
+ }
22593
+ return generateDrawMaticRound({
22594
+ tournamentParticipants,
22595
+ tournamentRecord,
22596
+ generateMatchUps,
22597
+ participantIds,
22598
+ addToStructure,
22599
+ drawDefinition,
22600
+ maxIterations,
22601
+ adHocRatings,
22602
+ matchUpIds,
22603
+ structure,
22604
+ eventType
22605
+ });
22606
+ }
22607
+ function getScaleValue({
22608
+ eventType = TypeEnum.Singles,
22609
+ scaleName = "dynamic",
22610
+ scaleAccessor,
22611
+ participant
22612
+ }) {
22613
+ const scaleAttributes = {
22614
+ eventType: eventType || TypeEnum.Singles,
22615
+ scaleType: RATING,
22616
+ scaleName
22617
+ };
22618
+ const result = participant && participantScaleItem({
22619
+ scaleAttributes,
22620
+ participant
22621
+ });
22622
+ const scaleValue = result?.scaleItem?.scaleValue;
22623
+ return scaleAccessor && isObject(scaleValue) ? scaleValue[scaleAccessor] : scaleValue;
22624
+ }
22625
+
22575
22626
  const POLICY_SEEDING_USTA = {
22576
22627
  [POLICY_TYPE_SEEDING]: {
22577
22628
  validSeedPositions: { ignore: true },
@@ -22899,12 +22950,28 @@ function generateDrawDefinition(params) {
22899
22950
  const matchUpsCount = entries2 ? Math.floor(entries2.length / 2) : 0;
22900
22951
  generateRange(1, params.roundsCount + 1).forEach(() => {
22901
22952
  if (params.automated) {
22953
+ const {
22954
+ restrictEntryStatus,
22955
+ generateMatchUps,
22956
+ addToStructure,
22957
+ maxIterations,
22958
+ structureId: structureId2,
22959
+ matchUpIds,
22960
+ scaleName
22961
+ } = params.drawMatic ?? {};
22902
22962
  drawMatic({
22903
- generateMatchUps: true,
22904
- eventType: matchUpType,
22905
22963
  tournamentRecord,
22906
22964
  participantIds,
22907
- drawDefinition
22965
+ drawDefinition,
22966
+ eventType: params.drawMatic?.eventType ?? matchUpType,
22967
+ generateMatchUps: generateMatchUps ?? true,
22968
+ restrictEntryStatus,
22969
+ addToStructure,
22970
+ maxIterations,
22971
+ structureId: structureId2,
22972
+ matchUpIds,
22973
+ scaleName
22974
+ // custom rating name to seed dynamic ratings
22908
22975
  });
22909
22976
  } else {
22910
22977
  generateAdHocMatchUps({
@@ -23146,5 +23213,5 @@ function generateFlightProfile(params) {
23146
23213
  }
23147
23214
  }
23148
23215
 
23149
- export { automatedPlayoffPositioning, automatedPositioning, createGroupParticipant, generateAndPopulatePlayoffStructures, generateDrawDefinition, generateFlightProfile, generateVoluntaryConsolation };
23216
+ export { automatedPlayoffPositioning, automatedPositioning, createGroupParticipant, generateAndPopulatePlayoffStructures, generateDrawDefinition, generateDrawMaticRound, generateFlightProfile, generateVoluntaryConsolation };
23150
23217
  //# sourceMappingURL=generate.mjs.map