Update matchmaking
This commit is contained in:
@@ -51,4 +51,4 @@ jobs:
|
||||
docker compose up -d
|
||||
|
||||
- name: Cleanup
|
||||
run: rm -rf /deploy/build
|
||||
run: rm -rf /deploy/build &
|
||||
|
||||
+42
-22
@@ -76,8 +76,37 @@ function lensFor(asset: AssetRow, map: Map<number, LensRef>): LensRef | null {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a weighted random pairing. Assets closer in Elo are more
|
||||
* likely to be paired. Uses inverse-square weighting on Elo difference.
|
||||
* Inverse-uncertainty proxy for a static-quality asset: each played match shrinks the term, so
|
||||
* under-played assets are preferentially surfaced.
|
||||
*/
|
||||
function uncertaintyWeight(matches: number): number {
|
||||
return 1 / (1 + matches);
|
||||
}
|
||||
|
||||
/**
|
||||
* Outcome-entropy proxy: close-Elo pairs have more uncertain outcomes and thus carry more
|
||||
* information per match. Inverse-square on the diff keeps the tails non-zero so outliers still
|
||||
* get sampled occasionally.
|
||||
*/
|
||||
function proximityWeight(eloA: number, eloB: number): number {
|
||||
const diff = eloA - eloB;
|
||||
return 1 / (1 + (diff * diff) / 30000);
|
||||
}
|
||||
|
||||
function weightedPick<T>(items: T[], weights: number[]): T {
|
||||
const total = weights.reduce((sum, w) => sum + w, 0);
|
||||
let rand = Math.random() * total;
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
rand -= weights[i];
|
||||
if (rand <= 0) return items[i];
|
||||
}
|
||||
return items[items.length - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a weighted random pairing. Sequentially samples two assets to
|
||||
* approximate joint info-gain: A is drawn by inverse match count (favour
|
||||
* under-played assets), then B by inverse match count × Elo proximity to A.
|
||||
*/
|
||||
export async function generatePairing(
|
||||
voterIp: string,
|
||||
@@ -89,27 +118,18 @@ export async function generatePairing(
|
||||
|
||||
if (activeAssets.length < 2) return null;
|
||||
|
||||
// Pick first asset uniformly at random
|
||||
const idxA = Math.floor(Math.random() * activeAssets.length);
|
||||
const assetA = activeAssets[idxA];
|
||||
const aWeights = activeAssets.map((a) =>
|
||||
uncertaintyWeight(a.matchesProvisional),
|
||||
);
|
||||
const assetA = weightedPick(activeAssets, aWeights);
|
||||
|
||||
// Weight remaining assets by Elo proximity (inverse square)
|
||||
const candidates = activeAssets.filter((_, i) => i !== idxA);
|
||||
const weights = candidates.map((c) => {
|
||||
const diff = Math.abs(c.eloProvisional - assetA.eloProvisional);
|
||||
return 1 / (1 + (diff * diff) / 10000);
|
||||
});
|
||||
|
||||
const totalWeight = weights.reduce((sum, w) => sum + w, 0);
|
||||
let rand = Math.random() * totalWeight;
|
||||
let assetB = candidates[candidates.length - 1];
|
||||
for (let i = 0; i < candidates.length; i++) {
|
||||
rand -= weights[i];
|
||||
if (rand <= 0) {
|
||||
assetB = candidates[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
const candidates = activeAssets.filter((c) => c.id !== assetA.id);
|
||||
const bWeights = candidates.map(
|
||||
(c) =>
|
||||
uncertaintyWeight(c.matchesProvisional) *
|
||||
proximityWeight(c.eloProvisional, assetA.eloProvisional),
|
||||
);
|
||||
const assetB = weightedPick(candidates, bWeights);
|
||||
|
||||
const uuid = crypto.randomUUID();
|
||||
const now = new Date();
|
||||
|
||||
Reference in New Issue
Block a user