import java.util.Random;
 
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.midlet.MIDlet;
 
public class TemplateMIDlet extends MIDlet implements Runnable {
    
    int FIELD_WIDTH = 12;
    int FIELD_HEIGHT = 22;
    
    int[][][] pattern = {
        {
            {0, 1, 0},
            {0, 1, 0},
            {0, 1, 0}
        }, {
            {0, 1, 0},
            {0, 1, 1},
            {0, 0, 0}
        }
    };
    
    int mode = 0; // ゲーム内の状態を表すモード(0:落下開始、1:落下中、2:着地)
    int score = 0;
    int totalLines = 0;
    Thread thread;
    Random random = new Random();
    StringBuffer keyBuffer = new StringBuffer();
    int[][] field = new int[FIELD_HEIGHT][FIELD_WIDTH];
    int[][] next = new int[pattern[0].length][pattern[0].length];
    
    Image offi;
    Canvas canvas;
    
    int COLOR_GRAY = 0xC0C0C0;
    int COLOR_BLUE = 0x0000FF;
    int COLOR_RED = 0xFF0000;
    int COLOR_BLACK = 0x000000;
    int COLOR_WHITE = 0xFFFFFF;
    
    public TemplateMIDlet() {
        canvas = new Canvas() {
            public void paint(Graphics g) {
                g.drawImage(offi, 0, 0, Graphics.TOP | Graphics.LEFT);
            }
            
            protected void keyPressed(int keyCode) {
                // 落下中でかつ先行入力が2つ未満の場合のみキー入力を受け付ける
                if (mode == 1 && keyBuffer.length() < 2) {
                    int ga = getGameAction(keyCode);
                    if (ga == Canvas.DOWN) {
                        keyBuffer.append('D');
                    } else if (ga == Canvas.UP) {
                        keyBuffer.append('U');
                    } else if (ga == Canvas.LEFT) {
                        keyBuffer.append('L');
                    } else if (ga == Canvas.RIGHT) {
                        keyBuffer.append('R');
                    } else {
                        keyBuffer.append('A');
                    }
                }
            }
            
            protected void keyRepeated(int keyCode) {
                // キーを押しっぱなしの場合の動作はキーが押された場合と同じ
                keyPressed(keyCode);
            }
        };
        offi = Image.createImage(canvas.getWidth(), canvas.getHeight());
        Display.getDisplay(this).setCurrent(canvas);
    }
    
    protected void startApp() {
        if (thread == null) {
            thread = new Thread(this);
            thread.start();
        }
    }
    
    protected void pauseApp() {
        // このイベントは利用しないので実装する必要はない
    }
    
    protected void destroyApp(boolean unconditional) {
        // このイベントは利用しないので実装する必要はない
    }
    
    public void run() {
        int x = 0;
        int y = 0;
        int count = 0;
        int prevKey = '0';
        Graphics g = offi.getGraphics();
        initField();
        int[][] current = new int[pattern[0].length][pattern[0].length];
        copy(pattern[Math.abs(random.nextInt() % pattern.length)], next);
        while (true) {
            if (mode == 0) {
                if (keyBuffer.length() > 0) {
                    keyBuffer.delete(0, keyBuffer.length() - 1);
                }
                copy(next, current);
                copy(pattern[Math.abs(random.nextInt() % pattern.length)], next);
                int rotateCount = Math.abs(random.nextInt() % 4);
                for (int i = 0; i < rotateCount; i++) {
                    rotate(next);
                }
                x = FIELD_WIDTH / 2 - (int)Math.sqrt(pattern[0].length);
                y = 0;
                // ゲーム終了判定
                if (isHit(x, y, current)) {
                    initField();
                    score = 0;
                    totalLines = 0;
                }
                mode = 1;
                count = 0;
            } else if (mode == 1) {
                boolean fix = false;
                if (count != 0) {
                    char key = 'N'; // 'N' は何も押していないことを表す
                    if (count % 20 == 0) {
                        if (!(keyBuffer.length() > 0 && keyBuffer.charAt(0) == 'D')) {
                            keyBuffer.insert(0, 'D');
                        }
                    }
                    if (keyBuffer.length() > 0) {
                        key = keyBuffer.charAt(0);
                        keyBuffer.deleteCharAt(0);
                        if (key == 'L') {
                            x--;
                        } else if (key == 'R') {
                            x++;
                        } else if (key == 'D') {
                            y++;
                        } else if (key == 'A') {
                            rotate(current);
                        } else if (key == 'U') {
                            while (!isHit(x, y, current)) {
                                y++;
                            }
                        }
                        if (isHit(x, y, current)) {
                            if (key == 'L') {
                                x++;
                            } else if (key == 'R') {
                                x--;
                            } else if (key == 'A') {
                                if (prevKey == 'L') {
                                    if (!isHit(x + 1, y, current)) {
                                        x++;
                                    } else {
                                        rotate(current);
                                        rotate(current);
                                        rotate(current);
                                    }
                                } else if (prevKey == 'R') {
                                    if (!isHit(x - 1, y, current)) {
                                        x--;
                                    } else {
                                        rotate(current);
                                        rotate(current);
                                        rotate(current);
                                    }
                                }
                            } else if (key == 'D' || key == 'U') {
                                y--;
                                fix = true;
                            }
                        }
                        if (key == 'L' || key == 'R') {
                            prevKey = key;
                        }
                    }
                }
                
                for (int i = 0; i < FIELD_HEIGHT; i++) {
                    for (int j = 0; j < FIELD_WIDTH; j++) {
                        if (field[i][j] == 1) {
                            field[i][j] = 0;
                        }
                    }
                }
                
                for (int i = 0; i < current.length; i++) {
                    for (int j = 0; j < current[i].length; j++) {
                        if (current[i][j] == 1) {
                            field[y + i][x + j] = 1;
                            if (fix) {
                                field[y + i][x + j] = 2;
                            }
                        }
                    }
                }
                
                // メインフィールドを描画
                drawBlocks(g);
                
                // 次に出現するブロックを描画
                drawNext(g);
                
                // 得点と消去したライン数を描画
                drawScore(g);
                
                if (fix) {
                    mode = 2;
                }
                if (count == 0) {
                    sleep(150);
                } else {
                    sleep(50);
                }
                count++;
            } else if (mode == 2) {
                // 一列揃ったかどうかを判定する
                int[] eraseLines = new int[pattern[0].length];
                int index = 0;
                for (int i = 0; i < FIELD_HEIGHT; i++) {
                    boolean complete = true;
                    for (int j = 1; j < FIELD_WIDTH - 1; j++) {
                        if (field[i][j] != 2) {
                            complete = false;
                            break;
                        }
                    }
                    if (complete) {
                        eraseLines[index++] = i;
                    }
                }
                if (index != 0) {
                    for (int i = 0; i < 5; i++) {
                        for (int j = 0; j < index; j++) {
                            for (int k = 1; k < FIELD_WIDTH - 1; k++) {
                                field[eraseLines[j]][k] = i % 2 == 0 ? 0 : 2;
                            }
                        }
                        drawBlocks(g);
                        canvas.repaint();
                        canvas.serviceRepaints();
                        sleep(150);
                    }
                    for (int i = 0; i < index; i++) {
                        for (int j = eraseLines[i] - 1; j > 0; j--) {
                            field[j + 1] = field[j];
                            field[j] = new int[FIELD_WIDTH];
                            field[j][0] = field[j][FIELD_WIDTH - 1] = -1;
                        }
                    }
                    score += index * index * 10;
                    totalLines += index;
                }
                mode = 0;
            }
            canvas.repaint();
            canvas.serviceRepaints();
        }
    }
    
    void copy(int[][] src, int[][] dest) {
        for (int i = 0; i < src.length; i++) {
            System.arraycopy(src[i], 0, dest[i], 0, src[i].length);
        }
    }
    
    void rotate(int[][] omino) {
        int[][] temp = new int[omino[0].length][omino[0].length];
        temp[0][2] = omino[0][0];
        temp[1][2] = omino[0][1];
        temp[2][2] = omino[0][2];
        temp[0][1] = omino[1][0];
        temp[1][1] = omino[1][1];
        temp[2][1] = omino[1][2];
        temp[0][0] = omino[2][0];
        temp[1][0] = omino[2][1];
        temp[2][0] = omino[2][2];
        for (int i = 0; i < omino.length; i++) {
            System.arraycopy(temp[i], 0, omino[i], 0, temp[i].length);
        }
    }
    
    boolean isHit(int x, int y, int[][] omino) {
        for (int i = 0; i < omino.length; i++) {
            for (int j = 0; j < omino[i].length; j++) {
                if (omino[i][j] == 1) {
                    if (field[y + i][x + j] == -1 || field[y + i][x + j] == 2) {
                        return true;
                    }
                }
            }
        }
        return false;
    }
    
    void initField() {
        for (int i = 0; i < FIELD_HEIGHT; i++) {
            for (int j = 0; j < FIELD_WIDTH; j++) {
                if (j == 0 || j == FIELD_WIDTH - 1) {
                    field[i][j] = -1;
                } else if (i == FIELD_HEIGHT - 1) {
                    field[i][j] = -1;
                } else {
                    field[i][j] = 0;
                }
            }
        }
        for (int i = 1; i < FIELD_WIDTH - 1; i++) {
            field[FIELD_HEIGHT - 1][i] = -1;
        }
        for (int i = 0; i < FIELD_HEIGHT; i++) {
            field[i][0] = -1;
            field[i][FIELD_WIDTH - 1] = -1;
        }
    }
    
    void drawBlocks(Graphics g) {
        int blockSize = canvas.getHeight() / (FIELD_HEIGHT + 2);
        for (int i = 0; i < FIELD_HEIGHT; i++) {
            for (int j = 0; j < FIELD_WIDTH; j++) {
                if (field[i][j] == -1) {
                    g.setColor(COLOR_GRAY);
                    g.fillRect(j * blockSize, i * blockSize, blockSize, blockSize);
                } else if (field[i][j] == 1) {
                    g.setColor(COLOR_RED);
                    g.fillRect(j * blockSize, i * blockSize, blockSize, blockSize);
                } else if (field[i][j] == 2) {
                    g.setColor(COLOR_BLUE);
                    g.fillRect(j * blockSize, i * blockSize, blockSize, blockSize);
                } else {
                    g.setColor(COLOR_BLACK);
                    g.fillRect(j * blockSize, i * blockSize, blockSize, blockSize);
                }
            }
        }
    }
    
    void drawNext(Graphics g) {
        int ominoSize = pattern[0].length;
        int blockSize = canvas.getHeight() / (FIELD_HEIGHT + 2);
        for (int i = 0; i < ominoSize; i++) {
            for (int j = 0; j < ominoSize; j++) {
                if (next[i][j] == 1) {
                    g.setColor(COLOR_RED);
                    g.fillRect((FIELD_WIDTH + j + 1) * blockSize, (i + 1) * blockSize, blockSize, blockSize);
                } else {
                    g.setColor(COLOR_WHITE);
                    g.fillRect((FIELD_WIDTH + j + 1) * blockSize, (i + 1) * blockSize, blockSize, blockSize);
                }
            }
        }
    }
    
    void drawScore(Graphics g) {
        int blockSize = canvas.getHeight() / (FIELD_HEIGHT + 2);
        int fontHeight = g.getFont().getHeight();
        int offx = (FIELD_WIDTH + 1) * blockSize;
        int offy = (FIELD_HEIGHT) * blockSize - fontHeight * 2;
        g.setColor(COLOR_WHITE);
        g.fillRect(offx, offy, canvas.getWidth() - offx, fontHeight * 2);
        g.setColor(COLOR_BLACK);
        g.drawString("Line:" + totalLines, offx, offy + fontHeight, Graphics.BOTTOM | Graphics.LEFT);
        g.drawString("Score:" + score, offx, offy + fontHeight * 2, Graphics.BOTTOM | Graphics.LEFT);
    }
    
    void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            // 例外は発生しない
        }
    }
    
 
 
 
 
 
 
}
import java.util.Random;
 
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.midlet.MIDlet;
 
public class TemplateMIDlet extends MIDlet implements Runnable {
    
    int FIELD_WIDTH = 12;
    int FIELD_HEIGHT = 22;
    
    int[][][] pattern = {
        {
            {0, 1, 0},
            {0, 1, 0},
            {0, 1, 0}
        }, {
            {0, 1, 0},
            {0, 1, 1},
            {0, 0, 0}
        }
    };
    
    int mode = 0; // ゲーム内の状態を表すモード(0:落下開始、1:落下中、2:着地)
    int score = 0;
    int totalLines = 0;
    Thread thread;
    Random random = new Random();
    StringBuffer keyBuffer = new StringBuffer();
    int[][] field = new int[FIELD_HEIGHT][FIELD_WIDTH];
    int[][] next = new int[pattern[0].length][pattern[0].length];
    
    Image offi;
    Canvas canvas;
    
    int COLOR_GRAY = 0xC0C0C0;
    int COLOR_BLUE = 0x0000FF;
    int COLOR_RED = 0xFF0000;
    int COLOR_BLACK = 0x000000;
    int COLOR_WHITE = 0xFFFFFF;
    
    public TemplateMIDlet() {
        canvas = new Canvas() {
            public void paint(Graphics g) {
                g.drawImage(offi, 0, 0, Graphics.TOP | Graphics.LEFT);
            }
            
            protected void keyPressed(int keyCode) {
                // 落下中でかつ先行入力が2つ未満の場合のみキー入力を受け付ける
                if (mode == 1 && keyBuffer.length() < 2) {
                    int ga = getGameAction(keyCode);
                    if (ga == Canvas.DOWN) {
                        keyBuffer.append('D');
                    } else if (ga == Canvas.UP) {
                        keyBuffer.append('U');
                    } else if (ga == Canvas.LEFT) {
                        keyBuffer.append('L');
                    } else if (ga == Canvas.RIGHT) {
                        keyBuffer.append('R');
                    } else {
                        keyBuffer.append('A');
                    }
                }
            }
            
            protected void keyRepeated(int keyCode) {
                // キーを押しっぱなしの場合の動作はキーが押された場合と同じ
                keyPressed(keyCode);
            }
        };
        offi = Image.createImage(canvas.getWidth(), canvas.getHeight());
        Display.getDisplay(this).setCurrent(canvas);
    }
    
    protected void startApp() {
        if (thread == null) {
            thread = new Thread(this);
            thread.start();
        }
    }
    
    protected void pauseApp() {
        // このイベントは利用しないので実装する必要はない
    }
    
    protected void destroyApp(boolean unconditional) {
        // このイベントは利用しないので実装する必要はない
    }
    
    public void run() {
        int x = 0;
        int y = 0;
        int count = 0;
        int prevKey = '0';
        Graphics g = offi.getGraphics();
        initField();
        int[][] current = new int[pattern[0].length][pattern[0].length];
        copy(pattern[Math.abs(random.nextInt() % pattern.length)], next);
        while (true) {
            if (mode == 0) {
                if (keyBuffer.length() > 0) {
                    keyBuffer.delete(0, keyBuffer.length() - 1);
                }
                copy(next, current);
                copy(pattern[Math.abs(random.nextInt() % pattern.length)], next);
                int rotateCount = Math.abs(random.nextInt() % 4);
                for (int i = 0; i < rotateCount; i++) {
                    rotate(next);
                }
                x = FIELD_WIDTH / 2 - (int)Math.sqrt(pattern[0].length);
                y = 0;
                // ゲーム終了判定
                if (isHit(x, y, current)) {
                    initField();
                    score = 0;
                    totalLines = 0;
                }
                mode = 1;
                count = 0;
            } else if (mode == 1) {
                boolean fix = false;
                if (count != 0) {
                    char key = 'N'; // 'N' は何も押していないことを表す
                    if (count % 20 == 0) {
                        if (!(keyBuffer.length() > 0 && keyBuffer.charAt(0) == 'D')) {
                            keyBuffer.insert(0, 'D');
                        }
                    }
                    if (keyBuffer.length() > 0) {
                        key = keyBuffer.charAt(0);
                        keyBuffer.deleteCharAt(0);
                        if (key == 'L') {
                            x--;
                        } else if (key == 'R') {
                            x++;
                        } else if (key == 'D') {
                            y++;
                        } else if (key == 'A') {
                            rotate(current);
                        } else if (key == 'U') {
                            while (!isHit(x, y, current)) {
                                y++;
                            }
                        }
                        if (isHit(x, y, current)) {
                            if (key == 'L') {
                                x++;
                            } else if (key == 'R') {
                                x--;
                            } else if (key == 'A') {
                                if (prevKey == 'L') {
                                    if (!isHit(x + 1, y, current)) {
                                        x++;
                                    } else {
                                        rotate(current);
                                        rotate(current);
                                        rotate(current);
                                    }
                                } else if (prevKey == 'R') {
                                    if (!isHit(x - 1, y, current)) {
                                        x--;
                                    } else {
                                        rotate(current);
                                        rotate(current);
                                        rotate(current);
                                    }
                                }
                            } else if (key == 'D' || key == 'U') {
                                y--;
                                fix = true;
                            }
                        }
                        if (key == 'L' || key == 'R') {
                            prevKey = key;
                        }
                    }
                }
                
                for (int i = 0; i < FIELD_HEIGHT; i++) {
                    for (int j = 0; j < FIELD_WIDTH; j++) {
                        if (field[i][j] == 1) {
                            field[i][j] = 0;
                        }
                    }
                }
                
                for (int i = 0; i < current.length; i++) {
                    for (int j = 0; j < current[i].length; j++) {
                        if (current[i][j] == 1) {
                            field[y + i][x + j] = 1;
                            if (fix) {
                                field[y + i][x + j] = 2;
                            }
                        }
                    }
                }
                
                // メインフィールドを描画
                drawBlocks(g);
                
                // 次に出現するブロックを描画
                drawNext(g);
                
                // 得点と消去したライン数を描画
                drawScore(g);
                
                if (fix) {
                    mode = 2;
                }
                if (count == 0) {
                    sleep(150);
                } else {
                    sleep(50);
                }
                count++;
            } else if (mode == 2) {
                // 一列揃ったかどうかを判定する
                int[] eraseLines = new int[pattern[0].length];
                int index = 0;
                for (int i = 0; i < FIELD_HEIGHT; i++) {
                    boolean complete = true;
                    for (int j = 1; j < FIELD_WIDTH - 1; j++) {
                        if (field[i][j] != 2) {
                            complete = false;
                            break;
                        }
                    }
                    if (complete) {
                        eraseLines[index++] = i;
                    }
                }
                if (index != 0) {
                    for (int i = 0; i < 5; i++) {
                        for (int j = 0; j < index; j++) {
                            for (int k = 1; k < FIELD_WIDTH - 1; k++) {
                                field[eraseLines[j]][k] = i % 2 == 0 ? 0 : 2;
                            }
                        }
                        drawBlocks(g);
                        canvas.repaint();
                        canvas.serviceRepaints();
                        sleep(150);
                    }
                    for (int i = 0; i < index; i++) {
                        for (int j = eraseLines[i] - 1; j > 0; j--) {
                            field[j + 1] = field[j];
                            field[j] = new int[FIELD_WIDTH];
                            field[j][0] = field[j][FIELD_WIDTH - 1] = -1;
                        }
                    }
                    score += index * index * 10;
                    totalLines += index;
                }
                mode = 0;
            }
            canvas.repaint();
            canvas.serviceRepaints();
        }
    }
    
    void copy(int[][] src, int[][] dest) {
        for (int i = 0; i < src.length; i++) {
            System.arraycopy(src[i], 0, dest[i], 0, src[i].length);
        }
    }
    
    void rotate(int[][] omino) {
        int[][] temp = new int[omino[0].length][omino[0].length];
        temp[0][2] = omino[0][0];
        temp[1][2] = omino[0][1];
        temp[2][2] = omino[0][2];
        temp[0][1] = omino[1][0];
        temp[1][1] = omino[1][1];
        temp[2][1] = omino[1][2];
        temp[0][0] = omino[2][0];
        temp[1][0] = omino[2][1];
        temp[2][0] = omino[2][2];
        for (int i = 0; i < omino.length; i++) {
            System.arraycopy(temp[i], 0, omino[i], 0, temp[i].length);
        }
    }
    
    boolean isHit(int x, int y, int[][] omino) {
        for (int i = 0; i < omino.length; i++) {
            for (int j = 0; j < omino[i].length; j++) {
                if (omino[i][j] == 1) {
                    if (field[y + i][x + j] == -1 || field[y + i][x + j] == 2) {
                        return true;
                    }
                }
            }
        }
        return false;
    }
    
    void initField() {
        for (int i = 0; i < FIELD_HEIGHT; i++) {
            for (int j = 0; j < FIELD_WIDTH; j++) {
                if (j == 0 || j == FIELD_WIDTH - 1) {
                    field[i][j] = -1;
                } else if (i == FIELD_HEIGHT - 1) {
                    field[i][j] = -1;
                } else {
                    field[i][j] = 0;
                }
            }
        }
        for (int i = 1; i < FIELD_WIDTH - 1; i++) {
            field[FIELD_HEIGHT - 1][i] = -1;
        }
        for (int i = 0; i < FIELD_HEIGHT; i++) {
            field[i][0] = -1;
            field[i][FIELD_WIDTH - 1] = -1;
        }
    }
    
    void drawBlocks(Graphics g) {
        int blockSize = getBlockSize();
        for (int i = 0; i < FIELD_HEIGHT; i++) {
            for (int j = 0; j < FIELD_WIDTH; j++) {
                if (field[i][j] == -1) {
                    g.setColor(COLOR_GRAY);
                    g.fillRect(j * blockSize, i * blockSize, blockSize, blockSize);
                } else if (field[i][j] == 1) {
                    g.setColor(COLOR_RED);
                    g.fillRect(j * blockSize, i * blockSize, blockSize, blockSize);
                } else if (field[i][j] == 2) {
                    g.setColor(COLOR_BLUE);
                    g.fillRect(j * blockSize, i * blockSize, blockSize, blockSize);
                } else {
                    g.setColor(COLOR_BLACK);
                    g.fillRect(j * blockSize, i * blockSize, blockSize, blockSize);
                }
            }
        }
    }
    
    void drawNext(Graphics g) {
        int ominoSize = pattern[0].length;
        int blockSize = getBlockSize();
        for (int i = 0; i < ominoSize; i++) {
            for (int j = 0; j < ominoSize; j++) {
                if (next[i][j] == 1) {
                    g.setColor(COLOR_RED);
                    g.fillRect((FIELD_WIDTH + j + 1) * blockSize, (i + 1) * blockSize, blockSize, blockSize);
                } else {
                    g.setColor(COLOR_WHITE);
                    g.fillRect((FIELD_WIDTH + j + 1) * blockSize, (i + 1) * blockSize, blockSize, blockSize);
                }
            }
        }
    }
    
    void drawScore(Graphics g) {
        int blockSize = getBlockSize();
        int fontHeight = g.getFont().getHeight();
        int offx = (FIELD_WIDTH) * blockSize + blockSize / 2; // 表示位置を微調整
        int offy = (FIELD_HEIGHT) * blockSize - fontHeight * 2;
        g.setColor(COLOR_WHITE);
        g.fillRect(offx, offy, canvas.getWidth() - offx, fontHeight * 2);
        g.setColor(COLOR_BLACK);
        g.drawString("Line:" + totalLines, offx, offy + fontHeight, Graphics.BOTTOM | Graphics.LEFT);
        g.drawString("Score:" + score, offx, offy + fontHeight * 2, Graphics.BOTTOM | Graphics.LEFT);
    }
    
    void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            // 例外は発生しない
        }
    }
    
    int getBlockSize() {
        int blockSizeH = canvas.getHeight() / (FIELD_HEIGHT + 2);
        int blockSizeW = canvas.getWidth() / (FIELD_WIDTH + 9);
        return Math.min(blockSizeH, blockSizeW);
    }
    
}