Processinでテトリスを作ろう
TET "L" ISなのはパチもんだから。
一気に書いてないので、語尾が統一されてませんが悪しからず。
冗長なところは省略があるのでソースから適宜補完してってね
## 仕組み
2次元行列でブロックの配置を管理して、動く位置に障害物がなかったら移動、みたいな・・・
あとついでにCUIでプレイできたら面白そう(Javaでやれ)
ということでCUIベースで作っていく。
作らなきゃならんのは、ブロックを下ろす、着底、揃ったら消す、次のブロック下ろす そして表示する作業。
###クラス
メインクラスと、ブロックの座標とか色とかのOBJクラス、座標をAAA.x AAA.yとかでgetしたかったので座標のZクラスを作った。
以降ブロックのことをオブジェと呼ぶ。
OBJクラス
private int type ;
private Z[] z = new Z[4];
private int foword = 1 ;
public OBJ( int type){
this.type = type ;
switch (type){
case 1:
z[0] = new Z(1,0);
z[1] = new Z(2,0);
z[2] = new Z(0,1);
z[3] = new Z(1,1);
break;
case 2:
z[0] = new Z(0,0);
z[1] = new Z(1,0);
z[2] = new Z(1,1);
z[3] = new Z(2,1);
break;
}
}
typeがT型とか四角とか棒とか。
type指定したコンストラクタは、それぞれのオブジェの初期位置をゴリ押しで打ち込む。
ちなみに1からZ,逆Z、J、逆J、棒、四角、T型
zがクラスZの配列(ややこしい)コンストラクタにあるように実際の座標が入る
fowordは向いてる向き。回転するとインクリメントする。
Zクラス
private int x;
private int y;
public Z(int x, int y) {
this.x = x;
this.y = y;
}
そのまんま。
settings()等
void settings() {
size(960 , 860);
}
final int Xnum = 10;
final int Ynum = 15;
int[][] field = new int[Xnum][Ynum];
int[][] created = new int[Xnum][Ynum];
OBJ Target = new OBJ(1+(int)random(7));
void setup() {
//under line fill 3
for (int i = 0; i < Xnum; i++) {
field[i][Ynum - 1] = 9;
created[i][Ynum - 1] = 3;
}
}
とりあえずサイズ決めて、枠を10 x 15で作ることにする。後で変えてもいいようにFinalでdefineしとく
降りてくるオブジェをTargetて呼んで、型はランダムで生成する。
setupで最下段(indexはYnum-1)を0でない数字にして土台とする(今思うとなくてもいいのでは)
数字は適当。fieldの数字でブロックに色がつくので、ブロック1~7種以外のナンバリングを
draw()中
for (int i = 0; i < Ynum - 1; i++) { //y
for (int j = 0; j < Xnum; j++) { //x
field[j][i] = 0;
}
}
for (int i = 0; i < Ynum - 1; i++) { //y
for (int j = 0; j < Xnum; j++) { //x
if (created[j][i] > 0) {
field[j][i] = created[j][i];
}
}
}
まず、表示用配列fieldをまっさらにする。
こっから全部for文が2連続(X、Y軸)になるが、field[X軸][Y軸]にしたかったので、カウンタの順番がj,iになるがそういうもの・・・
次にすでに設置済みのオブジェがcreatedに入ってるから、そこは埋めとく。
Ynum-1にするのは、最下段が台だから
###下せるか
OBJ Target = new OBJ()
int[][] A = Target.getZZZ();
int[] UnderNumber = Target.getUnder();
int dflag = 0;
for (int i = 0; i < 4; i++) {
field[A[i][0]][A[i][1]] = Target.getType();
if (UnderNumber[i] > 0) {
if (field[A[i][0]][A[i][1] + 1] > 0) {
dflag = 1;
}
}
}
次に、今あるオブジェが上から降りてくるプログラムを書くが、その真下に別のブロックが存在していると降りれないので、それを判定する部分をまず書く必要がありますん
とりあえず降りてくるオブジェをTargetとして、こいつの今の座標をAという配列に入れ。getZZZはA[2][0]でオブジェの2番めのx座標、A[2][1]で2番めのy座標を返すように設計
getUnderて関数は、そのオブジェの底のブロック(例えば上の画像なら1,2,3ブロックが底)を返す。底じゃなかったら0、底だったら1に。
dflagをオブジェをdown できない かのflagにする。
次のFor文で
まずfieldにブロックを現在位置にセットする。ついでに自分のタイプ番号にしとく。
そのブロックがオブジェの底だったら、そのブロックの下にブロックが無いか見て(Y+1で下が見れて、>0ならブロック存在)あったらフラグを建てる
getZZZとgetUnderを書く。
public int[][] getZZZ(){
int[][] x= new int[4][2];
for (int i = 0; i < 4; i++) {
x[i][0] = z[i].getX();
x[i][1] = z[i].getY();
}
return x ;
}
public int[] getUnder(){
int[] a = new int[4] ;
switch (type){
case 1:
switch (foword){
case 1:
case 3:
a[1]=1;
a[2]=1;
a[3]=1;
break;
case 2:
case 4:
a[2]=1;
a[3]=1;
break;
}
break;
}
return a ;
}
getZZZはまあgetX,Yで持ってくるだけ。
getUnderはお恥ずかしいことにゴリ押しです。オブジェの底は、そのオブジェの型と、今の向きに依存するんで、それでswitchしたら直打ちです。法則性があればいいんだけどね。
今回はcase 1のZしか書いてませんが、7まで書きます・・・
###着底
次は、そのブロックが下に行けなかった場合を考える。
下に行けないってことは次のオブジェを作って上から落とさなきゃいけないので、これを書く。
if (dflag != 0) {
for (int i = 0; i < 4; i++) {
created[A[i][0]][A[i][1]] = Target.getType();
}
if (!chechUndRemove()) {
print("GAME OVER");
Speed = Integer.MAX_VALUE;
}
Release();
}
下に行けない、イコール dflag > 0
生成済みオブジェ置き場createdに今の座標を登録。( A[i][0]でX座標、A[i][0]でY座標だからcreated[][]に直接)
checkUndRemove関数は、最上段にブロックが詰まった=ゲームオーバーかチェックするのと、横一列揃ってたらその行を消す関数。ゲームオーバーならfalseを返すので!で反転
。
ゲームオーバーならGAMEOVER表示。
Speed = Integer.MAX_VALUE;
は後述
関数が評価されると実行されるので、そろってた行はちゃっかり消える。
Releaseで新しいオブジェを生成。
この2つを同じクラスに書く。↓
boolean chechUndRemove() {
int checkSum = 0;
for (int i = 0; i < Xnum; i++) {
checkSum += created[i][0];
}
if (checkSum > 0) return false;
//Remove
for (int i = 0; i < Ynum - 1; i++) {
int c = 0;
for (int j = 0; j < Xnum; j++) {
if (created[j][i] > 0) {
c++;
}
}
if (c >= Xnum) {
for (int l = i; l > 0; l--) {
for (int k = 0; k < Xnum; k++) {
created[k][l] = created[k][l - 1];
}
}
}
}
return true;
}
void Release() {
Target = new OBJ(1+(int)random(7));
}
最上段のブロックを全部足して、それが1以上ならブロックが存在する=ゲームオーバーということでfalse。
次に揃ってる行を探すんだが、まずcreated全行に対し、ブロック存在してる数数えて、Xnum以上=揃った、てことで消す。
消すんだが、消すというか行を詰める?ので、消す行より上にある全ブロックで「自分の1つ上のブロックを自分に代入する。」をやる
でtrue。
Releaseはターゲットを新しく生成したやつに切り替えるだけ。
###表示
ここまででやることはだいたい終わってて、あとはオブジェを下ろすのと表示だけ。
で、単に表示したり下ろしたりすると、パソコンの爆速処理速度で一瞬で着底して速攻で終了する。
ではこまるので、いい感じにディレイをかけたい。けど単にディレイかけるのはなんかやだ。
じゃどうすんねんと言うと、(毎回インクリメントするカウンタ)÷ ディレイ の余り == 0のときに処理するようにすれば、パソコンの処理速度のディレイ分の1の頻度で処理が起こせる!
ということで、どっかでint counter
を定義して、if(counter++ % Delay == 0){
てすればよいのです。↓
Speedは適当に50とかにする。
if (counter++ % Speed == 0) {
// if ok then down
if (dflag == 0) {
Target.next();
}
// Show table
for (int i = 0; i < Ynum; i++) {
String s = nf(i, 2);
print(s + ";");
for (int j = 0; j < Xnum; j++) {
if (field[j][i] == 0) {
print(" ");
} else {
print(field[j][i] + " ");
}
}
println();
}
}
}
}
ディレイは、小さくすれば処理の頻度が加速するわけなので、Speedとして、あとでいじれるようにしましょうね。
上で、Speed = Integer.MAX_VALUE;
とやりましたが、これでカウンタがintで飽和するまでは処理が止まるわけです。
さっき使ったdflag(downできないフラグ)が0ならおろしていい、ということなのでOBJクラスメソッド next()で下ろします。
public void next(){
for (int i = 0; i < z.length; i++) {
z[i].next();
}
}
public void next(){
this.y++ ;
}
次でフィールドをCUIに表示するわけですが、field[j][i]に値が入ってたらそれを表示、0ならスペース表示するだけ。
String s = nf(i, 2);
とやってるのは、Processingで桁数合わせるにはこうしなきゃならんからです。
最後に改行して終了!
##キーの操作
ここまでで、とりあえずオブジェが生成されてゆっくり降りてって、底についたら次のが降りてくる、までは作れた。
しかし、移動はできんわ回転もできんわ、加速もできんしゲームですらない。のでキーの操作を実装します。
特に回転が地獄です。
キーが押されたら、で判定すると長押しの処理がめんどくさいので、キーが押されて離されたら、を使いましょう
###←↓→
void keyReleased() {
if (keyCode == DOWN) {
if (sCounter == 0) {
Speed = 10;
} else if (sCounter == 1) {
Speed = 30;
}
}
if (keyCode == RIGHT) {
if (Target.canR() && canLLorRR(Target, 1)) {
Target.moveLR(1);
}
} else if (keyCode == LEFT) {
if (Target.canL() && canLLorRR(Target, -1)) {
Target.moveLR(-1);
}
}
}
}
まず加速。↓キーが押されたら、加速と通常速度を切り替えるわけですが、チャチャっと書きます。
次に移動。まず、→キーが押されたら、そこが右端じゃないか、かつ、そのオブジェが右に行くスペースはあるか、をチェックし、問題ないなら右に移動。 (左も同じように)
右用と左用の関数を作るのももったいないので、1で右、-1で左、と引数で切り替え。
関数はこんな感じで↓
boolean canLLorRR( int f) { // RIGHT:1 ,LEFT :-1
int[][] b = Target.getZZZ();
for (int i = 0; i < 4; i++) {
if (created[b[i][0] + f][b[i][1]] > 0) {
return false;
}
}
return true;
}
座標持ってきて、その右か左かが(+ fのとこ)存在してるならfalse返すだけ。
public boolean canLR(int arr){
for (int i = 0; i < 4; i++) {
if(arr == 1) if( z[i].getX() == 9 ) return false ;
if(arr == -1) if( z[i].getX() == 0 ) return false ;
}
return true ;
}
こっちは右なら、1ブロックでも右端ならfalse。逆もしかり。
で、肝心の移動は
public void moveLR(int arr){
for (int i = 0; i < 4; i++) {
z[i].moveLR(arr);
}
}
public void moveLR(int arr){
if(arr == -1) this.x-- ;
if(arr == 1) this.x++ ;
###回転
回転の何がめんどくさいかと言うと、1:移動と違って+1、-1で処理できない。2:棒が端で回転するなど、臨機応変に対応しなきゃならんとこ。
まず、1だけを考える。
####1:回転の処理
// keyReleased()の中
if (keyCode == ENTER) {
if (Target.canTurn(created)) {
Target.turn(i);
}
}
TargetがcanTurnできるなら、回転させる。でまずturnが、
public void turn(int arr){
switch (type){
case 3:
switch (foword) {
case 1:
z[2].changeXY(0, -1);
z[3].changeXY(-2,1);
foword = 2;
break;
case 2:
z[2].changeXY(-3, 0);
z[3].changeXY(-1,-2);
foword = 3;
break;
case 3:
z[2].changeXY(2, -1);
z[3].changeXY(0,1);
foword = 4;
break;
case 4:
z[2].changeXY(1, 2);
z[3].changeXY(3, 0);
foword = 1;
break;
}
またも二重Switch文。他6つ省略。わかりづらいのでcase3のj型を。
全部時計回りで回転することにして、回転に際し移動するブロックを手打ちで移動させる。↓
これを紙に書きながら全パターンやります・・・
changeXYは
public void changeXY(int x,int y){
this.x += x;
this.y += y ;
}
こんな感じで。
ついでにfowordも変更します。
先のcanTurnは、この移動を行えるかどうか?を見る関数で、またもや二重switchで
public boolean canTurn(int arr, int[][] created){
int tflag = 0 ;
switch (type){
case 3:
switch (foword) {
case 1:
tflag += canTurnX(arr,created,z[2].getXY(),0, -1);
tflag += canTurnX(arr,created,z[3].getXY(),-2,1);
break;
case 2:
tflag += canTurnX(arr,created,z[2].getXY(),-3, 0);
tflag += canTurnX(arr,created,z[3].getXY(),-1,-2);
break;
case 3:
tflag += canTurnX(arr,created,z[2].getXY(),2, -1);
tflag += canTurnX(arr,created,z[3].getXY(),0,1);
break;
case 4:
tflag += canTurnX(arr,created,z[2].getXY(),1, 2);
tflag += canTurnX(arr,created,z[3].getXY(),3, 0);
break;
}
break;
}
if(tflag > 0) return false ;
return true ;
}
public int canTurnX(int arr,int[][] c,int[] za,int x,int y) {
int reX = za[0] + x;
int reY = za[1] + y;
if (reX < 0 || reX >= 10) return 100;
if(reY <0) return 200;
if (c[reX][reY] > 0) {
return 1;
}
return 0;
}
}
とする。一旦int arr
は無視して、canTurnからcanTurnXに、created(設置したオブジェの配列)移動するブロックの座標、移動する変化量を送る。
canTurnXは、それもらって、変更後の座標をreX,reYと仮定し、そいつが範囲外にいないか、変更後の座標にすでになんか置かれてないか、を見る。
動かせなさそうなら1以上の数字返して、大丈夫なら0を返す。
canTurnに戻って、移動する全ブロックで問題がないなら(=tFlagの合計が0)trueを返す。
で、無事turnが実行される、というわけですね。
####2:臨機応変に対応
例えば、棒が縦の状態で右端で回転しようとする。今のままだと3つくらいブロックが範囲外にでてまう。
これを防ぐにはいい感じに3つ左にズレなきゃならん。
もちろん1つ上に行く場合もあれば右にもいかなきゃならんかったりする。
さあどうしよう、ってときに、1つづつ左右上にシフトしてみて、ダメなら2つシフト、ダメなら3つシフト、とするように解決することにします。
ここで各回転関数をUpdateします。
//KeyReleased()の中
if (keyCode == ENTER) {
for (int i = 0; i < 13; i++) {
if (Target.canTurn(i, created)) {
Target.turn(i);
break;
}
}
}
まず回転するときに、左上右x4回=12回 canTurnを実行させて、カウンタもcanTurnに送ります。
そんでcanTurnで(復習)
boolean canTurn(int arr, int[][] created){
tflag += canTurnX(arr,created,z[1].getXY(),0,1);
を書いたように、カウンタint arr
をcanTurnXに送ります。
public int canTurnX(int arr,int[][] c,int[] za,int x,int y) {
int[] copyZa = za;
int marge;
if(arr == 0) marge =0 ;
else marge = 1 + arr/3 ;
switch (arr%3){
case 0://Right
copyZa[0] += marge ;
break;
case 1://Left
copyZa[0] -= marge;
break;
case 2://Up
copyZa[1] -= marge;
break;
}
int reX = copyZa[0] + x;
int reY = copyZa[1] + y;
if (reX < 0 || reX >= 10) return 100;
if(reY <0) return 200;
if (c[reX][reY] > 0) {
return 1;
}
return 0;
}
で、大幅に変更して、まずまだ仮定段階なので、実際に値変わってしまってはマズイ(配列は参照系だから元が変わる)ので、copyZaに座標を移す。
このように判定します。移動する分はint marge
にmarge = 1 + arr/3 ;
で決定。(0の場合分けに注意)
そしたらswitch文で、各方向ごとにcopyZaをいじって、回転の前にシフトを完了。
で、turnするときの移動変化量を足して仮定座標決定、これがOKかを先ほどと同様に判定。
で、これがダメだと、Mainのfor文までもどってきて、0~12まで繰り返される、というわけですね。
各方面移動できんとなると、回転できずに終わり。
turn関数は
public void turn(int arr){
int marge;
if(arr == 0) marge = arr/3 ;
else marge = 1 + arr/3 ;
for (int i = 0; i < 4; i++) {
switch (arr%3){
case 0://Right
z[i].changeXY(marge,0);
break;
case 1://Left
z[i].changeXY(-marge,0);
break;
case 2://Up
z[i].changeXY(0,-marge);
break;
}
}
switch (type){
case 1:
switch (foword){
case 1:
case 3:
z[1].changeXY(0,1);
先頭に先ほどと同様のmarge計算が入って、こっちでは動かせる値だとわかっているので、現在座標をシフトして、そこから回転処理に入ります。
アレ?先にシフトするときに座標変えちゃうってことはもともとの座標の値は消えるってことか?
シフト後の回転後座標になにもないのは分かってるが、シフト後の座標になにもないとは判定してない・・・、もし既にあったら退避して埋め戻さなダメじゃね?今度対応します。
とりあえず、シフトしたらあとは先程と同じ。
これで無事窮地で回転しても耐える。
##とりあえず完成!
ここまででCUIでは無事動くはず。
こっからProcessingの真骨頂、簡単GUI機能を活かして設計しますが、あとは表示するだけなので省略。
各ブロックの幅を50pxとして、fieldで表示するときにrect(X座標,Y座標,50,50)とするだけ。あとはfillで適当に色つけるだけでうごきますねえ。
一応コード貼っときます。
コピペしてもいいけどここのURLを明記しといて
package TETLIS;
import processing.core.*;
public class Main extends PApplet {
@Override
public void settings() {
size(960 , 860);
}
long counter = 0;
int waiter = 0;
int Score = 0;
int IncScore = 1;
int BonusScore = 10000;
final int Xnum = 10;
final int Ynum = 15;
int startFlag = 0;
int[][] field = new int[Xnum][Ynum];
int[][] created = new int[Xnum][Ynum];
int Speed = 30;
int sCounter = 0;
//test object a
OBJ Target = new OBJ(1+(int)random(7));
OBJ nextTarget = new OBJ(1+(int)random(7));
@Override
public void setup() {
//under line fill 3
for (int i = 0; i < Xnum; i++) {
field[i][Ynum - 1] = 9;
created[i][Ynum - 1] = 3;
}
background(200);
strokeWeight(2);
rect(100, 100, 500, 700);
showNext();
textSize(50);
text(" ↑ : Start",630,640);
text("←↓→ : Move",630,700);
text("ENTER : Rotate",600,800);
}
@Override
public void keyReleased() {
if (keyCode == RIGHT) {
if (Target.canLR(1) && canLLorRR(1)) {
Target.moveLR(1);
}
} else if (keyCode == LEFT) {
if (Target.canLR(-1) && canLLorRR(-1)) {
Target.moveLR(-1);
}
}
if (keyCode == ENTER) {
for (int i = 0; i < 13; i++) {
if (Target.canTurn(i, created)) {
Target.turn(i);
break;
}
}
}
if(keyCode == UP){
if(startFlag == -1) startFlag = -2 ;
else startFlag = 1;
}
if (keyCode == DOWN) {
if (sCounter == 0) {
Speed = 10;
sCounter = 1;
IncScore = 3;
} else if (sCounter == 1) {
Speed = 30;
sCounter = 0;
IncScore = 1;
}
}
}
boolean canLLorRR( int f) { // RIGHT:1 ,LEFT :-1
int[][] b = Target.getZZZ();
for (int i = 0; i < 4; i++) {
if (created[b[i][0] + f][b[i][1]] > 0) {
return false;
}
}
return true;
}
void Release() {
Target = nextTarget ;
nextTarget = new OBJ(1+(int)random(7));
}
boolean chechUndRemove() {
int checkSum = 0;
for (int i = 0; i < Xnum; i++) {
checkSum += created[i][0];
}
if (checkSum > 0) return false;
int BonusCnt = 1 ;
for (int i = 0; i < Ynum - 1; i++) {
int c = 0;
for (int j = 0; j < Xnum; j++) {
if (created[j][i] > 0) {
c++;
}
}
if (c >= Xnum) {
for (int l = i; l > 0; l--) {
for (int k = 0; k < Xnum; k++) {
created[k][l] = created[k][l - 1];
}
}
Score += BonusScore;
BonusScore *= ++BonusCnt ;
}
BonusScore = 10000;
}
return true;
}
void setColor(int type){
switch (type){
case 7:
fill(245,51,200);
break;
case 1:
fill(77,245,51);
break;
case 2:
fill(206,245,51);
break;
case 3:
fill(51,151,245);
break;
case 4:
fill(74,51,245);
break;
case 5:
fill(51,238,245);
break;
case 6:
fill(245,232,51);
break;
}
}
void showNext(){
stroke(0);
rect(700,100,200,150);
strokeWeight(3);
OBJ next =new OBJ(nextTarget.getType());
setColor(next.getType());
int zure = 0;
if(next.getType() == 5) {
next.turn(1);
zure = 75 ;
}
int[][] za = next.getZZZ();
for (int i = 0; i <4; i++) {
rect(725+zure+za[i][0]*50,125-zure/2+za[i][1]*50,50,50);
}
strokeWeight(0);
stroke(200);
fill(255);
}
@Override
public void draw() {
textSize(70);
fill(130);
text("TETLIS GAME",270,80);
fill(255);
if(startFlag == 0 ) {
}else if(startFlag == -1){
}else if(startFlag == -2){
Score = 0;
IncScore = 1;
Speed = 30;
for (int i = 0; i < Ynum-1; i++) {
for (int j = 0; j < Xnum; j++) {
created[j][i] = 0 ;
}
}
startFlag = 1 ;
}else if(startFlag == 1){
Score += IncScore ;
// fill table 0
for (int i = 0; i < Ynum - 1; i++) { //y
for (int j = 0; j < Xnum; j++) { //x
field[j][i] = 0;
}
}
//set created
for (int i = 0; i < Ynum - 1; i++) { //y
for (int j = 0; j < Xnum; j++) { //x
if (created[j][i] > 0) {
field[j][i] = created[j][i];
}
}
}
// put object und check under can down
int[][] A = Target.getZZZ();
int[] UnderNumber = Target.getUnder();
int dflag = 0;
for (int i = 0; i < 4; i++) {
field[A[i][0]][A[i][1]] = Target.getType();
if (UnderNumber[i] > 0) {
if (field[A[i][0]][A[i][1] + 1] > 0) {
dflag = 1;
}
}
}
rect(700,300,200,150);
textSize(30);
fill(40);
text("now Score",750,450);
text(Score,720,380);
fill(255);
if (dflag != 0) {
if (waiter == 3) {
for (int i = 0; i < 4; i++) {
created[A[i][0]][A[i][1]] = Target.getType();
}
Speed = 30;
if (!chechUndRemove()) {
print("GAME OVER");
Speed = Integer.MAX_VALUE;
IncScore = 0;
startFlag = -1;
}
Release();
waiter = 0;
showNext();
}
}
if (counter++ % Speed == 0) {
rect(100, 100, 500, 700);
// if ok then down
if (dflag == 0) {
Target.next();
} else {
waiter++;
}
// Show table
for (int i = 0; i < Ynum; i++) {
String s = nf(i, 2);
print(s + ";");
stroke(0);
strokeWeight(3);
for (int j = 0; j < Xnum; j++) {
if (field[j][i] == 0) {
print(" ");
}else if(field[j][i] == 9){
fill(50);
print(field[j][i] + " ");
rect(j*50+100,i*50+100,50,50);
} else {
print(field[j][i] + " ");
setColor(field[j][i]);
rect(j*50+100,i*50+100,50,50);
}
}
println();
}
strokeWeight(0);
stroke(200);
fill(255);
}
}
}
public static void main(String[] args) {
PApplet.main("TETLIS.Main");
}
}
package TETLIS;
public class OBJ{
private int type ;
private Z[] z = new Z[4];
private int foword = 1 ;
public OBJ(){
this.type = -1;
z[0] = new Z(0,0);
z[1] = new Z(0,1);
z[2] = new Z(1,1);
z[3] = new Z(0,0);
}
public OBJ( int type){
this.type = type ;
switch (type){
case 1:
z[0] = new Z(1,0);
z[1] = new Z(2,0);
z[2] = new Z(0,1);
z[3] = new Z(1,1);
break;
case 2:
z[0] = new Z(0,0);
z[1] = new Z(1,0);
z[2] = new Z(1,1);
z[3] = new Z(2,1);
break;
case 3:
z[0] = new Z(0,0);
z[1] = new Z(0,1);
z[2] = new Z(1,1);
z[3] = new Z(2,1);
break;
case 4:
z[0] = new Z(2,0);
z[1] = new Z(0,1);
z[2] = new Z(1,1);
z[3] = new Z(2,1);
break;
case 5:
z[0] = new Z(0,0);
z[1] = new Z(0,1);
z[2] = new Z(0,2);
z[3] = new Z(0,3);
break;
case 6:
z[0] = new Z(0,0);
z[1] = new Z(1,0);
z[2] = new Z(0,1);
z[3] = new Z(1,1);
break;
case 7:
z[0] = new Z(1,0);
z[1] = new Z(0,1);
z[2] = new Z(1,1);
z[3] = new Z(2,1);
break;
}
}
public void next(){
for (int i = 0; i < z.length; i++) {
z[i].next();
}
}
public int getType() {
return type;
}
public int[][] getZZZ(){
int[][] x= new int[4][2];
for (int i = 0; i < 4; i++) {
x[i][0] = z[i].getX();
x[i][1] = z[i].getY();
}
return x ;
}
public int[] getUnder(){
int[] a = new int[4] ;
switch (type){
case -1:
switch (foword){
case 1:
case 2:
a[1] = 1;
a[2] = 1;
break;
case 3:
case 4:
a[0] = 1;
a[1] = 1;
break;
}
break;
case 1:
switch (foword){
case 1:
case 3:
a[1]=1;
a[2]=1;
a[3]=1;
break;
case 2:
case 4:
a[2]=1;
a[3]=1;
break;
}
break;
case 2:
switch (foword){
case 1:
case 3:
a[0]=1;
a[2]=1;
a[3]=1;
break;
case 2:
case 4:
a[2]=1;
a[3]=1;
break;
}
break;
case 3:
switch (foword){
case 1:
a[1] = 1;
a[2] = 1;
a[3] = 1;
break;
case 2:
a[3] = 1;
a[2] = 1;
break;
case 3 :
a[3] = 1;
a[1] = 1;
a[2] = 1;
break;
case 4:
a[3] = 1;
a[1] = 1;
break;
}
break;
case 4:
switch (foword){
case 1:
a[1] = 1;
a[2] = 1;
a[3] = 1;
break;
case 2:
a[3] = 1;
a[2] = 1;
break;
case 3 :
a[3] = 1;
a[1] = 1;
a[2] = 1;
break;
case 4:
a[2] = 1;
a[1] = 1;
break;
}
break;
case 5:
switch (foword){
case 1:
case 3:
a[3]=1;
break;
case 2:
case 4:
a[0]=1;
a[3] = 1;
a[1] = 1;
a[2] = 1;
break;
}
break;
case 6:
switch (foword){
case 1:
a[2] = 1;
a[3] = 1;
break;
}
break;
case 7:
switch (foword){
case 1:
a[1] = 1;
a[2] = 1;
a[3] = 1;
break;
case 2:
a[1] = 1;
a[3] = 1;
break;
case 3 :
a[0] = 1;
a[1] = 1;
a[3] = 1;
break;
case 4:
a[0] = 1;
a[1] = 1;
break;
}
break;
}
return a ;
}
public void moveLR(int arr){
for (int i = 0; i < 4; i++) {
z[i].moveLR(arr);
}
}
public boolean canLR(int arr){
for (int i = 0; i < 4; i++) {
if(arr == 1) if( z[i].getX() == 9 ) return false ;
if(arr == -1) if( z[i].getX() == 0 ) return false ;
}
return true ;
}
public void turn(int arr){
int marge;
if(arr == 0) marge = arr/3 ;
else marge = 1 + arr/3 ;
for (int i = 0; i < 4; i++) {
switch (arr%3){
case 0://Right
z[i].changeXY(marge,0);
break;
case 1://Left
z[i].changeXY(-marge,0);
break;
case 2://Up
z[i].changeXY(0,-marge);
break;
}
}
switch (type){
case 1:
switch (foword){
case 1:
case 3:
z[1].changeXY(0,1);
z[2].changeXY(2,1);
foword = 2 ;
break;
case 2:
case 4:
z[1].changeXY(0,-1);
z[2].changeXY(-2,-1);
foword = 1;
break;
}
break;
case 2:
switch (foword){
case 1:
case 3:
z[0].changeXY(0,1);
z[3].changeXY(-2,1);
foword = 2 ;
break;
case 2:
case 4:
z[0].changeXY(0,-1);
z[3].changeXY(2,-1);
foword = 1;
break;
}
break;
case 3:
switch (foword) {
case 1:
z[2].changeXY(0, -1);
z[3].changeXY(-2,1);
foword = 2;
break;
case 2:
z[2].changeXY(-3, 0);
z[3].changeXY(-1,-2);
foword = 3;
break;
case 3:
z[2].changeXY(2, -1);
z[3].changeXY(0,1);
foword = 4;
break;
case 4:
z[2].changeXY(1, 2);
z[3].changeXY(3, 0);
foword = 1;
break;
}
break;
case 4:
switch (foword) {
case 1:
z[2].changeXY(2, -0);
z[1].changeXY(2,-2);
foword = 2;
break;
case 2:
z[2].changeXY(0, -1);
z[1].changeXY(2,1);
foword = 3;
break;
case 3:
z[2].changeXY(-2, 0);
z[1].changeXY(-2,2);
foword = 4;
break;
case 4:
z[2].changeXY(0, 1);
z[1].changeXY(-2, -1);
foword = 1;
break;
}
break;
case 5:
switch (foword){
case 1:
case 3:
z[0].changeXY(-1,1);
z[2].changeXY(1,-1);
z[3].changeXY(2,-2);
foword = 2 ;
break;
case 2:
case 4:
z[0].changeXY(1,-1);
z[2].changeXY(-1,1);
z[3].changeXY(-2,2);
foword = 1;
break;
}
break;
case 7:
switch (foword){
case 1:
z[1].changeXY(1,1);
foword = 2 ;
break;
case 2:
z[0].changeXY(-1,1);
foword = 3;
break;
case 3:
z[3].changeXY(-1,-1);
foword = 4;
break;
case 4:
z[0].changeXY(1,-1);
z[1].changeXY(-1,-1);
z[3].changeXY(1,1);
foword = 1;
break;
}
break;
}
}
public boolean canTurn(int arr, int[][] created){
int tflag = 0 ;
switch (type){
case 1:
switch (foword){
case 1:
case 3:
tflag += canTurnX(arr,created,z[1].getXY(),0,1);
tflag += canTurnX(arr,created,z[2].getXY(),2,1);
break;
case 2:
case 4:
tflag += canTurnX(arr,created,z[1].getXY(),0,-1);
tflag += canTurnX(arr,created,z[2].getXY(),-2,-1);
break;
}
break;
case 2:
switch (foword){
case 1:
case 3:
tflag += canTurnX(arr,created,z[0].getXY(),0,1);
tflag += canTurnX(arr,created,z[3].getXY(),-2,1);
break;
case 2:
case 4:
tflag += canTurnX(arr,created,z[0].getXY(),0,-1);
tflag += canTurnX(arr,created,z[3].getXY(),2,-1);
break;
}
break;
case 3:
switch (foword) {
case 1:
tflag += canTurnX(arr,created,z[2].getXY(),0, -1);
tflag += canTurnX(arr,created,z[3].getXY(),-2,1);
break;
case 2:
tflag += canTurnX(arr,created,z[2].getXY(),-3, 0);
tflag += canTurnX(arr,created,z[3].getXY(),-1,-2);
break;
case 3:
tflag += canTurnX(arr,created,z[2].getXY(),2, -1);
tflag += canTurnX(arr,created,z[3].getXY(),0,1);
break;
case 4:
tflag += canTurnX(arr,created,z[2].getXY(),1, 2);
tflag += canTurnX(arr,created,z[3].getXY(),3, 0);
break;
}
break;
case 4:
switch (foword) {
case 1:
tflag += canTurnX(arr,created,z[2].getXY(),2, -0);
tflag += canTurnX(arr,created,z[1].getXY(),2,-2);
break;
case 2:
tflag += canTurnX(arr,created,z[2].getXY(),0, -1);
tflag += canTurnX(arr,created,z[1].getXY(),2,1);
break;
case 3:
tflag += canTurnX(arr,created,z[2].getXY(),-2, 0);
tflag += canTurnX(arr,created,z[1].getXY(),-2,2);
break;
case 4:
tflag += canTurnX(arr,created,z[2].getXY(),0, 1);
tflag += canTurnX(arr,created,z[1].getXY(),-2, -1);
break;
}
break;
case 5:
switch (foword){
case 1:
case 3:
tflag += canTurnX(arr,created,z[0].getXY(),-1,1);
tflag += canTurnX(arr,created,z[2].getXY(),1,-1);
tflag += canTurnX(arr,created,z[3].getXY(),2,-2);
break;
case 2:
case 4:
tflag += canTurnX(arr,created,z[0].getXY(),1,-1);
tflag += canTurnX(arr,created,z[2].getXY(),-1,1);
tflag += canTurnX(arr,created,z[3].getXY(),-2,2);
break;
}
break;
case 7:
switch (foword){
case 1:
tflag += canTurnX(arr,created,z[1].getXY(),1,1);
break;
case 2:
tflag += canTurnX(arr,created,z[0].getXY(),-1,1);
break;
case 3:
tflag += canTurnX(arr,created,z[3].getXY(),-1,-1);
break;
case 4:
tflag += canTurnX(arr,created,z[0].getXY(),1,-1);
tflag += canTurnX(arr,created,z[1].getXY(),-1,-1);
tflag += canTurnX(arr,created,z[3].getXY(),1,1);
break;
}
break;
}
if(tflag > 0) return false ;
return true ;
}
public int canTurnX(int arr,int[][] c,int[] za,int x,int y) {
int[] copyZa = za;
int marge;
if(arr == 0) marge =0 ;
else marge = 1 + arr/3 ;
switch (arr%3){
case 0://Right
copyZa[0] += marge ;
break;
case 1://Left
copyZa[0] -= marge;
break;
case 2://Up
copyZa[1] -= marge;
break;
}
int reX = copyZa[0] + x;
int reY = copyZa[1] + y;
if (reX < 0 || reX >= 10) return 100;
if(reY <0) return 200;
if (c[reX][reY] > 0) {
return 1;
}
return 0;
}
}
package TETLIS;
public class Z{
private int x;
private int y;
public Z(int x, int y) {
this.x = x;
this.y = y;
}
public void next(){
this.y++ ;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int[] getXY(){
int[] a ={this.x,this.y};
return a;
//ここを一行で書く書き方があったんだが忘れた。
}
public void changeXY(int x,int y){
this.x += x;
this.y += y ;
}
public void moveLR(int arr){
if(arr == -1) this.x-- ;
if(arr == 1) this.x++ ;
}
}