Skip to content

Commit

Permalink
quiz infinite compact repr (#889)
Browse files Browse the repository at this point in the history
Resolves #888
  • Loading branch information
kavigupta authored Feb 1, 2025
1 parent 0bddd3d commit 069c13e
Show file tree
Hide file tree
Showing 12 changed files with 117 additions and 9 deletions.
1 change: 1 addition & 0 deletions react/src/page_template/settings-vector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,7 @@ type NotIncludedInSettingsVector = (
| StatCategorySavedIndeterminateKey
| StatCategoryExpandedKey
| 'theme' | 'colorblind_mode' | 'clean_background'
| 'juxtastatCompactEmoji'
)

// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- No deprecations yet
Expand Down
2 changes: 2 additions & 0 deletions react/src/page_template/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export type SettingsDictionary = {
clean_background: boolean
temperature_unit: TemperatureUnit
mobile_article_pointers: MobileArticlePointers
juxtastatCompactEmoji: boolean
}
/* eslint-enable no-restricted-syntax */
& { [G in GroupIdentifier as StatGroupKey<G>]: boolean }
Expand Down Expand Up @@ -95,6 +96,7 @@ export const defaultSettingsList = [
...statPathsWithExtra.map(statPath => [`expanded__${statPath}`, false] as const),
['temperature_unit', 'fahrenheit'],
['mobile_article_pointers', 'pointer_in_class'],
['juxtastatCompactEmoji', false],
] as const

// Having a default settings object allows us to statically check that we have default values for all settings
Expand Down
105 changes: 97 additions & 8 deletions react/src/quiz/quiz-result.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import React, { CSSProperties, ReactNode, useContext, useEffect, useRef, useStat
import { isFirefox, isMobile } from 'react-device-detect'

import { JuxtastatInfiniteButton } from '../components/quiz-panel'
import { CheckboxSetting } from '../components/sidebar'
import { Statistic } from '../components/table'
import { Navigator } from '../navigation/Navigator'
import { JuxtastatColors } from '../page_template/color-themes'
import { useColors, useJuxtastatColors } from '../page_template/colors'
import { Settings } from '../page_template/settings'
import { Settings, useSetting } from '../page_template/settings'
import { getVector, VectorSettingsDictionary } from '../page_template/settings-vector'
import { allGroups, allYears, statParents, StatPath } from '../page_template/statistic-tree'

Expand Down Expand Up @@ -185,7 +186,28 @@ interface ShareButtonProps {
medal?: Medal
}

function ShareButton({ buttonRef, todayName, correctPattern, quizKind, medal }: ShareButtonProps): ReactNode {
function ShareButton(props: ShareButtonProps): ReactNode {
const [compactRepr] = useSetting('juxtastatCompactEmoji')
const asb = <ActualShareButton {...props} compactRepr={compactRepr} />
if (props.correctPattern.length <= maxPerLine) {
return asb
}
return (
<div>
<div style={{ display: 'flex', justifyContent: 'center' }}>
<CheckboxSetting
name="Compact emoji representation"
settingKey="juxtastatCompactEmoji"
testId="juxtastatCompactEmoji"
/>
</div>
<div className="gap_small"></div>
{asb}
</div>
)
}

function ActualShareButton({ buttonRef, todayName, correctPattern, quizKind, medal, compactRepr }: (ShareButtonProps & { compactRepr: boolean })): ReactNode {
const colors = useColors()
const juxtaColors = useJuxtastatColors()
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- We need to check the condition for browser compatibility.
Expand All @@ -198,7 +220,7 @@ function ShareButton({ buttonRef, todayName, correctPattern, quizKind, medal }:
style={buttonStyle(colors.hueColors.green)}
ref={buttonRef}
onClick={async () => {
const [text, url] = await summary(juxtaColors, todayName, correctPattern, quizKind, medal)
const [text, url] = await summary(juxtaColors, todayName, correctPattern, quizKind, medal, compactRepr)

async function copyToClipboard(): Promise<void> {
await navigator.clipboard.writeText(`${text}\n${url}`)
Expand Down Expand Up @@ -372,6 +394,7 @@ function summaryTexts(correctPattern: CorrectPattern, quizKind: QuizKind): [stri
}

export function Summary(props: { correctPattern: CorrectPattern, quizKind: QuizKind }): ReactNode {
const [compactRepr] = useSetting('juxtastatCompactEmoji')
const juxtaColors = useJuxtastatColors()
const [prefix, summaryText] = summaryTexts(props.correctPattern, props.quizKind)
const show = `${prefix} ${summaryText}`
Expand All @@ -380,7 +403,7 @@ export function Summary(props: { correctPattern: CorrectPattern, quizKind: QuizK
<span className="serif quiz_summary" id="quiz-result-summary-words">{show}</span>
<div id="quiz-result-summary-emoji">
{
redAndGreenSquares(juxtaColors, props.correctPattern).map((line, index) => (
redAndGreenSquares(juxtaColors, props.correctPattern, compactRepr).map((line, index) => (
<div className="serif quiz_summary" key={index}>{line}</div>
))
}
Expand All @@ -393,7 +416,7 @@ function renderMedalAsString(medal: Medal): string {
return ['🥇 Personal Best!', '🥈 Personal 2nd Best!', '🥉 Personal 3rd Best!'][medal - 1]
}

export async function summary(juxtaColors: JuxtastatColors, todayName: string | undefined, correctPattern: CorrectPattern, quizKind: QuizKind, medal: Medal | undefined): Promise<[string, string]> {
export async function summary(juxtaColors: JuxtastatColors, todayName: string | undefined, correctPattern: CorrectPattern, quizKind: QuizKind, medal: Medal | undefined, compactRepr: boolean): Promise<[string, string]> {
// wordle-style summary
const [, summaryText] = summaryTexts(correctPattern, quizKind)
let text = nameOfQuizKind(quizKind)
Expand All @@ -405,7 +428,7 @@ export async function summary(juxtaColors: JuxtastatColors, todayName: string |
text += '\n'
text += '\n'

text += redAndGreenSquares(juxtaColors, correctPattern).join('\n')
text += redAndGreenSquares(juxtaColors, correctPattern, compactRepr).join('\n')

if (medal !== undefined) {
text += '\n'
Expand Down Expand Up @@ -625,11 +648,77 @@ function settingsOverrides(questionStatPath?: StatPath): Partial<VectorSettingsD
])
}

export function redAndGreenSquares(juxtaColors: JuxtastatColors, correctPattern: CorrectPattern): string[] {
function emojiForCount(count: number): string[] {
if (count >= 10) {
return [...emojiForCount(count / 10), ...emojiForCount(count % 10)]
}
switch (count) {
case 0:
return ['0️⃣']
case 1:
return ['1️⃣']
case 2:
return ['2️⃣']
case 3:
return ['3️⃣']
case 4:
return ['4️⃣']
case 5:
return ['5️⃣']
case 6:
return ['6️⃣']
case 7:
return ['7️⃣']
case 8:
return ['8️⃣']
case 9:
return ['9️⃣']
}
throw new Error(`unexpected count ${count}`)
}

function toCompactRepresentation(correctPattern: CorrectPattern, correct: string, incorrect: string): string[] {
// RRGGG -> R<emoji 2>G<emoji 3>
const result: string[][] = [[]]
let currentSymbol: boolean | undefined = undefined
let currentCount = 0

const add = (symbol: boolean | undefined, count: number): void => {
if (symbol === undefined) {
return
}
const last = result[result.length - 1]
last.push(symbol ? correct : incorrect)
if (count > 1) {
last.push(...emojiForCount(count))
}
if (last.length >= maxPerLine) {
result.push([])
}
}

for (const x of correctPattern.map(t => t ? true : false)) {
if (x === currentSymbol) {
currentCount += 1
}
else {
add(currentSymbol, currentCount)
currentSymbol = x
currentCount = 1
}
}
add(currentSymbol, currentCount)
return result.map(line => line.join(''))
}

export function redAndGreenSquares(juxtaColors: JuxtastatColors, correctPattern: CorrectPattern, compactRepr: boolean): string[] {
if (compactRepr) {
return toCompactRepresentation(correctPattern, juxtaColors.correctEmoji, juxtaColors.incorrectEmoji)
}
if (correctPattern.length > maxPerLine) {
const lines = []
for (let i = 0; i < correctPattern.length; i += maxPerLine) {
lines.push(redAndGreenSquares(juxtaColors, correctPattern.slice(i, i + maxPerLine))[0])
lines.push(redAndGreenSquares(juxtaColors, correctPattern.slice(i, i + maxPerLine), compactRepr)[0])
}
return lines
}
Expand Down
18 changes: 17 additions & 1 deletion react/test/quiz_infinite_test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Selector } from 'testcafe'

import { runQuery } from './quiz_test_template'
import { collectCorrectJuxtaInfiniteAnswersFixture, friendsText, provideAnswers, quizFixture, withMockedClipboard } from './quiz_test_utils'
import { collectCorrectJuxtaInfiniteAnswersFixture, friendsText, provideAnswers, quizFixture, quizScreencap, withMockedClipboard } from './quiz_test_utils'
import {
target,
safeReload,
Expand Down Expand Up @@ -149,6 +149,22 @@ test('19-correct', async (t) => {
])
// low bit order first: 1111,1111 1111,1111 1111,0000 000[0,0000] This becomes FF FF 0F 00
await t.expect(await juxtastatInfiniteTable()).eql(`7|${seedStr}|FFFF0F00|20|27\n`)
await quizScreencap(t)
await t.click(Selector('[data-test-id=juxtastatCompactEmoji]'))
await quizScreencap(t)
await safeReload(t) // Copied! -> Copy Link
await t.expect(await Selector('#quiz-result-summary-emoji').innerText).eql(
'🟩2️⃣0️⃣🟥7️⃣',
)
await t.expect(await copyLines(t)).eql([
'Juxtastat Infinite 20/∞',
'',
'🟩2️⃣0️⃣🟥7️⃣',
'',
'🥇 Personal Best!',
'',
`https://juxtastat.org/${param}`,
])
})

async function doNotReportPartial(t: TestController): Promise<void> {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 069c13e

Please sign in to comment.