From d0cebce60507d54697562321b6be270fcf73f91e Mon Sep 17 00:00:00 2001 From: Brandon Reid Date: Mon, 6 Oct 2014 13:33:43 -0600 Subject: [PATCH] Fixed bug when calculating the reverse delta. Added in a case for a todo with checklists so the gp and exp for those are reversed correctly. Added a couple test cases for checking then unchecking tasks. --- dist/habitrpg-shared.js | 30 +++++++++++++++++++----------- script/index.coffee | 35 +++++++++++++++++++++++------------ test/algos.mocha.coffee | 15 +++++++++++++++ 3 files changed, 57 insertions(+), 23 deletions(-) diff --git a/dist/habitrpg-shared.js b/dist/habitrpg-shared.js index 95526bfe55..eb3dea7521 100644 --- a/dist/habitrpg-shared.js +++ b/dist/habitrpg-shared.js @@ -14180,10 +14180,10 @@ api.wrap = function(user, main) { return nextDelta; }; calculateReverseDelta = function() { - var calc, closeEnough, currVal, diff, testVal; + var calc, closeEnough, currVal, diff, nextDelta, testVal, _ref1; currVal = task.value < -47.27 ? -47.27 : task.value > 21.27 ? 21.27 : task.value; testVal = currVal + Math.pow(0.9747, currVal) * (direction === 'down' ? -1 : 1); - closeEnough = 0.0001; + closeEnough = 0.00001; while (true) { calc = testVal + Math.pow(0.9747, testVal); diff = currVal - calc; @@ -14196,12 +14196,20 @@ api.wrap = function(user, main) { testVal += diff; } } - return testVal - currVal; + nextDelta = testVal - currVal; + if (((_ref1 = task.checklist) != null ? _ref1.length : void 0) > 0) { + if (task.type === 'todo') { + nextDelta *= 1 + _.reduce(task.checklist, (function(m, i) { + return m + (i.completed ? 1 : 0); + }), 0); + } + } + return nextDelta; }; - changeTaskValue = function(reverse) { + changeTaskValue = function() { return _.times(options.times, function() { var nextDelta, _ref1; - nextDelta = reverse ? calculateReverseDelta() : calculateDelta(); + nextDelta = !options.cron && direction === 'down' ? calculateReverseDelta() : calculateDelta(); if (task.type !== 'reward') { if (user.preferences.automaticAllocation === true && user.preferences.allocationMode === 'taskbased' && !(task.type === 'todo' && direction === 'down')) { user.stats.training[task.attribute] += nextDelta; @@ -14218,7 +14226,7 @@ api.wrap = function(user, main) { }); }; addPoints = function() { - var afterStreak, gpMod, intBonus, perBonus, streakBonus, _crit; + var afterStreak, currStreak, gpMod, intBonus, perBonus, streakBonus, _crit; _crit = (delta > 0 ? user.fns.crit() : 1); if (_crit > 1) { user._tmp.crit = _crit; @@ -14227,7 +14235,7 @@ api.wrap = function(user, main) { stats.exp += Math.round(delta * intBonus * task.priority * _crit * 6); perBonus = 1 + user._statsComputed.per * .02; gpMod = delta * task.priority * _crit * perBonus; - return stats.gp += task.streak ? (streakBonus = task.streak / 100 + 1, afterStreak = gpMod * streakBonus, gpMod > 0 ? user._tmp.streakBonus = afterStreak - gpMod : void 0, afterStreak) : gpMod; + return stats.gp += task.streak ? (currStreak = direction === 'down' ? task.streak - 1 : task.streak, streakBonus = currStreak / 100 + 1, afterStreak = gpMod * streakBonus, currStreak > 0 ? gpMod > 0 ? user._tmp.streakBonus = afterStreak - gpMod : void 0 : void 0, afterStreak) : gpMod; }; subtractPoints = function() { var conBonus, hpMod; @@ -14269,8 +14277,8 @@ api.wrap = function(user, main) { task.streak = 0; } } else { - changeTaskValue(delta < 0); - if (delta < 0) { + changeTaskValue(); + if (direction === 'down') { delta = calculateDelta(); } addPoints(); @@ -14292,8 +14300,8 @@ api.wrap = function(user, main) { changeTaskValue(); } else { task.dateCompleted = direction === 'up' ? new Date : void 0; - changeTaskValue(delta < 0); - if (delta < 0) { + changeTaskValue(); + if (direction === 'down') { delta = calculateDelta(); } addPoints(); diff --git a/script/index.coffee b/script/index.coffee index 6d8b7c595f..cd86afd909 100644 --- a/script/index.coffee +++ b/script/index.coffee @@ -877,7 +877,7 @@ api.wrap = (user, main=true) -> testVal = currVal + Math.pow(0.9747, currVal) * (if direction is 'down' then -1 else 1) # Now keep moving closer to the original value until we get "close enough" - closeEnough = 0.0001 + closeEnough = 0.00001 while true # Check how close we are to the original value by computing the delta off our guess # and looking at the difference between that and our current value. @@ -889,16 +889,25 @@ api.wrap = (user, main=true) -> testVal -= diff else testVal += diff + # When we get close enough, return the difference between our approximated value # and the current value. This will be the delta calculated from the original value # before the task was checked. - testVal - currVal + nextDelta = testVal - currVal - changeTaskValue = (reverse) -> + # Checklists + if task.checklist?.length > 0 + # If To-Do, point-match the TD per checklist item completed + if task.type is 'todo' + nextDelta *= (1 + _.reduce(task.checklist,((m,i)->m+(if i.completed then 1 else 0)),0)) + nextDelta + + + changeTaskValue = -> # If multiple days have passed, multiply times days missed _.times options.times, -> # Each iteration calculate the nextDelta, which is then accumulated in the total delta. - nextDelta = if reverse then calculateReverseDelta() else calculateDelta() + nextDelta = if not options.cron and direction is 'down' then calculateReverseDelta() else calculateDelta() unless task.type is 'reward' if (user.preferences.automaticAllocation is true and user.preferences.allocationMode is 'taskbased' and !(task.type is 'todo' and direction is 'down')) then user.stats.training[task.attribute] += nextDelta # ===== STRENGTH ===== @@ -930,9 +939,11 @@ api.wrap = (user, main=true) -> gpMod = (delta * task.priority * _crit * perBonus) stats.gp += if task.streak - streakBonus = task.streak / 100 + 1 # eg, 1-day streak is 1.1, 2-day is 1.2, etc + currStreak = if direction is 'down' then task.streak-1 else task.streak + streakBonus = currStreak / 100 + 1 # eg, 1-day streak is 1.01, 2-day is 1.02, etc afterStreak = gpMod * streakBonus - user._tmp.streakBonus = afterStreak - gpMod if (gpMod > 0) # keep this on-hand for later, so we can notify streak-bonus + if currStreak > 0 + user._tmp.streakBonus = afterStreak - gpMod if (gpMod > 0) # keep this on-hand for later, so we can notify streak-bonus afterStreak else gpMod @@ -965,9 +976,9 @@ api.wrap = (user, main=true) -> subtractPoints() task.streak = 0 unless user.stats.buffs.streaks else - changeTaskValue(delta < 0) - if delta < 0 - delta = calculateDelta() + changeTaskValue() + if direction is 'down' + delta = calculateDelta() # recalculate delta for unchecking so the gp and exp come out correctly addPoints() # obviously for delta>0, but also a trick to undo accidental checkboxes if direction is 'up' task.streak = if task.streak then task.streak + 1 else 1 @@ -986,9 +997,9 @@ api.wrap = (user, main=true) -> #don't touch stats on cron else task.dateCompleted = if direction is 'up' then new Date else undefined - changeTaskValue(delta < 0) - if delta < 0 - delta = calculateDelta() + changeTaskValue() + if direction is 'down' + delta = calculateDelta() # recalculate delta for unchecking so the gp and exp come out correctly addPoints() # obviously for delta>0, but also a trick to undo accidental checkboxes # MP++ per checklist item in ToDo, bonus per CLI multiplier = _.max([(_.reduce(task.checklist,((m,i)->m+(if i.completed then 1 else 0)),1)),1]) diff --git a/test/algos.mocha.coffee b/test/algos.mocha.coffee index 199c5eb887..fb5ccdef6b 100644 --- a/test/algos.mocha.coffee +++ b/test/algos.mocha.coffee @@ -93,6 +93,11 @@ expectNoChange = (before,after) -> _.each $w('stats items gear dailys todos rewards flags preferences'), (attr)-> expect(after[attr]).to.eql before[attr] +expectClosePoints = (before, after, taskType) -> + expect( Math.abs(after.stats.exp - before.stats.exp) ).to.be.lessThan 0.0001 + expect( Math.abs(after.stats.gp - before.stats.gp) ).to.be.lessThan 0.0001 + expect( Math.abs(after["#{taskType}s"][0].value - before["#{taskType}s"][0].value) ).to.be.lessThan 0.0001 + expectDayResetNoDamage = (b,a) -> [before,after] = [_.cloneDeep(b), _.cloneDeep(a)] _.each after.dailys, (task,i) -> @@ -379,10 +384,20 @@ describe 'Simple Scoring', -> @after.ops.score {params: {id: @after.dailys[0].id, direction: 'up'}} expectGainedPoints(@before, @after,'daily') + it 'Dailys : Up, Down', -> + @after.ops.score {params: {id: @after.dailys[0].id, direction: 'up'}} + @after.ops.score {params: {id: @after.dailys[0].id, direction: 'down'}} + expectClosePoints(@before, @after, 'daily') + it 'Todos : Up', -> @after.ops.score {params: {id: @after.todos[0].id, direction: 'up'}} expectGainedPoints(@before, @after,'todo') + it 'Todos : Up, Down', -> + @after.ops.score {params: {id: @after.todos[0].id, direction: 'up'}} + @after.ops.score {params: {id: @after.todos[0].id, direction: 'down'}} + expectClosePoints(@before, @after, 'todo') + describe 'Cron', -> it 'computes shouldCron', ->