GPN19:SPN/upload.py

aus dem Wiki des Entropia e.V., CCC Karlsruhe

Als Alternative zur Web-IDE kann man auch einen externen Editor verwenden und den code per REST-API hochladen. Leider gibt es hier noch einen Bug mit einem CSRF token. Als Workaround kann hierfür folgendes (etwas frickeliges) Python-Script verwendet werden, was sich genauso wie der Browser verhält. Hierfür muss man sich zunächst mit einem Browser einloggen und die session und parent id in die config-Datei im JSON-Format eintragen, die im gleichen Verzeichnis wie das Pythonscript liegen muss. Der eigentliche Quellcode, den man normalerweise in die Web-IDE eintippt, muss im gleichen Verzeichnis in der bot.cpp vorliegen. upload.py muss nur einmalig gestartet werden und läuft dann im Hintergrund. Immer dann wenn man die bot.cpp speichert, wird der aktuelle Stand automatisch hochgeladen.

upload.py:

#!/usr/bin/env python3

import os
import json
import requests
from bs4 import BeautifulSoup
from colorama import Fore
from colorama import Style

def upload():
    config = json.load(open('config'))

    get_url = 'https://schlangen.bytewerk.org/snake/edit/latest'
    cookies = {'sessionid': config['session_id']}

    r = requests.get(get_url, cookies=cookies)
    cookies['csrftoken'] = r.cookies['csrftoken']

    soup = BeautifulSoup(r.text, 'lxml')
    xcsrf = soup.select_one('input[name="csrfmiddlewaretoken"]')['value']
    code = open('bot.cpp').read()

    post_url = 'https://schlangen.bytewerk.org/snake/edit/save'
    form_data = {'action': 'run', 'code': code, 'comment': None, 'parent': config['snake_id']}
    r = requests.post(post_url, headers={'X-CSRFToken': xcsrf, 'X-Requested-With': 'XMLHttpRequest'}, json=form_data, cookies=cookies)

    print(r.text)

    print('waiting for compilation...')

    state = 'not_compiled'

    while state == 'not_compiled':
        r = requests.get('https://schlangen.bytewerk.org/api/v1/compile_state', cookies=cookies)
        j = json.loads(r.text)
        state = j['compile_state']

        for l in j['build_log']:
            fmt = f'{Fore.GREEN}' if not 'e' in l.keys() else f'{Fore.RED}'
            fmt_reset = f'{Style.RESET_ALL}'

            key = list(l.keys())[0]
            print('%s%s%s' % (fmt, l[key], fmt_reset), end='')


while True:
    try:
        os.system('inotifywait -e modify bot.cpp')
        upload()
    except KeyboardInterrupt:
        break

config:

{
  "session_id": "xxx",
  "snake_id": 1234
}

bot.cpp:

// You can use the entire C/C++ standard library, just add the relevant
// #includes. We recommend math.h ;-)

#include "usercode.h"

/*
 * This is your bot's startup function. Here you can set your snake's colors,
 * set up persistent variables, etc.
 */
bool init(Api *api)
{
    // remove the default color
    api->clearColors();

    // I'm green!
    api->addColor(40, 255, 0);
    api->addColor(20, 128, 0);
    api->addColor(10,  64, 0);
    api->addColor(20, 128, 0);

    // indicate successful startup. If anything goes wrong,
    // return false and we'll clean you up.
    return true;
}

/*
 * This function will be called by the framework on every step. Here you decide
 * where to move next!
 *
 * Use the provided Api object to interact with the world and make sure you set
 * the following outputs:
 *
 * - api->angle: Set your relative movement angle
 * - api->boost: Set this to true to move faster, but you will loose mass.
 *
 * The Api object also provides information about the world around you. See the
 * documentation for more details.
 */
bool step(Api *api)
{
    // let's start by moving in a large circle. Please note that all angles are
    // represented in radians, where -π to +π is a full circle.
    api->angle = 0.001;

    // check for other snakes
    for(size_t i = 0; i < api->getSegmentCount(); i++) {
        const IpcSegmentInfo &seg = api->getSegments()[i];

        if(!seg.is_self && seg.dist < 20) {
            // you can send log messages to your browser or any other viewer with the
            // appropriate Viewer Key.
            api->log("Oh no, I'm going to die!");
            break;
        }
    }

    // finding food is quite similar

    // Signal that everything is ok. Return false here if anything goes wrong but
    // you want to shut down cleanly.
    return true;
}