#はじめに
あるハッカソンに参加した時、任意の言語で問題を解き、解答と用意した答えを比較するような競技プログラミングっぽいことを行うシステムの作成が求められた。
「オンラインコンパイラ API」と検索してみると、Wandboxとpaiza.IOが有望かなと感じた。個人的にpaizaの方が使いやすそうだったので、こっちを採用することにした。
Qiitaの記事書くの初なのでお手柔らかに見てください。
#参考にしたページ
#どうやったか
###paiza.IOの使い方
まず、参考にしたページ-1でにあったAPIを試せるページ(以下テストページ)でAPIの仕様を理解した。どうやらcreate、get_status、get_detailの3つのAPIがあるらしい。
これはテストページのcreateの項。
createに使用言語とコードなどを渡してidを受け取り、get_statusにidを入れてstatusがcompletedになってることを確認、get_detailにもid入れて受け取ったjsonの"stdout"が出力となっている。
JSON hash.
{
id: Session id
language: language,
note: note,
status: status('running', 'completed'),
build_stdout: build output to stdout,
build_stderr: build output to stderr,
build_exit_code: build exit code,
build_time: build time(second),
build_memory: build memory usage(bytes),
build_result: build result('success', 'failure', 'error'),
stdout: code output to stdout, //これが出力
stderr: code output to stderr,
exit_code: exit code,
time: time to run(seconds),
memory: code memory usage(bytes),
result: code result('success', 'failure', 'error'),
}
1回テストページで何か動かしてみると分かりやすいと思う。
これを基に、idを手に入れるべく書いたものがこれ。
<!DOCTYPE html>
<body>
<script>
$( function () {
var strid = "";
var request = new XMLHttpRequest();
var url = 'http://api.paiza.io/runners/create'
request.open('POST',url, true);
request.responseType = 'json';
var params = {
source_code:"print('hello')",//ソースコード
language:"python3",//言語指定、http://api.paiza.io/docs/swagger/#!/runners を参考に
input:"",
longpoll:"",
longpoll_timeout:"",
api_key:"guest"};
request.onload = function () {
var data = this.response;
console.log(data);
};
}
</script>
</body>
</html>
正直入力はsource_codeとlanguageだけでいい。api_key:"guest"を忘れずに。
これでidを取得できた。
テストページでやった時、get_statusを挟まずにget_detailにidを入れてを実行してもちゃんと答えが出たのでここでもそうしようとした。
するとstdoutがnullで返ってくる。
図を見ればわかるが、createの実行直後にget_detailが実行されている。
どうやらid取得から実行までにウェイトタイムが必要らしく、そのためにstatus:completedを待つようだ。
本来ならget_statusに送り続けてcompletedが返されたらget_detailへ移行などするが、paiza.IOの仕様上2sしか実行されないので、sleep関数を作って2s待つことにした。脳筋ですね。
###テキストエディタ
ここから先はハッカソンのメンバーのつよつよエンジニアが大部分を担当したため、正直あやふやな部分もあるが一応理解はしたので書いていく。
JavaScriptのライブラリを参照して、入力補完機能や強調色などの機能を搭載したテキストエディタを組み込んだ。
前述のsleep関数とエディタを出すためにbodyタグに入れていたコードがこれ。
<div id="editor" style="height: 600px"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.0/ace.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.0/ext-language_tools.js"></script>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script>
function sleep(a){
var dt1 = new Date().getTime();
var dt2 = new Date().getTime();
while (dt2 < dt1 + a){
dt2 = new Date().getTime();
}
return;
}
var editor = ace.edit("editor");
editor.$blockScrolling = Infinity;
editor.setOptions({
enableBasicAutocompletion: true,
enableSnippets: true,
enableLiveAutocompletion: true
});
editor.setTheme("ace/theme/monokai");
editor.getSession().setMode("ace/mode/python");
</body>
###テストケース処理
実際にどうやってテストケースを通しているのかを。多分AtCoderとかはテストファイル与えて見てると思うけど、1問しか作らなかったので手打ちした。
document.getElementById("ansSend").onclick = function(){
$("#judge_system").fadeIn();
let text = editor.getValue();
console.log(text);
var pass = 0;
if(judge(text,"3 5")){
pass++;
}
sleep(1000)
if(judge(text,"7 7")){
pass++;
}
sleep(1000)
if(judge(text,"15 33")){
pass++;
}
count = 0;
if(pass == 3){
document.getElementById("body").classList.add("done");
}
else if(pass != 3 && miss_ans != null){
console.log(miss_ans);
judgeNow.textContent = miss_ans;
}else if(pass != 3){
judgeNow.textContent = "計算が違います";
}
};
次の項で説明するjudge関数にテストケースとして用意した入力を渡す簡単なもの。judge関数はTrueかFalseで返されるのを見てる。
一々1秒のスリープタイムを挟んでいるのでとても遅い。テストケース3つだからなんとかなってるけど、もっと増やすと大変そうなのでstatus:completedの検知は絶対必要だなあと感じた。
###ジャッジシステム
解答を提出された後、テストケースを用意した解答と比べるシステムを構成した。
受け取ったコードをcode、テストケースをinputValueとして渡すjudge関数。
function judge(code,inputValue) {
var err_msg = null;
$.ajaxSetup({async: false});
request.open('POST',url, true);
request.responseType = 'json';
var params = {
source_code:code,//ソースコード
language:"python3",//言語指定、http://api.paiza.io/docs/swagger/#!/runners を参考に
input:inputValue,
longpoll:"",
longpoll_timeout:"",
api_key:"guest"};
request.onload = function () {
var data = this.response;
console.log(data);
};
$.post(url,params,null,"json")
.done(function(result) {
console.log(result.id);
sleep(2000);
request.open('GET',url2, true);
request.responseType = 'json';
var param = {
id: result.id,
api_key:"guest"
}
$.getJSON( url2, param, function( result2 ) {
// console.log(result2)
console.log(result2.stdout)
ans = result2.stdout;
status = result2.result;
err_msg = result2.stderr;
console.log(result2);
});
});
count++;
if(count == 1){
if(ans == 15){
console.log("テスト"+count+"成功");
return true;
}
}else if(count == 2){
if(ans == 98){
console.log("テスト"+count+"成功");
return true;
}
}else if(count == 3){
if(ans == 495){
console.log("テスト"+count+"成功");
return true;
}
}
if(status == "failure"){
console.log(err_msg);
miss_ans = err_msg;
return false;
}
$.ajaxSetup({async: true});
}
出力paiza.IOの出力をansで受け取って、それを用意した答えと比較、全問正解なら通す感じにした。
文字小さいねって審査員の方にも言われたが、直前に組まれたものなのでやむなし。
#実際に作ってみた感想
出来は中々評価されたっぽい。嬉しい。
APIのテストサイトが改行やインデントを上手く読まなかったので、Pythonでテストするのに向かずテストに時間がかかってしまった。
JSバリバリ書いてくれたつよつよエンジニアの先輩には深く感謝しております!!!