以下のURLの7行テトリスのプログラムを、プログラミング初心者(プログラミング言語共通の「教科書的な文法」を理解しようやくアルゴリズムを勉強する重要性がわかってきた程度の)が理解できる初心者向け入門書に載っているような基本的な記述方法で書き直して下さい(javascript)、コードは動作確認が取れたものをご回答下さい。
こちらも理解する努力はするつもりです。変数は増やしても、名称を変えても、コードが長くなっても構いませんのでよろしくお願いします。その際、その処理は何をするのかなどコメントもつけて下さると嬉しさに涙があふれます。
参考:
(公式解説)http://web.archive.org/web/20060111010926/http://www.isl.cs.gunma-u.ac.jp/~shingo/make/7line/teto.html
(紹介ブログ)http://zapanet.info/blog/item/1125
私も興味があったので先ほど読んでみました。
(書き換えたコードはインデントが全角スペースになっています)
>A||B は、Bに副作用が無ければ A|B あるいは A+B に等価 ?
副作用とは、例えば文Bが x=1 という文だとすると、文Bの評価値は(代入演算子'='は代入した値を返すので)1になります。
このとき代入演算子の作用としてxに1が代入されますが、この、文Bが評価されることで"変数xの値が変わること"を文Bの副作用と言います。
>また、等価というのはA=0,B=0の場合A||BはA=B=falseなのでA||B=false。A|B=0+0=0=falseという意味でしょうか?
最終的に真理値(bool)として評価されるのでそのとおりです。
(ただし、A=-2,B=+2などでA+B=0となる場合があるときには成り立たなくなります)
>7: C[i]=p*A-(p/9|0)*145;//A=12,p=x+y*12
A=12,p=x+y*12,(p/9|0)=yを使って代入演算子の右辺を書き換えてみます。
(右辺)=(x+y*12)*12-y*145=x*12+y*144-y*145=-y+x*12。
つまりp=(x,y)をp'=(-y,x)に写していることになります。
>k 1行消去で加算する得点、毎回どこかで 0 か 1 に初期化する必要がある
>という説明ですが、「どこか」というのは12行目だと思うのですが、なぜ初期かは0でも1でもよいのでしょうか?
見たところkを使っているのは12行目(初期化)と21行目(得点Pへの加算)だけのようなので
得点が1→3→6→10と増えるか0→1→3→6と増えるかの違いだけのようです。
>18: Z=X,X=[],
>(略)Zは元Xを向いていて、新Xは新しい配列である空の配列に向けられるという理解で合うっていますか?
合っています。
>19: B=[[-7,-20,6,h=17,-9,3,3][t=++t%7]-4,0,1,t-6?-A:2];
書き換えると
h=17;
t = ++t%7;
TMP=[-7,-20,6,17,-9,3,3];
b0 = TMP[t] -4;
b1 = 0;
b2 = 1;
if(t != 6){
b3 = -12;
}else{
b3 = 2;
}
B = [b0,b1,b2,b3];
のようになります。(わざわざb0で-4しているのは文字数節約のためだと思います)
>20: for(l=228;l--;){
>lというのはブロックの座標を表しているんですよね?下から2段目より上のブロックを全て調べるということで合っていますか?
はい。
>21: for(l%A?l-=l%A*!Z[l]:(P+=k++,c=l+=A);--c>A;){
書き換えると
if(l%12 != 0){
// lが左端のブロックでなければ
if(!Z[l]){
// lにブロックがなければ1つ上の行の右端に移動
l -= l%12;
}
// lにブロックがあれば左のブロックへ
}else{
// lが左端のブロックであれば
P += k++;//得点の加算
l += 12; //lを今の行の右端に戻す
c = l; //次のforのためのカウンターセット
}
//上にあるブロックを1行ずつ下げる処理
for(;--c>12;){
Z[c] = Z[c-12];
}
となります。
28: S+=X[i]|(X[i]=Z[i]|=++i%A<2|i>228)?i%A?"■":"■<br>":"_";
書き換えると
x = X[i]; // 前フレームの固定ブロックと現在の落下中ブロックを退避
side = ++i%A<2; // 左右の端かどうか。左端のとき++i%A=1,右端のとき++i%A=0になる。
// 元のコードではこのインクリメントはあとで実行されるので
// 以下ではi→i-1として補正しています
bottom = i>228; // 底面かどうか
Z[i-1] |= (side|bottom); // Zにフィールドの縁のブロックを合成
X[i-1] = Z[i-1]; // Xにも上書き、落下中ブロックは消去。落下ブロックは10行目で追加される
if(x|X[i-1]){
//ブロックの描画
if(i%A){
S+="■";
}else{
//i%Aが0のとき、すなわち右端のとき
S+="■<br>";
}
}else{
//空白の描画
S+="_";
}
となると思います(コメントにはやや自信がありません)
理解の助けになれば幸いです。
回答ありがとうございます!
大変分かりやすく、該当箇所はほぼ理解することができました!
ただ、まだ一点不明の箇所があります。
7 C[i]=p*A-(p/9|0)*145=-(p/9|0)+(p-(p/9|0)*12)*12;
x,yを回転行列で-90度回転させると-y,xとなる。これをp=x+y*12の式に入れるとp=-y+x*12。
解説に「ここでは、Math.roundでありフィールドの大きさが小さいためp/9|0がyに等価」とありますが、y=Math.round(p/12)ではないのですか?何故Math.round(p/12)がmath.floor(p/9)=p/9|0となるのか手取り足取り教えていただけますか。p=x+y*12で定義されていたのでは?と混乱しています。