Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@
],
"homepage": "https://github.com/Drarig29/brackets-viewer.js#readme",
"dependencies": {
"brackets-manager": "^1.6.2",
"brackets-memory-db": "^1.0.4",
"brackets-model": "^1.5.0",
"brackets-manager": "^1.8.0",
"brackets-memory-db": "^1.0.5",
"brackets-model": "^1.6.0",
"i18next": "^21.6.14",
"i18next-browser-languagedetector": "^6.1.4",
"path-browserify": "^1.0.1",
Expand Down
6 changes: 3 additions & 3 deletions src/dom.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Match, ParticipantResult, FinalType, GroupType, Id, MatchGame } from 'brackets-model';
import { Connection, Placement, Ranking, RankingItem } from './types';
import { Match, ParticipantResult, FinalType, GroupType, Id, MatchGame, type RankingItem } from 'brackets-model';
import { Connection, Placement } from './types';
import { isMatchGame, rankingHeader } from './helpers';
import { t } from './lang';

Expand Down Expand Up @@ -239,7 +239,7 @@ export function createCell(data: string | number): HTMLElement {
*
* @param ranking The object containing the ranking.
*/
export function createRankingHeaders(ranking: Ranking): HTMLElement {
export function createRankingHeaders(ranking: RankingItem[]): HTMLElement {
const headers = document.createElement('tr');
const firstItem = ranking[0];

Expand Down
101 changes: 2 additions & 99 deletions src/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Match, ParticipantResult, GroupType, MatchGame } from 'brackets-model';
import { RankingHeader, Ranking, RankingFormula, RankingItem, RankingMap, Side, MatchWithMetadata } from './types';
import { Match, GroupType, MatchGame, RankingItem } from 'brackets-model';
import { RankingHeader, Side, MatchWithMetadata } from './types';
import { t } from './lang';

/**
Expand Down Expand Up @@ -171,103 +171,6 @@ export function rankingHeader(itemName: keyof RankingItem): RankingHeader {
return t(`ranking.${itemName}`, { returnObjects: true }) as RankingHeader;
}

/**
* Calculates the ranking based on a list of matches and a formula.
*
* @param matches The list of matches.
* @param formula The points formula to apply.
*/
export function getRanking(matches: Match[], formula?: RankingFormula): Ranking {
formula = formula || (
(item: RankingItem): number => 3 * item.wins + 1 * item.draws + 0 * item.losses
);

const rankingMap: RankingMap = {};

for (const match of matches) {
processParticipant(rankingMap, formula, match.opponent1, match.opponent2);
processParticipant(rankingMap, formula, match.opponent2, match.opponent1);
}

return createRanking(rankingMap);
}

/**
* Processes a participant and edits the ranking map.
*
* @param rankingMap The ranking map to edit.
* @param formula The points formula to apply.
* @param current The current participant.
* @param other The opponent.
*/
function processParticipant(rankingMap: RankingMap, formula: RankingFormula, current: ParticipantResult | null, other: ParticipantResult | null): void {
if (!current || current.id === null) return;

const state = rankingMap[current.id] || {
rank: 0,
id: 0,
played: 0,
wins: 0,
draws: 0,
losses: 0,
forfeits: 0,
scoreFor: 0,
scoreAgainst: 0,
scoreDifference: 0,
points: 0,
};

state.id = current.id;

if (current.forfeit || current.result)
state.played++;

if (current.result === 'win')
state.wins++;

if (current.result === 'draw')
state.draws++;

if (current.result === 'loss')
state.losses++;

if (current.forfeit)
state.forfeits++;

state.scoreFor += current.score || 0;
state.scoreAgainst += other && other.score || 0;
state.scoreDifference = state.scoreFor - state.scoreAgainst;

state.points = formula(state);

rankingMap[current.id] = state;
}

/**
* Creates the final ranking based on a ranking map. (Sort + Total points)
*
* @param rankingMap The ranking map (object).
*/
function createRanking(rankingMap: RankingMap): RankingItem[] {
const ranking = Object.values(rankingMap).sort((a, b) => a.points !== b.points
? b.points - a.points
: a.played !== b.played
? b.played - a.played
: b.scoreDifference - a.scoreDifference);

const rank = {
value: 0,
lastPoints: -1,
};

for (const item of ranking) {
item.rank = rank.lastPoints !== item.points ? ++rank.value : rank.value;
rank.lastPoints = item.points;
}

return ranking;
}

/**
* Indicates whether the input is a match.
*
Expand Down
13 changes: 6 additions & 7 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import './style.scss';
import { Participant, Match, ParticipantResult, Stage, Status, GroupType, FinalType, Id } from 'brackets-model';
import { splitBy, getRanking, getOriginAbbreviation, findRoot, completeWithBlankMatches, sortBy, isMatchGame, isMatch, splitByWithLeftovers } from './helpers';
import { Participant, Match, ParticipantResult, Stage, Status, GroupType, FinalType, Id, type RankingItem } from 'brackets-model';
import { splitBy, getOriginAbbreviation, findRoot, completeWithBlankMatches, sortBy, isMatchGame, isMatch, splitByWithLeftovers } from './helpers';
import * as dom from './dom';
import * as lang from './lang';
import { Locale } from './lang';
Expand All @@ -9,7 +9,6 @@ import {
Config,
OriginHint,
ParticipantContainers,
RankingItem,
RoundNameGetter,
ViewerData,
ParticipantImage,
Expand Down Expand Up @@ -78,7 +77,7 @@ export class BracketsViewer {
showPopoverOnMatchLabelClick: config?.showPopoverOnMatchLabelClick ?? true,
highlightParticipantOnHover: config?.highlightParticipantOnHover ?? true,
showRankingTable: config?.showRankingTable ?? true,
rankingFormula: config?.rankingFormula,
rankingFormula: config?.rankingFormula ?? ((item): number => 3 * item.wins + 1 * item.draws + 0 * item.losses),
};

if (config?.onMatchClick)
Expand Down Expand Up @@ -482,13 +481,13 @@ export class BracketsViewer {
}

/**
* Creates a ranking table based on matches of a round-robin stage.
* Creates a ranking table for a group of a round-robin stage.
*
* @param matches The list of matches.
* @param matches The list of matches in the group.
*/
private createRanking(matches: Match[]): HTMLElement {
const table = dom.createTable();
const ranking = getRanking(matches, this.config.rankingFormula);
const ranking = helpers.getRanking(matches, this.config.rankingFormula!);

table.append(dom.createRankingHeaders(ranking));

Expand Down
36 changes: 2 additions & 34 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Stage, Match, MatchGame, Participant, GroupType, FinalType, Id, StageType } from 'brackets-model';
import { Stage, Match, MatchGame, Participant, GroupType, FinalType, StageType, RankingItem, RankingFormula } from 'brackets-model';
import { CallbackFunction, FormConfiguration } from './form';
import { InMemoryDatabase } from 'brackets-memory-db';
import { BracketsViewer } from './main';
Expand Down Expand Up @@ -190,7 +190,7 @@ export interface Config {
highlightParticipantOnHover?: boolean,

/**
* Whether to show a ranking table on round-robin stages.
* Whether to show a ranking table in each group of a round-robin stage.
*
* @default true
*/
Expand Down Expand Up @@ -263,23 +263,6 @@ export interface Connection {
connectNext?: ConnectionType,
}

/**
* An item of the ranking.
*/
export interface RankingItem {
rank: number,
id: Id,
played: number,
wins: number,
draws: number,
losses: number,
forfeits: number,
scoreFor: number,
scoreAgainst: number,
scoreDifference: number,
points: number,
}

/**
* Contains information about a header of the ranking and its tooltip.
*/
Expand All @@ -288,26 +271,11 @@ export interface RankingHeader {
tooltip: string,
}

/**
* A formula which computes points given a ranking row.
*/
export type RankingFormula = (ranking: RankingItem) => number;

/**
* An object mapping ranking properties to their header.
*/
export type RankingHeaders = Record<keyof RankingItem, RankingHeader>;

/**
* An object mapping a participant id to its row in the ranking.
*/
export type RankingMap = Record<Id, RankingItem>;

/**
* Definition of a ranking.
*/
export type Ranking = RankingItem[];

/**
* Structure containing all the containers for a participant.
*/
Expand Down