0

I am doing a Kattis challenge, Booking a Room.

Basically, I get initial input - let's say 6, 4.

I have to store that input somewhere and then ask for another x inputs where x = the first value, ie. 6. Those inputs are stored elsewhere, in an array of arrays.

I tried so many different things, but either my initially stored values (6 and 4) change, or it iterates too much through the rest of the input.

I find the documentation on their website to be terrible.

https://open.kattis.com/help/javascript - for nodeJS example

My code attempts:

rl.question("initial", answer => {
  let nums = answer.split(" ");
  numberKittens = parseInt(nums[0]);
  spareBeds = parseInt(nums[1]);
  console.log("spare be", spareBeds);
  console.log("num of kit", numberKittens);
  rl.on(
    (numberKittens,
    answer => {
      let first = answer.split(" ");
      initialValue.push([parseInt(first[1]), parseInt(first[0])]);
      console.log("initial val", initialValue);
    })enter code here
  );
});

The initial part works ok, but never gets to the rl.on part and keeps asking for input forever.

Attempt two:

rl.on("line", line => {
  let nums = line.split(" ");
  numberKittens = parseInt(nums[0]);
  spareBeds = parseInt(nums[1]);
  let first = line.split(" ");
  let initialValue = [];
  initialValue.push([parseInt(first[1]), parseInt(first[0])]);
})

changes the numberKittens and spareBeds every time, messes up with the iteration.

Basically, I am trying to do something like this in Go:

fmt.Scanln(&numOfKittens, &numOfBeds)

for i := 1; i <= numOfKittens; i++ {
    fmt.Scanln(&arrivalDate, &departureDate)
    fmt.Println(arrivalDate, departureDate)
}
ggorlen
  • 44,755
  • 7
  • 76
  • 106
tachko
  • 161
  • 1
  • 8

1 Answers1

3

Kattis has a pretty tricky setup for Node due to the asynchronous callback API of readline. I'd normally use promises, then use split, map and + to parse the relevant input since numbers are usually involved. However, Kattis will stream readline's "line" events, then fire an end-of-file "close" event when the stream ends, and these aren't easy to use with promises.

Here's the high-level overview:

const readline = require("readline");

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

rl.once("line", line => {
  // collect line 1, the preamble data

  rl.on("line", line => {
      // parse a line of the body data
    })
    .on("close", () => {
      // all data has been read
      // print the solution
    })
  ;
});

Here's an example that works for the booking a room problem:

const readline = require("readline");

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

rl.once("line", line => {
  // collect line 1, the preamble data
  const rooms = [];
  const [r, n] = line.split(/ +/).map(Number);
  rl.on("line", line => {
      // parse a line of the body data
      rooms.push(+line);
    })
    .on("close", () => {
      // all data has been read
      // print the solution
      if (r === n) {
        console.log("too late");
      }
      else {
        for (let i = 1; i <= r; i++) {
          if (!rooms.includes(i)) {
            console.log(i);
            break;
          }
        }
      }
    })
  ;
});

If the preamble is more than one line or you don't like the nesting, you could use an array or object of handlers, where each index is the handler for that particular line:

const readline = require("readline");

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

let r;
let n;
const rooms = [];

const handlers = [
  line => { // handler for line 0
    [r, n] = line.split(/ +/).map(Number);
  },

  /* add more handlers as needed, for lines 2, 3... */

  // handler for all remaining lines
  line => rooms.push(+line),
];
let lines = 0;

rl.on("line", line => {
    handlers[Math.min(lines++, handlers.length - 1)](line);
  })
  .on("close", () => {
    // print the solution
    if (r === n) {
      console.log("too late");
    }
    else {
      for (let i = 1; i <= r; i++) {
        if (!rooms.includes(i)) {
          console.log(i);
          break;
        }
      }
    }
  })
;

This isn't nearly as nice as promises or languages that allow blocking input, but it gets the job done.

See this gist for a similar approach that uses n as a counter to figure out when to print the final solution rather than listening for the "close" event. I haven't seen any problems where this is necessary yet -- Kattis usually seems to send the EOF, but I haven't used Kattis enough to know that my proposal will always work.

On many problems, you don't need to aggregate a final result at the end, so you can skip .close() and print results as they stream into your body data's "line" handler.

ggorlen
  • 44,755
  • 7
  • 76
  • 106