int DEADNODE = 0;
int LIVENODE = 1;
int STARTNODE = 2;
int ENDNODE = 3;
int SOLVENODE = 4;
class Puzzle{
int COLS;// = 7;
int ROWS;// = 7;
float scaleBase = 100;
float xgapMUL = .6;
float ygapMUL = .5;
float nodesizeMUL = .2;
float xgap = xgapMUL*scaleBase;
float ygap = ygapMUL*scaleBase;
float nodesize = nodesizeMUL*scaleBase;
int startingNodeName = -1;
int endingNodeName = -1;
int hintCount = 0;
ArrayList links = new ArrayList();
HashMap allLinkMap = new HashMap();
HashMap fixedLinks = new HashMap();
HashMap solveLinks = new HashMap();
int[][] nodes;
int[][] fixed;
int[][] solve;
float centerX = 250;
float centerY = 250;
int nodeTag;//for kids only really
Link findSolveLinkForNode(int nodeName){
for(String linkName : solveLinks.keySet()){
String[]parts = linkName.split("_");
int partA = parseInt(parts[0]);
int partB = parseInt(parts[1]);
if(nodeName == partA || nodeName == partB) return solveLinks.get(linkName);
}
return null;
}
Link findOtherSolveLink(int nodeName, Link oldLink){
for(String linkName : solveLinks.keySet()){
String[]parts = linkName.split("_");
int partA = parseInt(parts[0]);
int partB = parseInt(parts[1]);
if(nodeName == partA || nodeName == partB){
if(solveLinks.get(linkName)!= oldLink){
return solveLinks.get(linkName);
}
}
}
return null;
}
Puzzle(String s){
readPuzzle(s);
store(fixed,fixedLinks);
store(solve,solveLinks);
addLinks();
}
float zooming = 0;
float startZoomX;
float startZoomY;
float endZoomX;
float endZoomY;
float startZoom;
float endZoom;
void draw(){
if(zooming > 0){
zooming-=.075;
scaleBase = lerp(endZoom,startZoom,zooming);
centerX = lerp(endZoomX,startZoomX, zooming);
centerY = lerp(endZoomY,startZoomY ,zooming);
} else zooming = 0;
recalcScale();
drawLinks();
drawNodes();
animateTrain();
if(this == currentPuzzle && hintCount != 0){
textAlign(CENTER);
fill(0);
text("HINTS USED: "+hintCount,250,15);
}
}
void startZoom(float sx,float sy,float ex,float ey,float sz,float ez){
startZoomX = sx;
startZoomY = sy;
endZoomX = ex;
endZoomY = ey;
startZoom = sz;
endZoom = ez;
zooming = 1;
}
void readPuzzle(String s){
String parts[] = s.split(" ");
int ptr = 0;
ROWS = parseInt(parts[ptr++]);
COLS = parseInt(parts[ptr++]);
int numNodes = parseInt(parts[ptr++]);
// println(numNodes);
nodes = new int[ROWS][];
for(int y = 0; y < ROWS; y++){
nodes[y] = new int[COLS];
//print("ROW "+y+":");
for(int x = 0; x < COLS; x++){
nodes[y][x] = parseInt(parts[ptr++]);
if(nodes[y][x] == STARTNODE){
startingNodeName = getNodeName(x,y);
}
if(nodes[y][x] == ENDNODE){
endingNodeName = getNodeName(x,y);
}
// print(nodes[y][x] +" ");
}
//println();
}
int numLinkNodes = parseInt(parts[ptr++]);
fixed = new int[numLinkNodes/2][];
for(int i = 0; i < numLinkNodes / 2; i++){
fixed[i] = new int[2];
fixed[i][0] = parseInt(parts[ptr++]);
fixed[i][1] = parseInt(parts[ptr++]);
}
int numSolveNodes = parseInt(parts[ptr++]);
solve = new int[numSolveNodes/2][];
for(int i = 0; i < numSolveNodes / 2; i++){
solve[i] = new int[2];
solve[i][0] = parseInt(parts[ptr++]);
solve[i][1] = parseInt(parts[ptr++]);
}
}
void drawNodes(){
for(int y = 0; y < ROWS; y++){
noStroke();
for(int x = 0; x < COLS; x++){
int thisNode = nodes[y][x];
if(thisNode != 0){
if(thisNode > 1) fill(64,64,200);
else fill(255);
ellipse(getScreenX(x,y),getScreenY(x,y) ,nodesize,nodesize);
}
}
}
}
void printNodes(){
for(int y = 0; y < ROWS; y++){
if(y % 2 == 1) print(" ");
for(int x = 0; x < COLS; x++){
print(nodes[y][x] + " ");
}
println("");
}
}
float getScreenX(float x, float y){
float loc = centerX - ((xgap * COLS)/2);
if(y % 2 == 1) loc += xgap / 2;
loc += x*xgap;
if(COLS == 5 || COLS == 9) loc += xgap/2; //HACK
return loc;
}
float getScreenY(float x, float y){
float loc = centerY - ((ygap * ROWS)/2);
loc += y * ygap +(ygap/2);
return loc;
}
void addLinks(){
stroke(128);strokeWeight(2);
//look for side to side possibles
for(int y = 0; y < ROWS; y++){ for(int x = 0; x < COLS; x++){
if(nodes[y][x] != 0){
//side to side
int ex = x + 1;
int ey = y;
tryToAddLink(x,y,ex,ey);
//down slant
ey = y+1;
ex = x;
if(y % 2 == 1)ex++;
tryToAddLink(x,y,ex,ey);
//upslant
ex = x-1;
if(y % 2 == 1) ex++;
tryToAddLink(x,y,ex,ey);
}
}
}
}
boolean areLiveNodes(int x,int y,int ex,int ey){
int startNodeContent =nodes[y][x];
int endNodeContent =nodes[ey][ex];
if(startNodeContent != DEADNODE && endNodeContent != DEADNODE) return true;
return false;
}
void tryToAddLink(int x, int y, int ex, int ey){
int n1 = getNodeName(x,y);
int n2 = getNodeName(ex,ey);
if(!( n1 < n2)){
println("ERROR IN TRYTOADDLINK, NO *YOURE* OUT OF ORDER::"+n1+" "+n2);
}
String name = n1+"_"+n2;
if(allLinkMap.get(name) != null){
// println(name + " is already a node");
return;
}
if(ex>=0 && ey>=0){
if( ex < COLS && ey < ROWS ){
if(areLiveNodes(x,y,ex,ey)){
Link newLink = new Link(x,y,ex,ey);
newLink.checkIfFixed(fixedLinks);
links.add(newLink);
allLinkMap.put(newLink.name,newLink);
}
}
}
// else print("sm ");
}
void drawLinks(){
for(Link k : links){
k.draw(this == currentPuzzle);
}
}
void checkClick(){
boolean needToCheckWin = false;
for(Link k : links){
if(k.checkClick(this == currentPuzzle)) needToCheckWin = true;
}
if(needToCheckWin) {
if(checkSolve()){
startTrain(startingNodeName);
puzzleWon();
}}
}
Link trainLink = null;
int trainStartNode = -1;
int trainEndNode = -1;
float moveVal;
boolean trainMoving = false;
int trainReverseNodeName = -1;
void puzzleWon(){
// println("WOOT");
fx_whistle();
if(seenArrowHint == false){
seenArrowHint = true;
showArrowHint = true;
}
//mark this one one as solved in the parent puzzle (needed for unlocking paths)
if(currentPuzzle != parentPuzzle){
int nodeNameAsKid = currentPuzzle.nodeTag;
parentPuzzle.nodes[getNodeYFromName(nodeNameAsKid)][getNodeXFromName(nodeNameAsKid)] = SOLVENODE;
parentPuzzle.printNodes();
parentPuzzle.addLinks(); //redo and add any links we can do because it's all unlocked and stuff...
} else {
startMusic();
wonIt = true;
}
}
void startTrain(int goalPointNode){
trainLink = findSolveLinkForNode(goalPointNode);
moveVal = 0;
trainMoving = true;
trainStartNode = goalPointNode;
trainEndNode = trainLink.getOtherNode(trainStartNode);
trainReverseNodeName = findOtherGoalPoint(goalPointNode);
}
int findOtherGoalPoint(int goalPointNode){
if(goalPointNode == endingNodeName) return startingNodeName;
return endingNodeName;
}
void animateTrain(){
if(trainMoving){
moveVal += .08;
if(moveVal >= 1){
moveVal = 0;
if(trainEndNode != trainReverseNodeName){
trainLink = findOtherSolveLink(trainEndNode,trainLink);
trainStartNode = trainEndNode;
trainEndNode = trainLink.getOtherNode(trainStartNode);
} else {
trainMoving = false;
}
}
if(!trainMoving){
startTrain(trainReverseNodeName);
}
float trainX = lerp(
getScreenX(getNodeXFromName(trainStartNode),getNodeYFromName(trainStartNode)),
getScreenX(getNodeXFromName(trainEndNode),getNodeYFromName(trainEndNode))
,moveVal);
float trainY = lerp(
getScreenY(getNodeXFromName(trainStartNode),getNodeYFromName(trainStartNode)),
getScreenY(getNodeXFromName(trainEndNode),getNodeYFromName(trainEndNode))
,moveVal);
if(oldTrainX == null){
oldTrainX = new float[TRAINHISTORY];
oldTrainY = new float[TRAINHISTORY];
for(int i = 0; i < TRAINHISTORY; i++){
oldTrainX[i] = trainX;
oldTrainY[i] = trainY;
}
} else {
for(int i = TRAINHISTORY - 2; i >= 0; i--){
oldTrainX[i+1] = oldTrainX[i];
oldTrainY[i+1] = oldTrainY[i];
}
oldTrainX[0] = trainX;
oldTrainY[0] = trainY;
}
fill(210,175,0);
ellipse(oldTrainX[6],oldTrainY[6],scaleBase*.3,scaleBase*.3);
fill(230,195,0);
ellipse(oldTrainX[3],oldTrainY[3],scaleBase*.3,scaleBase*.3);
fill(255,215,0);
ellipse(oldTrainX[0],oldTrainY[0],scaleBase*.3,scaleBase*.3);
}
}
float oldTrainX[];
float oldTrainY[];
int TRAINHISTORY = 10;
void showHint(){
Link badLink = null;
//look for something they clicked that they shouldn't have done
for(Link k : links){
if(k.mode == PICKED){
if(solveLinks.get(k.name) == null){
badLink = k;
}
}
}
if(badLink != null){
badLink.xOut();
return;
}
//ok, all is good, lets give them something they need to click..
for(String s : solveLinks.keySet()){
Link k = solveLinks.get(s);
Link testLink = allLinkMap.get(k.name);
if(testLink.mode != PICKED && fixedLinks.get(k.name) == null){
badLink = testLink;
}
}
if(badLink != null){
badLink.oUp();
return;
}
}
boolean checkSolve(){
ArrayList pickedLink = new ArrayList();
//HashMap solveLinksCopy = (HashMap)solveLinks.clone();
boolean isGoodSoFar = true;
for(Link k : links){
if(k.mode == PICKED || k.mode == FIXED){
pickedLink.add(k);
if(solveLinks.get(k.name) == null) isGoodSoFar = false;
}
}
if(isGoodSoFar && pickedLink.size() == solveLinks.size()){
return true;
}
return false;
}
void store(int[][] nodepairs,HashMap mapLinks){
for(int[] pair : nodepairs){
Link fixedLink = new Link(pair[0],pair[1]);
mapLinks.put(fixedLink.name,fixedLink);
}
}
int getNodeName(int x, int y){
return x + (y * COLS);
}
int getNodeXFromName(int n){
return n % COLS;
}
int getNodeYFromName(int n){
return n / COLS;
}
int POSSIBLE = 1;
int FIXED = 2;
int SOLVE = 3;
int PICKED = 4;
class Link {
int mode = POSSIBLE;
String name;
int x,y,ex,ey;
float hoverRadius;
Link(int px,int py, int pex, int pey){
x = px; y = py;
ex = pex; ey = pey;
int n1 = getNodeName(x,y);
int n2 = getNodeName(ex,ey);
name = n1+"_"+n2;
calcCenter();
}
void checkIfFixed(HashMap fixedLinks){
if(fixedLinks.get(name) != null){
mode = FIXED;
}
}
int getOtherNode(int firstNode){
String[]parts = name.split("_");
int partA = parseInt(parts[0]);
int partB = parseInt(parts[1]);
if(firstNode == partA) return partB;
return partA;
}
Link(int pn1,int pn2){
this(getNodeXFromName(pn1),getNodeYFromName(pn1),
getNodeXFromName(pn2),getNodeYFromName(pn2)
) ;
}
boolean matches(Link other){
return name.equals(other.name);
}
void xOut(){
cx = (getScreenX(x,y) + getScreenX(ex,ey)) / 2;
cy = (getScreenY(x,y) + getScreenY(ex,ey)) / 2;
strokeWeight(6);
stroke(200,30,30);
line(cx-15,cy-15,cx+15,cy+15);
line(cx+15,cy-15,cx-15,cy+15);
}
void oUp(){
cx = (getScreenX(x,y) + getScreenX(ex,ey)) / 2;
cy = (getScreenY(x,y) + getScreenY(ex,ey)) / 2;
strokeWeight(6);
stroke(200,30,30);
noFill();
ellipse(cx,cy,30,30);
}
void draw(boolean isCurrent){
calcCenter();
strokeWeight(2);
stroke(200);
if(mode == FIXED) stroke(0);
float p1x = getScreenX(x,y);
float p1y = getScreenY(x,y);
float p2x = getScreenX(ex,ey);
float p2y = getScreenY(ex,ey);
if(! isMouseOver(isCurrent) && mode == POSSIBLE) {
strokeWeight(2);
line(p1x,p1y,p2x,p2y);
} else {
float alpha = 255;
if(isMouseOver(isCurrent) && mode == POSSIBLE){
alpha = 100;
doHand = true;
}
pushMatrix();
float a = atan2(p2y-p1y,p2x-p1x);
translate(p1x,p1y);
rotate(a);
float full = scaleBase*.6;
if(mode == FIXED) strokeWeight(4);
else strokeWeight(2);
stroke(120,120,0,alpha);
int parts = 7;
for(int i = 1; i < parts; i++){
float r = i* full / parts;
line(r,-full/14,r,full/14);
}
if(mode == FIXED) strokeWeight(3);
else strokeWeight(1);
stroke(50,alpha);
line(0,-full/20,full,-full/20);
line(0,full/20,full,full/20);
popMatrix();
}
}
float cx, cy;
void calcCenter(){
cx = (getScreenX(x,y) + getScreenX(ex,ey)) / 2;
cy = (getScreenY(x,y) + getScreenY(ex,ey)) / 2;
hoverRadius = (xgap + ygap) / 8;
}
void drawCenter(){
strokeWeight(1);
stroke(0);
noFill();
ellipse(cx,cy,hoverRadius*2,hoverRadius*2);
}
boolean checkClick(boolean isCurrent){
if(trainMoving) return false; //they solved, don't let them change a thing...
if(isMouseOver(isCurrent)){
if(mode == POSSIBLE){
fx_scratch();
mode = PICKED;
return true;
} else {
if(mode == PICKED){
mode = POSSIBLE;
return true;
}
}
}
return false;
}
boolean isMouseOver(boolean isCurrent){
// println("IZZY "+trainMoving);
if(! isCurrent) return false;
if(trainMoving) return false;
return (dist(mouseX,mouseY,cx,cy) < hoverRadius);
}
}
void recalcScale(){
xgap = xgapMUL * scaleBase;
ygap = ygapMUL * scaleBase;
nodesize = nodesizeMUL * scaleBase;
}
}//end puzzle class