autoAllocate: compactify some code (no reason, just for the ninja

feel). Add simulation script to show results for all auto-allocate
methods for levels 1-20
This commit is contained in:
Tyler Renelle 2014-01-04 16:35:10 -07:00
parent f4f6dcebdc
commit 2721800f37
4 changed files with 161 additions and 99 deletions

View file

@ -63,7 +63,7 @@ process.chdir = function (dir) {
};
},{}],3:[function(require,module,exports){
var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {};/**
var global=self;/**
* @license
* Lo-Dash 2.4.1 (Custom Build) <http://lodash.com/>
* Build: `lodash modern -o ./dist/lodash.js`
@ -12059,69 +12059,66 @@ var process=require("__browserify_process");(function() {
*/
autoAllocate: function() {
var diff, ideal, preference, suggested;
switch (user.preferences.allocationMode) {
case "flat":
suggested = Math.min(user.stats.str, user.stats.int, user.stats.con, user.stats.per);
if (user.stats.int === suggested) {
return "int";
} else if (user.stats.per === suggested) {
return "per";
} else if (user.stats.str === suggested) {
return "str";
} else {
return "con";
}
break;
case "classbased":
ideal = [user.stats.lvl / 7 * 3, user.stats.lvl / 7 * 2, user.stats.lvl / 7, user.stats.lvl / 7];
switch (user.stats["class"]) {
case "wizard":
preference = ["int", "per", "con", "str"];
break;
case "rogue":
preference = ["per", "str", "int", "con"];
break;
case "healer":
preference = ["con", "int", "str", "per"];
break;
default:
preference = ["str", "con", "per", "int"];
}
diff = [user.stats[preference[0]] - ideal[0], user.stats[preference[1]] - ideal[1], user.stats[preference[2]] - ideal[2], user.stats[preference[3]] - ideal[3]];
suggested = _.findIndex(diff, (function(val) {
if (val === _.min(diff)) {
return true;
return user.stats[(function() {
var diff, ideal, preference, suggested;
switch (user.preferences.allocationMode) {
case "flat":
switch (Math.min(user.stats.str, user.stats.int, user.stats.con, user.stats.per)) {
case user.stats.int:
return "int";
case user.stats.per:
return "per";
case user.stats.str:
return "str";
default:
return "con";
}
}));
if (suggested === -1) {
return "str";
} else {
return preference[suggested];
}
break;
case "taskbased":
suggested = _.findKey(user.stats.training, (function(val) {
if (val === _.max(user.stats.training)) {
return val;
break;
case "classbased":
ideal = [user.stats.lvl / 7 * 3, user.stats.lvl / 7 * 2, user.stats.lvl / 7, user.stats.lvl / 7];
preference = (function() {
switch (user.stats["class"]) {
case "wizard":
return ["int", "per", "con", "str"];
case "rogue":
return ["per", "str", "int", "con"];
case "healer":
return ["con", "int", "str", "per"];
default:
return ["str", "con", "per", "int"];
}
})();
diff = [user.stats[preference[0]] - ideal[0], user.stats[preference[1]] - ideal[1], user.stats[preference[2]] - ideal[2], user.stats[preference[3]] - ideal[3]];
suggested = _.findIndex(diff, (function(val) {
if (val === _.min(diff)) {
return true;
}
}));
if (~suggested) {
return preference[suggested];
} else {
return "str";
}
}));
user.stats.training.str = 0;
user.stats.training.int = 0;
user.stats.training.con = 0;
user.stats.training.per = 0;
if (suggested === void 0) {
case "taskbased":
suggested = _.findKey(user.stats.training, (function(val) {
if (val === _.max(user.stats.training)) {
return val;
}
}));
_.merge(user.stats.training, {
str: 0,
int: 0,
con: 0,
per: 0
});
return suggested || "str";
default:
return "str";
} else {
return suggested;
}
break;
default:
return "str";
}
}
})()]++;
},
updateStats: function(stats) {
var suggested, tnl;
var tnl;
if (stats.hp <= 0) {
return user.stats.hp = 0;
}
@ -12140,8 +12137,7 @@ var process=require("__browserify_process");(function() {
user.stats.lvl++;
tnl = api.tnl(user.stats.lvl);
if (user.preferences.automaticAllocation) {
suggested = user.fns.autoAllocate();
user.stats[suggested]++;
user.fns.autoAllocate();
} else {
user.stats.points = user.stats.lvl - (user.stats.con + user.stats.str + user.stats.per + user.stats.int);
}

View file

@ -1006,41 +1006,36 @@ api.wrap = (user) ->
{update} if aggregated changes, pass in userObj as update. otherwise commits will be made immediately
###
autoAllocate: ->
switch user.preferences.allocationMode
when "flat"
suggested = Math.min(user.stats.str, user.stats.int, user.stats.con, user.stats.per)
if user.stats.int is suggested # In case of ties, favor INT first, to get the next point sooner
return "int"
else if user.stats.per is suggested # Then favor PER, it's a god stat
return "per"
else if user.stats.str is suggested # Then favor STR, everyone loves crits
return "str"
else
return "con" # CON, the unsexiest of attributes
when "classbased"
# Attributes get 3:2:1:1 per 7 levels.
ideal = [(user.stats.lvl / 7 * 3), (user.stats.lvl / 7 * 2), (user.stats.lvl / 7), (user.stats.lvl / 7)]
# Primary, secondary etc. attributes aren't explicitly defined, so hardcode them. In order as above
switch user.stats.class
when "wizard" then preference = ["int", "per", "con", "str"]
when "rogue" then preference = ["per", "str", "int", "con"]
when "healer" then preference = ["con", "int", "str", "per"]
else preference = ["str", "con", "per", "int"]
# Get the difference between the ideal attribute spread according to level, and the user's current spread.
diff = [(user.stats[preference[0]]-ideal[0]),(user.stats[preference[1]]-ideal[1]),(user.stats[preference[2]]-ideal[2]),(user.stats[preference[3]]-ideal[3])]
suggested = _.findIndex(diff, ((val) -> if val is _.min(diff) then true)) # Returns the index of the first attribute that's furthest behind the ideal
if suggested is -1 then return "str" else return preference[suggested] # If _.findIndex failed, we'd get a -1...
when "taskbased"
suggested = _.findKey(user.stats.training, ((val) -> if val is _.max(user.stats.training) then val)) # Returns the stat that's been trained up the most this level
# FIXME Reset training for this level. Tried _.each but couldn't get it to take.
user.stats.training.str = 0
user.stats.training.int = 0
user.stats.training.con = 0
user.stats.training.per = 0
if suggested is undefined then return "str" else return suggested # Failed _.findkey gives undefined
# tallies = _.reduce user.tasks, ((m,v)-> m[v.attribute or 'str'] += v.value;m), {str:0,int:0,con:0,per:0}
# suggested = _.reduce tallies, ((m,v,k)-> if v>tallies[m] then k else m), 'str'
else return "str" # if all else fails, dump into STR
user.stats[(->
switch user.preferences.allocationMode
when "flat"
switch Math.min(user.stats.str, user.stats.int, user.stats.con, user.stats.per)
when user.stats.int then "int" # In case of ties, favor INT first, to get the next point sooner
when user.stats.per then "per" # Then favor PER, it's a god stat
when user.stats.str then "str" # Then favor STR, everyone loves crits
else "con" # CON, the unsexiest of attributes
when "classbased"
# Attributes get 3:2:1:1 per 7 levels.
ideal = [(user.stats.lvl / 7 * 3), (user.stats.lvl / 7 * 2), (user.stats.lvl / 7), (user.stats.lvl / 7)]
# Primary, secondary etc. attributes aren't explicitly defined, so hardcode them. In order as above
preference = switch user.stats.class
when "wizard" then ["int", "per", "con", "str"]
when "rogue" then ["per", "str", "int", "con"]
when "healer" then ["con", "int", "str", "per"]
else ["str", "con", "per", "int"]
# Get the difference between the ideal attribute spread according to level, and the user's current spread.
diff = [(user.stats[preference[0]]-ideal[0]),(user.stats[preference[1]]-ideal[1]),(user.stats[preference[2]]-ideal[2]),(user.stats[preference[3]]-ideal[3])]
suggested = _.findIndex(diff, ((val) -> if val is _.min(diff) then true)) # Returns the index of the first attribute that's furthest behind the ideal
return if ~suggested then preference[suggested] else "str" # If _.findIndex failed, we'd get a -1...
when "taskbased"
suggested = _.findKey(user.stats.training, ((val) -> if val is _.max(user.stats.training) then val)) # Returns the stat that's been trained up the most this level
# Reset training for this level.
_.merge user.stats.training, {str:0,int:0,con:0,per:0}
return suggested or "str" # Failed _.findkey gives undefined
# tallies = _.reduce user.tasks, ((m,v)-> m[v.attribute or 'str'] += v.value;m), {str:0,int:0,con:0,per:0}
# suggested = _.reduce tallies, ((m,v,k)-> if v>tallies[m] then k else m), 'str'
else "str" # if all else fails, dump into STR
)()]++
updateStats: (stats) ->
# Game Over
@ -1067,8 +1062,7 @@ api.wrap = (user) ->
# Auto-allocate a point, or give them a new manual point
if user.preferences.automaticAllocation
suggested = user.fns.autoAllocate()
user.stats[suggested]++
user.fns.autoAllocate()
else
# add new allocatable points. We could do user.stats.points++, but this does a fail-safe just in case
user.stats.points = user.stats.lvl - (user.stats.con + user.stats.str + user.stats.per + user.stats.int);

View file

@ -0,0 +1,72 @@
###
1) clone the repo
2) npm install
3) coffee ./tests/math_samples.coffee
Current results at https://gist.github.com/lefnire/8049676
###
shared = require '../../script/index.coffee'
_ = require 'lodash'
$w = (s)->s.split(' ')
id = shared.uuid()
user =
stats:
class: 'warrior'
lvl:1, hp:50, gp:0, exp:10
per:0, int:0, con:0, str:0
buffs: {per:0, int:0, con:0, str:0}
training: {int:0,con:0,per:0,str:0}
preferences: automaticAllocation: false
party: quest: key:'evilsanta', progress: {up:0,down:0}
achievements: {}
items:
eggs: {}
hatchingPotions: {}
food: {}
gear:
equipped:
weapon: 'weapon_warrior_4'
armor: 'armor_warrior_4'
shield: 'shield_warrior_4'
head: 'head_warrior_4'
habits: [{value:1,type:'habit'}]
dailys: []
todos: []
rewards: []
modes =
flat: _.cloneDeep user
classbased_warrior: _.cloneDeep user
classbased_rogue: _.cloneDeep user
classbased_wizard: _.cloneDeep user
classbased_healer: _.cloneDeep user
taskbased: _.cloneDeep user
modes.classbased_warrior.stats.class = 'warrior'
modes.classbased_rogue.stats.class = 'rogue'
modes.classbased_wizard.stats.class = 'wizard'
modes.classbased_healer.stats.class = 'healer'
_.each $w('flat classbased_warrior classbased_rogue classbased_wizard classbased_healer taskbased'), (mode) ->
_.merge modes[mode].preferences,
automaticAllocation: true
allocationMode: if mode.indexOf('classbased') is 0 then 'classbased' else mode
shared.wrap(modes[mode])
console.log "\n\n================================================"
console.log "New Simulation"
console.log "================================================\n\n"
_.times [20], (lvl) ->
console.log ("[lvl #{lvl}]\n--------------\n")
_.each $w('flat classbased_warrior classbased_rogue classbased_wizard classbased_healer taskbased'), (mode) ->
u = modes[mode] #local var
u.stats.exp = shared.tnl(lvl)+1 # level up
u.habits[0].attribute = u.fns.randomVal({str:'str',int:'int',per:'per',con:'con'})
u.ops.score {params:{id:u.habits[0].id},direction:'up'}
u.fns.updateStats(u.stats) # trigger stats update
str = mode + (if mode is 'taskbased' then " (#{u.habits[0].attribute})" else "")
console.log str, _.pick(u.stats, $w 'per int con str')

View file

@ -6,7 +6,7 @@
Current results at https://gist.github.com/lefnire/8049676
###
shared = require '../script/index.coffee'
shared = require '../../script/index.coffee'
_ = require 'lodash'
id = shared.uuid()