chrisokasaki / TradeMaximizer

Tool for finding the largest possible set of trades in BoardGameGeek-style "math trades".
MIT License
39 stars 14 forks source link

[feature request] Prioritizing offers within named lists #3

Closed JoannaFalkowska closed 2 years ago

JoannaFalkowska commented 2 years ago

It's been said many times that prioritizing doesn't do much; however I do believe that there still is a good case for prioritizing specifically within named lists.

The most important thing about named lists is that they should guarantee that I will receive only one copy of the game from the given named list.

The case would be:

When creating my "wanted games" list, I want to priorize the best editions/offers of a given specific game over worse editions/offers.

Moreover, if possible, I would like to be able to nest named lists inside of other named lists. I expect this to guarantee that I will only receive one game from each "top-level" named list that I place in my exchanges list.

// desired default syntax
(nickname) %WANTED_GAME: 1 2 ; 3 4 ; 5 6
(nickname) 987 : %WANTED_GAME

// desired alternative syntax 
(nickname) %WANTED_GAME_BEST: 1 2
(nickname) %WANTED_GAME_MEDIOCRE: 3 4
(nickname) %WANTED_GAME_WORST: 5 6
(nickname) %WANTED_GAME: %WANTED_GAME_BEST ; %WANTED_GAME_MEDIOCRE ; %WANTED_GAME_WORST
(nickname) 987 : %WANTED_GAME

// expected outcome: 
// my desired offers priority order will be respected, and also, if I get game 1, then I won't get any of 2 3 4 5 6. 

As a side note, there was a real-life case where I wanted to prioritize games like this using the current syntax, in which ; inside named lists definitions completely doesn't matter.

I tried to be smart (lol) about it and use duplicated entries in my named lists to game the existing algorithm, but it ended up badly:

(nickname) %WANTED_GAME_BEST_OFFERS: 1 2 3
(nickname) %WANTED_GAME_ALL_OFFERS: 1 2 3 4 5 6
(nickname) 987 : %WANTED_GAME_BEST_OFFERS; %WANTED_GAME_ALL_OFFERS

// expected outcome: 
// if I get game 1, then I won't get any of games 2 3 4 5 6.

// non-desirable, but at least understandable outcome:
// if I get game 1 from %WANTED_GAME_BEST_OFFERS list, I can also get games 4 5 6 from %WANTED_GAME_ALL_OFFERS list,
// but at least I still won't get any of games 2 3.

// actual real-life outcome:
// I got games 1 and 2, despite the fact that they are *both* on *both* lists.

I guess this happened because the algorithm doesn't check whether any game from the currently considered named list was already assigned, but rather whether this named list already had some game from it assigned, regardless of its contents (which feels completely backwards, but possibly it makes the whole thing more efficient?...)

I won't say this is a bug, since named lists with duplicates like this aren't really a supported feature, but it's an outcome that really surprised me.


I'm happy to, hm, try to raise a PR implementing this feature (I'm not a Java dev, and not familiar with this code), but I'd love a response to understand if this is something that is viable/doable at all, and to ensure this repo is still actually maintained.

chrisokasaki commented 2 years ago

In this example

(nickname) %WANTED_GAME_BEST_OFFERS: 1 2 3
(nickname) %WANTED_GAME_ALL_OFFERS: 1 2 3 4 5 6
(nickname) 987 : %WANTED_GAME_BEST_OFFERS; %WANTED_GAME_ALL_OFFERS

you couldn't have received both 1 and 2 if 987 was your only item. Presumably, you also had another entry like

(nickname) 988 : %WANTED_GAME_BEST_OFFERS; %WANTED_GAME_ALL_OFFERS

And then you won 1 for 987 (via %WANTED_GAME_BEST_OFFERS) and 2 for 988 (via %WANTED_GAME_ALL_OFFERS).

Moreover, if possible, I would like to be able to nest named lists inside of other named lists.

You can already do this, but that nesting by itself doesn't fix the problem.

 I expect this to guarantee that I will only receive one game from each "top-level" named list that I place in my exchanges list.

which is exactly what happened. However, receiving 1 from %WANTED_GAME_BEST_OFFERS does not stop you from receiving 2 from %WANTED_GAME_ALL_OFFERS if 1 and 2 happen to be on both lists.

So duplicate protection doesn't work the way you want it to. When you have a line like

(nickname) %WANTED_GAME_BEST_OFFERS: 1 2 3

think of it as creating an artificial user who wants 1, 2, or 3, and is offering a single artificial item that only you (and other artificial users created by you) are allowed to want. If the artificial user wins a game, the real prize actually goes to you. Because that artificial user is only offering a single item, they can only receive a single item (which then goes to you).

If your case, you have two artificial users who want the same things. Each of them can only receive one such item, but that means that YOU can receive two.

Unfortunately, I don't see an efficient way to provide the behavior that you want.

JoannaFalkowska commented 2 years ago

Hey, I don't want to push too much or anything, but it seems the entirety of your answer is about the examples from my side-note, while not really addressing the issue I originally raised, i.e. the fact that it is impossible to prioritize items inside of named lists? The only reason for the real-life outcome I mentioned is the very fact that this feature does not exist - as the only thing I actually tried to accomplish was prioritization.

And after reading your reply, I still don't understand why that feature cannot be done. The "artificial users" that you mentioned (and yeah, I can understand how this is the accurate mental model here) should still be able to prioritize the items that they want, shouldn't they?

chrisokasaki commented 2 years ago

Remember that the primary criteria is (virtually) always maximize the number of items traded. For this purpose, the artificial users do not count. And what it means to "not count" is that an artificial user counts the same whether it trades or not. The reason this matters is that, if the artificial user (or rather the artificial user's one artificial game) was treated in the same ways as a regular user/game then you could gain an advantage by making very long chains of artificial users/games. (Even a single such dummy item would give an advantage.)

To prevent that very easy way to illicitly manipulate the system, the best approach would be to make the cost of an artificial user be at least as big as the cost of an item not trading at all. Which means that items using such priorities would be at a double disadvantage. (Or triple disadvantage if there are nested artificial users.)

That's technically doable but I would expect it be used very rarely. Admittedly, that's a guess.

I also understand your particular use case is to use the duplicate protection for things that are not actually duplicates (such as copies of the same game but in different conditions), which is not the situation the mechanism was designed for.