DEV Community

Cover image for Codeforces - How to use Typescript/Javascript like a pro
Iulian Preda
Iulian Preda

Posted on • Edited on

Codeforces - How to use Typescript/Javascript like a pro

Hi everybody,

For a TL;DR section just skip to the end.

Getting that out of the way, let's get started. So, today I wanted to explore again some algorithmics problems and I returned to CodeForces.

Lately, I have used a lot of Typescript so naturally, I wanted to use it here too and I gladly discovered that you can actually use javascript in it.

Then the next question was, but how can I run only a single file in my Typescript project that holds many other problems (or at least it will)?

I tried many solutions but I came about creating an npm package that I can simply import or require since otherwise, I had issues with imports, paths, and more.

This is the package codeforces-io.
It is a simple mock for the readline() and print() functions so you do not have to modify your existing code while copying it to submission.

The documentation covers how to use it as a simple package, so I will write here a tutorial from start to finish with some advanced usages that you might like.

I created this package for those who want their code to be as close to the submission code as possible without having to deal with the boilerplate™ e.g.1,2.

GitHub logo IP-Algorithmics / CodeForcesIO

Small package to mock Codeforces Javascript IO functions

CodeForces IO

Small package to mock Codeforces Javascript IO functions.

Build Test NPM

Install

npm i @ip-algorithmics/codeforces-io

Intro

Codeforces for Javascript/Typescript uses readline() and print() functions for input and output to the standard input/console.

How to use

This library exposes the functions in a manner that allows you to just copy the source code for the submission.

Importing the functions

The functions can be imported both as ES6 modules or using the require function. The difference lays in how you access it.

// using ES6 modules import - you need to have the "module" property set to "commonjs" in the package.json
import { readline, print } from '@ip-algorithmics/codeforces-io';

// using require
const codeForcesIO = require('@ip-algorithmics/codeforces-io');
const readline = codeForcesIO.readline;
const print = codeForcesIO.print;

// alternative
const { readline, print, console } = require('@ip-algorithmics/codeforces-io');
Enter fullscreen mode Exit fullscreen mode

readline()

This…

Too much talk, let's see the usage

First, you have to have an npm project in the folder. So open up a terminal in your desired folder and just type npm init. You can use the default.

Then npm i @ip-algorithmics/codeforces-io

If you do not use Typescript this is all you need to do, otherwise... just run this one after the other.

  • npm i -g typescript ts-node

Note: if you want to configure Typescript you will additionally need to run

  • tsc --init

Remember to not include the "DOM" library into the lib because the print function is already declared in the DOM library for printing files.
You can just require/import the print function for this package for output to the console. If need other functions from the console object you can require the entire console.

Usage with ts-node or node

Now that you have a working environment you can create a .js or .ts file and get going.
This library uses modern ES6 import/export modules but that means declaring a few more things in the project configuration.
So let's stay to require for now.
At the top of your file just write

const {nextLine, print, console} = require('@ip-algorithmics/codeforces-io')
Enter fullscreen mode Exit fullscreen mode

Now that we have our functions we can use them in the project.

The readline function reads from input.txt file that needs to be in the same folder.
Here is a quick example

// example.js or example.ts
const { readline, print } = require('@ip-algorithmics/codeforces-io');

let numberOfLines = parseInt(readline(), 10);
for (let i = 0; i < numberOfLines; i++) {
    let x = readline()
        .trim()
        .split(',')
        .map((x) => parseInt(x, 10));
    if (x[0] < 90 || x[0] > 90 || x[1] < 90 || x[1] > 90) {
        print(x);
        break;
    }
    // do something with x
}
Enter fullscreen mode Exit fullscreen mode

And here is the input.txt

3
-47,23
34,56
91,82
Enter fullscreen mode Exit fullscreen mode

Now that we have our files in place we can just run
node example.js or ts-node example.ts. You can use an absolute or relative path, this library will search for the place where the script is and then read the input file from there.
So you do not have to cd <path> you can just use node <path>/<file>.

Usage with jest

image
For a simple reason. Paths can get really long and jest can use test names to find the file directly

Compare these:

  • ts-node src/1300-points/totally-real-problem/index.ts
  • jest -t 'totally-real-problem I'd say it's a bit shorter. But how can you obtain that? Well, that is simple actually.

Just run npm i -g jest ts-jest and npm i @types/jest and then in the root of the project you need to create a file named jest.config.ts and put the next lines in it:

export default {
    preset: 'ts-jest',
    testEnvironment: 'node',
    transform: {
        'node_modules/variables/.+\\.(j|t)sx?$': 'ts-jest'
    },
    transformIgnorePatterns: ['node_modules/(?!variables/.*)']
};

Enter fullscreen mode Exit fullscreen mode

For javascript you can just run jest --init and that will work by default.

Except for this, now all of your scripts will have to be a test
so the naming should be .test.ts or .spec.ts or create a folder named __tests__ and place your scripts in it (same things apply to javascript also).

Now you have to convert your scripts to tests. This is as simple as wrapping everything in

test('name of test', () => { 
// your code
})
Enter fullscreen mode Exit fullscreen mode

The example from above will get transformed to this.

test('example', () => {
const { readline, print, testOutput } = require('@ip-algorithmics/codeforces-io');

let numberOfLines = parseInt(readline(), 10);
for (let i = 0; i < numberOfLines; i++) {
    let x = readline()
        .trim()
        .split(',')
        .map((y) => parseInt(y, 10));

    if ((x[0] < 90 && x[0] > -90) || (x[1] < 90 && x[1] > -90)) {
        print(x[0] + ',' + x[1]);
        break;
    }
}
}
Enter fullscreen mode Exit fullscreen mode

To run it just type jest -t 'example'.

Special mention

For typescript projects if you use the same function names without putting them in a class you will get an error that the name is taken, so you have to add export {}; at the end of the file so the names will not conflict.

If something doesn't work do not hesitate to leave a comment, open an issue or even better create a pull request.

TL;DR

  • Created a library for Codeforces that reads input.txt from the folder the script was called.
  • npm i @ip-algorithmics/codeforces-io
  • Code example
// example.js or example.ts
const { readline, print, testOutput } = require('@ip-algorithmics/codeforces-io');

let numberOfLines = parseInt(readline(), 10);
for (let i = 0; i < numberOfLines; i++) {
    let x = readline()
        .trim()
        .split(',')
        .map((y) => parseInt(y, 10));

    if ((x[0] < 90 && x[0] > -90) || (x[1] < 90 && x[1] > -90)) {
        print(x[0] + ',' + x[1]);
        break;
    }
}

Enter fullscreen mode Exit fullscreen mode

And here is the input.txt

3
-47,23
34,56
91,82
Enter fullscreen mode Exit fullscreen mode
  • node <path to file>/file.js or ts-node <path to file>/file.ts

Happy codding!

Update

I added the testOutput function. Now you can create your output.txt file and put the outputs in it.
You just have to run the function at the end of the script and it will prompt you with a Passed or Failed output in the console.
Remember not to copy this function in the submission.

Internally now the print caches everything so that the testOutput can check if what was printed is the same as what is in the output.txt

Example:
input.txt

3
-47,23
34,56
91,82
Enter fullscreen mode Exit fullscreen mode

output.txt

-47,23
Enter fullscreen mode Exit fullscreen mode
const { readline, print, testOutput } = require('@ip-algorithmics/codeforces-io');

let numberOfLines = parseInt(readline(), 10);
for (let i = 0; i < numberOfLines; i++) {
    let x = readline()
        .trim()
        .split(',')
        .map((y) => parseInt(y, 10));

    if ((x[0] < 90 && x[0] > -90) || (x[1] < 90 && x[1] > -90)) {
        print(x[0] + ',' + x[1]);
        break;
    }
}

testOutput(); // Result Passed
Enter fullscreen mode Exit fullscreen mode

Update 2

The solution generator is completed. Now you can just create the input.txt, output.txt, index.ts and the folder with a simple command. It will also add the imports for you.

You can use this feature in 2 ways:

  • Without installing globally the library using npx @ip-algorithmics/codeforces-io.
  • Installing globally the library npm i -g @ip-algorithmics/codeforces-io and then using cf in the command line.

Parameters

  • path - the path to the solution. E.g. ./ProblemA. Defaults to ./New Solution.
  • --f or --file - the name of the js/ts file to create. E.g. mainFile. Defaults to index.
  • --js - Uses .js extension instead of .ts
  • --cjs - Uses require instead of ES6 import/export
  • --c or --comment - Adds a comment at the beginning of the file. E.g. the link to the problem.

Usage examples

  • npx @ip-algorithmics/codeforces-io ./ProblemA --js --cjs
  • cf ./ProblemA --js --cjs
  • cf ./ProblemA
  • cf ./ProblemA --c http://link.to.problem

Update 3 - Typescript ideas

I noticed that Codforces uses ES5 internally in both node and V8 so it has problems with const, let and features like spreading an array.

Personally I found that the best solution is to:

  • add in the package.json the following script "build": "tsc --module es6 --moduleResolution node" using it like npm run build <path>
  • manually run tsc --module es6 --moduleResolution node <path>

Top comments (0)