Fix duplicate picks and recursive rating updates

This commit is contained in:
Maciej Pędzich 2023-06-09 12:57:53 +02:00
parent 053e4e0302
commit 5cc46fbd6b
2 changed files with 86 additions and 76 deletions

View File

@ -1,13 +1,13 @@
import { computed, reactive, ref, toRefs } from 'vue';
import { computed, reactive, ref, toRefs, watchEffect } from 'vue';
import glicko2 from 'glicko2-lite';
import photos from '@/photos.json';
import defaultPhotos from '@/photos.json';
import { Database, Photo } from '@/models';
import { randomNumber } from '@/utils/randomNumber';
const db = reactive<Database>(
JSON.parse(localStorage.getItem('db') as string) || {
photos,
photos: defaultPhotos,
votes: []
}
);
@ -24,93 +24,100 @@ const photosForFirstPick = computed(() =>
})
);
export function useVote() {
const pickPhotosForNewVote = () => {
const firstPick =
photosForFirstPick.value[
randomNumber(0, photosForFirstPick.value.length - 1)
];
const pickPhotosForNewVote = () => {
const firstPick =
photosForFirstPick.value[
randomNumber(0, photosForFirstPick.value.length - 1)
];
const photosForSecondPick = db.photos.filter(({ fileName }) => {
const votesWithFirstPick = db.votes.filter(({ photos }) =>
photos.includes(firstPick.fileName)
);
const photosForSecondPick = db.photos.filter(({ fileName }) => {
const votesWithFirstPick = db.votes.filter(({ photos }) =>
photos.includes(firstPick.fileName)
);
const fileNamesToExclude = [
...new Set(votesWithFirstPick.flatMap(({ photos }) => photos))
];
const fileNamesToExclude = [
...new Set(
votesWithFirstPick
.flatMap(({ photos }) => photos)
.concat([firstPick.fileName])
)
];
return !fileNamesToExclude.includes(fileName);
});
return !fileNamesToExclude.includes(fileName);
});
const secondPick =
photosForSecondPick[randomNumber(0, photosForSecondPick.length - 1)];
const secondPick =
photosForSecondPick[randomNumber(0, photosForSecondPick.length - 1)];
photosInCurrentVote.value.push(firstPick, secondPick);
};
photosInCurrentVote.value = [firstPick, secondPick];
};
const submitVote = (result: 0 | 0.5 | 1) => {
const fileNames = [...photosInCurrentVote.value].map(
({ fileName }) => fileName
) as [string, string];
const submitVote = (result: 0 | 0.5 | 1) => {
const fileNames = ([...photosInCurrentVote.value] as Photo[]).map(
({ fileName }) => fileName
) as [string, string];
db.votes.unshift({ photos: fileNames, result });
photosInCurrentVote.value = [];
pickPhotosForNewVote();
};
db.votes.unshift({ photos: fileNames, result });
const updateRatings = () => {
const twelveMostRecentVotes = db.votes.slice(0, 12).reverse();
if (db.votes.length % 12 === 0) {
updateRatings();
}
const votesGrouppedByPhotos = twelveMostRecentVotes.reduce((obj, vote) => {
const [firstPick, secondPick] = vote.photos;
pickPhotosForNewVote();
};
const firstPhoto = db.photos.find(
({ fileName }) => fileName === firstPick
)!;
const secondPhoto = db.photos.find(
({ fileName }) => fileName === secondPick
)!;
const updateRatings = () => {
const twelveMostRecentVotes = db.votes.slice(0, 12).reverse();
const firstPhotoOpponentParams = [
secondPhoto.rating,
secondPhoto.rd,
vote.result
] as [number, number, number];
const votesGrouppedByPhotos = twelveMostRecentVotes.reduce((obj, vote) => {
const [firstFileName, secondFileName] = vote.photos;
const secondPhotoOpponentParams = [
firstPhoto.rating,
firstPhoto.rd,
1 - vote.result
] as [number, number, number];
const firstPhoto = db.photos.find(
({ fileName }) => fileName === firstFileName
)!;
if (obj[firstPick]) {
obj[firstPick].push(firstPhotoOpponentParams);
} else {
obj[firstPick] = [firstPhotoOpponentParams];
}
const secondPhoto = db.photos.find(
({ fileName }) => fileName === secondFileName
)!;
if (obj[secondPick]) {
obj[secondPick].push(secondPhotoOpponentParams);
} else {
obj[secondPick] = [secondPhotoOpponentParams];
}
const firstPhotoOpponentParams = [
secondPhoto.rating,
secondPhoto.rd,
vote.result
] as [number, number, number];
return obj;
}, {} as Record<string, [number, number, number][]>);
const secondPhotoOpponentParams = [
firstPhoto.rating,
firstPhoto.rd,
1 - vote.result
] as [number, number, number];
for (const [photoFileName, votes] of Object.entries(
votesGrouppedByPhotos
)) {
const photo = db.photos.find(
({ fileName }) => fileName === photoFileName
)!;
const newRatingParams = glicko2(photo.rating, photo.rd, photo.vol, votes);
Object.assign(photo, newRatingParams);
if (obj[firstFileName]) {
obj[firstFileName].push(firstPhotoOpponentParams);
} else {
obj[firstFileName] = [firstPhotoOpponentParams];
}
};
if (obj[secondFileName]) {
obj[secondFileName].push(secondPhotoOpponentParams);
} else {
obj[secondFileName] = [secondPhotoOpponentParams];
}
return obj;
}, {} as Record<string, [number, number, number][]>);
for (const [photoFileName, votes] of Object.entries(votesGrouppedByPhotos)) {
const photo = db.photos.find(({ fileName }) => fileName === photoFileName)!;
const newRatingParams = glicko2(photo.rating, photo.rd, photo.vol, votes);
Object.assign(photo, newRatingParams);
}
};
watchEffect(() => localStorage.setItem('db', JSON.stringify(db)));
export function useVote() {
return {
photosInCurrentVote,
photosForFirstPick,

File diff suppressed because one or more lines are too long