improved social questions
This commit is contained in:
parent
834db89c9e
commit
e4e392de51
4 changed files with 79 additions and 18 deletions
|
|
@ -83,7 +83,11 @@ export type QuizState = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PartySocketEvent =
|
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: "member_payload"; fromUserId: string; payload: unknown }
|
||||||
| { type: "error"; message: string }
|
| { type: "error"; message: string }
|
||||||
| { type: "pong" };
|
| { type: "pong" };
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ export type PartyAnalytics = {
|
||||||
name: string;
|
name: string;
|
||||||
artists?: { name: string }[];
|
artists?: { name: string }[];
|
||||||
albumName?: string;
|
albumName?: string;
|
||||||
|
memberScores?: { userId: string; score: number }[];
|
||||||
}[];
|
}[];
|
||||||
artists?: { name: string }[];
|
artists?: { name: string }[];
|
||||||
genres?: { name: string }[];
|
genres?: { name: string }[];
|
||||||
|
|
@ -95,12 +96,28 @@ export function getTopClusterArtists(analytics: PartyAnalytics): string[] {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTopClusterTracks(
|
export function getTopClusterTracks(analytics: PartyAnalytics): Array<{
|
||||||
analytics: PartyAnalytics,
|
name: string;
|
||||||
): Array<{ name: string; artists?: { name: string }[]; albumName?: string }> {
|
artists?: { name: string }[];
|
||||||
|
albumName?: string;
|
||||||
|
memberScores?: { userId: string; score: number }[];
|
||||||
|
}> {
|
||||||
return analytics?.storyClusters?.[0]?.tracks ?? [];
|
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(
|
export function buildOrderedOptions(
|
||||||
values: Array<string | undefined>,
|
values: Array<string | undefined>,
|
||||||
desiredCount: number,
|
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(
|
export function getMostDiverseMember(
|
||||||
analytics: PartyAnalytics,
|
analytics: PartyAnalytics,
|
||||||
members: PartyQuestionMember[],
|
members: PartyQuestionMember[],
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,12 @@ import {
|
||||||
getCurrentLeader,
|
getCurrentLeader,
|
||||||
getMostAlignedMember,
|
getMostAlignedMember,
|
||||||
getMostDiverseMember,
|
getMostDiverseMember,
|
||||||
|
getTopClusterTracks,
|
||||||
|
getTopTrackListener,
|
||||||
|
hasClearLeader,
|
||||||
type PartyAnalytics,
|
type PartyAnalytics,
|
||||||
type PartyQuestionMember,
|
type PartyQuestionMember,
|
||||||
|
pickRandom,
|
||||||
} from "./question-utils";
|
} from "./question-utils";
|
||||||
|
|
||||||
export function buildSocialQuestion(
|
export function buildSocialQuestion(
|
||||||
|
|
@ -16,35 +20,60 @@ export function buildSocialQuestion(
|
||||||
index: number,
|
index: number,
|
||||||
): Question {
|
): Question {
|
||||||
type ChoiceQuestion = Extract<Question, { type: "choice" }>;
|
type ChoiceQuestion = Extract<Question, { type: "choice" }>;
|
||||||
const leader = getCurrentLeader(quizState, members);
|
|
||||||
const diverse = getMostDiverseMember(analytics, members);
|
|
||||||
const aligned = getMostAlignedMember(analytics, members);
|
|
||||||
|
|
||||||
const questions: Array<
|
const questions: Array<
|
||||||
Omit<ChoiceQuestion, "startTimestamp" | "endTimestamp">
|
Omit<ChoiceQuestion, "startTimestamp" | "endTimestamp">
|
||||||
> = [
|
> = [];
|
||||||
{
|
|
||||||
|
const hasMultipleMembers = members.length >= 2;
|
||||||
|
if (hasMultipleMembers && hasClearLeader(quizState)) {
|
||||||
|
const leader = getCurrentLeader(quizState, members);
|
||||||
|
questions.push({
|
||||||
type: "choice",
|
type: "choice",
|
||||||
text: "Who is leading the quiz right now?",
|
text: "Who is leading the quiz right now?",
|
||||||
options: buildMemberOptions(leader, members),
|
options: buildMemberOptions(leader, members),
|
||||||
correct: 0,
|
correct: 0,
|
||||||
points: 10,
|
points: 10,
|
||||||
},
|
});
|
||||||
{
|
}
|
||||||
|
|
||||||
|
if (hasMultipleMembers) {
|
||||||
|
const diverse = getMostDiverseMember(analytics, members);
|
||||||
|
questions.push({
|
||||||
type: "choice",
|
type: "choice",
|
||||||
text: "Who looks like the most diverse listener in the party?",
|
text: "Who looks like the most diverse listener in the party?",
|
||||||
options: buildMemberOptions(diverse, members),
|
options: buildMemberOptions(diverse, members),
|
||||||
correct: 0,
|
correct: 0,
|
||||||
points: 10,
|
points: 10,
|
||||||
},
|
});
|
||||||
{
|
|
||||||
|
const aligned = getMostAlignedMember(analytics, members);
|
||||||
|
questions.push({
|
||||||
type: "choice",
|
type: "choice",
|
||||||
text: "Which member seems most aligned with the rest of the party?",
|
text: "Which member seems most aligned with the rest of the party?",
|
||||||
options: buildMemberOptions(aligned, members),
|
options: buildMemberOptions(aligned, members),
|
||||||
correct: 0,
|
correct: 0,
|
||||||
points: 10,
|
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];
|
const question = questions[index % questions.length];
|
||||||
if (!question) throw new Error("Question not found");
|
if (!question) throw new Error("Question not found");
|
||||||
|
|
|
||||||
|
|
@ -101,9 +101,9 @@ export class QuizWorkflow extends ConfiguredInstance {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (receivedPlayers.has(response.playerId)) continue;
|
if (receivedPlayers.has(response.playerId)) continue;
|
||||||
|
|
||||||
receivedPlayers.add(response.playerId);
|
receivedPlayers.add(response.playerId);
|
||||||
const answeredAt = Date.now();
|
const answeredAt = Date.now();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue