[PYTHON] Geschichte auf Programmebene von libscips ① (α0.0.1)

** Hinweis: Dieser Inhalt ist eine Migration der alten Site. Bitte haben Sie Verständnis dafür, dass der Inhalt seltsam sein kann. ** **. ** Hinweis 2 Dieser Inhalt ist eine Kopie aus kumitatepazurus Blog, um mehr Menschen über Ihr Tool zu informieren. ist. Benutze es! ** **.

Dieses Mal werde ich die Bibliotheks-Libscips erklären, die ich mache.

Erstellungsprozess

Unser zyo_sen-Team hat bisher agent2d (gliders2d) verwendet. Agent2d ist jedoch C ++ und unsere Spezialität (?) Ist Python, daher war es schwierig zu entschlüsseln. Und da es ursprünglich ein KI-Klassenzimmerteam war, wollte ich mit KI kämpfen. Aber es gibt nicht viele Informationen in C ++ ... was soll ich tun?

Zu dieser Zeit habe ich mir diese Bibliothek ausgedacht.

Die von agent2d verwendete Basis ist eine Bibliothek namens librcsc, die mit dem Server kommuniziert. Ich dachte, ich sollte es selbst machen. Glücklicherweise habe ich in der Vergangenheit eine Methode verwendet, die nicht auf agent2d basierte, also habe ich sie unter Bezugnahme darauf erstellt. Und was ich konnte

lib soccer communicate in python system In ** libscips ** war.

Diese Bibliothek ist

90% oder mehr der Gesamtsumme sind Programme wie die Fußballberechnung

Ich habe vor, es mit dem Ziel zu tun, das zu sagen.

Schau dir player.py an.

Schauen wir uns zuerst player.py an.

import json
from socket import socket, AF_INET, SOCK_DGRAM


class analysis:
    def __init__(self, error, analysis_log):
        self.error = error
        self.analysis_log = analysis_log

    def msg_analysis(self, text, log_show=None):
        text = text[0]
        if text[0] == "error":
            text = text[1].replace("_", " ")
            log = "\033[38;5;1m[ERR]" + (
                    "\033[38;5;13mno \033[4m" + self.no + "\033[0m ") * (
                          self.no != "") + "\t\033[4m" + text + "\033[0m"
            r = {"type": "error", "value": str(self.error.get(text) + (self.error.get(text) is None))}
        elif text[0] == "init":
            self.no = text[2]
            log = "\033[38;5;10m[OK]" + (
                    "\033[38;5;13mno \033[4m" + self.no + "\033[0m ") * (
                          self.no != "") + "\t\033[38;5;10minit msg.\t\033[4m" + "\033[38;5;11mleft team" * (
                          text[1] == "l") + \
                  "\033[38;5;1mright team" * (text[1] == "r") + "\033[0m\033[38;5;6m no \033[4m" + text[2] + "\033[0m"
            r = {"type": "init", "value": text[:-2]}
        elif text[0] == "server_param" or text[0] == "player_param" or text[0] == "player_type":
            log = "\033[38;5;12m[INFO]" + (
                    "\033[38;5;13mno \033[4m" + self.no + "\033[0m ") * (
                          self.no != "") + "\t\033[38;5;10m" + text[0] + " msg.\033[0m"
            r = {"type": text[0], "value": text[1:]}
        elif text[0] == "see" or text[0] == "sense_body":
            log = "\033[38;5;12m[INFO]" + (
                    "\033[38;5;13mno \033[4m" + self.no + "\033[0m ") * (
                          self.no != "") + "\t\033[38;5;10m" + text[0] + " msg. \033[38;5;9mtime \033[4m" + text[
                      1] + "\033[0m"
            r = {"type": text[0], "time": int(text[1]), "value": text[2:]}
        elif text[0] == "hear":
            log = "\033[38;5;12m[INFO]" + (
                    "\033[38;5;13mno \033[4m" + self.no + "\033[0m ") * (
                          self.no != "") + "\t\033[38;5;10mhear msg. \033[38;5;9mtime \033[4m" + text[1] + "\033[0m " + \
                  "\033[38;5;6mspeaker \033[4m" + text[2] + "\033[0m " + "\033[38;5;13mcontents \033[4m" + text[3] + \
                  "\033[0m"
            r = {"type": "hear", "time": int(text[1]), "speaker": text[2], "contents": text[3]}
        elif text[0] == "change_player_type":
            log = "\033[38;5;12m[INFO]" + (
                    "\033[38;5;13mno \033[4m" + self.no + "\033[0m ") * (
                          self.no != "") + "\t\033[38;5;10mhear msg. \033[0m"
            r = {"type": "change_player_type", "value": text[1]}
        else:
            log = "\033[38;5;12m[INFO]" + (
                    "\033[38;5;13mno \033[4m" + self.no + "\033[0m ") * (
                          self.no != "") + "\t\033[38;5;10mUnknown return value \033[0m\033[4m" + str(text) + "\033[0m"
            r = {"type": "unknown", "value": text}
        if log_show is None:
            log_show = r["type"] in self.analysis_log
        if log_show:
            print(log)
        return r

    def see_analysis(self, text, hit, log_show=None):
        if type(hit) == str:
            hit = [hit]
        text = text[0]
        for i in text[2:]:
            if i[0] == hit:
                if log_show is None:
                    log_show = hit in self.analysis_log or hit[0] in self.analysis_log
                if log_show:
                    print("\033[38;5;12m[INFO]\t\033[38;5;10mThere was a " + str(
                        hit) + " in the visual information.\033[0m")
                return i[1:]
        if log_show:
            print("\033[38;5;12m[INFO]\t\033[38;5;10mThere was no " + str(hit) + " in the visual information.\033[0m")
        return None


class player_signal(analysis):
    def __init__(self, ADDRESS="127.0.0.1", HOST="", send_log=False, recieve_log=False, analysis_log=("unknown",
                                                                                                      "init", "error")):
        self.ADDRESS = ADDRESS
        self.s = socket(AF_INET, SOCK_DGRAM)
        ok = 0
        i = 0
        print("\033[38;5;12m[INFO]\t\033[38;5;13mSearching for available ports ...\033[0m")
        while ok == 0:
            try:
                self.s.bind((HOST, 1000 + i))
                ok = 1
            except OSError:
                i += 1
        self.recieve_port = 1000 + i
        self.recieve_log = recieve_log
        self.send_log = send_log
        self.analysis_log = analysis_log
        self.no = ""
        self.player_port = 0
        self.error = {"no more player or goalie or illegal client version": 0}
        super().__init__(self.error, self.analysis_log)

    def __del__(self):
        self.s.close()

    def send_msg(self, text, PORT=6000, log=None):
        self.s.sendto((text + "\0").encode(), (self.ADDRESS, PORT))
        self.send_logging(text, PORT, log=log)

    def send_logging(self, text, PORT, log=None):
        if log is None:
            log = self.send_log
        if log:
            print("\033[38;5;12m[INFO]\t" + (
                    "\033[38;5;13mno \033[4m" + self.no + "\033[0m ") * (self.no != "") + "\033[38;5;10mSend msg.\t" +
                  "\033[38;5;9mPORT \033[4m" + str(self.recieve_port) + "\033[0m\033[38;5;9m → \033[4m" + str(PORT) +
                  "\033[0m\t\033[38;5;6mTEXT \033[4m" + text + "\033[0m")

    def send_init(self, name, goalie=False, version=15, log=None):
        msg = "(init " + name + " (goalie)" * goalie + " (version " + str(version) + "))"
        self.send_msg(msg, log=log)
        r = self.recieve_msg(log=log)
        self.player_port = r[1][1]
        return r

    def send_move(self, x, y, log=None):
        msg = "(move " + str(x) + " " + str(y) + ")"
        self.send_msg(msg, self.player_port, log=log)

    def send_dash(self, power, log=None):
        msg = "(dash " + str(power) + ")"
        self.send_msg(msg, self.player_port, log=log)

    def send_turn(self, moment, log=None):
        msg = "(turn " + str(moment) + ")"
        self.send_msg(msg, self.player_port, log=log)

    def send_turn_neck(self, angle, log=None):
        msg = "(turn_neck " + str(angle) + ")"
        self.send_msg(msg, self.player_port, log=log)

    def send_kick(self, power, direction, log=None):
        msg = "(kick " + str(power) + " " + str(direction) + ")"
        self.send_msg(msg, self.player_port, log=log)

    def recieve_msg(self, log=None):
        msg, address = self.s.recvfrom(8192)
        if log is None:
            log = self.recieve_log
        if log:
            print("\033[38;5;12m[INFO]" + (
                    "\033[38;5;13mno \033[4m" + self.no + "\033[0m ") * (
                          self.no != "") + "\t\033[0m\033[38;5;10mGet msg.\t\033[38;5;9mPORT \033[4m" + str(
                self.recieve_port) + "\033[0m\033[38;5;9m ← \033[4m" +
                  str(address[1]) + "\033[0m\t\033[38;5;6mIP \033[4m" + address[0] + "\033[0m")
        return json.loads(msg[:-1].decode("utf-8").replace("  ", " ").replace("(", '["').replace(")", '"]').
                          replace(" ", '","').replace('"[', "[").replace(']"', "]").replace("][", "],[").
                          replace('""', '"')), address

Es ist ein kurzes Programm mit 156 Zeilen. Ich werde dies eins nach dem anderen erklären.

Dieses Programm verwendet derzeit keine externe Bibliothek.

Informationen zur Verwendung der einzelnen Klassen finden Sie unter libscips WIKI.

Analyseklasse

Zunächst werde ich aus der Analyse erklären.

Normalerweise wird es verwendet, indem an das später beschriebene player_signal geerbt wird. Die Klassen sind nur der Übersichtlichkeit halber getrennt.

Also die allererste \ _ \ _ init \ __ Funktion

def __init__(self, error, analysis_log):
        self.error = error
        self.analysis_log = analysis_log

Dient zum vollständigen Vermeiden von NameError- und Pycharm-Warnungen. Das musst du nicht.

Die folgende msg_analysis ist ein Programm, das den Argumenttext zerlegt, die Befehlstypen klassifiziert, eine bedingte Verzweigung durchführt und ihn als benutzerfreundlichen Wörterbuchtyp zurückgibt.

Der Inhalt des Argumenttextes ist

(["see", "0", [["b"], "10", "0"], ...], (127.0.0.1, 6000))

So was.

def msg_analysis(self, text, log_show=None):
        text = text[0]
        if text[0] == "error":
            text = text[1].replace("_", " ")
            log = "\033[38;5;1m[ERR]" + (
                    "\033[38;5;13mno \033[4m" + self.no + "\033[0m ") * (
                          self.no != "") + "\t\033[4m" + text + "\033[0m"
            r = {"type": "error", "value": str(self.error.get(text) + (self.error.get(text) is None))}
        elif text[0] == "init":
            self.no = text[2]
            log = "\033[38;5;10m[OK]" + (
                    "\033[38;5;13mno \033[4m" + self.no + "\033[0m ") * (
                          self.no != "") + "\t\033[38;5;10minit msg.\t\033[4m" + "\033[38;5;11mleft team" * (
                          text[1] == "l") + \
                  "\033[38;5;1mright team" * (text[1] == "r") + "\033[0m\033[38;5;6m no \033[4m" + text[2] + "\033[0m"
            r = {"type": "init", "value": text[:-2]}
        # ---Der bedingte Zweig wird fortgesetzt---
        
        if log_show is None:
            log_show = r["type"] in self.analysis_log
        if log_show:
            print(log)
        return r

Es ist eine Funktion, bei der die bedingte Verzweigung von Befehlen für immer fortgesetzt wird.

Der variable Text enthält die zu analysierenden Daten. Der Grund, warum text = text [0] ganz am Anfang enthalten ist, ist, dass, wie Sie am Beispiel des oben geschriebenen Argumenttextes sehen können, die Originaldaten auch die Absenderinformationen enthalten, was ein Hindernis darstellt. Ich lösche es.

Das Variablenprotokoll wird angezeigt, wenn der Befehl auf log_show oder self.analysis_log angewendet wird.

Die Variable r enthält den zurückzugebenden Wörterbuchtyp.

Als nächstes folgt see_analysis. Es ist eine Funktion, die Informationen zurückgibt, die einfach zu handhaben sind, wenn überprüft wird, ob die angezeigten Informationen ein bestimmtes Objekt enthalten.

def see_analysis(self, text, hit, log_show=None):
    if type(hit) == str:
        hit = [hit]
    text = text[0]
    for i in text[2:]:
        if i[0] == hit:
            if log_show is None:
                log_show = hit in self.analysis_log or hit[0] in self.analysis_log
            if log_show:
                print("\033[38;5;12m[INFO]\t\033[38;5;10mThere was a " + str(
                    hit) + " in the visual information.\033[0m")
            return i[1:]
    if log_show:
        print("\033[38;5;12m[INFO]\t\033[38;5;10mThere was no " + str(hit) + " in the visual information.\033[0m")
    return None

Der Erste

if type(hit) == str:
        hit = [hit]

Da die Objektdaten aufgelistet sind, tritt ein Fehler auf, wenn die Verarbeitung im Zeichenfolgenformat ausgeführt wird. Im Fall des Zeichenfolgenformats wird es also in eine Liste konvertiert.

Wenn danach die Objektdaten durchsucht werden, werden die Objektinformationen zurückgegeben. Und wenn log_show True ist, ist die Protokollierung wirklich einfach.

player_signal Klasse

Als nächstes folgt die Klasse player_signal.

Die allererste \ _ \ _ init \ _ \ _ Funktion

    def __init__(self, ADDRESS="127.0.0.1", HOST="", send_log=False, recieve_log=False, analysis_log=("unknown","init", "error")):
        self.ADDRESS = ADDRESS
        self.s = socket(AF_INET, SOCK_DGRAM)
        ok = 0
        i = 0
        print("\033[38;5;12m[INFO]\t\033[38;5;13mSearching for available ports ...\033[0m")
        while ok == 0:
            try:
                self.s.bind((HOST, 1000 + i))
                ok = 1
            except OSError:
                i += 1
        self.recieve_port = 1000 + i
        self.recieve_log = recieve_log
        self.send_log = send_log
        self.analysis_log = analysis_log
        self.no = ""
        self.player_port = 0
        self.error = {"no more player or goalie or illegal client version": 0}
        super().__init__(self.error, self.analysis_log)

Ist eine Funktion, die anfängliche Einstellungen vornimmt und einen Port sichert. Setzen Sie das Argument in sich selbst

while ok == 0:
    try:
        self.s.bind((HOST, 1000 + i))
        ok = 1
    except OSError:
    	i += 1

Drehen Sie hier, bis der Betriebssystemfehler verschwindet, um nach einem freien Port zu suchen und diesen zu sichern.

Und

super().__init__(self.error, self.analysis_log)

Führen Sie nun die Fehlervermeidung init aus.

Die Funktion \ _ \ _ init \ _ \ _ sieht folgendermaßen aus.

Als nächstes folgt die Funktion \ _ \ _ del \ _ \ _.

def __del__(self):
    self.s.close()

Eine Funktion, die einen Port öffnet, wenn das Programm endet. Es wird automatisch freigegeben, aber nur für den Fall.

Als nächstes kommt die Funktion send_msg.

def send_msg(self, text, PORT=6000, log=None):
    self.s.sendto((text + "\0").encode(), (self.ADDRESS, PORT))
    self.send_logging(text, PORT, log=log)

Diese Funktion sendet eine Nachricht, sendet die Nachricht jedoch in der zweiten Zeile und zeigt sie an, wenn das Protokoll in der dritten Zeile angezeigt wird (rufen Sie send_logging auf, das später beschrieben wird). Das ist es.

Als nächstes folgt send_logging, das bereits erwähnt wurde. Ich verwende nur die print-Anweisung zum Protokollieren.

def send_logging(self, text, PORT, log=None):
    if log is None:
        log = self.send_log
    if log:
        print("\033[38;5;12m[INFO]\t" + (
                "\033[38;5;13mno \033[4m" + self.no + "\033[0m ") * (self.no != "") + "\033[38;5;10mSend msg.\t" +
              "\033[38;5;9mPORT \033[4m" + str(self.recieve_port) + "\033[0m\033[38;5;9m → \033[4m" + str(PORT) +
              "\033[0m\t\033[38;5;6mTEXT \033[4m" + text + "\033[0m")

Wenn log None ist, bezieht es sich auf das send_log, das von der Funktion \ _ \ _ init \ _ \ _ angegeben wird. Wenn es True ist, wird es angezeigt, und wenn es True ist, wird es ohne Fragen angezeigt.

Als nächstes kommt send_init. Wie der Name schon sagt, ist es eine Funktion, die nur einen Init-Befehl sendet.

def send_init(self, name, goalie=False, version=15, log=None):
    msg = "(init " + name + " (goalie)" * goalie + " (version " + str(version) + "))"
    self.send_msg(msg, log=log)
    r = self.recieve_msg(log=log)
    self.player_port = r[1][1]
    return r

Grob gesagt

Erstellen Sie eine Nachricht zum Senden in der zweiten Zeile

Senden Sie tatsächlich in der 3. Zeile (rufen Sie die Funktion send_msg auf)

Überprüfen Sie die Serverantwort in der 4. Zeile (rufen Sie die später beschriebene Funktion recieve_msg auf).

Überprüfen Sie den Server-Port in der 5. Zeile auf Verschiebungsbefehle usw. (Server-Port 6000 akzeptiert sicherlich nur Init-Befehle).

Die 6. Zeile gibt eine Antwort als Rückgabewert zurück.

Fühle mich wie.

Als nächstes folgen send_move, send_dash, send_turn, send_turn_neck, send_kick. Eine Funktion, die den Spieler bewegt.

Der Inhalt ist (send_move)

def send_move(self, x, y, log=None):
    msg = "(move " + str(x) + " " + str(y) + ")"
    self.send_msg(msg, self.player_port, log=log)

Es wird nur eine Nachricht erstellt und gesendet (ruft send_msg auf).

Zum Schluss recieve_msg. Wie der Name schon sagt, eine Funktion, die vom Server gesendete Informationen empfängt. Es ist auch meine beste Meisterfunktion.

def recieve_msg(self, log=None):
    msg, address = self.s.recvfrom(8192)
    if log is None:
        log = self.recieve_log
    if log:
        print("\033[38;5;12m[INFO]" + (
                "\033[38;5;13mno \033[4m" + self.no + "\033[0m ") * (
                      self.no != "") + "\t\033[0m\033[38;5;10mGet msg.\t\033[38;5;9mPORT \033[4m" + str(
            self.recieve_port) + "\033[0m\033[38;5;9m ← \033[4m" +
              str(address[1]) + "\033[0m\t\033[38;5;6mIP \033[4m" + address[0] + "\033[0m")
    return json.loads(msg[:-1].decode("utf-8").replace("  ", " ").replace("(", '["').replace(")", '"]').
                      replace(" ", '","').replace('"[', "[").replace(']"', "]").replace("][", "],[").
                      replace('""', '"')), address

Erklären

Empfangen Sie eine Nachricht vom Server in der zweiten Zeile

Zeigen Sie ggf. das Protokoll in den Zeilen 3-8 an

Korrigieren Sie in den Zeilen 9 bis 11 die Nachricht so, dass sie leicht als Rückgabewert behandelt werden kann.

Was großartig ist, ist, dass nur eine Zeile benötigt wird, um die Informationen einfach zu handhaben. (Die automatische Formatierung von Pycharm besteht nur aus 3 Zeilen.)

Der Grund für die Verwendung von json.loads ist, dass json sowohl Listen als auch Wörterbuchtypen wie unten gezeigt verarbeiten kann.

Also verwende ich json.loads, um von einer Liste von Zeichenfolgen in eine Liste zu konvertieren.

Da die empfangenen Informationen jedoch nicht in den Listentyp konvertiert werden können, werden sie nach der Konvertierung in den Listentyp in den Listentyp konvertiert.

[
	{
	"hello":"jobs"
	},
	[
	"contents"
	]
]

Dies ist die einzige Funktion in dieser Phase. Wenn dem Update Funktionen hinzugefügt werden, werde ich es jederzeit in ② und ③ erhöhen.

Schließlich

Ich möchte bald eine CSZP-Version erstellen ... Ich glaube nicht, dass sie in meinem Leben enden wird.

Ich dachte, ich schreibe es, aber die Schriftart des Artikels ist leicht zu lesen und herzerwärmend und perfekt zum Bloggen! Es ist ein tolles Spiel. Ich bin ein bisschen glücklich

Wir sehen uns eines Tages.


Bei persönlichen Fragen wenden Sie sich bitte hier.

https://forms.gle/V6NRhoTooFw15hJdA

Auch das Robocup Soccer Simulation League Team, an dem ich teilnehme, sucht Teilnehmer! Wir freuen uns von Ihnen zu hören, wenn Sie die Aktivität beobachten oder an der Aktivität teilnehmen möchten!

Klicken Sie hier für Details

Recommended Posts

Geschichte auf Programmebene von libscips ① (α0.0.1)
Die Geschichte von sys.path.append ()
Die Geschichte des Baus von Zabbix 4.4
Die Geschichte von Python und die Geschichte von NaN
Die Geschichte der Teilnahme an AtCoder
Die Geschichte des "Lochs" in der Akte
[Memo] Kleine Geschichte von Pandas, numpy
Die Geschichte des erneuten Bereitstellens des Anwendungsservers
Geschichte der Potenznäherung von Python
Die Geschichte des Exportierens eines Programms