ansjs
(2022.11.1-2024.2.15)
ansjs 231031 DownLoad from [researchmap]
設置
ansjsと正解ファイルを読み込みむ:
<script src="ans.js" type="text/javascript"></script>
<script src="a64.js" type="text/javascript"></script>
ansjsはzipファイルから展開したもの,正解ファイルは各自で作るもの,ここではa64.jsとした。そして,bodyタグに解答状態をロードする関数を書く:
<body onload="loadallvalues();">
解答状態は,ブラウザに保持されるが,それを全てクリアして初期状態に戻すには,clearallvalues関数をcallする。例えば,次のようなボタンを設置する:
<input type="button" value="clear all" onclick="clearallvalues();">
問題の書き方
チェックボタン
正誤チェックボタン
<input type="button" value="check" name="check" onclick="check(this);">
を挿入し,その親要素に,半角英数でQ1などのidをふる。そのidが正誤判定に使われる問題idになる。
idはアルファベットで始めなければならない。
ボタンのname属性はcheckで始まる名前にする。
ボタンに表示される文字valueは何でもよい。
穴埋め問題の要素は,問題idを設定した要素の子要素として(問題idを設定したタグで囲まれた部分に),readonly属性が設定されていない1行のtext入力
<input type="text" size="4">
か,複数行のtextarea
<textarea rows="2" cols="50" spellcheck="false" style="overflow: hidden;">文字列</textarea>
で設置する。選択型の要素は次のように1行のtext入力にlist属性を設けて,datalistで設置する。
<input type="text" size="4" list="q3">
<datalist id="q3">
<option>call</option>
<option>命令</option>
<option>クラス</option>
<option>メソッド</option>
</datalist>
チェックボタンを押すと,正誤判定がなされ,正解の要素は緑(lightgreen),不正解の要素は赤(pink),未解答の要素は変化しない。
チェックマーク
問題番号などを表示する要素に,
id=Q1t
のように問題idの末尾にtを付したidを振ると,その問題が全て正解の場合,その要素の末尾に✅がつく。
正解の書き方
正解はJava Scriptファイルに次のように書く。
ans64オブジェクト
正解は,問題idをキーとするオブジェクトans64の要素に配列で書く。その配列要素が順にその問題の穴埋め要素に対する正解になる:
ans64 = {
'問題id1':[要素1の正解, 要素2の正解, ...,],
'問題id2':[要素1の正解, 要素2の正解, ...,],
};
正解が順不同である穴埋め要素の組は,その配列内で,さらに配列にする。例えば,問題id1の要素1と要素2の正解が,順不同である場合,
ans64 = {
'問題id1':[[要素1の正解, 要素2の正解], 要素3の正解, ...,],
'問題id2':[要素1の正解, 要素2の正解, ...,],
}
とする。
文字列,数値・数式,正規表現
配列要素は,シングルコーテーションまたはダブルコーテーションで囲った文字列か,スラッシュで囲んだ正規表現である。
シングルコーテーションで囲んだ文字列は,解答と文字列として一致した場合正解となる。
ダブルコーテーションで囲んだ文字列は,数値・数式としてとして評価され,解答も数値・数式としてとして評価した上で比較され,一致していれば正解となる。
ダブルコーテーションで囲んだ文字列の先頭を { 末尾を } にすると,Java Scriptのプログラムとして評価される。正解なら最後の文がtrue,そうでなければfalseになるようプログラムする。
プログラムの中で,変数ansは解答,配列要素ansa[0], ansa[1], ansa[2], ..(長さansa.length)には,同じ問題の0, 1, 2, ...番目の穴埋め要素に記入された解答(文字列)が代入されている。数値に変換するにはNumber関数を使ってNumber(ans)のように,式として評価するにはeval関数を使ってeval(ans)のようにする。
他の問題の解答を参照するには,グローバル配列ansg[0], ansg[1], ..., ansg[9]を使う。参照元でansg[0]=ans; のようにして,解答をグローバル配列に代入しておき,参照先でansg[0]を参照する。
スラッシュで囲んだ正規表現は,スラッシュの右側に英小文字で正規表現のオプションを付けることができる。例)/abc/i
解答文字列の前処理
正解との比較に先立ち,解答文字列は次のように前処理される。
先頭・末尾のスペース・タブは削除される(trim)。
正解が正規表現の場合,
連続する半角空白は半角空白1つにされる。
正解が半角空白も改行も含まない場合,連続する半角空白を半角空白1つにし,単語区切の半角空白を除いて, 解答文字列の区切り文字(半角空白,改行,タブなど,\s)はすべて除去する。
正規表現で単語区切を\sと記述すれば,半角空白を含まない記述ができる。
この前処理を避けるには,正規表現の先頭に *と記述すればよい。
例
問題idがQ02-1,Q03-4,Q04-1,Q04-3,Q04-4,Q04-5,Q04-6に穴埋め要素がそれぞれ4, 5, 1, 4, 12, 8, 1箇所ある場合の正解ファイルの例:
ans64 = {
'Q02-1':[['エディタ','コンパイラ'],'ソースプログラム','機械語プログラム'],
'Q03-4':['java Hello','Visual Studio Code','Hello.java','javac Hello.java','Hello.class'],
'Q04-1':["7"],
'Q04-3':[['クラス','メソッド'],'call','クラス'],
'Q04-4':[ ['空白','改行'], '字下げ', "0", "0", "2", "2", "4", "2", "0", '同じ', '大きく'],
'Q04-5':['javac','java', '文法', '文法エラー', '論理エラー', '文法エラー', '論理エラー', '最初'],
'Q04-6':[/^System\.out\.println\("(?!.*(Hello|world)).*"\);System\.out\.println\("(?!.*(Hello|world)).*"\);/],
}
Q02-1,Q04-3,Q04-4の1番目と2番目の空所,は順不同。
正解ファイルのエンコード
正解ファイルができたら(a.jsとする)
perl ans.pl a.js
とコマンドを打って内容が簡単には読めないように,配列要素をbase64エンコードしたファイルを生成する。生成されるエンコードされた正解ファイル名は,正解ファイルのベース名に64をつけたもの,例えば,正解ファイルがa.jsならa64.jsとなる。
以下は,上の例の正解ファイルをエンコードしたもの:
ans64 = {
'Q02-1':[['J+OCqOODh+OCo+OCvyc=','J+OCs+ODs+ODkeOCpOODqSc='],'J+OCveODvOOCueODl+ODreOCsOODqeODoCc=','J+apn+aisOiqnuODl+ODreOCsOODqeODoCc='],
'Q03-4':['J2phdmEgSGVsbG8n','J1Zpc3VhbCBTdHVkaW8gQ29kZSc=','J0hlbGxvLmphdmEn','J2phdmFjIEhlbGxvLmphdmEn','J0hlbGxvLmNsYXNzJw=='],
'Q04-1':['Ijci'],
'Q04-3':[['J+OCr+ODqeOCuSc=','J+ODoeOCveODg+ODiSc='],'J2NhbGwn','J+OCr+ODqeOCuSc='],
'Q04-4':[ ['J+epuueZvSc=','J+aUueihjCc='], 'J+Wtl+S4i+OBkic=', 'IjAi', 'IjAi', 'IjIi', 'IjIi', 'IjQi', 'IjIi', 'IjAi', 'J+WQjOOBmCc=', 'J+Wkp+OBjeOBjyc='],
'Q04-5':['J2phdmFjJw==','J2phdmEn', 'J+aWh+azlSc=', 'J+aWh+azleOCqOODqeODvCc=', 'J+irlueQhuOCqOODqeODvCc=', 'J+aWh+azleOCqOODqeODvCc=', 'J+irlueQhuOCqOODqeODvCc=', 'J+acgOWInSc='],
'Q04-6':['L15TeXN0ZW1cLm91dFwucHJpbnRsblwoIig/IS4qKEhlbGxvfHdvcmxkKSkuKiJcKTtTeXN0ZW1cLm91dFwucHJpbnRsblwoIig/IS4qKEhlbGxvfHdvcmxkKSkuKiJcKTsv'],
};
上記コマンドは,VSCodeの[terminal]-[Configure Tasks...]で,tasks.jasonにブロック
{
"label": "perl ans.pl",
"type": "shell",
"command": "perl ans.pl ${file}",
"problemMatcher": [],
"group": {
"kind": "build",
"isDefault": false
}
},
を挿入すれば,VSCodeで正解ファイルを編集して,そのままメニュー[Terminal]-[Run Tasks]で実行できる(ans.plは絶対パスで書くこと)。
ハード再読み込み Shift+Ctrl+R
正解ファイルを修正した場合,ブラウザでページを再読み込みするだけでは,正解ファイル(Java script)が再読み込みされない場合があるので,キャッシュをクリアして再読み込みする「ハード再読み込み」をする必要がある。強制再読み込みはShit+Ctrl+R。
see https://qiita.com/kondo0602/items/7cc6e0e7783b3533ce2f
正解の例
サンプル参照。その他:
JavaScriptで記述する場合:
数式の正誤:
解答された数式が正しいか確認するために,幾つかの値を代入して計算される値が正しければ数式が合っていると推測して,解答された数式の正誤を判定する。
例1:解答の式が100000+7*(omosa-10000)であるか,2点omosa=10000,20000で確認する:
"{function d(omosa){return Math.abs(eval(ans)-(10000*10+(omosa-10000)*7))<1e-10;} d(10000)&&d(20000);}"
解答文字列ansをプログラムとして評価するためには,eval関数を使ってeval(ans)と書く必要がある。
記述を短くするにはアロー記法を用いるとよい:
"{var d=omosa=>return Math.abs(eval(ans)-(10000*10+(omosa-10000)*7))<1e-10; d(10000)&&d(20000);}"
例2:解答の数式がf(c)*(b-a)であるか(c=(a+b)/2であることは与えられている),f(x)=√xに対して,2点(a,b)=(0.2,0.6),(0.1,0.11)で確認する:
"{function f(x){return Math.sqrt(x);} function d(a,b){var c=(a+b)/2; return eval(ans)==f(c)*(b-a);} d(0.2,0.6) && d(0.1,0.11);}"
アロー記法
"{var f=x=>return Math.sqrt(x), d=(a,b)=>{var c=(a+b)/2; return eval(ans)==f(c)*(b-a); d(0.2,0.6) && d(0.1,0.11);}"
数値の誤差:
解答された値が誤差の範囲で正解と一致しているか確認する。
例:解答が0.02125±0.001か?
"{Math.abs(parseFloat(ans)-0.02125)<0.001;}"
条件式:
解答された条件式に含まれる変数に,(幾つかの)数値を代入して,条件式が正しいかどうか確認する。
正規表現で記述する場合:
正規表現で複数のキーワードが入っているか順不同で調べるには次のように書く:
(?=.*キーワード1)(?=.*キーワード2)(?=.*キーワード3)
クリップボードを介した解答の送信機能
LMSやメールなどを使って,穴埋めの解答をコピーして送付してもらうための関数copyallvaluesとそれを解読するバッチファイルansclip。
copyallvalues関数は,全ての穴埋め要素を正誤判定し,時刻,乱数,正解でない要素,問題idがidである要素(学籍番号や氏名),問題idの末尾がdocである要素(記述問題)の内容(value)をbase64でエンコードして,クリップボードにコピーする。
copyallvalues関数の引数は,name属性の値が引数に一致する問題のみがコピーされる。必須と発展のように問題のカテゴリーを分ける時に利用する。
バッチファイルansclip.batは(注:ansclip.batは,zipファイルに含まれる。同じくzipファイルに含まれるansclip.plが同じフォルダにないと動きません),クリップボードにコピーされた,複数の改行で区切られたエンコードされた解答を,見出し行と複数の解答行からなる,次のようなtsv(tab separated value)形式に変換する:
score \t \t \t 問題id \t 問題id...
正答率% \t 年/月/日 時:分:秒 \t 乱数 \t 値 \t 値 ...
正答率% \t 年/月/日 時:分:秒 \t 乱数 \t 値 \t 値 ...
...
ここで,\tはタブ。1行目は見出しで,
問題idは,同じ問題idに複数の空所がある場合は,2つ目以降の空所に対する見出しは空白。
問題idが「id」である問題は出現位置が後ろでも最初に表示される。
copyallvalues関数に引数がある場合は,見出しのscoreの後に「for その値」が付加される。
2行目以降の値は,
記述問題でない場合:
正答なら空白,誤答なら解答,空白の場合は !
記述問題の場合:
正答なら解答,誤答なら解答に ! を付加した解答,空白の場合は !
例:
score for bas Q02-1
50% 2023/2/18 22:52:21 547 300MB
50% 2023/2/18 22:48:50 439 -
tsv形式は,表計算ソフトにそのまま貼り付けられる形式である。この機能によって,以下のようにして解答一覧を得ることができる:
学生はクリップボードにコピーした解答をLMSの記述問題に貼り付けて提出する。
教師はスプレッドシートに全学生の解答をダウンロードして,学生が解答を貼り付けた列を選択,コピーする。ansclip.batは動かしておく。
選択を開始したセルの右上のセルで「貼り付け」ると,得点と誤答,記述問題が見出し付きでシートに入力される。
乱数と時刻が一致する解答は,他の学生の解答のコピーであると推測できる。
サンプル
Windowsでローカルに使うには
Windowsで,[researchmap]から配布zipファイルをダウンロードして,展開して,simpleフォルダ内のindex.htmlをダブルクリックするとデフォルトブラウザで以下のようなシンプルな例が表示されます。これを編集することで,ansjsをWindowsで使う方法を説明します。
エディタ
HTMLとJavaScriptを編集するエディタを準備します。メモ帳でもかまいませんが,Visual Studio Code(VSCode)をインストールするとより便利です。VSCodeはSystem Installerでインストールしてください。
HTMLファイル編集
VSCode(またはメモ帳)を起動して,index.htmlをドラッグ&ドロップして開きます。「1から10までの和」を「1から5までの和」に書き換えて,[File]-[Save]で上書き保存します。
正解ファイル編集
次に,正解ファイルa.jsをVSCode(またはメモ帳)にドラッグ&ドロップして開き,55を15に書き換えます。[File]-[Save]で上書き保存します。
WSLのインストール
a.jsをエンコードするためにperlを使うので,WSLをインストールする必要があります。コマンド プロンプトを管理者モードで開き,wsl --installと打ってEnterして,指示に従ってインストールしてください。
エンコード
zipファイルを展開したフォルダにあるans.bat(⚙2つのアイコンのファイル)に,a.jsをドラッグ&ドロップします。エンコードされたファイルa64.jsが作成され,元のa64.jsが上書きされます。
以上で,HTMLの編集,正解の編集,エンコードの方法がわかったと思います。さらに,VSCodeに以下の設定をしておくと,VSCodeのメニューからエンコードができるようになります。
方法1 ans.batの登録
エンコードは,VSCodeの[Terminal]-[Configure tasks]に
{
"label": "ans.bat",
"type": "shell",
"command": "ans.bat ${file}",
"problemMatcher": []
},
以下のようなブロックを追加することで,a.jsを編集ながらメニュー[Terminal]-[Run Task...]で[ans.bat]を選ぶことで実行できるようになります。なお,ans.batは絶対パスで書いて下さい(c:/Users/ユーザ名/ans.batなど)。
方法2 ans.pl登録
VSCodeにWSL拡張機能をインストールします(インストールをクリックする)。インストールが終了したら,コマンドプロンプトで,wsl -u rootと打ってwslを起動して,続いてcodeと打ってVSCodeを起動して,WSL拡張を有効にします。VSCodeはすぐに終了します。
WindowsからVSCodeを起動し,[terminal]-[Configure Tasks...]で,tasks.jasonにブロック
{
"label": "perl ans.pl",
"type": "shell",
"command": "perl ans.pl ${file}",
"problemMatcher": [],
"group": {
"kind": "build",
"isDefault": false
}
},
を挿入します。ans.plはWSL形式の絶対パスで書いて下さい(/mnt/c/Users/ユーザ名/ans.plなど)。これで,VSCodeで正解ファイルを編集して,そのままメニュー[Terminal]-[Run Tasks]で実行できます。
設定したメニューを使うには:VSCodeを実行し,ウインドウ左端のRemote Explorerアイコンをクリックして,上部のREMOTE EXPLORERからWSL Targetsを選び,編集したいjsファイルをWSLから開いてください。