> show canvas only <


/* built with Studio Sketchpad: 
 *   https://sketchpad.cc
 * 
 * observe the evolution of this sketch: 
 *   https://fgcs.sketchpad.cc/sp/pad/view/ro.3uzMYA0YFMm/rev.3915
 * 
 * authors: 
 *   Markus Roberts

 * license (unless otherwise specified): 
 *   creative commons attribution-share alike 3.0 license.
 *   https://creativecommons.org/licenses/by-sa/3.0/ 
 */ 



int grid_size = 4;
int  [][] grid = new int[grid_size][grid_size];
int  [][] shine = new int[grid_size][grid_size];
boolean game_over = false;

void setup() {
    size(400, 400); 
    frameRate(30);
    grid[1][1] = 1;
} 

String debug;

void draw() {
    background(255,255,200);
    int cell_width  = round(width/grid_size);
    int cell_height = round(height/grid_size);
    int margin = 5;
    for (int i = 0; i<grid_size; i++) {
        for (int j = 0; j<grid_size; j++) {
            fill(255-(17*grid[i][j] % 100),255-(23*grid[i][j] % 100),255-shine[i][j]);
            shine[i][j] = (shine[i][j]*98)/100;
            noStroke();
            rect(i*cell_width+margin,j*cell_height+margin,cell_width-2*margin,cell_height-2*margin);
            textSize(30);
            if (grid[i][j] > 0) {
                fill(0);
                text(""+grid[i][j],i*cell_width+(cell_width-textWidth(""+grid[i][j]))/2,j*cell_height+margin+cell_height/2);
            }
        }
    }
    fill(0,0,0,30);
    textSize(50);
    text(debug,width/2-textWidth(debug)/2,height/2);
    if (game_over) {
        fill(255,255,255,50);
        rect(0,0,width,height);
        fill(0);
        textSize(50);
        text("Game Over",width/2-textWidth("Game Over")/2,height/2);
    }
}

void keyPressed() {
    if      (key == 'z') shift_grid( 0,-1);
    else if (key == 'w') shift_grid( 0, 1);
    else if (key == 'a') shift_grid( 1, 0);
    else if (key == 's') shift_grid(-1, 0);
}

void shift_grid(int delta_x, int delta_y) {
    int [][] rotation = new int [][] {
        {1-abs(delta_x),delta_x,(delta_x < 0 ? grid_size-1 : 0)},
        {1-abs(delta_y),delta_y,(delta_y < 0 ? grid_size-1 : 0)}
    };
    int zeros = 0, changes = 0;
    for (int row = 0; row<grid_size; row++) {
        int r = 0, w = 0;
        while (r<grid_size) {
            int
              rx = row*rotation[0][0]+r*rotation[0][1]+rotation[0][2],
              ry = row*rotation[1][0]+r*rotation[1][1]+rotation[1][2],
              wx = row*rotation[0][0]+w*rotation[0][1]+rotation[0][2],
              wy = row*rotation[1][0]+w*rotation[1][1]+rotation[1][2];
            if (grid[rx][ry] == 0) {
                zeros++;
                r++;
            } else if (r == w) {
                r++;
            } else if (grid[wx][wy] == 0) {
                grid[wx][wy] = grid[rx][ry];
                shine[wx][wy] = shine[rx][ry];
                grid[rx][ry] = 0;
                shine[rx][ry] = 0;
                changes++;
                r++;
            } else if (addable(grid[rx][ry],grid[wx][wy])) {
                grid[wx][wy] += grid[rx][ry];
                shine[wx][wy] = 128;
                grid[rx][ry] = 0;
                shine[rx][ry] = 0;
                changes++;
                zeros++;
                r++;
                w++;
            } else {
                w++
                if (w>=r) r++;
            }
        }
    }
    if (zeros > 0 && changes > 0) {
        int new_x,new_y;
        boolean found = false;
        while (!found) {
            new_x = round(random(grid_size-1));
            new_y = round(random(grid_size-1));
            found = grid[new_x][new_y] == 0;
            }
        grid[new_x][new_y] = 1;
        shine[new_x][new_y] = 128;
    }
    if (zeros == 0) game_over = true;
}

boolean addable(int a, int b) {
    return (a == 1 && (b == 1 || b == 2)) || (a > b && addable(b,a)) || (max(0,b-a) < a && a < b && addable(b-a,a));
}