export const TIMESTAMPS_COUNT = 50000; export const PROBABILITY_SCORE_CHANGED = 0.01; export const PROBABILITY_HOME_SCORE = 0.45; export const OFFSET_MAX_STEP = 3; type Score = { home: number; away: number; }; export type Stamp = { offset: number; score: Score; }; const emptyScoreStamp: Stamp = { offset: 0, score: { home: 0, away: 0, }, }; export const generateStamps = (): Stamp[] => { const scoreStamps = Array(TIMESTAMPS_COUNT) .fill(emptyScoreStamp) .map( ((acc) => () => { const scoreChanged = Math.random() > 1 - PROBABILITY_SCORE_CHANGED; const homeScoreChange = scoreChanged && Math.random() < PROBABILITY_HOME_SCORE ? 1 : 0; const awayScoreChange = scoreChanged && !homeScoreChange ? 1 : 0; return { offset: (acc.offset += Math.floor(Math.random() * OFFSET_MAX_STEP) + 1), score: { home: (acc.score.home += homeScoreChange), away: (acc.score.away += awayScoreChange), }, }; })(emptyScoreStamp) ); return scoreStamps; }; export const getScore = ( gameStamps: Stamp[] | undefined, offset: number ): Score | undefined => { // Implementing binary search on gameStamps to provide log(n) complexity solution if (gameStamps && gameStamps.length > 0 && offset) { let start = 0; let end = gameStamps.length - 1; while (start <= end) { const middle = Math.floor((start + end) / 2); if (gameStamps[middle].offset === offset) { // Exact match found return gameStamps[middle].score; } else if (gameStamps[middle].offset < offset) { start = middle + 1; } else { end = middle - 1; } } // If no exact match found, determine the closest offset if (end < 0) { // No stamps before offset, return the first stamp return gameStamps[start].score; } else if (start >= gameStamps.length) { // No stamps after offset, return the last stamp return gameStamps[end].score; } else { // Determine which stamp is closer to the offset if ( offset - gameStamps[end].offset <= gameStamps[start].offset - offset ) { return gameStamps[end].score; } else { return gameStamps[start].score; } } } }; // const score = getScore(generateStamps(), 4000); // console.log(score);