aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--frontend/app/api/schema.d.ts5
-rw-r--r--frontend/app/components/Gaming/CodePopover.tsx8
-rw-r--r--frontend/app/components/Gaming/ProblemColumn.tsx9
-rw-r--r--frontend/app/components/Gaming/ProblemColumnContent.tsx97
-rw-r--r--frontend/app/components/Gaming/RankingTable.tsx11
-rw-r--r--frontend/app/components/GolfPlayApp.tsx1
-rw-r--r--frontend/app/components/GolfPlayApps/GolfPlayAppGaming.tsx10
-rw-r--r--frontend/app/components/GolfWatchApp.tsx2
-rw-r--r--frontend/app/components/GolfWatchApps/GolfWatchAppGaming1v1.tsx14
-rw-r--r--frontend/app/components/GolfWatchApps/GolfWatchAppGamingMultiplayer.tsx6
-rw-r--r--frontend/app/states/play.ts24
-rw-r--r--frontend/app/states/watch.ts24
-rw-r--r--frontend/app/types/SupportedLanguage.ts1
-rw-r--r--openapi/api-server.yaml9
14 files changed, 162 insertions, 59 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 &amp; ~E_WARNING &amp; ~E_NOTICE &amp; ~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 &amp; ~E_WARNING &amp; ~E_NOTICE &amp; ~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";
diff --git a/openapi/api-server.yaml b/openapi/api-server.yaml
index 8fd2874..49b1a43 100644
--- a/openapi/api-server.yaml
+++ b/openapi/api-server.yaml
@@ -1,7 +1,7 @@
openapi: 3.0.0
info:
title: Albatross internal web API
- version: 0.1.0
+ version: 0.2.0
paths:
/login:
post:
@@ -349,6 +349,12 @@ components:
description:
type: string
example: "This is a problem"
+ language:
+ type: string
+ example: "php"
+ enum:
+ - php
+ - swift
sample_code:
type: string
example: "echo 'hello world';"
@@ -356,6 +362,7 @@ components:
- problem_id
- title
- description
+ - language
- sample_code
ExecutionStatus:
type: string