PageSourceSearch

https://htwins.net/rubikschess/sketch.js

js htwins.net collected 2026-09-24 09:02:38 UTC 53,009 bytes, 1,612 lines download raw bytes

1function createBoard(n, str){
2  var parts = str.split(/\r?\n|\r| |\n/g);
3  var freq;
4  if(parts.length <= 1){
5    freq = 0.40;
6  }else if(parts.length == 2 && !isNaN(parseFloat(parts[1]))){
7    freq = Math.max(0,parseFloat(parts[1]));
8  }else{
9    freq = -1;
10  }
11
12  var PIECE_CHARS = "PNBRQKHSOA";
13  var SIDE_COUNT = 6;
14  var board = [];
15  for(var s = 0; s < SIDE_COUNT; s++){
16    board[s] = [];
17    for(var x = 0; x < n; x++){
18      board[s][x] = [];
19      for(var y = 0; y < n; y++){
20        if(freq == -1){
21          var ya = y;
22          var xa = x;
23          if(s != 1){
24            ya = (B_N-1-y);
25          }
26          if(s == 1){
27            xa = (B_N-1-x);
28          }
29          var stringy = arr_index_safe(parts,1+s*B_N+ya);
30          var x2 = Math.min(xa,stringy.length-1);
31          var ch = stringy.toUpperCase().charAt(x2);
32          if(ch == '-'){
33            board[s][x][y] = 0;
34          }else{
35	    var type = PIECE_CHARS.indexOf(ch);
36	    var player = 1-Math.floor((stringy.charCodeAt(x2)-65)/32);
37	    board[s][x][y] = 1+type+player*PIECE_TYPE_COUNT;
38          }
39        }else{
40	  if(Math.random() < freq){
41	    var pt = WEIRD_PIECES ? PIECE_TYPE_COUNT : NORMAL_TYPE_COUNT;
42	    var team = Math.floor(Math.random()*2);
43	    board[s][x][y] = 1+Math.floor(Math.random()*pt+team*PIECE_TYPE_COUNT);
44	  }else{
45	    board[s][x][y] = 0;
46	  }
47        }
48      }
49    }
50  }
51  return board;
52}
53function arr_index_safe(arr, n){
54	var result = "";
55	if(n < arr.length){
56		result = arr[n];	
57	}
58	while(result.length < B_N){
59		result += "-";
60	}
61	return result;
62}
63function createCube(n, basic){
64  var SIDE_COUNT = 6;
65  var cube = [];
66  for(var s = 0; s < SIDE_COUNT; s++){
67    cube[s] = [];
68    for(var x = 0; x < n; x++){
69      cube[s][x] = [];
70      for(var y = 0; y < n; y++){
71        if(basic){
72          cube[s][x][y] = [0,0]; // 0 = no check, 1 = on check path, 2 = perpetrator of check
73        }else if(BOARD_RUBIK){
74          cube[s][x][y] = s;
75        }else{
76          cube[s][x][y] = (x+y)%2;
77        }
78      }
79    }
80  }
81  return cube;
82}
83function createRotGrid(n){
84  var SIDE_COUNT = 6;
85  var rotGrid = new Array();
86  for(var s = 0; s < SIDE_COUNT; s++){
87    rotGrid[s] = new Array();
88    for(var x = 0; x < n; x++){
89      rotGrid[s][x] = new Array();
90      for(var y = 0; y < n; y++){
91        rotGrid[s][x][y] = Math.random()*2*Math.PI;
92      }
93    }
94  }
95  return rotGrid;
96}
97function createValidDest(n){
98  var SIDE_COUNT = 6;
99  var b = [];
100  for(var s = 0; s < SIDE_COUNT; s++){
101    b[s] = [];
102    for(var x = 0; x < n; x++){
103      b[s][x] = [];
104      for(var y = 0; y < n; y++){
105        b[s][x][y] = [];
106      }
107    }
108  }
109  return b;
110}
111function createVisualBorders(n){
112  var SIDE_COUNT = 6;
113  var vb = [];
114  for(var s = 0; s < SIDE_COUNT; s++){
115    vb[s] = [];
116    for(var d = 0; d < 2; d++){
117      vb[s][d] = [];
118      for(var z = 0; z < n+1; z++){
119        vb[s][d][z] = [0,0];
120      }
121    }
122  }
123  return vb;
124}
125function translateToSide(c, s){
126    c.rotateX(-PI/2);
127    if(s == 1){
128        c.rotateY(PI);
129    }
130    if(s >= 2){
131        c.rotateX(PI/2);
132        c.rotateY((s-2)*PI/2);
133    }
134}
135function calculateScoresAndCheck(){
136  scores = new Array(2);
137  for(var p = 0; p < 2; p++){
138    scores[p] = new Array(PIECE_TYPE_COUNT+1);
139    for(var t = 0; t < PIECE_TYPE_COUNT+1; t++){
140      scores[p][t] = 0;
141    }
142  }
143  var kingLocations = [[0,0,0],[0,0,0]];
144  for(var s = 0; s < board.length; s++){
145    for(var x = 0; x < B_N; x++){
146      for(var y = 0; y < B_N; y++){
147        var b = board[s][x][y];
148        if(b >= 1){
149          var player = getPlayerOf(b);
150          var type = getTypeOf(b);
151          scores[player][type] += 1;
152          scores[player][PIECE_TYPE_COUNT] += PIECE_SCORES[type];
153          if(type == KING_TYPE){
154              kingLocations[player][0] = s;
155              kingLocations[player][1] = x;
156              kingLocations[player][2] = y;
157          }
158        }
159      }
160    }
161  }
162  checkCube = createCube(B_N,true);
163  checks = [0, 0];
164  for(var p = 0; p < 2; p++){
165    if(scores[p][KING_TYPE] == 1){ // you have one king left, who is check-able.
166        var k = kingLocations[p];
167        kingCheckCheck(k[0],k[1],k[2],p);
168    }
169  }
170  detectWinner();
171}
172function drawBoard(c, board, sca){
173  var time_now = Date.now();
174  var drawSliceMove = shouldDrawSliceMove();
175  var sliceInfo;
176  var sliceProg;
177  if(drawSliceMove){
178    var src = lastMoveLocation[0];
179    var dst = lastMoveLocation[1];
180    sliceInfo = getSliceInfo(src, dst);
181    sliceProg = cosInter((time_now-lastMoveTime)/TRANSITION_TIME);
182    drawInnerSlice(c, sliceInfo, sliceProg, sca);
183  }
184  for(var s = 0; s < 6; s++){
185      c.push();
186      translateToSide(c,s);
187      drawSide(c,board,sca,s,time_now,drawSliceMove,sliceInfo,sliceProg);
188      c.pop();
189  }
190  drawMovingPiece(c, board, sca,time_now);
191}
192
193function drawBorders(x,y,sca){
194    for(var dire = 0; dire < 2; dire++){
195        if(x == dire*(B_N-1)){
196            var startY = (y == 0) ? -0.1*sca : 0;
197            var endY = (y == B_N-1) ? sca+0.1*sca : sca;
198            c.push();
199            c.translate(dire*sca,0,-0.1*sca);
200            c.fill(0,0,0);
201            c.rect(-0.1*sca,startY,0.2*sca,endY-startY);
202            c.pop();
203        }
204        if(y == dire*(B_N-1)){
205            var startX = (x == 0) ? -0.1*sca : 0;
206            var endX = (x == B_N-1) ? sca+0.1*sca : sca;
207            c.push();
208            c.translate(0,dire*sca,-0.1*sca);
209            c.fill(0,0,0);
210            c.rect(startX,-0.1*sca,endX-startX,0.2*sca);
211            c.pop();
212        }
213    }
214}
215
216function drawSide(c, board, sca, side, time_now, drawSliceMove, sliceInfo, sliceProg){
217  var n = board[0].length;
218  c.push();
219  c.translate(-n/2*sca,-n/2*sca,-sca*n/2);
220  for(var d = 0; d < 2; d++){
221    for(var z = 0; z < n+1; z++){
222      if(d == 0){
223        vb[side][d][z][0] = c.screenPosition(z*sca,0,0);
224        vb[side][d][z][1] = c.screenPosition(z*sca,n*sca,0);
225      }else if(d == 1){
226        vb[side][d][z][0] = c.screenPosition(0,z*sca,0);
227        vb[side][d][z][1] = c.screenPosition(n*sca,z*sca,0);
228      }
229    }
230  }
231  c.pop();
232
233  for(var x = 0; x < n; x++){
234    for(var y = 0; y < n; y++){
235      var tile = cube[side][x][y];
236      c.fill(PLAYER_COLORS[tile][0]);
237      c.push();
238      if(drawSliceMove){
239          getSliceRotation(c, side, x, y, sliceInfo, sliceProg);
240      }
241      c.translate(-n/2*sca,-n/2*sca,-sca*n/2);
242      c.translate(x*sca,y*sca,0);
243      c.rect(0.02*sca,0.02*sca,0.96*sca,0.96*sca);
244
245      c.push();
246      c.translate(0,0,0.001*sca);
247      c.fill(PLAYER_COLORS[tile][1]);
248      c.rect(0,0,sca,sca);
249      if(!GO_OVER_EDGES){
250        drawBorders(x,y,sca);
251      }
252      c.pop();
253      if(isValidOption(side,x,y)){
254        drawSmallSquare(c,sca,VALID_COLOR[Math.min(VALID_COLOR.length-1,cube[side][x][y])],0.12,0.001);
255      }
256      if(side == selection[0] && x == selection[1] && y == selection[2]){
257        drawSmallSquare(c,sca,SELECTION_COLOR,0.125,0.001);
258      }
259      var check_col = SHOW_CHECK[0] ? Math.max(checkCube[side][x][y][0],checkCube[side][x][y][1]) : 0;
260      if(check_col >= 1){
261          var ccd = darken(CHECK_COLOR,0.3+0.7*checkColorFac(time_now));
262          drawSmallSquare(c,sca,ccd,0.25,0.002);
263      }
264      var piece = board[side][x][y];
265      if(piece >= 1){
266        c.push();
267        if(shouldDrawTransition(time_now) && (side == lastMoveLocation[1][0] && x == lastMoveLocation[1][1] && y == lastMoveLocation[1][2])){
268            var fac = 1-cosInter((time_now-lastMoveTime)/TRANSITION_TIME);
269            if(lastMovePiece >= 1){
270                drawPiece(lastMovePiece, sca, fac, rotGrid[side][x][y],0,0,check_col,time_now);
271            }
272        }else{
273            drawPiece(piece, sca, 1.0, rotGrid[side][x][y],0,0,check_col,time_now);
274        }
275        c.pop();
276      }
277      c.pop();
278    }
279  }
280}
281function checkColorFac(time_now){
282    return 0.5+0.5*Math.sin(time_now*2*(2*Math.PI)/1000);
283}
284function darken(COL, val){
285    return [COL[0]*val,COL[1]*val,COL[2]*val];
286}
287function drawSmallSquare(c,sca,col,ratio,height){
288    c.fill(col);
289    c.push();
290    c.translate(0,0,-height*sca);
291    c.rect(sca*ratio,sca*ratio,sca*(1-ratio*2),sca*(1-ratio*2));
292    c.pop();
293}
294function drawMovingPiece(c, board, sca, time_now){
295    if(shouldDrawTransition(time_now)){
296        var tile = deepIndex(board,lastMoveLocation[1]);
297
298        var fac = 1-cosInter((time_now-lastMoveTime)/TRANSITION_TIME);
299        
300        var step = Math.min(Math.max(Math.floor(fac*travelPath.length),0),travelPath.length-1);
301
302        var step_fac = fac*travelPath.length-step;
303        var startLoc = travelPath[step][0].slice();
304        var endLoc = travelPath[step][1].slice();
305        var end_OOB = travelPath[step][2].slice();
306
307
308        if(end_OOB.length >= 3){
309            startLoc = end_OOB.slice()
310        }
311        var s = startLoc[0];
312        var x = interp(startLoc[1],endLoc[1],step_fac);
313        var y = interp(startLoc[2],endLoc[2],step_fac);
314
315        var sL = travelPath[0][0];
316        var eL = travelPath[travelPath.length-1][1];
317        var startRot = rotGrid[sL[0]][sL[1]][sL[2]];
318        var endRot = rotGrid[eL[0]][eL[1]][eL[2]];
319
320        if(!inBoundsXY(x+0.5,y+0.5)){
321        	var pointer = [s,x+0.5,y+0.5];
322        	var dire = [0,0];
323        	var result = goOverEdge(pointer,dire,0);
324        	s = result[0][0];
325        	x = result[0][1]-0.5;
326        	y = result[0][2]-0.5;
327
328        }
329
330        var rotation = cosInterp(startRot,endRot,fac);
331        c.push();
332        translateToSide(c,s);
333        c.translate(-B_N/2*sca,-B_N/2*sca,-sca*B_N/2);
334        c.translate(x*sca,y*sca,0);
335        var rotX = 0;
336        if(x < 0){
337            rotX = -x*Math.PI/2;
338        }else if(x >= B_N-1){
339            rotX = -(x-(B_N-1))*Math.PI/2;
340        }
341        var rotY = 0;
342        if(y < 0){
343            rotY = y*Math.PI/2;
344        }else if(y >= B_N-1){
345            rotY = (y-(B_N-1))*Math.PI/2;
346        }
347        var L = lastMoveLocation[1];
348        var ch = checkCube[L[0]][L[1]][L[2]];
349        var check_col = SHOW_CHECK[0] ? Math.max(ch[0],ch[1]) : 0;
350        if(lastMoveLocation[2] == -1 || getTypeOf(tile) == PAWN_TYPE){
351            drawPiece(tile, sca, 1.0, rotation,rotX,rotY,check_col,time_now);
352        }else{
353            drawPiece(lastMoveLocation[2], sca, fac, rotation,rotX,rotY,check_col,time_now);
354            drawPiece(tile, sca, 1-fac, rotation,rotX,rotY,check_col,time_now);
355        }
356        c.pop();
357    }
358}
359function interp(a,b,x){
360    return a+(b-a)*x;
361}
362function cosInterp(a,b,x){
363	return a+(b-a)*cosInter(x);
364}
365function color_interp(a,b,x){
366    return [interp(a[0],b[0],x),interp(a[1],b[1],x),interp(a[2],b[2],x)];
367}
368
369function drawPiece(piece, sca, size, rotation, rotX, rotY, check_val, time_now){
370    c.push();
371    c.translate(0.5*sca,0.5*sca,0);
372    c.scale(0.195*size*sca);
373    c.rotateY(rotX);
374    c.rotateX(rotY);
375    c.rotateZ(rotation);
376
377    c.rotateX(-0.5*Math.PI);
378    var col = PLAYER_COLORS[getPlayerOf(piece)][2];
379    if(check_val >= 2){
380        col = color_interp(col,CHECK_COLOR,0.6*checkColorFac(time_now));
381    }
382    c.fill(col);
383    c.model(pieceModels[getTypeOf(piece)]);
384    c.pop();
385}
386function shouldDrawTransition(time_now){
387    var moveAge = time_now-lastMoveTime;
388    if(moveAge < TRANSITION_TIME){
389        return (lastMoveType != 3);
390    }else{
391        return false;
392    }
393}
394function shouldDrawSliceMove(){
395    var now = Date.now();
396    var moveAge = now-lastMoveTime;
397    if(moveAge < TRANSITION_TIME){
398        return (lastMoveType == 3);
399    }else{
400        return false;
401    }
402}
403function cosInter(x){
404    return 0.5-0.5*Math.cos(x*Math.PI);
405}
406function setWorldDrawings(c){
407  c.background(BG_COLOR);
408  c.noStroke();
409
410  c.directionalLight(255, 255, 230, -1, 0.7, -1.3);
411  c.directionalLight(255, 255, 230, 0.7, 0, -1);
412  c.directionalLight(255, 255, 230, 1, 0.7, 1.3);
413  c.directionalLight(255, 255, 230, -0.7, -1.3, 1);
414
415  if(pressed){
416      rotY += 0.005*(mouseX-prev_mouseX);
417      rotX += -0.005*(mouseY-prev_mouseY);
418      if(abs(prev_mouseX-mouseX) >= 2 || abs(prev_mouseY-mouseY) >= 2){
419          moved = true;
420      }
421      prev_mouseX = mouseX;
422      prev_mouseY = mouseY;
423  }
424  c.rotateX(rotX);
425  c.rotateY(rotY);
426}
427function invert2x2(m){
428  var a = m[0][0];
429  var b = m[0][1];
430  var c = m[1][0];
431  var d = m[1][1];
432  var result = new Array();
433  for(var i = 0; i < 2; i++){
434    result[i] = new Array();
435    for(var j = 0; j < 2; j++){
436      result[i][j] = 0;
437    }
438  }
439  var det = a*d-b*c;
440  if(det < 0){
441    return -1;
442  }
443  result[0][0] = d/det;
444  result[1][1] = a/det;
445  result[0][1] = -b/det;
446  result[1][0] = -c/det;
447  return result;
448}
449function vectorTimes2x2(v2,A){
450  if(v2 instanceof p5.Vector) {
451    var v = [v2.x,v2.y];
452  }else{
453    var v = v2;
454  }
455  return [v[0]*A[0][0]+v[1]*A[1][0],v[0]*A[0][1]+v[1]*A[1][1]];
456}
457function diff(a, b){
458  return [a.x-b.x,a.y-b.y];
459}
460function pluralize(p, t){
461    if(scores[p][t] == 1){
462        return scores[p][t]+" "+PIECE_NAMES[t];
463    }else{
464        return scores[p][t]+" "+PIECE_NAMES[t]+"s";
465    }
466}
467function drawUI(c){
468    push();
469    translate(-W_W/2,-W_H/2);
470    drawArsenals();
471    drawAlerts();
472    drawTogs();
473    drawHistory();
474    drawPP_UI();
475    pop();
476}
477function drawArsenals(){
478    ui.background(PLAYER_COLORS[turn][0]);
479    ui.noStroke();
480    ui.fill(PLAYER_COLORS[turn][1]);
481    ui.textFont('Arial', 22);
482    ui.textSize(22);
483    
484    var x = 12;
485    
486    var pName = "Any";
487    if(TAKE_TURNS){
488      pName = PLAYER_NAMES[turn];
489    }
490    ui.text(pName+"'s turn!",x,30);
491    
492    for(var p = 0; p < 2; p++){
493      ui.text(PLAYER_NAMES[p]+"'s arsenal:",x,80+290*p);
494      var t_count = WEIRD_PIECES ? PIECE_TYPE_COUNT : NORMAL_TYPE_COUNT;
495      for(var t = 0; t < t_count+1; t++){
496          var pointy = "";
497          if(t < t_count){
498              pointy = " ("+PIECE_SCORES[t]+"p)";
499          }
500          var t2 = t;
501          if(!WEIRD_PIECES && t == t_count){
502              t2 = PIECE_TYPE_COUNT;
503          }
504      	ui.text(pluralize(p,t2)+pointy,x,80+290*p+(t+1)*22);
505      }
506    }
507    var options = [TAKE_TURNS,GO_OVER_EDGES,ENEMY_COLOR_MUD,BOARD_RUBIK,WEIRD_PIECES];
508    for(var op = 0; op < 5; op++){
509    	var str = OPTION_NAMES[op]+" ";
510    	if(options[op]){
511    	  str += "Yes";
512    	}else{
513    	  str += "No";
514    	}
515    	ui.text(str,x,660+op*22);
516    }
517    ui.text("Size: "+B_N+"x"+B_N+"x"+B_N,x,660+110);
518    image(ui,20,20);
519}
520function drawAlerts(){
521    if(winner >= 0){
522        drawAlert([PLAYER_NAMES[winner].toUpperCase(),"WINS THE GAME!","","But I'll let you keep","going, because I'm","so nice."],[0,0,255],PLAYER_COLORS[winner][0],PLAYER_COLORS[winner][1]);
523    }else if(checks[0] >= 1 && checks[1] >= 1){
524        drawAlert(["Somehow, both","players are in","check right now.","","How is this","happening."],CHECK_COLOR,PLAYER_COLORS[0][0],PLAYER_COLORS[0][1]);
525    }else if((checks[0] >= 1|| checks[1] >= 1) && SHOW_CHECK[0]){
526        var p = checks[0] ? 0 : 1;
527        drawAlert([PLAYER_NAMES[p]+" is in check","from a",PLAYER_NAMES[1-p]+" "+PIECE_NAMES[checks[p]-1]+".","",PLAYER_NAMES[p]+", get your","king to safety!"],CHECK_COLOR,PLAYER_COLORS[p][0],PLAYER_COLORS[p][1]);
528    }
529}
530function drawTogs(){
531    for(var t = 0; t < toggles.length; t++){
532        var tog = toggles[t];
533        var col = (tog[0] ? 255 : 0);
534        var tc = toggles[t][6];
535        tc.noStroke();
536        tc.background(col);
537        tc.textFont('Arial', 22);
538        tc.textSize(22);
539        tc.fill(255-col);
540        tc.text(tog[5],10,30);
541        tc.text((tog[0] ? "Yes" : "No"),10,55);
542        image(tc,tog[1],tog[2]);
543    }
544}
545function drawHistory(){
546    var can_click = [(history_index >= 1),(game_history.length > history_index+1)];
547    var hc = history_UI[5];
548    hc.background(BG_COLOR);
549    hc.noStroke();
550    for(var a = 0; a < 2; a++){
551        var h = history_UI[1+a];
552        hc.fill(can_click[a] ? 255 : 158);
553        hc.rect(h[0],h[1],h[2],h[3]);
554        hc.fill(can_click[a] ? 0 : 98);
555        hc.beginShape();
556        hc.vertex(h[0]+50-30*a,h[1]+15);
557        hc.vertex(h[0]+50-30*a,h[1]+55);
558        hc.vertex(h[0]+15+40*a,h[1]+35);
559        hc.endShape(CLOSE);
560    }
561    hc.fill(darken(BG_COLOR,0.8));
562    var h = history_UI[3];
563    hc.rect(h[0],h[1],h[2],h[3]);
564    hc.fill(darken(BG_COLOR,0.2));
565    var f1 = (game_history.length <= 1 ? 0 : history_index/(game_history.length-1));
566    var f2 = 1/game_history.length;
567    var w = Math.max(h[4],f2*h[2]);
568    hc.rect(h[0]+f1*(h[2]-w),h[1],w,h[3]);
569    
570    hc.textFont('Arial', 30);
571    hc.textSize(22);
572    hc.textAlign(CENTER);
573    hc.fill(0);
574    hc.text(history_index+" / "+(game_history.length-1),history_UI[4][0],history_UI[4][1]);
575    image(hc,history_UI[0][0],history_UI[0][1]);
576}
577function getPlayerOf(b){
578    return Math.floor((b-1)/PIECE_TYPE_COUNT);
579}
580function getTypeOf(b){
581    return (b-1)%PIECE_TYPE_COUNT;
582}
583function deepIndex(arr,index){
584    return arr[index[0]][index[1]][index[2]];
585}
586function drawPP_UI(){
587    if(pawnPromotionInfo[0]){
588        var pc = PP_UI[3];
589        var player = getPlayerOf(deepIndex(board,pawnPromotionInfo[1]));
590        pc.noStroke();
591        pc.background(PLAYER_COLORS[player][0]);
592        pc.fill(PLAYER_COLORS[player][1]);
593        pc.textFont('Arial', 22);
594        pc.textSize(22);
595        pc.textAlign(CENTER);
596        pc.text("Promote to:",PP_UI[2][0],PP_UI[2][1]);
597        for(var i = 0; i < PAWN_PROMOTION_OPTIONS.length; i++){
598            var t = PAWN_PROMOTION_OPTIONS[i];
599            if(WEIRD_PIECES || t < NORMAL_TYPE_COUNT){
600                pc.fill(128,128,128,64+64*(i%2));
601                var y = PP_UI[1][1]+i*PP_UI[1][3];
602                var h = PP_UI[1][3]
603                pc.rect(PP_UI[1][0],y,PP_UI[1][2],h);
604                pc.fill(PLAYER_COLORS[player][1]);
605                pc.text(PIECE_NAMES[t],PP_UI[2][0],y+h*0.7);
606            }
607        }
608        image(pc,PP_UI[0][0],PP_UI[0][1]);
609    }
610}
611function drawAlert(strs,col1,col2,col3){
612    alert.noStroke();
613    alert.background(col1);
614    alert.fill(col2);
615    alert.rect(20,20,220,220);
616    alert.fill(col3);
617    alert.textFont('Arial', 22);
618    alert.textSize(22);
619    for(var i = 0; i < strs.length; i++){
620        alert.text(strs[i],30,50+25*i);
621    }
622    image(alert,W_W-280,20);
623}
624function customGameButton(){
625  var newGameString = document.getElementById("newGame").value;
626  createNewGame(newGameString);
627}
628function standardGameButton(){
629  createNewGame("YYYYY7\nhhopp--\nhrbbp--\nonrqs--\npnakp--\nppspp--\n-------\n-------\n--PPOHH\n--PBBRH\n--SQRNO\n--PKANP\n--PPSPP\n-------\n-------");
630}
631function sandboxGameButton(){
632  createNewGame("NYNYY7 0.4");
633}
634function walledGameButton(){
635  createNewGame("YNYYY7\nhhopp--\nhrbbp--\nonrqs--\npnakp--\nppspp--\n-------\n-------\n--PPOHH\n--PBBRH\n--SQRNO\n--PKANP\n--PPSPP\n-------\n-------");
636}
637function simpleGameButton(){
638  createNewGame("YYNYN5\nhnpp-\nnbrp-\npqkp-\npppp-\n-----\n-PPNH\n-PRBN\n-PKQP\n-PPPP\n-----\n\n\n\n\nh\n\n\n\n\n----h\nH\n\n\n\n\n----H");
639}
640function createNewGame(str){
641  document.getElementById("newGame").value = str;
642  var parts = str.split(/\r?\n|\r| |\n/g);
643  var options = parts[0].toUpperCase();
644  TAKE_TURNS = (options.charAt(0) == 'Y');
645  GO_OVER_EDGES = (options.charAt(1) == 'Y');
646  ENEMY_COLOR_MUD = (options.charAt(2) == 'Y');
647  BOARD_RUBIK = (options.charAt(3) == 'Y');
648  WEIRD_PIECES = (options.charAt(4) == 'Y');
649  B_N = 7;
650  if(options.length >= 6){
651      B_N = Math.min(Math.max(Number(options.substring(5,options.length)),2),20);
652  }
653  clearSelection();
654  winner = -1;
655  board = createBoard(B_N, str);
656  cube = createCube(B_N, false);
657  game_history = [];
658  turn = 0;
659  appendGameHistory();
660  history_index = 0;
661  checkCube = createCube(B_N,true);
662  rotGrid = createRotGrid(B_N);
663  validDest = createValidDest(B_N);
664  vb = createVisualBorders(B_N);
665  calculateScoresAndCheck();
666  lastMoveTime = 0;
667}
668
669function getClickedOn(s,inverse,mX,mY,d){
670  var orig = vb[s][0][0][0];
671  var mouseB = vectorTimes2x2([mX-orig.x,mY-orig.y],inverse);
672
673  var n = board[0].length;
674  for(var z = 0; z < n+1; z++){
675    var p1 = vectorTimes2x2(diff(vb[s][d][z][0],orig),inverse);
676    var p2 = vectorTimes2x2(diff(vb[s][d][z][1],orig),inverse);
677    var e = 1-d;
678    var m = (p2[e]-p1[e])/(p2[d]-p1[d]);
679    var b = -p1[d]*m+p1[e];
680    if(mouseB[e] <= mouseB[d]*m+b){
681      return z-1;
682    }
683  }
684  return -1;
685}
686function drawWireframe(){
687  var colors = [[255,0,0],[255,128,0],[255,255,0],[0,255,0],[0,0,255],[128,0,255]];
688  for(var s = 0; s < 6; s++){
689    strokeWeight(2);
690    stroke(colors[s]);
691    for(var d = 0; d < 2; d++){
692      for(var z = 0; z < 8; z++){
693        var x1 = vb[s][d][z][0].x;
694        var y1 = vb[s][d][z][0].y;
695        var x2 = vb[s][d][z][1].x;
696        var y2 = vb[s][d][z][1].y;
697        line(x1,y1,x2,y2);
698      }
699    }
700  }
701}
702function inBounds(point){
703    var x = point[1];
704    var y = point[2];
705    return inBoundsXY(x,y);
706}
707function inBoundsXY(x,y){
708	return (x >= 0 && x < B_N && y >= 0 && y < B_N);
709}
710function getLastMoveType(dst){
711    var v = validDest[dst[0]][dst[1]][dst[2]];
712    return v[v.length-1][3];
713}
714function doMove(src, dst, transformTo){
715    lastMoveLocation[0] = src;
716    lastMoveLocation[1] = dst;
717    lastMoveLocation[2] = -1;
718    lastMoveTime = Date.now();
719    lastMoveType = getLastMoveType(dst);
720
721    if(lastMoveType == 3){
722        sliceMove(src,dst);
723    }else{
724        lastMovePiece = board[dst[0]][dst[1]][dst[2]];
725        board[dst[0]][dst[1]][dst[2]] = board[src[0]][src[1]][src[2]];
726        if(transformTo >= 0){
727            lastMoveLocation[2] = board[src[0]][src[1]][src[2]];
728            board[dst[0]][dst[1]][dst[2]] = transformTo;
729        }
730        board[src[0]][src[1]][src[2]] = 0;
731        travelPath = validDest[dst[0]][dst[1]][dst[2]];
732    }
733    if(TAKE_TURNS){
734    	turn = 1-turn;
735    }
736    clearSelection();
737    calculateScoresAndCheck();
738    appendGameHistory();
739}
740function appendGameHistory(){
741    if(history_index < game_history.length-1){
742        game_history = game_history.slice(0,history_index+1);
743    }
744    game_history.push([deepCopy(board),deepCopy(cube),turn,winner]);
745    history_index = game_history.length-1;
746}
747function loadFromGameHistory(){
748    board = deepCopy(game_history[history_index][0]);
749    cube = deepCopy(game_history[history_index][1]);
750    turn = game_history[history_index][2];
751    winner = game_history[history_index][3];
752    clearSelection();
753    calculateScoresAndCheck();
754    lastMoveTime = 0;
755}
756function detectWinner(){
757    if(winner == -1){
758        for(var p = 0; p < 2; p++){
759            if(scores[p][5] == 0){
760                winner = 1-p;
761            }
762        }
763    }
764}
765function index_flip(arr){
766    var result = new Array(5);
767    for(var i = 0; i < 2; i++){
768        result[i] = arr[i].slice();
769    }
770    result[2] = B_N-1-arr[2];
771    result[3] = arr[3];
772    result[4] = arr[4];
773    return result;
774}
775function invertA(arr){
776    var result = new Array(4);
777    result[2] = arr[2];
778    for(var i = 0; i < 2; i++){
779        result[i] = arr[i].slice();
780        result[i][1] = arr[i][3];
781        result[i][3] = arr[i][1];
782    }
783    result[3] = -arr[3];
784    result[4] = arr[4];
785    return result;
786}
787
788function getSliceInfo(src, dst){
789    var sFace = src[0];
790    var eFace = dst[0];
791
792    // the first array are the sides that are cycled, where the first four are the ones we care about, and the last two are the "left and right" sides to be rotated.
793    // the second array is the "axis" (row, column, upside-down row, upside-down column) of those sides to be altered, respectively.
794    // The third info is the index of the row, the fourth is the direction of it, and the fifth is the axis of rotation in world space (M, S, or E).
795
796    var M = [[0,2,1,4,3,5],[0,0,2,2],src[1],1,0];
797    var M_if = [[0,2,1,4,3,5],[0,0,2,2],B_N-1-src[1],1,0];
798    var S = [[0,3,1,5,4,2],[1,0,1,2],src[2],1,1];
799    var S_alt = [[0,3,1,5,4,2],[1,0,1,2],src[1],1,1];
800    var S_alt_if = [[0,3,1,5,4,2],[1,0,1,2],B_N-1-src[1],1,1];
801    var E = [[2,3,4,5,0,1],[1,1,1,1],src[2],1,2];
802
803    var sliceInfo = [
804        [[], [], M, S, invertA(M), invertA(S)],
805        [[], [], invertA(M_if), invertA(S), M_if, S],
806        [invertA(M), M, [], E, [], invertA(E)],
807        [invertA(S_alt), S_alt, invertA(E), [], E, []],
808        [M_if, invertA(M_if), [], invertA(E),  [], E],
809        [S_alt_if, invertA(S_alt_if), E, [], invertA(E), []]
810        ];
811    return sliceInfo[sFace][eFace];
812}
813
814function sliceMove(src, dst){
815    var sliceInfo = getSliceInfo(src, dst);
816    sliceMoveBoth(sliceInfo[0],sliceInfo[1],sliceInfo[2],sliceInfo[3]);
817}
818
819function sliceMoveBoth(faces,axes,index,dire){
820    sliceMoveInner(board,faces,axes,index);
821    sliceMoveInner(cube,faces,axes,index);
822    if(index == 0){
823    	rotateFace(board, faces[4], dire);
824    	rotateFace(cube, faces[4], dire);
825    }else if(index == B_N-1){
826    	rotateFace(board, faces[5], -dire);
827    	rotateFace(cube, faces[5], -dire);
828    }
829}
830
831function rotateFace(arr, f, dire){
832	var newFace = new Array(B_N);
833	for(var x = 0; x < B_N; x++){
834		newFace[x] = new Array(B_N);
835		for(var y = 0; y < B_N; y++){
836			if(dire > 0){
837				newFace[x][y] = arr[f][y][B_N-1-x];
838			}else{
839				newFace[x][y] = arr[f][B_N-1-y][x];
840			}
841		}
842	}
843	arr[f] = newFace;
844}
845
846function sliceMoveInner(arr,faces,axes,index){
847    var ph_values = getSlice(arr,faces[3],axes[3],index);
848    for(var i = 2; i >= 0; i--){
849        var slice_values = getSlice(arr,faces[i],axes[i],index);
850        setSlice(arr,faces[i+1],axes[i+1],index,slice_values);
851    }
852    setSlice(arr,faces[0],axes[0],index,ph_values);
853}
854
855function getSlice(arr,face,axis,index){
856    var result = new Array(B_N);
857    for(var i = 0; i < B_N; i++){
858        var j = i;
859        var jindex = index;
860        if(axis == 1 || axis == 2){
861            j = B_N-1-i;
862        }
863        if(axis == 2 || axis == 3){
864            jindex = B_N-1-index;
865        }
866        if(axis%2 == 0){
867            result[i] = arr[face][jindex][j];
868        }else{
869            result[i] = arr[face][j][jindex];
870        }
871    }
872    return result;
873}
874function setSlice(arr,face,axis,index,values){
875    var result = new Array(B_N);
876    for(var i = 0; i < B_N; i++){
877        var j = i;
878        var jindex = index;
879        if(axis == 1 || axis == 2){
880            j = B_N-1-i;
881        }
882        if(axis == 2 || axis == 3){
883            jindex = B_N-1-index;
884        }
885        if(axis%2 == 0){
886            arr[face][jindex][j] = values[i];
887        }else{
888            arr[face][j][jindex] = values[i];
889        }
890    }
891}
892function drawInnerSlice(c, sliceInfo, sliceProg, sca){
893    var b = B_N*sca;
894    var index = sliceInfo[2];
895    var dire = sliceInfo[3];
896    
897    for(var ss = 0; ss < 4; ss++){ // two different sides of the slice.
898        if((index == 0 && ss < 2) || (index == B_N-1 && ss >= 2)){
899            continue;
900        }
901        c.push();
902        if(sliceInfo[4] == 0){ // M slice
903            c.rotateY(Math.PI/2);
904        }else if(sliceInfo[4] == 1){ // S slice
905            c.rotateY(Math.PI);
906        }else if(sliceInfo[4] == 2){ // E slice
907            c.rotateX(-Math.PI/2);
908        }
909        c.translate(0,0,-b/2+(index+Math.floor(ss/2))*sca);
910        if(ss%2 == 0){ // half of the inner slice squares should be on the rotating slice, and half should be on the stable remainder.
911            c.rotateZ(dire*sliceProg*Math.PI/2);
912        }
913        c.fill(INSIDE_COLOR);
914        c.rect(-b/2,-b/2,b,b);
915        c.pop();
916    }
917}
918function getSliceRotation(c, s, x, y, sliceInfo, sliceProg){
919    var rotating = false;
920    var axis_choice = 0;
921    var faces = sliceInfo[0];
922    var index = sliceInfo[2];
923    var dire = sliceInfo[3];
924    for(var ss = 0; ss < 4; ss++){
925        if(faces[ss] == s){
926            var axis = sliceInfo[1][ss];
927            for(var i = 0; i < B_N; i++){
928                var j = i;
929                var jindex = index;
930                if(axis == 1 || axis == 2){
931                    j = B_N-1-i;
932                }
933                if(axis == 2 || axis == 3){
934                    jindex = B_N-1-index;
935                }
936                if(axis%2 == 0){
937                    if(jindex == x && j == y){
938                        // WILL ROTATE!
939                        rotating = true;
940                        axis_choice = axis;
941                    }
942                }else{
943                    if(j == x && jindex == y){
944                        // WILL ROTATE!
945                        rotating = true;
946                        axis_choice = axis;
947                    }
948                }
949            }
950        }
951    }
952    var fac = -PI/2*sliceInfo[3]*(1-sliceProg);
953    if(rotating){
954        if(mouseX < 100){
955        	fac = 0;
956        }
957        c.rotateZ(axis_choice*PI/2);
958        c.rotateX(fac);
959        c.rotateZ(-axis_choice*PI/2);
960    }
961    if(index == 0){
962    	if(s == faces[4]){
963    	    c.rotateZ(fac);
964    	}
965    }else if(index == B_N-1){
966    	if(s == faces[5]){
967    	    c.rotateZ(-fac);
968    	}
969    }
970    return 0;
971}
972
973function areSamePoint(a, b){
974    return (a[0] == b[0] && a[1] == b[1] && a[2] == b[2]);
975}
976function clearSelection(){
977    selection = [-1,-1,-1];
978    pawnPromotionInfo = [false,0,0];
979    validDest = createValidDest(B_N);
980}
981
982function splatoonSpread(loc, player, boundaryType, check_check){
983	var dires = [[0,1],[1,0],[0,-1],[-1,0]];
984	var locationList = [loc.slice()];
985	var checklist = []; // for checking for if any splatoons are checking the king.
986	var typeToSee = SPLATOON_TYPE + (boundaryType ? 1 : 2) + (player == 0 ? 0 : PIECE_TYPE_COUNT); // if doing a king-check-check, which piece would you be "scared" to see in your line-of-vision?
987	while(locationList.length >= 1){
988	    var currLoc = locationList.shift();
989    	var s = currLoc[0];
990    	var x = currLoc[1];
991    	var y = currLoc[2];
992    	if(ENEMY_COLOR_MUD && cube[s][x][y] == 1-player){
993    		continue; // banned because you're on the opposite color (toxic stuff)
994    	}
995    	var move_type = boundaryType ? 4 : 5;
996    	for(var d = 0; d < dires.length; d++){
997    		var x_a = x+dires[d][0];
998    		var y_a = y+dires[d][1];
999    		var appLoc = [s,x_a,y_a];
1000    		var newLoc = appLoc;
1001    		if(!inBounds(appLoc) && GO_OVER_EDGES){
1002                newLoc = goOverEdge(appLoc,[0,0],1)[0];
1003    		}
1004    		var s_n = newLoc[0];
1005    		var x_n = newLoc[1];
1006    		var y_n = newLoc[2];
1007    		if(inBoundsXY(x_n,y_n) && !areSamePoint(newLoc,loc) && boundaryType == (cube[s][x][y] == cube[s_n][x_n][y_n]) && validDest[s_n][x_n][y_n].length == 0 && 
1008    		(!ENEMY_COLOR_MUD || cube[s_n][x_n][y_n] != 1-player)){
1009    		    var t = board[s_n][x_n][y_n];
1010    		    var moveType = (t == 0 || (t <= PIECE_TYPE_COUNT) != (player == 0)) ? move_type : -1;
1011    		    var validDestChain = validDest[s][x][y].slice();
1012    		    validDestChain.unshift([newLoc.slice(),currLoc.slice(),appLoc,moveType]);
1013    			validDest[s_n][x_n][y_n] = validDestChain;
1014    			locationList.push(newLoc);
1015    			
1016    			if(check_check && t == typeToSee){
1017			        for(var v = 0; v < validDestChain.length; v++){
1018			            checklist.push(validDestChain[v][0]);
1019			        }
1020			        checklist.push(loc);
1021    			}
1022    		}
1023    	}
1024	}
1025	if(check_check){ // cleanse the validDest array, since no piece is actually selected.
1026	    validDest = createValidDest(B_N);
1027	    return checklist;
1028	}
1029}
1030function isValidOption(side,x,y){
1031    if(pawnPromotionInfo[0]){
1032        var p = pawnPromotionInfo[2];
1033        return (side == p[0] && x == p[1] && y == p[2]);
1034    }
1035    if(validDest[side][x][y].length >= 1){
1036        var v = validDest[side][x][y];
1037        return (v[0][3] >= 0);
1038    }else{
1039        return false;
1040    }
1041}
1042
1043var DIRE_TYPES = [  // HERE ARE THE FIVE FUNDAMENTAL DIRECTION TYPES
1044      [[0,1],[0,-1],[1,0],[-1,0]],    // orthogonal direction (rook)
1045      [[1,1],[1,-1],[-1,1],[-1,-1]],    // diagonal direction (bishop)
1046      [[1,0],[1,0],[1,0],[1,0],
1047      [-1,0],[-1,0],[-1,0],[-1,0],
1048      [0,1],[0,1],[0,1],[0,1],
1049      [0,-1],[0,-1],[0,-1],[0,-1]
1050      ],   // knight direction (simplified for the weird corner case)
1051      [[0,1,1],[0,-1,1],[1,0,1],[-1,0,1]], // hand turn (Rubik's slice move)
1052      [], // splatoon
1053      ];
1054  // 1st number is the direction type (index) the piece can do. The second number is whether it can move 
1055  // 2nd number whether the piece can:
1056  //     0) move 1 tile into an empty space, only
1057  //     1) move 1 tile to capture. only
1058  //     2) move 1 tile into empty OR to capture
1059  //     3) do a slice turn on the puzzle
1060  //     4) sliding motion, unlimited until it hits a piece or enemy color
1061  //     5) weird knight behavior (this doesn't count as a DIRE_TYPE because internally, a knight move is considered a 3-step move)
1062  // 3rd number is whether this move can still go over edges if GOE is disabled.
1063  // 4th number is whether the move bans same-color movement (nootnoots)
1064  var PIECE_DIRES = [
1065      [[0,0,0,0],[1,1,0,0]], // pawn
1066      [[2,5,0,0]], // knight
1067      [[1,4,0,0]], // bishop
1068      [[0,4,0,0]],  // rook
1069      [[1,4,0,0],[0,4,0,0]], // queen
1070      [[0,2,0,0],[1,2,0,0]], // king
1071      [[0,2,0,0],[1,2,0,0],[3,3,1,0]], // hand
1072      [[4,0,0,0],[0,2,0,0]], // splatoon
1073      [[1,4,0,1],[0,4,0,1],[2,5,0,1]], // nootnoot
1074      [[1,4,0,0],[0,4,0,0],[2,5,0,0]] // amazon
1075      ];
1076
1077function checkForPawnPromotion(s, boardX, boardY){
1078    var b = deepIndex(board,selection);
1079    var player = getPlayerOf(b);
1080    var t = cube[s][boardX][boardY]
1081    if(getTypeOf(b) == PAWN_TYPE && t == 1-player){ // is this a pawn about to be promoted?
1082         pawnPromotionInfo[0] = true;
1083         pawnPromotionInfo[1] = selection.slice();
1084         pawnPromotionInfo[2] = [s,boardX,boardY];
1085         return true;
1086    }
1087    return false;
1088}
1089
1090function setSelection(s, boardX, boardY){
1091    if(isValidOption(s,boardX,boardY)){
1092        if(!checkForPawnPromotion(s, boardX, boardY)){
1093            var dest = [s,boardX,boardY];
1094            doMove(selection,dest,-1);
1095        }
1096        return;
1097    }
1098  var tile = board[s][boardX][boardY];
1099  var type = getTypeOf(tile);
1100  var player = getPlayerOf(tile);
1101  if(tile == 0 || (TAKE_TURNS && player != turn)){
1102    clearSelection();
1103    return;
1104  }
1105  selection = [s,boardX,boardY];
1106  validDest = createValidDest(B_N);
1107    for(var dt = 0; dt < PIECE_DIRES[type].length; dt++){
1108        var dire_type = PIECE_DIRES[type][dt][0];
1109        var dire_power = PIECE_DIRES[type][dt][1];
1110        var dire_goe = PIECE_DIRES[type][dt][2];
1111        var dire_noot = PIECE_DIRES[type][dt][3];
1112        if(dire_type == 4){ // splatoon spread
1113        	splatoonSpread([s,boardX,boardY], player, (dire_type == 4), false);
1114        }
1115        if(ENEMY_COLOR_MUD && dire_type == 3 && cube[s][boardX][boardY] == 1-player){
1116            continue; // if ECM is on, the hand can't turn on an enemy color.
1117        }
1118        for(var d = 0; d < DIRE_TYPES[dire_type].length; d++){
1119            var dire = DIRE_TYPES[dire_type][d].slice();
1120            if(DIRE_TYPES[dire_type][d].length >= 3){
1121                dire[0] *= B_N;
1122                dire[1] *= B_N;
1123            }
1124            var pointer = [s,boardX,boardY];
1125            var prevPointer = [s,boardX,boardY];
1126            var validDestChain = [];
1127            var keepGoing = true;
1128            var steps = 0;
1129            while(keepGoing){
1130                if(dire_power == 5){
1131                    dire = knightDire(dire,d,steps);
1132                }
1133                var OOB_pointer = [];
1134                pointer[1] += dire[0];
1135                pointer[2] += dire[1];
1136                if(!inBounds(pointer)){
1137                    if (GO_OVER_EDGES || dire_goe == 1){ // allow going over the edges if that feature is allowed, OR you're a hand.
1138                        OOB_pointer = pointer.slice();
1139                        var results = goOverEdge(pointer,dire,1);
1140                        pointer = results[0];
1141                        dire = results[1];
1142                        if(!inBounds(pointer)){ // weird corner case
1143                            keepGoing = false;
1144                        }
1145                    }else{
1146                        keepGoing = false;
1147                    }
1148                }
1149
1150                if(pointer[0] == s && pointer[1] == boardX && pointer[2] == boardY){ // you looped back to where you were.
1151                    keepGoing = false;
1152                }
1153                if(dire_noot == 1 && inBounds(pointer)){ // banning the same-color moves of nootnoots
1154                    var c = cube[pointer[0]][pointer[1]][pointer[2]];
1155                    var prevC = cube[prevPointer[0]][prevPointer[1]][prevPointer[2]];
1156                    var firstC = cube[s][boardX][boardY];
1157                    if((dire_power == 5 && steps == 2 && c == firstC) || (dire_power != 5 && c == prevC)){
1158                        keepGoing = false;
1159                    }
1160                }
1161                if(keepGoing){
1162                    var t = board[pointer[0]][pointer[1]][pointer[2]];
1163                    var c = cube[pointer[0]][pointer[1]][pointer[2]];
1164                    var blocked = (t >= 1);
1165                    var sameTeam = ((t <= PIECE_TYPE_COUNT) == (tile <= PIECE_TYPE_COUNT));
1166                    var validMove = false;
1167                    if(ENEMY_COLOR_MUD && type == 5 && c == 1-player){ // the king can't travel onto enemy territory
1168                    	validMove = false;
1169                    	keepGoing = false;
1170                    }else if(dire_power == 0){
1171                        if(!blocked){
1172                            validMove = true;
1173                        }
1174                        keepGoing = false;
1175                    }else if(dire_power == 1){
1176                        if(blocked && !sameTeam){
1177                            validMove = true;
1178                        }
1179                        keepGoing = false;
1180                    }else if(dire_power == 2 || dire_power == 3){
1181                        if(!blocked || !sameTeam || dire_power == 3){
1182                            validMove = true;
1183                        }
1184                        keepGoing = false;
1185                    }else if(dire_power == 4){
1186                        if(blocked){
1187                            validMove = (!sameTeam);
1188                            keepGoing = false;
1189                        }else if(ENEMY_COLOR_MUD && c == 1-player){
1190                            keepGoing = false;
1191                            validMove = true;
1192                        }else{
1193                            validMove = true;
1194                        }
1195                    }else if(dire_power == 5){ // knight weirdness
1196                        if(steps < 2){
1197                            validMove = false;
1198                            keepGoing = true;
1199                        }else{
1200                            validMove = (!blocked || !sameTeam);
1201                            keepGoing = false;
1202                        }
1203                    }
1204                    if(validMove || keepGoing){
1205                        validDestChain.unshift([pointer.slice(),prevPointer.slice(),OOB_pointer.slice(),dire_power]);
1206                    }
1207                    if(validMove){
1208                        validDest[pointer[0]][pointer[1]][pointer[2]] = validDestChain.slice();
1209                    }
1210                    prevPointer = pointer.slice();
1211                }
1212                steps++;
1213            }
1214        }
1215    }
1216}
1217// check is calculated by pretending the king is every type of piece, and seeing which enemy pieces are "visible".
1218function kingCheckCheck(s, boardX, boardY, player){
1219    var start_c = cube[s][boardX][boardY];
1220    for(var type = 0; type < 3; type++){ // First three directions: Ortho, diag, and knight.
1221        for(var d = 0; d < DIRE_TYPES[type].length; d++){
1222            var dire = DIRE_TYPES[type][d].slice();
1223            var pointer = [s,boardX,boardY];
1224            var cPath = [pointer.slice()];
1225            var keepGoing = true;
1226            var steps = 0;
1227            var prevC = cube[s][boardX][boardY];
1228            var hadSameColorMove = false;
1229            while(keepGoing){
1230                if(type == 2){ // knight movement
1231                    dire = knightDire(dire,d,steps);
1232                }
1233                pointer[1] += dire[0];
1234                pointer[2] += dire[1];
1235                if(!inBounds(pointer)){
1236                    if (GO_OVER_EDGES){
1237                        var results = goOverEdge(pointer,dire,1);
1238                        pointer = results[0];
1239                        dire = results[1];
1240                        if(!inBounds(pointer)){ // weird corner case
1241                            keepGoing = false;
1242                        }
1243                    }else{
1244                        keepGoing = false;
1245                    }
1246                }
1247                if(pointer[0] == s && pointer[1] == boardX && pointer[2] == boardY){ // you looped back to where you were.
1248                    keepGoing = false;
1249                }
1250                steps++;
1251                if(keepGoing){
1252                    var t = board[pointer[0]][pointer[1]][pointer[2]];
1253                    var c = cube[pointer[0]][pointer[1]][pointer[2]];
1254                    if((type != 2 && c == prevC) || (type == 2 && steps == 3 && c == start_c)){
1255                        hadSameColorMove = true;
1256                    }
1257                    prevC = c;
1258                    var blocked = ((t >= 1) && !(type == 2 && steps < 3));
1259                    var enemyTeam = ((t <= PIECE_TYPE_COUNT) != (player == 0));
1260                    if(type == 2 && steps < 3){ // knight weirdness - the 0th and 3rd squares on a knight's path should show a check warning path, but NOT the 1st and 2nd pieces.
1261                    }else{
1262                        cPath.push(pointer.slice());
1263                    }
1264                    if(blocked){
1265                        if(enemyTeam){
1266                            var pt = getTypeOf(t);
1267                            if(ENEMY_COLOR_MUD && start_c == player && pt == KING_TYPE){ // an enemy king cannot attack you on your color.
1268                                continue;
1269                            }
1270                            var validAttack = false;
1271                            for(var m = 0; m < PIECE_DIRES[pt].length; m++){
1272                                if(PIECE_DIRES[pt][m][0] == type && !(PIECE_DIRES[pt][m][3] == 1 && hadSameColorMove)){ // If the king's line-of-sight hits a piece, let's see... does the piece's moveset include that move? And, if there was a same-color move, is it a nootnoot?
1273                                    var dire_power = PIECE_DIRES[pt][m][1];
1274                                    var ONE_STEP = (dire_power == 1 || dire_power == 2);
1275                                    var SLIDE = (dire_power == 4);
1276                                    var KNIGHT = (dire_power == 5);
1277                                    if(SLIDE || (ONE_STEP && steps == 1) || (KNIGHT && steps == 3)){
1278                                        validAttack = true;
1279                                    }
1280                                }
1281                            }
1282                            if(validAttack){
1283                                for(var c = 0; c < cPath.length; c++){
1284                                    var val = (c == 0 || c == cPath.length-1) ? 2 : 1;
1285                                    checkCube[cPath[c][0]][cPath[c][1]][cPath[c][2]][player] = val;
1286                                }
1287                                checks[player] = pt+1; // This variable being set to non-zero tells us, THAT PLAYER IS IN CHECK, and WHICH TYPE OF PIECE IS INFLICTING CHECK.
1288                            }
1289                        }
1290                        keepGoing = false;
1291                    }
1292                    if(type == 2 && steps >= 3){ // knight weirdness
1293                        keepGoing = false;
1294                    }
1295                    if(type != 2 && ENEMY_COLOR_MUD && c == player && steps >= 1){ // If you're the black king, black tiles (your own) stops white opponents!
1296                        keepGoing = false;
1297                    }
1298                }
1299            }
1300        }
1301    }
1302    var sp = splatoonSpread([s, boardX, boardY], 1-player, true, true); // SPLATOON CHECK CHECK - pretend you're a splatoon of the opposite color
1303    if(sp.length >= 1){
1304        for(var i = 0; i < sp.length; i++){
1305            var L = sp[i];
1306            var val = (i == 0 || i == sp.length-1) ? 2 : 1;
1307            checkCube[L[0]][L[1]][L[2]][player] = val;
1308        }
1309        checks[player] = SPLATOON_TYPE;
1310    }
1311}
1312
1313function knightDire(dire,d,steps){
1314    var startDist = 1+d%2;
1315    if(steps == startDist){ // the knight should turn
1316        var turnDire = -1+2*(Math.floor(d/2)%2);
1317        if(dire[0] == 0){
1318            return [turnDire,0];
1319        }else{
1320            return [0,turnDire];
1321        }
1322    }else{ // the knight should not turn
1323        return dire;
1324    }
1325}
1326function goOverEdge(pointer,dire,m){
1327    var s = pointer[0];
1328    var x = pointer[1];
1329    var y = pointer[2];
1330    if(s == 0){
1331        if(y >= B_N){
1332            return [[2,x,y-B_N],[dire[0],dire[1]]];
1333        }else if(y < 0){
1334            return [[4,B_N-m-x,-m-y],[-dire[0],-dire[1]]];
1335        }
1336        if(x >= B_N){
1337            return [[5,B_N-m-y,x-B_N],[-dire[1],dire[0]]];
1338        }else if(x < 0){
1339            return [[3,y,-m-x],[dire[1],-dire[0]]];
1340        }
1341    }else if(s == 1){
1342        if(y >= B_N){
1343            return [[2,B_N-m-x,B_N*2-m-y],[-dire[0],-dire[1]]];
1344        }else if(y < 0){
1345            return [[4,x,B_N+y],[dire[0],dire[1]]];
1346        }
1347        if(x >= B_N){
1348            return [[3,y,B_N*2-m-x],[dire[1],-dire[0]]];
1349        }else if(x < 0){
1350            return [[5,B_N-m-y,B_N+x],[-dire[1],dire[0]]];
1351        }
1352    }else{
1353        if(x >= B_N){
1354            return [[((s-2)+3)%4+2,x-B_N,y],[dire[0],dire[1]]];
1355        }else if(x < 0){
1356            return [[((s-2)+1)%4+2,x+B_N,y],[dire[0],dire[1]]];
1357        }
1358        if(s == 2){
1359            if(y >= B_N){
1360                return [[1,B_N-m-x,B_N*2-m-y],[-dire[0],-dire[1]]];
1361            }else if(y < 0){
1362                return [[0,x,B_N+y],[dire[0],dire[1]]];
1363            }
1364        }else if(s == 3){
1365            if(y >= B_N){
1366                return [[1,B_N*2-m-y,x],[-dire[1],dire[0]]];
1367            }else if(y < 0){
1368                return [[0,-m-y,x],[-dire[1],dire[0]]];
1369            }
1370        }else if(s == 4){
1371            if(y >= B_N){
1372                return [[1,x,y-B_N],[dire[0],dire[1]]];
1373            }else if(y < 0){
1374                return [[0,B_N-m-x,-m-y],[-dire[0],-dire[1]]];
1375            }
1376        }else if(s == 5){
1377            if(y >= B_N){
1378                return [[1,y-B_N,B_N-m-x],[dire[1],-dire[0]]];
1379            }else if(y < 0){
1380                return [[0,y+B_N,B_N-m-x],[dire[1],-dire[0]]];
1381            }
1382        }
1383    }
1384    return [pointer,dire];
1385}
1386function detectChessSquareClick(mX,mY){
1387  var hitSomething = false;
1388  for(var s = 0; s < 6; s++){
1389    var matrix = [diff(vb[s][0][0][1],vb[s][0][0][0]),diff(vb[s][1][0][1],vb[s][0][0][0])];
1390    var inverse = invert2x2(matrix);
1391    if(inverse == -1){
1392      //back side face
1393    }else{
1394      var boardX = getClickedOn(s,inverse,mX,mY,0);
1395      var boardY = getClickedOn(s,inverse,mX,mY,1);
1396      if(boardX >= 0 && boardY >= 0){
1397        setSelection(s,boardX,boardY);
1398        hitSomething = true;
1399      }
1400    }
1401  }
1402  if(pawnPromotionInfo[0]){ // a pawn is being promoted, so don't clear the selection just yet.
1403  }else if(!hitSomething){
1404    validDest = createValidDest(B_N);
1405    clearSelection();
1406  }
1407}
1408function spinCamera(){
1409    if(CAMERA_SPIN_SLOW[0]){
1410        var diff = Date.now()-lastCameraTime;
1411        rotY += diff*0.000034;
1412        lastCameraTime = Date.now();
1413    }
1414}
1415
1416var TAKE_TURNS = true;
1417var GO_OVER_EDGES = false;
1418var ENEMY_COLOR_MUD = false;
1419var BOARD_RUBIK = true; // 0 = rubiks cube, 1 = chessboard
1420var WEIRD_PIECES = true;
1421var SHOW_CHECK = [true,1650,850,240,70,"Show check warnings?"];
1422var CAMERA_SPIN_SLOW = [false,1650,760,240,70,"Spin camera slowly?"];
1423var toggles = [SHOW_CHECK,CAMERA_SPIN_SLOW];
1424var history_UI = [[1390,960,500,90],[0,0,70,70],[430,0,70,70],[90,50,320,20,20],[250,30]];
1425var PP_UI = [[1650,380,240,350],[20,50,200,40],[120,35]];
1426
1427var OPTION_NAMES = ["Take turns?","Go over edges?","Enemy color mud?","Rubik's colors?","Weird pieces?"];
1428
1429var TRANSITION_TIME = 1200; // milliseconds
1430var PIECE_SCORES = [1,3,3,5,9,20,4,3,6,10];
1431var PIECE_NAMES = ["pawn","knight","bishop","rook","queen","king","hand","splatoon","nootnoot","amazon","point"];
1432var PAWN_PROMOTION_OPTIONS = [0,1,2,3,6,7,8];
1433var pawnPromotionInfo = [false,0,0];
1434var PAWN_TYPE = 0;
1435var KING_TYPE = 5;
1436var SPLATOON_TYPE = 7;
1437
1438var turn = 0;
1439var W_W = 1920;
1440var W_H = 1080;
1441var B_N = 7;
1442var PIECE_TYPE_COUNT = 10;
1443var NORMAL_TYPE_COUNT = 7;
1444var PLAYER_COLORS = [
1445[[255,255,255],[0,0,0],[255,235,241]],
1446[[0,0,0],[255,255,255],[90,90,90]],
1447[[0,40,255],[0,0,0],[0,40,255]],
1448[[255,140,0],[0,0,0],[255,140,0]],
1449[[0,180,0],[0,0,0],[0,180,0]],
1450[[255,0,0],[0,0,0],[255,0,0]]];
1451var INSIDE_COLOR = [160,136,100];
1452var PLAYER_NAMES = ["White","Black","Blue","Orange","Green","Red"];
1453var SELECTION_COLOR = [128,255,128];
1454var CHECK_COLOR = [255,0,0];
1455var BG_COLOR = [100,200,255];
1456var VALID_COLOR = [[220,170,0],[255,255,0]];
1457var board;
1458var cube;
1459var checkCube;
1460var checks;
1461var rotGrid;
1462var validDest;
1463var vb;
1464
1465var pieceModels = new Array();
1466var playerTurn = 0;
1467var selection = [-1,-1,-1];
1468
1469var rotX = 0;
1470var rotY = 0;
1471var prev_mouseX = 0;
1472var prev_mouseY = 0;
1473var moved = false;
1474var pressed = false;
1475var lastMoveLocation = [[-1,-1,-1],[-1,-1,-1],-1];
1476var lastMoveTime = 0;
1477var lastMovePiece = 0;
1478var lastMoveType = 0;
1479var travelPath = [];
1480var scores = [];
1481var winner = -1;
1482var lastCameraTime = -1;
1483var ui;
1484var game_history = [];
1485var history_index = 0;
1486var alert;
1487for(var p = 0; p < PIECE_TYPE_COUNT; p++){
1488  let m;
1489  pieceModels[p] = m;
1490}
1491let c;
1492
1493function preload(){
1494   console.log("eee!"); 
1495  for(var p = 0; p < PIECE_TYPE_COUNT; p++){
1496    pieceModels[p] = loadModel("models/piece"+p+".obj");
1497  }
1498  console.log("fff!");
1499}
1500function setup() {
1501  console.log("STARTING!");
1502  createCanvas(W_W, W_H, WEBGL);
1503  console.log("1!");
1504  c = createGraphics(W_W, W_H, WEBGL);
1505  console.log("b!");
1506  addScreenPositionFunction(c);
1507  console.log("c!");
1508  sandboxGameButton();
1509  console.log("d!");
1510  ui = createGraphics(260,900);
1511  alert = createGraphics(260,260);
1512  console.log("e!");
1513  for(var t = 0; t < toggles.length; t++){
1514      var tog = toggles[t];
1515      tog.push(createGraphics(tog[3],tog[4]));
1516  }
1517  console.log("f!");
1518  history_UI.push(createGraphics(history_UI[0][2],history_UI[0][3]));
1519  PP_UI.push(createGraphics(PP_UI[0][2],PP_UI[0][3]));
1520  console.log("g!");
1521};
1522
1523function draw() {
1524  spinCamera();
1525  c.push();
1526  setWorldDrawings(c);
1527  var sca = 690/(B_N+2);
1528  drawBoard(c, board, sca);
1529  c.pop();
1530
1531  background(255,255,255);
1532  image(c,-W_W/2,-W_H/2);
1533  //drawWireframe();
1534  drawUI();
1535}
1536function mousePressed(){
1537  var clickedTog = false;
1538  for(var t = 0; t < toggles.length; t++){
1539    var tog = toggles[t];
1540    if(mouseX >= tog[1] && mouseX < tog[1]+tog[3] && mouseY >= tog[2] && mouseY < tog[2]+tog[4]){
1541      tog[0] = !tog[0];
1542      clickedTog = true;
1543      lastCameraTime = Date.now();
1544    }
1545  }
1546  for(var a = 0; a < 2; a++){
1547      var a1 = history_UI[0];
1548      var a2 = history_UI[a+1];
1549      var ax = a1[0]+a2[0];
1550      var ay = a1[1]+a2[1];
1551      var aw = a2[2];
1552      var ah = a2[3];
1553      if(mouseX >= ax && mouseX < ax+aw && mouseY >= ay && mouseY < ay+ah){
1554          if((a == 0 && history_index >= 1) || (a == 1 && history_index < game_history.length-1)){
1555            history_index += -1+2*a;
1556            loadFromGameHistory();
1557            clickedTog = true;
1558          }
1559      }
1560  }
1561  if(pawnPromotionInfo[0]){
1562      var p = PP_UI[0];
1563      if(mouseX >= p[0] && mouseX < p[0]+p[2] && mouseY >= p[1] && mouseY < p[1]+p[3]){
1564          var px = PP_UI[0][0]+PP_UI[1][0];
1565          var py = PP_UI[0][1]+PP_UI[1][1];
1566          var pw = PP_UI[1][2];
1567          var ph = PP_UI[1][3];
1568          var pth = ph*PAWN_PROMOTION_OPTIONS.length;
1569          if(mouseX >= px && mouseX < px+pw && mouseY >= py && mouseY < py+pth){
1570              var choice = Math.floor((mouseY-py)/ph);
1571              if(WEIRD_PIECES || PAWN_PROMOTION_OPTIONS[choice] < NORMAL_TYPE_COUNT){
1572                  var src = pawnPromotionInfo[1];
1573                  var player = getPlayerOf(deepIndex(board,src));
1574                  var t = 1+PAWN_PROMOTION_OPTIONS[choice]+player*PIECE_TYPE_COUNT;
1575                  doMove(pawnPromotionInfo[1],pawnPromotionInfo[2],t);
1576                  clearSelection();
1577                  clickedTog = true;
1578              }
1579          }
1580      }else{
1581          clearSelection();
1582      }
1583  }
1584  if(!clickedTog && mouseX >= 0 && mouseX < W_W && mouseY >= 0 && mouseY < W_H){
1585      pressed = true;
1586      moved = false;
1587      prev_mouseX = mouseX;
1588      prev_mouseY = mouseY;
1589  }
1590}
1591function deepCopy(arr){
1592    var result = [arr.length];
1593    for(var i = 0; i < arr.length; i++){
1594        result[i] = [arr[i].length];
1595        for(var j = 0; j < arr[i].length; j++){
1596            result[i][j] = arr[i][j].slice();
1597        }
1598    }
1599    return result;
1600}
1601function keyPressed(){
1602  if (keyCode == SHIFT) {
1603  }
1604}
1605function mouseReleased(){
1606    if(!moved){
1607        var mY = mouseY-W_H/2;
1608        var mX = mouseX-W_W/2;
1609        detectChessSquareClick(mX,mY);
1610    }
1611    pressed = false;
1612}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.