グカネータ軽量モード廃止 (#370) #372
@@ -30,7 +30,7 @@ export type GekanatorQuestionSource =
|
|||||||
| 'ai_generated'
|
| 'ai_generated'
|
||||||
| 'admin_curated'
|
| 'admin_curated'
|
||||||
|
|
||||||
export type GekanatorPerformanceMode = 'lite' | 'normal'
|
export type GekanatorPerformanceMode = 'normal'
|
||||||
|
|
||||||
export type GekanatorQuestionCondition =
|
export type GekanatorQuestionCondition =
|
||||||
| { type: 'tag'; key: string }
|
| { type: 'tag'; key: string }
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ import type { FC } from 'react'
|
|||||||
import type { GekanatorAnswerLog,
|
import type { GekanatorAnswerLog,
|
||||||
GekanatorAnswerValue,
|
GekanatorAnswerValue,
|
||||||
GekanatorExtraQuestion,
|
GekanatorExtraQuestion,
|
||||||
GekanatorPerformanceMode,
|
|
||||||
GekanatorQuestionCondition,
|
GekanatorQuestionCondition,
|
||||||
GekanatorQuestionKind,
|
GekanatorQuestionKind,
|
||||||
GekanatorQuestion,
|
GekanatorQuestion,
|
||||||
@@ -162,7 +161,6 @@ const confidenceTemperature = 6
|
|||||||
const gameStorageKey = 'gekanator:game:v1'
|
const gameStorageKey = 'gekanator:game:v1'
|
||||||
const recentGamesStorageKey = 'gekanator:recent-games:v1'
|
const recentGamesStorageKey = 'gekanator:recent-games:v1'
|
||||||
const backgroundMotionStorageKey = 'gekanator:background-motion:v1'
|
const backgroundMotionStorageKey = 'gekanator:background-motion:v1'
|
||||||
const performanceModeStorageKey = 'gekanator:performance-mode:v1'
|
|
||||||
const maxQuestionSuggestionsPerGame = 3
|
const maxQuestionSuggestionsPerGame = 3
|
||||||
const maxStoredRecentGames = 12
|
const maxStoredRecentGames = 12
|
||||||
const mascotAssetByState: Record<MascotState, string> = {
|
const mascotAssetByState: Record<MascotState, string> = {
|
||||||
@@ -391,14 +389,8 @@ const storeRecentGameSummary = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const loadBackgroundMotionMode = (
|
const loadBackgroundMotionMode = (): BackgroundMotionMode => {
|
||||||
performanceMode?: GekanatorPerformanceMode,
|
const fallbackMode = 'on'
|
||||||
): BackgroundMotionMode => {
|
|
||||||
const fallbackMode =
|
|
||||||
performanceMode === 'lite' ? 'off'
|
|
||||||
: performanceMode === 'normal' ? 'on'
|
|
||||||
: detectDefaultPerformanceMode () === 'lite' ? 'off'
|
|
||||||
: 'on'
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
const raw = localStorage.getItem (backgroundMotionStorageKey)
|
const raw = localStorage.getItem (backgroundMotionStorageKey)
|
||||||
@@ -414,38 +406,6 @@ const loadBackgroundMotionMode = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const detectDefaultPerformanceMode = (): GekanatorPerformanceMode => {
|
|
||||||
if (typeof window === 'undefined')
|
|
||||||
return 'normal'
|
|
||||||
|
|
||||||
const isMobileWidth =
|
|
||||||
typeof window.matchMedia === 'function'
|
|
||||||
? window.matchMedia ('(max-width: 767px)').matches
|
|
||||||
: window.innerWidth <= 767
|
|
||||||
const memory = (navigator as Navigator & { deviceMemory?: number }).deviceMemory
|
|
||||||
if ((typeof memory === 'number' && memory <= 4) || isMobileWidth)
|
|
||||||
return 'lite'
|
|
||||||
|
|
||||||
return 'normal'
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const loadPerformanceMode = (): GekanatorPerformanceMode => {
|
|
||||||
try
|
|
||||||
{
|
|
||||||
const raw = localStorage.getItem (performanceModeStorageKey)
|
|
||||||
if (raw === 'lite' || raw === 'normal')
|
|
||||||
return raw
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return detectDefaultPerformanceMode ()
|
|
||||||
}
|
|
||||||
|
|
||||||
return detectDefaultPerformanceMode ()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const resettableExtraQuestionState = (): {
|
const resettableExtraQuestionState = (): {
|
||||||
extraQuestions: GekanatorExtraQuestion[]
|
extraQuestions: GekanatorExtraQuestion[]
|
||||||
extraQuestionAnswers: Record<string, GekanatorAnswerValue>
|
extraQuestionAnswers: Record<string, GekanatorAnswerValue>
|
||||||
@@ -1055,10 +1015,8 @@ const rankedEntriesForCounts = <T extends string | number> (
|
|||||||
const buildQuestionsForCandidateIds = (
|
const buildQuestionsForCandidateIds = (
|
||||||
{ candidateIds,
|
{ candidateIds,
|
||||||
materialIndex,
|
materialIndex,
|
||||||
performanceMode,
|
|
||||||
acceptedQuestions }: { candidateIds: number[]
|
acceptedQuestions }: { candidateIds: number[]
|
||||||
materialIndex: GekanatorQuestionMaterialIndex
|
materialIndex: GekanatorQuestionMaterialIndex
|
||||||
performanceMode: GekanatorPerformanceMode
|
|
||||||
acceptedQuestions: GekanatorQuestion[] },
|
acceptedQuestions: GekanatorQuestion[] },
|
||||||
): GekanatorQuestion[] => {
|
): GekanatorQuestion[] => {
|
||||||
const total = candidateIds.length
|
const total = candidateIds.length
|
||||||
@@ -1089,7 +1047,6 @@ const buildQuestionsForCandidateIds = (
|
|||||||
const monthDay = materialIndex.originalMonthDayByPostId.get (postId)
|
const monthDay = materialIndex.originalMonthDayByPostId.get (postId)
|
||||||
if (monthDay)
|
if (monthDay)
|
||||||
monthDayCounts.set (monthDay, (monthDayCounts.get (monthDay) ?? 0) + 1)
|
monthDayCounts.set (monthDay, (monthDayCounts.get (monthDay) ?? 0) + 1)
|
||||||
if (performanceMode === 'normal')
|
|
||||||
materialIndex.titleTermsByPostId.get (postId)?.forEach (term =>
|
materialIndex.titleTermsByPostId.get (postId)?.forEach (term =>
|
||||||
titleTermCounts.set (term, (titleTermCounts.get (term) ?? 0) + 1))
|
titleTermCounts.set (term, (titleTermCounts.get (term) ?? 0) + 1))
|
||||||
const titleLength = materialIndex.titleLengthByPostId.get (postId) ?? 0
|
const titleLength = materialIndex.titleLengthByPostId.get (postId) ?? 0
|
||||||
@@ -1098,14 +1055,8 @@ const buildQuestionsForCandidateIds = (
|
|||||||
++asciiCount
|
++asciiCount
|
||||||
})
|
})
|
||||||
|
|
||||||
const tagCap =
|
const tagCap = total >= 120 ? 128 : 96
|
||||||
performanceMode === 'lite'
|
const titleTermCap = total >= 80 ? 10 : total >= 24 ? 14 : 20
|
||||||
? total >= 80 ? 96 : 64
|
|
||||||
: total >= 120 ? 128 : 96
|
|
||||||
const titleTermCap =
|
|
||||||
performanceMode === 'lite'
|
|
||||||
? 0
|
|
||||||
: total >= 80 ? 10 : total >= 24 ? 14 : 20
|
|
||||||
const factCap = total >= 80 ? 8 : 12
|
const factCap = total >= 80 ? 8 : 12
|
||||||
const sortedLengths = [...titleLengths].sort ((a, b) => a - b)
|
const sortedLengths = [...titleLengths].sort ((a, b) => a - b)
|
||||||
const titleLengthMedian = sortedLengths[Math.floor (sortedLengths.length / 2)] ?? 0
|
const titleLengthMedian = sortedLengths[Math.floor (sortedLengths.length / 2)] ?? 0
|
||||||
@@ -1181,7 +1132,6 @@ const buildQuestionsForCandidateIds = (
|
|||||||
materialIndex }))
|
materialIndex }))
|
||||||
})
|
})
|
||||||
|
|
||||||
if (performanceMode === 'normal')
|
|
||||||
rankedEntriesForCounts ({ counts: titleTermCounts, total, cap: titleTermCap })
|
rankedEntriesForCounts ({ counts: titleTermCounts, total, cap: titleTermCap })
|
||||||
.filter (([term]) => String (term).length <= 24)
|
.filter (([term]) => String (term).length <= 24)
|
||||||
.forEach (([term]) => {
|
.forEach (([term]) => {
|
||||||
@@ -1615,8 +1565,8 @@ const contradictionPenaltyFor = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const chooseQuestion = ({
|
const chooseQuestion = (
|
||||||
posts,
|
{ posts,
|
||||||
questions,
|
questions,
|
||||||
scores,
|
scores,
|
||||||
answers,
|
answers,
|
||||||
@@ -1624,11 +1574,8 @@ const chooseQuestion = ({
|
|||||||
gameSeed,
|
gameSeed,
|
||||||
recentFirstQuestionPenaltyById,
|
recentFirstQuestionPenaltyById,
|
||||||
userPriorWeights,
|
userPriorWeights,
|
||||||
performanceMode,
|
|
||||||
materialIndex,
|
materialIndex,
|
||||||
matchIndex,
|
matchIndex }: { posts: Post[]
|
||||||
}: {
|
|
||||||
posts: Post[]
|
|
||||||
questions: GekanatorQuestion[]
|
questions: GekanatorQuestion[]
|
||||||
scores: Map<number, number>
|
scores: Map<number, number>
|
||||||
answers: GekanatorAnswerLog[]
|
answers: GekanatorAnswerLog[]
|
||||||
@@ -1636,20 +1583,15 @@ const chooseQuestion = ({
|
|||||||
gameSeed: string
|
gameSeed: string
|
||||||
recentFirstQuestionPenaltyById: Map<string, number>
|
recentFirstQuestionPenaltyById: Map<string, number>
|
||||||
userPriorWeights: Map<number, number>
|
userPriorWeights: Map<number, number>
|
||||||
performanceMode: GekanatorPerformanceMode
|
|
||||||
materialIndex: GekanatorQuestionMaterialIndex
|
materialIndex: GekanatorQuestionMaterialIndex
|
||||||
matchIndex: GekanatorMatchIndex
|
matchIndex: GekanatorMatchIndex },
|
||||||
}): GekanatorQuestion | null => {
|
): GekanatorQuestion | null => {
|
||||||
const candidateIds = posts.map (post => post.id)
|
|
||||||
const candidateIdSet = new Set (candidateIds)
|
|
||||||
const dynamicMatchIndex = new Map<string, Set<number>> ()
|
const dynamicMatchIndex = new Map<string, Set<number>> ()
|
||||||
|
|
||||||
const invertedSignature = (signature: string): string =>
|
const invertedSignature = (signature: string): string =>
|
||||||
signature.replace (/[01]/g, value => value === '1' ? '0' : '1')
|
signature.replace (/[01]/g, value => value === '1' ? '0' : '1')
|
||||||
|
|
||||||
const redundantSignatures = (
|
const redundantSignatures = (candidates: Post[]): Set<string> => {
|
||||||
candidates: Post[],
|
|
||||||
): Set<string> => {
|
|
||||||
const signatures = new Set<string> ()
|
const signatures = new Set<string> ()
|
||||||
questions
|
questions
|
||||||
.filter (question => askedIds.has (question.id))
|
.filter (question => askedIds.has (question.id))
|
||||||
@@ -1668,89 +1610,6 @@ const chooseQuestion = ({
|
|||||||
return signatures
|
return signatures
|
||||||
}
|
}
|
||||||
|
|
||||||
if (performanceMode === 'lite')
|
|
||||||
{
|
|
||||||
const nonTagCount =
|
|
||||||
questions.filter (question => askedIds.has (question.id) && question.kind !== 'tag').length
|
|
||||||
const ranked = questions
|
|
||||||
.filter (question => !(askedIds.has (question.id)))
|
|
||||||
.map (question => {
|
|
||||||
if (isQuestionHardFilteredAfterAnswers (question, answers))
|
|
||||||
return null
|
|
||||||
|
|
||||||
const yes = matchingPostCountInIds ({
|
|
||||||
candidateIds,
|
|
||||||
candidateIdSet,
|
|
||||||
posts,
|
|
||||||
materialIndex,
|
|
||||||
matchIndex,
|
|
||||||
question,
|
|
||||||
dynamicMatchIndex })
|
|
||||||
const no = posts.length - yes
|
|
||||||
if (yes === 0 || no === 0)
|
|
||||||
return null
|
|
||||||
|
|
||||||
const splitScore = Math.abs (posts.length / 2 - yes) / posts.length
|
|
||||||
const minSide = posts.length < 10 ? 1 : Math.max (2, Math.floor (posts.length * .08))
|
|
||||||
const narrowPenalty = yes < minSide || no < minSide ? .18 : 0
|
|
||||||
const tagPenalty = question.kind === 'tag' && nonTagCount < 3 ? .1 : 0
|
|
||||||
const contradictionPenalty = contradictionPenaltyFor ({ question, answers })
|
|
||||||
const sourceBonus = sourcePriorityOffset (question)
|
|
||||||
const priorityBonus = priorityWeightOffset (question)
|
|
||||||
const categoryPenalty = questionCategoryPenalty (question, answers.length, 0)
|
|
||||||
|
|
||||||
return {
|
|
||||||
question,
|
|
||||||
score: splitScore * 100
|
|
||||||
+ narrowPenalty
|
|
||||||
+ tagPenalty
|
|
||||||
+ contradictionPenalty
|
|
||||||
+ sourceBonus
|
|
||||||
+ priorityBonus
|
|
||||||
+ categoryPenalty,
|
|
||||||
narrow: narrowPenalty > 0 }
|
|
||||||
})
|
|
||||||
.filter ((item): item is {
|
|
||||||
question: GekanatorQuestion
|
|
||||||
score: number
|
|
||||||
narrow: boolean } => item !== null && Number.isFinite (item.score))
|
|
||||||
.sort ((a, b) => {
|
|
||||||
if (a.score !== b.score)
|
|
||||||
return a.score - b.score
|
|
||||||
|
|
||||||
return a.question.id.localeCompare (b.question.id)
|
|
||||||
})
|
|
||||||
const pool = (
|
|
||||||
ranked.some (item => !(item.narrow))
|
|
||||||
? ranked.filter (item => !(item.narrow))
|
|
||||||
: ranked)
|
|
||||||
.slice (0, 8)
|
|
||||||
|
|
||||||
if (pool.length === 0)
|
|
||||||
return null
|
|
||||||
|
|
||||||
const bestScore = pool[0]?.score ?? 0
|
|
||||||
const weightedPool = pool.map (item => ({
|
|
||||||
...item,
|
|
||||||
weight: Math.exp ((bestScore - item.score) / 1.6) }))
|
|
||||||
const totalPoolWeight =
|
|
||||||
weightedPool.reduce ((sum, item) => sum + item.weight, 0) || 1
|
|
||||||
const seed = `${ gameSeed }:lite:${ [...askedIds].sort ().join ('|') }:${
|
|
||||||
weightedPool.map (item => `${ item.question.id }:${ item.score.toFixed (4) }`).join ('|')
|
|
||||||
}`
|
|
||||||
const target = deterministicUnitFloat (seed) * totalPoolWeight
|
|
||||||
let cumulative = 0
|
|
||||||
|
|
||||||
for (const item of weightedPool)
|
|
||||||
{
|
|
||||||
cumulative += item.weight
|
|
||||||
if (target <= cumulative)
|
|
||||||
return item.question
|
|
||||||
}
|
|
||||||
|
|
||||||
return weightedPool[weightedPool.length - 1]?.question ?? null
|
|
||||||
}
|
|
||||||
|
|
||||||
const scoredPosts = posts
|
const scoredPosts = posts
|
||||||
.map (post => ({ post, score: scores.get (post.id) ?? 0 }))
|
.map (post => ({ post, score: scores.get (post.id) ?? 0 }))
|
||||||
.sort ((a, b) => b.score - a.score)
|
.sort ((a, b) => b.score - a.score)
|
||||||
@@ -2361,7 +2220,6 @@ const nextQuestionPlanFor = (
|
|||||||
gameSeed,
|
gameSeed,
|
||||||
recentFirstQuestionPenaltyById,
|
recentFirstQuestionPenaltyById,
|
||||||
userPriorWeights,
|
userPriorWeights,
|
||||||
performanceMode,
|
|
||||||
materialIndex,
|
materialIndex,
|
||||||
matchIndex,
|
matchIndex,
|
||||||
lastGuessQuestionCount,
|
lastGuessQuestionCount,
|
||||||
@@ -2376,7 +2234,6 @@ const nextQuestionPlanFor = (
|
|||||||
gameSeed: string
|
gameSeed: string
|
||||||
recentFirstQuestionPenaltyById: Map<string, number>
|
recentFirstQuestionPenaltyById: Map<string, number>
|
||||||
userPriorWeights: Map<number, number>
|
userPriorWeights: Map<number, number>
|
||||||
performanceMode: GekanatorPerformanceMode
|
|
||||||
materialIndex: GekanatorQuestionMaterialIndex
|
materialIndex: GekanatorQuestionMaterialIndex
|
||||||
matchIndex: GekanatorMatchIndex
|
matchIndex: GekanatorMatchIndex
|
||||||
lastGuessQuestionCount: number
|
lastGuessQuestionCount: number
|
||||||
@@ -2439,7 +2296,6 @@ const nextQuestionPlanFor = (
|
|||||||
buildQuestionsForCandidateIds ({
|
buildQuestionsForCandidateIds ({
|
||||||
candidateIds: scopePosts.map (post => post.id),
|
candidateIds: scopePosts.map (post => post.id),
|
||||||
materialIndex,
|
materialIndex,
|
||||||
performanceMode,
|
|
||||||
acceptedQuestions })
|
acceptedQuestions })
|
||||||
|
|
||||||
if (eligiblePosts.length === 1)
|
if (eligiblePosts.length === 1)
|
||||||
@@ -2505,7 +2361,6 @@ const nextQuestionPlanFor = (
|
|||||||
gameSeed,
|
gameSeed,
|
||||||
recentFirstQuestionPenaltyById,
|
recentFirstQuestionPenaltyById,
|
||||||
userPriorWeights,
|
userPriorWeights,
|
||||||
performanceMode,
|
|
||||||
materialIndex,
|
materialIndex,
|
||||||
matchIndex })
|
matchIndex })
|
||||||
|
|
||||||
@@ -3108,10 +2963,8 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
const canPersistGame = user !== null
|
const canPersistGame = user !== null
|
||||||
const [recentGames, setRecentGames] = useState<RecentGameSummary[]> (
|
const [recentGames, setRecentGames] = useState<RecentGameSummary[]> (
|
||||||
() => loadRecentGames ())
|
() => loadRecentGames ())
|
||||||
const [performanceMode] =
|
|
||||||
useState<GekanatorPerformanceMode> (() => loadPerformanceMode ())
|
|
||||||
const [backgroundMotionMode, setBackgroundMotionMode] = useState<BackgroundMotionMode> (
|
const [backgroundMotionMode, setBackgroundMotionMode] = useState<BackgroundMotionMode> (
|
||||||
() => loadBackgroundMotionMode (loadPerformanceMode ()))
|
() => loadBackgroundMotionMode ())
|
||||||
const [prefersReducedMotion, setPrefersReducedMotion] = useState (false)
|
const [prefersReducedMotion, setPrefersReducedMotion] = useState (false)
|
||||||
const [gameSeed, setGameSeed] = useState (
|
const [gameSeed, setGameSeed] = useState (
|
||||||
storedGame?.gameSeed ?? createGameSeed ())
|
storedGame?.gameSeed ?? createGameSeed ())
|
||||||
@@ -3328,17 +3181,6 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
}
|
}
|
||||||
}, [backgroundMotionMode])
|
}, [backgroundMotionMode])
|
||||||
|
|
||||||
useEffect (() => {
|
|
||||||
try
|
|
||||||
{
|
|
||||||
localStorage.setItem (performanceModeStorageKey, performanceMode)
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}, [performanceMode])
|
|
||||||
|
|
||||||
const askedQuestionById = useMemo (
|
const askedQuestionById = useMemo (
|
||||||
() => new Map (askedQuestionBank.map (question => [question.id, question])),
|
() => new Map (askedQuestionBank.map (question => [question.id, question])),
|
||||||
[askedQuestionBank])
|
[askedQuestionBank])
|
||||||
@@ -3361,9 +3203,6 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
() => new Map (scoringQuestions.map (question => [question.id, question])),
|
() => new Map (scoringQuestions.map (question => [question.id, question])),
|
||||||
[scoringQuestions])
|
[scoringQuestions])
|
||||||
const recentFirstQuestionPenaltyById = useMemo (() => {
|
const recentFirstQuestionPenaltyById = useMemo (() => {
|
||||||
if (performanceMode === 'lite')
|
|
||||||
return new Map<string, number> ()
|
|
||||||
|
|
||||||
const penalties = new Map<string, number> ()
|
const penalties = new Map<string, number> ()
|
||||||
|
|
||||||
recentGames.forEach ((game, index) => {
|
recentGames.forEach ((game, index) => {
|
||||||
@@ -3376,12 +3215,10 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
return penalties
|
return penalties
|
||||||
}, [performanceMode, recentGames])
|
}, [recentGames])
|
||||||
const userPriorWeights = useMemo (
|
const userPriorWeights = useMemo (
|
||||||
() => performanceMode === 'lite'
|
() => userPriorWeightsFor (posts, recentGames),
|
||||||
? new Map<number, number> ()
|
[posts, recentGames])
|
||||||
: userPriorWeightsFor (posts, recentGames),
|
|
||||||
[performanceMode, posts, recentGames])
|
|
||||||
const availablePosts = useMemo (
|
const availablePosts = useMemo (
|
||||||
() => posts.filter (post => !(rejectedPostIds.has (post.id))),
|
() => posts.filter (post => !(rejectedPostIds.has (post.id))),
|
||||||
[posts, rejectedPostIds])
|
[posts, rejectedPostIds])
|
||||||
@@ -3397,7 +3234,6 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
gameSeed,
|
gameSeed,
|
||||||
recentFirstQuestionPenaltyById,
|
recentFirstQuestionPenaltyById,
|
||||||
userPriorWeights,
|
userPriorWeights,
|
||||||
performanceMode,
|
|
||||||
materialIndex,
|
materialIndex,
|
||||||
matchIndex: acceptedQuestionMatchIndex,
|
matchIndex: acceptedQuestionMatchIndex,
|
||||||
lastGuessQuestionCount,
|
lastGuessQuestionCount,
|
||||||
@@ -3405,7 +3241,7 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
winningRunStartAnswerCount }),
|
winningRunStartAnswerCount }),
|
||||||
[posts, eligiblePosts, availablePosts, acceptedQuestions, scores,
|
[posts, eligiblePosts, availablePosts, acceptedQuestions, scores,
|
||||||
answers, askedIds, gameSeed, recentFirstQuestionPenaltyById,
|
answers, askedIds, gameSeed, recentFirstQuestionPenaltyById,
|
||||||
userPriorWeights, performanceMode, materialIndex, acceptedQuestionMatchIndex,
|
userPriorWeights, materialIndex, acceptedQuestionMatchIndex,
|
||||||
lastGuessQuestionCount, winningRunTargetId, winningRunStartAnswerCount])
|
lastGuessQuestionCount, winningRunTargetId, winningRunStartAnswerCount])
|
||||||
const winningRunTargetPost = useMemo (
|
const winningRunTargetPost = useMemo (
|
||||||
() => questionPlan.winningRunTargetId === null
|
() => questionPlan.winningRunTargetId === null
|
||||||
@@ -3458,35 +3294,28 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
const reviewCorrectPost =
|
const reviewCorrectPost =
|
||||||
posts.find (post => post.id === reviewCorrectPostId) ?? null
|
posts.find (post => post.id === reviewCorrectPostId) ?? null
|
||||||
const effectiveResultWon =
|
const effectiveResultWon =
|
||||||
resultWon ?? (
|
resultWon
|
||||||
reviewGuessedPostId !== null
|
?? ((reviewGuessedPostId !== null && reviewCorrectPostId !== null)
|
||||||
&& reviewCorrectPostId !== null
|
|
||||||
? reviewGuessedPostId === reviewCorrectPostId
|
? reviewGuessedPostId === reviewCorrectPostId
|
||||||
: null)
|
: null)
|
||||||
const effectiveBackgroundMotionMode =
|
const effectiveBackgroundMotionMode =
|
||||||
performanceMode === 'lite'
|
backgroundMotionMode === 'off'
|
||||||
? 'off'
|
? 'off'
|
||||||
: backgroundMotionMode === 'off'
|
: (prefersReducedMotion
|
||||||
? 'off'
|
|
||||||
: prefersReducedMotion
|
|
||||||
? 'calm'
|
? 'calm'
|
||||||
: backgroundMotionMode
|
: backgroundMotionMode)
|
||||||
const backgroundPosts = useMemo (
|
const backgroundPosts = useMemo (
|
||||||
() => performanceMode === 'lite'
|
() => backgroundPostsFor ({
|
||||||
? []
|
|
||||||
: backgroundPostsFor ({
|
|
||||||
phase,
|
phase,
|
||||||
eligiblePosts,
|
eligiblePosts,
|
||||||
availablePosts,
|
availablePosts,
|
||||||
displayedGuess,
|
displayedGuess,
|
||||||
reviewCorrectPost,
|
reviewCorrectPost,
|
||||||
reviewGuessedPost }),
|
reviewGuessedPost }),
|
||||||
[performanceMode, phase, eligiblePosts, availablePosts, displayedGuess,
|
[phase, eligiblePosts, availablePosts, displayedGuess, reviewCorrectPost,
|
||||||
reviewCorrectPost, reviewGuessedPost])
|
reviewGuessedPost])
|
||||||
const backgroundVisualSeed =
|
const backgroundVisualSeed =
|
||||||
performanceMode === 'lite'
|
`${ gameSeed }:${ phase }:${ answers.length }:${ activeGuessId ?? '' }:${
|
||||||
? ''
|
|
||||||
: `${ gameSeed }:${ phase }:${ answers.length }:${ activeGuessId ?? '' }:${
|
|
||||||
questionPlan.question?.id ?? ''
|
questionPlan.question?.id ?? ''
|
||||||
}:${ questionPlan.questionMode ?? '' }:${ winningRunQuestionsAsked }:${
|
}:${ questionPlan.questionMode ?? '' }:${ winningRunQuestionsAsked }:${
|
||||||
rejectedPostIds.size
|
rejectedPostIds.size
|
||||||
@@ -3617,7 +3446,6 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
let recoveredQuestions = buildQuestionsForCandidateIds ({
|
let recoveredQuestions = buildQuestionsForCandidateIds ({
|
||||||
candidateIds: recoveredEligiblePosts.map (post => post.id),
|
candidateIds: recoveredEligiblePosts.map (post => post.id),
|
||||||
materialIndex,
|
materialIndex,
|
||||||
performanceMode,
|
|
||||||
acceptedQuestions })
|
acceptedQuestions })
|
||||||
let recoveredScoringQuestions = mergeQuestions ([
|
let recoveredScoringQuestions = mergeQuestions ([
|
||||||
...recoveredQuestions,
|
...recoveredQuestions,
|
||||||
@@ -3643,7 +3471,6 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
recoveredQuestions = buildQuestionsForCandidateIds ({
|
recoveredQuestions = buildQuestionsForCandidateIds ({
|
||||||
candidateIds: recoveredEligiblePosts.map (post => post.id),
|
candidateIds: recoveredEligiblePosts.map (post => post.id),
|
||||||
materialIndex,
|
materialIndex,
|
||||||
performanceMode,
|
|
||||||
acceptedQuestions })
|
acceptedQuestions })
|
||||||
recoveredScoringQuestions = mergeQuestions ([
|
recoveredScoringQuestions = mergeQuestions ([
|
||||||
...recoveredQuestions,
|
...recoveredQuestions,
|
||||||
@@ -3667,7 +3494,6 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
gameSeed,
|
gameSeed,
|
||||||
recentFirstQuestionPenaltyById,
|
recentFirstQuestionPenaltyById,
|
||||||
userPriorWeights,
|
userPriorWeights,
|
||||||
performanceMode,
|
|
||||||
materialIndex,
|
materialIndex,
|
||||||
matchIndex: acceptedQuestionMatchIndex })
|
matchIndex: acceptedQuestionMatchIndex })
|
||||||
const fallbackQuestion = nextQuestion ?? chooseFallbackQuestion ({
|
const fallbackQuestion = nextQuestion ?? chooseFallbackQuestion ({
|
||||||
@@ -3735,7 +3561,6 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
}, [
|
}, [
|
||||||
posts,
|
posts,
|
||||||
gameSeed,
|
gameSeed,
|
||||||
performanceMode,
|
|
||||||
materialIndex,
|
materialIndex,
|
||||||
acceptedQuestions,
|
acceptedQuestions,
|
||||||
acceptedQuestionMatchIndex,
|
acceptedQuestionMatchIndex,
|
||||||
@@ -3806,7 +3631,6 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
gameSeed,
|
gameSeed,
|
||||||
recentFirstQuestionPenaltyById,
|
recentFirstQuestionPenaltyById,
|
||||||
userPriorWeights,
|
userPriorWeights,
|
||||||
performanceMode,
|
|
||||||
materialIndex,
|
materialIndex,
|
||||||
matchIndex: acceptedQuestionMatchIndex,
|
matchIndex: acceptedQuestionMatchIndex,
|
||||||
lastGuessQuestionCount,
|
lastGuessQuestionCount,
|
||||||
@@ -3840,7 +3664,6 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
gameSeed,
|
gameSeed,
|
||||||
recentFirstQuestionPenaltyById,
|
recentFirstQuestionPenaltyById,
|
||||||
userPriorWeights,
|
userPriorWeights,
|
||||||
performanceMode,
|
|
||||||
materialIndex,
|
materialIndex,
|
||||||
matchIndex: acceptedQuestionMatchIndex,
|
matchIndex: acceptedQuestionMatchIndex,
|
||||||
lastGuessQuestionCount,
|
lastGuessQuestionCount,
|
||||||
@@ -4071,7 +3894,6 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
gameSeed,
|
gameSeed,
|
||||||
recentFirstQuestionPenaltyById,
|
recentFirstQuestionPenaltyById,
|
||||||
userPriorWeights,
|
userPriorWeights,
|
||||||
performanceMode,
|
|
||||||
materialIndex,
|
materialIndex,
|
||||||
matchIndex: acceptedQuestionMatchIndex,
|
matchIndex: acceptedQuestionMatchIndex,
|
||||||
lastGuessQuestionCount,
|
lastGuessQuestionCount,
|
||||||
@@ -4286,7 +4108,6 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
<title>{`グカネータ | ${ SITE_TITLE }`}</title>
|
<title>{`グカネータ | ${ SITE_TITLE }`}</title>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
|
|
||||||
{performanceMode !== 'lite' && (
|
|
||||||
<GekanatorBackdrop
|
<GekanatorBackdrop
|
||||||
posts={backgroundPosts}
|
posts={backgroundPosts}
|
||||||
mascotAsset={mascotAsset}
|
mascotAsset={mascotAsset}
|
||||||
@@ -4295,7 +4116,7 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
visualSeed={backgroundVisualSeed}
|
visualSeed={backgroundVisualSeed}
|
||||||
motionMode={effectiveBackgroundMotionMode}
|
motionMode={effectiveBackgroundMotionMode}
|
||||||
winningRunTargetPost={winningRunActive ? winningRunTargetPost : null}
|
winningRunTargetPost={winningRunActive ? winningRunTargetPost : null}
|
||||||
winningRunQuestionCount={winningRunQuestionsAsked}/>)}
|
winningRunQuestionCount={winningRunQuestionsAsked}/>
|
||||||
|
|
||||||
<div className="relative z-10 mx-auto max-w-4xl space-y-6">
|
<div className="relative z-10 mx-auto max-w-4xl space-y-6">
|
||||||
<header className="flex flex-wrap items-end justify-between gap-3">
|
<header className="flex flex-wrap items-end justify-between gap-3">
|
||||||
@@ -4305,7 +4126,6 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-wrap justify-end gap-2">
|
<div className="flex flex-wrap justify-end gap-2">
|
||||||
{performanceMode === 'normal' && (
|
|
||||||
<div className="rounded-full border border-yellow-300 bg-white/80 px-2 py-1
|
<div className="rounded-full border border-yellow-300 bg-white/80 px-2 py-1
|
||||||
text-xs shadow-sm backdrop-blur dark:border-red-800
|
text-xs shadow-sm backdrop-blur dark:border-red-800
|
||||||
dark:bg-red-950/75">
|
dark:bg-red-950/75">
|
||||||
@@ -4330,7 +4150,7 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
<span className="ml-2 text-[11px] text-neutral-500 dark:text-neutral-400">
|
<span className="ml-2 text-[11px] text-neutral-500 dark:text-neutral-400">
|
||||||
端末設定により控えめ表示
|
端末設定により控えめ表示
|
||||||
</span>)}
|
</span>)}
|
||||||
</div>)}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
@@ -4608,7 +4428,7 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
|
|
||||||
{reviewGuessedPostId !== null && reviewCorrectPostId !== null && (
|
{reviewGuessedPostId !== null && reviewCorrectPostId !== null && (
|
||||||
<p className="text-sm text-neutral-600 dark:text-neutral-300">
|
<p className="text-sm text-neutral-600 dark:text-neutral-300">
|
||||||
判定: {reviewGuessedPostId === reviewCorrectPostId ? '当たり' : '違ひ'}
|
判定: {reviewGuessedPostId === reviewCorrectPostId ? '当たり' : 'はずれ'}
|
||||||
</p>)}
|
</p>)}
|
||||||
|
|
||||||
{saveMutation.isError && (
|
{saveMutation.isError && (
|
||||||
|
|||||||
新しい課題から参照
ユーザをブロックする