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.
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".
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.
I know that part wasn't intentional, but I think rebatemaxxing>corpsemaxxing is nontrivially more compelling than the other way round.
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.
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:
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.
Apparently November 2024. Feels longer ago somehow.
I hadn't actually gone beyond breadth-first search though.
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 😅
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:
Tax Brackets
Depending on the total value of all goods you have, you determine a tax bracket:
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:
STRATEGY
There are three relatively-low tax rates accessible:
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:
LEADERBOARD
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?
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.
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.
Not 8, as you will shortly see...
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.
In order to fully benefit from the cockatrice rebate, they want to get as close to 30gp as possible without going over.
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.
Dividing the loot as evenly as possible among adventurers.
Monte Carlo distribution.