Skip to content

Commit

Permalink
feat: implement challenge information caching feature
Browse files Browse the repository at this point in the history
  • Loading branch information
async3619 committed Oct 6, 2022
1 parent 3112295 commit 5b93ba4
Show file tree
Hide file tree
Showing 10 changed files with 455 additions and 41 deletions.
16 changes: 8 additions & 8 deletions src/index.ts
Expand Up @@ -8,13 +8,14 @@ import { getProvider } from "./providers";

import { breakLine, clearConsole, drawLine, drawLogo, parseCommandLine } from "./utils/cli";
import { normalizeFilePath } from "./utils/normalizeFilePath";
import { retrieveChallenge } from "./utils/retrieveChallenge";
import { runChallenge } from "./utils/runChallenge";
import { truncate } from "./utils/truncate";
import { Config } from "./utils/types";
import { truncate } from "./utils/truncate";
import logger from "./utils/logger";

async function main() {
const { targetUrl, configPath, noOverwrite, source } = await parseCommandLine(process.argv);
const { targetUrl, configPath, noCache, noOverwrite, source } = await parseCommandLine(process.argv);

try {
drawLogo();
Expand All @@ -26,10 +27,9 @@ async function main() {
logger.info(`it seems a code challenge of \`${chalk.yellow(provider.getName())}\` service.`);
logger.info("trying to fetch and parse challenge information...");

const challenge = await provider.retrieve(targetUrl);
const { title, description } = challenge;

const targetPath = normalizeFilePath(source || `./${provider.getName().toLowerCase()}_${challenge.id}.ts`);
const challenge = await retrieveChallenge(targetUrl, provider, noCache);
const { title, description, id, initialCode } = await retrieveChallenge(targetUrl, provider, noCache);
const targetPath = normalizeFilePath(source || `./${provider.getName().toLowerCase()}_${id}.ts`);
logger.info(`use following url: ${targetUrl}`);
logger.info(`use following source code path: ${targetPath}`);

Expand All @@ -53,11 +53,11 @@ async function main() {

if (overwrite) {
await fs.unlink(targetPath);
await fs.writeFile(targetPath, challenge.initialCode || "");
await fs.writeFile(targetPath, initialCode || "");
}
}
} else {
await fs.writeFile(targetPath, challenge.initialCode || "");
await fs.writeFile(targetPath, initialCode || "");
}

logger.info("successfully retrieved challenge information:");
Expand Down
58 changes: 32 additions & 26 deletions src/providers/baekjoon.ts
Expand Up @@ -5,6 +5,31 @@ import { Challenge } from "../utils/types";

import { BaseProvider } from "./base";

const INITIAL_CODE = `function solution(input: string[]) {
// ...
}
(callback => {
if (typeof process !== "undefined" && "env" in process && "arguments" in process.env && process.env.arguments) {
solution(process.env.arguments.split("\\n"));
return;
}
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const input: string[] = [];
rl.on("line", line => {
input.push(line);
}).on("close", function () {
callback(input);
process.exit();
});
})(solution);`.trim();

export class BaekjoonProvider extends BaseProvider {
private readonly urlRegex = /^https?:\/\/(www\.)?acmicpc\.net\/problem\/[1-9]([0-9]*)$/;

Expand All @@ -15,6 +40,12 @@ export class BaekjoonProvider extends BaseProvider {
public checkUrl(url: string) {
return this.urlRegex.test(url);
}
public getId(url: string): string | number {
const targetUrl = new URL(url);
const id = targetUrl.pathname.split("/").slice(-1)[0];

return parseInt(id);
}

public async retrieve(url: string): Promise<Challenge> {
const targetUrl = new URL(url);
Expand Down Expand Up @@ -55,32 +86,7 @@ export class BaekjoonProvider extends BaseProvider {
input: ioText.filter(([i]) => i).map(([, t]) => t),
output: ioText.filter(([i]) => !i).map(([, t]) => decode(t).replace(/\r\n/g, "\n")),
provider: this,
initialCode: `
function solution(input: string[]) {
// ...
}
(callback => {
if (typeof process !== "undefined" && "env" in process && "arguments" in process.env && process.env.arguments) {
solution(process.env.arguments.split("\\n"));
return;
}
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const input: string[] = [];
rl.on("line", line => {
input.push(line);
}).on("close", function () {
callback(input);
process.exit();
});
})(solution);
`.trim(),
initialCode: INITIAL_CODE,
};
}
}
10 changes: 10 additions & 0 deletions src/providers/base.ts
@@ -1,13 +1,23 @@
import fetch from "node-fetch";

import { Challenge, InputType } from "../utils/types";
import { CachedChallenge } from "../utils/retrieveChallenge";

export abstract class BaseProvider {
public abstract checkUrl(url: string): boolean;
public abstract retrieve(url: string): Promise<Challenge>;
public abstract getId(url: string): string | number;

protected constructor(public readonly needJs: boolean) {}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
public hydrateCache({ version, ...challenge }: CachedChallenge): Challenge {
return {
...challenge,
provider: this,
};
}

public serializeOutput(outputItem: any): string {
return typeof outputItem !== "string" ? JSON.stringify(outputItem) : outputItem;
}
Expand Down
6 changes: 6 additions & 0 deletions src/providers/programmers.ts
Expand Up @@ -16,6 +16,12 @@ export class ProgrammersProvider extends BaseProvider {
url,
);
}
public getId(url: string): string | number {
const targetUrl = new URL(url);
const id = targetUrl.pathname.split("/").slice(-1)[0];

return parseInt(id);
}

public serializeOutput(outputItem: any): string {
return typeof outputItem === "string" ? `"${outputItem}"` : JSON.stringify(outputItem);
Expand Down
132 changes: 132 additions & 0 deletions src/utils/__snapshots__/cache.yml
@@ -0,0 +1,132 @@
id: "1018"
title: 체스판 다시 칠하기
description: "지민이는 자신의 저택에서 MN개의 단위 정사각형으로 나누어져 있는 M×N 크기의 보드를 찾았다. 어떤 정사각형은
검은색으로 칠해져 있고, 나머지는 흰색으로 칠해져 있다. 지민이는 이 보드를 잘라서 8×8 크기의 체스판으로 만들려고 한다.\r
\r
체스판은 검은색과 흰색이 번갈아서 칠해져 있어야 한다. 구체적으로, 각 칸이 검은색과 흰색 중 하나로 색칠되어 있고, 변을 공유하는 두
개의 사각형은 다른 색으로 칠해져 있어야 한다. 따라서 이 정의를 따르면 체스판을 색칠하는 경우는 두 가지뿐이다. 하나는 맨 왼쪽 위 칸이
흰색인 경우, 하나는 검은색인 경우이다.\r
\r
보드가 체스판처럼 칠해져 있다는 보장이 없어서, 지민이는 8×8 크기의 체스판으로 잘라낸 후에 몇 개의 정사각형을 다시 칠해야겠다고
생각했다. 당연히 8*8 크기는 아무데서나 골라도 된다. 지민이가 다시 칠해야 하는 정사각형의 최소 개수를 구하는 프로그램을 작성하시오."
inputDescription: 첫째 줄에 N과 M이 주어진다. N과 M은 8보다 크거나 같고, 50보다 작거나 같은 자연수이다. 둘째 줄부터
N개의 줄에는 보드의 각 행의 상태가 주어진다. B는 검은색이며, W는 흰색이다.
outputDescription: 첫째 줄에 지민이가 다시 칠해야 하는 정사각형 개수의 최솟값을 출력한다.
input:
- |-
8 8
WBWBWBWB
BWBWBWBW
WBWBWBWB
BWBBBWBW
WBWBWBWB
BWBWBWBW
WBWBWBWB
BWBWBWBW
- |-
10 13
BBBBBBBBWBWBW
BBBBBBBBBWBWB
BBBBBBBBWBWBW
BBBBBBBBBWBWB
BBBBBBBBWBWBW
BBBBBBBBBWBWB
BBBBBBBBWBWBW
BBBBBBBBBWBWB
WWWWWWWWWWBWB
WWWWWWWWWWBWB
- |-
8 8
BWBWBWBW
WBWBWBWB
BWBWBWBW
WBWBWBWB
BWBWBWBW
WBWBWBWB
BWBWBWBW
WBWBWBWB
- |-
9 23
BBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBW
- |-
10 10
BBBBBBBBBB
BBWBWBWBWB
BWBWBWBWBB
BBWBWBWBWB
BWBWBWBWBB
BBWBWBWBWB
BWBWBWBWBB
BBWBWBWBWB
BWBWBWBWBB
BBBBBBBBBB
- |-
8 8
WBWBWBWB
BWBWBWBW
WBWBWBWB
BWBBBWBW
WBWBWBWB
BWBWBWBW
WBWBWWWB
BWBWBWBW
- |-
11 12
BWWBWWBWWBWW
BWWBWBBWWBWW
WBWWBWBBWWBW
BWWBWBBWWBWW
WBWWBWBBWWBW
BWWBWBBWWBWW
WBWWBWBBWWBW
BWWBWBWWWBWW
WBWWBWBBWWBW
BWWBWBBWWBWW
WBWWBWBBWWBW
output:
- "1"
- "12"
- "0"
- "31"
- "0"
- "2"
- "15"
initialCode: >-
function solution(input: string[]) {
// ...
}
(callback => {
if (typeof process !== "undefined" && "env" in process && "arguments" in process.env && process.env.arguments) {
solution(process.env.arguments.split("\n"));
return;
}
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const input: string[] = [];
rl.on("line", line => {
input.push(line);
}).on("close", function () {
callback(input);
process.exit();
});
})(solution);
version: 1.2.4

0 comments on commit 5b93ba4

Please sign in to comment.