From 77b5c8f2621fcab1ea0ce1d953329d0c05f768f5 Mon Sep 17 00:00:00 2001 From: miteruzo Date: Mon, 8 Jun 2026 17:47:19 +0900 Subject: [PATCH] #41 --- frontend/src/pages/GekanatorPage.tsx | 41 ++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/frontend/src/pages/GekanatorPage.tsx b/frontend/src/pages/GekanatorPage.tsx index 7d2b68f..d6f2567 100644 --- a/frontend/src/pages/GekanatorPage.tsx +++ b/frontend/src/pages/GekanatorPage.tsx @@ -306,6 +306,10 @@ const chooseQuestion = ({ const scoredPosts = posts .map (post => ({ post, score: scores.get (post.id) ?? 0 })) .sort ((a, b) => b.score - a.score) + const topScore = scoredPosts[0]?.score ?? 0 + const nearTopCandidates = scoredPosts.filter (item => topScore - item.score <= 2) + const focusCandidates = + nearTopCandidates.length >= 2 && nearTopCandidates.length <= 8 ? nearTopCandidates : [] const signatureFor = ( question: GekanatorQuestion, @@ -333,6 +337,7 @@ const chooseQuestion = ({ const rank = ( questionsToRank: GekanatorQuestion[], candidates: { post: Post; score: number }[], + focusCandidates: { post: Post; score: number }[], ) => { const redundant = redundantSignatures (candidates) const nonTagCount = @@ -349,25 +354,49 @@ const chooseQuestion = ({ if (yes === 0 || no === 0) return null + const focusSignature = signatureFor (question, focusCandidates) + const focusYes = focusSignature.split ('').filter (value => value === '1').length + const focusNo = focusCandidates.length - focusYes + const separatesFocus = focusCandidates.length >= 2 && focusYes > 0 && focusNo > 0 + const splitScore = Math.abs (candidates.length / 2 - yes) + const focusSplitScore = focusCandidates.length >= 2 + ? Math.abs (focusCandidates.length / 2 - focusYes) + : 0 const tagPenalty = question.kind === 'tag' && nonTagCount < 4 ? 12 : 0 const minSide = candidates.length < 10 ? 1 : Math.max (3, candidates.length * .08) const narrowPenalty = yes < minSide || no < minSide ? candidates.length : 0 + const focusPenalty = + focusCandidates.length >= 2 && !(separatesFocus) ? candidates.length * 10 : 0 return { question, - score: splitScore + tagPenalty + narrowPenalty, - narrow: narrowPenalty > 0 } + score: focusPenalty + + focusSplitScore * 20 + + splitScore + + tagPenalty + + narrowPenalty, + narrow: narrowPenalty > 0, + separatesFocus } }) .filter ((item): item is { - question: GekanatorQuestion - score: number - narrow: boolean } => item !== null && Number.isFinite (item.score)) + question: GekanatorQuestion + score: number + narrow: boolean + separatesFocus: boolean } => item !== null && Number.isFinite (item.score)) .sort ((a, b) => a.score - b.score) } const unansweredQuestions = questions.filter (question => !(askedIds.has (question.id))) - const ranked = rank (unansweredQuestions, scoredPosts) + const ranked = rank (unansweredQuestions, scoredPosts, focusCandidates) + + if (focusCandidates.length >= 2) + return ( + ranked.find (item => item.separatesFocus && !(item.narrow)) + ?? ranked.find (item => item.separatesFocus) + ?? ranked.find (item => !(item.narrow)) + ?? ranked[0] + )?.question ?? null return (ranked.find (item => !(item.narrow)) ?? ranked[0])?.question ?? null }