Browse Source

Allow multiple outputs.

* Checker is added via `Upgrade` or `Set Checker` commands
* testlib.h is added along the checker.
* There is a pool of custom checkers. (Examples from testlib.h)
Marcelo Fornet 5 years ago
parent
commit
85fea73450
5 changed files with 93 additions and 7 deletions
  1. 8 0
      package.json
  2. 34 4
      src/core.ts
  3. 44 2
      src/extension.ts
  4. 1 1
      src/gwen.ts
  5. 6 0
      src/static/checkers/yesno.cpp

+ 8 - 0
package.json

@@ -111,6 +111,10 @@
             {
                 "command": "acmx.compile",
                 "title": "ACMX: Compile"
+            },
+            {
+                "command": "acmx.setChecker",
+                "title": "ACMX: Set Checker"
             }
         ],
         "keybindings": [
@@ -129,6 +133,10 @@
             {
                 "command": "acmx.coding",
                 "key": "ctrl+alt+o"
+            },
+            {
+                "command": "acmx.upgrade",
+                "key": "ctrl+alt+u"
             }
         ]
     },

+ 34 - 4
src/core.ts

@@ -8,7 +8,7 @@ import { TestcaseResult, Veredict, SolutionResult, Problem, Contest, SiteDescrip
 
 export const TESTCASES = 'testcases';
 export const ATTIC = 'attic';
-const SRC = dirname(__filename);
+export const SRC = dirname(__filename);
 
 /**
  * Name of program file. Take extension dynamically from configuration
@@ -214,6 +214,7 @@ export function testcasesName(path: string){
 // }
 
 export function upgradeArena(path: string) {
+    // Create brute force solution
     let brute = join(path, 'brute.cpp');
 
     if (!existsSync(brute)){
@@ -221,12 +222,22 @@ export function upgradeArena(path: string) {
         copyFileSync(join(SRC, 'static', 'template.cpp'), brute);
     }
 
+    // Create test case generator
     let generator = join(path, 'gen.py');
 
     if (!existsSync(generator)){
         // TODO: If generator already exist ask whether to overwrite or not.
         gwen.create(path, generator);
     }
+
+    // Create checker for multiple answers.
+    let checker = join(path, ATTIC, 'checker.cpp');
+
+    if (!existsSync(checker)){
+        let testlib_path = join(path, ATTIC, 'testlib.h');
+        copyFileSync(join(SRC, 'static', 'checkers', 'wcmp.cpp'), checker);
+        copyFileSync(join(SRC, 'static', 'checkers', 'testlib.h'), testlib_path);
+    }
 }
 
 function newProblem(path: string, problem: Problem){
@@ -297,6 +308,26 @@ export async function newContestFromId(path: string, site: SiteDescription, cont
     return contestPath;
 }
 
+function get_checker_path() {
+    let path = currentProblem();
+    let default_checker = join(globalAtticPath(), 'checkers', 'wcmp.exe');
+
+    if (path === undefined) {
+        return default_checker;
+    }
+
+    let potential_checker_path = join(path, ATTIC, 'checker.cpp');
+
+    if (existsSync(potential_checker_path)) {
+        let checker_output = join(path, ATTIC, 'checker.exe');
+        // TODO: Only compile on changes.
+        compileCode(potential_checker_path, checker_output);
+        return checker_output;
+    }
+
+    return default_checker;
+}
+
 /**
  *
  * @param path
@@ -343,9 +374,8 @@ export function timedRun(path: string, tcName: string, timeout: number){
     writeSync(currentFd, xresult.stdout);
     closeSync(currentFd);
 
-    let checker = join(globalAtticPath(), 'checkers', 'wcmp.exe');
-
-    let checker_result = child_process.spawnSync(checker, [tcInput, tcCurrent, tcOutput]);
+    let checker_path = get_checker_path();
+    let checker_result = child_process.spawnSync(checker_path, [tcInput, tcCurrent, tcOutput]);
 
     if (checker_result.status !== 0){
         return new TestcaseResult(Veredict.WA);

+ 44 - 2
src/extension.ts

@@ -1,9 +1,9 @@
 'use strict';
 import * as vscode from 'vscode';
-import { existsSync, writeFileSync, readdirSync } from 'fs';
+import { existsSync, writeFileSync, readdirSync, copyFileSync } from 'fs';
 import { join, extname } from 'path';
 import { SITES, getSite } from './conn';
-import { newContestFromId, testSolution, veredictName, stressSolution, upgradeArena, newProblemFromId, removeExtension, solFile, initAcmX, currentProblem, compileCode, ATTIC } from './core';
+import { newContestFromId, testSolution, veredictName, stressSolution, upgradeArena, newProblemFromId, removeExtension, solFile, initAcmX, currentProblem, compileCode, ATTIC, SRC } from './core';
 import { Veredict, SiteDescription } from './types';
 import { startCompetitiveCompanionService } from './companion';
 
@@ -271,6 +271,46 @@ async function upgrade(){
     upgradeArena(path);
 }
 
+function fileList(dir: string): string[]{
+    return readdirSync(dir).reduce((list: string[], file: string) => {
+        return list.concat([file]);
+    }, []);
+}
+
+async function setChecker(){
+    let path = currentProblem();
+
+    if (path === undefined){
+        vscode.window.showErrorMessage("No active problem");
+        return;
+    }
+
+    let all_checkers_plain = fileList(join(SRC, 'static', 'checkers'))
+                                .filter((name: string) => name !== 'testlib.h')
+                                .map((name: string) => name.slice(0, name.length - 4));
+
+    let all_checkers = all_checkers_plain.map((value: string) => {
+        return {
+            'label' : value,
+            'target' : value + '.cpp'
+        };
+    });
+
+    let checker_info = await vscode.window.showQuickPick(all_checkers, { placeHolder: 'Select custom checker.' });
+
+    if (checker_info === undefined){
+        vscode.window.showErrorMessage("Checker not provided.");
+        return;
+    }
+
+    let checker = checker_info.target;
+
+    let checker_path = join(SRC, 'static', 'checkers', checker);
+    let checker_dest = join(path, ATTIC, 'checker.cpp');
+
+    copyFileSync(checker_path, checker_dest);
+}
+
 async function debugTest(){
     console.log("no bugs :O");
 }
@@ -290,6 +330,7 @@ export function activate(context: vscode.ExtensionContext) {
     let stressCommand = vscode.commands.registerCommand('acmx.stress', stress);
     let upgradeCommand = vscode.commands.registerCommand('acmx.upgrade', upgrade);
     let compileCommand = vscode.commands.registerCommand('acmx.compile', compile);
+    let setCheckerCommand = vscode.commands.registerCommand('acmx.setChecker', setChecker);
 
     let debugTestCommand = vscode.commands.registerCommand('acmx.debugTest', debugTest);
 
@@ -302,6 +343,7 @@ export function activate(context: vscode.ExtensionContext) {
     context.subscriptions.push(stressCommand);
     context.subscriptions.push(upgradeCommand);
     context.subscriptions.push(compileCommand);
+    context.subscriptions.push(setCheckerCommand);
 
     context.subscriptions.push(debugTestCommand);
 }

+ 1 - 1
src/gwen.ts

@@ -6,7 +6,7 @@ import { join } from "path";
 const DEFAULT = 'import random\n\nprint(random.randint(1, 100))\n';
 
 export function create(problemPath: string, outputPath: string) {
-    let tcPath = join(problemPath, TESTCASES)
+    let tcPath = join(problemPath, TESTCASES);
     let exitCode = spawnSync("python", ["-m", `tcgen`, "--path", `${tcPath}`, "--output", `${outputPath}`]);
 
     console.log("exticode:", exitCode);

+ 6 - 0
src/static/checkers/yesno.cpp

@@ -23,5 +23,11 @@ int main(int argc, char * argv[])
     if (ja != pa)
         quitf(_wa, "expected %s, found %s", compress(ja).c_str(), compress(pa).c_str());
 
+    // ignore extra tokens
+    // while (!ans.seekEof())
+    //     ans.skipChar();
+    // while (!ouf.seekEof())
+    //     ouf.skipChar();
+
     quitf(_ok, "answer is %s", ja.c_str());
 }