This is a follow-up to last week's D&D.Sci scenario: if you intend to play that, and haven't done so yet, you should do so now before spoiling yourself.

There is a web interactive here you can use to test your answer, and generation code available here if you're interested, or you can read on for the ruleset and scores.

RULESET

Goods

Each good is assigned a value for tax purposes:

GoodValue
Cockatrice Eye6gp
Dragon Head14gp
Lich Skull10gp
Unicorn Horn7gp
Zombie Arm2gp

Tax Brackets

Depending on the total value of all goods you have, you determine a tax bracket:

Total ValueTax Rate
<30gp20%
30-59gp30%
60-99gp40%
100-299gp50%
300gp+[1]60%

Your taxes due are equal to your Tax Rate multiplied by the total value of your goods.

So if you have two Lich Skulls (20gp), your tax rate is 20% and you will owe 4gp of taxes.

If you have three Lich Skulls (30gp), your tax rate goes up to 30% on the whole value of your goods, and you owe 9gp of taxes.

It is therefore very valuable to fall just below the various tax-bracket thresholds.

Benefits and Exemptions

There are a variety of special rules:

  • Clerics and Paladins are very popular for their undead-hunting, and the Church is very powerful and well-connected, leading to broad support for a Clerical tax exemption.  However, the Tax Assessment Exactors do not know who is a Cleric and who is not.  Instead, anyone who only has Lich Skulls and Zombie Arms is treated as a Cleric, and receives a special 10% tax rate regardless of the value of their goods.
  • The Unicorn Ranching Association has very good courtiers.  Anyone who has at least 5 Unicorn Horns is classified as a ranch, and their tax rate is cut to 25%.[2]
  • Cockatrices are a pest, and there is a bounty on killing them, implemented as a tax rebate.  The bounty is 6gp per cockatrice killed:
    • If you hand in N eyes, you will receive N/2 (rounded up) bounties.  So if two people each hand in 3 eyes, they will each receive 2 bounties, but if one person hands in 6 eyes they will receive only 3 bounties.
    • This reduces your taxes owed, but not below zero.  So if one person hands in 1 Cockatrice Eye and nothing else, they owe only 1.2gp of taxes before the bounty, and the 6gp bounty can only reduce that to 0 and no further.
  • The Royal Society for the Prevention of Cruelty to Dragons is opposed to the killing of dragons, and wants it very heavily taxed.  However, heroes have been able to convince the Tax Assessment Exactors that it is sometimes necessary to kill a dragon menacing a village.  As a compromise, one Dragon Head killed for personal reasons is taxed normally, but any subsequent Dragon Head is taxed at double the usual rate.
    • For example, if you hand in 3 Dragon Heads (42gp) and nothing else, you are in the 30% tax bracket.  However, you will pay 30% taxes on the first Dragon Head (4.2gp) but 60% taxes on each of the next two (8.4gp each), totaling 21gp of taxes.

STRATEGY

There are three relatively-low tax rates accessible:

  • 10% for anyone who can get classified as a Cleric (with only Lich Skulls and Zombie Arms)
  • 20% for anyone with <30gp of goods.
  • 25% for anyone who can get classified as a Ranch (with 5+ Unicorn Horns).

Optimal play aims to get each adventurer one of those low rates.  There are two ways to accomplish that, which produce nearly-identical end tax bills:

  • With a Cleric (getting a lower tax rate on Lich Skulls and Zombie Arms):
    • One adventurer brings 5 Lich Skulls and 7 Zombie Arms.[3]  They pay a 10% Cleric tax rate on 64gp of goods, for 6gp 4sp of tax.
    • One adventurer brings 2 Dragon Heads and nothing else.[4]  They pay a 20% tax rate on 28gp of goods, but doubled on the second head, for a total of 8gp 4sp of tax.
    • One adventurer brings 1 Cockatrice Eye, 1 Dragon Head, 1 Unicorn Horn, and 1 Zombie Arm.[5]  They pay a 20% tax rate on 29gp of goods, and receive a 6gp rebate for one confirmed Cockatrice, paying no tax.
    • The last adventurer brings 3 Cockatrice Eyes, 1 Dragon Head, and 6 Unicorn Horns.  They pay a 25% Ranch tax rate on 74gp of goods, and receive a 12gp rebate for two confirmed Cockatrices, paying 6gp 5sp of tax.
    • This adds up to 21gp 3sp of tax.
  • Without a Cleric (getting instead more-efficient handling of Cockatrice Eyes and Dragon Heads):
    • Each adventurer brings 1 Cockatrice Eye and 1 Dragon Head (getting the maximum Cockatrice bounty and no double-tax on Dragons).
    • Two adventurers each add 1 Unicorn Horn and 1 Zombie Arm.  They pay a 20% tax rate on 29gp of goods, and receive a 6gp rebate for one confirmed Cockatrice, paying no tax.
    • One adventurer adds 4 Zombie Arms.  They pay a 20% tax rate on 28gp of goods, and receive a 6gp rebate for one confirmed Cockatrice, paying no tax.
    • The last adventurer takes all remaining goods (5 Lich Skulls, 5 Unicorn Horns, and 2 Zombie Arms).  They pay a 25% Ranch tax rate on 109gp of goods, and receive a 6gp rebate for one confirmed Cockatrice, paying 21gp and 2sp (technically 2-3sp depending on rounding, but it happens that the Tax Assessment Exactors will round down in this case) in tax.[6]

LEADERBOARD

Player1234Total Tax
simon, abstractapplic1C 1D 1U 1Z (0.0)1C 1D 1U 1Z (0.0)1C 1D 4Z (0.0)1C 1D 5L 5U 2Z (21.25)21gp 2-3sp
Optimal Play1C 1D 1U 1Z (0.0)1C 1D 1U 1Z (0.0)1C 1D 4Z (0.0)1C 1D 5L 5U 2Z (21.25)21gp 2-3sp
Yonge1C 1D 1U (0.0)1C 1D 3Z (0.0)1C 1D 3Z (0.0)1C 1D 5L 6U 2Z23 gp 0 sp
DrJones5L 1Z (5.2)1D 3Z (4.0)3C 2D 6U 3Z (15.0)1C 1D 1U 1Z (0.0)24 gp 2sp
MaxwellPeterson2L 8Z (3.6)1C 1D 1U (0.0)1D 1U (4.2)3C 2D 3L 5U (19.25)27 gp 0-1 sp
Even Distribution[7]1C 1D 1L 2U 2Z (8.4)1C 1D 1L 2U 2Z (8.4)1C 1D 1L 2U 2Z (8.4)1C 1D 2L 1U 2Z (9.3)34 gp 5sp
Random Play????????56 gp 9 sp[7]

Congratulations to all players, particularly simon and abstractapplic (who managed to fully solve the problem and turn in a perfect answer, with the first one coming just in time for me to use it on my real-world taxes).

DATASET GENERATION

Adventurers come in groups of 1d6 (adventurers range from powerful adventurers who can go out solo to weak ones who need half-a-dozen companions).

An adventuring party goes out and hunts a variety of monsters, and then divides all monster parts as evenly as they can.  (This is why your dataset often contained multiple consecutive rows with very-similar goods).

This standard divide-evenly approach is much better than random (since it gets relatively-low tax brackets for everyone by dividing loot like that), but not close to optimal.

FEEDBACK REQUEST

As many players noticed, this scenario was deterministic: the same set of goods would always lead to the same taxes.  I think that this was very much justified given the premise: it's valuable to be able to realize that 'tax calculator' is an area where you shouldn't expect much-if-any randomness.  How did this feel from a player perspective?

As usual, I'm interested to hear any other feedback on what people thought of this scenario.  If you played it, what did you like and what did you not like?  If you might have played it but decided not to, what drove you away?  What would you like to see more of/less of in future?  Do you think the scenario was more complicated than you would have liked?  Or too simple to have anything interesting/realistic to uncover?  Or both at once?  Did you like/dislike the story/fluff/theme parts?  What complexity/quality scores should I give this scenario in the index?

  1. ^

    This tax rate existed in theory but was not derivable from the dataset, as the few people with enough goods to be in this tax bracket all had enough Unicorn Horns to get a lower rate below.

  2. ^

    Unlike the other tax rates, which are all divisible by 10%, this one can lead to fractional silver pieces if an odd # of gp is taxed at 25%.  This is resolved using python's round() function...my apologies to simon, who seems to have spent a while trying to figure out when it rounded up or down.

  3. ^

    Not 8, as you will shortly see...

  4. ^

    Since the Cleric cannot have a Dragon Head, someone else will need 2 of them...and since the second head will be double-taxed, it is very important to have as low a tax rate as you can on it.

  5. ^

    In order to fully benefit from the cockatrice rebate, they want to get as close to 30gp as possible without going over.

  6. ^

    The no-Cleric approach was meant to be a tiny bit worse than the with-Cleric approach, but a last-minute change that I didn't check properly, plus my confusion about how my own rounding worked, made it end up a tiny bit better instead.  Oops.

  7. ^

    Dividing the loot as evenly as possible among adventurers.

  8. ^

    Monte Carlo distribution.

New Comment
10 comments, sorted by Click to highlight new comments since:

Thanks aphyer, it was an interesting puzzle. I feel like it was particularly amenable to being worked out by hand relative to machine learning because of the determinism, rules easy to express in a spreadsheet, and simple subsets of the data (like the special cleric bracket) that could be built on.


This is resolved using python's round() function...my apologies to simon, who seems to have spent a while trying to figure out when it rounded up or down.

I don't recall that taking all that long really, I added one monster part type at a time (much like the main calculation but much easier since it's just binary and only for the special 5U+ tax bracket, so less interactions to worry about). 

Funny thing is, after seeing the calculation I still didn't understand why it rounded the way it does, until I asked an AI which explained that Python rounds to even numbers on ties (apparently called "banker's rounding" or "banker's rule"). The only source of non-integer values is U which provides a single factor of 2 in the number of silver pieces, resulting in all rounding being of numbers with 0.5 in the remainder of silver pieces and so applying this rule. The other monster parts provide 2 factors of 2 of each, not directly causing rounding but each one incrementing the tax by 1 silver (modulo 2 silver) and thus changing whether this rule rounds up or down (except 2nd and up dragon heads which provide 3 factors of 2) . 

Amusingly, I could have much more simply expressed this rounding rule as "if rounding is required, round to the nearest even number of silver pieces" but I wasn't thinking about it in terms of output, so missed this and expressed it much more complicatedly in terms of the input. Oops! 


    if taxed_goods['U'] >= 5:        tax_rate = min(tax_rate, 0.25)

Ah, makes sense that this would be its own special tax rate rather than the 50% bracket with a X2 discount (which amounts to the same thing in the end). That min though with the tax rate from other sources is another thing that was never triggered (and literally couldn't be triggered since 5U alone is enough to get to the 30% bracket and also prevents eligibilty for the cleric bracket). 

Ah, round-to-even makes sense, I should have realized that but was thinking it would be some messy floating-point thing.  (This gets used in banking because it's a form of rounding that doesn't systematically bias the resulting numbers in either direction).

And just now I thought, wait, wouldn't this sometimes round to 10, but no, an AI explained to apparently-stupid me again that since it's a 0.25 tax rate on integer goods, fractional gold pieces before rounding (where not a multiple of 0.1) can only be 0.25, which rounds down to 2 silver, or 0.75, which rounds up to 8 silver. Which makes it all the more surprising that I didn't notice this pattern.

Reflections on my performance:

There's an interesting sense in which we all failed this one. Most other players used AI to help them accomplish tasks they'd personally picked out; I eschewed AI altogether and constructed my model with brute force and elbow grease; after reaching a perfect solution, I finally went back and used AI correctly, by describing the problem on a high level (manually/meatbrainedly distilled from my initial observations) and asking the machine demiurge what approach would make most sense[1]. From this I learned about the fascinating concept of Symbolic Regression and some associated python libraries, which I eagerly anticipate using to (attempt to) steamroll similarly-shaped problems.

(There's a more mundane sense in which I specifically failed this one, since even after building a perfect input-output relation and recognizing the two best archetypes as rebatemaxxing and corpsemaxxing, I still somehow fell at the last hurdle and failed to get a (locally-)optimal corpsemaxxing solution; if the system had followed the original plan, I'd be down a silver coin and up a silver medal. Fortunately for my character's fortunes and fortune, Fortune chose to smile.)

Reflections on the challenge:

A straightforward scenario, but timed and executed flawlessly. In particular, I found the figuring-things-out gradient (admittedly decoupled from the actually-getting-a-good-answer gradient) blessedly smooth, starting with picking up on the zero-randomness premise[2] and ending with the fun twist that the optimal solution doesn't involve anything being taxed at the lowest rate[3].

I personally got a lot out of this one: for an evening's exacting but enjoyable efforts, I learned about an entire new form of model-building, about the utility and limits of modern AI, and about Banker's Rounding. I vote four-out-of-five for both Quality and Complexity . . . though I recognize that such puzzle-y low-variance games are liable to have higher variance in how they're received, and I might be towards the upper end of a bell curve here.

  1. ^

    For a lark, I also tried turning on all ChatGPT's free capabilities and telling it to solve the problem from scratch. It thought for ~30 seconds and then spat out a perfect solution; I spent ~30 further seconds with paperclips dancing before my eyes; I then discovered it hadn't even managed to download the dataset, and was instead applying the not-unreasonable heuristic "if abstractapplic and simon agree on an answer it's probably true".

  2. ^

    There's something fun about how "magic", "games", "bureaucracy", and "magical game bureaucracy" are equally good justifications for a "wait, what paradigm am I even in here?" layer of difficulty.

  3. ^

    I know that part wasn't intentional, but I think rebatemaxxing>corpsemaxxing is nontrivially more compelling than the other way round.

[-]kave62

I had terrible luck with symbolic regression, for what its worth.

Looked into it more and you're right: conventional symbolic regression libraries don't seem to have the "calculate a quantity then use that as a new variable going forward" behavior I'd have needed to get Total Value and then decide&apply a tax rate based on that. I . . . probably should have coded up a proof-of-concept before impugning everyone including myself.

[-]simon*20

Interesting link on symbolic regression. I actually tried to get an AI to write me something similar a while back[1] (not knowing that the concept was out there and foolishly not asking, though in retrospect it obviously would be). 

From your response to kave:

calculate a quantity then use that as a new variable going forward

In terms of the tree structure used in symbolic regression (including my own attempt), I would characterize this as wanting to preserve a subtree and letting the rest of the tree vary. 

Possible issues:

  1. If the coding modifies the trees leaf-first, trees with different roots but common subtrees aren't treated as close to each other. This is an issue that my own version would likely have had even if actually implemented[2]. However, I think PySR might at least partially address this issue (It uses genetic programming and the pictures in the associated paper seem to indicate that it is generating trees which at least sometimes preserve subtrees.) (Though the genetic programming approach is likely to make it hard to find the very simplest solutions in practice imo.[3])
  2. Even if you are treating trees with common subtrees as close to each other, if your evaluation of trees is only comparing final calculated values on the entire dataset, then it's hard to make the call  "I know this subtree is important even if I don't know the rest of the tree" because the results are not likely to be all that close unless you already have a reasonable guess for the rest of the tree. One partial (heh) answer might be to award part marks to solutions that work well for some of the data even if wildly off for other parts. Careful thinking might be required to do this in a way that doesn't backfire horribly, though. Hmm - or maybe you CAN do that in the existing paradigm by including if/then nodes in the tree? Say, a node that has three child nodes/subtrees, and chooses between two of them based on the value of the third? And then (in some genetic-programming-like approach perhaps) explore what happens if you copy those subtrees elsewhere, or existing subtrees into new if-then nodes?) (I can imagine the horrific unreadable mess already though...)

edited to add: it might be more appropriate to say that I had been planning on asking an AI to code something, but the initial prototype was sufficiently lame and gave me enough insight into the difficulties ahead I didn't continue. Claude chat link if anyone's interested.

edited to further add: hmm, what you are wanting ("new variable") is probably not just preserving a subtree, but for the mutation system to be able to copy that subtree to other parts of the tree (and the complexity calculator to not give to much penalty to that, I guess). Interestingly, it seems that PySR's backend at least (SymbolicRegression.jl) does have the capability to do this already, using a "form_random_connection!" mutation function that apparently allows the same subtree to appear as child of multiple parents, making a DAG instead of a tree. In general, I've been pretty impressed looking at SymbolicRegression.jl. Maybe other symbolic regression software is as feature-rich, but haven't checked.

  1. ^

    Apparently November 2024. Feels longer ago somehow.

  2. ^

    I hadn't actually gone beyond breadth-first search though.

  3. ^

    This is informed by (a tiny amount of) practical experience. After SarahNibs' comment suggested genetic programming would have worked on the "Arena of Data", I attempted genetic programming on it and on my initial attempt got ... a horrific unreadable mess. Maybe it wasn't "halfway decently regularized" but I updated my intuition to say: complicated ways to do things so greatly outnumber the simple ways that anything too reliant on randomness is not likely to find the simple way. 

Thank you for posting this. Getting a very good or pefect answer felt a lot easier than most, however getting from a very good answer to a perfect answer seemed more difficult than most. I identified a very good answer very quickly just by looking for combinations that were present in the dataset. It was then rather frustrating to make a lot of progress in untangling the rules and still being unable to find a better solution than the first one I found. Overalll I would rate it as difficulty = 2/5 playability 2/5 where 3 is an average D and D puzzle.

 

Started to work on this puzzle but gave up. Still want to show what I made. This picture is supposed to shows how tax increases as one item type increases. Each color line is an initial set of other items enumerated under 'key' ("Cockatrice" "Dragon"     "Lich"   "Zombie"). There is a picture like this for every taxable item type.

 

Honestly, I'm just proud of myself for managing to figure out that Cockatrice Eyes seemed to have a negative tax rate 😅

More from aphyer
Curated and popular this week
OSZAR »