おはやし日記

テーマ……バイク←プログラミング←旅

ナンプレ攻略プログラムがWebで使えるようになりました

o-treetree.hatenablog.com

1ヶ月ほど前にこれ↑が完成しました。

C言語で、(大抵の)ナンプレを解いてくれるプログラムです。

これをJavaScriptに書き換えて、Web上で使えるようにしました!

oha-yashi web test

↑こちらから。

特に機能に変わりはないですが、ボタン一発で解答できるようになったのはでかいですね

別に使い方は見ればわかると思うので、C→JSの移植で大変だったところをメモする。

本題は終了。

ちょっとまともなところがある。初歩の初歩の話だが

変更点

データの取り扱い、メソッド化

hoge.fuga()みたいなやつできればいいなぁってなったのでクラスを使ってみた

Cでは

typedef unsigned short np;

だったのをJSでは

class np{
    /** @param {Number} d */
    constructor(d){
        this.d = d;
    }

にしてみた。

calc.hの

#define NUM(d) ((d)>>12)
#define set_num(d, i) d = d & 0x0fff | (i<<12)
#define BLOCK(n) (LINE(n)/3*3 + ROW(n)/3)

をJSで

class np{
  NUM(){ return this.d>>12 }
  set_num(i){ this.d = this.d & 0x0fff | (i<<12) }
}
function BLOCK(n){return Math.floor(LINE(n)/3)*3 + Math.floor(ROW(n)/3)}

としたり。

そんなにいいことはなかった。書き換えがめんどくなった。

set_num(data[n], i)data[n].set_num(i)になったのでその辺をまるっと修正する必要があった。

あとは、整数÷整数がCのような切り捨てではなくJSだとご丁寧に小数にしてくれるのでMath.floorをかまさなければいけなかった。

データのみを扱う操作くらいしかメソッドにできない割にあとでめんどくさくなった

関数の書き換え

functionの引数に型を明示しなくていいのは楽だと思った

    /** @c NUM(data[n]) @js data[n].NUM() */
    NUM(){ return this.d>>12 }
    /** @c FLAG(data[n], i) @js data[n].FLAG(i) */
    FLAG(i){ return this.d>>i & 1 }

↑この辺の書き換えが面倒だった

read_and_delete_lrb

//@c
void read_delete_lrb(int start, int kouho, int byA, int byB, int byC, char c){
    int sum[3] = {}; int keep[3] = {};
    int i, j, k, roop, sum_all, cont;

//中略 keepに3つ数字が入る

    for(i=0; i<3; i++)for(j=0; j<3; j++){
        roop = start + i*byB + j*byC;

        for(k=0; k<3; k++){
            if(roop == keep[k]) goto NEXT;
        }
        flag_off(data[roop], kouho);
        NEXT:;
    }
}
//@js現行ver.
function read_delete_lrb(start, kouho, byA, byB, byC, c){
    var sum = [3]; var keep = [3];//←改めてみたら変だったので後で直した
    var i, j, roop, sum_all, cont;

//中略 keepに3つ数字が入る
    
    for(i=0; i<3; i++)for(j=0; j<3; j++){
        roop = start + i*byB + j*byC;

        if(roop===keep[0] || roop===keep[1] || roop===keep[2]){
            //何もしない
        }else{
            data[roop].flag_off(kouho);
        }
    }
}

roopはkeepのどれかに一つのみに一致するか、あるいはどれにも一致しない。

最初cの形をそのままjsに書き換えたらおかしくなった。gotoから↓みたいなcontinueにしたんだが

cの方自体は問題なく動作していたはずなのだが…………わからん

もう一回書き直してみたらちゃんと動作した。まあ現行の方が条件がわかりやすいな

//@js書き直し
//前略

    NEXT:
    for(i=0; i<3; i++)for(j=0; j<3; j++){
        roop = start + i*byB + j*byC;

       for(var k=0; k<3; k++){
           if(roop===keep[k]){
               continue NEXT;
           }
       }
       data[roop].flag_off(kouho);
    }
}

only_two_pair_lrb

//@c
void only_two_pair_lrb(int start, int byA, int byB){
    int i, j, k, l, roop, cnt;
    int q_k[10] = {};
    for(k=1;k<=9;k++){
        for(i=0;i<3;i++)for(j=0;j<3;j++){
            roop = start + i*byA + j*byB;
            if(FLAG(data[roop], k))q_k[k]++;
        }
    }
    int pair_k_in[10][2] = {};
    
    for(k=1; k<=9; k++){
        cnt = 0;
        if(q_k[k] == 2){
            for(i=0;i<3;i++)for(j=0;j<3;j++){
                roop = start + i*byA + j*byB;
                if(FLAG(data[roop], k)){
                    pair_k_in[k][cnt] = roop;
                    cnt++;
                }
            }
        }
    }
    //ブロック内で2箇所のみkが入るマスがpair_k_in[k][2]に入った
    for(k=1; k<=8; k++){//k<=8で正しい
        if(q_k[k] == 2){
            for(l=k+1; l<=9; l++){
                if(pair_k_in[k][0] == pair_k_in[l][0]
                && pair_k_in[k][1] == pair_k_in[l][1]){
                    //pairの組が一致したらそこでkとlが独占
                    data[ pair_k_in[k][0] ] = data[ pair_k_in[k][1] ] = 0x0;
                    data[ pair_k_in[k][0] ] = data[ pair_k_in[k][1] ] |= 1<<k;
                    data[ pair_k_in[k][0] ] = data[ pair_k_in[k][1] ] |= 1<<l;
                    //フラグ立てをkとlのみにする
                }
            }
        }
    }
}
//@js
function only_two_pair_lrb(start, byA, byB){
    var i, j, k, l, roop, cnt;
    var q_k = new Array(10).fill(0);//0で初期化
    for(k=1;k<=9;k++){
        for(i=0;i<3;i++)for(j=0;j<3;j++){
            roop = start + i*byA + j*byB;
            if(data[roop].FLAG(k))q_k[k]++;
        }
    }

    var pair_k_in = Array2Dnull(10, 2);
    
    for(k=1; k<=9; k++){
        cnt = 0;
        if(q_k[k] == 2){
            for(i=0;i<3;i++)for(j=0;j<3;j++){
                roop = start + i*byA + j*byB;
                if(data[roop].FLAG(k)){
                    pair_k_in[k][cnt] = roop;
                    cnt++;
                }
            }
        }
    }
    //ブロック内で2箇所のみkが入るマスがpair_k_in[k][2]に入った
    for(k=1; k<=8; k++){//k<=8で正しい
        if(q_k[k] == 2){
            for(l=k+1; l<=9; l++){
                if(pair_k_in[k][0] == pair_k_in[l][0]
                && pair_k_in[k][1] == pair_k_in[l][1]){
                    //pairの組が一致したらそこでkとlが独占
                    data[ pair_k_in[k][0] ].d = data[ pair_k_in[k][1] ].d  = 0x0;
                    data[ pair_k_in[k][0] ].d = data[ pair_k_in[k][1] ].d |= 1<<k;
                    data[ pair_k_in[k][0] ].d = data[ pair_k_in[k][1] ].d |= 1<<l;
                    //フラグ立てをkとlのみにする
                }
            }
        }
    }
}
罠にはまるその1
//@c
int a[10] = {};
for(int i=0; i<10; i++){
 a[i]++; 
}

これ(概略)をJSにしようとして最初こうやった

//@js
var a = new Array(10);
for (i=0; i<10; i++){
 a[i]++; 
}

失敗。ここでaは空白の配列なので元々a[i] = undefined。インクリメントできずNaNになる

//ブラウザコンソール
> var a = new Array(10);
for (i=0; i<10; i++){
 a[i]++; 
}
< NaN
> a
< [NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN] (10)

こうすれば良かった

var a = [0,0,0,0,0,0,0,0,0,0];
for (i=0; i<10; i++){
 a[i]++; 
}
//ブラウザコンソール
> var a = [0,0,0,0,0,0,0,0,0,0];
for (i=0; i<10; i++){
 a[i]++; 
}
< 0
> a
< [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] (10)

でもカッコよくないのでこうした

var a = new Array(10).fill(0);
for (i=0; i<10; i++){
 a[i]++; 
}
//ブラウザコンソール
> var a = new Array(10).fill(0);
for (i=0; i<10; i++){
 a[i]++; 
}
< 0
> a
< [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] (10)
罠にはまるその2(?)

多次元配列がJavaScriptには無い(?)のでつくらんといけない、ということで

function Array2Dnull(p, q){
    var a = new Array(p);
    for(var i=0; i<p; i++){
        a[i] = new Array(q);
    }
    return a;
}

var a = Array2Dnull(10, 2);
罠にはまるその3(?)

Cの時data[n]自体をビット演算できた(ただの整数型なので)けどJSだとdata[n].dにしなければいけなかった

まあこれは単に面倒だったという話。

HTMLを使う利点

マス目をinputで作る

jQueryは便利ですね。

/** <input>を入れる配列 */
var masus = [];
/** viewFieldに9*9マスを描画する */
function printMasu(){
    for(var i=0; i<9; i++){
        newRow = $("<div>").appendTo(viewField);
        for(var j=0; j<9; j++){
            roop = i*9 + j;
            masus[roop] = $("<input>").attr({
                type:"text", id:"box"+roop, maxLength:1
            }).css({
                borderStyle       : "solid",
                borderColor       : "black", 
                borderTopWidth    : i%3===0 ? "medium" : "thin",
                borderBottomWidth : i%3===2 ? "medium" : "thin",
                borderLeftWidth   : j%3===0 ? "medium" : "thin",
                borderRightWidth  : j%3===2 ? "medium" : "thin"
            }).val(
                data[roop].NUM()===0 ? "" : data[roop].NUM()
            ).appendTo(newRow);
        }
    }
}

iのループで縦9行の、9列inputを並べるためのdiv要素newRowを作る。サイズとかはCSSで整える。

jのループではroop=i*9+jについて

masu[roop]に対して、input要素を作って、typeとidを決めてmaxLengthを指定し、cssをマスの位置によって設定し、dataによってvalueを放り込み、newRowの末尾につける

これが一文でできるとは。

css設定のborderStyleとborderColor、cssファイルに書いてみたら適用されませんでした。CSS読み込んだ後にinputを生成したからなんですかね…………CSSの同じ場所に書いたwidthとかheightは適用されてるのに…………謎。

マスに入れた数字をデータに読み込んで、処理して、マスに反映させるというのが大きな流れですねぇ

ボタンが作れる

ボタンのclickイベントに関数を結びつけることでチェックが楽になった

よかったね。

おしまい。

プライバシーポリシー ・お問い合わせはこちら