// Copyright 2011 David Galles, University of San Francisco. All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, are // permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other materials // provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // The views and conclusions contained in the software and documentation are those of the // authors and should not be interpreted as representing official policies, either expressed // or implied, of the University of San Francisco function DPChange(am, w, h) { this.init(am, w, h); } DPChange.prototype = new Algorithm(); DPChange.prototype.constructor = DPChange; DPChange.superclass = Algorithm.prototype; DPChange.TABLE_ELEM_WIDTH = 30; DPChange.TABLE_ELEM_HEIGHT = 30; DPChange.TABLE_START_X = 500; DPChange.TABLE_START_Y = 50; DPChange.TABLE_DIFF_X = 70; DPChange.CODE_START_X = 10; DPChange.CODE_START_Y = 10; DPChange.CODE_LINE_HEIGHT = 14; DPChange.GREEDY_START_X = 100; DPChange.GREEDY_START_Y = 150; DPChange.RECURSIVE_START_X = 220; DPChange.RECURSIVE_START_Y = 10; DPChange.RECURSIVE_DELTA_Y = 14; DPChange.RECURSIVE_DELTA_X = 8; DPChange.CODE_HIGHLIGHT_COLOR = "#FF0000"; DPChange.CODE_STANDARD_COLOR = "#000000"; DPChange.TABLE_INDEX_COLOR = "#0000FF" DPChange.CODE_RECURSIVE_1_COLOR = "#339933"; DPChange.CODE_RECURSIVE_2_COLOR = "#0099FF"; DPChange.DPCode = [["def ","change(n, coinArray)",":"], [" if ","(n == 0) "], [" return 0"], [" best = -1"], [" for coin in coinArray:"], [" if ","(coin <= n)",":"], [" nextTry = ","change(n - coin, coinArray)"], [" if (", "best < 0", " or ", "best > nextTry + 1", ")"], [" best = nextTry + 1"], [" return best"]]; DPChange.GREEDYCode = [["def ","changeGreedy(n, coinArray)",":"], [" coinsRequired = 0"], [" for coin in reversed(coinArray): "], [" while ", "(n <= coin)"], [" n = n - coin"], [" coinsRequired = coinsRequired + 1"], [" return coinsRequired"]]; DPChange.COINS = [[1, 5, 10, 25], [1, 4, 6, 10]]; DPChange.MAX_VALUE = 30; DPChange.MESSAGE_ID = 0; DPChange.prototype.setCode = function(codeArray) { this.code = codeArray; this.codeID = Array(this.code.length); var i, j; for (i = 0; i < this.code.length; i++) { this.codeID[i] = new Array(this.code[i].length); for (j = 0; j < this.code[i].length; j++) { this.codeID[i][j] = this.nextIndex++; this.cmd("CreateLabel", this.codeID[i][j], this.code[i][j], DPChange.CODE_START_X, DPChange.CODE_START_Y + i * DPChange.CODE_LINE_HEIGHT, 0); this.cmd("SetForegroundColor", this.codeID[i][j], DPChange.CODE_STANDARD_COLOR); if (j > 0) { this.cmd("AlignRight", this.codeID[i][j], this.codeID[i][j-1]); } } } } DPChange.prototype.deleteCode = function() { var i,j for (i = 0; i < this.codeID.length; i++) { for (j = 0; j < this.codeID[i].length; j++) { this.cmd("Delete", this.codeID[i][j]); } } this.codeID = []; } DPChange.prototype.setCodeAlpha = function(codeArray, alpha) { var i, j for (i = 0; i < codeArray.length; i++) { var foo = 3; foo = codeArray[i]; for (j = 0; j < codeArray[i].length; j++) { this.cmd("SetAlpha", codeArray[i][j], alpha); } } } DPChange.prototype.init = function(am, w, h) { DPChange.superclass.init.call(this, am, w, h); this.nextIndex = 0; this.addControls(); // HACK!! this.setCode(DPChange.GREEDYCode); this.greedyCodeID = this.codeID; this.setCodeAlpha(this.greedyCodeID, 0); /// this.setCode(DPChange.DPCode); this.usingDPCode = true; this.animationManager.StartNewAnimation(this.commands); this.animationManager.skipForward(); this.animationManager.clearHistory(); this.initialIndex = this.nextIndex; this.oldIDs = []; this.commands = []; } DPChange.prototype.addControls = function() { this.controls = []; this.fibField = addControlToAlgorithmBar("Text", ""); this.fibField.onkeydown = this.returnSubmit(this.fibField, this.emptyCallback.bind(this), 2, true); this.controls.push(this.fibField); this.recursiveButton = addControlToAlgorithmBar("Button", "Change Recursive"); this.recursiveButton.onclick = this.recursiveCallback.bind(this); this.controls.push(this.recursiveButton); this.tableButton = addControlToAlgorithmBar("Button", "Change Table"); this.tableButton.onclick = this.tableCallback.bind(this); this.controls.push(this.tableButton); this.memoizedButton = addControlToAlgorithmBar("Button", "Change Memoized"); this.memoizedButton.onclick = this.memoizedCallback.bind(this); this.controls.push(this.memoizedButton); this.greedyButton = addControlToAlgorithmBar("Button", "Change Greedy"); this.greedyButton.onclick = this.greedyCallback.bind(this); this.controls.push(this.greedyButton); var coinLabels = []; var i, j; for (i = 0; i < DPChange.COINS.length; i++) { var nextLabel = "Coins: [" + DPChange.COINS[i][0]; for (j = 1; j < DPChange.COINS[i].length; j++) { nextLabel = nextLabel + ", " + DPChange.COINS[i][j]; } nextLabel = nextLabel + "]"; coinLabels.push(nextLabel); } this.coinTypeButtons = addRadioButtonGroupToAlgorithmBar(coinLabels, "CoinType"); for (i = 0; i < this.coinTypeButtons.length; i++) { this.coinTypeButtons[i].onclick = this.coinTypeChangedCallback.bind(this, i); this.controls.push(this.coinTypeButtons[i]); } this.coinTypeButtons[0].checked = true; this.coinIndex = 0; } DPChange.prototype.coinTypeChangedCallback = function(coinIndex) { this.implementAction(this.coinTypeChanged.bind(this), coinIndex); } DPChange.prototype.coinTypeChanged = function(coinIndex) { this.commands = []; this.coinIndex = coinIndex; this.coinTypeButtons[coinIndex].checked = true; this.clearOldIDs(); return this.commands; } DPChange.prototype.greedyCallback = function(value) { if (this.fibField.value != "") { this.implementAction(this.implementGreedy.bind(this),parseInt(this.fibField.value)); } else { this.implementAction(this.helpMessage.bind(this), ""); } } DPChange.prototype.implementGreedy = function(value) { this.commands = []; this.clearOldIDs(); var initialValue = value; if (this.usingDPCode) { this.setCodeAlpha(this.greedyCodeID, 1); this.setCodeAlpha(this.codeID, 0); this.usingDPCode = false; } var currX = DPChange.GREEDY_START_X; var currY = DPChange.GREEDY_START_Y + 2.5 * DPChange.TABLE_ELEM_HEIGHT; var messageID = this.nextIndex++; this.oldIDs.push(messageID); var valueRemainingID = this.nextIndex++; this.oldIDs.push(valueRemainingID); this.cmd("CreateRectangle", valueRemainingID, value, DPChange.TABLE_ELEM_WIDTH, DPChange.TABLE_ELEM_HEIGHT, DPChange.GREEDY_START_X, DPChange.GREEDY_START_Y); var tempLabel = this.nextIndex++; this.oldIDs.push(tempLabel); this.cmd("CreateLabel", tempLabel, "Amount remaining:",0, 0); this.cmd("AlignLeft", tempLabel, valueRemainingID); var requiredCoinsID = this.nextIndex++; this.oldIDs.push(requiredCoinsID); this.cmd("CreateRectangle", requiredCoinsID, value, DPChange.TABLE_ELEM_WIDTH, DPChange.TABLE_ELEM_HEIGHT, DPChange.GREEDY_START_X, DPChange.GREEDY_START_Y + DPChange.TABLE_ELEM_HEIGHT); tempLabel = this.nextIndex++; this.oldIDs.push(tempLabel); this.cmd("CreateLabel", tempLabel, "Required Coins:",0, 0); this.cmd("AlignLeft", tempLabel, requiredCoinsID); var requiredCoins = 0; var i; for (i = DPChange.COINS[this.coinIndex].length - 1; i >=0; i--) { while (value >= DPChange.COINS[this.coinIndex][i]) { requiredCoins = requiredCoins + 1; value = value - DPChange.COINS[this.coinIndex][i]; this.cmd("SetText", valueRemainingID, value); this.cmd("SetText", requiredCoinsID, requiredCoins); var moveIndex = this.nextIndex++; this.oldIDs.push(moveIndex); this.cmd("CreateLabel", moveIndex, DPChange.COINS[this.coinIndex][i], DPChange.GREEDY_START_X, DPChange.GREEDY_START_Y); this.cmd("Move", moveIndex, currX, currY); currX += DPChange.TABLE_ELEM_WIDTH; this.cmd("Step"); } } this.cmd("CreateLabel", messageID, "changeGreedy(" + String(initialValue)+ ", [" +String(DPChange.COINS[this.coinIndex]) +"]) = " + String(requiredCoins), DPChange.RECURSIVE_START_X, DPChange.RECURSIVE_START_Y, 0); return this.commands; } DPChange.prototype.buildTable = function(maxVal) { this.tableID = new Array(2); this.tableVals = new Array(2); this.tableXPos = new Array(2); this.tableYPos = new Array(2); var i; for (i = 0; i < 2; i++) { this.tableID[i] = new Array(maxVal + 1); this.tableVals[i] = new Array(maxVal + 1); this.tableXPos[i] = new Array(maxVal + 1); this.tableYPos[i] = new Array(maxVal + 1); } var j; var indexID; var xPos; var yPos; var table_rows = Math.floor((this.canvasHeight - DPChange.TABLE_ELEM_HEIGHT - DPChange.TABLE_START_Y) / DPChange.TABLE_ELEM_HEIGHT); var table_cols = Math.ceil((maxVal + 1) / table_rows); var header1ID = this.nextIndex++; this.oldIDs.push(header1ID); this.cmd("CreateLabel", header1ID, "# of Coins Required", DPChange.TABLE_START_X, DPChange.TABLE_START_Y - 30); var header2ID = this.nextIndex++; this.oldIDs.push(header2ID); this.cmd("CreateLabel", header2ID, "Coins to Use", DPChange.TABLE_START_X + table_cols*DPChange.TABLE_DIFF_X + 1.5*DPChange.TABLE_DIFF_X, DPChange.TABLE_START_Y - 30); for (i = 0; i <= maxVal; i++) { yPos = i % table_rows * DPChange.TABLE_ELEM_HEIGHT + DPChange.TABLE_START_Y; xPos = Math.floor(i / table_rows) * DPChange.TABLE_DIFF_X + DPChange.TABLE_START_X; for (j = 0; j < 2; j++) { this.tableID[j][i] = this.nextIndex++; this.tableVals[j][i] = -1; this.oldIDs.push(this.tableID[j][i]); this.tableXPos[j][i] = xPos; this.tableYPos[j][i] = yPos; this.cmd("CreateRectangle", this.tableID[j][i], "", DPChange.TABLE_ELEM_WIDTH, DPChange.TABLE_ELEM_HEIGHT, xPos, yPos); indexID = this.nextIndex++; this.oldIDs.push(indexID); this.cmd("CreateLabel", indexID, i, xPos - DPChange.TABLE_ELEM_WIDTH, yPos); this.cmd("SetForegroundColor", indexID, DPChange.TABLE_INDEX_COLOR); xPos = xPos + table_cols * DPChange.TABLE_DIFF_X + 1.5*DPChange.TABLE_DIFF_X; } } } DPChange.prototype.clearOldIDs = function() { for (var i = 0; i < this.oldIDs.length; i++) { this.cmd("Delete", this.oldIDs[i]); } this.oldIDs =[]; this.nextIndex = this.initialIndex; } DPChange.prototype.reset = function() { this.oldIDs =[]; this.coinIndex = 0; this.usingDPCode = true; this.coinTypeButtons[0].checked = true; this.nextIndex = this.initialIndex; } DPChange.prototype.emptyCallback = function(event) { this.implementAction(this.helpMessage.bind(this), ""); // TODO: Put up a message to push the appropriate button? } DPChange.prototype.displayCoinsUsed = function() { var currValue = this.tableVals[1].length - 1; var currX = 30; var currY = 200; var moveID; moveID = this.nextIndex++; while (currValue > 0) { moveID = this.nextIndex++; this.oldIDs.push(moveID); this.cmd("CreateLabel", moveID, this.tableVals[1][currValue], this.tableXPos[1][currValue], this.tableYPos[1][currValue]); this.cmd("Move", moveID, currX, currY); this.cmd("Step"); currX += 20; currValue = currValue - this.tableVals[1][currValue]; } } DPChange.prototype.recursiveCallback = function(event) { var fibValue; if (this.fibField.value != "") { var fibValue = Math.min(parseInt(this.fibField.value), DPChange.MAX_VALUE - 5); this.fibField.value = String(fibValue); this.implementAction(this.recursiveChange.bind(this),fibValue); } else { this.implementAction(this.helpMessage.bind(this), ""); } } DPChange.prototype.tableCallback = function(event) { var fibValue; if (this.fibField.value != "") { var fibValue = Math.min(parseInt(this.fibField.value), DPChange.MAX_VALUE); this.fibField.value = String(fibValue); this.implementAction(this.tableChange.bind(this),fibValue); } else { this.implementAction(this.helpMessage.bind(this), ""); } } DPChange.prototype.memoizedCallback = function(event) { var fibValue; if (this.fibField.value != "") { var changeVal = Math.min(parseInt(this.fibField.value), DPChange.MAX_VALUE); this.fibField.value = String(changeVal); this.implementAction(this.memoizedChange.bind(this),changeVal); } else { this.implementAction(this.helpMessage.bind(this), ""); } } DPChange.prototype.helpMessage = function(value) { this.commands = []; this.clearOldIDs(); var messageID = this.nextIndex++; this.oldIDs.push(messageID); this.cmd("CreateLabel", messageID, "Enter a value between 0 and " + String(DPChange.MAX_VALUE) + " in the text field.\n" + "Then press the Change Recursive, Change Table, Change Memoized, or Change Greedy button", DPChange.RECURSIVE_START_X, DPChange.RECURSIVE_START_Y, 0); return this.commands; } DPChange.prototype.recursiveChange = function(value) { this.commands = []; this.clearOldIDs(); if (!this.usingDPCode) { this.setCodeAlpha(this.greedyCodeID, 0); this.setCodeAlpha(this.codeID, 1); this.usingDPCode = true; } this.currentY = DPChange.RECURSIVE_START_Y; var functionCallID = this.nextIndex++; this.oldIDs.push(functionCallID); var final = this.change(value, DPChange.RECURSIVE_START_X, functionCallID); this.cmd("SetText", functionCallID, "change(" + String(value)+ ", [" +String(DPChange.COINS[this.coinIndex]) +"]) = " + String(final[0])); return this.commands; } DPChange.prototype.change = function(value, xPos, ID) { var coins = DPChange.COINS[this.coinIndex]; this.cmd("CreateLabel", ID, "change(" + String(value)+ ", [" +String(coins) +"])", xPos, this.currentY, 0); this.currentY += DPChange.RECURSIVE_DELTA_Y; this.cmd("SetForegroundColor", this.codeID[0][1], DPChange.CODE_HIGHLIGHT_COLOR); this.cmd("Step"); this.cmd("SetForegroundColor", this.codeID[0][1], DPChange.CODE_STANDARD_COLOR); this.cmd("SetForegroundColor", this.codeID[1][1], DPChange.CODE_HIGHLIGHT_COLOR); this.cmd("Step"); this.cmd("SetForegroundColor", this.codeID[1][1], DPChange.CODE_STANDARD_COLOR); // return 1; if (value > 0) { this.cmd("SetForegroundColor", this.codeID[3][0], DPChange.CODE_HIGHLIGHT_COLOR); this.cmd("Step"); this.cmd("SetForegroundColor", this.codeID[3][0], DPChange.CODE_STANDARD_COLOR); this.cmd("SetForegroundColor", this.codeID[4][0], DPChange.CODE_HIGHLIGHT_COLOR); this.cmd("Step"); this.cmd("SetForegroundColor", this.codeID[4][0], DPChange.CODE_STANDARD_COLOR); var i; var best = -1; var nextID = this.nextIndex++; var nextID2 = this.nextIndex++; var recID = nextID; var bestList; for (i = 0; i < coins.length; i++) { this.cmd("SetForegroundColor", this.codeID[5][1], DPChange.CODE_HIGHLIGHT_COLOR); this.cmd("Step"); this.cmd("SetForegroundColor", this.codeID[5][1], DPChange.CODE_STANDARD_COLOR); if (value >= coins[i]) { this.cmd("SetForegroundColor", this.codeID[6][1], DPChange.CODE_HIGHLIGHT_COLOR); this.cmd("Step"); this.cmd("SetForegroundColor", this.codeID[6][1], DPChange.CODE_STANDARD_COLOR); var nextTry = this.change(value - coins[i], xPos + DPChange.RECURSIVE_DELTA_X, recID); // TODO: SOMEHTING ELSE HERE if (best == -1) { this.cmd("SetForegroundColor", this.codeID[7][1], DPChange.CODE_HIGHLIGHT_COLOR); this.cmd("Step"); this.cmd("SetForegroundColor", this.codeID[7][1], DPChange.CODE_STANDARD_COLOR); best = nextTry[0] + 1; bestList = nextTry[1]; bestList.push(coins[i]); this.currentY += DPChange.RECURSIVE_DELTA_Y; recID = nextID2; } else if (best > nextTry[0] + 1) { this.cmd("SetForegroundColor", this.codeID[7][2], DPChange.CODE_HIGHLIGHT_COLOR); this.cmd("Step"); this.cmd("SetForegroundColor", this.codeID[7][2], DPChange.CODE_STANDARD_COLOR); best = nextTry[0] + 1; bestList = nextTry[1]; bestList.push(coins[i]);; this.cmd("Delete", recID); this.cmd("SetText", nextID, String(best) + " ([" + String(bestList) + "])"); this.cmd("SetPosition", nextID, xPos + DPChange.RECURSIVE_DELTA_X, this.currentY); this.cmd("Move", nextID, xPos + DPChange.RECURSIVE_DELTA_X, this.currentY - DPChange.RECURSIVE_DELTA_Y); this.cmd("Step"); } else { this.cmd("Delete", recID); } } else { break; } } this.cmd("Delete", nextID); this.cmd("SetText", ID, String(best) + " ([" + String(bestList) + "])"); this.cmd("SetPosition", ID, xPos + DPChange.RECURSIVE_DELTA_X, this.currentY); this.cmd("Move", ID, xPos, this.currentY - 2 * DPChange.RECURSIVE_DELTA_Y); this.currentY = this.currentY - 2 * DPChange.RECURSIVE_DELTA_Y; this.cmd("SetForegroundColor", this.codeID[9][0], DPChange.CODE_HIGHLIGHT_COLOR); this.cmd("Step"); this.cmd("SetForegroundColor", this.codeID[9][0], DPChange.CODE_STANDARD_COLOR); this.cmd("Step"); return [best, bestList]; } else { this.cmd("SetText", ID, "0"); this.cmd("SetForegroundColor", this.codeID[2][0], DPChange.CODE_HIGHLIGHT_COLOR); this.cmd("Step"); this.cmd("SetForegroundColor", this.codeID[2][0], DPChange.CODE_STANDARD_COLOR); this.currentY -= DPChange.RECURSIVE_DELTA_Y; return [0, []]; } } DPChange.prototype.tableChange = function(value) { this.commands = []; this.clearOldIDs(); if (!this.usingDPCode) { this.setCodeAlpha(this.greedyCodeID, 0); this.setCodeAlpha(this.codeID, 1); this.usingDPCode = true; } this.buildTable(value); coins = DPChange.COINS[this.coinIndex]; var i; for (i = 0; i <= value && i <= 0; i++) { this.cmd("SetForegroundColor", this.codeID[1][1], DPChange.CODE_HIGHLIGHT_COLOR); this.cmd("SetForegroundColor", this.codeID[2][0], DPChange.CODE_HIGHLIGHT_COLOR); this.cmd("SetHighlight", this.tableID[0][i], 1); this.cmd("SetText", this.tableID[0][i], 0); this.tableVals[0][i] = 0; this.cmd("Step"); this.cmd("SetForegroundColor", this.codeID[1][1], DPChange.CODE_STANDARD_COLOR); this.cmd("SetForegroundColor", this.codeID[2][0], DPChange.CODE_STANDARD_COLOR); this.cmd("SetHighlight", this.tableID[0][i], 0); } for (i = 1; i <= value; i++) { this.tableVals[0][i] = -1; var j; for (j = 0; j < coins.length; j++) { if (coins[j] <= i) { this.cmd("SetHighlight", this.tableID[0][i-coins[j]], 1); this.cmd("SetHighlight", this.tableID[0][i], 1); this.cmd("Step"); if (this.tableVals[0][i] == -1 || this.tableVals[0][i] > this.tableVals[0][i - coins[j]] + 1) { this.tableVals[0][i] = this.tableVals[0][i- coins[j]] + 1; this.cmd("SetText", this.tableID[0][i], this.tableVals[0][i]); this.cmd("SetHighlight", this.tableID[1][i], 1); this.cmd("SetText", this.tableID[1][i], coins[j]); this.tableVals[1][i] = coins[j]; this.cmd("Step") this.cmd("SetHighlight", this.tableID[1][i], 0); } this.cmd("SetHighlight", this.tableID[0][i-coins[j]], 0); this.cmd("SetHighlight", this.tableID[0][i], 0); } } } var finalID = this.nextIndex++; this.oldIDs.push(finalID); this.cmd("CreateLabel", finalID, this.tableVals[0][value], this.tableXPos[0][value] - 5, this.tableYPos[0][value] - 5, 0); this.cmd("Move", finalID, DPChange.RECURSIVE_START_X, DPChange.RECURSIVE_START_Y); this.cmd("Step"); this.cmd("SetText", finalID, "change(" + String(value) + ") = " + String(this.tableVals[0][value])); this.displayCoinsUsed(); return this.commands; } DPChange.prototype.fibMem = function(value, xPos, ID) { this.cmd("CreateLabel", ID, "fib(" + String(value)+")", xPos, this.currentY, 0); this.cmd("SetHighlight", this.tableID[value], 1); // TODO: Add an extra pause here? this.cmd("Step"); if (this.tableVals[value] >= 0) { this.cmd("Delete", ID, "fib(" + String(value)+")", xPos, this.currentY, 0); this.cmd("CreateLabel", ID, this.tableVals[value], this.tableXPos[value] - 5, this.tableYPos[value] - 5, 0); this.cmd("Move", ID, xPos, this.currentY); this.cmd("Step") this.cmd("SetHighlight", this.tableID[value], 0); return this.tableVals[value]; } this.cmd("SetHighlight", this.tableID[value], 0); this.currentY += DPChange.RECURSIVE_DELTA_Y; this.cmd("SetForegroundColor", this.codeID[0][1], DPChange.CODE_HIGHLIGHT_COLOR); this.cmd("Step"); this.cmd("SetForegroundColor", this.codeID[0][1], DPChange.CODE_STANDARD_COLOR); this.cmd("SetForegroundColor", this.codeID[1][1], DPChange.CODE_HIGHLIGHT_COLOR); this.cmd("Step"); this.cmd("SetForegroundColor", this.codeID[1][1], DPChange.CODE_STANDARD_COLOR); if (value > 1) { var firstID = this.nextIndex++; var secondID = this.nextIndex++; this.cmd("SetForegroundColor", this.codeID[4][1], DPChange.CODE_HIGHLIGHT_COLOR); this.cmd("Step"); this.cmd("SetForegroundColor", this.codeID[4][1], DPChange.CODE_STANDARD_COLOR); var firstValue = this.fibMem(value-1, xPos + DPChange.RECURSIVE_DELTA_X, firstID); this.currentY += DPChange.RECURSIVE_DELTA_Y; this.cmd("SetForegroundColor", this.codeID[4][3], DPChange.CODE_HIGHLIGHT_COLOR); this.cmd("Step"); this.cmd("SetForegroundColor", this.codeID[4][3], DPChange.CODE_STANDARD_COLOR); var secondValue = this.fibMem(value-2, xPos + DPChange.RECURSIVE_DELTA_X, secondID); this.cmd("SetForegroundColor", this.codeID[4][1], DPChange.CODE_RECURSIVE_1_COLOR); this.cmd("SetForegroundColor", firstID, DPChange.CODE_RECURSIVE_1_COLOR); this.cmd("SetForegroundColor", this.codeID[4][2], DPChange.CODE_HIGHLIGHT_COLOR); this.cmd("SetForegroundColor", this.codeID[4][3], DPChange.CODE_RECURSIVE_2_COLOR); this.cmd("SetForegroundColor", secondID, DPChange.CODE_RECURSIVE_2_COLOR); this.cmd("Step"); this.cmd("SetForegroundColor", this.codeID[4][1], DPChange.CODE_STANDARD_COLOR); this.cmd("SetForegroundColor", this.codeID[4][2], DPChange.CODE_STANDARD_COLOR); this.cmd("SetForegroundColor", this.codeID[4][3], DPChange.CODE_STANDARD_COLOR); this.cmd("Delete", firstID); this.cmd("Delete", secondID); this.cmd("SetText", ID, firstValue + secondValue); this.cmd("Step"); this.tableVals[value] = firstValue + secondValue; this.currentY = this.currentY - 2 * DPChange.RECURSIVE_DELTA_Y; this.cmd("CreateLabel", this.nextIndex, this.tableVals[value], xPos+5, this.currentY + 5); this.cmd("Move", this.nextIndex, this.tableXPos[value], this.tableYPos[value], this.currentY); this.cmd("Step"); this.cmd("Delete", this.nextIndex); this.cmd("SetText", this.tableID[value], this.tableVals[value]); return firstValue + secondValue; } else { this.cmd("SetText", ID, "1"); this.cmd("SetForegroundColor", this.codeID[2][0], DPChange.CODE_HIGHLIGHT_COLOR); this.cmd("Step"); this.cmd("SetForegroundColor", this.codeID[2][0], DPChange.CODE_STANDARD_COLOR); this.tableVals[value] = 1; this.cmd("CreateLabel", this.nextIndex, this.tableVals[value], xPos + 5, this.currentY + 5); this.cmd("Move", this.nextIndex, this.tableXPos[value], this.tableYPos[value], this.currentY); this.cmd("Step"); this.cmd("Delete", this.nextIndex); this.cmd("SetText", this.tableID[value], this.tableVals[value]); this.currentY -= DPChange.RECURSIVE_DELTA_Y; return 1; } } DPChange.prototype.memoizedChange = function(value) { this.commands = []; if (!this.usingDPCode) { this.setCodeAlpha(this.greedyCodeID, 0); this.setCodeAlpha(this.codeID, 1); this.usingDPCode = true; } this.clearOldIDs(); this.buildTable(value); this.currentY = DPChange.RECURSIVE_START_Y; var functionCallID = this.nextIndex++; this.oldIDs.push(functionCallID); var final = this.changeMem(value, DPChange.RECURSIVE_START_X, functionCallID); this.cmd("SetText", functionCallID, "change(" + String(value)+ ", [" +String(DPChange.COINS[this.coinIndex]) +"]) = " + String(final[0])); return this.commands; this.currentY = DPChange.RECURSIVE_START_Y; return this.commands; } DPChange.prototype.changeMem = function(value, xPos, ID) { var coins = DPChange.COINS[this.coinIndex]; this.cmd("CreateLabel", ID, "change(" + String(value)+ ", [" +String(coins) +"])", xPos, this.currentY, 0); this.cmd("SetForegroundColor", this.codeID[0][1], DPChange.CODE_HIGHLIGHT_COLOR); this.cmd("SetHighlight", this.tableID[0][value], 1); this.cmd("Step"); if (this.tableVals[0][value] >= 0) { this.cmd("Delete", ID); this.cmd("CreateLabel", ID, this.tableVals[0][value], this.tableXPos[0][value] - 5, this.tableYPos[0][value] - 5, 0); this.cmd("Move", ID, xPos, this.currentY); this.cmd("Step") this.cmd("SetHighlight", this.tableID[0][value], 0); this.cmd("SetForegroundColor", this.codeID[0][1], DPChange.CODE_STANDARD_COLOR); return [this.tableVals[0][value], []]; } this.cmd("SetHighlight", this.tableID[0][value], 0); this.currentY += DPChange.RECURSIVE_DELTA_Y; this.cmd("SetForegroundColor", this.codeID[0][1], DPChange.CODE_STANDARD_COLOR); this.cmd("SetForegroundColor", this.codeID[1][1], DPChange.CODE_HIGHLIGHT_COLOR); this.cmd("Step"); this.cmd("SetForegroundColor", this.codeID[1][1], DPChange.CODE_STANDARD_COLOR); // return 1; if (value > 0) { this.cmd("SetForegroundColor", this.codeID[3][0], DPChange.CODE_HIGHLIGHT_COLOR); this.cmd("Step"); this.cmd("SetForegroundColor", this.codeID[3][0], DPChange.CODE_STANDARD_COLOR); this.cmd("SetForegroundColor", this.codeID[4][0], DPChange.CODE_HIGHLIGHT_COLOR); this.cmd("Step"); this.cmd("SetForegroundColor", this.codeID[4][0], DPChange.CODE_STANDARD_COLOR); var i; var best = -1; var nextID = this.nextIndex++; var nextID2 = this.nextIndex++; var recID = nextID; var bestList; for (i = 0; i < coins.length; i++) { this.cmd("SetForegroundColor", this.codeID[5][1], DPChange.CODE_HIGHLIGHT_COLOR); this.cmd("Step"); this.cmd("SetForegroundColor", this.codeID[5][1], DPChange.CODE_STANDARD_COLOR); if (value >= coins[i]) { this.cmd("SetForegroundColor", this.codeID[6][1], DPChange.CODE_HIGHLIGHT_COLOR); this.cmd("Step"); this.cmd("SetForegroundColor", this.codeID[6][1], DPChange.CODE_STANDARD_COLOR); var nextTry = this.changeMem(value - coins[i], xPos + DPChange.RECURSIVE_DELTA_X, recID); // TODO: SOMEHTING ELSE HERE if (best == -1) { this.cmd("SetForegroundColor", this.codeID[7][1], DPChange.CODE_HIGHLIGHT_COLOR); this.cmd("Step"); this.cmd("SetForegroundColor", this.codeID[7][1], DPChange.CODE_STANDARD_COLOR); best = nextTry[0] + 1; bestList = nextTry[1]; bestList.push(coins[i]);; this.currentY += DPChange.RECURSIVE_DELTA_Y; recID = nextID2; } else if (best > nextTry[0] + 1) { this.cmd("SetForegroundColor", this.codeID[7][2], DPChange.CODE_HIGHLIGHT_COLOR); this.cmd("Step"); this.cmd("SetForegroundColor", this.codeID[7][2], DPChange.CODE_STANDARD_COLOR); best = nextTry[0] + 1; bestList = nextTry[1]; bestList.push(coins[i]);; this.cmd("Delete", recID); this.cmd("SetText", nextID, String(best)); this.cmd("SetPosition", nextID, xPos + DPChange.RECURSIVE_DELTA_X, this.currentY); this.cmd("Move", nextID, xPos + DPChange.RECURSIVE_DELTA_X, this.currentY - DPChange.RECURSIVE_DELTA_Y); this.cmd("Step"); } else { this.cmd("Step"); this.cmd("Delete", recID); } } else { break; } } this.cmd("Delete", nextID); this.cmd("SetText", ID, String(best)); this.cmd("SetText", this.tableID[0][value], best); this.cmd("SetText", this.tableID[1][value], bestList[0]); this.tableVals[0][value] = best; this.tableVals[1][value] = bestList[0]; this.cmd("SetPosition", ID, xPos + DPChange.RECURSIVE_DELTA_X, this.currentY); this.cmd("Move", ID, xPos, this.currentY - 2 * DPChange.RECURSIVE_DELTA_Y); this.currentY = this.currentY - 2 * DPChange.RECURSIVE_DELTA_Y; this.cmd("SetForegroundColor", this.codeID[9][0], DPChange.CODE_HIGHLIGHT_COLOR); this.cmd("Step"); this.cmd("SetForegroundColor", this.codeID[9][0], DPChange.CODE_STANDARD_COLOR); this.cmd("Step"); return [best, bestList]; } else { this.cmd("SetText", ID, "0"); this.cmd("SetForegroundColor", this.codeID[2][0], DPChange.CODE_HIGHLIGHT_COLOR); this.cmd("Step"); this.cmd("SetForegroundColor", this.codeID[2][0], DPChange.CODE_STANDARD_COLOR); this.cmd("SetText", this.tableID[0][value], 0); this.currentY -= DPChange.RECURSIVE_DELTA_Y; return [0, []]; } } DPChange.prototype.enableUI = function(event) { for (var i = 0; i < this.controls.length; i++) { this.controls[i].disabled = false; } } DPChange.prototype.disableUI = function(event) { for (var i = 0; i < this.controls.length; i++) { this.controls[i].disabled = true; } } var currentAlg; function init() { var animManag = initCanvas(); currentAlg = new DPChange(animManag, canvas.width, canvas.height); }