diff options
Diffstat (limited to 'frontend/app')
| -rw-r--r-- | frontend/app/api/schema.d.ts | 5 | ||||
| -rw-r--r-- | frontend/app/components/Gaming/CodePopover.tsx | 8 | ||||
| -rw-r--r-- | frontend/app/components/Gaming/ProblemColumn.tsx | 9 | ||||
| -rw-r--r-- | frontend/app/components/Gaming/ProblemColumnContent.tsx | 97 | ||||
| -rw-r--r-- | frontend/app/components/Gaming/RankingTable.tsx | 11 | ||||
| -rw-r--r-- | frontend/app/components/GolfPlayApp.tsx | 1 | ||||
| -rw-r--r-- | frontend/app/components/GolfPlayApps/GolfPlayAppGaming.tsx | 10 | ||||
| -rw-r--r-- | frontend/app/components/GolfWatchApp.tsx | 2 | ||||
| -rw-r--r-- | frontend/app/components/GolfWatchApps/GolfWatchAppGaming1v1.tsx | 14 | ||||
| -rw-r--r-- | frontend/app/components/GolfWatchApps/GolfWatchAppGamingMultiplayer.tsx | 6 | ||||
| -rw-r--r-- | frontend/app/states/play.ts | 24 | ||||
| -rw-r--r-- | frontend/app/states/watch.ts | 24 | ||||
| -rw-r--r-- | frontend/app/types/SupportedLanguage.ts | 1 |
13 files changed, 154 insertions, 58 deletions
diff --git a/frontend/app/api/schema.d.ts b/frontend/app/api/schema.d.ts index af454e8..3f991f0 100644 --- a/frontend/app/api/schema.d.ts +++ b/frontend/app/api/schema.d.ts @@ -188,6 +188,11 @@ export interface components { title: string; /** @example This is a problem */ description: string; + /** + * @example php + * @enum {string} + */ + language: "php" | "swift"; /** @example echo 'hello world'; */ sample_code: string; }; diff --git a/frontend/app/components/Gaming/CodePopover.tsx b/frontend/app/components/Gaming/CodePopover.tsx index a574a77..91245df 100644 --- a/frontend/app/components/Gaming/CodePopover.tsx +++ b/frontend/app/components/Gaming/CodePopover.tsx @@ -2,15 +2,17 @@ import { Popover } from "@base-ui-components/react/popover"; import { faCode, faXmark } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { calcCodeSize } from "../../states/play"; +import type { SupportedLanguage } from "../../types/SupportedLanguage"; import BorderedContainer from "../BorderedContainer"; import CodeBlock from "../Gaming/CodeBlock"; type Props = { code: string; + language: SupportedLanguage; }; -export default function CodePopover({ code }: Props) { - const codeSize = calcCodeSize(code); +export default function CodePopover({ code, language }: Props) { + const codeSize = calcCodeSize(code, language); return ( <Popover.Root> @@ -33,7 +35,7 @@ export default function CodePopover({ code }: Props) { /> </Popover.Close> </div> - <CodeBlock code={code} language="php" /> + <CodeBlock code={code} language={language} /> </BorderedContainer> </Popover.Popup> </Popover.Positioner> diff --git a/frontend/app/components/Gaming/ProblemColumn.tsx b/frontend/app/components/Gaming/ProblemColumn.tsx index 3b7e58c..a355ac4 100644 --- a/frontend/app/components/Gaming/ProblemColumn.tsx +++ b/frontend/app/components/Gaming/ProblemColumn.tsx @@ -1,20 +1,27 @@ +import type { SupportedLanguage } from "../../types/SupportedLanguage"; import TitledColumn from "../TitledColumn"; import ProblemColumnContent from "./ProblemColumnContent"; type Props = { title: string; description: string; + language: SupportedLanguage; sampleCode: string; }; export default function ProblemColumn({ title, description, + language, sampleCode, }: Props) { return ( <TitledColumn title={title}> - <ProblemColumnContent description={description} sampleCode={sampleCode} /> + <ProblemColumnContent + description={description} + sampleCode={sampleCode} + language={language} + /> </TitledColumn> ); } diff --git a/frontend/app/components/Gaming/ProblemColumnContent.tsx b/frontend/app/components/Gaming/ProblemColumnContent.tsx index b85cc6d..0904a98 100644 --- a/frontend/app/components/Gaming/ProblemColumnContent.tsx +++ b/frontend/app/components/Gaming/ProblemColumnContent.tsx @@ -1,14 +1,77 @@ +import type { SupportedLanguage } from "../../types/SupportedLanguage"; import FoldableBorderedContainerWithCaption from "../FoldableBorderedContainerWithCaption"; import CodeBlock from "./CodeBlock"; import InlineCode from "./InlineCode"; +function PhpNotice() { + return ( + <FoldableBorderedContainerWithCaption caption="スコア計算・PHP 環境"> + <div className="text-gray-700 flex flex-col gap-2"> + <p> + スコアはコード中の全 ASCII + 空白文字を除去した後のバイト数です。また、先頭や末尾に置かれた PHP + タグ (<InlineCode code="<?php" />、<InlineCode code="<?" />、 + <InlineCode code="?>" />) はカウントされません。 + </p> + <p> + 同じスコアを出した場合、より提出が早かったプレイヤーの勝ちとなります。 + </p> + <p> + この環境の PHP バージョンは{" "} + <strong className="font-bold">8.4.4</strong> です。 mbstring + を除くほとんどの拡張は無効化されています。 + また、ファイルやネットワークアクセスはできません。 + </p> + <p> + テストの成否は、標準出力へ出力された文字列を比較して判定されます。 + 末尾の改行はあってもなくても構いません。 + 標準エラー出力の内容は無視されますが、fatal error + 等で実行が中断された場合は失敗扱いとなります。 + </p> + <p> + なお、 + <InlineCode code="error_reporting" /> は{" "} + <InlineCode code="E_ALL & ~E_WARNING & ~E_NOTICE & ~E_DEPRECATED" />{" "} + に設定されています。 + </p> + </div> + </FoldableBorderedContainerWithCaption> + ); +} + +function SwiftNotice() { + return ( + <FoldableBorderedContainerWithCaption caption="スコア計算・Swift 環境"> + <div className="text-gray-700 flex flex-col gap-2"> + <p>スコアはコード中の全 ASCII 空白文字を除去した後のバイト数です。</p> + <p> + 同じスコアを出した場合、より提出が早かったプレイヤーの勝ちとなります。 + </p> + <p> + この環境の PHP バージョンは{" "} + <strong className="font-bold">6.1.2</strong> です。 + ファイルアクセスやネットワークアクセスはできません。 + </p> + <p> + テストの成否は、標準出力へ出力された文字列を比較して判定されます。 + 末尾の改行はあってもなくても構いません。 + 標準エラー出力の内容は無視されますが、fatal error + 等で実行が中断された場合は失敗扱いとなります。 + </p> + </div> + </FoldableBorderedContainerWithCaption> + ); +} + type Props = { description: string; + language: SupportedLanguage; sampleCode: string; }; export default function ProblemColumnContent({ description, + language, sampleCode, }: Props) { return ( @@ -19,39 +82,9 @@ export default function ProblemColumnContent({ </pre> </FoldableBorderedContainerWithCaption> <FoldableBorderedContainerWithCaption caption="サンプルコード"> - <CodeBlock code={sampleCode} language="php" /> - </FoldableBorderedContainerWithCaption> - <FoldableBorderedContainerWithCaption caption="スコア計算・PHP 環境"> - <div className="text-gray-700 flex flex-col gap-2"> - <p> - スコアはコード中の全 ASCII - 空白文字を除去した後のバイト数です。また、先頭や末尾に置かれた PHP - タグ (<InlineCode code="<?php" />、<InlineCode code="<?" />、 - <InlineCode code="?>" />) はカウントされません。 - </p> - <p> - 同じスコアを出した場合、より提出が早かったプレイヤーの勝ちとなります。 - </p> - <p> - この環境の PHP バージョンは{" "} - <strong className="font-bold">8.4.4</strong> です。 mbstring - を除くほとんどの拡張は無効化されています。 - また、ファイルやネットワークアクセスはできません。 - </p> - <p> - テストの成否は、標準出力へ出力された文字列を比較して判定されます。 - 末尾の改行はあってもなくても構いません。 - 標準エラー出力の内容は無視されますが、fatal error - 等で実行が中断された場合は失敗扱いとなります。 - </p> - <p> - なお、 - <InlineCode code="error_reporting" /> は{" "} - <InlineCode code="E_ALL & ~E_WARNING & ~E_NOTICE & ~E_DEPRECATED" />{" "} - に設定されています。 - </p> - </div> + <CodeBlock code={sampleCode} language={language} /> </FoldableBorderedContainerWithCaption> + {language === "php" ? <PhpNotice /> : <SwiftNotice />} </> ); } diff --git a/frontend/app/components/Gaming/RankingTable.tsx b/frontend/app/components/Gaming/RankingTable.tsx index 3368f60..7359d40 100644 --- a/frontend/app/components/Gaming/RankingTable.tsx +++ b/frontend/app/components/Gaming/RankingTable.tsx @@ -1,6 +1,7 @@ import { useAtomValue } from "jotai"; import React from "react"; import { rankingAtom } from "../../states/watch"; +import type { SupportedLanguage } from "../../types/SupportedLanguage"; import CodePopover from "./CodePopover"; function TableHeaderCell({ children }: { children: React.ReactNode }) { @@ -29,7 +30,11 @@ function formatUnixTimestamp(timestamp: number) { return `${year}-${month}-${day} ${hours}:${minutes}`; } -export default function RankingTable() { +type Props = { + problemLanguage: SupportedLanguage; +}; + +export default function RankingTable({ problemLanguage }: Props) { const ranking = useAtomValue(rankingAtom); return ( @@ -57,7 +62,9 @@ export default function RankingTable() { {formatUnixTimestamp(entry.submitted_at)} </TableBodyCell> <TableBodyCell> - {entry.code && <CodePopover code={entry.code} />} + {entry.code && ( + <CodePopover code={entry.code} language={problemLanguage} /> + )} </TableBodyCell> </tr> ))} diff --git a/frontend/app/components/GolfPlayApp.tsx b/frontend/app/components/GolfPlayApp.tsx index e74edc7..6c77f8c 100644 --- a/frontend/app/components/GolfPlayApp.tsx +++ b/frontend/app/components/GolfPlayApp.tsx @@ -136,6 +136,7 @@ export default function GolfPlayApp({ game, player, initialGameState }: Props) { playerProfile={playerProfile} problemTitle={game.problem.title} problemDescription={game.problem.description} + problemLanguage={game.problem.language} sampleCode={game.problem.sample_code} initialCode={initialGameState.code} onCodeChange={onCodeChange} diff --git a/frontend/app/components/GolfPlayApps/GolfPlayAppGaming.tsx b/frontend/app/components/GolfPlayApps/GolfPlayAppGaming.tsx index 86b2379..9eab91e 100644 --- a/frontend/app/components/GolfPlayApps/GolfPlayAppGaming.tsx +++ b/frontend/app/components/GolfPlayApps/GolfPlayAppGaming.tsx @@ -8,6 +8,7 @@ import { statusAtom, } from "../../states/play"; import type { PlayerProfile } from "../../types/PlayerProfile"; +import type { SupportedLanguage } from "../../types/SupportedLanguage"; import BorderedContainer from "../BorderedContainer"; import LeftTime from "../Gaming/LeftTime"; import ProblemColumn from "../Gaming/ProblemColumn"; @@ -22,6 +23,7 @@ type Props = { playerProfile: PlayerProfile; problemTitle: string; problemDescription: string; + problemLanguage: SupportedLanguage; sampleCode: string; initialCode: string; onCodeChange: (code: string) => void; @@ -34,6 +36,7 @@ export default function GolfPlayAppGaming({ playerProfile, problemTitle, problemDescription, + problemLanguage, sampleCode, initialCode, onCodeChange, @@ -44,11 +47,13 @@ export default function GolfPlayAppGaming({ const score = useAtomValue(scoreAtom); const status = useAtomValue(statusAtom); - const [codeSize, setCodeSize] = useState(calcCodeSize(initialCode)); + const [codeSize, setCodeSize] = useState( + calcCodeSize(initialCode, problemLanguage), + ); const textareaRef = useRef<HTMLTextAreaElement>(null); const handleTextChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => { - setCodeSize(calcCodeSize(e.target.value)); + setCodeSize(calcCodeSize(e.target.value, problemLanguage)); if (!isFinished) { onCodeChange(e.target.value); } @@ -91,6 +96,7 @@ export default function GolfPlayAppGaming({ <ProblemColumn title={problemTitle} description={problemDescription} + language={problemLanguage} sampleCode={sampleCode} /> <TitledColumn title="ソースコード"> diff --git a/frontend/app/components/GolfWatchApp.tsx b/frontend/app/components/GolfWatchApp.tsx index a8b4630..41b5a01 100644 --- a/frontend/app/components/GolfWatchApp.tsx +++ b/frontend/app/components/GolfWatchApp.tsx @@ -137,6 +137,7 @@ export default function GolfWatchApp({ playerProfileB={playerProfileB} problemTitle={game.problem.title} problemDescription={game.problem.description} + problemLanguage={game.problem.language} sampleCode={game.problem.sample_code} /> ) : ( @@ -144,6 +145,7 @@ export default function GolfWatchApp({ gameDisplayName={game.display_name} problemTitle={game.problem.title} problemDescription={game.problem.description} + problemLanguage={game.problem.language} sampleCode={game.problem.sample_code} /> ); diff --git a/frontend/app/components/GolfWatchApps/GolfWatchAppGaming1v1.tsx b/frontend/app/components/GolfWatchApps/GolfWatchAppGaming1v1.tsx index 54a5895..f06728d 100644 --- a/frontend/app/components/GolfWatchApps/GolfWatchAppGaming1v1.tsx +++ b/frontend/app/components/GolfWatchApps/GolfWatchAppGaming1v1.tsx @@ -7,6 +7,7 @@ import { latestGameStatesAtom, } from "../../states/watch"; import type { PlayerProfile } from "../../types/PlayerProfile"; +import type { SupportedLanguage } from "../../types/SupportedLanguage"; import FoldableBorderedContainerWithCaption from "../FoldableBorderedContainerWithCaption"; import CodeBlock from "../Gaming/CodeBlock"; import LeftTime from "../Gaming/LeftTime"; @@ -24,6 +25,7 @@ type Props = { playerProfileB: PlayerProfile | null; problemTitle: string; problemDescription: string; + problemLanguage: SupportedLanguage; sampleCode: string; }; @@ -33,6 +35,7 @@ export default function GolfWatchAppGaming1v1({ playerProfileB, problemTitle, problemDescription, + problemLanguage, sampleCode, }: Props) { const gameStateKind = useAtomValue(gameStateKindAtom); @@ -50,8 +53,8 @@ export default function GolfWatchAppGaming1v1({ const scoreB = stateB?.score ?? null; const statusB = stateB?.status ?? "none"; - const codeSizeA = calcCodeSize(codeA); - const codeSizeB = calcCodeSize(codeB); + const codeSizeA = calcCodeSize(codeA, problemLanguage); + const codeSizeB = calcCodeSize(codeB, problemLanguage); const gameResultKind = checkGameResultKind(gameStateKind, stateA, stateB); @@ -125,15 +128,16 @@ export default function GolfWatchAppGaming1v1({ <FoldableBorderedContainerWithCaption caption={`コードサイズ: ${codeSizeA}`} > - <CodeBlock code={codeA} language="php" /> + <CodeBlock code={codeA} language={problemLanguage} /> </FoldableBorderedContainerWithCaption> </TitledColumn> <TitledColumn title={problemTitle} className="order-1 md:order-2"> <ProblemColumnContent description={problemDescription} + language={problemLanguage} sampleCode={sampleCode} /> - <RankingTable /> + <RankingTable problemLanguage={problemLanguage} /> </TitledColumn> <TitledColumn title={<SubmitStatusLabel status={statusB} />} @@ -142,7 +146,7 @@ export default function GolfWatchAppGaming1v1({ <FoldableBorderedContainerWithCaption caption={`コードサイズ: ${codeSizeB}`} > - <CodeBlock code={codeB} language="php" /> + <CodeBlock code={codeB} language={problemLanguage} /> </FoldableBorderedContainerWithCaption> </TitledColumn> </ThreeColumnLayout> diff --git a/frontend/app/components/GolfWatchApps/GolfWatchAppGamingMultiplayer.tsx b/frontend/app/components/GolfWatchApps/GolfWatchAppGamingMultiplayer.tsx index b1d6520..22c6df2 100644 --- a/frontend/app/components/GolfWatchApps/GolfWatchAppGamingMultiplayer.tsx +++ b/frontend/app/components/GolfWatchApps/GolfWatchAppGamingMultiplayer.tsx @@ -1,5 +1,6 @@ import { useAtomValue } from "jotai"; import { gamingLeftTimeSecondsAtom } from "../../states/watch"; +import type { SupportedLanguage } from "../../types/SupportedLanguage"; import LeftTime from "../Gaming/LeftTime"; import ProblemColumn from "../Gaming/ProblemColumn"; import RankingTable from "../Gaming/RankingTable"; @@ -10,6 +11,7 @@ type Props = { gameDisplayName: string; problemTitle: string; problemDescription: string; + problemLanguage: SupportedLanguage; sampleCode: string; }; @@ -17,6 +19,7 @@ export default function GolfWatchAppGamingMultiplayer({ gameDisplayName, problemTitle, problemDescription, + problemLanguage, sampleCode, }: Props) { const leftTimeSeconds = useAtomValue(gamingLeftTimeSecondsAtom)!; @@ -35,10 +38,11 @@ export default function GolfWatchAppGamingMultiplayer({ <ProblemColumn title={problemTitle} description={problemDescription} + language={problemLanguage} sampleCode={sampleCode} /> <TitledColumn title="順位表"> - <RankingTable /> + <RankingTable problemLanguage={problemLanguage} /> </TitledColumn> </TwoColumnLayout> </div> diff --git a/frontend/app/states/play.ts b/frontend/app/states/play.ts index 79b25c7..22d338c 100644 --- a/frontend/app/states/play.ts +++ b/frontend/app/states/play.ts @@ -1,5 +1,6 @@ import { atom } from "jotai"; import type { components } from "../api/schema"; +import type { SupportedLanguage } from "../types/SupportedLanguage"; const gameStartedAtAtom = atom<number | null>(null); export const setGameStartedAtAtom = atom(null, (_, set, value: number | null) => @@ -101,12 +102,23 @@ export const setLatestGameStateAtom = atom( }, ); -export function calcCodeSize(code: string): number { - const trimmed = code - .replace(/\s+/g, "") - .replace(/^<\?php/, "") - .replace(/^<\?/, "") - .replace(/\?>$/, ""); +function cleanCode(code: string, language: SupportedLanguage) { + if (language === "php") { + return code + .replace(/\s+/g, "") + .replace(/^<\?php/, "") + .replace(/^<\?/, "") + .replace(/\?>$/, ""); + } else { + return code.replace(/\s+/g, ""); + } +} + +export function calcCodeSize( + code: string, + language: SupportedLanguage, +): number { + const trimmed = cleanCode(code, language); const utf8Encoded = new TextEncoder().encode(trimmed); return utf8Encoded.length; } diff --git a/frontend/app/states/watch.ts b/frontend/app/states/watch.ts index 2c255f4..50fa425 100644 --- a/frontend/app/states/watch.ts +++ b/frontend/app/states/watch.ts @@ -1,5 +1,6 @@ import { atom } from "jotai"; import type { components } from "../api/schema"; +import type { SupportedLanguage } from "../types/SupportedLanguage"; const gameStartedAtAtom = atom<number | null>(null); export const setGameStartedAtAtom = atom(null, (_, set, value: number | null) => @@ -84,12 +85,23 @@ export const setLatestGameStatesAtom = atom( }, ); -export function calcCodeSize(code: string): number { - const trimmed = code - .replace(/\s+/g, "") - .replace(/^<\?php/, "") - .replace(/^<\?/, "") - .replace(/\?>$/, ""); +function cleanCode(code: string, language: SupportedLanguage) { + if (language === "php") { + return code + .replace(/\s+/g, "") + .replace(/^<\?php/, "") + .replace(/^<\?/, "") + .replace(/\?>$/, ""); + } else { + return code.replace(/\s+/g, ""); + } +} + +export function calcCodeSize( + code: string, + language: SupportedLanguage, +): number { + const trimmed = cleanCode(code, language); const utf8Encoded = new TextEncoder().encode(trimmed); return utf8Encoded.length; } diff --git a/frontend/app/types/SupportedLanguage.ts b/frontend/app/types/SupportedLanguage.ts new file mode 100644 index 0000000..1d427a2 --- /dev/null +++ b/frontend/app/types/SupportedLanguage.ts @@ -0,0 +1 @@ +export type SupportedLanguage = "php" | "swift"; |
