When they select their tools, programmers have a choice between command-line and GUI options. Most people use both. For example, I prefer to use GUI tools for diffing a set of files I’m preparing to commit to a source repository. I also like my editor to be graphical, though I use a lot of keyboard shortcuts. But command-line tools are preferable when I’ll be repeating the same action multiple times. It’s a lot more efficient to retrieve a typed command from the shell history than to do the same steps repeatedly using mouse clicks or even keyboard shortcuts. Today I’ll be explaining some commands that are useful for programming puzzles.
UVa OJ Submission
When I’m executing my puzzle solution process, I keep a lot of windows open: two editor windows (one for code, one for notes), a command prompt, and a few browser windows/tabs for sites like uHunt, uDebug, and Stack Overflow. I run local tests from the command line, but eventually I have to open the UVa OJ site to make an official submission. I recently tried out a way to do that from the command line.
uva-node is a Node.js-based command-line interface to UVa Online Judge. Here are the steps you can use to make your first command-line submission.
uva-node runs in Node.js, which you can install from the Node.js home page. When I installed it, I used the installer’s suggested settings.
Install uva-node using npm
npm is the Node Package Manager, which installs by default with Node.js. Once Node is installed, you can run this at a command prompt:
npm install -g uva-node
If all goes well, you’ll see the names of the directories the installer is using, and a list of uva-node dependencies and versions.
Next, you’ll want to run
uva from a command prompt, which launches the interactive environment. The first time you run uva-node, you’ll see:
>uva Setting file not found: (path)\.uva-node A new one will be auto-created after exiting the program
Add your UVa account
To use uva-node, you have to add your UVa account and set it as current:
> add uva duncans (password) Account added successfully > use uva duncans Account set as current
The account and password are the ones you use to log in to UVa Online Judge.
Submit a solution
uva-node has a few features that I didn’t try, like opening a problem statement by number, and launching an editor. It can also infer the source filename based on a problem number. Since all Java submissions to UVa OJ are named Main.java, this probably wouldn’t work for Java solutions. However, the
send command accepts a full path to the solution file, so you don’t have to rely on uva-node figuring it out:
> send 1047 (path)\Main.java Logging in... Sending code... Send ok
At this point, the solution is off to UVa Online Judge. I would then use the uHunt web site to monitor progress, since it automatically updates results as UVa OJ processes the submission. However, for a completely command-line solution, uva-node has a
stat command that returns the status of the most recent submissions for the current user.
And that’s it. A painless process for submitting a solution without visiting the UVa OJ website.
Local Development and Testing
Before I tried out uva-node, I was already using some of my own command-line tools for developing and testing puzzle solutions in my local environment. Here’s how they work.
In the early stages of my problem-solving process, I run a shell script that accepts the UVa OJ problem number, like this:
> uva 1047
This command does the following:
- Create a directory called 1047
- Copy various template files into that directory, including Main.java with template code, text files for input and output, a 1047.txt notes file with sections for each step in the process, and several shell scripts.
- Change to that directory.
- Open the files in an editor.
After I run that command, I can immediately start taking notes on the problem in 1047.txt. Then once I have a plan of attack and a pseudocode solution, I can move on to Main.java, which is already open in my editor.
When I think I have some working code, I run this simple command:
It does the following:
Since I previously copied the sample input from the problem statement into
input.txt, this is the first basic test of my program. It begins a code/compile/test cycle that continues until my output matches the sample output.
go script accepts an optional second parameter, an integer that indicates which input file to use. Without a parameter, it uses
input.txt. If I run
go 2, then it uses
input2.txt. I use
input2.txt for the random input supplied by uDebug. Once I can produce the correct output for that input, I usually make an official submission to UVa OJ. If that submission isn’t accepted, I continue testing with
input4.txt, etc. to narrow down the bug or performance issue.
When I’m having trouble writing the syntax that the compiler wants, and I’m getting compiler errors, I use a version of the
go script called
goc, which just executes the compile step. That allows me to fix compiler errors without worrying about output yet. I have an analogous script called
gor for just the run step.
For problems with more than a few lines of output, it’s an error-prone process to compare my program output with the correct output. To facilitate this process, I wrote another version of the
go script called
goo. You can think of
goo as “go with output comparison.” Like
goo script accepts an optional second parameter. So I can run this:
> goo 2
This command does the following:
input2.txtas input, and writing output to
output2-check.txt is the accepted output that I previously copied from uDebug. The
goo script provides a quick way to verify that my latest code change has fixed the bug I’m working on, and hasn’t broken anything that was previously working. If I have accumulated a few test input files, I can quickly run
goo 3 and so on to make sure everything works as expected before I submit. This makes
goo a kind of unit test suite. For a small set of tests, it’s almost as convenient as a jUnit run.
Since I started using my
goo script, the uDebug team has added their own diff tool to the website. Their diff is convenient for quick checks, but it involves more copying and pasting than my command-line approach.
Speaking of jUnit, I have one last script called
test that does the following:
javac Main.java MainTest.java
java org.junit.runner.JUnitCore MainTest
This script runs unit tests and reports the results. I use it for puzzles that are complicated enough to require writing unit tests for sections of the code rather than testing the whole program at once.
Advantages of the Command Line
I’m not a command-line purist like 1990s-era Neal Stephenson. I do plenty of mouse clicking and window resizing as I’m working on puzzles. However, there are cases where a command-line approach is more efficient. UVa OJ solutions lend themselves to the command-line approach because they involve short single-file programs that read from stdin and write to stdout. Having shell scripts around to take care of things you do repeatedly can help move you through your solutions faster.
(Image credit: Osama Khalid)