Hanabi-Live / hanabi-live

A web server that allows people to play Hanab, a cooperative card game of logic and reasoning.
https://hanab.live
GNU General Public License v3.0
179 stars 118 forks source link

API: Information about won game/max score #2922

Open bnordli opened 10 months ago

bnordli commented 10 months ago

I am compiling yearly statistics, and I use the https://hanab.live/api/v1/history-full/player endpoint to retrieve data. Currently, it is a bit hard to compute win ratio, since the maximum score is dependent on the variant.

Example response:

{
  "id": 1082228,
  "options": {
    "numPlayers": 3,
    "startingPlayer": 0,
    "variantID": 22,
    "variantName": "White (5 Suits)",
    "timed": false,
    "timeBase": 0,
    "timePerTurn": 0,
    "speedrun": false,
    "cardCycle": false,
    "deckPlays": false,
    "emptyClues": false,
    "oneExtraCard": false,
    "oneLessCard": false,
    "allOrNothing": false,
    "detrimentalCharacters": false
  },
  "seed": "p3v22s22",
  "score": 25,
  "numTurns": 53,
  "endCondition": 1,
  "datetimeStarted": "2023-12-19T21:40:10.547876Z",
  "datetimeFinished": "2023-12-19T21:54:36.704849Z",
  "numGamesOnThisSeed": 8,
  "playerNames": [
    "burger_king",
    "Espen",
    "Im2Good"
  ],
  "incrementNumGames": false,
  "tags": "",
  "users_tags": []
}

From what I understand, endCondition is always 1 when the game ended normally (i.e. deck depleted), and that should not change (for backwards compatibility).

I can always compute the max score by parsing variantName or variantID, but that is not a good solution, so I would suggest that the server would add either options.variantMaxScore: 25 or maxScore: true to each game response.

Zamiell commented 10 months ago

I can always compute the max score by parsing variantName or variantID, but that is not a good solution

why isn't it a good solution? it should be trivial now that @hanabi/game is uploaded on npm.

example code:

npm install @hanabi-live/game
import { getVariantByID } from "@hanabi-live/game";

const variant = getVariantByID(123);
console.log(variant.maxScore);
bnordli commented 10 months ago

That's a great solution when using JavaScript, yes, but not so much from other languages.

Zamiell commented 10 months ago

why not use javascript

bnordli commented 10 months ago

Because I am more familiar with other languages.

And I already had a framework ready (in C#) for massaging a JSON API into CSV.

Zamiell commented 10 months ago

there is other metadata than max score. what about a variant's future efficiency, or a variant's starting pace? should i bloat the API data returned with every game history with those statistics too? all of that metadata is independent of the actual game played. thus, for this reason, i dont want to put variant metadata into other various API endpoints.

at a high level, programming languages are about using the best tool for the job. you are not supposed to just use the one programming language that you already know as a hammer to accomplish every task. in this case, javascript can trivially query JSON APIs, trivially compute variant metadata, and trivially create CSV files, so there's no reason to use any other language. alternatively, you could try porting @hanabi/game to C#, but that does not sound fun, and you probably would not want to maintain such a solution in the long term, since variants will change.

bnordli commented 10 months ago

at a high level, programming languages are about using the best tool for the job. you are not supposed to just use the one programming language that you already know as a hammer to accomplish every task. in this case, javascript can trivially query JSON APIs, trivially compute variant metadata, and trivially create CSV files, so there's no reason to use any other language.

Hard disagree. No programming projects or tasks are performed in a vacuum. If a craftsman has a tool box which is good enough for the job, there is no reason to go to the tool shop to get the best tool each time. The other two tasks are trivial in most programming languages, while variant metadata is only trivial in JavaScript, after a pointer from you on what function to call. I can see npm is mentioned here, but I did not know of it beforehand, and the library looks like a tool shed when all I need is a hammer. (I know quite a few programming languages, tyvm, but JavaScript is quite far down on my familiarity list.)

there is other metadata than max score. what about a variant's future efficiency, or a variant's starting pace? should i bloat the API data returned with every game history with those statistics too? all of that metadata is independent of the actual game played. thus, for this reason, i dont want to put variant metadata into other various API endpoints.

I see your point, but with this logic, variantName is also superfluous, since it is simply a function of variantID, and directly retrievable from https://hanab.live/api/v1/variants. I think max score is a much more basic and useful information about the game than the other statistics, since it directly measures how well the players performed.

Would it be a better alternative to create a new endpoint, for instance https://hanab.live/api/v1/variants-info, to return metadata about variants?

To be clear, I have already worked around this issue by matching for (\d) Suits and special casing

No Variant (#0)
Ambiguous Mix (#20)
Dual-Color Mix (#7)
Ambiguous & Dual-Color (#184)

but this is not a future proof solution, and I just think the API would be more useful with some max score information.

Zamiell commented 10 months ago

with this logic, variantName is also superfluous, since it is simply a function of variantID, and directly retrievable

you are correct, but variantName is going to be removed in a future version of the API. since we are currently undergoing a variant revamp, it is only there for legacy purposes. (we are removing the both the name and the numerical id)

I have already worked around this issue by matching for (\d) Suits and special casing

as previous mentioned, your regex solution will break in the future, because that endpoint ultimately won't give you the name of the variant either

Would it be a better alternative to create a new endpoint

maybe, but it seems like kind of a waste of bandwidth when the end-user can somewhat easily compute this information on their own. at any rate, unless you are going to do a PR, i wouldn't expect a hypothetical endpoint like this to exist anytime soon.

Zamiell commented 10 months ago

also btw while i am thinking about it, your solution would also fail for any variant that does not have a stack size of 5, since that is an input into max-score. currently the only variant that doesn't meet this requirement is Sudoku 4 suit, so you could special case this. but there might be more variants in the future with a different stack size. so hopefully this starts to illustrate why special casing everything is kind of terrible and its better to use a single source of truth that will never get out of date, i.e. the actual official game logic that the website itself uses.

bnordli commented 10 months ago

Thank you for mentioning Sudoku 4 suit, I haven't played that yet, I will add a special case for that, too.

I absolutely agree there should be a single source of truth, but I think it is somewhat artificial to force usage of npm to get this information, and not letting the API stand on its own.

If anything, the npm library can easily get out of date if not maintained or regularly released, but the API should presumably always be on the most recent version (unless you run multiple servers, which I am not aware of).

Zamiell commented 10 months ago

the npm library can easily get out of date

well the idea is that i update the npm library whenever the game logic changes, which is pretty easy, just involves re-running the publishing script.

force usage of npm

i agree that we don't want to force usage of npm. you can do a pr if you like to add a new endpoint that provides variant metadata. its a non-trivial PR though, so unless you really, really don't like javascript/typescript, I would recommend using javascript for your relatively-small script that compiles yearly statistics, as javascript is a great scripting language that is perfectly suited to this task, as my two line code snippet above showcases.