improved social questions

This commit is contained in:
Daniel Bulant 2026-05-04 11:51:41 +02:00
parent 834db89c9e
commit e4e392de51
No known key found for this signature in database
4 changed files with 79 additions and 18 deletions

View file

@ -83,7 +83,11 @@ export type QuizState = {
};
export type PartySocketEvent =
| { type: "party_status"; party: Party|null; members: PartyMemberWithUser[] }
| {
type: "party_status";
party: Party | null;
members: PartyMemberWithUser[];
}
| { type: "member_payload"; fromUserId: string; payload: unknown }
| { type: "error"; message: string }
| { type: "pong" };

View file

@ -14,6 +14,7 @@ export type PartyAnalytics = {
name: string;
artists?: { name: string }[];
albumName?: string;
memberScores?: { userId: string; score: number }[];
}[];
artists?: { name: string }[];
genres?: { name: string }[];
@ -95,12 +96,28 @@ export function getTopClusterArtists(analytics: PartyAnalytics): string[] {
);
}
export function getTopClusterTracks(
analytics: PartyAnalytics,
): Array<{ name: string; artists?: { name: string }[]; albumName?: string }> {
export function getTopClusterTracks(analytics: PartyAnalytics): Array<{
name: string;
artists?: { name: string }[];
albumName?: string;
memberScores?: { userId: string; score: number }[];
}> {
return analytics?.storyClusters?.[0]?.tracks ?? [];
}
export function getTopTrackListener(
track: { memberScores?: { userId: string; score: number }[] },
members: PartyQuestionMember[],
): PartyQuestionMember | null {
const topMember = (track.memberScores ?? [])
.slice()
.sort((a, b) => b.score - a.score)
.at(0);
if (!topMember) return null;
return members.find((member) => member.userId === topMember.userId) ?? null;
}
export function buildOrderedOptions(
values: Array<string | undefined>,
desiredCount: number,
@ -143,6 +160,17 @@ export function getCurrentLeader(
);
}
export function hasClearLeader(quizState: {
scores: Record<string, number>;
}): boolean {
const scores = Object.values(quizState.scores);
if (scores.length < 2) return false;
const ordered = scores.slice().sort((a, b) => b - a);
const [first, second] = ordered;
if (first === undefined || second === undefined) return false;
return first > second;
}
export function getMostDiverseMember(
analytics: PartyAnalytics,
members: PartyQuestionMember[],

View file

@ -5,8 +5,12 @@ import {
getCurrentLeader,
getMostAlignedMember,
getMostDiverseMember,
getTopClusterTracks,
getTopTrackListener,
hasClearLeader,
type PartyAnalytics,
type PartyQuestionMember,
pickRandom,
} from "./question-utils";
export function buildSocialQuestion(
@ -16,35 +20,60 @@ export function buildSocialQuestion(
index: number,
): Question {
type ChoiceQuestion = Extract<Question, { type: "choice" }>;
const leader = getCurrentLeader(quizState, members);
const diverse = getMostDiverseMember(analytics, members);
const aligned = getMostAlignedMember(analytics, members);
const questions: Array<
Omit<ChoiceQuestion, "startTimestamp" | "endTimestamp">
> = [
{
> = [];
const hasMultipleMembers = members.length >= 2;
if (hasMultipleMembers && hasClearLeader(quizState)) {
const leader = getCurrentLeader(quizState, members);
questions.push({
type: "choice",
text: "Who is leading the quiz right now?",
options: buildMemberOptions(leader, members),
correct: 0,
points: 10,
},
{
});
}
if (hasMultipleMembers) {
const diverse = getMostDiverseMember(analytics, members);
questions.push({
type: "choice",
text: "Who looks like the most diverse listener in the party?",
options: buildMemberOptions(diverse, members),
correct: 0,
points: 10,
},
{
});
const aligned = getMostAlignedMember(analytics, members);
questions.push({
type: "choice",
text: "Which member seems most aligned with the rest of the party?",
options: buildMemberOptions(aligned, members),
correct: 0,
points: 10,
},
];
});
}
const topTracks = getTopClusterTracks(analytics);
const randomTrack = pickRandom(topTracks);
if (randomTrack && hasMultipleMembers) {
const topListener = getTopTrackListener(randomTrack, members);
if (topListener) {
questions.push({
type: "choice",
text: `Who listens the most to "${randomTrack.name}"?`,
options: buildMemberOptions(topListener, members),
correct: 0,
points: 10,
});
}
}
if (questions.length === 0) {
throw new Error("Question not found");
}
const question = questions[index % questions.length];
if (!question) throw new Error("Question not found");

View file

@ -101,9 +101,9 @@ export class QuizWorkflow extends ConfiguredInstance {
}
}
break;
}
}
if (receivedPlayers.has(response.playerId)) continue;
if (receivedPlayers.has(response.playerId)) continue;
receivedPlayers.add(response.playerId);
const answeredAt = Date.now();