xxxxxxxxxx
399
let tubeWidth = 50;
let tubeBase = 12;
let tubeHeight = 200 - tubeBase;
let tubeMargin = 20;
let playMargin = 20;
let playWidth = 500;
var bottomRow;
var topRow;
let btnRestart;
var colorMap = [];
var level = 1;
var state = 0;
var stateTubes = [];
var tubes = stateTubes.length;
var moves = 0;
var bottomTubes;
var topTubes;
var currentTube = -1;
var currentBlocks;
var a = 0;
var aCount = 0;
function setup() {
createCanvas(600, 600);
ellipseMode(CORNER);
colorMap = [
"#a6cee3",
"#1f78b4",
"#b2df8a",
"#33a02c",
"#fb9a99",
"#e31a1c",
"#fdbf6f",
"#ff7f00",
"#cab2d6",
"#6a3d9a",
"#ffff99",
"#b15928",
];
bottomRow = playMargin + playWidth - 2 * tubeBase;
topRow = int(playWidth / 2) + playMargin;
randomizeColors();
loadLevel(1);
}
function draw() {
if (aCount > 0) { a = min(1, 1.0*(frameCount - aCount)/15); }
if (state == 1 && a < 1) {
fill(30);
rect(20, 20, width - 100, width - 100);
drawBases();
drawBlocks();
}
}
function loadLevel(newLevel) {
level = newLevel;
state = 0;
moves = 0;
stateTubes = levels(newLevel);
tubes = stateTubes.length;
bottomTubes = int(tubes / 2);
topTubes = tubes - bottomTubes;
currentTube = -1;
currentBlocks = "";
basicDraw();
}
function randomizeColors() {
shuffle(colorMap, true);
}
function basicDraw() {
background(0);
strokeWeight(0);
fill(30);
rect(20, 20, width - 100, width - 100);
drawPanel();
drawBases();
drawBlocks();
if (state == 2) {
drawBanner();
}
}
function drawPanel() {
fill("#feb24c");
textSize(24);
text("Level " + str(level), 20, 570);
btnRestart = createButton('Restart level');
btnRestart.position(450,550);
btnRestart.mouseClicked(restart);
text("Best: " + goal(level) + ", Moves: " + str(moves), 150,570);
}
function restart(event) {
loadLevel(level);
}
function drawBases() {
stroke(189);
fill(255);
strokeWeight(1);
let leftStart =
playMargin +
int((playWidth - tubeWidth * topTubes - tubeMargin * (topTubes - 1)) / 2);
for (let i = 0; i < topTubes; i++) {
ellipse(
leftStart + i * (tubeWidth + tubeMargin),
topRow - tubeBase,
tubeWidth,
tubeBase
);
}
leftStart =
playMargin +
int(
(playWidth - tubeWidth * bottomTubes - tubeMargin * (bottomTubes - 1)) / 2
);
for (let i = 0; i < bottomTubes; i++) {
ellipse(
leftStart + i * (tubeWidth + tubeMargin),
bottomRow - tubeBase,
tubeWidth,
tubeBase
);
}
}
function drawBlocks() {
let blockSize = 40;
let blockMargin = 4;
var t;
var offset;
var r;
for (let p = 0; p < stateTubes.length; p++) {
let tube = stateTubes[p];
if (p < topTubes) {
t = topTubes;
r = topRow;
offset = 0;
} else {
t = bottomTubes;
r = bottomRow;
offset = topTubes;
}
let leftStart =
playMargin +
int(playWidth - tubeWidth * t - tubeMargin * (t - 1)) / 2 +
int((tubeWidth - blockSize) / 2);
for (let i = 0; i < tube.length; i++) {
let c = tube.charAt(i);
fill(getColor(c));
rect(
leftStart + (p - offset) * (tubeWidth + tubeMargin),
r - (tube.length - i - 1) * (blockSize + blockMargin) - blockSize - int(tubeBase / 2),
blockSize,
blockSize
);
}
}
if (state == 1) {
let j = currentTube;
if (j < topTubes) {
t = topTubes;
r = topRow;
offset = 0;
} else {
t = bottomTubes;
r = bottomRow;
offset = topTubes;
}
for (let i = 0; i < currentBlocks.length; i++) {
let block = currentBlocks.charAt(i);
let leftStart =
playMargin +
int(
(playWidth - tubeWidth * t - tubeMargin * (t - 1)) / 2
) +
int((tubeWidth - blockSize) / 2);
fill(getColor(block));
rect(
leftStart + (j-offset) * (tubeWidth + tubeMargin),
r + i * (blockSize + blockMargin) - 200*a + int(1.0*(1-a)*(-int(tubeBase/2) +blockMargin - (currentBlocks.length + stateTubes[j].length)*(blockSize + blockMargin))),
blockSize,
blockSize
);
}
}
}
function drawBanner() {
fill("#feb24c");
rect(120, 170, 300, 200);
fill(0);
textSize(40);
text("Congrats!", 270 - 100, 270);
return;
}
function getColor(i) {
return colorMap[unchar(i) - 65];
}
function mouseClicked() {
if (state == 2) {
randomizeColors();
loadLevel(level + 1);
return;
}
let tube = getTube(mouseX, mouseY);
if (tube == -1) {
return;
} else if (state == 0 && stateTubes[tube].length > 0) {
state = 1;
currentTube = tube;
pickup(tube);
aCount = frameCount;
} else if (state == 1) {
if (tube == currentTube) {
state = 0;
stateTubes[currentTube] = currentBlocks + stateTubes[currentTube];
currentTube = -1;
currentBlocks = "";
aCount = 0;
} else {
let move = [currentTube, tube];
if (canMove(move)) {
oneMove(move);
moves += 1;
aCount = 0;
if (win()) {
state = 2;
} else {
state = 0;
currentTube = -1;
aCount = 0;
}
} else {
stateTubes[currentTube] = currentBlocks + stateTubes[currentTube];
currentTube = tube;
pickup(tube);
aCount = frameCount;
}
}
}
basicDraw();
}
function win() {
for (let i = 0; i < stateTubes.length; i++) {
let tube = stateTubes[i];
if (
tube.length == 0 ||
(tube.length == 4 &&
tube.charAt(0) == tube.charAt(1) &&
tube.charAt(1) == tube.charAt(2) &&
tube.charAt(2) == tube.charAt(3))
) {
continue;
} else {
return false;
}
}
return true;
}
function pickup(tube) {
currentBlocks = str(stateTubes[tube].charAt(0));
stateTubes[tube] = stateTubes[tube].substring(1, stateTubes[tube].length);
let l = stateTubes[tube].length + 1;
for (let i = 1; i < l; i++) {
if (stateTubes[tube].charAt(0) == currentBlocks.charAt(0)) {
currentBlocks += currentBlocks.charAt(0);
stateTubes[tube] = stateTubes[tube].substring(1, stateTubes[tube].length);
} else {
return;
}
}
}
function getTube(x, y) {
if (topRow - 200 <= y && y <= topRow) {
let leftStart =
playMargin +
int(
(playWidth - tubeWidth * topTubes - tubeMargin * (topTubes - 1)) / 2
) -
int(tubeMargin / 2);
let leftEnd = playWidth - (leftStart - playMargin) + playMargin;
if (x < leftStart || leftEnd < x) {
return -1;
}
return int((x - leftStart) / (tubeWidth + tubeMargin));
} else if (bottomRow - 200 <= y && y <= bottomRow) {
let tube = topTubes;
let leftStart =
playMargin +
int(
(playWidth - tubeWidth * bottomTubes - tubeMargin * (bottomTubes - 1)) /
2
) -
int(tubeMargin / 2);
let leftEnd = playWidth - (leftStart - playMargin) + playMargin;
if (x < leftStart || leftEnd < x) {
return -1;
}
return tube + int((x - leftStart) / (tubeWidth + tubeMargin));
} else {
return -1;
}
}
function canMove(x) {
let a = currentBlocks;
let b = stateTubes[x[1]];
if (x[0] == x[1]) return false;
if (b.length == 4) return false;
if (a.length == 0) return false;
if (b.length == 0) return true;
return a.charAt(0) == b.charAt(0);
}
function oneMove(x) {
let a = stateTubes[x[0]];
let b = stateTubes[x[1]];
let move = min(4 - b.length, currentBlocks.length);
stateTubes[x[1]] = subset(currentBlocks, 0, move) + b;
stateTubes[x[0]] =
subset(currentBlocks, move, currentBlocks.length - move) + a;
currentBlocks = "";
return b.length <= 4;
}
function levels(level) {
if (level == 1) { return ["ABBB", "CAAA", "", "BCCC", ""]; } // [3, 4,1]
if (level == 2) { return ["AAAB", "BBCD", "DDDB", "ACCC", "", ""]; } // [4, 6,1]
if (level == 3) { return ["ABCC", "ABAB", "", "CCBA", ""]; } // [3, 8,1]
if (level == 4) { return ["ABBC", "DDCD", "AABA", "BDCC", "", ""]; } // [4, 11,3]
if (level == 5) { return ["ABAC", "BCAB", "", "CBAC", ""]; } // [3, 11,2]
if (level == 6) { return ["ABCD", "CCAA", "EBDE", "BEAD", "BDEC", "", ""]; } // [5, 14,1]
if (level == 7) { return ["ABCD", "ACBD", "ACBA", "BDCD", "", ""]; } // [4, 15,3]
if (level == 8) { return ["ABBA", "ABCC", "DECC", "FEBD", "AFDF", "FEED", "", ""]; } // [6, 18,4]
if (level == 9) { return ["ABCA", "DEBD", "DBEA", "DCEA", "BCEC", "", ""]; } // [5, 18,3]
if (level == 10) { return ["ABBC", "DEEE", "ADFD", "FCGE", "BCCD", "HGAG", "HHHB", "FFAG", "", ""]; } // [8, 20,3]
if (level == 11) { return ["ABCA", "DAEC", "DFEG", "CCGF", "DBGA", "DFBB", "FEGE", "", ""]; } // [7, 20,1]
if (level == 12) { return ["ABAC", "CBDE", "BACE", "AFDE", "FDFE", "BDFC", "", ""]; } // [6, 22,4]
if (level == 13) { return ["ABCD", "CEBD", "FABD", "GGHC", "HGFF", "AAGE", "BEEF", "HCHD", "", ""]; } // [8, 24,4]
if (level == 14) { return ["ABCD", "AEDC", "FBEG", "FDBG", "GCED", "AEBC", "GFAF", "", ""]; } // [7, 25,4]
if (level == 15) { return ["ABCD", "EFEA", "GCHE", "HBBF", "IEGG", "DIHB", "HAFC", "GIDA", "FDIC", "", ""]; } // [9, 27,2]
if (level == 16) { return ["ABCD", "BEFG", "CFAF", "AEHG", "BGEH", "DCDH", "GEFD", "BACH", "", ""]; } // [8, 28,4]
if (level == 17) { return ["ABCD", "EFGH", "DDIF", "GGHJ", "JKDJ", "AFFG", "AEHH", "LIKK", "BJAL", "CLLC", "KBEE", "BCII", "", ""]; } // [12, 30,2]
if (level == 18) { return ["ABBC", "DEFG", "EFAH", "EIIH", "IFCA", "BBDH", "HJEC", "DCFJ", "IGAJ", "JDGG", "", ""]; } // [10, 31,5]
if (level == 19) { return ["ABAC", "CDEA", "FDEG", "DBHG", "CBIF", "IBIH", "IEFG", "AHEC", "DGHF", "", ""]; } // [9, 32,5]
if (level == 20) { return ["ABCD", "EFGE", "HCIH", "JCKA", "HAIE", "KDFG", "DCKK", "BAJI", "GHBE", "FJDJ", "BGIF", "", ""]; } // [11, 34,2]
if (level == 21) { return ["ABAC", "DAEC", "CAFG", "BHBI", "DBEJ", "JDFC", "IHGJ", "GEDI", "FHFJ", "GEIH", "", ""]; } // [10, 36,6]
if (level == 22) { return ["ABCD", "EFGC", "GCHI", "FIFE", "JHKL", "CLAB", "LEGJ", "IDKK", "DHBK", "HDAF", "BILA", "JEGJ", "", ""]; } // [12, 37,2]
if (level == 23) { return ["ABCD", "BCEF", "GHIJ", "KIGE", "ACHJ", "DGFE", "KFHD", "KGBJ", "AIAI", "CHBJ", "KEDF", "", ""]; } // [11, 39,6]
if (level == 24) { return ["ABCD", "EFCG", "DHBA", "IBFD", "IEGJ", "KCKJ", "IKCG", "IGFJ", "LBEA", "HDLE", "FKLJ", "HLHA", "", ""]; } // [12, 43,7]
//if (level == 25)
return ["ABCD", "EFGG", "HICJ", "KFIK", "CECL", "JFGH", "JIFA", "BGBH", "KLEL", "BJLA", "AHED", "DIKD", "", ""]; // [12, 43,8]
}
function goal(level) {
if (level < 25) {
var x = [0,4,6,8,11,11,14,15,18,18,20,20,22,24,25,27,28,30,31,32,34,36,37,39,43,43];
return x[level];
}
else { return 43; }
}