battlecode / battlecode-hackathon

0 stars 0 forks source link

API brainstorming #1

Open kazimuth opened 6 years ago

kazimuth commented 6 years ago

What should the game's API look like for players?

kazimuth commented 6 years ago

Brainstorming:

Python

Outer loop

The top level of the player's code should maybe look like one of the following? It would start with:

from battlecode import *

and then would have one of a variety of types of loops, depending on what type of control the player wants:

# simplest loop; player code has control when it's their turn, blocks otherwise.
for state in run_game('mybot'):
    # `state` is an object containing the state of the world for a particular turn.
    # new turns would return new states, leaving old ones untouched.
    run_my_turn(state)

print('finished')
# or, alternatively:
game = Game('mybot')
for state in game.run():
    run_my_turn(state)
print('finished')
# sometimes it's the other team's turn, and players don't have any control, but they
# can still use the extra time for processing
# (instead of doing nothing for the whole enemy turn).
# code still blocks while waiting for the next turn.
for state in run_game('mybot', all_turns=True):
    if state.active_team == state.my_team:
        run_my_turn(state):
    else:
        intermediate_processing(state)
print('finished')
# entirely non-blocking; player code checks repeatedly for new messages from the server,
# never pauses waiting for things to happen
game = Game('mybot')
while not game.finished:
    state = game.poll()
    if state:
        if state.active_team == state.my_team:
            run_my_turn(state)
            state.submit_actions()
        else:
            intermediate_processing(state)
    else:
        # state is None, no new turn has happened
        background_processing()
print('finished')
# reduce copying overhead?
for state in run_game('mybot', copy_state=False):
    # state is the same object, updated repeatedly;
    # instead of a new copy of the state each round
    run_my_turn(state)

print('finished')
# current API; does not return copies of the game state, blocks
game = Game('mybot')
while True:
    game.next_turn()
    run_my_turn(game)
    if game.done:
        break
print('finished')

State management

on_my_team = state.entities(team=state.my_team)
for ent in on_my_team:
    ent.queue_move(ent.loc + (1,0))
    ent.queue_move(NORTH_WEST)

nearby = list(state.entities(loc=(5,6), within_sq=8))
can_move = set(state.entities(cooldown=0))

nearby_can_move_on_my_team = state.entities(
    team=state.my_team,
    loc=(5,6),
    within_sq=8,
    cooldown=0
)

by_id = state.entity(id=37)

by_id.queue_build(SOUTH_EAST)

holding = state.entity(loc=(5,6))
holding.queue_throw(holding.loc + (7,8))

JS / TS

const game = new Game('mybot');
game.on('my-turn', (state) => {
    runMyTurn(state);
});
game.on('their-turn', (state) => {
    intermediateProcessing(state);
});
game.on('end', (state) => {
    console.log('finished');
});
game.run();
runGame('mybot', async (game) => {
    // ES7 async iterators
    async for (const state of game.iter()) {

    }
    console.log('finished');
});
kazimuth commented 6 years ago

Players need to know about actions that succeeded / failed, for debugging purposes, as well as for cheaply updating i.e. spatial indices or machine learning models.

The API could look like:

# process last turn's actions
for action in state.successful_actions_last_turn:
    # action is a Move, Throw, etc. with attached information
    # can use it to update spatial indexes, train ML models, etc.

for action in state.failed_actions_last_turn:
    # update ML model, etc.

As well as showing failed actions in the viewer somehow.

We could even make the forward facing api be:

state.submit_action(Move(entityid, location))
# or
game.submit_action(Move(entityid, location))

So that things are symmetric, although that could be confusing.