1ヶ月ほど前にこれ↑が完成しました。
これをJavaScriptに書き換えて、Web上で使えるようにしました!
↑こちらから。
特に機能に変わりはないですが、ボタン一発で解答できるようになったのはでかいですね
別に使い方は見ればわかると思うので、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イベントに関数を結びつけることでチェックが楽になった
よかったね。
おしまい。