I am a big fan of leet code so when we decided to have a code challenge in our team I thought it wouldn’t be a bad idea to build one which should include pretty basic functionalities like:
Overall, it was an amazing learning experience and I am happy to walk through how I did it.
Before I start working on this I decided to come up with some guidelines so I don't over-engineer this and end up building a Frankenstein out of it, here are few of those guidelines:
The system consists of four keys functionalities based on which these modules are designed:
We have a single lambda function that takes care of registering a new user
and authenticating an existing user in the application.
When the user submits the code, the web application will call the API gateway with the following parameters:
API gateway will invoke the lambda function which will get information
about the question/problem statement from the “Questions” table in
Dynamodb (data looks like below).
From the table data, we take the “GeneratedCode” attribute value and
replace various tokens like — name — , — codeworking — & — testcases
— with the class name (generated), the user typed code, and test cases
(from columns “Test_1”… “Test_N”). Here is how the replaced code will
look:
// --name--
public class f_sdsds_a11223_dd {
// --codeworking--
static class Solution{
public static int calculate(String s){
int count=0;
int index=0;
while(true){
try{
s.charAt(index++);
count++;
}catch(Exception e){
break;
}
}
return count;
}
}
public static void main(String args[]) {
int actual=0;
int expected = 3;
String input = null;
// --testcases--
input = "Hi1";
expected = 3;
actual = Solution.calculate(input);
assert actual == expected : String.format("--helper-- Input %s -> Expected = %s but got = %s", input, expected, actual);
}
}
Since Lambda containers don't have JDK installed we have to package
“tools.jar” inside the deployment package so we can run the compilation
inside the containers, here is the code snippet on how we run compile
inside lambda:
try (StringInputStream inputStream = new StringInputStream(data);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ByteArrayOutputStream errorOutputStream = new ByteArrayOutputStream()) {
Class<?> javacTool = Class.forName("com.sun.tools.javac.api.JavacTool");
Method create = javacTool.getMethod("create");
JavaCompiler compiler = (JavaCompiler) create.invoke(null);
compiler.run(inputStream, outputStream, errorOutputStream, path.toFile().getAbsolutePath());
return new ProcessResponse(replaceNewLine(outputStream), replaceNewLine(errorOutputStream));
} catch (Exception e) {
return new ProcessResponse(null, "Compilation error --> " + e.getMessage());
}
If compilation is successful, we run the generated class which contains the test cases.
java -ea -cp /tmp/f_sdsds_a11223_dd
If execution is successful without any assertion failures, execution time is stored in “UserSubmission” table.
We utilize DynamoDB streams to update the total points and total execution time for any given user. A lambda function listens to all updates done on “UserSubmission” table and performs these calculations once the
submission is successful.
To find the winners, we run a simple query to get the top 3 rows in
“UserStastics” table generated by sorting “TotalPoints” column in
descending order and “TotalExecution” column in ascending
Here is the demo of the application:
I know this doesn't solve all the use cases that a popular code challenge websites like “leetcode” or “topcoder” solves. But this is a good start & foundation which we can keep adding on.
If you like this post then leave a comment. That way, I know you have made it all the way to the end 😀 . Thanks again for reading.
Previously published at https://medium.com/javarevisited/build-code-challenge-application-23aa40d183fa