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