Blef-team / blef_game_engine

The game engine API service for the game of Blef
GNU Affero General Public License v3.0
1 stars 0 forks source link

Issue 165 port api to python #169

Closed adrian-golian closed 3 years ago

adrian-golian commented 3 years ago

Re-write of the game engine in Python.

maciej-pomykala commented 3 years ago

From what I've seen so far, to preserve the behaviour of the engine, we need to:

Less important things (that don't seem to break anything but we can watch out for them):

adrian-golian commented 3 years ago

I was kind of surprised to see hands as a flat array, would be curious to see what R-to-Python quirk let this mistranslation happen.

You say

the old engine also allowed 0, but that's not intended

Well, as you see yourself, we are fully in position to make the calls on what's "not intended" and can be changed. I would use this reasoning to get rid of round=-1 and cp_nickname={} as either is a weird idiosyncrasy - what should actually be happening in a JSON payload is either 1) omit the key in the request, or 2) set it to null.

I would try to convince you to use our power to decide what is "not intended" and choose the bright, sunlit uplands of good JSON.

EDIT (23.06.2021 10:49 UTC+3): It at least makes sense if we had agreed on round=-1, as it's a "neither" kind of integer, but an empty object as a placeholder for a missing string is a different matter.

maciej-pomykala commented 3 years ago

I was kind of surprised to see hands as a flat array, would be curious to see what R-to-Python quirk let this mistranslation happen.

In the definition of draw_cards (line 83 in start_game.py and line 232 of play.py), I think we're looking to use append instead of extend to make more hierarchy 🦞.

cp_nicknames definitely makes more sense than null, let's just watch out for clients complaining after we change it.

Yeah, let's drop round = -1 too.

adrian-golian commented 3 years ago

append instead of extend

It should actually be an object, like:

hands[player_nickname] = [{"value": value, "colour": colour}, {"value": value, "colour": colour}]

to achieve the behaviour you described.

maciej-pomykala commented 3 years ago

In R, hands was an unnamed list (to make it a series instead of an object), where each player was a named list with two elements, their nickname and their cards, where the former was just a string and the latter was a data frame (data frames get translated into series of objects).

adrian-golian commented 3 years ago

each player was a named list

Yeah, there's not such class in Python. Object with player keys - is what the JSON you described is, right?

EDIT: Or was it an array of objects like this?

[{"nickname": [card1, card2, card3]}, {"nickname2": [card4]}]
maciej-pomykala commented 3 years ago

This is an example of a game in the old engine, with spaces added by me:

{
  "admin_nickname":"Annoying_Alligator",
  "public":false,
  "status":"Running",
  "round_number":2,
  "max_cards":11,
  "players":[
    {"nickname":"Annoying_Alligator","n_cards":2},
    {"nickname":"Woolly_Wolverine","n_cards":1}
  ],
  "hands":[
    {"nickname":"Annoying_Alligator","hand":[{"value":5,"colour":2},{"value":2,"colour":1}]},
    {"nickname":"Woolly_Wolverine","hand":[{"value":1,"colour":1}]}
  ],
  "cp_nickname":{},
  "history":[
    {"player":"Annoying_Alligator","action_id":0},
    {"player":"Woolly_Wolverine","action_id":88},
    {"player":"Annoying_Alligator","action_id":89}
  ]
}
maciej-pomykala commented 3 years ago

Other things:

{"admin_nickname": "Normal_Newt", "public": false, "status": "Running", "round_number": 1, "max_cards": 11, "players": [{"nickname": "Normal_Newt", "n_cards": 1}, {"nickname": "Nano_Nightelf", "n_cards": 1}], "hands": [], "cp_nickname": null, "history": [{"action_id": 0, "player": "Normal_Newt"}, {"action_id": 1, "player": "Nano_Nightelf"}, {"action_id": 2, "player": "Normal_Newt"}, {"action_id": 3, "player": "Nano_Nightelf"}, {"action_id": 4, "player": "Normal_Newt"}, {"action_id": 7, "player": "Nano_Nightelf"}, {"action_id": 10, "player": "Normal_Newt"}, {"action_id": 89, "player": {"nickname": "Normal_Newt", "n_cards": 1, "uuid": "10733c7f-57b0-49ed-92b6-9186052cbc56"}}]}
adrian-golian commented 3 years ago

As to wrapping error payloads in {"error": "message..."}, I'm already not that happy that I'm doing a lot of:

response_payload(400, "This action not allowed right now")

Instead of

parameter_error_payload("action_id", "This action not allowed right now")

So we can replace all these cases. While inside parameter_error_payload, instead of:

def parameter_error_payload(param_key, param_value, message=None):
    body = "Bad input value in '{}': {}".format(param_key, param_value)
    if message:
        body = "{}\n{}".format(body, message)
    return response_payload(400, body)

do:

def parameter_error_payload(param_key, param_value, message=None):
    body = "Bad input value in '{}': {}".format(param_key, param_value)
    if message:
        body = {"error": "{}\n{}".format(body, message)}             #  <------ CHANGED HERE
    return response_payload(400, body)

And same (wrapper) goes for request_error_payload, and internal_error_payload.

adrian-golian commented 3 years ago

For now (maybe indefinitely), while we want to use the Lambda editor to make code changes, each Lambda needs all these functions in its own module, so lots of functions are duplicated (risking inconsistency). If we refactor common functions, we'll need to zip and upload each lambda package individually.

maciej-pomykala commented 3 years ago

A public games request consumes 15 reads from DynamoDB. There are currently 196 game objects, with 4 games public. It might be because the whole database gets scanned whenever public games are requested, and every 4KB counts as one read. That would make sense, given that 196 games (many of them quite empty) would be around 60KB.

If that's true, we have at least these options:

The third option sounds best to me.

adrian-golian commented 3 years ago

All looks good to me as well. 🚀