Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hard to find a better title.

In a game you can have many progress bars: health, mana, spells, etc that use attribute points. I want to create a script that helps you determine the easiest way to level off each of them to purchase another object.

For example

HP: 60, 12 per hour Mana: 80, 17 per hour Spells: 31, 7 per hour NEW SPELL: requires 51 HP, 107 mana, 38 spells
We have three things to note: the identifier, the total on hand (first number) and the amount they earn per hour. Using the above sample, to buy our new spell we'd..
HP: 60 OH, 51 needed, 0 change Mana: 80 OH, 107 needed, +27 change Spells: 31 OH, 38 needed, +7 change
We'd need to add to both mana and spells from any surplus we have (which we have 9 extra from HP we don't need). Unfortunately we don't have enough extra to bring all of them to the min. required to buy the new spell so I want the script to then be smart and determine how to shuffle all of our points where we can meet the requirement FASTEST by taking the total we have now and taking how many we earn per hour into consideration.

In short, I'm trying to shuffle everything to determine 1) Do we already meet the requirement 2) Can we shuffle them where they'll all meet it, and how 3) If we don't have enough, determine how to shuffle the points in such a way where we'd earn the rest fasted (by the amount earned per hour).

I know I won't get a full solution here but if people can help get me started I'd be very grateful.

Replies are listed 'Best First'.
Re: Calculate easiest way to 'level results'
by Roy Johnson (Monsignor) on Sep 19, 2007 at 16:36 UTC
    Kind of an interesting math problem. Since the points are all apparently interchangeable, you can start by adding everything up: You have 60+80+31=171 points, and you need 51+107+38=196. That leaves you 25 points short. You gain 12+17+7=36 per hour. So you'll need something under an hour to gain your points. 25/36 of an hour, to be exact.

    Update: I interrupt my own post to point out that there is no reason to do a shuffle if you don't have enough total points. Just figure out how long you need to wait, wait that long, and then shuffle.

    So shuffle your points to be 25/36 of an hour from having what you need:
    HP: 50-(25/36)*12
    Mana: 107-(25/36)*17
    Spells: 38-(25/36)*7
    should do it, not counting some rounding errors.

    Update: here's some ham-handed code (updated again to optimize leftover placement):

    # Have/gain per hour my @points = ([60,12,'HP'],[80,17,'Mana'],[31,7,'Spells']); my @need = (51, 107, 38); use List::Util qw(sum reduce); my $total_have = sum map $_->[0], @points; my $total_need = sum @need; print "You have $total_have and need $total_need\n"; my $diff_need = $total_need - $total_have; my $gain_per_hour = sum map$_->[1], @points; print "You need $diff_need more, and you gain them at $gain_per_hour p +er hour\n"; my $time_left = $diff_need/$gain_per_hour; print "That should take $diff_need/$gain_per_hour hour(s).\n"; my @new_values = map $need[$_] - $time_left * $points[$_][1], 0..$#nee +d; my $leftover = $total_have - sum map int, @new_values; # Put leftovers on furthest-from-goal while ($leftover--) { my $slow = reduce { $new_values[$a]-int($new_values[$a]) > $new_values[$b]-int($new_va +lues[$b]) ? $a : $b } 0..$#points; $new_values[$slow]++; } @new_values = map int, @new_values; for (0..$#need) { print "New value for $points[$_][2]: $new_values[$_]\n"; }

    Caution: Contents may have been coded under pressure.

      If the character is far from his goal, you might subtract more than you are allowed.

      my @points = ([10,12,'HP'],[10,17,'Mana'],[10,7,'Spells']); my @need = (40, 40, 40);
      You have 30 and need 120 You need 90 more, and you gain them at 36 per hour That should take 90/36 hour(s). New value for HP: 10 New value for Mana: -2 <----------- New value for Spells: 22
        In that case, it is better to wait to do any shuffling. The total time required to achieve your goal will be longer if you choose a sub-optimal distribution of points. As I noted in my now-often-updated original post, there's no reason you shouldn't just wait until you have the required number of points, anyway.

        Caution: Contents may have been coded under pressure.
Re: Calculate easiest way to 'level results'
by ikegami (Patriarch) on Sep 19, 2007 at 17:17 UTC

    This solution uses the same base idea as Roy Johnson's (summing the original stats), but uses an approach that won't give negative stats and doesn't suffer from rounding error. (It does this by quickly trying every possible combination of initial stats.)

    use strict; use warnings; use List::Util qw( max sum ); my @stats = qw( hp mana spells ); my %rates = ( hp => 12, mana => 17, spells => 7 ); my %stats = ( hp => 60, mana => 80, spells => 31 ); my %needed = ( hp => 51, mana => 107, spells => 38 ); { my $avail = sum @stats{@stats}; our @parts; ('.' x $avail) =~ / ^ (.*) (?(?{ length($1) > $needed{$stats[0]} })(?!)) (.*) (?(?{ length($2) > $needed{$stats[1]} })(?!)) (.*) (?(?{ length($3) > $needed{$stats[2]} })(?!)) \z (??{ push @parts, { $stats[0] => length($1), $stats[1] => length($2), $stats[2] => length($3), }; }) (?!) /x; my ($result) = sort { $a->[1] <=> $b->[1] } map { my $part = $_; my $time = max map { ( $needed{$_} - $part->{$_} ) + / $rates{$_} } @stats; [ $_, $time ] } @parts; print(join("\t", @stats, 'hours'), "\n"); printf(join("\t", ("%d")x@stats, "%.2f") . "\n", @{$result->[0]}{@stats}, $result->[1], ); }
    hp mana spells hours 43 95 33 0.71

    There's surely a better way of finding the partitions, but I don't know it off the top of my head, the method I used works, and the method I used is fast.

Re: Calculate easiest way to 'level results'
by Scott7477 (Chaplain) on Sep 19, 2007 at 17:11 UTC
    If I understand your explanation of the requirement, what you have here is what can be referred to as an optimization problem. You want to minimize the amount of time needed to purchase an object subject to the cost constraints, in this case the amount of time needed to accumulate mana and spells. You can find the minimal time using linear programming. The link is to the Wikipedia entry on the subject which seems to be aimed at folks with fairly advanced knowledge of the subject, but I checked a couple of the external links, which seemed to explain the basics. A cursory look at CPAN didn't turn up any LP modules, which surprised me. It's an interesting problem you've posed, but I've not got the time to work it out this morning:) Hope this helps...
Re: Calculate easiest way to 'level results'
by ikegami (Patriarch) on Sep 19, 2007 at 16:15 UTC

    After an hour, do you gain 17 mana *and* 7 spells, or do you gain 17 mana *or* 7 spells?

    Can you suffle your stats every hour, or can you shuffle your stats only once?

      Hi.

      They are ALL gained.

      HP: 60, 12 per hour Mana: 80, 17 per hour Spells: 31, 7 per hour
      You'd earn 12 HP, 17 mana AND 7 spell points per hour. In reality you don't earn the points at the turn of the hour, they are earned gradually over the hour (30 pts/hr means you'd literally gain 1 point every two minutes for a total of 30 that hour).

      You can shuffle the stats as often as you want in the game but it costs gold each time you do it. So you wouldn't want to shuffle now and then in 2 hours shuffle again to get something faster. Best in one shuffle would be ideal.

      Sorry for not clarifying

Re: Calculate easiest way to 'level results'
by fenLisesi (Priest) on Sep 20, 2007 at 15:41 UTC
    But who would spend a moment on even the most gorgeous vista in (HP, Mana, Spell) land, when you can enjoy fulfilling XP--feel it diffuse through your veins--in the cloister? A script with the correct behaviour would sell all your huge tracts of land in HMS Space--'tis an unweeded garden--and plant kernels in the greenhouse of the Monastery. There if they grow, the harvest is your own--for, else, who would bear the whips and scorns of tye, the net's delay, and the spurns that patient newbie of the unworthy takes? O, my mana for one more drop of XP. One more. Please.