@jpetit/toolkit 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.prettierignore +11 -0
- package/.prettierrc.json +9 -0
- package/.vscode/settings.json +26 -0
- package/README.md +22 -0
- package/assets/lua/fixCodeBlocks.lua +19 -0
- package/assets/lua/removeEnvs.lua +20 -0
- package/assets/lua/removeHtmlOnly.lua +11 -0
- package/assets/problems/graphics/japanese-flag.pbm/README.md +14 -0
- package/assets/problems/graphics/japanese-flag.pbm/award.png +0 -0
- package/assets/problems/graphics/japanese-flag.pbm/handler.yml +2 -0
- package/assets/problems/graphics/japanese-flag.pbm/problem.ca.tex +21 -0
- package/assets/problems/graphics/japanese-flag.pbm/problem.ca.yml +3 -0
- package/assets/problems/graphics/japanese-flag.pbm/sample-1.inp +1 -0
- package/assets/problems/graphics/japanese-flag.pbm/sample-2.inp +1 -0
- package/assets/problems/graphics/japanese-flag.pbm/solution.cc +25 -0
- package/assets/problems/graphics/japanese-flag.pbm/solution.py +11 -0
- package/assets/problems/graphics/tortuga.pbm/README.md +13 -0
- package/assets/problems/graphics/tortuga.pbm/award.png +0 -0
- package/assets/problems/graphics/tortuga.pbm/handler.yml +2 -0
- package/assets/problems/graphics/tortuga.pbm/problem.ca.tex +23 -0
- package/assets/problems/graphics/tortuga.pbm/problem.ca.yml +3 -0
- package/assets/problems/graphics/tortuga.pbm/sample.inp +0 -0
- package/assets/problems/graphics/tortuga.pbm/solution.py +11 -0
- package/assets/problems/standard/campanar-de-la-torrassa.pbm/README.md +15 -0
- package/assets/problems/standard/campanar-de-la-torrassa.pbm/award.html +1 -0
- package/assets/problems/standard/campanar-de-la-torrassa.pbm/award.png +0 -0
- package/assets/problems/standard/campanar-de-la-torrassa.pbm/campanar.eps +1113 -0
- package/assets/problems/standard/campanar-de-la-torrassa.pbm/campanar.png +0 -0
- package/assets/problems/standard/campanar-de-la-torrassa.pbm/generate.cc +10 -0
- package/assets/problems/standard/campanar-de-la-torrassa.pbm/handler.yml +2 -0
- package/assets/problems/standard/campanar-de-la-torrassa.pbm/problem.ca.tex +59 -0
- package/assets/problems/standard/campanar-de-la-torrassa.pbm/problem.ca.yml +3 -0
- package/assets/problems/standard/campanar-de-la-torrassa.pbm/problem.en.tex +52 -0
- package/assets/problems/standard/campanar-de-la-torrassa.pbm/problem.en.yml +4 -0
- package/assets/problems/standard/campanar-de-la-torrassa.pbm/sample.inp +7 -0
- package/assets/problems/standard/campanar-de-la-torrassa.pbm/slow.cc +29 -0
- package/assets/problems/standard/campanar-de-la-torrassa.pbm/solution.cc +48 -0
- package/assets/problems/standard/campanar-de-la-torrassa.pbm/test-1.inp +12 -0
- package/assets/problems/standard/campanar-de-la-torrassa.pbm/test-2.inp +100000 -0
- package/assets/problems/standard/campanar-de-la-torrassa.pbm/test-2.ops +1 -0
- package/assets/problems/standard/campanar-de-la-torrassa.pbm/test-b.inp +0 -0
- package/assets/problems/standard/maximum-of-2-integers.pbm/README.md +11 -0
- package/assets/problems/standard/maximum-of-2-integers.pbm/handler.yml +1 -0
- package/assets/problems/standard/maximum-of-2-integers.pbm/problem.ca.tex +17 -0
- package/assets/problems/standard/maximum-of-2-integers.pbm/problem.ca.yml +3 -0
- package/assets/problems/standard/maximum-of-2-integers.pbm/problem.en.tex +16 -0
- package/assets/problems/standard/maximum-of-2-integers.pbm/problem.en.yml +4 -0
- package/assets/problems/standard/maximum-of-2-integers.pbm/sample-1.inp +1 -0
- package/assets/problems/standard/maximum-of-2-integers.pbm/sample-2.inp +1 -0
- package/assets/problems/standard/maximum-of-2-integers.pbm/sample-3.inp +1 -0
- package/assets/problems/standard/maximum-of-2-integers.pbm/solution.c +18 -0
- package/assets/problems/standard/maximum-of-2-integers.pbm/solution.cc +13 -0
- package/assets/problems/standard/maximum-of-2-integers.pbm/solution.java +16 -0
- package/assets/problems/standard/maximum-of-2-integers.pbm/solution.py +5 -0
- package/assets/problems/standard/maximum-of-2-integers.pbm/test-1.inp +1 -0
- package/assets/problems/standard/maximum-of-2-integers.pbm/test-2.inp +1 -0
- package/assets/problems/standard/maximum-of-2-integers.pbm/test-3.inp +1 -0
- package/assets/problems/standard/maximum-of-2-integers.pbm/test-4.inp +1 -0
- package/assets/problems/standard/maximum-of-2-integers.pbm/test-5.inp +1 -0
- package/assets/problems/standard/treasures-in-a-map.pbm/README.md +12 -0
- package/assets/problems/standard/treasures-in-a-map.pbm/atzar.cc +85 -0
- package/assets/problems/standard/treasures-in-a-map.pbm/award.png +0 -0
- package/assets/problems/standard/treasures-in-a-map.pbm/generate-1.cc +26 -0
- package/assets/problems/standard/treasures-in-a-map.pbm/generate-2.cc +26 -0
- package/assets/problems/standard/treasures-in-a-map.pbm/generate-3.cc +26 -0
- package/assets/problems/standard/treasures-in-a-map.pbm/generate-4.cc +26 -0
- package/assets/problems/standard/treasures-in-a-map.pbm/handler.yml +1 -0
- package/assets/problems/standard/treasures-in-a-map.pbm/problem.ca.tex +40 -0
- package/assets/problems/standard/treasures-in-a-map.pbm/problem.ca.yml +3 -0
- package/assets/problems/standard/treasures-in-a-map.pbm/problem.en.tex +40 -0
- package/assets/problems/standard/treasures-in-a-map.pbm/problem.en.yml +4 -0
- package/assets/problems/standard/treasures-in-a-map.pbm/random-1.inp +24 -0
- package/assets/problems/standard/treasures-in-a-map.pbm/random-2.inp +27 -0
- package/assets/problems/standard/treasures-in-a-map.pbm/random-3.inp +38 -0
- package/assets/problems/standard/treasures-in-a-map.pbm/random-4.inp +50 -0
- package/assets/problems/standard/treasures-in-a-map.pbm/sample-1.inp +9 -0
- package/assets/problems/standard/treasures-in-a-map.pbm/sample-2.inp +6 -0
- package/assets/problems/standard/treasures-in-a-map.pbm/sample-3.inp +7 -0
- package/assets/problems/standard/treasures-in-a-map.pbm/solution.cc +38 -0
- package/assets/problems/standard/treasures-in-a-map.pbm/test-1.inp +5 -0
- package/assets/problems/standard/treasures-in-a-map.pbm/test-2.inp +6 -0
- package/assets/problems/standard/treasures-in-a-map.pbm/test-3.inp +6 -0
- package/assets/problems/standard/treasures-in-a-map.pbm/test-4.inp +9 -0
- package/assets/problems/standard/treasures-in-a-map.pbm/test-5.inp +10 -0
- package/assets/problems/standard/treasures-in-a-map.pbm/test-6.inp +9 -0
- package/assets/problems/standard/treasures-in-a-map.pbm/test-7.inp +12 -0
- package/assets/problems/standard/treasures-in-a-map.pbm/test-8.inp +3 -0
- package/assets/problems/standard/treasures-in-a-map.pbm/test-9.inp +37 -0
- package/assets/problems/standard/treasures-in-a-map.pbm/test-91.inp +52 -0
- package/assets/problems/standard/treasures-in-a-map.pbm/test-92.inp +25 -0
- package/assets/problems/standard/treasures-in-a-map.pbm/test-93.inp +3 -0
- package/assets/sty/judgeit.ca.sty +54 -0
- package/assets/sty/judgeit.de.sty +61 -0
- package/assets/sty/judgeit.en.sty +60 -0
- package/assets/sty/judgeit.es.sty +54 -0
- package/assets/sty/judgeit.fr.sty +59 -0
- package/assets/sty/judgeit.sty +307 -0
- package/assets/sty/picins.sty +579 -0
- package/assets.zip +0 -0
- package/eslint.config.mjs +31 -0
- package/lib/ai.ts +138 -0
- package/lib/assets.ts +31 -0
- package/lib/cleaner.ts +58 -0
- package/lib/compilers/_frompython.ts +388 -0
- package/lib/compilers/base.ts +97 -0
- package/lib/compilers/gcc.ts +47 -0
- package/lib/compilers/gxx.ts +47 -0
- package/lib/compilers/index.ts +61 -0
- package/lib/compilers/python3.ts +67 -0
- package/lib/data.ts +19 -0
- package/lib/doctor.ts +104 -0
- package/lib/generate.ts +333 -0
- package/lib/maker.ts +535 -0
- package/lib/settings.ts +42 -0
- package/lib/tui.ts +69 -0
- package/lib/utils.ts +83 -0
- package/package.json +56 -0
- package/problems/graphic.pbm/README.md +14 -0
- package/problems/graphic.pbm/award.png +0 -0
- package/problems/graphic.pbm/handler.yml +2 -0
- package/problems/graphic.pbm/problem.ca.html +13 -0
- package/problems/graphic.pbm/problem.ca.md +20 -0
- package/problems/graphic.pbm/problem.ca.tex +21 -0
- package/problems/graphic.pbm/problem.ca.txt +20 -0
- package/problems/graphic.pbm/problem.ca.yml +3 -0
- package/problems/graphic.pbm/sample-1.inp +1 -0
- package/problems/graphic.pbm/sample-2.inp +1 -0
- package/problems/graphic.pbm/solution.py +11 -0
- package/problems/maxim2.pbm/Main.java +13 -0
- package/problems/maxim2.pbm/distillation.yml +7 -0
- package/problems/maxim2.pbm/distilled-01.inp +1 -0
- package/problems/maxim2.pbm/distilled-02.inp +1 -0
- package/problems/maxim2.pbm/distilled-03.inp +1 -0
- package/problems/maxim2.pbm/distiller.yml +2 -0
- package/problems/maxim2.pbm/generate-inputs.py +9 -0
- package/problems/maxim2.pbm/handler.yml +2 -0
- package/problems/maxim2.pbm/ma-1.inp +1 -0
- package/problems/maxim2.pbm/ma-2.inp +1 -0
- package/problems/maxim2.pbm/ma-3.inp +1 -0
- package/problems/maxim2.pbm/ma-4.inp +1 -0
- package/problems/maxim2.pbm/ma-5.inp +1 -0
- package/problems/maxim2.pbm/per-doubles.inp +1 -0
- package/problems/maxim2.pbm/problem.ca.html +11 -0
- package/problems/maxim2.pbm/problem.ca.md +19 -0
- package/problems/maxim2.pbm/problem.ca.tex +17 -0
- package/problems/maxim2.pbm/problem.ca.txt +19 -0
- package/problems/maxim2.pbm/problem.ca.yml +3 -0
- package/problems/maxim2.pbm/problem.en.html +11 -0
- package/problems/maxim2.pbm/problem.en.md +19 -0
- package/problems/maxim2.pbm/problem.en.tex +16 -0
- package/problems/maxim2.pbm/problem.en.txt +19 -0
- package/problems/maxim2.pbm/problem.en.yml +4 -0
- package/problems/maxim2.pbm/sample-1.inp +1 -0
- package/problems/maxim2.pbm/sample-2.inp +1 -0
- package/problems/maxim2.pbm/sample-3.inp +1 -0
- package/problems/maxim2.pbm/solution.c +12 -0
- package/problems/maxim2.pbm/solution.cc +13 -0
- package/problems/maxim2.pbm/solution.java +13 -0
- package/problems/maxim2.pbm/solution.pas +9 -0
- package/problems/maxim2.pbm/solution.py +5 -0
- package/problems/maxim2.pbm/tags.yml +2 -0
- package/problems/maxim2.pbm/test_-1_-1.inp +1 -0
- package/problems/maxim2.pbm/test_-1_-2.inp +1 -0
- package/problems/maxim2.pbm/test_-1_0.inp +1 -0
- package/problems/maxim2.pbm/test_-1_1.inp +1 -0
- package/problems/maxim2.pbm/test_-2_-1.inp +1 -0
- package/problems/maxim2.pbm/test_-2_-2.inp +1 -0
- package/problems/maxim2.pbm/test_-2_0.inp +1 -0
- package/problems/maxim2.pbm/test_-2_1.inp +1 -0
- package/problems/maxim2.pbm/test_0_-1.inp +1 -0
- package/problems/maxim2.pbm/test_0_-2.inp +1 -0
- package/problems/maxim2.pbm/test_0_0.inp +1 -0
- package/problems/maxim2.pbm/test_0_1.inp +1 -0
- package/problems/maxim2.pbm/test_1_-1.inp +1 -0
- package/problems/maxim2.pbm/test_1_-2.inp +1 -0
- package/problems/maxim2.pbm/test_1_0.inp +1 -0
- package/problems/maxim2.pbm/test_1_1.inp +1 -0
- package/test.ts +3 -0
- package/toolkit/ai.ts +30 -0
- package/toolkit/clean.ts +19 -0
- package/toolkit/compilers.ts +29 -0
- package/toolkit/create-jutge-ai.ts +101 -0
- package/toolkit/create-template.ts +51 -0
- package/toolkit/create-wizard.ts +4 -0
- package/toolkit/create.ts +75 -0
- package/toolkit/doctor.ts +17 -0
- package/toolkit/index.ts +28 -0
- package/toolkit/init.ts +66 -0
- package/toolkit/make.ts +60 -0
- package/toolkit/verify.ts +19 -0
- package/tsconfig.json +38 -0
- package/types/zip.d.ts +4 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as tui from '@/lib/tui.js'
|
|
2
|
+
import { Compiler } from './base'
|
|
3
|
+
import { rm, exists } from 'fs/promises'
|
|
4
|
+
import { join } from 'path'
|
|
5
|
+
|
|
6
|
+
export class Compiler_GCC extends Compiler {
|
|
7
|
+
id(): string {
|
|
8
|
+
return 'GCC'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
name(): string {
|
|
12
|
+
return 'GNU C Compiler'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
type(): string {
|
|
16
|
+
return 'compiler'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
language(): string {
|
|
20
|
+
return 'C'
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async version(): Promise<string> {
|
|
24
|
+
return await this.getVersion('gcc --version', 0)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
flags1(): string {
|
|
28
|
+
return '-D_JUDGE_ -O2 -DNDEBUG -Wall -Wextra -Wno-sign-compare'
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
flags2(): string {
|
|
32
|
+
return ''
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
extension(): string {
|
|
36
|
+
return 'c'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async compile(directory: string, sourcePath: string): Promise<void> {
|
|
40
|
+
const outputPath = sourcePath + '.exe'
|
|
41
|
+
const fullOutputPath = join(directory, outputPath)
|
|
42
|
+
await rm(fullOutputPath, { force: true })
|
|
43
|
+
tui.command(`gcc ${this.flags1()} ${sourcePath} -o ${outputPath}`)
|
|
44
|
+
await Bun.$`gcc ${this.flags1().split(' ')} ${sourcePath} -o ${outputPath}`.cwd(directory).nothrow()
|
|
45
|
+
if (!(await exists(fullOutputPath))) throw new Error(`Compilation failed for ${sourcePath}`)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { rm, exists } from 'fs/promises'
|
|
2
|
+
import { Compiler } from './base'
|
|
3
|
+
import chalk from 'chalk'
|
|
4
|
+
import { join } from 'path'
|
|
5
|
+
|
|
6
|
+
export class Compiler_GXX extends Compiler {
|
|
7
|
+
id(): string {
|
|
8
|
+
return 'GXX'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
name(): string {
|
|
12
|
+
return 'GNU C++ Compiler'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
type(): string {
|
|
16
|
+
return 'compiler'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
language(): string {
|
|
20
|
+
return 'C++'
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async version(): Promise<string> {
|
|
24
|
+
return await this.getVersion('g++ --version', 0)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
flags1(): string {
|
|
28
|
+
return '-std=c++11 -D_JUDGE_ -O2 -DNDEBUG -Wall -Wextra -Wno-sign-compare -Wshadow'
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
flags2(): string {
|
|
32
|
+
return '-std=c++11 -D_JUDGE_ -O2 -DNDEBUG -Wall -Wextra -Wno-sign-compare -Wshadow'
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
extension(): string {
|
|
36
|
+
return 'cc'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async compile(directory: string, sourcePath: string): Promise<void> {
|
|
40
|
+
const outputPath = sourcePath + '.exe'
|
|
41
|
+
const fullOutputPath = join(directory, outputPath)
|
|
42
|
+
await rm(fullOutputPath, { force: true })
|
|
43
|
+
console.log(chalk.magenta(`❯ g++ ${this.flags1()} ${sourcePath} -o ${outputPath}`))
|
|
44
|
+
await Bun.$`g++ ${this.flags1().split(' ')} ${sourcePath} -o ${outputPath}`.cwd(directory).nothrow()
|
|
45
|
+
if (!(await exists(fullOutputPath))) throw new Error(`Compilation failed for ${sourcePath}`)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Compiler, type CompilerInfo } from './base'
|
|
2
|
+
import { Compiler_GCC } from './gcc'
|
|
3
|
+
import { Compiler_GXX } from './gxx'
|
|
4
|
+
import { Compiler_Python3 } from './python3'
|
|
5
|
+
|
|
6
|
+
const compilersRegistryById: Record<string, new () => Compiler> = {
|
|
7
|
+
C: Compiler_GCC,
|
|
8
|
+
'C++': Compiler_GXX,
|
|
9
|
+
Python3: Compiler_Python3,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const compilersRegistryByExtension: Record<string, new () => Compiler> = {
|
|
13
|
+
c: Compiler_GCC,
|
|
14
|
+
cc: Compiler_GXX,
|
|
15
|
+
py: Compiler_Python3,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function getCompilerById(id: string): Compiler {
|
|
19
|
+
const CompilerClass = compilersRegistryById[id]
|
|
20
|
+
if (!CompilerClass) throw new Error(`Compiler '${id}' is not defined`)
|
|
21
|
+
const compilerInstance = new CompilerClass()
|
|
22
|
+
return compilerInstance
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function getCompilerByExtension(extension: string): Compiler {
|
|
26
|
+
const CompilerClass = compilersRegistryByExtension[extension]
|
|
27
|
+
if (!CompilerClass) throw new Error(`No compiler defined for extension '.${extension}'`)
|
|
28
|
+
const compilerInstance = new CompilerClass()
|
|
29
|
+
return compilerInstance
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function getDefinedCompilerIds(): Promise<string[]> {
|
|
33
|
+
await Bun.sleep(0) // to make function async
|
|
34
|
+
return Object.keys(compilersRegistryById)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function getAvailableCompilers(): Promise<string[]> {
|
|
38
|
+
const available: string[] = []
|
|
39
|
+
for (const id of Object.keys(compilersRegistryById)) {
|
|
40
|
+
const CompilerClass = compilersRegistryById[id]!
|
|
41
|
+
const compilerInstance = new CompilerClass()
|
|
42
|
+
if (await compilerInstance.available()) {
|
|
43
|
+
available.push(id)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return available
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function getCompilerInfo(id: string): Promise<CompilerInfo> {
|
|
50
|
+
const compilerInstance = getCompilerById(id)
|
|
51
|
+
if (!compilerInstance) throw new Error(`Compiler '${id}' is not defined`)
|
|
52
|
+
return await compilerInstance.info()
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export async function getCompilersInfo(): Promise<Record<string, CompilerInfo>> {
|
|
56
|
+
const infos: Record<string, CompilerInfo> = {}
|
|
57
|
+
for (const id of Object.keys(compilersRegistryById)) {
|
|
58
|
+
infos[id] = await getCompilerInfo(id)
|
|
59
|
+
}
|
|
60
|
+
return infos
|
|
61
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import * as tui from '@/lib/tui.js'
|
|
2
|
+
import { exists, rm } from 'fs/promises'
|
|
3
|
+
import { join } from 'path'
|
|
4
|
+
import { Compiler } from './base'
|
|
5
|
+
|
|
6
|
+
export class Compiler_Python3 extends Compiler {
|
|
7
|
+
id(): string {
|
|
8
|
+
return 'Python3'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
name(): string {
|
|
12
|
+
return 'Python3'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
type(): string {
|
|
16
|
+
return 'interpreter'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
language(): string {
|
|
20
|
+
return 'Python'
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async version(): Promise<string> {
|
|
24
|
+
return await this.getVersion('python3 --version', 0)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
flags1(): string {
|
|
28
|
+
return ''
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
flags2(): string {
|
|
32
|
+
return ''
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
extension(): string {
|
|
36
|
+
return 'py'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async compile(directory: string, sourcePath: string): Promise<void> {
|
|
40
|
+
tui.command(`python3 -m py_compile ${sourcePath}`)
|
|
41
|
+
console.log('directory:', directory, 'sourcePath:', sourcePath)
|
|
42
|
+
const { exitCode } = await Bun.$`python3 -m py_compile ${sourcePath}`.cwd(directory).nothrow()
|
|
43
|
+
if (exitCode !== 0) throw new Error(`Compilation failed for ${sourcePath}`)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
override async execute(directory: string, inputPath: string, outputPath: string): Promise<void> {
|
|
47
|
+
const sourcePath = 'solution.py'
|
|
48
|
+
if (!(await exists(join(directory, sourcePath)))) {
|
|
49
|
+
throw new Error(`Source file ${sourcePath} does not exist in directory ${directory}`)
|
|
50
|
+
}
|
|
51
|
+
const fullInputPath = join(directory, inputPath)
|
|
52
|
+
const fullOutputPath = join(directory, outputPath)
|
|
53
|
+
await rm(fullOutputPath, { force: true })
|
|
54
|
+
tui.command(`python3 ${sourcePath} < ${inputPath} > ${outputPath}`)
|
|
55
|
+
|
|
56
|
+
const proc = Bun.spawn(['python3', sourcePath], {
|
|
57
|
+
cwd: directory,
|
|
58
|
+
stdin: Bun.file(fullInputPath),
|
|
59
|
+
stdout: Bun.file(fullOutputPath),
|
|
60
|
+
stderr: 'inherit',
|
|
61
|
+
})
|
|
62
|
+
await proc.exited
|
|
63
|
+
const exitCode = proc.exitCode
|
|
64
|
+
|
|
65
|
+
if (exitCode !== 0) throw new Error(`Execution failed for ${sourcePath}`)
|
|
66
|
+
}
|
|
67
|
+
}
|
package/lib/data.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { invert } from 'radash'
|
|
2
|
+
|
|
3
|
+
export const languageNames: Record<string, string> = {
|
|
4
|
+
en: 'English',
|
|
5
|
+
ca: 'Catalan',
|
|
6
|
+
es: 'Spanish',
|
|
7
|
+
fr: 'French',
|
|
8
|
+
de: 'German',
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const proglangNames: Record<string, string> = {
|
|
12
|
+
c: 'C',
|
|
13
|
+
cc: 'C++',
|
|
14
|
+
py: 'Python3',
|
|
15
|
+
hs: 'Haskell',
|
|
16
|
+
clj: 'Clojure',
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const proglangExtensions: Record<string, string> = invert(proglangNames)
|
package/lib/doctor.ts
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import chalk from 'chalk'
|
|
2
|
+
import * as tui from '@/lib/tui.js'
|
|
3
|
+
|
|
4
|
+
export async function probePython3(showInfo: boolean = false): Promise<boolean> {
|
|
5
|
+
if (showInfo) tui.command('python3 --version')
|
|
6
|
+
const output = await Bun.$`python3 --version`.nothrow().text()
|
|
7
|
+
const version = output.trim()
|
|
8
|
+
if (showInfo) console.log(version)
|
|
9
|
+
return version.startsWith('Python 3')
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function probeGCC(showInfo: boolean = false): Promise<boolean> {
|
|
13
|
+
if (showInfo) tui.command('g++ --version')
|
|
14
|
+
const output = await Bun.$`g++ --version`.nothrow().text()
|
|
15
|
+
const version = output.split('\n')[0]!.trim()
|
|
16
|
+
if (showInfo) console.log(version)
|
|
17
|
+
return output.startsWith('Apple clang') || output.startsWith('g++')
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function probeLaTeX(showInfo: boolean = false): Promise<boolean> {
|
|
21
|
+
if (showInfo) tui.command('pdflatex --version')
|
|
22
|
+
const output = await Bun.$`pdflatex --version`.nothrow().text()
|
|
23
|
+
const version = output.split('\n')[0]!.trim()
|
|
24
|
+
if (showInfo) console.log(version)
|
|
25
|
+
return output.startsWith('pdfTeX')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function probePandoc(showInfo: boolean = false): Promise<boolean> {
|
|
29
|
+
if (showInfo) tui.command('pandoc --version')
|
|
30
|
+
const output = await Bun.$`pandoc --version`.nothrow().text()
|
|
31
|
+
const version = output.split('\n')[0]!.trim()
|
|
32
|
+
if (showInfo) console.log(version)
|
|
33
|
+
return output.startsWith('pandoc') && output.includes('+lua')
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function probeImageMagick(showInfo: boolean = false): Promise<boolean> {
|
|
37
|
+
if (showInfo) tui.command('magick --version')
|
|
38
|
+
const output = await Bun.$`magick --version`.nothrow().text()
|
|
39
|
+
const version = output.split('\n')[0]!.trim()
|
|
40
|
+
if (showInfo) console.log(version)
|
|
41
|
+
return output.startsWith('Version: ImageMagick')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function checkPython3(): Promise<void> {
|
|
45
|
+
if (await probePython3(true)) {
|
|
46
|
+
tui.success('Python3 seems installed')
|
|
47
|
+
} else {
|
|
48
|
+
tui.error('Python3 does not appear to be installed')
|
|
49
|
+
tui.warning('This is not a problem if you do not plan to use Python solutions.')
|
|
50
|
+
tui.warning('See https://www.python.org/downloads/')
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function checkGCC(): Promise<void> {
|
|
55
|
+
if (await probeGCC(true)) {
|
|
56
|
+
tui.success('C/C++ seems installed')
|
|
57
|
+
} else {
|
|
58
|
+
tui.error('C/C++ does not appear to be installed')
|
|
59
|
+
tui.warning('This is not a problem if you do not plan to use C or C++ solutions.')
|
|
60
|
+
tui.warning('Please install GCC or Clang.')
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function checkLaTeX(): Promise<void> {
|
|
65
|
+
if (await probeLaTeX(true)) {
|
|
66
|
+
tui.success('LaTeX seems installed')
|
|
67
|
+
} else {
|
|
68
|
+
tui.error('LaTeX does not appear to be installed')
|
|
69
|
+
tui.warning('You will not be able to generate PDF statements.')
|
|
70
|
+
tui.warning('TODO: Provide instructions for installing LaTeX')
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export async function checkPandoc(): Promise<void> {
|
|
75
|
+
if (await probePandoc(true)) {
|
|
76
|
+
tui.success('Pandoc with Lua support seems installed')
|
|
77
|
+
} else {
|
|
78
|
+
tui.error('Pandoc with Lua support does not appear to be installed')
|
|
79
|
+
tui.warning('You will not be able to generate text statements (HTML, TXT, MD).')
|
|
80
|
+
tui.warning('See https://pandoc.org/installing.html')
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export async function checkImageMagick(): Promise<void> {
|
|
85
|
+
if (await probeImageMagick(true)) {
|
|
86
|
+
tui.success('ImageMagick seems installed')
|
|
87
|
+
} else {
|
|
88
|
+
tui.error('ImageMagick does not appear to be installed')
|
|
89
|
+
tui.warning('You will not be able to convert images for text statements.')
|
|
90
|
+
tui.warning('See https://imagemagick.org/script/download.php')
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export async function checkEnvVars(): Promise<void> {
|
|
95
|
+
await Bun.sleep(0) // to prevent "async function has no await" warning
|
|
96
|
+
const vars = ['OPENAI_API_KEY', 'GEMINI_API_KEY']
|
|
97
|
+
for (const v of vars) {
|
|
98
|
+
if (process.env[v]) {
|
|
99
|
+
tui.success(`${v} is set`)
|
|
100
|
+
} else {
|
|
101
|
+
tui.error(`${v} is not set`)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
package/lib/generate.ts
ADDED
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
import { ChatBot, estimatePowerConsumption } from '@/lib/ai'
|
|
2
|
+
import * as tui from '@/lib/tui.js'
|
|
3
|
+
import chalk from 'chalk'
|
|
4
|
+
import { appendFile, mkdir } from 'fs/promises'
|
|
5
|
+
import { oraPromise } from 'ora'
|
|
6
|
+
import { join } from 'path'
|
|
7
|
+
import { z } from 'zod'
|
|
8
|
+
import { paths } from './settings'
|
|
9
|
+
import { proglangNames, languageNames } from './data'
|
|
10
|
+
|
|
11
|
+
const showPrompt = true
|
|
12
|
+
const showAnswer = true
|
|
13
|
+
|
|
14
|
+
export const ZProblemData = z.object({
|
|
15
|
+
title: z.string(),
|
|
16
|
+
description: z.string(),
|
|
17
|
+
author: z.string(),
|
|
18
|
+
email: z.string().email(),
|
|
19
|
+
type: z.string(),
|
|
20
|
+
proglangs: z.array(z.string()),
|
|
21
|
+
languages: z.array(z.string()),
|
|
22
|
+
model: z.string(),
|
|
23
|
+
outputDir: z.string(),
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
export type ProblemData = z.infer<typeof ZProblemData>
|
|
27
|
+
|
|
28
|
+
export async function generateProblemWithJutgeAI(info: ProblemData): Promise<void> {
|
|
29
|
+
const generator = new ProblemGenerator(info)
|
|
30
|
+
await generator.run()
|
|
31
|
+
await generator.save(info.outputDir)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
class ProblemGenerator {
|
|
35
|
+
private bot: ChatBot
|
|
36
|
+
private info: ProblemData
|
|
37
|
+
|
|
38
|
+
// generated problem parts
|
|
39
|
+
private problemStatement: string = ''
|
|
40
|
+
private problemSampleTests: string = ''
|
|
41
|
+
private problemPrivateTests: string = ''
|
|
42
|
+
private problemSolutions: Record<string, string> = {}
|
|
43
|
+
private problemTranslations: Record<string, string> = {}
|
|
44
|
+
private problemReadme: string = ''
|
|
45
|
+
|
|
46
|
+
private latexExample = `
|
|
47
|
+
\\Problem{Which one is missing?}
|
|
48
|
+
|
|
49
|
+
\\Statement
|
|
50
|
+
|
|
51
|
+
Johny has a list of objects, labeled between 1 and $n$, but he lost one of them. He wants to know which one is missing.
|
|
52
|
+
|
|
53
|
+
\\medskip
|
|
54
|
+
|
|
55
|
+
Write a program that reads sequences with all the numbers between 1 and $n$ but one, and tells which one is missing.
|
|
56
|
+
|
|
57
|
+
\\Input
|
|
58
|
+
|
|
59
|
+
Input consists of several sequences.
|
|
60
|
+
Every sequence begins with a number $n$ between~1 and~$10^4$ followed by $n - 1$ natural numbers.
|
|
61
|
+
Every number between 1 and $n$ appears exactly once, except one of them, which is missing.
|
|
62
|
+
|
|
63
|
+
\\Output
|
|
64
|
+
|
|
65
|
+
For every sequence, print the missing number.
|
|
66
|
+
`
|
|
67
|
+
|
|
68
|
+
private systemPrompt = `
|
|
69
|
+
You are an expert in coding and writing programmming problems.
|
|
70
|
+
You provide accurate information and write following the instructed format.
|
|
71
|
+
`
|
|
72
|
+
|
|
73
|
+
private statementCoda = `
|
|
74
|
+
|
|
75
|
+
\\Observation
|
|
76
|
+
|
|
77
|
+
This problem has been generated by Jutge$^{\\text{AI}}$. Remove this observation after reviewing it carefully.
|
|
78
|
+
|
|
79
|
+
\\Sample
|
|
80
|
+
|
|
81
|
+
`
|
|
82
|
+
constructor(info: ProblemData) {
|
|
83
|
+
this.info = info
|
|
84
|
+
this.bot = new ChatBot(info.model, this.systemPrompt)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async generateStatement(): Promise<string> {
|
|
88
|
+
return await tui.section('Generating problem statement', async () => {
|
|
89
|
+
const statementPrompt = `
|
|
90
|
+
You are to write the statement of a programming problem
|
|
91
|
+
|
|
92
|
+
The statement must be written in LaTeX, using a few predefined macros.
|
|
93
|
+
Use LaTeX math syntax for formulas and variables.
|
|
94
|
+
Use dollars for inline maths and use \\[ and \\] for display maths.
|
|
95
|
+
Do not add input/output test cases in the statement.
|
|
96
|
+
Separate paragraphs by a blank line and \\medskip macro.
|
|
97
|
+
|
|
98
|
+
Write in the style of Salvador Roura, the famous problem setter from Jutge.org.
|
|
99
|
+
|
|
100
|
+
Here is an example for an unrelated problem, follow its structure and macros:
|
|
101
|
+
|
|
102
|
+
${this.latexExample}
|
|
103
|
+
|
|
104
|
+
The title for this problem is:
|
|
105
|
+
|
|
106
|
+
${this.info.title}
|
|
107
|
+
|
|
108
|
+
Here is the description of this problem and some additional instructions:
|
|
109
|
+
|
|
110
|
+
${this.info.description}
|
|
111
|
+
`
|
|
112
|
+
const answer = cleanMardownCodeString(await this.complete(statementPrompt)) + this.statementCoda
|
|
113
|
+
return answer
|
|
114
|
+
})
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async generateSampleTests(): Promise<string> {
|
|
118
|
+
return await tui.section('Generating sample test cases', async () => {
|
|
119
|
+
const sampleTestCasesPrompt = `
|
|
120
|
+
Now, write a sample test case file to illustrate the input that the program must read according to the problem statement.
|
|
121
|
+
|
|
122
|
+
Only the input should be given, the output will be computed from it.
|
|
123
|
+
Sample test cases should be relatively small but cover interesting cases but not all edge cases.
|
|
124
|
+
`
|
|
125
|
+
const answer = cleanMardownCodeString(await this.complete(sampleTestCasesPrompt))
|
|
126
|
+
return answer
|
|
127
|
+
})
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async generatePrivateTests(): Promise<string> {
|
|
131
|
+
return await tui.section('Generating private test cases', async () => {
|
|
132
|
+
const privateTestCasesPrompt = `
|
|
133
|
+
Now, write a private test case file to check the correctness and efficiency of the program.
|
|
134
|
+
|
|
135
|
+
Only the input should be given, the output will be computed from it.
|
|
136
|
+
The private test cases should cover edge cases.
|
|
137
|
+
Limit the number of test cases to 20.
|
|
138
|
+
`
|
|
139
|
+
const answer = cleanMardownCodeString(await this.complete(privateTestCasesPrompt))
|
|
140
|
+
return answer
|
|
141
|
+
})
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async generateSolutions(): Promise<Record<string, string>> {
|
|
145
|
+
return await tui.section('Generating solutions', async () => {
|
|
146
|
+
const solutions: Record<string, string> = {}
|
|
147
|
+
for (const proglang of this.info.proglangs) {
|
|
148
|
+
await tui.section(`Generating solution in ${proglangNames[proglang]}`, async () => {
|
|
149
|
+
const solutionPrompt = `
|
|
150
|
+
Now, write a solution in ${proglangNames[proglang]} to solve this problem
|
|
151
|
+
|
|
152
|
+
It must be written in an idiomatic way and only include relevant comments.
|
|
153
|
+
The code must be efficient and handle all edge cases.
|
|
154
|
+
The code must read from standard input and write to standard output.
|
|
155
|
+
The code must be well written and easy to understand to novices.
|
|
156
|
+
The code does not have to check the preconditions stated in the probleM
|
|
157
|
+
Do not use any non-standard libraries.
|
|
158
|
+
${proglang === 'cpp' ? 'Do not use fast input/output methods.' : ''}
|
|
159
|
+
${proglang === 'cpp' ? 'Add a using namespace std; declaration after the includes and do not use std:: prefixes.' : ''}
|
|
160
|
+
${proglang === 'py' ? 'Use type annotations.' : ''}
|
|
161
|
+
`
|
|
162
|
+
const answer = cleanMardownCodeString(await this.complete(solutionPrompt))
|
|
163
|
+
this.bot.forgetLastInteraction()
|
|
164
|
+
solutions[proglang] = answer
|
|
165
|
+
})
|
|
166
|
+
}
|
|
167
|
+
return solutions
|
|
168
|
+
})
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async translateStatements(): Promise<Record<string, string>> {
|
|
172
|
+
return await tui.section('Translating problem statements', async () => {
|
|
173
|
+
const translations: Record<string, string> = {}
|
|
174
|
+
for (const language of this.info.languages) {
|
|
175
|
+
await tui.section(`Translating to ${languageNames[language]}`, async () => {
|
|
176
|
+
const translationPrompt = `
|
|
177
|
+
Now, translate the problem statement to ${languageNames[language]}.
|
|
178
|
+
|
|
179
|
+
The translation must be accurate and use proper technical terminology.
|
|
180
|
+
Maintain the LaTeX formatting and macros.
|
|
181
|
+
The texts that the program must read and write should not be translated.
|
|
182
|
+
`
|
|
183
|
+
const answer = cleanMardownCodeString(await this.complete(translationPrompt)) + this.statementCoda
|
|
184
|
+
this.bot.forgetLastInteraction()
|
|
185
|
+
translations[language] = answer
|
|
186
|
+
})
|
|
187
|
+
}
|
|
188
|
+
return translations
|
|
189
|
+
})
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async generateReadme(): Promise<string> {
|
|
193
|
+
return tui.section('Generating README.md', async () => {
|
|
194
|
+
await Bun.sleep(0)
|
|
195
|
+
const readme = `
|
|
196
|
+
# ${this.info.title}
|
|
197
|
+
|
|
198
|
+
This programming problem for Jutge.org was generated by Jutge<sup>AI</sup> through the Jutge.org API using ${this.info.model} and a prompt by ${this.info.author}.
|
|
199
|
+
|
|
200
|
+
**Warning**: This problem may contain inaccuracies or errors. Review the problem statements, test cases, and solutions carefully before using them in a real setting. Output tests and statement PDFs have not been generated, use \`jutge-make-problem\` to generate them.
|
|
201
|
+
|
|
202
|
+
## Author
|
|
203
|
+
|
|
204
|
+
${this.info.author}
|
|
205
|
+
|
|
206
|
+
## Original prompt
|
|
207
|
+
|
|
208
|
+
${this.info.description}
|
|
209
|
+
|
|
210
|
+
## Generated solutions
|
|
211
|
+
|
|
212
|
+
- ${this.info.proglangs.map((proglang) => proglangNames[proglang]).join('\n- ')}
|
|
213
|
+
|
|
214
|
+
## Generated languages
|
|
215
|
+
|
|
216
|
+
- English
|
|
217
|
+
- ${this.info.languages.map((language) => languageNames[language]).join('\n- ')}
|
|
218
|
+
|
|
219
|
+
## Model information
|
|
220
|
+
|
|
221
|
+
\`\`\`yaml
|
|
222
|
+
${Bun.YAML.stringify(this.bot.modelInformation(), null, 2)}
|
|
223
|
+
\`\`\`
|
|
224
|
+
|
|
225
|
+
## Estimated cost
|
|
226
|
+
|
|
227
|
+
The following information is based on estimations from token counts and do not reflect the actual costs incurred. Using GPT-5 pricing as reference.
|
|
228
|
+
|
|
229
|
+
- Total input tokens: ${this.bot.totalInputTokens}
|
|
230
|
+
- Total output tokens: ${this.bot.totalOutputTokens}
|
|
231
|
+
- Total input cost: ${this.bot.totalInputCost.toFixed(6)} USD
|
|
232
|
+
- Total output cost: ${this.bot.totalOutputCost.toFixed(6)} USD
|
|
233
|
+
- Total estimated cost: ${(this.bot.totalInputCost + this.bot.totalOutputCost).toFixed(6)} USD
|
|
234
|
+
- Energy: ${estimatePowerConsumption(this.bot.totalInputTokens, this.bot.totalOutputTokens).wattHours.toFixed(6)} Wh
|
|
235
|
+
- CO₂ emissions: ${estimatePowerConsumption(this.bot.totalInputTokens, this.bot.totalOutputTokens).co2Grams.toFixed(6)} g CO₂
|
|
236
|
+
|
|
237
|
+
`
|
|
238
|
+
return readme
|
|
239
|
+
})
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async run() {
|
|
243
|
+
tui.title('Generating problem with JutgeAI')
|
|
244
|
+
await this.bot.init()
|
|
245
|
+
this.problemStatement = await this.generateStatement()
|
|
246
|
+
this.problemSampleTests = await this.generateSampleTests()
|
|
247
|
+
this.problemPrivateTests = await this.generatePrivateTests()
|
|
248
|
+
this.problemSolutions = await this.generateSolutions() // these are forgotten inside
|
|
249
|
+
this.bot.forgetLastInteraction() // forget private tests
|
|
250
|
+
this.bot.forgetLastInteraction() // forget sample tests
|
|
251
|
+
this.problemTranslations = await this.translateStatements()
|
|
252
|
+
this.problemReadme = await this.generateReadme()
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
async save(path: string) {
|
|
256
|
+
await tui.section(`Saving problem to ${path}`, async () => {
|
|
257
|
+
await mkdir(path, { recursive: true })
|
|
258
|
+
await Bun.write(join(path, 'problem.en.tex'), this.problemStatement)
|
|
259
|
+
|
|
260
|
+
const yml = {
|
|
261
|
+
title: this.info.title,
|
|
262
|
+
author: this.info.author,
|
|
263
|
+
email: this.info.email,
|
|
264
|
+
model: this.info.model,
|
|
265
|
+
}
|
|
266
|
+
await Bun.write(`${path}/problem.en.yml`, Bun.YAML.stringify(yml, null, 2) + '\n')
|
|
267
|
+
|
|
268
|
+
for (const [lang, translation] of Object.entries(this.problemTranslations)) {
|
|
269
|
+
await Bun.write(join(path, `problem.${lang}.tex`), translation)
|
|
270
|
+
const yml = {
|
|
271
|
+
title: getTitleFromTranslation(translation) || this.info.title,
|
|
272
|
+
translator: this.info.author,
|
|
273
|
+
translator_email: this.info.email,
|
|
274
|
+
original_language: 'en',
|
|
275
|
+
model: this.info.model,
|
|
276
|
+
}
|
|
277
|
+
await Bun.write(join(path, `problem.${lang}.yml`), Bun.YAML.stringify(yml, null, 2) + '\n')
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
await Bun.write(join(path, 'sample.inp'), this.problemSampleTests)
|
|
281
|
+
|
|
282
|
+
await Bun.write(join(path, 'test.inp'), this.problemPrivateTests)
|
|
283
|
+
|
|
284
|
+
for (const [proglang, solution] of Object.entries(this.problemSolutions)) {
|
|
285
|
+
const ext = proglang
|
|
286
|
+
await Bun.write(join(path, `solution.${ext}`), solution)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const handlerYml: any = {
|
|
290
|
+
handler: 'std',
|
|
291
|
+
}
|
|
292
|
+
await Bun.write(join(path, 'handler.yml'), Bun.YAML.stringify(handlerYml, null, 2) + '\n')
|
|
293
|
+
|
|
294
|
+
await Bun.write(join(path, 'README.md'), this.problemReadme)
|
|
295
|
+
})
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
async complete(prompt: string): Promise<string> {
|
|
299
|
+
if (showPrompt) console.log(chalk.gray(prompt))
|
|
300
|
+
await appendFile(join(paths.log, 'jutge-ai.log'), '------------------- PROMPT -------------------\n\n')
|
|
301
|
+
await appendFile(join(paths.log, 'jutge-ai.log'), prompt + '\n\n')
|
|
302
|
+
|
|
303
|
+
const answer = await oraPromise(
|
|
304
|
+
async () => {
|
|
305
|
+
return await this.bot.complete(prompt)
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
text: `JutgeAI is thinking`,
|
|
309
|
+
},
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
if (showAnswer) console.log(chalk.gray(answer))
|
|
313
|
+
await appendFile(join(paths.log, 'jutge-ai.log'), '------------------- ANSWER -------------------\n\n')
|
|
314
|
+
await appendFile(join(paths.log, 'jutge-ai.log'), answer + '\n\n')
|
|
315
|
+
|
|
316
|
+
return answer
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function getTitleFromTranslation(translation: string): string | null {
|
|
321
|
+
const pattern = /\\Problem\{(.*?)\}/
|
|
322
|
+
const match = translation.match(pattern)
|
|
323
|
+
if (match) {
|
|
324
|
+
return match[1]!.trim()
|
|
325
|
+
}
|
|
326
|
+
return null
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function cleanMardownCodeString(s: string): string {
|
|
330
|
+
const pattern = /^\n*```\w*\s*(.*?)\s*```\n*$/s
|
|
331
|
+
const clean = s.replace(pattern, '$1')
|
|
332
|
+
return clean
|
|
333
|
+
}
|