This is something NetHack inherited from Hack and hasn't been changed
ever since.
AFAIK D&D doesn't have a score only experience points.
So what's the reason for this?
Bye
Patric
--
NetHack-De: NetHack auf Deutsch - http://nethack-de.sourceforge.net/
NetHack for AROS: http://sourceforge.net/projects/nethack-aros/
I think you probably pretty much said it yourself. It's to keep
score. Nethack, being a computer game with a score board, needs some
way of deciding how high on the board a player should be ranked. D&D,
being a role-playing game, with no way to "win" and no set goal beyond
that of collaborative storytelling, needs no such thing.
As it happens, Nethack's scoring system is mostly based on how many
monsters you've killed and, if you survive, on how much money and
valuables you're taking with you. I believe this actually goes all
the way back to Rogue, where it made perfect sense, given that the
goal of the game was to fight monsters, collect loot and escape with
it. Since then, Nethack's goals have gradually shifted from "steal
everything valuable" to "complete the divinely ordained quest", but
the original scoring system has been kept with minor adjustments
(ascension doubles your score).
The fact that, in modern Nethack, "ascending" and "getting a high
score" are, at least for sufficiently skilled players, more or less
orthogonal goals has a source of much discussion. Alas, few if any
concrete proposals for a better scoring system seem to have emerged.
A major reason for this is probably that Nethack is a complex enough
game that it's very hard if not impossible to come up with a good
linear measure of progress towards the "ultimate goal" of ascension
(or of efficiency in attaining it, for those who do). About the best
I could personally think of is "turns to ascend" (fewer being better,
of course), but that's a completely useless measure for those who fail
to ascend in the first place.
What we've come up with instead is essentially a system of multiple
goals. You can play to ascend. You can play to ascend every race and
class. You can play to ascend as fast as you can, or with as many
voluntary conducts as you can think of and feel like trying. Or you
can play for score, which is a perfectly valid goal, even if it does
seem a bit unfashionable currently. All of these goals can become
trivial if they're no longer challenging: once you've ascended every
race, class and alignment twice, ascension on its own starts to feel
pretty trivial, and once you've finished with 2^31-1 points, there's
no point in trying for a higher score. But, appearances to the
contrary, few players actually attain those levels of skill -- and
even for those who have, new challenges are always possible. Some
day, someone is going to ascend a naked atheist pacifist petless vegan
orc wizard with a score rolled over back to zero -- and they'll be
forever remembered in Nethack legend for it.
--
Ilmari Karonen
To reply by e-mail, please replace ".invalid" with ".net" in address.
My question wasn't why NetHack does have a score but why the score is
calculated as *four times* the experience.
At the time when Hack was written almost every game did have score. Off
the top of my head I don't remember a game around that time that
didn't have score. Even adventures had one!
In NetHack the only sources of unlimited score points are killing
monsters or gold resp. gems. The original Hack code suggests that this
isn't different there.
Maybe this emphasis on killing monster score points was made to ensure
that high scores are not attained by carrying large amount of gold and
gems.
It's called Hack not Gold, after all :)
> As it happens, Nethack's scoring system is mostly based on how many
> monsters you've killed and, if you survive, on how much money and
> valuables you're taking with you. I believe this actually goes all
> the way back to Rogue, where it made perfect sense, given that the
> goal of the game was to fight monsters, collect loot and escape with
> it. Since then, Nethack's goals have gradually shifted from "steal
> everything valuable" to "complete the divinely ordained quest", but
> the original scoring system has been kept with minor adjustments
> (ascension doubles your score).
I've noticed that new players tend at first to gauge their progress in
the game by the score they get.
But when they are nearing ascension readiness the score gets ignored.
> The fact that, in modern Nethack, "ascending" and "getting a high
> score" are, at least for sufficiently skilled players, more or less
> orthogonal goals has a source of much discussion. Alas, few if any
> concrete proposals for a better scoring system seem to have emerged.
I remember some discussion about a better scoring system but IIRC I have
only seen one alternate scoring system implemented: the alternate
scoring patch by Michael Lehotay.
Although IIRC this only implemented an upper limit to prevent pudding
farmer to gain unlimited high scores).
If I'm not mistaken neither Slash'Em nor SporkHack have changed
anything in regards to scoring.
> A major reason for this is probably that Nethack is a complex enough
> game that it's very hard if not impossible to come up with a good
> linear measure of progress towards the "ultimate goal" of ascension
> (or of efficiency in attaining it, for those who do). About the best
> I could personally think of is "turns to ascend" (fewer being better,
> of course), but that's a completely useless measure for those who fail
> to ascend in the first place.
Yes. At it is now, NetHack would at least need two high score tables.
One for those who die (where higher is better) and one for those who
ascend (where lower is better).
As you said, ascension and high score are two orthogonal goals I don't
think highest score must correlate with an ascension. People are
intelligent enough to notice that winning doesn't need to put you on
the top of the high score list.
But as scoring for non-ascenders is completely broken (copy-paste in a
pudding or death farm gets you any high score you want), at least this
should be fixed first.
> What we've come up with instead is essentially a system of multiple
> goals. You can play to ascend. You can play to ascend every race and
> class. You can play to ascend as fast as you can, or with as many
> voluntary conducts as you can think of and feel like trying. Or you
> can play for score, which is a perfectly valid goal, even if it does
> seem a bit unfashionable currently. All of these goals can become
> trivial if they're no longer challenging: once you've ascended every
> race, class and alignment twice, ascension on its own starts to feel
> pretty trivial, and once you've finished with 2^31-1 points, there's
> no point in trying for a higher score. But, appearances to the
> contrary, few players actually attain those levels of skill -- and
> even for those who have, new challenges are always possible. Some
> day, someone is going to ascend a naked atheist pacifist petless vegan
> orc wizard with a score rolled over back to zero -- and they'll be
> forever remembered in Nethack legend for it.
I can't almost stand the thought of the grinding such an ascension
would need.
Although this would be a perfect task for a bot. :-)
That's what I'd assume, too. It's just a scaling factor, and since XP
is already measured in arbitrary units, it doesn't really carry much
meaning IMHO. We could get rid of it and just make the amounts of XP
needed for each level and given by each monster (and other XP source)
four times larger without actually changing the gameplay in any real
sense.
> It's called Hack not Gold, after all :)
Indeed. :)
> But as scoring for non-ascenders is completely broken (copy-paste in a
> pudding or death farm gets you any high score you want), at least this
> should be fixed first.
IMHO the problem with the various types of "farming" isn't so much
that they're possible as that they're scriptable. There are two ways
to approach this problem. One is to accept that the loophole exists,
and simply decide that scripting counts as cheating; the other is to
try to make the scriptable aspects of the game either less profitable
or more complex, so that they're not as easy to automate.
For example, you could give puddings a small chance -- say, 1/5000 per
turn -- of spontaneously polymorphing into a random monster.
Shouldn't affect normal gameplay much, but large-scale pudding farming
would suddenly become _much_ more interesting. ;-)
That let's them get away to easy :)
At least mark them as cheaters. But this is hard to code right, so
fixing farming in the first place is probably easier to do.
> the other is to
> try to make the scriptable aspects of the game either less profitable
> or more complex, so that they're not as easy to automate.
Yes.
Of the four farming methods on
http://nethack.wikia.com/wiki/Farming
I only consider Pudding farming to be a real defect.
If you are able to pull off kraken or Death farming you are already
way beyond the point where you really benefit from it (besides the
rapid accumulation of score points).
You probably have enough experience at that point and death drops
aren't that important (and I think it should be trivial to disable
death drops for revived monsters).
I think Wraith farming shouldn't be restricted as it quite tedious to
do and probably not scriptable at all.
> For example, you could give puddings a small chance -- say, 1/5000 per
> turn -- of spontaneously polymorphing into a random monster.
> Shouldn't affect normal gameplay much, but large-scale pudding farming
> would suddenly become _much_ more interesting. ;-)
I don't like changes that make no sense in-game.
Halving the max HP for the two cloned puddings (what SporkHack and
GruntHack do IIRC) is quite effective to limit the number of created
puddings.
Disabling death drops for puddings altogether is another possibility.
I wonder if gremlin farming might be get viable if pudding farming is
somehow no longer effective.
IIRC gremlins get halved max HP when they split, so they already have
the previously-mentioned effective limit applied :)
Correct and I forgot that gremlins are small monsters and so their
death drop is already restricted.
The may be a way around that: monsters gain 1dN maxHP when they kill
other monsters, where N is the level of the monster killed. Gremlins
have base level 5, so a gremlin killing another gremlin should gain
1d5 maxHP and on average half as many actual HP. If in water, it then
promptly divides into about 1d5/2+1 new single-HP gremlins (modulo
some losses from hits taken before division is complete and some gains
from HP regeneration).
I believe the average number of new gremlins thus produced can, at
least with enough water squares to ensure rapid division, be greater
than one. Thus, all you need to produce an unlimited supply of
gremlins is a watery level and a ring of conflict.
(Of course, an altar on the level would be useful too. As it happens,
though, at least two classes -- Healer and Knight -- have guaranteed
altars surrounded by swamp on their Quest.)
>The fact that, in modern Nethack, "ascending" and "getting a high
>score" are, at least for sufficiently skilled players, more or less
>orthogonal goals has a source of much discussion. Alas, few if any
>concrete proposals for a better scoring system seem to have emerged.
Heck, that's easy.
Define checkpoints of whatever--mines, minetown, quest, finish sokoban, etc.
Newscore = checkpoints completed + oldscore/maxval(oldscore)
(or even multiply the fraction by 2 or 4.
In the raw form, you get a base score less than 20 or so plus a
fraction. Then multiply by 10,000 or whatever to make the newscore
look like a score.
hawk
I think important points for a score to keep track of, assuming it's
supposed to measure player skill in a non-conduct game, are:
proportion of game completion achieved,
"checkpoint achievements" which can only be done once each,
whether the game was actually won, and
time (in a winning game, faster should earn a better score).
Oddly, I don't think character experience should have anything to
do with score. If someone is able to ascend with a 5th-level
character, that's a heck of a lot more impressive, and should
score at least as high, as doing it with a 40th-level character.
We'd score games like this: all-cap names denote constants
(or in the case of CHECKPOINTVALS a constant array) you'd need to
tune; flag128 means a 128-bit flags variable with appropriate
macro accessor (named flagcheck) defined. checkpointachieved is
a bitvector with one bit indicating completion status for each
defined checkpoint.
Permille completion would be a score of 0 to 1000 indicating what
fraction of the "critical path" of the game is completed. The
critical path is measured as how far the character makes it on the
path to the amulet (first 499 points) plus how far the amulet makes
it on the path to the final altar (next 500 points). The final 1
point, making it 1000, would happen when the amulet is sacrificed
and the game is won.
/*** CUT HERE ***/
int finalscore (int permillecompletion, /* range 0-1000 */
int turncount,
flag128 checkpointachieved) {
int checkpointscore = 0;
int allcheckpoints = 0;
int completionscore = permillecompletion * SCORE_COMPLETION_WEIGHT;
int totalcompletion = 1000 * SCORE_COMPLETION_WEIGHT;
double timebonus;
int rawscore;
int count;
for (count = 0; count < NUM_CHECKPOINTS; count++){ /* for all checkpoints */
allcheckpoints += CHECKPOINTVALS[count]; /* add to the total. */
if (flagcheck(checkpointachieved, count)) /* if achieved then */
checkpointscore += CHECKPOINTVALS[count]; /* add to score too.*/
}
rawscore = checkpointscore + completionscore;
/* if the game is WON then assure that the raw score is strictly
higher than any score achievable by doing all checkpoints, and
also award time bonuses for faster games. */
if (permillecompletion == 1000){
/* ensure that winning counts more than any list of checkpoints */
rawscore += allcheckpoints;
/* SCORETURNCOUNT is the number of turns for which we want
to award quadruple score. Probably around half the turncount
of a typical unhurried "Expert" ascenscion. If a player
manages to ascend in 1/n SCORETURNCOUNT turns, his time
bonus will multiply his score by (n+1) squared. Thus,
half SCORETURNCOUNT = 9x score,
1/3 SCORETURNCOUNT = 16x score,
1/4 SCORETURNCOUNT = 25x score, etc.
Shaving turns makes a bigger score difference the faster
your speed run.
But no matter how many turns the player takes,
timebonus will always be greater than one. */
/* sigh, paranoid typecasting to satisfy all typechecking tools,
silence LINT, and avoid both undefined behavior and known bugs
of a few compilers in optimizing modes....Can this be done with
just ints? */
timebonus = ((double)(turncount + SCORETURNCOUNT)) / ((double)turncount);
rawscore = (int)(((double)rawscore) * timebonus * timebonus);
}
return(rawscore);
}
/***CUT HERE***/
..... or at least that's my opinion.
Bear
I like the idea for giving bonuses for speed runs. The polynomial
growth rate should definitely exceed the gains from extinctionist
behavior.
Conducts are another thing that might be worth considering. Each
worth relatively small bonuses individually (maybe along the lines of
the bonuses for bringing invocation artifacts), with polynomial
increase in score as the number of conducts increases.
Something like this:
--
/* n_conducts is the total number of conducts earned */
int i, conduct_bonus = 0;
for (i = 0; i < n_conducts; i++) conduct_bonus += i*i * CONDUCT_SCORE;
/* closed form formula:
int conduct_bonus = 1.0/6 * n_conducts * (n_conducts + 1) *
(2 * n_conducts + 1) * CONDUCT_BONUS;
*/
--
with a CONDUCT_SCORE of 5000 would yield 5000 for 1 conduct, 25,000
for 2, 70,000 for 3, and 2,530,000 for all 11. The bonus should of
course be applied before the one for a speed run. If someone finishes
so quickly that they don't have time to pray for some holy water to
bless that scroll of genocide they wished for, they deserve the extra
points.
> /* sigh, paranoid typecasting to satisfy all typechecking tools,
> silence LINT, and avoid both undefined behavior and known bugs
> of a few compilers in optimizing modes....Can this be done with
> just ints? */
>
> timebonus = ((double)(turncount + SCORETURNCOUNT)) / ((double)turncount);
> rawscore = (int)(((double)rawscore) * timebonus * timebonus);
> }
Only the first cast to double is strictly necessary. From the C99
standard (which I believe uses the same rules for implicit conversions
as C89 and K&R C):
--
6.3.1.8 Usual arithmetic conversions
...
Otherwise, if the corresponding real type of either operand is double,
the other operand is converted, without change of type domain, to a
type whose corresponding real type is double.
--
The final cast for int isn't necessary for well-defined behavior as
long as the result fits in an int and you're okay with the truncation,
but is probably needed to shut the compiler up about the possible loss
of precision.
It might not completely silence lint, but nothing short of a /wishing
will make Nethack pass lint cleanly, so I wouldn't worry about it too
much.
> I like the idea for giving bonuses for speed runs. The polynomial
> growth rate should definitely exceed the gains from extinctionist
> behavior.
>
> Conducts are another thing that might be worth considering. Each
> worth relatively small bonuses individually (maybe along the lines of
> the bonuses for bringing invocation artifacts), with polynomial
> increase in score as the number of conducts increases.
I'm not entirely sure that scores in a conduct game are really
comparable to scores in "straight" play. But, hmmm..... there
are a couple of good ways to get the effect you describe.
As long as no LOSING conduct game can ever get a score greater
than a WINNING non-conduct game, I'd have no problem with it.
That would mean you'd either need to award conduct bonuses for
winning games only, or "pad" the score of winning games with the
maximum that anyone could ever get from conducts.
>> /* sigh, paranoid typecasting to satisfy all typechecking tools,
>> silence LINT, and avoid both undefined behavior and known bugs
>> of a few compilers in optimizing modes....Can this be done with
>> just ints? */
>>
>> timebonus = ((double)(turncount + SCORETURNCOUNT)) /
>> ((double)turncount); rawscore = (int)(((double)rawscore) * timebonus *
>> timebonus); }
>
> Only the first cast to double is strictly necessary. From the C99
> standard ...
Strictly necessary according to the standard? I agree. You're correct.
But consistent across every last implementation in every optimization
mode? That's not something I'm willing to assume. And the order of
operations and casts really matters here, because partial products in
this case are likely to overflow an int. So I'll use paranoid typecasting
and insert lots of sequence points with parens, even though, according
to the standard, it's not strictly necessary. Let's just say I've been
bitten by a few buggy compilers before and leave it at that.
Bear
The former option is probably the better. Since the scoring for
conducts would need to be scaled relative to the normal scores, making
the normal scores dependent on the max conduct bonus introduces a
bit of a circular dependency that would be a hassle to deal with.
Whereas just adding the conduct bonuses right before the timing bonus
would be relatively straightforward.
>>> timebonus = ((double)(turncount + SCORETURNCOUNT)) /
>>> ((double)turncount); rawscore = (int)(((double)rawscore) * timebonus *
>>> timebonus); }
> ...
> But consistent across every last implementation in every optimization
> mode? That's not something I'm willing to assume. And the order of
> operations and casts really matters here, because partial products in
> this case are likely to overflow an int. So I'll use paranoid typecasting
> and insert lots of sequence points with parens, even though, according
> to the standard, it's not strictly necessary. Let's just say I've been
> bitten by a few buggy compilers before and leave it at that.
I avoided the issue of a buggy compiler since in such a case, I don't
really have *any* guarantees as to the behavior of the object code.
Since there's any number of things a compiler could have done wrong,
at some point, it becomes impossible to do anything other than code
according to the standard and hope the compiler's implemented right.
Of course, I've never seen a compiler that screws up implicit type
conversion in optimizing mode; if you have, then I suppose I'd have
to agree that it makes sense to use known workarounds.
But as far as overflow goes, I don't think casting would help in this
case. In the first expression, the intermediate product is first
calculated as type int, and only then cast to double. So if it
overflows without the cast, it overflows with it, unless the
optimizer is taking advantage of the field properties of the reals,
which it *really* shouldn't be doing for any finite representation.
timebonus = ((double)turncount + SCORETURNCOUNT) / turncount;
would be enough to guarantee the first expression never overflows
on an intermediate product (as long as the whole thing doesn't).
And in the second expression, yes, the intermediate products won't
overflow if each is cast to double, but the final product still
will when it's cast to int no matter what happens in the meantime.
But then again, while I stand by what I've said in theory, in
practice, I also tend to be pretty paranoid about casting and
parenthesization.