[PYTHON] Histoire de niveau programme de libscips ① (α0.0.1)

** Remarque: Ce contenu est une migration de l'ancien site. Veuillez donc comprendre que le contenu peut être étrange. ** ** ** Note 2 Ce contenu est une copie du blog de kumitatepazuru pour informer davantage de personnes sur votre outil. est. Utilise le! ** **

Cette fois, je vais vous expliquer la bibliothèque libscips que je crée.

Processus de création

Notre équipe zyo_sen a utilisé agent2d (gliders2d) jusqu'à présent. Cependant, agent2d est C ++ et notre spécialité (?) Est python, donc c'était difficile à déchiffrer. Et comme il s'agissait à l'origine d'une équipe de classe d'IA, je voulais me battre avec l'IA. Mais il n'y a pas beaucoup d'informations en C ++ ... que dois-je faire?

À ce moment-là, j'ai créé cette bibliothèque.

La base utilisée par agent2d est une bibliothèque appelée librcsc qui communique avec le serveur. J'ai pensé que je devrais le faire moi-même. Heureusement, j'avais l'habitude d'utiliser une méthode qui n'était pas basée sur agent2d dans le passé, alors je l'ai créée en référence à cela. Et ce que j'ai pu faire

lib soccer communicate in python system Dans ** libscips ** était.

Cette bibliothèque est

90% ou plus du total est un programme tel que le calcul du football

J'ai l'intention de le faire dans le but de le dire.

Regardez player.py.

Tout d'abord, jetons un œil à player.py.

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

C'est un programme court de 156 lignes. J'expliquerai celui-ci un par un.

Ce programme n'utilise actuellement pas de bibliothèque externe.

Veuillez vous référer à libscips WIKI pour savoir comment utiliser chaque classe.

classe d'analyse

Tout d'abord, je vais expliquer à partir de l'analyse.

Normalement, il est utilisé en héritant de player_signal décrit plus loin. Les classes sont séparées uniquement pour des raisons de clarté.

Donc la toute première fonction \ _ \ _ init \ __

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

Est pour éviter complètement les avertissements NameError & Pycharm. Vous n'êtes pas obligé.

Le msg_analysis suivant est un programme qui décompose le texte de l'argument, classe les types de commandes, effectue un branchement conditionnel et le renvoie comme un type de dictionnaire facile à utiliser.

Le contenu du texte de l'argument est

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

Comme ça.

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]}
        # ---La branche conditionnelle continue---
        
        if log_show is None:
            log_show = r["type"] in self.analysis_log
        if log_show:
            print(log)
        return r

C'est une fonction dans laquelle le branchement conditionnel des commandes se poursuit pour toujours.

Le texte variable contient les données à analyser. La raison pour laquelle text = text [0] est inclus au tout début est que, comme vous pouvez le voir dans l'exemple de l'argument texte écrit ci-dessus, les données d'origine contiennent également les informations sur l'expéditeur, ce qui est un obstacle. Je l'efface.

Le journal des variables s'affiche lorsque la commande s'applique à log_show ou self.analysis_log.

La variable r contient le type de dictionnaire à renvoyer.

Vient ensuite see_analysis. Il s'agit d'une fonction qui renvoie des informations faciles à gérer si l'on vérifie s'il existe un objet spécifique dans les informations de visualisation.

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

la première

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

Étant donné que les données d'objet sont répertoriées, une erreur se produit si le traitement est effectué au format chaîne. Donc, dans le cas du format chaîne, il est converti en liste.

Après cela, si les données d'objet sont recherchées, les informations d'objet sont renvoyées. Et si log_show vaut True, c'est une fonction très simple à enregistrer.

classe player_signal

Vient ensuite la classe player_signal.

La toute première fonction \ _ \ _ init \ _ \ _

    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)

Est une fonction qui effectue les réglages initiaux et sécurise un port. Mettez l'argument en soi

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

Ici, tournez jusqu'à ce que l'erreur OS disparaisse pour rechercher et sécuriser un port libre.

Et

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

Exécutez maintenant l'initialisation d'évitement d'erreur.

La fonction \ _ \ _ init \ _ \ _ ressemble à ceci.

Vient ensuite la fonction \ _ \ _ del \ _ \ _.

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

Une fonction qui ouvre un port lorsque le programme se termine. Il sera publié automatiquement, mais juste au cas où.

Vient ensuite la fonction 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)

Cette fonction envoie un message, mais elle envoie le message sur la deuxième ligne et l'affiche lors de l'affichage du journal sur la troisième ligne (appelez send_logging décrit plus loin). C'est tout.

Vient ensuite send_logging, qui a été mentionné précédemment. J'utilise juste l'instruction d'impression pour me connecter.

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")

Si log est None, reportez-vous au send_log spécifié par la fonction \ _ \ _ init \ _ \ _ et affichez-le s'il a la valeur True.

Le suivant est send_init. Comme son nom l'indique, c'est une fonction qui envoie simplement une commande init.

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

Grosso modo

Créer un msg à envoyer en deuxième ligne

En fait envoyer sur la 3ème ligne (appelez la fonction send_msg)

Vérifiez la réponse du serveur sur la 4ème ligne (appelez la fonction recieve_msg décrite plus loin)

Vérifiez le port du serveur comme la commande move sur la 5ème ligne (le port du serveur 6000 n'accepte certainement que la commande init)

La 6ème ligne renvoie une réponse comme valeur de retour.

Se sentir comme.

Vient ensuite send_move, send_dash, send_turn, send_turn_neck, send_kick. Une fonction qui déplace le joueur.

Le contenu est (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)

Il crée et envoie simplement des msg (appelle send_msg).

Enfin recieve_msg. Comme son nom l'indique, une fonction qui reçoit des informations envoyées par le serveur. C'est aussi ma meilleure fonction de chef-d'œuvre.

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

Expliquer

Recevoir un message du serveur sur la deuxième ligne

Afficher le journal si nécessaire sur les lignes 3-8

Aux lignes 9 à 11, corrigez le message afin qu'il soit facile à gérer comme valeur de retour.

Ce qui est génial, c'est qu'il suffit d'une seule ligne pour rendre les informations faciles à gérer. (Le formatage automatique de Pycharm ne comporte que 3 lignes.)

La raison de l'utilisation de json.loads est que json peut gérer des listes ainsi que des types de dictionnaire, comme indiqué ci-dessous.

Donc, j'utilise json.loads pour convertir une liste de chaînes en une liste.

Cependant, étant donné que les informations reçues ne peuvent pas être converties en type de liste, elles sont converties en type de liste après avoir été converties en type de liste.

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

C'est la seule fonction à ce stade. Si des fonctions sont ajoutées dans la mise à jour, je l'augmenterai en ② et ③ à tout moment.

finalement

J'aimerais faire une version cszp bientôt ... Je ne pense pas que ça se terminera de mon vivant.

Je pensais l'écrire, mais la police de caractères de l'article est facile à lire et elle est réconfortante et parfaite pour les blogs! C'est un super match. Je suis un peu content.

À un de ces jours.


Pour des questions personnelles, veuillez contacter ici.

https://forms.gle/V6NRhoTooFw15hJdA

Aussi, l'équipe de la ligue de simulation de soccer Robocup à laquelle je participe est à la recherche de participants! Nous sommes impatients de vous entendre si vous souhaitez observer l'activité ou participer à l'activité!

Cliquez ici pour plus de détails

Recommended Posts

Histoire de niveau programme de libscips ① (α0.0.1)
L'histoire de sys.path.append ()
L'histoire de la construction de Zabbix 4.4
L'histoire de Python et l'histoire de NaN
L'histoire de la participation à AtCoder
L'histoire du "trou" dans le fichier
[Mémo] Petite histoire de pandas, stupide
L'histoire du remontage du serveur d'application
Histoire d'approximation de puissance par Python
L'histoire de l'exportation d'un programme