String[] letters = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"};
final int SOLVES_IN_AVERAGE = 5;
boolean horizontalLast = false;
int[] times = new int[SOLVES_IN_AVERAGE];
boolean isScrambled = false;
int centerTileX = BOARD_WIDTH/2;
int centerTileY = BOARD_HEIGHT/2;
int timeSinceLastWASD = -9999;
float TRANSITION_FACTOR = 0.666667;
boolean lastMoused = true;
boolean isSpaceDown = false;
boolean isShiftDown = false;
boolean isMouseDown = false;
font = loadFont("ArialMT-48.vlw");
void resetBoard(int inputSize){
BOARD_HEIGHT = inputSize;
highlightX = centerTileX = (int)(BOARD_WIDTH/2);
highlightY = centerTileY = (int)(BOARD_HEIGHT/2);
TILE_SIZE = ((float)1000.0)/inputSize;
board = new int[BOARD_HEIGHT][BOARD_WIDTH];
shades = new float[BOARD_HEIGHT*BOARD_WIDTH];
transitions = new float[2][max(BOARD_WIDTH,BOARD_HEIGHT)];
for(int y = 0; y < BOARD_HEIGHT; y++){
for(int x = 0; x < BOARD_WIDTH; x++){
board[y][x] = y*BOARD_WIDTH+x;
shades[y*BOARD_WIDTH+x] = random(0.5,1.0);
for(int i = 0; i < 2; i++){
for(int j = 0; j < max(BOARD_HEIGHT,BOARD_WIDTH); j++){
for(int i = 0; i < SOLVES_IN_AVERAGE; i++){
correctTiles = BOARD_WIDTH*BOARD_HEIGHT;
if(isMouseDown && endMillis == -1){
int newX = floor((float)mouseX/TILE_SIZE+BOARD_WIDTH)%BOARD_WIDTH;
int newY = floor((float)mouseY/TILE_SIZE+BOARD_HEIGHT)%BOARD_HEIGHT;
while(hasDiff(newX,highlightX,true) && endMillis == -1 && isMoveableTile(true)){
move(0,highlightX,highlightY, false);
while(hasDiff(newX,highlightX,false) && endMillis == -1 && isMoveableTile(true)){
move(1,highlightX,highlightY, false);
while(hasDiff(newY,highlightY,true) && endMillis == -1 && isMoveableTile(false)){
move(2,highlightX,highlightY, false);
while(hasDiff(newY,highlightY,false) && endMillis == -1 && isMoveableTile(false)){
move(3,highlightX,highlightY, false);
for(int i = 0; i < 2; i++){
for(int j = 0; j < max(BOARD_HEIGHT,BOARD_WIDTH); j++){
if(transitions[i][j] > ((float)BOARD_WIDTH)/2){
transitions[i][j] -= BOARD_WIDTH;
}else if(transitions[i][j] < -((float)BOARD_WIDTH)/2){
transitions[i][j] += BOARD_WIDTH;
if(transitions[i][j] > ((float)BOARD_HEIGHT)/2){
transitions[i][j] -= BOARD_HEIGHT;
}else if(transitions[i][j] < -((float)BOARD_HEIGHT)/2){
transitions[i][j] += BOARD_HEIGHT;
transitions[i][j] *= TRANSITION_FACTOR;
for(int y = 0; y < BOARD_HEIGHT; y++){
for(int x = 0; x < BOARD_WIDTH; x++){
ax = (ax+BOARD_WIDTH)%BOARD_WIDTH;
ay = (ay+BOARD_HEIGHT)%BOARD_HEIGHT;
if(GAME_MODE == 1 && x == centerTileX && y == centerTileY){
float colorX = (float)(i%BOARD_WIDTH)/(BOARD_WIDTH-1);
float colorY = (float)(i/BOARD_WIDTH)/(BOARD_HEIGHT-1);
fill((1-colorX)*255,colorY*255,colorX*255);
rect(aax*TILE_SIZE,ay*TILE_SIZE,TILE_SIZE,TILE_SIZE);
if(BOARD_WIDTH*BOARD_HEIGHT > 26){
int py = floor(i/BOARD_WIDTH);
text((i+1),(aax+0.5)*TILE_SIZE,(ay+0.7)*TILE_SIZE);
text(letters[i],(aax+0.5)*TILE_SIZE,(ay+0.82)*TILE_SIZE);
if(!lastMoused && x == highlightX && y == highlightY){
float f = TILE_SIZE*0.038;
stroke(abs((frameCount*20)%510-255));
if(isSpaceDown || millis()-timeSinceLastWASD < 500){
rect(aax*TILE_SIZE+f*2,ay*TILE_SIZE+f*2,TILE_SIZE-f*4,TILE_SIZE-f*4);
line(aax*TILE_SIZE+f*2,ay*TILE_SIZE+f*2,aax*TILE_SIZE+f*6,ay*TILE_SIZE+f*6);
line((aax+1)*TILE_SIZE-f*2,ay*TILE_SIZE+f*2,(aax+1)*TILE_SIZE-f*6,ay*TILE_SIZE+f*6);
line(aax*TILE_SIZE+f*2,(ay+1)*TILE_SIZE-f*2,aax*TILE_SIZE+f*6,(ay+1)*TILE_SIZE-f*6);
line((aax+1)*TILE_SIZE-f*2,(ay+1)*TILE_SIZE-f*2,(aax+1)*TILE_SIZE-f*6,(ay+1)*TILE_SIZE-f*6);
rect(aax*TILE_SIZE+f,ay*TILE_SIZE+f,TILE_SIZE-f*2,TILE_SIZE-f*2);
rect(BOARD_WIDTH*TILE_SIZE,0,UI_SIZE,BOARD_HEIGHT*TILE_SIZE);
if(startMillis >= 0 && endMillis >= 0){
s = timeToString(endMillis-startMillis);
}else if(startMillis >= 0 && correctTiles < BOARD_WIDTH*BOARD_HEIGHT){
s = timeToString(millis()-startMillis);
mps = ((float)1000.0)*(moveCount-1)/(endMillis-startMillis);
mps = ((float)1000.0)*(moveCount-1)/(millis()-startMillis);
details = moveCount+" mov, "+nf(mps,0,2)+" mps";
text(details,width-10,208);
text("1st move not counted\nin mps calculation",width-10,240);
for(int i = 0; i < SOLVES_IN_AVERAGE; i++){
str = timeToString(times[i]);
if(i == minIndex || i == maxIndex){
text(str,width-10,height-450-i*50);
s = timeToString(average);
text("= "+s,width-10,height-350);
String[] names = {"Controls","Reset with\nbigger board","Reset with\nsmaller board"};
for(int i = 0; i < 3; i++){
rect(1020,height-330+110*i,260,90);
text(names[i],1150,height-295+110*i);
if(abs(mouseX-1150) <= 150 && abs(mouseY-(height-285)) <= 45){
rect(1020,height-740,260,720);
text("CONTROLS\n(not all necessary)\n\nMouse/touch screen:\ndrag tiles\n\nIJKL / arrow keys:\nmove cursor\n\nHold space:\ncling cursor to grid\n\nWASD:\ncling & move cursor\n\nShift + letter:\nTeleport to that tile\n\nEnter:\ninsta-click\nupper-right button",1150,height-705);
String ss = "[SCRAMBLE]";
if(startMillis >= 0 && endMillis >= 0){
}else if(startMillis >= 0 && correctTiles < BOARD_WIDTH*BOARD_HEIGHT){
resizeDelay = max(0,resizeDelay-1);
boolean hasDiff(int newn, int old, boolean isLower){
int diff = (newn+BOARD_WIDTH-old)%BOARD_WIDTH;
return (diff >= 1 && diff <= BOARD_WIDTH/2);
return (diff >= (BOARD_WIDTH+1)/2 && diff <= BOARD_WIDTH-1);
boolean isMoveableTile(boolean isHorizontal){
return (GAME_MODE != 1 ||
((highlightX == centerTileX && !isHorizontal) ||
(highlightY == centerTileY && isHorizontal)));
String timeToString(int mil){
int min = floor(mil/60000);
return nf((float)(sec)/1000,0,3);
return min+":0"+nf((float)(sec)/1000,0,3);
return min+":"+nf((float)(sec)/1000,0,3);
if(BOARD_WIDTH*BOARD_HEIGHT >= 26){
textFont(font,TILE_SIZE*0.53);
textFont(font,TILE_SIZE*0.9);
for(int i = 0; i < 2; i++){
float xDiff = abs(mouseX-1150);
float yDiff = abs(mouseY-(height-185+110*i));
if(xDiff <= 150 && yDiff <= 45){
resetBoard(min(max(BOARD_WIDTH+(1-2*i),2),20));
if(mouseX >= 1020 && mouseY < 100){
highlightX = floor(mouseX/TILE_SIZE%BOARD_WIDTH)%BOARD_WIDTH;
highlightY = floor(mouseY/TILE_SIZE%BOARD_HEIGHT)%BOARD_HEIGHT;
}else if(endMillis == -1){
if(key == CODED && keyCode == SHIFT){
if(isShiftDown && BOARD_WIDTH*BOARD_HEIGHT <= 26){
if(theKey >= 0 && theKey < 26){
for(int x = 0; x < BOARD_WIDTH; x++){
for(int y = 0; y < BOARD_HEIGHT; y++){
if(board[y][x] == theKey){
if(key == 'd' || key == 'D' || key == 'a' || key == 'A' ||
key == 's' || key == 'S' || key == 'w' || key == 'W'){
timeSinceLastWASD = millis();
if((rightPressed() && isSpaceDown) || key == 'd' || key == 'D'){
move(0,highlightX,highlightY, false);
}else if((leftPressed() && isSpaceDown) || key == 'a' || key == 'A'){
move(1,highlightX,highlightY, false);
}else if((downPressed() && isSpaceDown) || key == 's' || key == 'S'){
move(2,highlightX,highlightY, false);
}else if((upPressed() && isSpaceDown) || key == 'w' || key == 'W'){
move(3,highlightX,highlightY, false);
highlightX = (highlightX+1)%BOARD_WIDTH;
highlightX = (highlightX+BOARD_WIDTH-1)%BOARD_WIDTH;
highlightY = (highlightY+1)%BOARD_HEIGHT;
highlightY = (highlightY+BOARD_HEIGHT-1)%BOARD_HEIGHT;
return ((key == CODED && keyCode == RIGHT) || key == 'l' || key == 'L');
return ((key == CODED && keyCode == LEFT) || key == 'j' || key == 'J');
return ((key == CODED && keyCode == DOWN)|| key == 'k' || key == 'K');
return ((key == CODED && keyCode == UP) || key == 'i' || key == 'I');
if(key == CODED && keyCode == SHIFT){
}else if(!isScrambled && startMillis == -1){
float low = pow(max(BOARD_WIDTH,BOARD_HEIGHT),1.4)*60;
float high = pow(max(BOARD_WIDTH,BOARD_HEIGHT),1.5)*60+30;
scramble((int)(random(low, high)));
void move(int moveDirection, int hx, int hy, boolean scrambleMove){
int ph = board[hy][BOARD_WIDTH-1];
for(int x = BOARD_WIDTH-1; x >= 1; x--){
setBoardTile(x,hy,board[hy][x-1]);
if(highlightY == hy) highlightX++;
}else if(moveDirection == 1){
for(int x = 0; x < BOARD_WIDTH-1; x++){
setBoardTile(x,hy,board[hy][x+1]);
setBoardTile(BOARD_WIDTH-1,hy,ph);
if(highlightY == hy) highlightX--;
}else if(moveDirection == 2){
int ph = board[BOARD_HEIGHT-1][hx];
for(int y = BOARD_HEIGHT-1; y >= 1; y--){
setBoardTile(hx,y,board[y-1][hx]);
if(highlightX == hx) highlightY++;
for(int y = 0; y < BOARD_HEIGHT-1; y++){
setBoardTile(hx,y,board[y+1][hx]);
setBoardTile(hx,BOARD_HEIGHT-1,ph);
if(highlightX == hx) highlightY--;
highlightX = (highlightX+BOARD_WIDTH)%BOARD_WIDTH;
highlightY = (highlightY+BOARD_HEIGHT)%BOARD_HEIGHT;
if(startMillis == -1 && isScrambled){
if(correctTiles == BOARD_WIDTH*BOARD_HEIGHT){
}else if(endMillis >= 0){
for(int i = SOLVES_IN_AVERAGE-1; i >= 1; i--){
times[0] = endMillis-startMillis;
if(times[SOLVES_IN_AVERAGE-1] >= 0){
for(int i = 0; i < SOLVES_IN_AVERAGE; i++){
void setBoardTile(int x, int y, int value){
if(isCorrect(x,y)) correctTiles--;
if(isCorrect(x,y)) correctTiles++;
if(value == BOARD_WIDTH*(BOARD_HEIGHT/2)+BOARD_WIDTH/2){
boolean isCorrect(int x, int y){
return (board[y][x] == y*BOARD_WIDTH+x);
void scramble(int scrambleMoveCount){
while(i < scrambleMoveCount || correctTiles == BOARD_WIDTH*BOARD_HEIGHT){
currentMove = getNextRandomMove(currentMove);
move(currentMove,centerTileX,centerTileY,true);
move(currentMove,floor(random(0,BOARD_WIDTH)),floor(random(0,BOARD_HEIGHT)),true);
int getNextRandomMove(int prev){
return floor(random(0,4));
return floor(random(2,4));
return floor(random(0,2));