作業中のメモ

よく「計算機」を使って作業をする.知らなかったことを中心にまとめるつもり.

CAB の命令表を解くツール作成

どうも,筆者です.

時期的には,就活も終わり休みに入っている方が多いと思う.貴重な長期休暇を満喫してほしい.就活と言えば,面接等嫌な思い出がよみがえる.その中でも避けて通れないものとして,Webテストがある. 今回はその中でも比較的機械的に解ける CAB の「命令表」に注目した.

CAB では,暗算(四則演算),法則性,命令表,暗号解読がある.Web でやる場合,どれも難易度が高く,ひとつでも良いので楽をしたいと考えるのが人間である. ここでは,効率良く解くために,命令表の支援ツールを作成した.筆者は,最近,趣味で javascript を学び始めたので,javascript と HTML を利用して作成した.

命令表とは

ツールの紹介をする前に,命令表がどんなものであるか,紹介しておこうと思う.命令表は,図のような 10 種類の命令記号を指示通りに実行した場合にどのような結果になるかを考える問題である. ちなみに,以下の図は例として説明するために,筆者が作成したものである. f:id:mathematicsphysical:20170825230024p:plain

慣れている人はすぐに解けると思うが,多くの人は時間をかけて解くことになる.このような問題が 50 問出題され,制限時間 20 分で解く必要がある.選択肢はあるものの難易度は高い. これをツールの力を借りて解こうというのが今回の目的である.

作成したもの

作成したものを見てもらう方が早いと思う.以下に図を示す. f:id:mathematicsphysical:20170826100653p:plain

問題の図は表示されない代わりに,問題の図を上から順に 1 番,2 番,3 番,4 番と番号を振ったものと見なし,問題を解いていく.答えも番号で与えられ,上下反転・左右反転は,数字の後ろに「上下」や「左右」が付加される. また,図形が削除される場合,該当する解は空欄になる.

コード

今回は,HTML,CSSJavascript を利用して作成した.以下にコードを示す.HTML 5 が動作するブラウザなら動作するはずである.

<!DOCTYPE HTML>
<html lang="ja">
<head>
   <meta charset="utf-8">
   <title>CAB 命令表</title>

   <!-- CSS -->
   <style type="text/css">
        .backGroundPanel {
            /* いい感じに描画領域を決める */
            width: 60%;
            min-width: 400px;
            margin-left: 1%;
        }
        
        .dataPanel {
            /* いい感じにデータパネルの設定をする */
            width: 100%;
        }
        .dataPanel table {
            /* いい感じにテーブルの設定する */
            width: 100%;
            border: 1px #000 solid;
            border-collapse: collapse;
            border-spacing: 0px;
            -ms-user-select: none;
            -webkit-user-select: none;
            user-select: none;
            cursor: default;
            text-align: center;
            vertical-align: middle;
        }
        .dataPanel button {
            /* いい感じにボタンの大きさを決める */
            width: 100%;
            font-size: 110%;
        }
        .radioButton {
            /* ラジオボタンは表示しない */
            display: none;
        }

        .exeList {
            /* いい感じに配置 */
            width: 100%;
            
        }
        .exeList button {
            /* いい感じにボタンを配置する */
            width: 45%;
            margin-left: 2.5%;
            margin-right: 2.5%;
        }

        .usage {
            width: 100%;
        }
    </style>

   <!-- javascript -->
   <script type="text/javascript">
       // ======================== //
       // Coded by: certain person //
       // ======================== //
       var g_command = [];       // 入力したコマンド
       var g_maxData = 4;        // データ数
       var g_invalidCommand = 0; // 無効なコマンド
       var g_useBGC = "#0f0";    // 使用する背景色
       var g_whiteBGC = "#fff";  // 白色の背景色

       // 色の設定
       var setColor = function (idx, colorStr) {
           document.getElementById("cell" + idx).style.backgroundColor = colorStr;
       }

       // 初期化
       var initData = function () {

           for ( var i = 0 ; i <= g_maxData ; i++ ) {
               document.getElementById("input" + i).innerHTML = "";
               document.getElementById("output" + i).innerHTML = "";
               setColor(i, g_whiteBGC);
               g_command[i] = g_invalidCommand;
           }
           setColor(0, g_useBGC);
           document.getElementsByName("data")[0].checked = true;
       };

       // ロード時
       window.onload = function () {
           initData();
       };

       // 選択されているラジオボタンを取得
       var getSelectedRadio = function () {
           //ラジオボタンオブジェクトを取得する
           var radios = document.getElementsByName("data");
           var idx;

           for ( var i = 0, len = radios.length ; i < len ; i++ ) {
               if ( radios[i].checked ) {
                   idx = i;
                   break;
               }
           }

           return idx;
       };

       // 命令をクリックしたときの動作
       var deleteNum = function (idx) {
           for ( var i = 0 ; i < g_maxData ; i++ ) {
               setColor(i, g_whiteBGC);
           }
           document.getElementById("input" + idx).innerHTML = "";
           document.getElementsByName("data")[idx].checked = true;
           setColor(idx, g_useBGC);
       };
       // オプションをクリックしたときの動作
       var deleteOpt = function (idx) {
           document.getElementById("input" + idx).innerHTML = "";
           setColor(idx, g_whiteBGC);
       };

       // 値の設定
       var setValue = function (val) {
           var idx;

           // 図形の場合
           if ( val < 8 ) {
               idx = getSelectedRadio();
               setColor(idx, g_whiteBGC);
               document.getElementsByName("data")[idx].checked = false;
               if ( idx + 1 < g_maxData ) {
                   setColor(idx + 1, g_useBGC);
                   document.getElementsByName("data")[idx + 1].checked = true;
               }
           }
           // オプションの場合
           else {
               idx = g_maxData;
               g_command[g_maxData] = val;
               setColor(idx, "#f8f");
           }
           document.getElementById("input" + idx).innerHTML = val;
       };

       // 実行
       var execute = function () {
           // データの設定
           var invalidData = 0; // 無効なデータ
           var dataList = [];   // 出力するデータ
           var idxList = [];    // 出力する順番
           // データの初期化
           for ( var i = 0 ; i < g_maxData ; i++ ) {
               g_command[i] = parseInt(document.getElementById("input" + i).innerHTML);
               dataList[i] = i + 1;
               idxList[i] = i;
           }
           g_command[g_maxData] = parseInt(document.getElementById("input" + g_maxData).innerHTML);
           // 最終的な表示順序を決める
           if ( g_command[g_maxData] !== g_invalidCommand ) {
               switch ( g_command[g_maxData] ) {
                   case 8:
                       idxList[0] = 3;
                       idxList[1] = 2;
                       idxList[2] = 1;
                       idxList[3] = 0;
                       break;

                   case 9:
                       idxList[0] = 2;
                       idxList[1] = 3;
                       idxList[2] = 0;
                       idxList[3] = 1;
                       break;

                   case 10:
                       idxList[0] = 1;
                       idxList[1] = 0;
                       idxList[2] = 3;
                       idxList[3] = 2;
                       break;

                   default:
                       break;
               }
           }

           // ========================
           // 命令を消すコマンドの探索
           // ========================
           var deletePrevCommand = 6; // 前の命令を消す
           var deleteNextCommand = 7; // 次の命令を消す

           // 前の命令を消すコマンドがあるかどうか探索する(下から探索する)
           for ( var i = g_maxData - 1 ; i >= 0 ; i-- ) {
               // 前の命令を消すコマンドの場合
               if ( deletePrevCommand === g_command[i] ) {
                   if ( i > 0 ) {
                       g_command[i - 1] = g_invalidCommand;
                   }
                   g_command[i] = g_invalidCommand;
               }
           }
           // 次の命令を消すコマンドがあるかどうか探索する(上から探索する)
           for ( var i = 0 ; i < g_maxData ; i++ ) {
               // 次の命令を消すコマンドの場合
               if ( deleteNextCommand === g_command[i] ) {
                   if ( i + 1 < g_maxData ) {
                       g_command[i + 1] = g_invalidCommand;
                   }
                   g_command[i] = g_invalidCommand;
               }
           }

           // ==========
           // 図形の操作
           // ==========
           var deletePrevFigure = 3; // 前の図形を消す
           var deleteNextFigure = 4; // 次の図形を消す
           var changePrevFigure = 5; // 前の図形と交換する
           for ( var i = 0 ; i < g_maxData ; i++ ) {
               // 前の図形を消すコマンドの場合
               if ( g_command[i] === deletePrevFigure ) {
                   if ( i > 0 ) {
                       dataList[i - 1] = invalidData;
                   }
                   g_command[i] = g_invalidCommand;
               }
               // 次の図形を消すコマンドの場合
               else if ( g_command[i] === deleteNextFigure ) {
                   if ( i + 1 < g_maxData ) {
                       dataList[i + 1] = invalidData;
                   }
                   g_command[i] = g_invalidCommand;
               }
               // 前の図形と交換するコマンドの場合
               else if ( g_command[i] === changePrevFigure ) {
                   if ( i > 0 ) {
                       var tmp = dataList[i - 1];
                       dataList[i - 1] = dataList[i];
                       dataList[i] = tmp;
                   }
                   g_command[i] = g_invalidCommand;
               }
               // 上記以外の場合
               else {
                   // 何もしない
                   ;
               }
           }

           // ========================
           // 上下反転・左右反転の処理
           // ========================
           var changeUpperLower = 1; // 上下反転のコマンド
           var changeLeftRight = 2;  // 左右反転のコマンド
           for ( var i = 0 ; i < g_maxData ; i++ ) {
               // 図形が無い場合
               if ( dataList[i] === invalidData ) {
                   dataList[i] = "";
               }
               // 図形がある場合
               else {
                   // 上下反転のコマンドの場合
                   if ( g_command[i] === changeUpperLower ) {
                       dataList[i] += " 上下";
                   }
                   // 左右反転のコマンドの場合
                   else if ( g_command[i] === changeLeftRight ) {
                       dataList[i] += " 左右";
                   }
                   // 上記以外の場合
                   else {
                       // 何もしない
                       ;
                   }
               }
           }

           // 出力
           for ( var i = 0 ; i < g_maxData ; i++ ) {
               document.getElementById("output" + i).innerHTML = "<font color='#f00'>" + dataList[idxList[i]] + "</font>";
           }
       };
   </script>
</head>
<body>
    <div class="backGroundPanel">
        <h2 align="center">CAB 命令表</h2>
        <p align="center">いい感じに命令表の問題を解く。</p>

        <div class="dataPanel" align="center">
            <table rules="all">
                <tr><th>番号</th><th>命令</th><th colspan="2">ボタン</th><th>実行結果</th></tr>
                <tr>
                    <th><input type="radio" name="data" class="radioButton">&nbsp;1&nbsp;</th>
                    <td id="cell0" onclick="deleteNum(0);"><span id="input0"></span></td>
                    <td><button type="button" onclick="setValue(1);">1. 上下反転</button></td>
                    <td><button type="button" onclick="setValue(6);">6. 前命令消</button></td>
                    <td><span id="output0"></span></td>
                </tr>
                <tr>
                    <th><input type="radio" name="data" class="radioButton">&nbsp;2&nbsp;</th>
                    <td id="cell1" onclick="deleteNum(1);"><span id="input1"></span></td>
                    <td><button type="button" onclick="setValue(2);">2. 左右反転</button></td>
                    <td><button type="button" onclick="setValue(7);">7. 次命令消</button></td>
                    <td><span id="output1"></span></td>
                </tr>
                <tr>
                    <th><input type="radio" name="data" class="radioButton">&nbsp;3&nbsp;</th>
                    <td id="cell2" onclick="deleteNum(2);"><span id="input2"></span></td>
                    <td><button type="button" onclick="setValue(3);">3. 前の図消</button></td>
                    <td><button type="button" onclick="setValue(8);">8. 4321</button></td>
                    <td><span id="output2"></span></td>
                </tr>
                <tr>
                    <th><input type="radio" name="data" class="radioButton">&nbsp;4&nbsp;</th>
                    <td id="cell3" onclick="deleteNum(3);"><span id="input3"></span></td>
                    <td><button type="button" onclick="setValue(4);">4. 次の図消</button></td>
                    <td><button type="button" onclick="setValue(9);">9. 3412</button></td>
                    <td><span id="output3"></span></td>
                </tr>
                <tr>
                    <th>opt</th>
                    <td id="cell4" onclick="deleteOpt(4);"><span id="input4"></span></td>
                    <td><button type="button" onclick="setValue(5);">5. 前図交換</button></td>
                    <td><button type="button" onclick="setValue(10);">10. 2143</button></td>
                    <td><span id="output4"></span></td>
                </tr>
            </table>
            <br />
            <div class="exeList">
                <button type="button" onclick="execute();">実行</button><button type="button" onclick="initData();">リセット</button>
            </div>
            <hr>
        </div>

        <div class="usage">
            <h2 align="center">ボタンを押したときの動作</h2>
            <ul>
                <li>1~7のボタンを押した場合、命令項目のうち、緑色のセルに値が設定される。</li>
                <li>8~10のボタンを押した場合、命令項目のうち、ピンク色のセルに値が設定される。</li>
                <li>リセットのボタンを押した場合、ロード時の状態に戻る。</li>
                <li>実行のボタンを押した場合、処理が行われ、実行結果項目に結果が表示される。図形が削除された場合、空欄となる。
                実行結果に表示される数字は、番号項目にある数字に対応する。</li>
            </ul>

            <h2 align="center">命令のセルをクリックしたときの動作</h2>
            <p>命令項目のセルを選択すると、内容が消去され、選択したセルから再び入力できる。</p>
        </div>
    </div>
</body>
</html>

このコードをコピーし,適当なテキストエディタ(メモ帳など)に貼り付ける.その後,拡張子を「.html」として保存する.既存の Web ブラウザで立ち上げるとツールが使えるようになる. 筆者は,Google Chrome で動作を確認している.知識がある人は,CSS や HTML や Javascript を書き換えて自分好みのものに改造してもらってかまわない.

調べつつ作ったものなので,出来はあまり良くない.Javascript は記述を間違えると静かに落ちるので,苦労した.バグを見つけたら連絡してほしい.以上.

追記 2017/08/26

試しにコードをコピペして,メモ帳に貼り付けて保存したら文字化けした.Windows文字コードが Shift-JIS であるためだ. 解決策として,メモ帳の場合,「名前を付けて保存」をクリックし,文字コード(保存ボタンの左側)を「ANSI」から「UTF-8」に変更して保存すればよい.