[PYTHON] Dessinez un ensemble de Mandelbrot avec Brainf * ck

Dessinez un ensemble de Mandelbrot avec Brainf * ck

introduction

mandelbrot(color)

Spécification du langage Brainf * ck

Brainf * ck a 8 types d'instructions

L'explication est empruntée à la section Brainfuck (ja) de wikipedia.

Brainf * ck a un tableau d'au moins 30000 cellules et un flux d'octets d'entrée / sortie, qui peuvent être manipulés pour réaliser le traitement.

Problèmes de portabilité Brainf * ck

Selon la version anglaise de wikipedia Brainfuck (en), il y a quatre problèmes avec la portabilité de Brainf * ck:

Cell size

La taille de la cellule pointée par le pointeur est généralement de 8 bits, mais il existe également d'autres tailles.

Il semble y avoir des implémentations 16 bits, 32 bits ou autres.

Array size

Dans l'implémentation classique, le tableau se compose de 30000 cellules, avec le premier pointeur sur le bord gauche (position 0).

Cependant, il semble y avoir une implémentation qui a une cellule sur le côté gauche (l'indice du tableau est négatif).

En outre, il semble qu'il existe des implémentations qui provoquent une erreur lors du déplacement au-delà de la plage du tableau, des implémentations qui étendent le tableau, des implémentations qui se déplacent dans la direction opposée du tableau et des implémentations qui ne vérifient pas la plage (mousse).

End-of-line code

Non limité à Brainf * ck, mais le code de saut de ligne est souvent différent.

La plupart traitent LF (\ n) comme un code de saut de ligne, mais il existe des implémentations (ou OS d'exploitation) qui utilisent CR + LF ( \ r \ n) ou CR uniquement (\ r).

End-of-file behavior

Il semble qu'il y ait trois comportements (principalement?) Lors de la lecture de la fin du fichier, en fonction de l'implémentation.

Implémentation du compilateur Brainf * ck

Afin de créer un programme avec certaines fonctions avec Brainf * ck, j'ai pensé qu'il serait préférable d'ajouter des fonctions au compilateur, j'ai donc créé le compilateur bf2c en langage C. C'est un compilateur, ou plus exactement un traducteur qui traduit la source de Brainf * ck en C. L'optimisation du code est laissée au compilateur C.

Il a les caractéristiques suivantes

Les options suivantes peuvent être spécifiées pour bf2c.

Même si vous spécifiez «-2» ou «-4», cela dépend du compilateur C si la taille de la cellule est de 16 bits ou 32 bits. Utilisez respectivement «short» et «int »comme spécifications.

Si vous ne spécifiez pas les informations de version avec -V, les informations par défaut sont incorporées en fonction du nom du fichier source, de la date actuelle, du nom de connexion, etc. Pour le nom de connexion, vérifiez les variables d'environnement dans l'ordre de LOGNAME ʻUSER`` LNAME ʻUSERNAME, et utilisez celle avec la première chaîne de caractères non vide (la variable d'environnement à vérifier est python(Reportez-vous à getpass.getuser ()). Si rien n'est défini, utilisez «" noname "».

Le flash forcé de -F a été ajouté pour qu'il puisse être affiché caractère par caractère car le programme de l'ensemble de Mandelbrot que j'ai créé au début était assez lent et je n'étais pas sûr s'il fonctionnait avec un flash ligne par ligne. Fait.

La commande d'exécution compilée accepte les options suivantes

Cependant, si le fichier source Brainf * ck n'utilise pas la commande ,, vous ne pourrez pas spécifier les options de fichier d'entrée et de message d'entrée.

De même, vous ne pouvez pas spécifier les options du fichier de sortie si la commande . n'est pas utilisée.

En outre, les informations de version et le message d'aide seront affichés de la même manière.

Si le fichier d'entrée et le message d'entrée ne sont pas spécifiés, l'entrée standard est lue. De même, si aucun fichier de sortie n'est spécifié, il écrit dans la sortie standard.

Si l'argument par défaut de la commande d'exécution a été spécifié au moment de la compilation (-I ou -M ou -O), un seul argument peut être spécifié sans aucune option.

Introduction au développement de programmes avec Brainf * ck

Les instructions de Brainf * ck sont simples, mais vous pouvez en combiner plusieurs pour faire un travail complet. Il y a différentes descriptions de ce niveau dans l'article de Qiita, je vais donc l'expliquer brièvement rapidement.

Définition de la valeur

Vous ne pouvez pas définir la valeur directement dans la cellule, mais vous pouvez définir n'importe quelle valeur en combinant «+» et «-».

Vous pouvez effacer la valeur de la cellule sur laquelle vous pointez actuellement en faisant quelque chose comme «[-]».

Traitement en boucle

Pour réaliser la fonction équivalente à l'instruction «while» d'un langage général, il est possible de la réaliser en préparant une cellule pour le contrôle. Par exemple, si la cellule à gauche de l'une est la cellule de contrôle, vous pouvez traiter la boucle en utilisant <[> (Traitement dans la boucle) <]>. Cependant, à la fin de "Traitement en boucle", le pointeur doit être ramené à la position de départ.

De même, si le compteur est sur le côté gauche, la fonction équivalente à l'instruction for qui effectue une boucle le nombre de fois spécifié sera <[-> (traitement dans la boucle) <]>. Dans ce cas, le compteur a été décrémenté pendant le traitement et est à 0 à la fin de la boucle. Il doit être copié à l'avance pour éviter de détruire le compteur.

Déplacer, ajouter, copier des données

Par exemple, pour ajouter la valeur pointée par le pointeur actuel à la valeur à côté, vous pouvez utiliser [-> + <]. 1 Appliquer ceci, par exemple, pour le doubler serait [-> ++ <].

Pour ajouter à deux endroits (sur le côté droit et sur le côté droit), utilisez [-> +> + <<].

Vous pouvez copier la valeur, en supposant que le côté droit est entièrement à 0. Tout d'abord, entrez la même valeur une à côté de un et deux à côté d'elle comme [-> +> + <<]. Après cela, si vous passez aux deux suivants et ajoutez la valeur à la position d'origine (version destructive) [>>, vous pouvez copier la valeur [- << +] <<.

Le traitement de Brainf * ck est fondamentalement destructeur, donc la copie de données est très fréquente.

Branche conditionnelle

Le branchement conditionnel tel que l'instruction ʻif` dans les langages généraux est également réalisé sous la forme d'une boucle ponctuelle.

Par exemple, si vous voulez exécuter (alors) le processus 1 si la valeur actuelle est différente de 0, et (sinon) le processus 2 si elle est 0, préparez une cellule en tant que travail et utilisez-la comme indicateur. Ici, nous utiliserons le suivant comme indicateur pour else et supposerons que la valeur est 0.

Tout d'abord, définissez l'indicateur else sur 1 > + <. Et boucle avec la valeur actuelle [. Si la valeur actuelle est différente de zéro, elle entrera dans la boucle, et si elle est égale à 0, elle n'entrera pas dans la boucle. À l'intérieur de la boucle, effacez la valeur actuelle à 0, [-], et effacez également l'indicateur else > - <. Après cela, le processus 1 est exécuté et la boucle est quittée ].

Puis boucle basée sur l'indicateur else > [. Effacez le drapeau pour else, -, exécutez le processus 2 et quittez la boucle] <.

C'est le même processus que ʻif`. À la fin du processus, la valeur actuelle et l'indicateur sont 0.

En résumé, `> + <[[-]> - <(Process 1)]> [- (Process 2)] <ʻIl ressemble à ceci.

Programmation Brainf * ck avec macros

Si vous assemblez le contenu ci-dessus, vous serez en mesure d'effectuer divers processus.

Mais honnêtement, vous ne pouvez pas le faire, non?

Par conséquent, j'ai décidé de préparer une certaine quantité de traitement cohésif sous forme de macro et de l'utiliser pour la programmation.

Pour le moment, considérons une macro qui appelle une fonction (ou une méthode) et renvoie l'instruction Brainf * ck correspondante.

Macro de base

J'ai écrit la macro en Python pour le moment.

Par exemple, une macro qui déplace un pointeur ressemble à ceci.

def move_ptr(pos: int) -> str:
    """
Déplacer le pointeur

    >>> move_ptr(0)
    ''
    >>> move_ptr(2)
    '>>'
    >>> move_ptr(-3)
    '<<<'
    """
    return ">" * pos if 0 <= pos else "<" * (-pos)

Si vous faites cela, la macro (provisoire) qui traite à la position spécifiée et revient à la position d'origine une fois terminé ressemblera à ceci.

def exec_pos(pos: int, statement: str) -> str:
    "Exécuter le traitement à la position spécifiée"
    return move_ptr(pos) + statement + move_ptr(-pos)

Cependant, la programmation d'une combinaison de macros peut entraîner un traitement inutile. Par exemple, un processus comme ʻexec_pos (3, "+") + exec_pos (4, "-") ʻoutmet le résultat>>> + <<< >>>> - <<<<. Je vais finir. Le <<< >>> sur le chemin est inutile, n'est-ce pas?

Donc, nous allons d'abord créer quelques macros de base.

import re


def delete_useless(statement: str) -> str:
    """
Mouvement inutile/Décalage et suppression des calculs

    >>> delete_useless("+++--<<>>>")
    '+>'
    >>> delete_useless("---++>><<<")
    '-<'
    >>> delete_useless(">++++[-][-]")
    '>[-]'
    >>> delete_useless(">--[-]++[-]")
    '>[-]'
    """
    while True:
        if "<>" in statement:
            #Compenser les mouvements inutiles, partie 1
            statement = statement.replace("<>", "")
            continue
        if "><" in statement:
            #Compenser les mouvements inutiles, partie 2
            statement = statement.replace("><", "")
            continue
        if "+-" in statement:
            #Compenser l'addition et la soustraction inutiles, partie 1
            statement = statement.replace("+-", "")
            continue
        if "-+" in statement:
            #Compenser l'addition et la soustraction inutiles, partie 2
            statement = statement.replace("-+", "")
            continue
        if "+[-]" in statement or "-[-]" in statement:
            #Supprimer l'addition / soustraction avant la suppression de zéro
            statement = re.sub(r'[-+]+\[-\]', "[-]", statement)
            continue
        if "[-][-]" in statement:
            #Plusieurs zéro efface à la fois
            statement = statement.replace("[-][-]", "[-]")
            continue
        break
    return statement


def delete_useless_all(statement: str) -> str:
    """
Mouvement inutile/Décalage et suppression des calculs. Supprimer le dernier processus inutile

    >>> delete_useless_all("[+>-<]>++++[-][-]")
    '[+>-<]'
    >>> delete_useless_all("[-<+>][-]>>++")
    '[-<+>]'
    >>> delete_useless_all("[-<+>][-]<<--")
    '[-<+>]'
    """
    statement = delete_useless(statement)
    while statement:
        if statement[-1] in "-+><":
            #À la fin"+" "-" ">" "<"Est supprimé
            statement = re.sub(r'[-+><]+$', "", statement)
            continue
        if statement.endswith("[-]"):
            #À la fin"[-]"Est supprimé
            statement = re.sub(r'\[-\]$', "", statement)
            continue
        break
    return statement


def block_of(*statements: str) -> str:
    """
Combinez plusieurs instructions

    >>> block_of("[", "-", "]")
    '[-]'
    """
    return delete_useless("".join(statements))


def program_of(*statements: str) -> str:
    source = delete_useless_all("".join(statements))
    source = re.sub(r'(.{1,72})', "\\1\n", source)
    return source

D'une manière ou d'une autre, il y a une logique folle, mais je m'en fiche car c'est un code jetable de toute façon. Le nom de la fonction n'est pas clair non plus, mais je ne peux pas m'en empêcher car je ne comprends pas l'anglais.

En bref, il existe des macros qui suppriment les mouvements inutiles et les calculs faciles à comprendre, et bloquent les macros qui facilitent l'écriture de macros ensemble.

En utilisant ceux-ci, le précédent ʻexec_pos` peut également être réécrit comme suit.

def exec_pos(pos: int, statement: str) -> str:
    """
Exécuter le traitement à la position spécifiée

    >>> exec_pos(3, "+")
    '>>>+<<<'
    >>> exec_pos(3, "<+>")
    '>>+<<'
    """
    return block_of(
        move_ptr(pos),
        statement,
        move_ptr(-pos)
    )

Macro de définition de valeur

La macro de définition de valeur ressemble à ce qui suit.

def inc_pos(pos: int) -> str:
    """
Incrémenter la position spécifiée

    >>> inc_pos(2)
    '>>+<<'
    """
    return exec_pos(pos, "+")


def dec_pos(pos: int) -> str:
    """
Décrémenter la position désignée

    >>> dec_pos(3)
    '>>>-<<<'
    """
    return exec_pos(pos, "-")


def clear_pos(pos: int) -> str:
    """
Effacer la position spécifiée

    >>> clear_pos(3)
    '>>>[-]<<<'
    """
    return exec_pos(pos, "[-]")


def set_value(pos: int, value: int) -> str:
    """
Définir la valeur spécifiée

    >>> set_value(2, 3)
    '>>[-]+++<<'
    >>> set_value(2, -1)
    '>>[-]-<<'
    """
    (op, value) = ("+", value) if 0 < value else ("-", -value)
    return block_of(
        clear_pos(pos),
        exec_pos(pos, op * value)
    )

La logique de set_value semble être un peu folle, mais cela ressemble à ceci en partant du principe que le travail ne peut pas être utilisé.

Par exemple, si vous souhaitez définir la valeur initiale (tous les 0 sur le côté droit peuvent être utilisés pour le travail), vous pouvez la définir avec un peu plus de réflexion.

import math


def _init_value_sub(value: int) -> str:
    "Réglage de la valeur initiale.Prémisse qu'il peut être utilisé pour le travail après le prochain"
    (op1, op2) = ("+", "-")
    if value < 0:
        value = -value
        (op1, op2) = ("-", "+")
    if value < 16:
        return op1 * value
    len0 = value
    str0 = op1 * value
    xmin = int(math.sqrt(value))
    xmax = int(math.ceil(value / 2.0))
    for x in range(xmin, xmax + 1):
        strx = _init_value_sub(x)
        lenx = len(strx)
        # len0 = x * y1 +Divisé en forme c1
        y1 = value // x
        c1 = value % x
        len1 = lenx + y1 + c1 + 7
        if len1 < len0:
            len0 = len1
            str0 = ">" + strx + "[<" + op1 * y1 + ">-]<" + op1 * c1
        if c1 != 0:
            # len0 = x * y2 -Divisé en forme c1
            y2 = y1 + 1
            c2 = x - c1
            len2 = lenx + y2 + c2 + 7
            if len2 < len0:
                len0 = len2
                str0 = ">" + strx + "[<" + op1 * y2 + ">-]<" + op2 * c2
    return str0


def init_value(pos: int, value: int) -> str:
    """
Réglage de la valeur initiale.
Prémisse qu'il peut être utilisé pour le travail après le prochain.Soyez prudent dans l'ordre d'initialisation
    """
    return delete_useless(exec_pos(pos, _init_value_sub(value)))

Cependant, en supposant que vous traduisez en langage C puis appliquez l'optimisation, il se peut que set_value (), qui semble fou, soit plus efficace.

Macro de boucle

C'est un système en boucle.

def while_loop(pos: int, *statements: str) -> str:
    """
boucle while.
    >>> while_loop(3, ">>+<<")
    '>>>[<+>]<<<'
    """
    return block_of(
        exec_pos(pos, "["),
        block_of(*statements),
        exec_pos(pos, "]")
    )


def for_loop(pos: int, *statements: str) -> str:
    """
répétition.Version destructrice
    >>> for_loop(3, "+")
    '>>>[-<<<+>>>]<<<'
    """
    return block_of(
        exec_pos(pos, "[-"),
        block_of(*statements),
        exec_pos(pos, "]")
    )


def for_safe(pos: int, work1: int, statement: str) -> str:
    """
répétition.Version non destructive(Cependant, ne mettez pas à jour la référence à pos pendant la boucle.)
    >>> for_safe(3, 4, "+")
    '>>>[->+<<<<+>>>]>[-<+>]<<<<'
    """
    return block_of(
        for_loop(pos, inc_pos(work1), statement),
        move_data(work1, pos)
    )

La version non destructive de la boucle for est détruite puis renvoyée à la fin.

Si vous avez deux œuvres, vous pouvez copier la valeur, puis effectuer une boucle à la destination de la copie, mais pour le moment, vous n'avez qu'à regarder la valeur dans la boucle, c'est donc la première étape.

Copie / macro mobile

Déplacer ou copier des données.

def move_data(source: int, destination: int) -> str:
    "Mouvement d'1 octet/Ou ajouter."
    return for_loop(source, inc_pos(destination))


def copy_data(source: int, destination: int, work1: int) -> str:
    "Copie 1 octet."
    return block_of(
        clear_pos(destination),
        for_safe(source, work1, inc_pos(destination))
    )


def override_data(source: int, destination: int) -> str:
    "Mouvement d'1 octet."
    return block_of(
        clear_pos(destination),
        move_data(source, destination)
    )


def swap_data(target1: int, target2: int, work1: int) -> str:
    "Permutation des valeurs entre 1 octets"
    return block_of(
        move_data(target1, work1),
        move_data(target2, target1),
        move_data(work1, target2)
    )

Macro de branche conditionnelle

Une simple macro d'instruction ʻif`.

Un travail est nécessaire pour joindre la clause else, donc pour le moment, seulement alors.

def if_nz_then(pos: int, then_statement: str) -> str:
    "if_Version destructrice de nz.Une version simplifiée d'alors seulement"
    return while_loop(
        pos,
        clear_pos(pos),
        then_statement
    )


def if_one_then(pos: int, then_statement: str) -> str:
    "En supposant que la position de pos est 1 ou 0.Traiter si 1(Version destructrice)."
    return while_loop(
        pos,
        dec_pos(pos),
        then_statement
    )

ʻIf_one_then est presque le même que ʻif_nz_then, mais si la valeur est 0 ou 1, la taille de la source sera légèrement plus petite, donc nous l'avons préparée. Cela n'a peut-être pas beaucoup de sens.

Une petite macro délicate

Nous avons également des macros étranges. La version non destructive de l'instruction ʻif` qui m'est venue à l'esprit et le traitement arithmétique qui l'utilise (addition, soustraction, multiplication avec report).

Cependant, comme le travail est requis à une position étrange, le mouvement du pointeur est différent (éventuellement le même) entre la clause then et la clause ʻelse`, ce qui est un peu une macro déroutante.

C'est peut-être un frein à l'optimisation en langage C.

def if_nz_tricky(
        pos: int,
        n: int,
        m: int,
        then_statement: str,
        else_statement: str = "") -> str:
    """
    if_Version non destructive de nz.Un peu délicat
Conditions préalables
      *(ptr + pos + n) == 0
      *(ptr + pos + m) == 0
      *(ptr + pos + n + m) == 0
    ※ n ==m est OK
    """
    return block_of(
        move_ptr(pos),
        inc_pos(n),  # pos+n =Marquer pour autre
        "[",  #Pour NZ
        dec_pos(n),  #Effacer l'indicateur else
        exec_pos(-pos, then_statement),
        move_ptr(m),  #NZ est pos+m /Z reste pos
        "c]",
        move_ptr(n),  #NZ est pos+n+m /Z est pos+n
        "[-",  #Pour Z
        exec_pos(-pos - n, else_statement),
        move_ptr(m),  #NZ est pos+n+Reste m/Z également pos+n+Déplacer vers m
        "c]",
        move_ptr(-pos - n - m)
    )


def if_z_tricky(
        pos: int,
        n: int,
        m: int,
        then_statement: str,
        else_statement: str = "") -> str:
    """
    if_Version non destructive de z.Un peu délicat
Conditions préalables
      *(ptr + pos + n) == 0
      *(ptr + pos + m) == 0
      *(ptr + pos + n + m) == 0
    ※ n ==m est OK
    """
    return if_nz_tricky(pos, n, m, else_statement, then_statement)


def inc_data_tricky(pos: int, digit: int) -> str:
    """
Incrément avec report.Un peu délicat
Conditions préalables
      *(ptr + pos + 1) == 0
      *(ptr + pos + 2) == 0
    """

    if 1 < digit:
        #Besoin de porter
        return block_of(
            inc_pos(pos),
            if_z_tricky(pos, 1, 1,
                        inc_data_tricky(pos - 1, digit - 1))
        )
    else:
        #Pas besoin de porter
        return inc_pos(pos)


def dec_data_tricky(pos: int, digit: int) -> str:
    """
Décrémenter avec report.Un peu délicat
Conditions préalables
      *(ptr + pos + 1) == 0
      *(ptr + pos + 2) == 0
    """
    if 1 < digit:
        return block_of(
            if_z_tricky(pos, 1, 1,
                        dec_data_tricky(pos - 1, digit - 1)),
            dec_pos(pos)
        )
    else:
        return dec_pos(pos)


def add_data_tricky(source: int, pos: int, work: int, digit: int) -> str:
    """
1 octet supplémentaire. pos += source.Il y a un report.Version non destructive.Un peu délicat
Conditions préalables
      *(ptr + pos + 1) == 0
      *(ptr + pos + 2) == 0
    """
    return for_safe(source, work, inc_data_tricky(pos, digit))


def multi_data_tricky(
        source1: int,
        source2: int,
        pos: int,
        digit: int) -> str:
    """
Multiplication de 1 octet. pos = source1 * source2.Il y a un report.Version non destructive.Un peu délicat
Conditions préalables
      *(ptr + pos + 1) == 0
      *(ptr + pos + 2) == 0
      *(ptr + pos + 3) == 0
      *(ptr + pos + 4) == 0
    """
    return for_safe(
        source1,
        pos + 3,
        add_data_tricky(source2, pos, pos + 4, digit)
    )

Simulateur Brainf * CK

Vous avez peut-être remarqué que la première macro avait doctest répertorié, mais pas au milieu.

Pour les macros de base, c'est un code simple, donc je pourrais facilement vérifier le résultat avec doctest, mais pour les macros avec mouvement, il est nécessaire de vérifier que le code de sortie fonctionne plutôt que ce qu'il est. il y a.

Par conséquent, j'ai créé un simulateur Brainf * ck (comme un interpréteur) en python et vérifié son fonctionnement avec unittest (car à mesure que cela se complique, je ne sais pas ce qui ne va pas si la macro ne fonctionne pas correctement).

Test unitaire important.

Planification de machine Brainf * CK stack

J'ai fait divers programmes avec la macro ci-dessus.

C'était difficile de réfléchir à l'endroit où placer le travail, ce qui m'a donné l'impression de l'avoir assemblé. S'il est trop loin, le code source sera inutilement volumineux.

À la suite de diverses réflexions, l'idée est qu'il serait préférable d'utiliser une machine à pile (langage de traitement de pile). Eh bien, c'est une macro de type langage de traitement de pile.

De plus, dans le cas de l'ensemble de Mandelbrot, une addition / soustraction / multiplication de fractions est nécessaire, mais au départ, nous avons utilisé des fractions à virgule fixe dans la deuxième représentation du complément. Cependant, la vitesse de calcul était très lente (bien qu'il y ait une forte possibilité d'un problème logique), donc cette fois j'ai essayé d'utiliser une fraction de signe à virgule fixe + valeur absolue.

Les fractions à virgule fixe sont de 1 octet pour la partie entière et de 1 octet pour la partie fraction (c'est-à-dire qu'elles ont une précision de 1/256).

Macro d'opération de pile de base

Depuis que j'ai changé la fraction à virgule fixe en signe + partie entière (1 octet) + partie fraction (1 octet), j'ai essayé de créer un élément de la pile de 4 octets.

Les entiers de 1 octet sont stockés au début de 4 octets.

Pour les fractions à virgule fixe, le premier octet est vide, le deuxième octet est la partie entière, le troisième octet est la partie fractionnaire et le quatrième octet est le signe (positif pour 0, négatif pour 1).

… Pourquoi ai-je mis le panneau derrière?

De plus, on suppose que les macros ci-dessus sont enregistrées dans le fichier bf_core.py.

import bf_core as c

#Il existe deux types de nombres:
#・ Entier 1 octet(Non signé) {VALUE, 0, 0, 0}
#・ Point décimal fixe de 3 octets. {0,Partie entière,Partie décimale,Code 0/1}Signe + valeur absolue

#Un élément de la pile est fixé à 4 octets
ELEMENT_SIZE = 4
#Le haut de la pile est un élément avant
TOP = ELEMENT_SIZE * (-1)
#Le deuxième de la pile est deux éléments avant
SECOND = ELEMENT_SIZE * (-2)
#La position actuelle de la pile est vide
NOW = 0
#Reculer d'un élément lorsqu'il est chargé sur la pile
NEXT = ELEMENT_SIZE * (1)

#Placement d'entiers de 1 octet{Valeur entière, 0, 0, 0 }
IDX_BYTE = 0
IDX_DMY1 = 1
IDX_DMY2 = 2
IDX_DMY3 = 3

#Placement des fractions à virgule fixe{ 0,Partie entière,Partie décimale,Code(0=+/1=-) }
IDX_DMY = 0
IDX_INT = 1
IDX_DEC = 2
IDX_SGN = 3

def push_byte(value: int) -> str:
    "Mettez un entier de 1 octet en haut de la pile"
    value = int(value) & 0xff
    return c.block_of(
        c.init_value(NOW + IDX_BYTE, value & 0xff),
        c.move_ptr(NEXT)
    )


def push_decimal(value: float) -> str:
    "Placez un point décimal fixe de 3 octets en haut de la pile"
    (sign, value) = (0, value) if 0 <= value else (1, -value)
    value = int(value * 256) & 0xffff
    return c.block_of(
        c.init_value(NOW + IDX_INT, (value >> 8) & 0xff),
        c.init_value(NOW + IDX_DEC, value & 0xff),
        c.init_value(NOW + IDX_SGN, sign),
        c.move_ptr(NEXT)
    )


def drop() -> str:
    "Jeter le haut de la pile"
    return c.block_of(
        c.clear_pos(TOP + 3),
        c.clear_pos(TOP + 2),
        c.clear_pos(TOP + 1),
        c.clear_pos(TOP + 0),
        c.move_ptr(TOP)
    )


def dup(num: int) -> str:
    "Copiez les éléments de la pile et empilez-les en haut de la pile. num=0 copie le début"
    pos = -ELEMENT_SIZE * (num + 1)
    return c.block_of(
        c.copy_data(pos + 0, NOW + 0, NOW + 1),
        c.copy_data(pos + 1, NOW + 1, NOW + 2),
        c.copy_data(pos + 2, NOW + 2, NOW + 3),
        c.copy_data(pos + 3, NOW + 3, NEXT),
        c.move_ptr(NEXT)
    )


def swap(num: int) -> str:
    "Échangez le premier élément de la pile avec l'élément correspondant de la pile. 1<=Soyez num"
    pos = -ELEMENT_SIZE * (num + 1)
    work = NOW
    return c.block_of(
        c.swap_data(pos + 0, TOP + 0, work),
        c.swap_data(pos + 1, TOP + 1, work),
        c.swap_data(pos + 2, TOP + 2, work),
        c.swap_data(pos + 3, TOP + 3, work)
    )


def override(num: int) -> str:
    "Écraser l'élément correspondant de la pile par le premier élément de la pile. 1<=Soyez num."
    pos = -ELEMENT_SIZE * (num + 1)
    return c.block_of(
        c.override_data(TOP + 3, pos + 3),
        c.override_data(TOP + 2, pos + 2),
        c.override_data(TOP + 1, pos + 1),
        c.override_data(TOP + 0, pos + 0),
        c.move_ptr(TOP)
    )

C'est comme ça.

Macro de boucle de version de pile

Avec un entier de 1 octet chargé en haut de la pile, il boucle le nombre de fois.

Lorsque la boucle se termine, le premier entier de 1 octet de la pile est rejeté.

loop_last est utilisé pour interrompre le traitement de la boucle, mais contrairement au frein général, le traitement lui-même est exécuté jusqu'à la fin de la boucle ] (pensez-y comme simplement définir le drapeau de fin). Kato. En fait, je viens de réinitialiser la variable de contrôle à 0).

def loop_of(*statements: str) -> str:
    "Boucle pour un entier de 1 octet de TOP"
    return c.block_of(
        c.for_loop(TOP, *statements),
        c.move_ptr(TOP)
    )


def loop_last(num: int) -> str:
    "Préparez-vous à terminer la boucle.num est la position de la variable de contrôle dans la boucle.Le traitement continue"
    pos = -ELEMENT_SIZE * (num + 1)
    return c.clear_pos(pos + IDX_BYTE)

macro système d'addition / soustraction de version d'octet

Addition et soustraction de 1 octet.

Si deux entiers de 1 octet se trouvent sur la pile, ajoutez ou soustrayez les deux. Après le calcul, les deux éléments d'origine sont supprimés et remplacés par le résultat du calcul (c'est-à-dire qu'un élément de la pile est réduit).

def add_byte() -> str:
    "1 octet supplémentaire"
    return c.block_of(
        c.for_loop(TOP + IDX_BYTE, c.inc_pos(SECOND + IDX_BYTE)),
        c.move_ptr(TOP)
    )


def sub_byte() -> str:
    "Soustraction de 1 octet"
    return c.block_of(
        c.for_loop(TOP + IDX_BYTE, c.dec_pos(SECOND + IDX_BYTE)),
        c.move_ptr(TOP)
    )

macro de branche conditionnelle de version d'octet

Ceci est une instruction ʻif` sur 1 octet.

Après la fin de l'instruction ʻif`, l'entier de 1 octet au début de la pile est ignoré.

De plus, puisque le travail utilise l'autre partie de l'élément 1 octet (3 octets restants), La profondeur de la pile ne change pas.

def if_nz(then_statement: str, else_statement: str = "") -> str:
    "Lorsque le premier octet de la pile est NZ.Jeter le haut de la pile après la fin"
    else_statement = c.delete_useless(else_statement)
    if else_statement != "":
        else_flag = TOP + IDX_DMY1
        return c.block_of(
            c.set_value(else_flag, 1),
            c.if_nz_then(
                TOP + IDX_BYTE,
                c.dec_pos(else_flag) + then_statement),
            c.if_one_then(
                else_flag,
                else_statement),
            c.move_ptr(TOP)
        )
    else:
        return c.block_of(
            c.if_nz_then(
                TOP + IDX_BYTE,
                then_statement),
            c.move_ptr(TOP)
        )


def if_z(then_statement: str, else_statement: str = "") -> str:
    "Lorsque le premier octet de la pile est Z.Jeter le haut de la pile après la fin"
    return if_nz(else_statement, then_statement)


def if_eq(value: int, then_statement: str, else_statement: str = "") -> str:
    "Lorsque le premier octet de la pile est égal à value.Jeter le haut de la pile après la fin"
    return c.block_of(
        push_byte(value),
        sub_byte(),
        if_z(then_statement, else_statement)
    )

Macro fractionnaire à virgule fixe

Calculs de fraction à virgule fixe (addition, soustraction, multiplication) et instructions «si».

En raison de la représentation signe + valeur absolue, l'addition et la soustraction sont inopinément gênantes.

Je pense qu'il vaut peut-être mieux utiliser la méthode Karatsuba pour la multiplication, mais je crains que cela ne change pas car l'addition et la soustraction semblent être plus lentes que prévu, mais maintenant je calcule avec la méthode de calcul du pinceau.

def if_nz_decimal(then_statement: str, else_statement: str = "") -> str:
    "Lorsque la virgule décimale fixe de 3 octets est NZ.Jeter le haut de la pile après la fin"
    nz_flag = TOP + IDX_DMY
    else_flag = TOP + IDX_INT
    then_flag = TOP + IDX_DEC
    return c.block_of(
        c.clear_pos(TOP + IDX_SGN),
        c.if_nz_then(TOP + IDX_DEC, c.inc_pos(nz_flag)),
        c.if_nz_then(TOP + IDX_INT, c.inc_pos(nz_flag)),
        c.inc_pos(else_flag),
        c.if_nz_then(nz_flag, c.dec_pos(else_flag) + c.inc_pos(then_flag)),
        c.if_one_then(then_flag, then_statement),
        c.if_one_then(else_flag, else_statement),
        c.move_ptr(TOP)
    )


def if_z_decimal(then_statement: str, else_statement: str = "") -> str:
    "Lorsque la virgule décimale fixe de 3 octets est NZ.Jeter le haut de la pile après la fin"
    return if_nz_decimal(else_statement, then_statement)


def if_negative_decimal(then_statement: str, else_statement: str = "") -> str:
    "Lorsque la virgule décimale fixe de 3 octets est un nombre négatif.Jeter le haut de la pile après la fin"
    then_flag = TOP + IDX_DEC
    else_flag = TOP + IDX_INT
    return c.block_of(
        c.clear_pos(then_flag),
        c.set_value(else_flag, 1),
        c.if_nz_then(
            TOP + IDX_SGN,
            c.dec_pos(else_flag) + c.inc_pos(then_flag)
        ),
        c.if_one_then(then_flag, then_statement),
        c.if_one_then(else_flag, else_statement),
        c.move_ptr(TOP)
    )


def _add_abs() -> str:
    "Ajout de valeurs absolues de virgule décimale fixe de 3 octets"
    # SECOND/En supposant que les symboles de TOP sont les mêmes
    return c.block_of(
        c.clear_pos(TOP + IDX_SGN),
        c.for_loop(SECOND + IDX_DEC, c.inc_data_tricky(TOP + IDX_DEC, 2)),
        c.for_loop(SECOND + IDX_INT, c.inc_pos(TOP + IDX_INT)),
        c.override_data(TOP + IDX_DEC, SECOND + IDX_DEC),
        c.override_data(TOP + IDX_INT, SECOND + IDX_INT)
    )


def _dec_both_abs_int() -> str:
    "Décrémenter les deux entiers jusqu'à ce que l'un devienne 0"
    count = NOW
    work1 = NOW + 1
    return c.block_of(
        c.copy_data(SECOND + IDX_INT, count, work1),
        c.for_loop(
            count,
            c.if_z_tricky(
                TOP + IDX_INT,
                ELEMENT_SIZE,  # work2 = NOW + IDX_INT
                ELEMENT_SIZE,  # work3 = NEXT + IDX_INT
                then_statement=loop_last(count),
                else_statement=c.block_of(
                    c.dec_pos(SECOND + IDX_INT),
                    c.dec_pos(TOP + IDX_INT)
                )
            )
        )
    )


def _dec_both_abs_decimal() -> str:
    "Décrémenter les deux fractions jusqu'à ce que l'une devienne 0"
    count = NOW
    work1 = NOW + 1
    return c.block_of(
        c.copy_data(SECOND + 2, count, work1),
        c.for_loop(
            count,
            c.if_z_tricky(
                TOP + IDX_DEC,
                ELEMENT_SIZE,  # work2 = NOW + IDX_DEC
                ELEMENT_SIZE,  # work3 = NEXT + IDX_DEC
                then_statement=loop_last(count),
                else_statement=c.block_of(
                    c.dec_pos(SECOND + IDX_DEC),
                    c.dec_pos(TOP + IDX_DEC)
                )
            )
        )
    )


def _if_nz_int_swap() -> str:
    "Si la partie entière de SECOND est différente de 0, TOP/Retournez DEUXIÈME"
    work = NOW
    return c.if_nz_tricky(
        SECOND + IDX_INT,
        ELEMENT_SIZE * 2,  # work2 = NOW + IDX_INT
        ELEMENT_SIZE * 2,  # work3 = NEXT + NEXT + IDX_INT
        then_statement=c.block_of(
            c.swap_data(SECOND + IDX_INT, TOP + IDX_INT, work),
            c.swap_data(SECOND + IDX_DEC, TOP + IDX_DEC, work),
            c.swap_data(SECOND + IDX_SGN, TOP + IDX_SGN, work)
        )
    )


def _if_top_decimal_is_nz_then_override() -> str:
    "Si la partie fractionnaire de TOP est différente de 0, déplacez TOP à SECOND"
    return c.if_z_tricky(
        TOP + IDX_DEC,
        ELEMENT_SIZE,  # work1 = NOW + IDX_DEC
        ELEMENT_SIZE,  # work2 = NEXT + IDX_DEC
        then_statement=c.clear_pos(TOP + IDX_SGN),
        else_statement=c.block_of(
            c.override_data(TOP + IDX_SGN, SECOND + IDX_SGN),
            c.move_data(TOP + IDX_DEC, SECOND + IDX_DEC)
        )
    )


def _top_minus_second() -> str:
    "Soustrayez de TOP par la partie fractionnaire de SECOND et passez à la position SECOND"
    return c.block_of(
        #Déplacer le signe(Déplacez-vous d'abord avec des mesures délicates)
        c.override_data(TOP + IDX_SGN, SECOND + IDX_SGN),
        #Décrémenter seulement une petite partie de SECOND
        c.for_loop(
            SECOND + IDX_DEC,
            c.dec_data_tricky(TOP + IDX_DEC, 2)
        ),
        #Déplacer les résultats vers SECOND
        c.move_data(TOP + IDX_DEC, SECOND + IDX_DEC),
        c.move_data(TOP + IDX_INT, SECOND + IDX_INT)
        # TODO -0.0 à 0.Doit-il être converti en 0?
    )


def _sub_abs() -> str:
    "Soustraction de la valeur absolue de la virgule décimale fixe de 3 octets"
    #Diminuer jusqu'à ce que l'un ou l'autre devienne 0.La réponse est celle qui reste(Y compris le code)
    return c.block_of(
        #Décrémenter les deux entiers jusqu'à ce que l'un devienne 0
        _dec_both_abs_int(),
        #Si la partie entière de SECOND est différente de 0, TOP/Retournez DEUXIÈME
        _if_nz_int_swap(),
        c.if_nz_tricky(
            TOP + IDX_INT,
            ELEMENT_SIZE,  # work1 = NEXT + IDX_INT
            ELEMENT_SIZE,  # work2 = NEXT + NEXT + IDX_INT
            then_statement=_top_minus_second(),
            else_statement=c.block_of(
                # TOP/Lorsque les deux SECONDES ont une partie entière de 0
                _dec_both_abs_decimal(),
                _if_top_decimal_is_nz_then_override()
            )
        )
    )


def add_decimal() -> str:
    "Ajout de fractions à virgule fixe"
    #Si les signes sont identiques, ajoutez les valeurs absolues
    #Soustraction absolue si les signes sont différents
    work = SECOND + IDX_DMY
    diff_work = TOP + IDX_DMY
    same_flag = SECOND + IDX_DMY
    return c.block_of(
        c.for_safe(SECOND + IDX_SGN, work, c.inc_pos(diff_work)),
        c.for_safe(TOP + IDX_SGN, work, c.dec_pos(diff_work)),
        c.inc_pos(same_flag),
        c.if_nz_then(diff_work, c.dec_pos(same_flag) + _sub_abs()),
        c.if_nz_then(same_flag, _add_abs()),
        drop()
    )


def sub_decimal() -> str:
    "Soustraction de fraction à virgule fixe"
    #Inversez le signe et ajoutez(A-B => A+(-B))
    plus_flag = NOW + IDX_DMY
    return c.block_of(
        c.inc_pos(plus_flag),
        c.if_one_then(TOP + IDX_SGN, c.dec_pos(plus_flag)),
        c.if_one_then(plus_flag, c.inc_pos(TOP + IDX_SGN)),
        add_decimal()
    )


def _multi_decimal_abs() -> str:
    "Multiplication des valeurs absolues de la virgule décimale fixe de 3 octets"
    #La partie entière et la partie fraction sont calculées en unités d'octets, comme le calcul de la brosse.
    #             A1. A2
    #           x B1. B2
    # ---------------------
    #               .A1xB2 A2xB2
    #       +  A1xB1.A2xB1
    # ----------------------
    #            R1 .  R2    R3
    #          <--------->Plage requise
    idx_a1 = SECOND + IDX_INT
    idx_a2 = SECOND + IDX_DEC
    idx_b1 = TOP + IDX_INT
    idx_b2 = TOP + IDX_DEC
    idx_r1 = NOW + IDX_INT
    idx_r2 = NOW + IDX_DEC
    idx_r3 = NOW + IDX_DEC + 1

    #Calculé à partir du chiffre supérieur en raison du traitement de report
    return c.block_of(
        c.multi_data_tricky(idx_a1, idx_b1, idx_r1, 1),  # AxC (1 octet)
        c.multi_data_tricky(idx_a1, idx_b2, idx_r2, 2),  # AxD (2 octets, y compris le report)
        c.multi_data_tricky(idx_a2, idx_b1, idx_r2, 2),  # BxC (2 octets, y compris le report)
        c.multi_data_tricky(idx_a2, idx_b2, idx_r3, 3),  # BxD (3 octets, y compris le report)
        c.clear_pos(idx_r3),  #Effacez R3 car seul le report est nécessaire
    )


def _xor_sign() -> str:
    #+ Si les signes sont les mêmes, moins s'ils sont différents
    idx_as = SECOND + IDX_SGN
    idx_bs = TOP + IDX_SGN
    idx_rs = NOW + IDX_SGN
    sign_work = NEXT
    return c.block_of(
        c.for_loop(idx_as, c.inc_pos(sign_work)),
        c.for_loop(idx_bs, c.dec_pos(sign_work)),
        c.if_nz_then(sign_work, c.inc_pos(idx_rs))
    )


def multi_decimal() -> str:
    "Multiplication à virgule fixe 3 octets"
    # A * B => R
    return c.block_of(
        _multi_decimal_abs(),  #Trouvez la valeur absolue de R
        _xor_sign(),  #Trouvez le signe de R
        c.move_ptr(NEXT),  #L'état de la pile{A, B, R}
        override(2),  #Écraser R en position A
        drop()  #Supprimer B
    )


def if_lt_decimal(then_statement: str, else_statement: str = "") -> str:
    return c.block_of(
        swap(1),  # {A, B} -> {B, A}Dans l'ordre de. then/Pour ne pas changer le nombre de piles lors de l'exécution d'autre
        dup(1),  # {B, A, B}
        sub_decimal(),  # {B, R (= A - B)}
        #A si R est négatif< B
        if_negative_decimal(then_statement, else_statement),
        drop()  # drop B
    )


def if_ge_decimal(then_statement: str, else_statement: str = "") -> str:
    return if_lt_decimal(else_statement, then_statement)


def if_gt_decimal(then_statement: str, else_statement: str = "") -> str:
    return c.block_of(
        swap(1),
        if_lt_decimal(then_statement, else_statement)
    )


def if_le_decimal(then_statement: str, else_statement: str = "") -> str:
    return if_gt_decimal(else_statement, then_statement)

Je coupe les coins ronds au même endroit, et il existe deux types de 0, «+ 0.0» et «-0.0». Par conséquent, ʻif_negative_decimaljugera-0.0` comme un nombre négatif.

En fait, puisque cette fonction est utilisée pour juger de la divergence de l'ensemble de Mandelbrot, elle peut être jugée par erreur de temps en temps. Eh bien, c'est dans la plage d'erreur, n'est-ce pas?

Macro de sortie de caractères

Une sortie de caractère et une macro de sortie de chaîne de caractères (idiote).

def put_char() -> str:
    "1 octet au début de la pile(Un personnage)Production"
    return c.block_of(
        c.exec_pos(TOP, "."),
        drop()
    )


def put_str(message: str) -> str:
    result = c.clear_pos(NOW)
    for ch in message:
        result += c.init_value(NOW, ord(ch))
        #TODO ↑ Il vaut mieux vérifier si la différence avec la valeur précédente est plus courte
        result += c.exec_pos(NOW, ".")
        result += c.clear_pos(NOW)
    return c.delete_useless(result)

Dessiner l'ensemble de Mandelbrot

C'est finalement le sujet principal.

Voir ci-dessous pour plus d'informations sur l'ensemble de Mandelbrot.

Serait-ce comme ça s'il était écrit en langage C?

#include <stdio.h>

#define X_BEGIN -2.0
#define X_END 1.0
#define Y_BEGIN -0.9375
#define Y_END 0.9375

//Nombre de répétitions
#define C_MAX 26
//Seuil de divergence(2.Valeur au carré de 0)
#define THRESHOLD2 4

void main(void)
{
    int columns = 128;
    int rows = 40;
    //Valeur d'ajout sur l'axe X
    float x_step = (X_END - X_BEGIN) / (columns - 1);
    //Valeur d'ajout sur l'axe Y
    float y_step = (Y_END - Y_BEGIN) / (rows - 1);

    float y0 = Y_BEGIN;
    for (int y = 0; y < rows; y++)
    {
        float x0 = X_BEGIN;
        for (int x = 0; x < columns; x++)
        {
            float cx = x0;
            float cy = y0;
            float zx = 0;
            float zy = 0;
            char ch = ' ';
            char count = 'A' - 1;
            for (int c = 0; c < C_MAX; c++)
            {
                float zx2 = zx * zx;
                float zy2 = zy * zy;
                float size2 = zx2 + zy2;

                if (size2 > 4.0)
                {
                    //Divergence
                    ch = count;
                    break;
                }
                else
                {
                    float zx_next = zx2 - zy2 + cx;
                    float zy_next = zx * zy * 2 + cy;
                    if (zx_next == zx && zy_next == zy)
                    {
                        //convergence
                        break;
                    }
                    else
                    {
                        zx = zx_next;
                        zy = zy_next;
                        count++;
                    }
                }
            }
            putchar(ch);
            x0 += x_step;
        }
        putchar('\n');
        y0 += y_step;
    }
}

Je pense qu'il y a beaucoup d'autres points, comme si la fraction peut être «float» ou la comparaison entre «float» avec «==», mais je vais laisser ça pour l'instant.

Pour rendre l'écriture aussi facile que possible avec Brainf * ck, le jugement de savoir si la valeur absolue du nombre complexe dépasse «2,0» est remplacé par le jugement de savoir si la valeur au carré dépasse directement «4,0» sans utiliser «sqrt». Je suis.

Écrivons ceci avec Brainf * ck.

Au fait, les macros de pile sont supposées être enregistrées dans bf_stack.py.

import bf_core as c
import bf_stack as s


#Gamme de l'axe X
(X_BEGIN, X_END) = (-2.0, 1.0)
#Plage de l'axe Y
(Y_BEGIN, Y_END) = (-0.9375, 0.9375)

#Nombre de répétitions
C_MAX = 26
#Seuil de divergence(2.Valeur au carré de 0)
THRESHOLD2 = 4


def mandelbrot(columns: int, rows: int) -> str:
    #Valeur d'ajout sur l'axe X
    x_step = (X_END - X_BEGIN) / (columns - 1)
    #Valeur d'ajout sur l'axe Y
    y_step = (Y_END - Y_BEGIN) / (rows - 1)

    return c.program_of(

        # #0: y0 = y_begin
        s.push_decimal(Y_BEGIN),

        # #1: y = rows
        s.push_byte(rows),
        s.loop_of(  # for(y)

            # #2: x0 = x_begin
            s.push_decimal(X_BEGIN),

            # #3: x = columns
            s.push_byte(columns),
            s.loop_of(  # for(x)

                s.dup(1),  # #4: cx = x0
                s.dup(4),  # #5: cy = y0
                s.push_decimal(0),  # #6: zx = 0
                s.push_decimal(0),  # #7: zy = 0
                s.push_byte(ord(" ")),  # #8: ch = ' '
                s.push_byte(ord("A") - 1),  # #9: count = 'A'-1

                s.push_byte(C_MAX),  # #10: c = 26
                s.loop_of(  # for(c)

                    # #11: zx2 = zx * zx
                    s.dup(4),
                    s.dup(0),
                    s.multi_decimal(),

                    # #12: zy2 = zy * zy
                    s.dup(4),
                    s.dup(0),
                    s.multi_decimal(),

                    # #13: size2 = zx2 + zy2
                    s.dup(1),
                    s.dup(1),
                    s.add_decimal(),

                    # #14: THRESHOLD2
                    s.push_decimal(THRESHOLD2),
                    # if size2 > 4.0
                    s.if_gt_decimal(
                        then_statement=c.block_of(
                            #Divergence

                            # ch = count
                            s.dup(5),  # #15
                            s.override(7),

                            # for_c break
                            s.loop_last(4)
                        ),
                        else_statement=c.block_of(
                            # #15: zx_next = zx2 - zy2 + cx
                            s.dup(3),
                            s.dup(3),
                            s.sub_decimal(),
                            s.dup(11),
                            s.add_decimal(),

                            # #16: zy_next = zx * zy * 2 + cy
                            s.dup(9),
                            s.dup(9),
                            s.multi_decimal(),
                            s.dup(0),
                            s.add_decimal(),
                            s.dup(11),
                            s.add_decimal(),

                            # #17: if zx_next == zx && zy_next == zy
                            s.dup(1),  # zx_next
                            s.dup(11),  # zx
                            s.sub_decimal(),
                            # #17: 0(zx_next == zx) or 1(zx_next != zx)
                            s.if_nz_decimal(
                                s.push_byte(1) + s.swap(1),
                                s.push_byte(0) + s.swap(1)),
                            s.dup(1),  # zy_next
                            s.dup(11),  # zy
                            s.sub_decimal(),
                            # #18: 0(zx_next == zx) or 1(zx_next != zx)
                            s.if_nz_decimal(
                                s.push_byte(1) + s.swap(1),
                                s.push_byte(0) + s.swap(1)),
                            # #17: 0(zx_next == zx && zy_next == zy) or other
                            s.add_byte(),
                            s.if_z(
                                then_statement=c.block_of(
                                    #convergence
                                    s.swap(1),
                                    s.drop(),  # drop zy_next
                                    s.swap(1),
                                    s.drop(),  # drop zx_next
                                    s.loop_last(5)  # last c
                                ),
                                else_statement=c.block_of(
                                    # zx = zx_next
                                    s.swap(1),
                                    s.override(10),

                                    # zy = zy_next
                                    s.swap(1),
                                    s.override(10),

                                    # count += 1
                                    s.dup(6),
                                    s.push_byte(1),
                                    s.add_byte(),
                                    s.override(7)
                                )
                            )
                        )
                    ),
                    s.drop() * 2  # drop zy2, zx2
                ),
                s.drop(),  # drop count
                s.put_char(),  # putchar(ch)
                s.drop() * 4,  # drop zy, zx, cy, cx

                # #4: x0 += x_step
                s.dup(1),
                s.push_decimal(x_step),
                s.add_decimal(),
                s.override(2)
            ),
            # drop x0
            s.drop(),

            # #2: putchar("\n")
            s.push_byte(ord("\n")),
            s.put_char(),

            # #2: y0 += y_step
            s.dup(1),
            s.push_decimal(y_step),
            s.add_decimal(),
            s.override(2)
        )
    )


if __name__ == '__main__':
    program = mandelbrot(128, 40)
    print(program)

Lorsque vous utilisez réellement la macro de traitement de pile, cela est difficile car vous devez écrire les éléments de la pile dans des positions relatives.

Quoi qu'il en soit, enregistrez-le sous mandelbrot.py et exécutez-le.

python3 -B mandelbrot.py > mandelbrot.bf
./bf2c -F mandelbrot.bf
gcc -O2 -o mandelbrot mandelbrot.c
./mandelbrot

Je pense que la compilation se terminera bientôt, mais cela prendra environ 5 à 7 secondes pour s'exécuter. lent.

Au fait, je pense que vous pouvez compiler normalement avec un compilateur Brainf * ck général, mais veuillez utiliser un compilateur optimisé autant que possible.

De plus, je pense que l'interprète Brainf * ck prendra beaucoup de temps. À tout le moins, je pense qu'il vaut mieux utiliser un interpréteur qui fonctionne pour l'optimisation.

mandelbrot

Il y a Implémentation de Mandelbrot dans le célèbre Brainf * ck, mais cela ne prend que 1 à 2 secondes, donc il est encore amélioré. Il semble y avoir de la place.

Colorez l'ensemble de Mandelbrot

Pour être précis, l'ensemble de Mandelbrot est un ensemble de nombres complexes qui ne divergent pas, donc la partie noire du résultat est l'ensemble de Mandelbrot.

Cependant, en suivant l'application générale de dessin de Mandelbrot, je vais la colorer en fonction du nombre de fois où elle diverge.

La source est simple, elle est donc omise (enregistrée dans github en tant qu'échantillon du compilateur). Si vous le déplacez sur un terminal qui peut utiliser la séquence d'échappement ANSI, il sera coloré.

mandelbrot(color)

application

Après cela, vous pouvez dessiner l'ensemble de Julia, etc. avec juste une petite modification.

julia(color)

Recommended Posts

Dessinez un ensemble de Mandelbrot avec Brainf * ck
Dessinez un graphique avec NetworkX
Dessinez un graphique avec networkx
Dessinez un graphique lâche avec matplotlib
Dessinez un beau cercle avec numpy
Dessinez un graphique avec Julia + PyQtGraph (3)
Dessinez un graphique avec des pandas + XlsxWriter
Dessinez un graphique avec l'interface graphique PySimple
Dessinez facilement une carte avec matplotlib.basemap
Configurer un serveur Samba avec Docker
Dessinez un cœur en rubis avec PyCall
Dessinez un graphique avec PyQtGraph Part 1-Drawing
Dessinez une surface plane avec un graphique 3D matplotlib
Configurer un serveur HTTPS simple avec asyncio
Dessinez un graphique avec des étiquettes japonaises dans Jupyter
Comment dessiner un graphique à 2 axes avec pyplot
Configurer un serveur local avec le téléchargement Go-File-
Dessinez un graphique avec les paramètres PyQtGraph Partie 3-PlotWidget
Dessinez un graphique en traitant avec Pandas groupby
[Python] Dessinez un graphe orienté avec Dash Cytoscape
Essayez de dessiner une courbe de vie avec python
[Python] Dessinez un Mickey Mouse avec une tortue [Débutant]
Configurer un serveur local avec le téléchargement Go-File-
Dessinez un graphique avec les paramètres PyQtGraph Part 4-PlotItem
Dessinez un graphique avec PyQtGraph Partie 6 - Affichage d'une légende
Dessinez un graphique avec PyQtGraph Partie 5-Augmentez l'axe Y
[Python] Dessinez un diagramme de relation de balises Qiita avec NetworkX
[Python] Comment dessiner un graphique linéaire avec Matplotlib
Une histoire qui a eu du mal avec l'ensemble commun HTTP_PROXY = ~
Dessinez de force quelque chose comme un organigramme avec Python, matplotlib
Dessinez un graphique avec PyQtGraph Partie 2 - Paramètres de tracé détaillés
[Python] Comment dessiner un diagramme de dispersion avec Matplotlib
Configurer un environnement de développement Python avec Sublime Text 2
[Vagrant] Configurer un serveur API simple avec python
Format A4 avec python-pptx
Décorer avec un décorateur
Étudier les mathématiques avec Python: dessiner un graphe sympy (scipy) avec matplotlib
Configurer un environnement de développement Python avec Visual Studio Code
Configurer un serveur Web avec CentOS7 + Anaconda + Django + Apache
[Visualisation] Je veux dessiner un beau graphique avec Plotly