1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
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) =>
set(gameStartedAtAtom, value),
);
export type GameStateKind =
| "loading"
| "waiting"
| "starting"
| "gaming"
| "finished";
type ExecutionStatus = components["schemas"]["ExecutionStatus"];
type LatestGameState = components["schemas"]["LatestGameState"];
export const gameStateKindAtom = atom<GameStateKind>((get) => {
const now = get(currentTimestampAtom);
if (!now) {
return "loading";
}
const startedAt = get(gameStartedAtAtom);
if (!startedAt) {
return "waiting";
}
const durationSeconds = get(durationSecondsAtom);
const finishedAt = startedAt + durationSeconds;
if (now < startedAt) {
return "starting";
} else if (now < finishedAt) {
return "gaming";
} else {
return "finished";
}
});
const currentTimestampAtom = atom<number | null>(null);
export const setCurrentTimestampAtom = atom(null, (_, set) =>
set(currentTimestampAtom, Math.floor(Date.now() / 1000)),
);
const durationSecondsAtom = atom<number>(0);
export const setDurationSecondsAtom = atom(null, (_, set, value: number) =>
set(durationSecondsAtom, value),
);
export const startingLeftTimeSecondsAtom = atom<number | null>((get) => {
const startedAt = get(gameStartedAtAtom);
if (startedAt === null) {
return null;
}
const currentTimestamp = get(currentTimestampAtom);
if (currentTimestamp === null) {
return null;
}
return Math.max(0, startedAt - currentTimestamp);
});
export const gamingLeftTimeSecondsAtom = atom<number | null>((get) => {
const startedAt = get(gameStartedAtAtom);
if (startedAt === null) {
return null;
}
const durationSeconds = get(durationSecondsAtom);
const finishedAt = startedAt + durationSeconds;
const currentTimestamp = get(currentTimestampAtom);
if (currentTimestamp === null) {
return null;
}
return Math.min(durationSeconds, Math.max(0, finishedAt - currentTimestamp));
});
const rawStatusAtom = atom<ExecutionStatus>("none");
const rawScoreAtom = atom<number | null>(null);
export const statusAtom = atom<ExecutionStatus>((get) => {
const isSubmittingCode = get(isSubmittingCodeAtom);
if (isSubmittingCode) {
return "running";
} else {
return get(rawStatusAtom);
}
});
export const scoreAtom = atom<number | null>((get) => {
return get(rawScoreAtom);
});
const isSubmittingCodeAtom = atom(false);
export const handleSubmitCodePreAtom = atom(null, (_, set) => {
set(isSubmittingCodeAtom, true);
});
export const handleSubmitCodePostAtom = atom(null, (_, set) => {
set(isSubmittingCodeAtom, false);
});
export const setLatestGameStateAtom = atom(
null,
(_, set, value: LatestGameState) => {
set(rawStatusAtom, value.status);
set(rawScoreAtom, value.score);
},
);
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;
}
|