Base objects (i.e. all consumables and all unenchanted wearables) use the same allocation table as in V. So when generating an item at any given depth, you create an allocation table containing all items which can possibly be generated at that depth, where each item has a number of entries according to its "commonness" rating. So an item with commonness 10 will have ten times as many entries as an item with commonness 1, and is therefore 10x more likely to be chosen. The commonness of items can vary with depth, so an item might have commonness 10 at depths 1 to 50, and commonness 5 at 51 to 75, and then not appear at all below that.
Ego items use the affix-and-theme system, which is based on the one in v4. Affixes also have the commonness-at-depth feature, and this is the basis for their place in the allocation table of affixes available to enchant any given item, but affixes have a lot more flexibility.
1. You can specify which items they may or may not be applied to (at each depth).
2. You can specify global minima and maxima for affixes of each type and goodness rating (i.e. each ego item shall have no more than one "bad" affix and at least one "arcane" affix, or whatever).
3. You can specify explicit conflicts (so the affix "Rusty" will never be applied to anything which already has the affix "Sharp").
Pyrel uses a system of "loot templates" to set out the parameters governing the generation of any particular item. So the "normal" template allows for the creation of any item, but has only a small chance of wearable items having any affixes (slightly higher at deeper levels). The "good" and "great" templates increase the number and quality of affixes.
But templates can do anything. A "food" template could specify that only mushrooms, rations and potions will be allowed in the allocation table. An "orc lair" template specifies that only certain types of Orcish armour and weapons can be generated, and most will have bad or average affixes.
Templates can override the global minima and maxima and the conflicts, so if you want a comedy "GHB" template to drop a Sharp Rusty Dagger of Silliness, you can make it so.
Most monsters will use the normal (/good/great) template most of the time, but some will use others too (e.g. a spellbook template for casters). You'll be able to put local overrides to a template into a particular monster drop profiles - so an archmage will never drop Magic For Beginners.
Dungeon floor items will also use the same system, and will also be able to override template values - so an orc lair at dl99 will have significantly better quality items than the standard orc lair template, while sticking to the same types ... it will presumably have tougher orcs too ...
Sorry, got carried away there. The basic answer to your question is that rarity in pyrel is relative - it depends what else is in the allocation table - i.e. what else can be generated at this depth. We'll use stats to tune the overall appearance of things.
Note that this probably won't apply to artifacts. Pyrel doesn't have artifacts yet, but they'll probably use the v4 system, where the check for artifact is done *before* the base item is chosen, rather than the V system where it's done afterwards. This makes tuning artifact drops much much easier. (Artifacts will still use an allocation table for rarities relative to each other, but they'll have absolute rarity relative to non-artifacts.)
Pyrel dev log, part 4
Collapse
X
-
How are item rarities handled in Pyrel? (The question was inspired by the recent talk about the awesomeness of the Oangband item rarity system.)Leave a comment:
-
Ok, I just noticed my code is -really- slow, which is no good for what it's meant to do. I won't post it again until it's faster.Leave a comment:
-
I'm familiar with it and "nose" is fully compatible with test cases based on it.
However, "unittest" is not quite good enough IMO. There are a few things which nose does much better (or which cannot be done with the bog standard "unittest" module):
a) It supports dynamically generated test cases. This is hugely useful because it lets you trivially generate real test cases rather than having to loop *inside* tests. Why is that important, you ask? For one, it lets you use tables for test cases where you still get real test feedback (failed, skipped, passed) for each individual test case. This is something which I specifically wanted for the grammar testing module because I wanted to highlight the presence of skipped test cases. Writing a full test case class (or method) definition for each skipped case would be silly when a simple list of ("input", "expected output") pairs will do.
b) It's a significantly larger amount of typing in the common case because of the "class" definition requirement. Nose requires no such thing -- it'll just find all functions named test_* and run those. (You can still use TestCase-derived classes if you really want all that extra typing.)
c) It requires use of its own special (verbosely named) "assert*" methods instead of just using the standard "assert".
d) More...?
Basically, "unittest" is very JUnit-esque and un-pythonic.Leave a comment:
-
Tests
Python 2.7 has a quite adequate test suite module builtin. Check out the unittest module. I do recommend installing nosetest and using it as a test runner. For instance, install nosetest and run 'nosetest -v' from the pyrel directory will recursively search for test cases (those derived from uniitest.testcase and nose's testcase base class) and run them. Additionally, it integrates with coverage and can generate nice HTML reports of test case code coverage.AnonymousHero contributed some nose tests to Pyrel, which you can use as inspiration.Leave a comment:
-
AnonymousHero contributed some nose tests to Pyrel, which you can use as inspiration.Leave a comment:
-
>>> calculatethis("+5")
5.0
>>> calculatethis("*3")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in calculatethis
ValueError: could not convert string to float: *3
>>> calculatethis("--7")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in calculatethis
ValueError: could not convert string to float: --7
>>> calculatethis("sin(10)+abs(5)")
4.45597888911
>>> calculatethis("sin")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in calculatethis
ValueError: could not convert string to float: sin
>>> calculatethis("sin(sin(sin(sin(3))))")
0.139730046912
The only surprising one is +5, I guess python's string to float parser allows starting with a +
I've never written a test suite in python, will look into it
Leave a comment:
-
Let's try some more crazy things (I'd suggest adding all the current examples to a little nose test suite -- have I suggested this before?EDIT: http://pastebin.com/VJihZXQG fixed 4-5*-5 and similar expressions
Everything not-completely-crazy should work now
:
(Some of these are obviously malformed.)Code:+5 => ? *3 => ? --7=> ? sin(10) + abs(5) => ? sin => ? sin(sin(sin(sin(3)))) => ?
I still think it's a mistake to conflate evaluation and parsing, but as long as you add a relatively complete test suite (did I mention that already?) any implementation complexity issues or mistakes should be fixable at a later time. Seriously, it's completely trivial to add a few tests.Leave a comment:
-
My guess is that where the skill-based algorithm doesn't match the V costs/fail rates, it will be those cases which are already known as anomalous - like the duplicate CMW etc. IOW I don't think we have to match V exactly, warts 'n' all.Leave a comment:
-
Absolutely. And if I turn out to have been wrong, feel free to say "I told you so."
Yes, of course. Though I doubt that Vanilla's spell costs and failure rates will match a skill system, at least not one with a simple cost algorithm. We should at least be able to get close though.Originally posted by MagnateI think I'd go so far as to say that our aim should be that the skill-based system should replicate V costs and expertise in the first instance. Variants can then use whichever system they like.Leave a comment:
-
I will reserve my objections, but I will not pursue it further until I have a chance to see how everything works in actuality. Sounds fair?Leave a comment:
-
Skill-based magic sounds really cool.
In Halls of Mist I'm going to have about the simplest possible skill-based spellcasting system. Learn spells by reading books. True spellcasting classes gain random bonus spells (from either Arcane or Divine list) on certain character levels. To cast any spell you have learned, roll 1d100 <= Spellcasting skill. All spells have a "mastery" level. When you reach that character level, you get to reroll a failed check once.
The exact same system is used for Magic Devices.Last edited by Mikko Lehtinen; November 29, 2012, 11:44.Leave a comment:
-
This is just brilliant.I don't personally think this statement can be supported, but frankly it doesn't matter because we aren't locked into a single system. If you want to have spells that use hardcoded variations depending on the player's class, then you can do that. Just make a different set of procs for determining failure rate / spell cost, and, yes, include the class-specific values in the procs' definitions.
I think I'd go so far as to say that our aim should be that the skill-based system should replicate V costs and expertise in the first instance. Variants can then use whichever system they like.I daresay most spells can simply use the skill-based system instead and only a few exceptions, if any, will need the more specific approach.
I just realised how easy it will be to reimplement Sangband in Pyrel ....Leave a comment:
-
Thanks Patashu - that is just fantastic, and perfect timing for me. After my discussion with Derakon I had moved to the point where I was just about to suggest using a 3rd-party library, but now we can try to avoid that.Sit down at home, review Calculator.cs for C#, hammer out bug fixes for 2 hours \o/ (Most mistakes were from not being used to python, a few were forgetting important things like the lookaheads/lookbehinds on unary operators so 2V2 doesn't turn into 21.414etc, and getting order of operations right for operators of the same precedence)
Pastebin.com is the number one paste tool since 2002. Pastebin is a website where you can store text online for a set period of time.
Everything not-completely-crazy should work now
>>> calculatethis("(8+5)*3")
39.0
>>> calculatethis("5+abs(5*abs(-5))")
30.0
>>>Leave a comment:
-
Sit down at home, review Calculator.cs for C#, hammer out bug fixes for 2 hours \o/ (Most mistakes were from not being used to python, a few were forgetting important things like the lookaheads/lookbehinds on unary operators so 2V2 doesn't turn into 21.414etc, and getting order of operations right for operators of the same precedence)
EDIT: http://pastebin.com/H07vMSEf fixed 4-5*-5 and similar expressions
Everything not-completely-crazy should work now
>>> calculatethis("(8+5)*3")Given your evaluator, what is the result of "(8+5)*3"? Or "5+abs(5*abs(-5))"?
39.0
>>> calculatethis("5+abs(5*abs(-5))")
30.0
>>>Last edited by Patashu; November 29, 2012, 21:40.Leave a comment:
Leave a comment: