Trash for benefit is a doozy. I think we could knock off Remodel, Expand, Upgrade, and Remake all at once, but it'll take a fair amount of new code to get there.
I've been working on a Dominion playing game myself, and indeed, I found this to be the case. All four cards follow the same logic, just different parameters: (1) How many cards to upgrade, (2) how much higher the cost of the new card may be, and (3) whether the cost of the new card must be exact or "up to."
in trash-for-benefit cards, you often want to trash good cards because the benefit is even better, while these methods will just consult the trashPriority list and probably trash an Estate, a Copper, or a Curse. So there should be a new method ("requireImproveCard"?), which asks the AI for a new kind of decision.
Possession is the only official card I have yet to touch, but aside from that, the trash-for-benefit cards were indeed the hardest to write A.I. for, and I still have a lot of tweaking to do. Actually, Remodel/Expand/Upgrade/Remake aren't that bad, because you can hook into your "buy a card" routine for choosing what new card to obtain.
But Salvager, Bishop, and Apprentice (probably in that order) are monsters. In case it helps, here's how I got something that probably isn't ideal but at least isn't embarrasing.
Common To All Three CardsWhen playing any of these three cards, I look at each card in my hand and evaluate it as a trash target. Doing this evaluation involves looking at three separate things:
(1) The benefit. How much extra buying power will I get if I trash this card. More importantly, what does that extra buying power allow me to get in return? More on this when I talk about the individual cards.
(2) The loss of the card in terms of the power of my deck. I have an "evaluate this deck" function that looks at your whole deck and tries to evaluate its power. This is a complicated analysis and (at present) very flawed, but basically it looks at what your average hand size is likely to be, which in turn helps it figure out what your average buying power, attacking power, etc., you're liable to have is, how many VPs you have, and so on. All those factors are weighted and averaged based on how far progressed the game is (for example, # of VPs is the overriding consideration in the end-game but barely at all in the early game).
(3) The "play value" of the card. This is an estimation of how useful the card is to actually play. I use the card's cost as a starting point, adjusting as necessary. For example, the play value of a Sea Hag is 4 normally but drops to -1 when the Curses run out. Cards with VPs (e.g., Island, Nobles, etc) are penalized, and "pure VP" cards have a -1 play value.
SalvagerSalvager is the easiest of the three because unless you trash a card whose supply pile is depleted, you can't make too bad a mistake. Worst case, you re-buy the card you trash. But you obviously want to improve upon no effect.
In calculating the benefit, I first look at how much money I anticipate having if I *don't* play the Salvager, then comapre that to how much I'll have if I *do* play the Salvager. Then I compare those two values to see if I will cross any particular critical threshold.
For example, if I anticipate having $7 without playing the Salvager and $9 if I do, that allows me to buy a Province, and so I consider that a good Salvager play. However, if I anticipate having $8 without playing the Salvager and $10 if I do, I consider that a poor Salvager play. Especially since Salvager allows multiple buys, I also look at thresholds such as $13 (Province + Duchy) and $16 (Province + Province) and other combinations. (Of course it's important not to look at actual fixed values, like $8 and $16, but at "the current cost of Province" and "the current cost of two Provinces," since Bridge and Princess can muck with those amounts.)
Anyway, the upshot is that I come out of this calculation not with a $ amount but a +VP amount. Ultimately I don't care if I'm getting +$2, I care what +VP change that causes. To make this more accurate, I then need to adjust this number by subtracting the # of VP that the trashed card would have given me.
I now balance this +/- VP consideration with the change to my "deck evaluation" and the "play value" of the card to trash. For the "play value," I'm particularly interested in seeing the difference between the play value and the card's base cost. That way dead Sea Hags (for example) really stand-out as strong Salvager candidates.
Then I assign at weight to all these heuristics (again valuing +/- VP high in the late game, low in the early game), and ultimately wind up with a number that is quite useless except when compared with the results of these same calculations for the other cards in my hand.
Whichever card results in the highest calculation gets trashed. If the numbers for all the cards in my hand are negative, I don't play the Salvager at all.
Obviously this is a HIGHLY fuzzy and error-prone calculation. But as my goal is to build something that approximates good play, rather than a provably optimal strategy, it suits my purposes fine.
ApprenticeEssentially the same calculation as Salvager, IF you are able to write a function that, given some number of +cards, will tell you how much extra spending power your deck is liable to produce with them. My such function tries to take into account what actions I'm liable to draw with those cards too, but it still ultimately tries to boil that consideration down into an estimate of increased buying power.
One complication is that you have to consider how many buys you have. With Salvager, if your expected buying power goes from $15 to $16, you don't have to worry about having the extra buy to afford the second Province, because it gives you that extra buy. With Apprentice, the crossing the $15 -> $16 threshold is only useful if you have an extra buy from somewhere else, or anticipate drawing a card that will give you +buy.
BishopActually this is probably the easiest of the three to code unless you want to take into account the benefit to the other players. I didn't, so I just look at the same three values: (1) +/- VP, (2) change in deck power, (3) play value of the card to trash.
(2) and (3) are calculated in the exact same way as for Salvager and Apprentice. Calculating (1) is a whole lot easier, because you don't have to look at cost thresholds or anything. You just look at the the number of VP chips you get and subtract any VPs attached to the card you're trashing.
In SummaryI hope the above helps. Even if you don't go to the lengths I did, maybe this rambling will help you think about an approach you do or do not wish to take to solving the problem. If you do decide to do something like what I did, recognize that it's a lot of work, and the results are fuzzy and imperfect. Probably what you want in a sim is something simpler anyhow. But for me, I'm kind of interested in seeing how far I can take this.