Crea un linguaggio di programmazione che sembra essere inutilizzabile


85

Il thread delle sfide dei ladri è qui .

La sfida dei poliziotti: progettare un linguaggio di programmazione che sembra essere inutilizzabile per la programmazione, ma ammette il calcolo (o almeno il completamento dell'attività) attraverso un meccanismo non ovvio.

Dovresti progettare un semplice linguaggio di programmazione che legge il codice da un file di input e quindi fa ... qualcosa. È necessario preparare un programma di soluzione che trova il terzo numero più grande nell'input quando viene eseguito nell'interprete. È necessario rendere il più difficile possibile per i ladri trovare un programma di soluzione. Nota che i ladri possono pubblicare qualsiasi soluzione che compie l'attività, non solo quella che avevi in ​​mente.

Questo è un concorso di popolarità. L'obiettivo degli sbirri è ottenere il maggior numero di voti possibile sopravvivendo 8 giorni dopo aver pubblicato l'interprete senza essere violati. A tal fine, dovrebbero essere utili le seguenti pratiche:

  • Spiegazione accurata della semantica della tua lingua
  • Scrivere codice leggibile

Le seguenti tattiche sono fortemente scoraggiate:

  • Utilizzo di crittografia, hash o altri metodi crittografici. Se vedi una lingua che utilizza la crittografia RSA o rifiuta di eseguire un programma a meno che il suo hash SHA-3 non sia uguale a 0x1936206392306, non esitare a effettuare il downgrade.

Sfida ai ladri: scrivi un programma che trova il terzo più grande intero nell'input quando viene eseguito nell'interprete della polizia.

Questo è relativamente semplice. Per decifrare la risposta di un poliziotto, è necessario creare un programma che completi l'attività quando eseguito nel suo interprete. Quando crei una risposta, pubblica un commento che dice "Cracked" sulla risposta del poliziotto che collega al tuo post. Chiunque crei più poliziotti vince il filo dei ladri.

Regole I / O

  • Gli interpreti devono prendere un nome file nella riga di comando per il programma e utilizzare input e output standard durante l'esecuzione.
  • L'input sarà dato in unario e consisterà solo dei caratteri 0e 1(48 e 49 in ASCII). Un numero N è codificato come N 1s seguito da a 0. C'è un ulteriore 0prima della fine del file. Esempio: per la sequenza (3, 3, 1, 14), l'ingresso è 11101110101111111111111100.
  • È garantito che l'input abbia almeno 3 numeri. Tutti i numeri sono numeri interi positivi.
  • L'output verrà valutato in base al numero di 1s stampati prima che il programma si arresti. Altri personaggi vengono ignorati.

Negli esempi seguenti, la prima riga è l'input in formato decimale; il secondo è l'input effettivo del programma; il terzo è un output di esempio.

1, 1, 3
101011100
1

15, 18, 7, 2, 15, 12, 3, 1, 7, 17, 2, 13, 6, 8, 17, 7, 15, 11, 17, 2
111111111111111011111111111111111101111111011011111111111111101111111111110111010111111101111111111111111101101111111111111011111101111111101111111111111111101111111011111111111111101111111111101111111111111111101100
111111,ir23j11111111111u

247, 367, 863, 773, 808, 614, 2
<omitted>
<contains 773 1's>

Regole noiose per le risposte degli sbirri:

  • Per prevenire la sicurezza attraverso l'oscurità, l'interprete dovrebbe essere scritto in una lingua tra i primi 100 di questo indice TIOBE e avere un compilatore / interprete disponibile gratuitamente.
  • L'interprete non deve interpretare una lingua pubblicata prima di questa sfida.
  • L'interprete dovrebbe inserirsi nel tuo post e non essere ospitato esternamente.
  • L'interprete dovrebbe essere deterministico
  • L'interprete dovrebbe essere portatile e seguire lo standard della propria lingua; non usare comportamenti o bug non definiti
  • Se il programma di soluzione è troppo lungo per adattarsi alla risposta, è necessario pubblicare un programma che lo genera.
  • Il programma di soluzione dovrebbe consistere solo di ASCII stampabili e newline.
  • È necessario eseguire il programma di soluzione in meno di 1 ora sul proprio computer per ciascuno degli input di esempio sopra.
  • Il programma dovrebbe funzionare con numeri interi inferiori a 10 6 e un numero qualsiasi di numeri inferiori a 10 6 (non necessariamente in meno di un'ora), a condizione che la lunghezza totale dell'input sia inferiore a 10 9 .
  • Per sicurezza, un poliziotto deve modificare il programma della soluzione nella risposta dopo che sono trascorsi 8 giorni.

punteggio

Il poliziotto che diventa al sicuro con il punteggio più alto e un punteggio positivo, vince questa domanda.


Non lo affermi esplicitamente, ma ho ragione nel ritenere che il poliziotto debba effettivamente scrivere e pubblicare l'interprete nella sua risposta?
Blu,

@muddyfish Sì, l'interprete dovrebbe essere il contenuto della risposta del poliziotto.
feersum,

1
@ kirbyfan64sos L' output verrà
mbomb007,


20
Mi sono preso una secchione con questa sfida. Ho creato un linguaggio di programmazione e poi trascorso ore di programmazione il compito di vedere se la lingua potrebbe anche lavorare solo per scoprire che in realtà era inutilizzabile.
Sanchises,

Risposte:


24

Changeling (sicuro)

ShapeScript

ShapeScript è un linguaggio di programmazione naturale. I modificatori di forma (o i cambiamenti , come preferiscono essere chiamati) possono trasformarsi in un insieme di istruzioni che consente loro di elaborare i dati.

ShapeScript è un linguaggio basato su stack con una sintassi relativamente semplice. Non sorprende che la maggior parte dei suoi built-in riguardi l'alterazione della forma delle stringhe. Viene interpretato, carattere per carattere, come segue:

  • 'e "inizia una stringa letterale.

    Fino a quando la virgoletta corrispondente non viene trovata nel codice sorgente, tutti i caratteri tra queste virgolette corrispondenti vengono raccolti in una stringa, che viene quindi inserita nello stack.

  • 0per 9spingere gli interi da 0 a 9 nello stack. Si noti che 10spinge due numeri interi.

  • ! estrae una stringa dallo stack e tenta di valutarla come ShapeScript.

  • ? estrae un numero intero dallo stack e invia una copia dell'elemento dello stack a quell'indice.

    L'indice 0 corrisponde all'elemento dello stack più in alto (LIFO) e l'indice -1 a quello più in basso.

  • _ estrae un iterabile dalla pila e ne spinge la lunghezza.

  • @ estrae due oggetti dalla pila e li spinge in ordine inverso.

  • $estrae due stringhe dallo stack e divide la prima in corrispondenza delle occorrenze della prima. L'elenco risultante viene inviato in cambio.

  • &estrae una stringa (più in alto) e un'iterabile dallo stack e unisce l'iterabile, usando la stringa come separatore. La stringa risultante viene inviata in cambio.

  • Se ShapeScript viene utilizzato sul nostro pianeta, dal momento che i pitoni sono i changeling più vicini parenti sulla Terra, tutti gli altri personaggi c pop due articoli x e y (più in alto) dalla pila, e tentano di valutare il codice Python x c y.

    Ad esempio, la sequenza di caratteri 23+valuterebbe 2+3, mentre la sequenza di caratteri "one"3*valuterebbe 'one'*3e la sequenza di caratteri 1''Avaluterebbe 1A''.

    Nell'ultimo caso, dal momento che il risultato non è Python valido, il Changeling si lamenterebbe che la sua forma attuale non è stata applicata (errore di sintassi), dal momento che non è ShapeScript valido.

Prima di eseguire il codice, l'interprete posizionerà l'intero input dell'utente sotto forma di una stringa nello stack. Dopo aver eseguito il codice sorgente, l'interprete stamperà tutti gli elementi nello stack. Se una delle parti intermedie fallisce, il Changeling si lamenterà che la sua forma attuale è inadeguata (errore di runtime).

Muta forma

Nel loro stato naturale, i cambiamenti non prendono la forma di ShapeScript. Tuttavia, alcuni di essi possono trasformarsi in un potenziale codice sorgente (che non è necessariamente utile o anche sintatticamente valido).

Tutti i cambiamenti ammissibili hanno la seguente forma naturale:

  • Tutte le righe devono avere lo stesso numero di caratteri.

  • Tutte le righe devono essere costituite da caratteri ASCII stampabili, seguiti da un singolo avanzamento riga.

  • Il numero di righe deve corrispondere al numero di caratteri stampabili per riga.

Ad esempio, la sequenza di byte ab\ncd\nè un Changeling idoneo.

Nel suo tentativo di passare a ShapeScript, il Changeling subisce la seguente trasformazione:

  • Inizialmente, non esiste un codice sorgente.

  • Per ogni riga accade quanto segue:

    • L'accumulatore del Changeling è impostato su zero.

    • Per ogni carattere c della riga (incluso il trascinamento della riga finale), il punto di codice di c è XORed con l'accumulatore diviso per 2 e il carattere Unicode che corrisponde al punto di codice risultante viene aggiunto al codice sorgente.

      Successivamente, la differenza tra il punto di codice di c e il punto di codice di uno spazio (32) viene aggiunta all'accumulatore.

Se una parte di quanto sopra fallisce, il Changeling si lamenterà che la sua forma attuale è spiacevole.

Dopo che tutte le righe sono state elaborate, la trasformazione del Changeling in (si spera valido) ShapeScript è completa e il codice risultante viene eseguito.

Soluzione (ShapeScript)

"0"@"0"$"0"2*&"0"@+0?_'@1?"0"$_8>"1"*+@1?"0"+$""&'*!#

ShapeScript in realtà si è rivelato abbastanza utilizzabile; può anche eseguire test di primalità ( prove ) e quindi soddisfa la nostra definizione di linguaggio di programmazione.

Ho ripubblicato ShapeScript su GitHub , con una sintassi leggermente modificata e un I / O migliore.

Il codice procede come segue:

"0"@    Push "0" (A) and swap it with the input string (S).
"0"$    Split S at 0's.
"0"2*   Push "00".
&       Join the split S, using "00" as separator.
"0"@+   Prepend "0" to S.
        The input has been transformed into
        "0<run of 1's>000<run of 1's>0...0<run of 1's>0000".
0?_     Push a copy and compute its length (L).
'       Push a string that, when evaluated, does the following:
  @1?     Swap A on top of S, and push a copy of S.
  "0"$    Split the copy of S at 0's.
  _8>     Get the length of the resulting array and compare it with 8.
          If this returns True, there are more than eight chunks, so there are
          more then seven 0's. With two 0's surrounding each run of 1's and
          three extra 0's at the end, this means that there still are three or
          more runs of 1's in the string.
  "1"*    Push "1" if '>' returned True and "" if it returned False.
  +       Append "1" or "" to A.
  @1?     Swap S on top of A, and push a copy of A.
  "0"+    Append "0" to the copy of A.
  $       Split S at occurrences of A+"0".
  ""&     Flatten the resulting array of strings.
'       This removes all occurrences of "010" in the first iteration, all
        occurrences of "0110" in the second, etc., until there are less than
        three runs of 1's left in S. At this point, A is no longer updated,
        and the code inside the string becomes a noop.
*!      Repeat the code string L times and evaluate.
#       Drop S, leaving only A on the stack.

Soluzione (modifica)

"1+-.......................
""B1.......................
"" 2)+7....................
"" 2)=<....................
""( $86=...................
""B8=......................
""247......................
""]`b......................
""B1.......................
""%D1=.....................
""%500=....................
""%&74?....................
""%&#.2....................
""%[bdG....................
""%D1=.....................
""%<5?.....................
""%:6>.....................
""%&65?....................
""%&-%7>...................
""%D1=.....................
""%500=....................
""%&74?....................
""%&,)5>...................
""%&%,79...................
"" )$?/=...................
""),-9=....................
""# !......................

Come tutti i programmi di Changeling, questo codice ha un avanzamento riga finale.

ShapeScript si guasterà immediatamente su qualsiasi carattere non capisca, ma possiamo inserire stringhe arbitrarie come riempitivi e visualizzarle in seguito. Questo ci consente di inserire solo una piccola quantità di codice su ogni riga (all'inizio, dove l'accumulatore è piccolo), seguito da un'apertura ". Se iniziamo con la riga successiva "#, chiudiamo e pop la stringa senza influire sul codice effettivo.

Inoltre, dobbiamo superare tre ostacoli minori:

  • La stringa lunga nel codice ShapeScript è un singolo token e non saremo in grado di adattarlo a una riga.

    Ci impegneremo questa stringa in blocchi ( '@', '1?', ecc), di cui parleremo più avanti concatenate.

  • Il punto di codice di _è piuttosto alto e la spinta '_'sarà problematica.

    Tuttavia, saremo in grado di spingere '_@'senza sforzo, seguito da un altro '@'per annullare lo scambio.

Il codice ShapeScript generato dal nostro Changeling è simile al seguente :

"0""
"#@"
"#"0"$"
"#"0"2"
"#*&"0""
"#@+"
"#0?"
"#_@"
"#@"
"#'@'"
"#'1?'"
"#'"0'"
"#'"$'"
"#'_@'"
"#'@'"
"#'8'"
"#'>'"
"#'"1'"
"#'"*+'"
"#'@'"
"#'1?'"
"#'"0'"
"#'"+$'"
"#'""&'"
"#"+"77"
"#+*!*"
"#!#"

Ho trovato il codice Changeling eseguendo il codice ShapeScript sopra tramite questo convertitore . 2

Interprete (Python 3)

#!/usr/bin/env python3

import fileinput

def error(code):
  print("This shape is " + ["unpleasant", "unpurposed", "inadequate"][code - 1] + ".")
  exit(code)

def interpret(code):
  global stack
  stringing = 0
  for char in code:
    quote = (char == "'") + 2 * (char == '"')
    if quote or stringing:
      if not stringing:
        string = ""
        stringing = quote
      elif stringing == quote:
        stack.append(string)
        stringing = 0
      else:
        string += char
    elif char in "0123456789":
      stack.append(int(char))
    else:
      x = stack.pop()
      if char == '!':
        interpret(x)
      else:
        if char == '?':
          y = stack[~x]
        elif char == "_":
          y = len(x)
        else:
          y = stack.pop()
          if char == '@':
            stack.append(x)
          elif char == '$':
            y = y.split(x)
          elif char == '&':
            y = x.join(map(str, y))
          else:
            try:
              y = eval(repr(y) + char + repr(x))
            except SyntaxError:
              error(2)
        stack.append(y)

source = ""
lengths = []

for line in fileinput.input():
  if not line or sorted(line)[:2][-1] < " " or max(line) > "~":
    error(1)
  lengths.append(len(line))
  accumulator = 0
  for char in line:
    value = ord(char)
    try:
      source += chr(value ^ (accumulator >> 1))
    except:
      error(1)
    accumulator += value - 32

lengths.append(len(lengths) + 1)

if min(lengths) != max(lengths):
  error(1)

stack = ""

for line in fileinput.input("-"):
  stack += line

stack = [stack]

try:
  interpret(source)
except:
  error(3)

print("".join(map(str, stack)))

1 Ogni riga è riempita con immondizia casuale per la quantità di righe e gli avanzamenti riga non sono effettivamente presenti.
2 I numeri in basso indicano il punto di codice più basso e più alto nel codice di cambio, che deve rimanere tra 32 e 126.


1
-1 per l'uso di xor / trasformazioni. Il passaggio da conversione a ShapeScript mi ​​assomiglia molto alla crittografia.
MegaTom,

10
@MegaTom Puoi votare a tuo piacimento, ma le domande corrugano la crittografia perché richiede una chiave , una costante nota solo al poliziotto che mette i rapinatori in una posizione di svantaggio significativo. La conversione è una trasformazione senza chiave .
Dennis,

1
ShapeScript, 67 byte: 0"#002?'+'&'0'$'0?2?-@2?>*+00'&!++'1'*'0'+@1?$0?''&@_2-2?*@+@"3*!@#. Ho rinunciato a trovare un Changeling per questo, però. Anche intervallato da dichiarazioni per lo più inutili, non sono stato in grado di ottenere più di 20 byte.
primo

2
@MegaTom In realtà sono abbastanza deluso dalla soluzione data. Mi aspettavo qualcosa di significativamente più intelligente del codice inutile del 92,9%.
primo

2
@primo Ci è voluto un po 'più di armeggiamento, ma ho trovato questo Changeling che funziona anche con Python 2. Non so quanto sia intelligente la mia risposta, ma il mio piano di inviare un poliziotto con una scappatoia che doveva essere trovata per rompersi sembra aver funzionato.
Dennis,

30

Shuffle (scritto in C ++), Cracked! di Martin

Modifica Martin l'ha rotto. Per vedere la sua soluzione, fare clic sul collegamento. Anche la mia soluzione è stata aggiunta.

Modifica Comando fisso print-dper poter gestire sia i registri che gli stack. Poiché si tratta di un comando di debug non consentito in una soluzione, ciò non dovrebbe influire su nessuno che utilizza la versione precedente dell'interprete

Sono ancora nuovo a questo, quindi se c'è qualcosa di sbagliato nella mia risposta o nel mio interprete, per favore fatemi sapere. Si prega di chiedere chiarimenti se qualcosa non è chiaro.

Non immagino che questo sarà terribilmente difficile, ma spero che fornirà una sorta di sfida. Ciò che rende lo shuffle abbastanza inutilizzabile è che stamperà solo quando le cose saranno al loro posto.

-> Nozioni di base:

Ci sono 24 stack, li chiamiamo stack1, ... stack24. Queste pile vivono in un elenco. All'inizio di ogni programma, queste pile sono pari a zero spinto e iniziano al loro posto, cioè pila i nel esima posizione nella lista (Si noti che ci indice iniziale da 1, a differenza di C ++). Durante un corso di un programma, l'ordine delle pile all'interno dell'elenco cambierà. Questo è importante per le ragioni che verranno spiegate quando discuterò i comandi.

Ci sono 5 registri disponibili per l'uso. Essi sono chiamati Alberto, Gertrude, Hans, Leopold, ShabbySam. Ognuno di questi è impostato su zero all'inizio di un programma.

Quindi, all'inizio di qualsiasi programma, ci sono 24 stack, ognuno con il suo numero corrispondente al suo indice nell'elenco degli stack. Ogni stack ha esattamente uno zero in cima. Ciascuno dei cinque registri è inizializzato su zero.

-> Comandi e sintassi :

Esistono 13 comandi (+1 comando di debug) disponibili in Shuffle. Sono i seguenti

  • cinpushquesto comando non accetta argomenti. Attende l'input della riga di comando nel modo descritto nella domanda (altri input porteranno a risultati non specificati / non definiti). Quindi divide la stringa di input in numeri interi, ad es. 101011100-> 1,1,3. Per ogni input ricevuto, effettua le seguenti operazioni: (1) consente l'elenco di stack in base al valore. Consenti al valore intero in questione di essere chiamato a . Se a è inferiore a 10, fa la permutazione u . Se a è compreso tra 9 e 30 (non inclusivo) fa la permutazione d . Altrimenti fa permutazione r . (2) Quindi spinge anello stack che è il primo nell'elenco. Nota che non intendo stack1(anche se potrebbe essere il primo caso stack1nell'elenco). Le permutazioni sono definite di seguito. Poiché cinpushè l'unico modo per ottenere l'input dell'utente , deve apparire in qualsiasi soluzione.
  • mov value registerIl movcomando è sostanzialmente un'assegnazione variabile. Assegna valuea register. valuepuò assumere diverse forme: può essere (1) un numero intero, ad es. 47 (2) il nome di un registro diverso, ad es. Hans (3) l'indice di uno stack seguito da 's', ad es 4s. Si noti che questo è l'indice nell'elenco, non il numero dello stack. Pertanto, il numero non deve superare 24.

    Alcuni movesempi:

    mov 4s Hans 
    mov Hans ShabbySam
    mov 9999 Gertrude
    
  • movfs index registerQuesto significa "sposta dallo stack". È simile al movcomando. Esiste in modo che tu possa accedere a uno stack indicizzato da un registro. Ad esempio, se in precedenza hai impostato Hans uguale a 4 ( mov 4 Hans) Quindi puoi utilizzare movfs Hans GertrudeGertrude uguale alla parte superiore dello stack 4. Questo tipo di comportamento non è accessibile semplicemente usando mov.

  • inc register aumenta il valore del registro di 1.
  • dec register diminuisce il valore del registro di 1.
  • compg value value registerQuesto sta per 'confronta maggiore'. Imposta il registro uguale al maggiore dei due valori. valuepuò essere un numero intero, un registro o un indice dello stack seguito da 's', come sopra.
  • compl value value register 'confronta meno' come sopra, tranne per il valore minore.
  • gte value1 value2 registerVerifica se value1 >= value2quindi inserisce il valore booleano (come 1 o 0) in register.
  • POP!! indexfa apparire la parte superiore dello stack indicizzato indexnell'elenco degli stack.
  • jmp labelsalta incondizionatamente all'etichetta label. Questo è un buon momento per parlare di etichette. Un'etichetta è una parola seguita da ':'. L'interprete pre-analizza le etichette, in modo da poter saltare avanti e indietro alle etichette.
  • jz value labelpassa a labelif valueè zero.
  • jnz value labelpassa a labelif valueè diverso da zero.

    Esempi di etichette e salti:

    this_is_my_label:
         jmp this_is_my_label
    
    mov 10 Hans
    jnz Hans infinite_loop
    
    infinite_loop:
         jmp infinite_loop
    
  • "shuffle" permutationEcco il comando shuffle. Questo ti permette di permutare l'elenco delle pile. Ci sono tre permutazioni validi che possono essere utilizzati come argomenti, l, f, e b.

  • print registerQuesto controlla se tutte le pile sono nelle loro posizioni iniziali, cioè lo stack i è all'indice i nell'elenco stack. In tal caso, stampa il valore in registerin unario. Altrimenti, stampa un brutto errore. Come puoi vedere, per produrre qualsiasi cosa, le pile devono essere tutte nei posti corretti.
  • done!questo dice al programma di chiudere senza errori. Se il programma termina senza done!, stamperà sulla console il numero sopra ogni pila seguito dal numero della pila. L'ordine in cui vengono stampati gli stack è l'ordine in cui compaiono nell'elenco stack. Se uno stack è vuoto, verrà omesso. Questo comportamento è a scopo di debug e non può essere utilizzato in una soluzione.
  • print-d valuequesto stampa il valore dello stack, del registro o dell'intero dato (per accedere allo stack i , passare iscome argomento, come spiegato sopra). Questo è uno strumento di debug e non fa parte del linguaggio, in quanto tale non è consentito in una soluzione.

-> Ecco il codice dell'interprete

Tutto l'analisi avviene nella funzione principale. Qui è dove lo troverai analizzando comandi specifici.

#include<fstream>
#include<iostream>
#include<string>
#include<stack>
#include<cmath>

using namespace std;

class superStack: public stack<long> {
    private:
        int m_place;
    public:
        static int s_index;


        superStack() {
            m_place = s_index;
            s_index++;
        }

        int place() {
            return m_place;
        }
};
int superStack::s_index=1;

superStack  stack1,stack2,stack3,stack4,stack5,stack6,stack7,stack8,stack9,stack10,stack11, \
            stack12,stack13,stack14,stack15,stack16,stack17,stack18,stack19,stack20,stack21,stack22,stack23,stack24;


superStack* stackptrs[]=    { \
                        &stack1,&stack2,&stack3,&stack4,&stack5,&stack6,&stack7,&stack8,&stack9,&stack10,&stack11, \
                        &stack12,&stack13,&stack14,&stack15,&stack16,&stack17,&stack18,&stack19,&stack20,&stack21,&stack22,&stack23,&stack24 \
                        };


long Gertrude=0;
long Hans=0;
long Alberto=0;
long ShabbySam=0;
long Leopold=0;


void SWAP( int i, int j) {    // 0 < i,j < 25

    i--;
    j--;


    superStack* tempptr = stackptrs[i];
    stackptrs[i]=stackptrs[j];
    stackptrs[j] = tempptr;



}

void u() {
    int list[3][4] = {
                        {1,9,6,13},
                        {2,10,5,14},
                        {17,19,20,18},
                    };

    for(int i=0;i<3;i++) {
        for(int j=1;j<4;j++) {
            SWAP( list[i][0], list[i][j] );         
        }
    }
}
void d() {
    int list[3][4] = {
                        {3,11,8,15},
                        {4,12,7,16},
                        {22,24,23,21},
                    };

    for(int i=0;i<3;i++) {
        for(int j=1;j<4;j++) {
            SWAP( list[i][0], list[i][j] );         
        }
    }
}
void r() {
    int list[3][4] = {
                        {2,17,8,24},
                        {4,18,6,23},
                        {9,10,12,11},
                    };

    for(int i=0;i<3;i++) {
        for(int j=1;j<4;j++) {
            SWAP( list[i][0], list[i][j] );         
        }
    }
}
void l() {
    int list[3][4] = {
                        {1,19,7,22},
                        {3,20,5,21},
                        {14,13,15,16},
                    };

    for(int i=0;i<3;i++) {
        for(int j=1;j<4;j++) {
            SWAP( list[i][0], list[i][j] );         
        }
    }
}
void f() {
    int list[3][4] = {
                        {20,9,24,16},
                        {18,11,22,14},
                        {1,2,4,3},
                    };

    for(int i=0;i<3;i++) {
        for(int j=1;j<4;j++) {
            SWAP( list[i][0], list[i][j] );         
        }
    }
}
void b() {
    int list[3][4] = {
                        {19,10,23,15},
                        {17,12,21,13},
                        {5,6,8,7},
                    };

    for(int i=0;i<3;i++) {
        for(int j=1;j<4;j++) {
            SWAP( list[i][0], list[i][j] );         
        }
    }
}

void splitpush(string input) {
    long value=0;

    for(long i=0;i<input.size();i++) {

        if(input[i]=='1'){
            value++;
        }
        else if(input[i]=='0' && value!=0 ) {
            if(value<10) {
                u();
            }
            else if (value<30) {
                d();

            }
            else {
                r();
            }
            (*stackptrs[0]).push(value);
            value=0;

        }
        else {
            break;
        }

    }

}

long strToInt(string str) { // IF STRING HAS NON DIGITS, YOU WILL GET GARBAGE, BUT NO ERROR
    long j=str.size()-1;
    long value = 0;
    for(long i=0;i<str.size();i++) {
        long x = str[i]-48;

        value+=x*floor( pow(10,j) );
        j--;
    }
    return value;
}

bool isEmpty(superStack stk) {
    if( stk.size()>0){return false; }
    else {return true;}

}    

long getValue(string str) {
    if(str=="ShabbySam") {
        return ShabbySam;
    }
    else if(str=="Hans") {
        return Hans;
    }
    else if(str=="Gertrude") {
        return Gertrude;
    }
    else if(str=="Alberto") {
        return Alberto;
    }   
    else if(str=="Leopold") {
        return Leopold;
    }
    else if(str[ str.size()-1 ]=='s'){
        str.pop_back();

        long index = strToInt(str)-1;

        if( !isEmpty( (*stackptrs[index]) ) ) {
            return (*stackptrs[index]).top();
        }
        else {
            cerr<<"Bad Expression or Empty Stack";


        }   
    }
    else {
        return strToInt(str);
    }

}

void printUnary(long i) {
    while(i>0) {
        cout<<1;
        i--;
    }
}

int main(int argc, char**argv) {

    if(argc<2){std::cerr<<"No input file given"; return 1;}
    ifstream inf(argv[1]);
    if(!inf){std::cerr<<"File open failed";return 1;}

    for(int i=0;i<24;i++) { 
        (*stackptrs[i]).push(0);         // Pre push a zero on every stack
    }

    string labels[20];
    unsigned labelPoints[20];
    int index=0;



    string str;
    while(inf) { //  parse for labels
        inf>>str;
        //cout<<inf.tellg()<<" ";
        if(inf) {
            if(str[str.size()-1]==':') {
                str.pop_back();
                bool alreadyExists = false;
                for(int i=0; i<index;i++){
                    if(labels[i] == str ) { alreadyExists=true;}
                }
                if(!alreadyExists) {
                    labels[index]=str;
                    labelPoints[index]= inf.tellg();
                    index++;
                }
            }

        }

    }
    inf.clear();
    inf.seekg(0,inf.beg);

    while(inf) { // parse for other commands 
        inf>>str;

        if(inf) {


            if(str=="cinpush") {
                string input;
                cin>>input;
                splitpush(input);
            }

            else if(str=="mov") {
                inf>>str;
                long value = getValue(str);

                inf>>str;
                if(str=="Gertrude"){Gertrude=value;}
                else if(str=="Hans"){Hans=value;}
                else if(str=="ShabbySam"){ShabbySam=value;}
                else if(str=="Alberto"){Alberto=value;}
                else if(str=="Leopold"){Leopold=value;}
                else {cerr<<"Bad register name. ";}

            }

            else if(str=="movfs") {
                inf>>str;
                long index = getValue(str);
                if(!isEmpty( *stackptrs[index-1] )) {
                    inf>>str;
                    long value = (*stackptrs[index-1]).top();
                    if(str=="Gertrude"){Gertrude=value;}
                    else if(str=="Hans"){Hans=value;}
                    else if(str=="ShabbySam"){ShabbySam=value;}
                    else if(str=="Alberto"){Alberto=value;}
                    else if(str=="Leopold"){Leopold=value;}
                    else {cerr<<"Bad register name.";}
                }
                else {
                    cerr<<"Empty Stack";
                }



            }

            else if(str=="inc") {
                inf>>str;
                if(str=="Gertrude"){Gertrude++;}
                else if(str=="Hans"){Hans++;}
                else if(str=="ShabbySam"){ShabbySam++;}
                else if(str=="Alberto"){Alberto++;}
                else if(str=="Leopold"){Leopold++;}
                else {cerr<<"Bad register name. ";}
            }
            else if(str=="dec") {
                inf>>str;
                if(str=="Gertrude"){Gertrude--;}
                else if(str=="Hans"){Hans--;}
                else if(str=="ShabbySam"){ShabbySam--;}
                else if(str=="Alberto"){Alberto--;}
                else if(str=="Leopold"){Leopold--;}
                else {cerr<<"Bad register name. ";}
            }


            else if(str=="compg") {
                inf>>str;
                long value1 = getValue(str);
                inf>>str;
                long value2 = getValue(str);
                inf>>str;
                long larger;
                if(value1>value2){larger = value1;}
                else {larger = value2;}

                if(str=="Gertrude"){Gertrude=larger;}
                else if(str=="Hans"){Hans=larger;}
                else if(str=="ShabbySam"){ShabbySam=larger;}
                else if(str=="Alberto"){Alberto=larger;}
                else if(str=="Leopold"){Leopold=larger;}
                else {cerr<<"Bad register name. ";}

            }
            else if(str=="compl") {
                inf>>str;
                long value1 = getValue(str);
                inf>>str;
                long value2 = getValue(str);
                inf>>str;
                long larger; //LARGER IS REALLY SMALLER HERE
                if(value1>value2){larger = value2;}
                else {larger = value1;}

                if(str=="Gertrude"){Gertrude=larger;}
                else if(str=="Hans"){Hans=larger;}
                else if(str=="ShabbySam"){ShabbySam=larger;}
                else if(str=="Alberto"){Alberto=larger;}
                else if(str=="Leopold"){Leopold=larger;}
                else {cerr<<"Bad register name. ";}

            }

            else if(str=="gte") {
                inf>>str;
                long value1= getValue(str);
                inf>>str;
                long value2= getValue(str);
                inf>>str;
                bool torf = value1 >= value2;

                if(str=="Gertrude"){Gertrude=torf;}
                else if(str=="Hans"){Hans=torf;}
                else if(str=="ShabbySam"){ShabbySam=torf;}
                else if(str=="Alberto"){Alberto=torf;}
                else if(str=="Leopold"){Leopold=torf;}
                else {cerr<<"Bad register name. ";}

            }

            else if(str=="lte") {
                inf>>str;
                long value1= getValue(str);
                inf>>str;
                long value2= getValue(str);
                inf>>str;
                bool torf = value1 <= value2;

                if(str=="Gertrude"){Gertrude=torf;}
                else if(str=="Hans"){Hans=torf;}
                else if(str=="ShabbySam"){ShabbySam=torf;}
                else if(str=="Alberto"){Alberto=torf;}
                else if(str=="Leopold"){Leopold=torf;}
                else {cerr<<"Bad register name. ";}

            }

            else if(str=="POP!!") {
                inf>>str;
                long index = getValue(str);
                index--; //because we (C++ and this interpreter) index differently
                if(!isEmpty( *stackptrs[index] )) {
                    (*stackptrs[index]).pop();
                }
                else {cerr<<"Can't POP!! from empty stack";}

            }

            else if(str=="push"){cerr<<" You can't. ";}

            /*
            else if(str[str.size()-1]==':') {
                str.pop_back();
                bool alreadyExists = false;
                for(int i=0; i<index;i++){
                    if(labels[i] == str ) { alreadyExists=true;}
                }
                if(!alreadyExists) {
                    labels[index]=str;
                    labelPoints[index]= inf.tellg();
                    index++;
                }
            }*/

            else if(str=="jmp") {
                inf>>str;
                for(int i=0;i<index;i++) {
                    if( labels[i]==str) {
                        inf.seekg( labelPoints[i], ios::beg);
                    }
                }
            }
            else if(str=="jz") {
                inf>>str;
                long value = getValue(str);

                if(value==0) {
                    inf>>str;
                    for(int i=0;i<index;i++) {
                        if( labels[i]==str) {
                            inf.seekg( labelPoints[i], ios::beg);
                        }
                    }
                }
            }

            else if(str=="jnz") {
                inf>>str;
                long value = getValue(str);

                if(value!=0) {
                    inf>>str;
                    for(int i=0;i<index;i++) {
                        if( labels[i]==str) {
                            inf.seekg( labelPoints[i], ios::beg);
                        }
                    }
                }
            }

            else if(str== "\"shuffle\"") {
                inf>>str;
                if(str=="l"){ l(); }
                else if(str=="f"){ f(); }
                else if(str=="b"){ b(); }
                else {cerr<<"Bad shuffle parameter";}

            }

            else if(str=="print") {

                for(int i=0;i<24;i++) {

                    if( (i+1) != (*stackptrs[i]).place() ) {
                        cerr<< "Sorry, your stacks are in the wrong place! You can't print unless you put them back! Exiting. ";
                        return 1;
                    }

                }
                inf>>str;
                if(str=="Gertrude"){printUnary(Gertrude);}
                else if(str=="Hans"){printUnary(Hans);}
                else if(str=="ShabbySam"){printUnary(ShabbySam);}
                else if(str=="Alberto"){printUnary(Alberto);}
                else if(str=="Leopold"){printUnary(Leopold);}
                else {cerr<<"Bad register name. ";}


            }

            else if(str=="done!") {return 0;}

            else if(str=="print-d" ){
                inf>>str;
                long value = getValue(str);
                cout<<str;
              }
        }

    }







    /*for(int i=1;i<25;i++) {
        (*(stackptrs[i-1])).push(i);
    }

    u();d();r();l();f();b();
    */

    cout<<"\n";
    for(int i=1;i<25;i++) {
        if( (*(stackptrs[i-1])).size()>0 ) {
            cout<<(*(stackptrs[i-1])).top()<<" "<<(*(stackptrs[i-1])).place()<<"\n";
            (*(stackptrs[i-1])).pop();
        }
    }
    /*
    for (int i=0;i<index;i++) {
        cout<<labels[i]<<": "<<labelPoints[i]<<"\n";
    }*/

    return 1;
}

-> Permutazioni Le permutazioni permeano gli elementi dell'elenco di stack nel modo seguente:

Dove significa quello

(Questi appaiono anche nel codice dell'interprete. Se c'è una discrepanza, l'interprete è corretto.)

-> Esempio semplice

Questi due semplici programmi stampano i numeri da 24 a 1 (in unario) senza spazi bianchi.

mov 24 Hans
start:
    print Hans
    dec Hans
    jnz Hans start
done!

o

mov 24 Hans start: print Hans dec Hans jnz Hans start done!

Sono lo stesso programma.

Spiegazione e soluzione:

Martin ha anche una buona spiegazione nella sua risposta .

Come Martin ha capito, questa lingua è stata ispirata dal cubo tascabile (noto anche come cubo di Rubik 2x2). Le 24 pile sono come i 24 singoli quadrati sul cubo. Le permutazioni sono le mosse di base consentite: su, giù, destra, sinistra, fronte, retro.

La lotta principale qui è che quando i valori vengono spinti, vengono utilizzate solo tre mosse: su, giù e destra. Tuttavia, non hai accesso a queste mosse quando "mescoli" le pile. Hai solo le altre tre mosse.

A quanto pare, entrambi i gruppi di tre mosse coprono effettivamente l'intero gruppo (cioè sono generatori), quindi il problema è risolvibile. Ciò significa che puoi effettivamente risolvere qualsiasi cubo di Rubik 2x2 usando solo 3 delle mosse.

Tutto quello che resta da fare è capire come annullare le mosse su, giù e destra usando le altre tre. A tal fine, ho utilizzato un sistema di algebra del computer chiamato GAP .

Dopo aver annullato le permutazioni, trovare il terzo numero più grande è abbastanza banale.

cinpush
main:
    mov 1s ShabbySam
    POP!! 1
    jmp compare
    continue:
        gte 0 ShabbySam Leopold
        jnz Leopold end
        gte ShabbySam 9 Leopold
        jz Leopold uinverse
        gte ShabbySam 29 Leopold
        jz Leopold dinverse
        jnz Leopold rinverse
compare:
    gte ShabbySam Alberto Leopold
    jz Leopold skip
    mov Gertrude Hans
    mov Alberto Gertrude
    mov ShabbySam Alberto
    jmp continue
    skip:
        gte ShabbySam Gertrude Leopold
        jz Leopold skip_2
        mov Gertrude Hans
        mov ShabbySam Gertrude
        jmp continue
    skip_2:
        gte ShabbySam Hans Leopold
        jz Leopold continue
        mov ShabbySam Hans
        jmp continue
uinverse: 
    "shuffle" f
    "shuffle" f
    "shuffle" f
    "shuffle" l
    "shuffle" b
    "shuffle" l
    "shuffle" b
    "shuffle" b
    "shuffle" b
    "shuffle" l
    "shuffle" l
    "shuffle" l
    "shuffle" b
    "shuffle" b
    "shuffle" b
    "shuffle" l
    "shuffle" l
    "shuffle" l
    "shuffle" f
    jmp main
dinverse:
    "shuffle" f
    "shuffle" b
    "shuffle" l
    "shuffle" b
    "shuffle" b
    "shuffle" b
    "shuffle" f
    "shuffle" f
    "shuffle" f
    jmp main
rinverse: 
    "shuffle" b "shuffle" l "shuffle" f "shuffle" l "shuffle" b
    "shuffle" f "shuffle" f "shuffle" f "shuffle" b
    "shuffle" l "shuffle" l "shuffle" l
    "shuffle" b "shuffle" b "shuffle" b
    "shuffle" f "shuffle" f "shuffle" f
    "shuffle" l "shuffle" f "shuffle" l "shuffle" f
    "shuffle" l "shuffle" f "shuffle" f "shuffle" f
    "shuffle" l "shuffle" l "shuffle" l 
    "shuffle" f "shuffle" l "shuffle" l 
    "shuffle" f "shuffle" f "shuffle" f
    "shuffle" l "shuffle" l "shuffle" l
    "shuffle" l "shuffle" l "shuffle" l "shuffle" f
    "shuffle" l "shuffle" l "shuffle" l
    "shuffle" f "shuffle" f "shuffle" f
    "shuffle" l "shuffle" l "shuffle" l
    "shuffle" f "shuffle" f "shuffle" f
    "shuffle" l "shuffle" l "shuffle" l
    "shuffle" f "shuffle" f "shuffle" f
    "shuffle" l "shuffle" l "shuffle" l
    "shuffle" f "shuffle" f "shuffle" f
    "shuffle" l "shuffle" f "shuffle" l "shuffle" f "shuffle" l "shuffle" f
    "shuffle" l "shuffle" l "shuffle" l
    jmp main
end:
    print Hans
    done!

2
Cracked. :) Sono davvero colpito dalla lingua!
Martin Ender,

Wow, è stato più veloce di quanto mi aspettassi. Grazie, sono contento che sia stato divertente capire come era scrivere.
Liam,

Sono curioso, sarebbe stato decentemente più difficile se avessi cambiato i nomi delle permutazioni in qualcosa di meno evidente sui cubi di Rubik?
Liam,

Erano decisamente un indizio, ma penso che non avrebbe preso che molto più a lungo se avessero avuto nomi diversi.
Martin Ender,

Heh, sembra che GAP non sia stato particolarmente intelligente nell'invertire le tre permutazioni di input. ;)
Martin Ender,

22

Brian e Chuck , incrinati dabox_box

Sono stato incuriosito da qualche tempo dall'idea di un linguaggio di programmazione in cui due programmi interagiscono tra loro (probabilmente ispirati da ROCB ). Questa sfida è stata un buon incentivo per affrontare questo concetto alla fine cercando di mantenere la lingua il più minimale possibile. Gli obiettivi del progetto erano di rendere completo il linguaggio Turing mentre ciascuna delle sue parti singolarmente non erano complete. Inoltre, anche entrambi non dovrebbero essere completi di Turing senza ricorrere alla manipolazione del codice sorgente. Io penso che abbia riuscito con questo, ma non ho formalmente ancora provata nessuna di queste cose. Quindi senza ulteriori indugi vi presento ...

I protagonisti

Brian e Chuck sono due programmi simili a Brainfuck. Solo uno di loro viene eseguito in qualsiasi momento, a partire da Brian. Il problema è che il nastro di memoria di Brian è anche il codice sorgente di Chuck. E il nastro di memoria di Chuck è anche il codice sorgente di Brian. Inoltre, la testina di Brian è anche il puntatore di istruzioni di Chuck e viceversa. I nastri sono semi-infiniti (cioè infiniti a destra) e possono contenere numeri interi di precisione arbitraria firmati, inizializzati su zero (se non diversamente specificato dal codice sorgente).

Poiché il codice sorgente è anche un nastro di memoria, i comandi sono tecnicamente definiti da valori interi, ma corrispondono a caratteri ragionevoli. Esistono i seguenti comandi:

  • ,( 44): Legge un byte da STDIN nella cella di memoria corrente. Solo Brian può farlo. Questo comando è vietato per Chuck.
  • .( 46): Scrivi la cella di memoria corrente, modulo 256, come byte su STDOUT. Solo Chuck può farlo. Questo comando è vietato per Brian.
  • +( 43): Incrementa la cella di memoria corrente.
  • -( 45): Decrementa la cella di memoria corrente.
  • ?( 63): Se la cella di memoria corrente è zero, questa è una no-op. Altrimenti, controllo manuale sull'altro programma. La testina del nastro sul programma che utilizza ?rimarrà sul ?. La testina dell'altro programma sposta una cella verso destra prima di eseguire il primo comando (quindi la cella utilizzata come test non viene eseguita da sola).
  • <( 60): Sposta la testina del nastro di una cella a sinistra. Questa è una no-op se la testina del nastro è già all'estremità sinistra del nastro.
  • >( 62): Sposta la testina del nastro di una cella verso destra.
  • {( 123): Spostare ripetutamente la testina del nastro verso sinistra fino a raggiungere la cella corrente a zero o raggiungere l'estremità sinistra del nastro.
  • }( 125): Spostare ripetutamente la testina del nastro verso destra fino a quando la cella corrente è zero.

Il programma termina quando il puntatore di istruzioni del programma attivo raggiunge un punto in cui non ci sono più istruzioni alla sua destra.

Il codice sorgente

Il file di origine viene elaborato come segue:

  • Se il file contiene la stringa ```, il file verrà suddiviso in due parti intorno alla prima occorrenza di quella stringa. Tutti gli spazi bianchi iniziali e finali vengono eliminati e la prima parte viene utilizzata come codice sorgente per Brian e la seconda parte per Chuck.
  • Se il file non contiene questa stringa, la prima riga del file verrà utilizzata come origine per Brian e la seconda parte per Chuck (a parte la delimitazione della nuova riga, non verrà rimosso nessuno spazio).
  • Tutte le occorrenze di _in entrambi i programmi vengono sostituite con byte NULL.
  • I due nastri di memoria sono inizializzati con i codici carattere corrispondenti alla stringa risultante.

Ad esempio, il seguente file di origine

  abc
```
0_1
23

Produrrebbe i seguenti nastri iniziali:

Brian: [97 98 99 0 0 0 0 ...]
Chuck: [48 0 49 10 50 51 0 0 0 0 ...]

L'interprete

L'interprete è scritto in Ruby. Sono necessari due flag della riga di comando che non devono essere utilizzati da alcuna soluzione (poiché non fanno parte delle specifiche della lingua effettiva):

  • -d: Con questa bandiera, Brian e Chuck comprendono altri due comandi. !stamperà una rappresentazione in formato stringa di entrambi i nastri di memoria, il programma attivo verrà elencato per primo (a ^contrassegnerà le testine del nastro correnti). @farà anche questo, ma poi termina immediatamente il programma. Perché sono pigro, nessuno di questi funziona se sono l'ultimo comando nel programma, quindi se vuoi usarli lì, ripeterli o scrivere una no-op dopo di loro.
  • -D: Questa è la modalità di debug dettagliata. Stamperà le stesse informazioni di debug di !ogni singolo segno di spunta. @funziona anche in questa modalità.

Ecco il codice:

# coding: utf-8

class Instance
    attr_accessor :tape, :code, :ip

    OPERATORS = {
        '+'.ord  => :inc,
        '-'.ord  => :dec,
        '>'.ord  => :right,
        '<'.ord  => :left,
        '}'.ord  => :scan_right,
        '{'.ord  => :scan_left,
        '?'.ord  => :toggle,
        ','.ord  => :input,
        '.'.ord  => :output,
        '!'.ord  => :debug,
        '@'.ord  => :debug_terminate
    }

    OPERATORS.default = :nop

    def initialize(src)
        @code = src.chars.map(&:ord)
        @code = [0] if code.empty?

        @ip = 0
    end

    def tick
        result = :continue
        case OPERATORS[@code[@ip]]
        when :inc
            @tape.set(@tape.get + 1)
        when :dec
            @tape.set(@tape.get - 1)
        when :right
            @tape.move_right
        when :left
            @tape.move_left
        when :scan_right
            @tape.move_right while @tape.get != 0
        when :scan_left
            @tape.move_left while @tape.ip > 0 && @tape.get != 0
        when :toggle
            if @tape.get != 0
                @tape.move_right
                result = :toggle
            end
        when :input
            input
        when :output
            output
        when :debug
            result = :debug
        when :debug_terminate
            result = :debug_terminate
        end

        return :terminate if result != :toggle && @ip == @code.size - 1

        move_right if result != :toggle

        return result
    end

    def move_right
        @ip += 1
        if @ip >= @code.size
            @code << 0
        end
    end

    def move_right
        @ip += 1
        if @ip >= @code.size
            @code << 0
        end
    end

    def move_left
        @ip -= 1 if @ip > 0
    end

    def get
        @code[@ip]
    end

    def set value
        @code[@ip] = value
    end

    def input() end
    def output() end

    def to_s
        str = self.class.name + ": \n"
        ip = @ip
        @code.map{|i|(i%256).chr}.join.lines.map do |l|
            str << l.chomp << $/
            str << ' '*ip << "^\n" if 0 <= ip && ip < l.size
            ip -= l.size
        end
        str
    end
end

class Brian < Instance
    def input
        byte = STDIN.read(1)
        @tape.set(byte ? byte.ord : -1)
    end
end

class Chuck < Instance
    def output
        $> << (@tape.get % 256).chr
    end
end

class BrianChuck

    class ProgramError < Exception; end

    def self.run(src, debug_level=0)
        new(src, debug_level).run
    end

    def initialize(src, debug_level=false)
        @debug_level = debug_level

        src.gsub!('_',"\0")

        if src[/```/]
            brian, chuck = src.split('```', 2).map(&:strip)
        else
            brian, chuck = src.lines.map(&:chomp)
        end

        chuck ||= ""

        brian = Brian.new(brian)
        chuck = Chuck.new(chuck)

        brian.tape = chuck
        chuck.tape = brian

        @instances = [brian, chuck]
    end

    def run
        (puts @instances; puts) if @debug_level > 1

        loop do
            result = current.tick

            toggle if result == :toggle

            (puts @instances; puts) if @debug_level > 1 || @debug_level > 0 && (result == :debug || result == :debug_terminate)

            break if result == :terminate || @debug_level > 0 && result == :debug_terminate
        end
    end

    private

    def current
        @instances[0]
    end

    def toggle
        @instances.reverse!
    end
end

case ARGV[0]
when "-d"
    debug_level = 1
when "-D"
    debug_level = 2
else
    debug_level = 0
end

if debug_level > 0
    ARGV.shift
end

BrianChuck.run(ARGF.read, debug_level)

Ecco la mia soluzione (scritta a mano) al problema:

>}>}>
brace left: >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
arrow left: >++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
brace left: >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
arrow left: >++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
question mk: >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>>} Append a bunch of 1s as a dummy list element:
+>+>+>+>+>+>+>+>+>+
Append two 1s and some code to the list; the first is a marker for later; the latter will be the integer
1: >+
brace right: >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
arrow left: >++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
question mk: >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1: >+
brace right: >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
arrow right: >++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
brace right: >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
arrow left: >++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
question mk: >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
_
{<<<<<<<<<{<{    Move back to the start
Read a character and subtract 48 to get actual 0 or 1
,------------------------------------------------
?   If 1 switch to Chuck otherwise continue
>}>}>>>>>>>>>}<<<<<<- Subtract 1 from the result to account for initial 1
?   If integer was positive switch to Chuck
@todo: process end
_
This code is run when reading 1:
}>}>>>>>>>>>}<<<<<<+ Move to the end of Chuck; skip one null cell; move to the end of the list
{<<<<<<<<<{<?   Move back to the code that resets this loop.
_
This code is run after finalising an integer:
change the code after the integer first
<<--<--<--}
Append two 1s and some code to the list; the first is a marker for later; the latter will be the integer
1: +
brace right: >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
arrow left: >++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
question mk: >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1: >+
brace right: >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
arrow right: >++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
brace right: >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
arrow left: >++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
question mk: >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
{<<<<<<<+<<{<{    Move back to the start; incrementing the list length
Read a character and subtract 48 to get actual 0 or 1
,------------------------------------------------
?   If 1 switch to Chuck otherwise continue
>}>}>>>>>>>>>}
Leave the resetting code, but remove the rest of the last list element:
<<<--<--<--
1: <-
question mk: <---------------------------------------------------------------
arrow left: <------------------------------------------------------------
brace right: <-----------------------------------------------------------------------------------------------------------------------------
1: <-
<{< Move back to the cell we reserved for the counter
<<<<<<-- decrement list size by two so we don't process the two largest elements
_

<{<<<<<<{<{<{<{<{>}>}>>>>>>> This is the beginning of the loop which decrements all list elements by 1
+ Add 1 to the running total
>>- Set marker of dummy list element to zero
_ Beginning of loop that is run for each list element
<{<<<<<<{<{<{<{<{>}>}>}>}+ set last marker back to 1
>>>>>>>>>> move to next marker
? Skip the next bit if we're not at the end of the list
<? Move back to the beginning of the loop
@ we should never get here
_
This is run when we're not at the end of the list
<<<- Set marker to 0 to remember current position
>>>>- Move to the current value and decrement it
? Move back to loop beginning if value is not zero yet
- Make element negative so it's skipped in the future
{<{<{>- Move to remaining list length and decrement it
? If it's nonzero switch to Chuck
>>>>>>>>->->->->->->->->->- Remove the dummy list to make some space for new code:
>}+ Fill in the marker in the list
{<<<<<<<<< Add some loop resetting code after the result:
brace left: +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
arrow left: >++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
question mk: >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
_
This loop adds a print command to Chuck's code while decrementing the result
>>>>>>>>}>>>>>}>>>>>} Move to the very end of Chuck's code and leave some space to seek the 1
print: ++++++++++++++++++++++++++++++++++++++++++++++
{<<<<<{<<<<<{<<<<<<<{<
- decrement the result
? if nonzero run loop again
At this point we just need to make Chuck seek for the 1 at the end of this code print it often enough
>>}>>>>>>>}>>>>>}
arrow right: ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
<?1 Switch to Chuck which moves Brian's tape head onto the 1 and then prints it N times


```

_   Dummy cell to hold input
This code is run when reading a 1:
{<{<{<{ ensure we're at the start
}>}<? Skip 0 handling code and switch to Brian
_ This code is run after a 1 has been processed:
{<{<?

Questo codice è eseguibile così com'è, poiché tutte le annotazioni utilizzano no-op e vengono ignorate da {e }.

L'idea di base è:

  1. Aggiungi un nuovo elemento zero all'elenco (alla fine del nastro di Chuck) e aumenta la lunghezza dell'elenco di 1.
  2. Mentre stiamo leggendo 1s, incrementa quell'elemento.
  3. Quando leggiamo un 0, fai un po 'di pulizia. Se il numero intero risultante era maggiore di zero, tornare a 1.

    Ora abbiamo un elenco dei numeri di input alla fine del nastro di Chuck e conosciamo la lunghezza dell'elenco.

  4. Sottrai 2 dalla lunghezza dell'elenco (quindi i seguenti passaggi ignorano i due elementi più grandi), chiama questo N.

  5. Mentre N > 0, incrementa un totale parziale e quindi decrementa tutti gli elementi dell'elenco. Ogni volta che un elemento dell'elenco raggiunge lo zero, diminuisce N.

    Alla fine di questo, il totale parziale conterrà il terzo numero di ingresso, M.

  6. Scrivi Mcopie .fino alla fine del nastro di Chuck.

  7. Su Chuck, cerca un 1nastro di Brian e quindi esegui quelli generati .alla fine.

Dopo aver finito questo mi sono reso conto che avrei potuto semplificarlo abbastanza in alcuni punti. Ad esempio, invece di tenere traccia di quel contatore e scrivere quelli .sul nastro di Chuck, potrei semplicemente stampare 1subito, prima di diminuire tutti gli elementi della lista. Tuttavia, apportare modifiche a questo codice è piuttosto doloroso, perché diffonde altre modifiche ovunque, quindi non mi sono preoccupato di apportare la modifica.

La parte interessante è come tenere traccia dell'elenco e come iterarlo. Non puoi semplicemente memorizzare i numeri back-to-back sul nastro di Chuck, perché se vuoi verificare una condizione su uno degli elementi dell'elenco, rischi di eseguire il resto dell'elenco che potrebbe contenere un codice valido. Inoltre non sai quanto sarà lungo l'elenco, quindi non puoi semplicemente riservare un po 'di spazio davanti al codice di Chuck.

Il prossimo problema è che dobbiamo abbandonare l'elenco per ridurlo Nmentre lo stiamo elaborando e dobbiamo tornare nello stesso posto in cui eravamo prima. Ma {e }sarebbe proprio saltare passato l'intero elenco.

Quindi dobbiamo scrivere un po 'di codice in modo dinamico su Chuck. In effetti, ogni elemento dell'elenco iha la forma:

[1 <Code A> i <Code B>]

1è un marker che possiamo impostare a zero per indicare da dove abbiamo interrotto l'elaborazione dell'elenco. Il suo scopo è quello di catturare {o }che passerà semplicemente sopra il codice e il i. Usiamo anche questo valore per verificare se siamo alla fine dell'elenco durante l'elaborazione, quindi mentre non lo siamo, questo sarà 1e il condizionale ?cambierà il controllo in Chuck. Code Aè usato per gestire quella situazione e spostare di conseguenza l'IP su Brian.

Ora, quando diminuiamo, idobbiamo verificare se iè già zero. Anche se non lo è, ?cambierà di nuovo il controllo, quindi Code Bè lì per gestirlo.



@cardboard_box Nice!
mbomb007,

15

HPR, scritto in Python 3 ( Cracked by TheNumberOne )

HPR (il nome non significa nulla) è una lingua per l'elaborazione di elenchi di numeri interi. È progettato per essere minimalista , estremamente limitato e privo di restrizioni "artificiali" . La programmazione in HPR è dolorosa, non perché devi risolvere un puzzle per impedire all'interprete di urlare contro di te, ma perché è difficile fare in modo che un programma faccia qualcosa di utile. Non so se HPR sia in grado di fare qualcosa di sostanzialmente più interessante che calcolare il terzo elemento più grande di un elenco.

Specifica del linguaggio

Un programma HPR viene eseguito in un ambiente , che è un insieme non ordinato duplicato di numeri interi non negativi e liste di numeri interi non negativi. Inizialmente, l'ambiente contiene solo l'elenco di input (l'interprete lo analizza per te). Esistono tre comandi e due operatori "flusso di controllo" che modificano l'ambiente:

  • *rimuove il primo elemento di ogni elenco non vuoto nell'ambiente e lo inserisce nell'ambiente. Gli elenchi vuoti non sono interessati. Ad esempio, si trasforma

    {4,1,[0,2],[1,3],[]} -> {4,1,0,[2],[3],[]}
    
  • -decrementa tutti i numeri nell'ambiente, quindi rimuove gli elementi negativi. Ad esempio, si trasforma

    {4,2,0,[0,2],[4,4,4]} -> {3,1,[0,2],[4,4,4]}
    
  • $ruota ogni elenco nell'ambiente di un passo a sinistra. Ad esempio, si trasforma

    {4,1,[0,2],[3,4,1]} -> {4,1,[2,0],[4,1,3]}
    
  • !(A)(B), dove Ae Bsono programmi, è sostanzialmente un whileciclo. Esegue "l'azione" Afino a quando il "test" non Bsi tradurrebbe in un ambiente vuoto. Questo può produrre un ciclo infinito.
  • #(A)(B), dove Ae Bsono programmi, si applica Ae Ball'ambiente attuale e assume la differenza simmetrica dei risultati.

I comandi vengono eseguiti da sinistra a destra. Alla fine, la dimensione dell'ambiente viene stampata in modo unario.

L'interprete

Questo interprete presenta il comando debug ?, che stampa l'ambiente senza modificarlo. Non dovrebbe apparire in nessuna soluzione all'attività. Tutti i caratteri tranne *-$!#()?vengono semplicemente ignorati, quindi puoi scrivere commenti direttamente nel codice. Infine, l'interprete riconosce il linguaggio !(A)(#(A)())come "esegui Afino a quando il risultato non cambia più", e lo ottimizza per una maggiore velocità (ne avevo bisogno per ottenere la mia soluzione per finire in meno di un'ora sull'ultimo test case).

import sys

def parse(prog):
    "Parse a prefix of a string into an AST. Return it and the remaining input."
    ret = []
    while prog:
        if prog[0] in "#!":
            sub1, prog1 = parse(prog[2:])
            sub2, prog2 = parse(prog1[1:])
            ret += [prog[0],sub1,sub2]
            prog = prog2
        elif prog[0] == ')':
            prog = prog[1:]
            break
        else:
            ret += [prog[0]]
            prog = prog[1:]
    return ret, prog

def intp(ast, L_env, N_env):
    "Interpret the AST on an environment, return the resulting environment."
    ptr = 0
    while ptr < len(ast):
        cmd = ast[ptr]
        if cmd == '*':
            N_env = N_env | set(L[0] for L in L_env if L)
            L_env = set(L[1:] for L in L_env)
            ptr += 1
        elif cmd == '-':
            N_env = set(N-1 for N in N_env if N>0)
            ptr += 1
        elif cmd == '$':
            L_env = set(L[1:]+L[:1] for L in L_env)
            ptr += 1
        elif cmd == '!':
            # Speed optimization
            cond = ast[ptr+2]
            if cond == ['#', ast[ptr+1], []]:
                L_next, N_next = intp(ast[ptr+1], L_env, N_env)
                while L_next != L_env or N_next != N_env:
                    L_env, N_env = L_next, N_next
                    L_next, N_next = intp(ast[ptr+1], L_env, N_env)
            else:
                while True:
                    L_test, N_test = intp(cond, L_env, N_env)
                    if not L_test and not N_test:
                        break
                    L_env, N_env = intp(ast[ptr+1], L_env, N_env)
            ptr += 3
        elif cmd == '#':
            L_env1, N_env1 = intp(ast[ptr+1], L_env, N_env)
            L_env2, N_env2 = intp(ast[ptr+2], L_env, N_env)
            L_env = L_env1 ^ L_env2
            N_env = N_env1 ^ N_env2
            ptr += 3
        elif cmd == '?':
            print(L_env | N_env)
            ptr += 1
        else:
            ptr += 1
    return L_env, N_env

def main(p, L):
    "Run the program on the input, return the output."
    # Parse the input list
    L = ''.join(c for c in L if c in "01")
    while "00" in L:
        L = L.replace("00","0")
    L = [-2] + [i for i in range(len(L)-1) if L[i:i+2] == "10"]
    L = tuple(b-a-1 for (a,b) in zip(L, L[1:]))
    # Parse the program
    ast = parse(p)[0]
    L_env, N_env = intp(ast, set([L]), set())
    return '1'*(len(L_env) + len(N_env))

if __name__ == "__main__":
    filename = sys.argv[1]
    f = open(filename, 'r')
    prog = f.read()
    f.close()
    L = input()
    print(main(prog, L))

La mia soluzione

La mia soluzione di riferimento è lunga 484 byte, quindi piuttosto breve rispetto al programma a 3271 byte di TheNumberOne. Ciò è probabilmente dovuto al sofisticato e straordinario sistema macro TheNumberOne sviluppato per la programmazione in HPR. L'idea principale in entrambi i nostri programmi è simile:

  1. Scopri come produrre il massimo elemento di un elenco.
  2. Per rimuovere l'elemento massimo, ruota l'elenco fino a quando il primo elemento è uguale al massimo, quindi pop.
  3. Rimuovere il massimo due volte, quindi stampare il nuovo elemento massimo.

Tuttavia, per quanto ne so, i dettagli esatti dell'implementazione sono piuttosto diversi. Ecco la mia soluzione:

!($)(!(-)(#(-)())#(!(-#(!(*)(#(*)())#(!(-)(#(-)()))())(!(-)(#(-)())))(#(-#(!(*)(#(*)())#(!(-)(#(-)()))())(!(-)(#(-)())))())#(-)(#(!(-)(#(-)()))()))(*)#(!(-)(#(-)()))())*!(-)(#(-)())!($)(!(-)(#(-)())#(!(-#(!(*)(#(*)())#(!(-)(#(-)()))())(!(-)(#(-)())))(#(-#(!(*)(#(*)())#(!(-)(#(-)()))())(!(-)(#(-)())))())#(-)(#(!(-)(#(-)()))()))(*)#(!(-)(#(-)()))())*!(-)(#(-)())!(-#(!(*)(#(*)())#(!(-)(#(-)()))())(!(-)(#(-)())))(#(-#(!(*)(#(*)())#(!(-)(#(-)()))())(!(-)(#(-)())))())-#(!(-)(#(-)()))()

Ed ecco il programma commentato Python che lo produce (niente di speciale qui, solo manipolazione di stringhe di base per sbarazzarsi di tutta la ripetizione):

# No numbers in the environment
no_nums = "#(-)()"
# No non-empty lists in the environment
no_lists = "#(*)()"
# Remove all numbers from the environment
del_nums = "!(-)(" + no_nums + ")"
# Remove all lists from the environment
del_lists = "#(" + del_nums + ")()"
# Splat the list to the environment:
#  pop the list until it's empty and delete the empty list,
#  then take symmetric difference with all numbers removed
splat = "#(!(*)(" + no_lists + ")" + del_lists + ")(" + del_nums + ")"
# Put into the environment all numbers up to list maximum:
#  decrement and splat until a fixed point is reached.
#  Without the speed optimization, this is very slow if the list elements are large.
splat_upto = "!(-" + splat + ")(#(-" + splat + ")())"
# Copy maximum element to environment:
#  take all elements up to maximum,
#  then take symmetric difference of decrement and list deletion
copy_max = splat_upto + "#(-)(" + del_lists + ")"
# Delete maximum element from list:
#  rotate until first element is maximal, then pop and delete it
del_max = "!($)(" + del_nums + "#(" + copy_max + ")(*)" + del_lists + ")*" + del_nums
# Full program:
#  remove the maximum twice, then produce all numbers up to maximum,
#  then decrement and remove lists. The environment will contain exactly
#  the integers from 0 to third largest - 1, and their number is printed.
main = del_max + del_max + splat_upto + "-" + del_lists
print(main)


@TheNumberOne Aggiunta la mia soluzione.
Zgarb,

12

TKDYNS (Per uccidere il drago, hai bisogno di una spada) - Incrinato da Martin Büttner

EDIT: ho aggiunto la mia soluzione e spiegazione sotto il post principale.

sfondo

In questa lingua, prendi il controllo di un valoroso guerriero a cui è stato assegnato il compito di uccidere un terribile drago. Il drago vive in un labirinto sotterraneo, pieno di pericoli e pericoli, e fino ad ora nessuno è stato in grado di mapparlo e sopravvivere. Ciò significa che devi orientarti verso il drago nel buio pesto, con nient'altro che intuizione e coraggio per guidarti.

...

Bene, non proprio. Hai portato con te una scorta quasi illimitata di servi usa e getta e sono disposti ad andare avanti per scoprire la via sicura. Sfortunatamente, sono tutti spessi come due assi corti e faranno esattamente ciò che dici loro di fare. Sta a te inventare un metodo intelligente per garantire che i tuoi servi scoprano il percorso corretto.

Qualche dettaglio in più

La tana del drago assume la forma di una griglia 10x10. Tra alcuni punti adiacenti nella griglia, c'è una passerella stretta; tra gli altri, c'è un profondo abisso e una certa morte. Un layout di esempio per una griglia 4x4 potrebbe essere il seguente:

 0---1   2---3
     |   |   |
 4---5---6   7
 |           |
 8   9--10  11
     |       |
12--13--14--15

Sai che c'è sempre un modo per andare da un punto all'altro, ma non ti è stato rivelato nulla di più.

Per sconfiggere con successo il drago, devi prima raccogliere alcuni oggetti che potrai combinare per creare una lama magica che uccide il drago. Convenientemente, tutti i pezzi di quest'arma sono stati sparpagliati nella tana del drago. Devi semplicemente raccoglierli.

Il colpo di scena è che ogni pezzo dell'arma è stato intrappolato. Ogni volta che ne viene raccolto uno, il layout delle passerelle cambia. Percorsi precedentemente sicuri potrebbero ora portare a morte certa e viceversa.

comandi

Ci sono solo 5 comandi validi.

  • < - Fai un passo a sinistra

  • > - Fai un passo a destra

  • ^ - Fai un passo verso l'alto

  • v - Fai un passo verso il basso

  • c- Raccogli tutti gli oggetti che si trovano nella posizione corrente. Se erano presenti elementi, il layout della tana cambia. Con le posizioni numerate nelle righe come sopra, prendi la tua posizione modulo 10. Ci sono 10 layout hardcoded nell'interprete e il layout cambia in quello corrispondente. Ad esempio, se ci si trova in posizione 13, il layout cambia inlayouts[3]

I layout come appaiono nell'interpeter sono stati codificati in numeri interi nel modo seguente:

  • Il layout vuoto è codificato a zero.

  • Per ogni bordo del layout, xsia la più piccola delle due posizioni a cui si unisce.

    • Se il passaggio è orizzontale, aggiungi 2^(2*x)alla codifica (che è power-of, non XOR)

    • Se il passaggio è verticale, aggiungi 2^(2*x + 1)alla codifica.

Flusso di esecuzione

L'interprete viene eseguito con il nome di un file di origine come argomento della riga di comando.

Quando viene eseguito l'interprete, verrà richiesto all'utente di fornire una missione. Questo input dovrebbe essere nella forma descritta nella domanda e determina le posizioni nella tana dei componenti dell'arma. In particolare, ogni intero di input viene preso modulo 100 e posizionato nella posizione corrispondente nella tana.

Ogni programma sorgente è composto da più righe, ciascuna riga è composta da una sequenza dei 5 caratteri validi sopra indicati. Queste linee rappresentano i tuoi servi. Tu, il guerriero, tieni traccia di una sequenza di azioni note per essere al sicuro. Inizialmente, non sai nulla della tana, quindi questa sequenza è vuota. Prendendo ogni seguace a turno, viene eseguito quanto segue, a partire dalla posizione 0:

  • Al servitore viene richiesto di eseguire tutte le azioni note per essere sicure, seguite dalle azioni nella propria riga di codice.

    • Se il servitore muore in qualsiasi momento, questo viene notificato e la tana si ripristina alla sua configurazione iniziale. Tutti gli articoli vengono sostituiti e le passerelle tornano alle loro posizioni iniziali.

    • Se invece il servitore sopravvive, lo vaporizzi comunque - dopo tutto è solo un servitore. Come prima, questo innesca il ripristino della tana al suo stato iniziale, ma questa volta aggiungi le azioni dalla riga di codice del servitore alla sequenza di azioni note per essere sicure.

Una volta esauriti tutti i servi, tu, il guerriero, esegui tutte le azioni che sono note per essere al sicuro, di nuovo a partire dalla posizione 0. Ci sono due possibili esiti:

  • Raccogli tutti i pezzi dell'arma - in questo caso, uccidi con successo il drago e viene emesso un emozionante messaggio di vittoria. Questo messaggio di vittoria conterrà, tra gli altri, nquelli in cui si ntrova il terzo numero più alto fornito come input.

  • Non sei riuscito a raccogliere alcuni dei pezzi dell'arma - in questo caso, il drago sopravvive e hai fallito nella tua ricerca.

Codice dell'interprete (Python 2)

import collections
import copy
import sys

size = 10
layouts = [108705550239329248440770931020110627243913363144548922111951,108386637020100924277694952798729434993885646706222210602969,133885860318189115027934177821170081234850573770998325845482,102397795295522647918061101991513921833376565032742993744791,131948019244359669407266662537098175265242405785636894694611,127512068876349726396523358265982765442984953916545984706958,106817519055019354200334114020150263381328246524221867629943,33472343358375525802921815863230485208221126168622186265959,133909781123963725874096031069972704024813281938330035579187,132244654022332695610020359820518831299843076834682749020986]

class Interpreter(object):

    def __init__(self, source_file):
        self.source_filename = source_file
        self.layout = layouts[0]
        self.position = 0
        self.ingredients = []
        self.quest_items = {}
        self.attempts = collections.deque()

        self.safe_position = 0
        self.safe_ingredients = []
        self.safe_quest_items = {}
        self.safe_layout = layouts[0]

    def get_input(self):
        s = raw_input("Which quest would you like to embark on today?")
        ind = s.find('00')
        s = s[:ind]
        items = map(len, s.split('0'))
        for item in items:
            if item not in self.safe_quest_items:
                self.safe_quest_items[item] = 1
            else:
                self.safe_quest_items[item] += 1

    def parse_attempts(self):
        with open(self.source_filename, 'r') as source:
            for line in source:
                self.attempts.append(line.strip())

    def enc(self, x, y):
        x, y = min(x, y), max(x, y)
        if x < 0:
            return 0
        if y == x + 1:
            return 1 << (2*x)
        elif y == x + size:
            return 1 << (2*x + 1)
        else:
            return 0

    def exec_command(self, char):
        if char == '<':
            if self.enc(self.position, self.position-1) & self.layout:
                self.position -= 1
            else:
                return False
        elif char == '>':
            if self.enc(self.position, self.position+1) & self.layout:
                self.position += 1
            else:
                return False
        elif char == '^':
            if self.enc(self.position, self.position-size) & self.layout:
                self.position -= size
            else:
                return False
        elif char == 'v':
            if self.enc(self.position, self.position+size) & self.layout:
                self.position += size
            else:
                return False
        elif char == 'c':
            for item in xrange(self.position, 10**6, size*size):
                if item in self.quest_items:
                    self.ingredients += ([item] * self.quest_items.pop(item))
                    self.ingredients.sort()
                    self.layout = layouts[self.position % 10]
        else:
            raise ValueError

        return True

    def run_minion(self):
        minion = self.attempts.popleft()
        self.position = self.safe_position
        self.ingredients = copy.copy(self.safe_ingredients)
        self.quest_items = copy.copy(self.safe_quest_items)
        self.layout = self.safe_layout
        dead = False

        for cmd in minion:
            if not self.exec_command(cmd):
                dead = True

        if not dead:
            self.safe_position = self.position
            self.safe_ingredients = copy.copy(self.ingredients)
            self.safe_quest_items = copy.copy(self.quest_items)
            self.safe_layout = self.layout

    def process_minions(self):
        while self.attempts:
            self.run_minion()

    def final_run(self):
        if len(self.safe_quest_items) == 0:
            print "You killed the dragon" + "!!1" * self.safe_ingredients[-3]
        else:
            print "The dragon lives on. Better luck next time..."

    def interpret(self):
        self.get_input()
        self.parse_attempts()
        self.process_minions()
        self.final_run()

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print "Wrong number of arguments"
    else:
        test = Interpreter(sys.argv[1])
        test.interpret()

Buona fortuna, valoroso guerriero.

La mia soluzione e spiegazione

Ecco uno script Python che genera codice sorgente per risolvere il problema. Per interesse, il codice sorgente finale di Martin è circa 5 volte più piccolo del codice prodotto dal mio script. D'altra parte, il mio script per generare il codice è circa 15 volte più piccolo del programma Mathematica di Martin ...

size = 10
layouts = [108705550239329248440770931020110627243913363144548922111951,108386637020100924277694952798729434993885646706222210602969,133885860318189115027934177821170081234850573770998325845482,102397795295522647918061101991513921833376565032742993744791,131948019244359669407266662537098175265242405785636894694611,127512068876349726396523358265982765442984953916545984706958,106817519055019354200334114020150263381328246524221867629943,33472343358375525802921815863230485208221126168622186265959,133909781123963725874096031069972704024813281938330035579187,132244654022332695610020359820518831299843076834682749020986]

def enc(edge):
    x,y = min(edge), max(edge)
    if x < 0:
        return 0
    if y == x + 1:
        return 1 << (2*x)
    elif y == x + size:
        return 1 << (2*x + 1)

def path(x, y, tree):
    stack = [(x,'')]
    while stack[-1][0] != y:
        vertex, path = stack.pop()
        if (enc((vertex, vertex+1))&tree) and ((not path) or path[-1] != '<'):
            stack.append((vertex+1, path+'>'))
        if (enc((vertex, vertex-1))&tree) and ((not path) or path[-1] != '>'):
            stack.append((vertex-1, path+'<'))
        if (enc((vertex, vertex+size))&tree) and ((not path) or path[-1] != '^'):
            stack.append((vertex+size, path+'v'))
        if (enc((vertex, vertex-size))&tree) and ((not path) or path[-1] != 'v'):
            stack.append((vertex-size, path+'^'))
    return stack[-1][1]

def reverse(path):
    rev = ''
    for i in xrange(len(path)-1, -1 ,-1):
        if path[i] == '<':
            rev += '>'
        if path[i] == '>':
            rev += '<'
        if path[i] == 'v':
            rev += '^'
        if path[i] == '^':
            rev += 'v'
    return rev

def assert_at(x, tree):
    path_ul = path(x, 0, tree)
    path_dr = path(x, size*size - 1, tree)
    return path_ul + reverse(path_ul) + path_dr + reverse(path_dr)

def safe_path(x, y, tree):
    return assert_at(x, tree) + path(x, y, tree)

with open('source.txt', 'w') as f:
    for x in xrange(size*size - 1):
        for tree in layouts:
            f.write(safe_path(x, x+1, tree) + 'c\n')

Struttura basilare

Questo genera 990 righe di codice sorgente, che si presentano in gruppi di 10. Le prime 10 righe contengono tutte istruzioni che tentano di spostare un servitore da una posizione 0all'altra 1, quindi raccolgono un oggetto - un set per ogni possibile layout. Le successive 10 righe contengono tutte le istruzioni che tentano di spostare un servitore da una posizione 1all'altra 2, quindi raccogliere un oggetto. E così via ... Questi percorsi sono calcolati con la pathfunzione nello script - fa solo una semplice ricerca approfondita.

Se possiamo garantire che, in ciascun gruppo di 10, esattamente un servitore abbia successo, allora avremo risolto il problema.

Il problema con l'approccio

Potrebbe non essere sempre il caso in cui esattamente un servitore del gruppo di 10 abbia successo - per esempio, un servitore progettato per spostarsi da una posizione 0all'altra 1potrebbe accidentalmente riuscire a spostarsi da una posizione 1all'altra 2(a causa di somiglianze nei layout), e che il calcolo errato si propagherà attraverso, causando potenzialmente errori.

Come sistemarlo

La correzione che ho usato era la seguente:

Per ogni seguace che sta cercando di andare da una posizione nall'altra n+1, prima fallo camminare da una posizione nall'altra 0(l'angolo in alto a sinistra) e viceversa, quindi da una posizione nall'altra 99(l'angolo in basso a destra) e viceversa. Queste istruzioni possono essere eseguite in sicurezza solo dalla posizione n: qualsiasi altra posizione di partenza e il servitore cammineranno fuori dal bordo.

Queste istruzioni extra impediscono quindi ai servitori di andare accidentalmente in un luogo a cui non sono destinati, e ciò garantisce che esattamente un servitore di ciascun gruppo di 10 abbia successo. Nota che non è necessariamente il servitore che ti aspetti che sia - potrebbe essere il caso che il servitore che crede di essere nel layout 0 abbia successo, anche quando siamo davvero nel layout 7 - ma in ogni caso, il fatto che abbiamo ora la posizione modificata significa che tutti gli altri servi del gruppo moriranno necessariamente. Questi passaggi aggiuntivi vengono calcolati dalla assert_atfunzione e la safe_pathfunzione restituisce un percorso che è la concatenazione di questi passaggi aggiuntivi con il percorso ordinario.


1
Cracked. È stato molto divertente, ma penso che sollevi un problema con la regola "il tuo linguaggio di programmazione deve risolvere solo questo compito", dal momento che decifrare il tuo puzzle non ha nulla a che fare con la risoluzione di quel compito di programmazione. ;)
Martin Ender,

Ho creato questa risposta principalmente perché ho notato che il problema esisteva e non sembra esserci nulla che impedisca a qualcuno di utilizzare al suo posto un problema di calcolo veramente difficile. Il divieto di "nessuna crittografia" sembra arbitrariamente stretto ...
Sam Cappleman-Lynes,

vero. Immagino sia per questo che questo è un concorso di popolarità. Se il problema fosse più di natura computazionale e non racchiuso in una storia così bella, tanti voti come una risposta con un linguaggio corretto (potenzialmente Turing completo) che in realtà è davvero molto difficile da usare.
Martin Ender,

Aggiunta la mia soluzione per interesse. Anche per interesse, inizialmente ho progettato il puzzle pensando a una griglia di 1000x1000, ma nell'interesse di non avere layout codificati in numeri di ~ 600000 cifre ho optato per dimensioni inferiori.
Sam Cappleman-Lynes,

8

Firetype, Cracked di Martin Büttner

Un mix davvero strano di BF e CJam. E chissà cos'altro! Abbastanza sicuro che sarà facile, ma è stato comunque divertente. Cordiali saluti, il nome si riferisce a Vermillion Fire da Final Fantasy Type-0 .

NOTA : Per favore perdonami per eventuali ambiguità in questa descrizione. Non sono il miglior scrittore ...: O

Martin l'ha risolto molto velocemente! Questo era il mio programma originale per risolvere il problema:

_
,
^
~
+
|
|
-
%
+
_
+
=











`
~
+
|
%

_
=

\
@
-
-
!
<
>
>
>

_
+
.
<
-
`
~
~
+
|
|
-
#
%

Sintassi

Uno script Firetype è fondamentalmente un elenco di righe. Il primo carattere di ogni riga è il comando da eseguire. Le righe vuote sono fondamentalmente NOP.

Semantica

Hai una matrice di numeri interi e un puntatore (pensa BF). È possibile spostare gli elementi a sinistra e a destra o "spingere" sull'array.

spingendo

Quando si "spinge" un elemento e ci si trova alla fine dell'array, un elemento aggiuntivo verrà aggiunto alla fine. Se non sei alla fine, l'elemento successivo verrà sostituito. Indipendentemente da ciò, il puntatore verrà sempre incrementato.

comandi

_

Inserire uno zero sull'array.

+

Incrementa l'elemento sul puntatore corrente.

-

Decrementa l'elemento sul puntatore corrente.

*

Raddoppia l'elemento nel puntatore corrente.

/

Dimezza l'elemento sul puntatore corrente.

%

Prendi l'elemento sul puntatore corrente e salta in avanti di molte linee e sposta il puntatore a sinistra. Se il valore è negativo, salta invece indietro.

=

Prendi l'elemento nel puntatore corrente e salta a quella linea + 1. Ad esempio, se l'elemento corrente è 0, questo salterà alla linea 1. Questo sposta anche il puntatore a sinistra.

,

Leggi un carattere dall'input standard e invia il suo valore ASCII.

^

Prendi l'elemento nel puntatore corrente, interpretalo come valore ASCII per un carattere e convertilo in un numero intero. Ad esempio, se il valore corrente è 49 (il valore ASCII di 1), l'elemento sul puntatore corrente verrà impostato sull'intero 1.

.

Scrivi il numero corrente sullo schermo.

!

Prendi l'elemento nel puntatore corrente e ripeti la riga successiva più volte. Sposta anche il puntatore a sinistra.

<

Sposta il puntatore a sinistra. Se sei già all'inizio, viene generato un errore.

>

Sposta il puntatore a destra. Se sei già alla fine, viene generato un errore.

~

Se l'elemento corrente è diverso da zero, sostituirlo con 0; in caso contrario, sostituirlo con 1.

|

Square l'elemento corrente.

@

Impostare l'elemento corrente sulla lunghezza dell'array.

`

Duplica l'elemento corrente.

\

Ordina gli elementi in corrispondenza e prima del puntatore.

#

Annulla l'elemento corrente.

L'interprete

Disponibile anche su Github .

#!/usr/bin/env python

# The FireType interpreter.
# Runs under Python 2 and 3.
import sys

class Array(object):
    def __init__(self):
        self.array = []
        self.ptr = 0

    def push_zero(self):
        if self.ptr == len(self.array):
            self.array.append(0)
        else:
            self.array[self.ptr] = 0
        self.ptr += 1

    def left(self):
        self.ptr -= 1
        assert self.ptr >= 0 and self.array, 'pointer underflow (at %d)' % i

    def right(self):
        self.ptr += 1
        assert self.ptr <= len(self.array), 'pointer overflow (at %d)' % i

    def sort(self):
        lhs = self.array[:self.ptr-1]
        rhs = self.array[self.ptr-1:]
        lhs.sort()
        lhs.reverse()
        self.array = lhs + rhs

    def __call__(self, *args):
        args = list(args)
        assert self.array, 'out-of-bounds (at %d)' % i
        if not args:
            return self.array[self.ptr-1]
        self.array[self.ptr-1] = args.pop()
        assert not args

    def __len__(self):
        return len(self.array)

    def __str__(self):
        return 'Array(array=%s, ptr=%s)' % (self.array, self.ptr)

    def __repr__(self):
        return 'Array(array=%r, ptr=%r)' % (self.array, self.ptr)

def execute(lines):
    global i, array
    i = 0
    array = Array()
    rep = 0
    while i < len(lines):
        line = lines[i]
        if not line:
            i += 1
            continue
        line = line[0]
        if line == '_':
            array.push_zero()
        elif line == '+':
            array(array() + 1)
        elif line == '-':
            array(array() - 1)
        elif line == '*':
            array(array() * 2)
        elif line == '/':
            array(int(array() / 2))
        elif line == '%':
            i += array()
            array.left()
        elif line == '=':
            i = array()-1
            array.left()
        elif line == ',':
            array.push_zero()
            array(ord(sys.stdin.read(1)))
        elif line == '.':
            sys.stdout.write(str(array()))
        elif line == '!':
            rep = array()
            array.left()
            i += 1
        elif line == '<':
            array.left()
        elif line == '>':
            array.right()
        elif line == '^':
            array(int(chr(array())))
        elif line == '~':
            array(int(not array()))
        elif line == '|':
            array(array() ** 2)
        elif line == '@':
            array(len(array))
        elif line == '`':
            top = array()
            array.push_zero()
            array(top)
        elif line == '\\':
            array.sort()
        elif line == '#':
            array(-array())

        if rep:
            rep -= 1
        else:
            i += 1

def main(args):
    try:
        _, path = args
    except ValueError:
        sys.exit('usage: %s <file to run, - for stdin>')

    if path == '-':
        data = sys.stdin.read()
    else:
        with open(path) as f:
            data = f.read()

    try:
        execute(data.split('\n'))
    except:
        print('ERROR!')
        print('Array was %r' % array)
        print('Re-raising error...')
        raise

if __name__ == '__main__':
    main(sys.argv)

Penso che l'affermazione per il limite inferiore dell'array sia errata. Dal momento che stai usando self.ptr-1per l'accesso, probabilmente dovresti controllare di self.ptr>0no >=0. (Questo non dovrebbe invalidare alcun programma valido ma potrebbe far funzionare accidentalmente alcuni programmi, cosa che non dovrebbe.)
Martin Ender,

Ancora una cosa: il codice dice =imposta il valore su array() - 1, dice la documentazione +1?
Martin Ender,

Cracked. (Supponendo che l'interprete sia normativo, non la descrizione.)
Martin Ender,

@ MartinBüttner Dang, è stato più veloce di quanto pensassi! : OI risolto i problemi relativi ai documenti menzionati.
kirbyfan64sos,

7

Acc !! (sicuro)

È baack ...

inserisci qui la descrizione dell'immagine

... e spero sicuramente più a prova di scappatoia.

Ho già letto l' Acc! spec. Come sta Acc !! diverso?

In Acc !! , le variabili del ciclo non rientrano nell'ambito quando il ciclo esce. Puoi usarli solo all'interno del loop. All'esterno, verrà visualizzato l'errore "nome non definito". Oltre a questo cambiamento, le lingue sono identiche.

dichiarazioni

I comandi vengono analizzati riga per riga. Esistono tre tipi di comando:

  1. Count <var> while <cond>

Conta <var>da 0 fino a quando <cond>è diverso da zero, equivalente a C ++ for(int <var>=0; <cond>; <var>++). Il contatore di loop può essere una singola lettera minuscola. La condizione può essere qualsiasi espressione, non necessariamente implicando la variabile loop. Il ciclo si interrompe quando il valore della condizione diventa 0.

I loop richiedono parentesi graffe in stile K & R (in particolare, la variante Stroustrup ):

Count i while i-5 {
 ...
}

Come accennato in precedenza, le variabili di loop sono disponibili solo all'interno dei loro loop ; il tentativo di riferirli successivamente provoca un errore.

  1. Write <charcode>

Emette un singolo carattere con il dato valore ASCII / Unicode su stdout. Il charcode può essere qualsiasi espressione.

  1. Espressione

Qualsiasi espressione in piedi da sola viene valutata e assegnata nuovamente all'accumulatore (che è accessibile come _). Pertanto, ad esempio, 3è un'istruzione che imposta l'accumulatore su 3; _ + 1incrementa l'accumulatore; e _ * Nlegge un personaggio e moltiplica l'accumulatore per il suo codice.

Nota: l'accumulatore è l'unica variabile a cui è possibile assegnare direttamente; variabili loop e Npuò essere utilizzato nei calcoli ma non modificato.

L'accumulatore è inizialmente 0.

espressioni

Un'espressione può includere valori letterali interi, variabili di ciclo ( a-z), _per l'accumulatore e il valore speciale N, che legge un carattere e valuta il suo codice ogni volta che viene utilizzato. Nota: questo significa che ottieni solo un colpo per leggere ogni personaggio; la prossima volta che utilizzerai N, leggerai il prossimo.

Gli operatori sono:

  • +, inoltre
  • -, sottrazione; negazione unaria
  • *, moltiplicazione
  • /, divisione intera
  • %, modulo
  • ^, esponenziazione

Le parentesi possono essere utilizzate per imporre la precedenza delle operazioni. Qualsiasi altro carattere in un'espressione è un errore di sintassi.

Spazio bianco e commenti

Gli spazi bianchi iniziali e finali e le righe vuote vengono ignorati. Lo spazio bianco nelle intestazioni del loop deve essere esattamente come mostrato, con un singolo spazio tra l'intestazione del loop e l'apertura del parentesi graffa. Lo spazio bianco all'interno delle espressioni è facoltativo.

# inizia un commento a riga singola.

Input Output

Acc !! si aspetta una singola riga di caratteri come input. Ogni carattere di input può essere recuperato in sequenza e il suo codice di accesso può essere elaborato utilizzando N. Il tentativo di leggere oltre l'ultimo carattere della riga provoca un errore. Un carattere può essere emesso passando il suo charcode Writeall'istruzione.

Interprete

L'interprete (scritto in Python 3) traduce Acc !! codice in Python e execs.

import re, sys

def main():
    if len(sys.argv) != 2:
        print("Please supply a filename on the command line.", file=sys.stderr)
        return
    codeFile = sys.argv[1]
    with open(codeFile) as f:
        code = f.readlines()
    code = translate(code)
    exec(code, {"inputStream": (ord(char) for char in input())})

def translate(accCode):
    indent = 0
    loopVars = []
    pyCode = ["_ = 0"]
    for lineNum, line in enumerate(accCode):
        if "#" in line:
            # Strip comments
            line = line[:line.index("#")]
        line = line.strip()
        if not line:
            continue
        lineNum += 1
        if line == "}":
            if indent:
                loopVar = loopVars.pop()
                pyCode.append(" "*indent + loopVar + " += 1")
                indent -= 1
                pyCode.append(" "*indent + "del " + loopVar)
            else:
                raise SyntaxError("Line %d: unmatched }" % lineNum)
        else:
            m = re.fullmatch(r"Count ([a-z]) while (.+) \{", line)
            if m:
                expression = validateExpression(m.group(2))
                if expression:
                    loopVar = m.group(1)
                    pyCode.append(" "*indent + loopVar + " = 0")
                    pyCode.append(" "*indent + "while " + expression + ":")
                    indent += 1
                    loopVars.append(loopVar)
                else:
                    raise SyntaxError("Line %d: invalid expression " % lineNum
                                      + m.group(2))
            else:
                m = re.fullmatch(r"Write (.+)", line)
                if m:
                    expression = validateExpression(m.group(1))
                    if expression:
                        pyCode.append(" "*indent
                                      + "print(chr(%s), end='')" % expression)
                    else:
                        raise SyntaxError("Line %d: invalid expression "
                                          % lineNum
                                          + m.group(1))
                else:
                    expression = validateExpression(line)
                    if expression:
                        pyCode.append(" "*indent + "_ = " + expression)
                    else:
                        raise SyntaxError("Line %d: invalid statement "
                                          % lineNum
                                          + line)
    return "\n".join(pyCode)

def validateExpression(expr):
    "Translates expr to Python expression or returns None if invalid."
    expr = expr.strip()
    if re.search(r"[^ 0-9a-z_N()*/%^+-]", expr):
        # Expression contains invalid characters
        return None
    elif re.search(r"[a-zN_]\w+", expr):
        # Expression contains multiple letters or underscores in a row
        return None
    else:
        # Not going to check validity of all identifiers or nesting of parens--
        # let the Python code throw an error if problems arise there
        # Replace short operators with their Python versions
        expr = expr.replace("^", "**")
        expr = expr.replace("/", "//")
        # Replace N with a call to get the next input character
        expr = expr.replace("N", "inputStream.send(None)")
        return expr

if __name__ == "__main__":
    main()

Soluzione

La caduta dell'Acc originale ! erano variabili loop che continuavano ad essere accessibili al di fuori dei loro loop. Ciò ha consentito di salvare copie dell'accumulatore, rendendo la soluzione troppo semplice.

Qui, senza questo foro per il loop (scusate), abbiamo solo l'accumulatore per conservare le cose. Per risolvere il compito, tuttavia, dobbiamo memorizzare quattro valori arbitrariamente grandi. 1 La soluzione: interlacciare i loro bit e memorizzare il numero di combinazione risultante nell'accumulatore. Ad esempio, un valore di accumulatore di 6437 memorizzerebbe i seguenti dati (utilizzando il bit di ordine più basso come flag a bit singolo):

1100100100101  Binary representation of accumulator
321032103210F  Key

The flag is 1 and the four numbers are
Number 0: 010 = 2
Number 1: 001 = 1
Number 2: 100 = 4
Number 3: 110 = 6

Possiamo accedere a qualsiasi bit di qualsiasi numero dividendo l'accumulatore per la potenza appropriata di 2, mod 2. Ciò consente anche di impostare o invertire singoli bit.

A livello macro, l'algoritmo passa in rassegna i numeri unari nell'input. Legge un valore nel numero 0, quindi esegue un passaggio dell'algoritmo di ordinamento a bolle per posizionarlo nella posizione corretta rispetto agli altri tre numeri. Infine, scarta il valore lasciato nel numero 0, poiché è il più piccolo dei quattro ora e non può mai essere il terzo più grande. Quando il ciclo termina, il numero 1 è il terzo più grande, quindi scartiamo gli altri e lo emettiamo.

La parte più difficile (e il motivo per cui ho avuto variabili globali del loop nella prima incarnazione) è il confronto di due numeri per sapere se scambiarli. Per confrontare due bit, possiamo trasformare una tabella di verità in un'espressione matematica; la mia svolta per Acc !! stava trovando un algoritmo di confronto che passava dai bit di ordine inferiore a bit di ordine superiore, poiché senza variabili globali non c'è modo di passare in rassegna i bit di un numero da sinistra a destra. Il bit di ordine più basso dell'accumulatore memorizza un flag che segnala se scambiare i due numeri attualmente in esame.

Ho il sospetto che Acc !! è Turing completo, ma non sono sicuro di voler andare a provarlo.

Ecco la mia soluzione con commenti:

# Read and process numbers until the double 0

Count y while N-48 {
    # Read unary number and store it (as binary) in number 0
    # The above loop header consumed the first 1, so we need to add 1 to number 0

    _ + 2

    # Increment number 0 for each 1 in input until next 0

    Count z while N-48 {
        # In this context, the flag indicates a need to carry; set it to 1
        _ - _%2 + 1

        # While carry flag is set, increment the next bit in line
        Count i while _%2 {
            # Set carry flag to 1 if i'th bit is 1, 0 if it's 0
            # Set i'th bit to 1 if it was 0, 0 if it was 1
            _ - _%2 + _/2^(i*4+1)%2 + (-1)^(_/2^(i*4+1)%2)*2^(i*4+1)
        }
    }

    # Bubble number 0 upwards

    Count n while n-3 {
        # The flag (rightmost bit of accumulator) needs to become 1 if we want to swap
        # numbers (n) and (n+1) and 0 if we don't
        # Iterate over bit-groups of accumulator, RTL
        Count i while _/2^(i*4+1) {
            # Adjust the flag as follows:
            # _/2^(i*4+n+1)%4 is the current bit of number (n+1) followed by the current
            # bit of number (n), a value between 0 and 3
            # - If this quantity is 1 (01), number (n) is bigger so far; set flag to 1
            # - If this quantity is 2 (10), number (n+1) is bigger so far; set flag to 0
            # - If this quantity is 0 (00) or 3 (11), the two bits are the same; keep
            #   current value of flag
            _ + (_/2^(i*4+n+1)%4%3 + 1)/2*(_/2^(i*4+n+1)%4%3%2 - _%2)
        }

        # Now swap the two if the flag is 1
        Count i while (_%2)*(_/2^(i*4+1)) {
            # _/2^(i*4+n+1)%2 is the current bit of number (n)
            # _/2^(i*4+n+2)%2 is the current bit of number (n+1)
            _ - (_/2^(i*4+n+1)%2)*2^(i*4+n+1) - (_/2^(i*4+n+2)%2)*2^(i*4+n+2) + (_/2^(i*4+n+2)%2)*2^(i*4+n+1) + (_/2^(i*4+n+1)%2)*2^(i*4+n+2)
        }
    }

    # Discard number 0, setting it to all zeros for the next iteration
    Count i while _/2^(i*4+1) {
        _ - _/2^(i*4+1)%2*2^(i*4+1)
    }
}

# Once the loop is over, all input has been read and the result is in number 1
# Divide by 2 to get rid of flag bit

_ / 2

# Zero out numbers 2 and 3

Count i while _/2^(i*4) {
    _ - _/2^(i*4+2)%2*2^(i*4+2)
}

Count i while _/2^(i*4) {
    _ - _/2^(i*4+3)%2*2^(i*4+3)
}

# Move bits of number 1 down to their final locations

Count i while _/2^i {
    _ - _/2^(i*4+1)%2*2^(i*4+1) + _/2^(i*4+1)%2*2^i
}

# _ now contains the correct answer in decimal; to output in unary:

Count z while z-_ {
    Write 49
}

1 Secondo le specifiche della domanda, è necessario supportare solo valori fino a 1 milione. Sono contento che nessuno ne abbia approfittato per una soluzione più semplice, anche se non sono del tutto sicuro di come faresti i confronti.


LOL @ the Bill the Cat picture
a spaghetto

7

Picofuck (sicuro)

Picofuck è simile a Smallfuck . Funziona su un nastro binario che non è legato a destra, legato a sinistra. Ha i seguenti comandi:

  • > sposta il puntatore a destra

  • <sposta il puntatore a sinistra. Se il puntatore cade dal nastro, il programma termina.

  • * capovolgi il puntatore

  • (se il bit sul puntatore è 0, passa al successivo)

  • )non fare nulla: le parentesi in Picofuck sono ifblocchi, non whileanelli.

  • .scrivere a stdout il bit sul puntatore come ascii 0o 1.

  • ,leggi da stdin fino a quando incontriamo un 0o 1, e memorizzalo nel bit sul puntatore.

A capo del codice Picofuck: una volta raggiunta la fine del programma, continua dall'inizio.

Inoltre, Picofuck non consente le parentesi nidificate. Le parentesi che compaiono in un programma Picofuck devono alternare tra (e ), a partire da (e finendo con ).

Interprete

Scritto in Python 2.7

utilizzo: python picofuck.py <source_file>

import sys

def interpret(code):
    # Ensure parentheses match and are not nested.
    in_if_block = False
    for c in code:
        if c == '(':
            if in_if_block:
                print "NESTED IFS DETECTED!!!!!"
                return
            in_if_block = True
        elif c == ')':
            if not in_if_block:
                print "Unmatched parenthesis"
                return
            in_if_block = False
    if in_if_block:
        print "Unmatched parenthesis"
        return


    code_ptr = 0
    array = [0]
    ptr = 0

    while 1:
        command = code[code_ptr]
        if command == '<':
            if ptr == 0:
                break
            ptr -= 1
        elif command == '>':
            ptr += 1
            if ptr == len(array):
                array.append(0)
        elif command == '*':
            array[ptr] = 1-array[ptr]
        elif command == '(':
            if array[ptr] == 0:
                while code[code_ptr] != ')':
                    code_ptr = (code_ptr + 1) % len(code)
        elif command == ',':
            while True:
                c = sys.stdin.read(1)
                if c in ['0','1']:
                    array[ptr] = int(c)
                    break
        elif command == '.':
            sys.stdout.write(str(array[ptr]))
        code_ptr = (code_ptr+1)%len(code)

if __name__ == "__main__":
    with open(sys.argv[1]) as source_file:
        code = source_file.read()
    interpret(code)

Soluzione

Il seguente programma python 2.7 mostra la mia soluzione che può essere trovata qui

Potrei modificare questo post in seguito con una spiegazione più approfondita di come funziona, ma risulta che Picofuck è Turing completo.

states = {
    "SETUP":(
        "*>",
        "GET_NUMBER",
        "GET_NUMBER"
    ),

    "GET_NUMBER":(
        ",",
        "CHANGE_A",
        "PREPARE_PRINT_C"
    ),

    "GET_DIGIT":(
        ",",
        "CHANGE_A",
        "GOT_DIGIT"
    ),

    "GOT_DIGIT":(
        ">>>>",
        "GO_BACK",
        "GO_BACK"
    ),

    "CHANGE_A":(
        ">",
        "CHANGE_B",
        "SET_A"
    ),

    "SET_A":(
        "*>>>>",
        "GET_DIGIT",
        "GET_DIGIT"
    ),

    "CHANGE_B":(
        ">",
        "CHANGE_C",
        "SET_B"
    ),

    "SET_B":(
        "*>>>",
        "GET_DIGIT",
        "GET_DIGIT"
    ),

    "CHANGE_C":(
        ">",
        "CONTINUE_GET_NUMBER",
        "SET_C"
    ),

    "SET_C":(
        "*>>",
        "GET_DIGIT",
        "GET_DIGIT"
    ),

    "CONTINUE_GET_NUMBER":(
        ">>",
        "GET_DIGIT",
        "GET_DIGIT"
    ),

    "GO_BACK":(
        "<<<<<",
        "FINISH_GO_BACK",
        "GO_BACK"
    ),

    "FINISH_GO_BACK":(
        ">",
        "GET_NUMBER",
        "GET_NUMBER"
    ),

    "PREPARE_PRINT_C":(
        ">>>",
        "PRINT_C",
        "PRINT_C"
    ),

    "PRINT_C":(
        ".>>>>>",
        "PRINT_C",
        "TERMINATE"
    ),

    "TERMINATE":(
        "<",
        "TERMINATE",
        "TERMINATE"
    ),
}

def states_to_nanofuck(states,start_state):
    state_list = list(states)
    state_list.remove(start_state)
    state_list.insert(0,start_state)

    states_by_index = []
    for i,state in enumerate(state_list):
        commands, next1, next0 = states[state]
        states_by_index.append((commands,state_list.index(next1),state_list.index(next0)))


    # setup first state
    fragments = ['*(*>>>>>*<<<<<)>>>>>']

    # reset states that don't match
    for index in range(len(states_by_index)):
        for bool in range(2):
            # at state_0_0
            state_dist = index*3 + bool
            # move state to curstate
            fragments.append('>'*state_dist)
            fragments.append('(*')
            fragments.append(  '<'*state_dist)
            fragments.append(  '<<*>>')
            fragments.append(  '>'*state_dist)
            fragments.append(')')
            fragments.append('<'*state_dist)

            # go to arr
            fragments.append('<<<')

            if bool == 0:
                #flip arr
                fragments.append('*')

            # compute match = arr & curstate
            fragments.append('(>)(>*<)<(<)>')

            if bool == 0:
                #flip arr
                fragments.append('*')

            # reset curstate
            fragments.append('>(*)')

            # move match to matchstate, go back to state_0_0
            matchstate_dist = index*3 + 2
            fragments.append('>(*>')
            fragments.append(  '>'*matchstate_dist)
            fragments.append(  '*')
            fragments.append(  '<'*matchstate_dist)
            fragments.append('<)>')

    #fragments.append('|')

    # do the commands of the matching state
    for index,state in enumerate(states_by_index):
        for bool in range(2):
            # at state_0_0
            matchstate_dist = index*3 + 2
            # move matchstate to curstate
            fragments.append('>'*matchstate_dist)
            fragments.append('(*')
            fragments.append(  '<'*matchstate_dist)
            fragments.append(  '<<*>>')
            fragments.append(  '>'*matchstate_dist)
            fragments.append(')')
            fragments.append('<'*matchstate_dist)

            # if curstate, reset curstate
            fragments.append('<<(*')

            # go to arr
            fragments.append('<')

            # do commands
            commands,next1,next0 = state
            for c in commands:
                if c in '<>':
                    fragments.append(c*(3*len(states_by_index)+5))
                else:
                    fragments.append(c)

            # go to state_0_0
            fragments.append('>>>')

            # set next states
            for dist in [next0*3, next1*3+1]:
                fragments.append('>'*dist)
                fragments.append('*')
                fragments.append('<'*dist)

            # go to curstate
            fragments.append('<<')

            # end if
            fragments.append(')')

            # go to state_0_0
            fragments.append('>>')


    # go back to setup and set it
    fragments.append('<<<<<*')

    code = ''.join(fragments)

    return code



print states_to_nanofuck(states, "SETUP")

2
aspetta quasi 2 anni È più tardi?
Calcolatrice

Cordiali saluti, il link che hai fornito è ora morto. "
Conor O'Brien,

Il link richiede un download e sono troppo pigro. Per favore, aggiusta.
Calcolatrice

@CalculatorFeline È di 13 KB, è molto più facile da gestire come download.
mbomb007,

7

PQRS - Sicuro! / Soluzione fornita

Nozioni di base

Tutte le istruzioni implicite hanno quattro operandi di indirizzi di memoria:

P₀ Q₀ R₀ S₀
P₁ Q₁ R₁ S₁
...
Pᵥ₋₁ Qᵥ₋₁ Rᵥ₋₁ Sᵥ₋₁

Dov'è vla dimensione della memoria nei quartetti.

Pᵢ, Qᵢ, Rᵢ, SᵢSono numeri interi in formato nativo della macchina firmato (ad esempio 16, 32 o 64 bit), che si farà riferimento a come le parole.

Per ogni quartetto i, l'operazione implicita, con []denotazione indiretta, è:

if (([Pᵢ] ← [Qᵢ] − [Rᵢ]) ≤ 0) go to Sᵢ else go to Pᵢ₊₁

Si noti che Subleq è un sottoinsieme di PQRS .

Subleq è stato dimostrato completo, quindi anche PQRS dovrebbe essere completo!

Struttura del programma

PQRS definisce un'intestazione iniziale come segue:

H₀ H₁ H₂ H₃ H₄ H₅ H₆ H₇

H₀ H₁ H₂ H₃è sempre la prima istruzione P₀ Q₀ R₀ S₀. H₀a H₃dover essere definita in fase di carico.

PQRS ha un I / O rudimentale, ma sufficiente per la sfida.

H₄ H₅: all'avvio del programma, legge un massimo di H₅caratteri ASCII dall'input standard e salva come parole dall'indice in H₄poi. H₄e H₅devono essere definiti al momento del caricamento. Dopo la lettura, H₅verrà impostato il numero di caratteri letti (e le parole salvate).

H₆ H₇: al termine del programma, a partire dall'indice H₆, stampa tutti i byte che comprendono le H₇parole sullo standard output come caratteri ASCII. H₆e H₇devono essere definiti prima della fine del programma. I byte null '\0'nell'output verranno ignorati.

fine

La risoluzione si ottiene impostando Sᵢlimiti i < 0o i ≥ v.

Trucchi

I quartetti Pᵢ Qᵢ Rᵢ Sᵢnon devono essere allineati o sequenziali, la ramificazione è consentita ad intervalli del sub-quartetto.

PQRS è indiretto, quindi, a differenza di Subleq, c'è abbastanza flessibilità per implementare le subroutine.

Il codice può essere auto-modificante!

Interprete

L'interprete è scritto in C:

#include <stdlib.h>
#include <stdio.h>

// The "architecture"
enum {P, Q, R, S, START_OF_INPUT, LENGTH_OF_INPUT, START_OF_OUTPUT, LENGTH_OF_OUTPUT};
const int NEXT = S + 1;

// Recommend optimized!
#define OPTIMIZED

#ifdef PER_SPECS
// This may not work on all OSes and architectures - just too much memory needed!
const int K = 1002000200;
#else // OPTIMIZED
// This provides enough space to run the test cases with challenge-optimized.pqrs
const int K = 5200;
#endif

int main(int argc, char *argv[])
{
    int i, *M;
    char *p;
    FILE *program;

    // Allocate enough memory
    M = calloc(K, sizeof(int));

    // Load the program
    for (i = 0, program = fopen(argv[1], "r"); i < K && !feof(program); i++)
        if (!fscanf(program, "%d", M + i))
            break;
    fclose(program);

    // Read in the input
    for (i = 0; i < M[LENGTH_OF_INPUT] && !feof(stdin); i++)
    {
        int c = fgetc(stdin);
        if (c != EOF)
            M[M[START_OF_INPUT] + i] = c;
        else
            break;
    }
    M[LENGTH_OF_INPUT] = i;

    // Execute until terminated
    for (i = 0; i >= 0 && i < K; )
        i = (M[M[P + i]] = M[M[Q + i]] - M[M[R + i]]) <= 0? M[S + i]: i + NEXT;

    // Write the output
    for (i = 0, p = (char *)(M + M[START_OF_OUTPUT]); i < M[LENGTH_OF_OUTPUT] * sizeof(int); i++)
        // Ignore '\0'
        if (p[i])
            fputc(p[i], stdout);

    // Done
    free(M);

    return 0;
}

Per usare, salva quanto sopra come pqrs.c quindi compila:

gcc -o pqrs pqrs.c

Programma di esempio

Gli echi immettono fino a 40 caratteri, preceduti da 'PQRS-'.

8 8 8 -1 14 40 9 45 0 80 81 82 83 45

Per eseguire, salvare quanto sopra come echo.pqrs, quindi:

$ ./prqs echo.pqrs
greetings[enter]
[ctrl-D]
PQRS-greetings

Esecuzione dei casi di test:

$ ./pqrs challenge-optimized.pqrs < test-1.txt
1

$ ./pqrs challenge-optimized.pqrs < test-2.txt
11111111111111111

$ ./pqrs challenge-optimized.pqrs < test-3.txt
[lots of ones!]

$ ./pqrs challenge-optimized.pqrs < test-3.txt | wc
0       1     773

Tutti i casi di test vengono eseguiti molto rapidamente, ad es. <500 ms.

La sfida

PQRS può essere considerato stabile, quindi la sfida inizia 2015-10-31 13:00 e termina 2015-11-08 13:00, volte in UTC.

In bocca al lupo!

Soluzione

La lingua è abbastanza simile a quella usata in "Baby" , la prima macchina digitale elettronica a programma memorizzato al mondo. In quella pagina c'è un programma per trovare il fattore più alto di un numero intero in meno di 32 parole di memoria (CRT!)!

Ho scoperto che scrivere la soluzione per conformarsi alle specifiche non era compatibile con il sistema operativo e la macchina che stavo usando (un derivato di Ubuntu Linux su hardware leggermente più vecchio). Stava solo richiedendo più memoria di quella disponibile e il core dumping. Su sistemi operativi con gestione avanzata della memoria virtuale o macchine con almeno 8 GB di memoria, è possibile eseguire la soluzione in base alle specifiche. Ho fornito entrambe le soluzioni.

È molto difficile codificare direttamente in PQRS , simile al linguaggio della macchina da scrivere, forse anche al microcodice. Invece è più facile scrivere in una sorta di linguaggio assembly, quindi "compilarlo". Di seguito è riportato un linguaggio di assemblaggio annotato per la soluzione ottimizzata per l'esecuzione dei casi di test:

; ANNOTATED PQRS ASSEMBLER CODE
; MINIMAL SIZED BUFFERS TO RUN THE TEST CASES

;OFFSET   LABEL       P-OP        Q-OP        R-OP        S-OP
0                     TEMP        ZERO        ZERO        L1

; INPUT AND OUTPUT LOCATIONS AND SIZES
4                     INPUT       4000        
6         L0:         OUTPUT      1000        

; GET CURRENT INPUT
8         L1:         TEMP        INPUT       ASCII_ZERO  L2
12                    SUM         SUM         INC         IGNORE
16                    L1+1        L1+1        INC         IGNORE
20                    TEMP        ZERO        ZERO        L1

; CHECK IF END OF NUMBERS
24        L2:         NUMBERS     SUM         ZERO        L3

; ADVANCE TO NEXT NUMBER
28                    L2          L2          INC         IGNORE

; ADVANCE COUNT
32                    COUNT       COUNT       INC         IGNORE
36                    L1+1        L1+1        INC         IGNORE

; CLEAR SUM AND GO BACK
40                    SUM         ZERO        ZERO        L1

; SORT NUMBERS                
44        L3:         P           NUMBERS     ZERO        LA
48        L4:         Q           NUMBERS+1   ZERO        L9

; COMPARE                
52                    TEMP        Q           P           L8

; SWAP IF OUT OF ORDER
56                    L5+1        L3+1        ZERO        IGNORE
60                    L6          L3+1        ZERO        IGNORE
64                    L6+1        L4+1        ZERO        IGNORE
68                    L7          L4+1        ZERO        IGNORE
72        L5:         TEMP        P           ZERO        IGNORE
76        L6:         P           Q           ZERO        IGNORE
80        L7:         Q           TEMP        ZERO        IGNORE

; INCREMENT INNER INDEX
84        L8:         L4+1        L4+1        INC         IGNORE
88                    TEMP        ZERO        ZERO        L3

; INCREMENT OUTER INDEX AND RESET INNER INDEX
92        L9:         L3+1        L3+1        INC         IGNORE
96                    L4+1        L3+1        INC         IGNORE
100                   TEMP        ZERO        ZERO        L3

; OUTPUT THIRD LARGEST NUMBER
104                   L0+1        NUMBERS+2   ZERO        IGNORE
108       LA:         TEMP        NUMBERS+2   ZERO        EXIT
112       LB:         OUTPUT      ASCII_ONE   ZERO        IGNORE
116                   LB          LB          INC         IGNORE
120                   NUMBERS+2   NUMBERS+2   DEC         EXIT
124                   TEMP        ZERO        ZERO        LA

; SAFETY LOOP – JUST IN CASE IGNORE DOESN'T WORK AS PLANNED!
128       IGNORE:     TEMP        ZERO        ZERO        IGNORE

; CONSTANTS
132       ZERO:        0
133       INC:        -1
134       DEC:         1
135       ASCII_ZERO: 48
136       ASCII_ONE:  49

; VARIABLES
137       TEMP:       [1]
138       SUM:        [1]
139       COUNT:      [1]
140       P:          [1]
141       Q:          [1]

; WORKING SPACE
142       NUMBERS:    [10]

; I/O SPACE
152       INPUT:      [4000]
4152      OUTPUT:     [1000]
5152

Ciò che fa è analizzare l'input, convertendo unario in binario, quindi un ordinamento a bolle sui numeri con i valori in ordine decrescente, quindi alla fine restituisce il terzo valore più grande convertendo il binario in unario.

Notare che INC(incremento) è negativo e DEC(decremento) è positivo! Dove sta usando L#o L#+1come P-o Q-OPs, quello che sta succedendo è che sta aggiornando i puntatori: incremento, decremento, scambio, ecc. L'assemblatore è stato compilato a mano in PQRS sostituendo le etichette con gli offset. Di seguito è la soluzione ottimizzata PQRS :

137 132 132 8
152 4000
4152 1000
137 152 135 24
138 138 133 128
9 9 133 128
137 132 132 8
142 138 132 44
24 24 133 128
139 139 133 128
9 9 133 128
138 132 132 8
140 142 132 108
141 143 132 92
137 141 140 84
73 45 132 128
76 45 132 128
77 49 132 128
80 49 132 128
137 140 132 128
140 141 132 128
141 137 132 128
49 49 133 128
137 132 132 44
45 45 133 128
49 45 133 128
137 132 132 44
7 144 132 128
137 144 132 -1
4152 136 132 128
112 112 133 128
144 144 134 -1
137 132 132 108
137 132 132 128
0
-1
1
48
49

Il codice sopra può essere salvato challenge-optimized.pqrsper eseguire i casi di test.

Per completezza, ecco la fonte delle specifiche:

; ANNOTATED PQRS ASSEMBLER CODE
; FULL SIZED BUFFERS TO RUN ACCORDING TO SPECS

;OFFSET   LABEL       P-OP        Q-OP        R-OP        S-OP
0                     TEMP        ZERO        ZERO        L1

; INPUT AND OUTPUT LOCATIONS AND SIZES
4                     INPUT       10^9        
6         L0:         OUTPUT      10^6        

; GET CURRENT INPUT
8         L1:         TEMP        INPUT       ASCII_ZERO  L2
12                    SUM         SUM         INC         IGNORE
16                    L1+1        L1+1        INC         IGNORE
20                    TEMP        ZERO        ZERO        L1

; CHECK IF END OF NUMBERS
24        L2:         NUMBERS     SUM         ZERO        L3

; ADVANCE TO NEXT NUMBER
28                    L2          L2          INC         IGNORE

; ADVANCE COUNT
32                    COUNT       COUNT       INC         IGNORE
36                    L1+1        L1+1        INC         IGNORE

; CLEAR SUM AND GO BACK
40                    SUM         ZERO        ZERO        L1

; SORT NUMBERS                
44        L3:         P           NUMBERS     ZERO        LA
48        L4:         Q           NUMBERS+1   ZERO        L9

; COMPARE                
52                    TEMP        Q           P           L8

; SWAP IF OUT OF ORDER
56                    L5+1        L3+1        ZERO        IGNORE
60                    L6          L3+1        ZERO        IGNORE
64                    L6+1        L4+1        ZERO        IGNORE
68                    L7          L4+1        ZERO        IGNORE
72        L5:         TEMP        P           ZERO        IGNORE
76        L6:         P           Q           ZERO        IGNORE
80        L7:         Q           TEMP        ZERO        IGNORE

; INCREMENT INNER INDEX
84        L8:         L4+1        L4+1        INC         IGNORE
88                    TEMP        ZERO        ZERO        L3

; INCREMENT OUTER INDEX AND RESET INNER INDEX
92        L9:         L3+1        L3+1        INC         IGNORE
96                    L4+1        L3+1        INC         IGNORE
100                   TEMP        ZERO        ZERO        L3

; OUTPUT THIRD LARGEST NUMBER
104                   L0+1        NUMBERS+2   ZERO        IGNORE
108       LA:         TEMP        NUMBERS+2   ZERO        EXIT
112       LB:         OUTPUT      ASCII_ONE   ZERO        IGNORE
116                   LB          LB          INC         IGNORE
120                   NUMBERS+2   NUMBERS+2   DEC         EXIT
124                   TEMP        ZERO        ZERO        LA

; SAFETY LOOP – JUST IN CASE IGNORE DOESN'T WORK AS PLANNED!
128       IGNORE:     TEMP        ZERO        ZERO        IGNORE

; CONSTANTS
132       ZERO:        0
133       INC:        -1
134       DEC:         1
135       ASCII_ZERO: 48
136       ASCII_ONE:  49

; VARIABLES
137       TEMP:       [1]
138       SUM:        [1]
139       COUNT:      [1]
140       P:          [1]
141       Q:          [1]

; WORKING SPACE
142       NUMBERS:    [10^6]

; I/O SPACE
1000142   INPUT:      [10^9]
1001000142 OUTPUT:    [10^6]
1002000142

E soluzione:

137 132 132 8
1000142 1000000000
1001000142 1000000
137 1000142 135 24
138 138 133 128
9 9 133 128
137 132 132 8
142 138 132 44
24 24 133 128
139 139 133 128
9 9 133 128
138 132 132 8
140 142 132 108
141 143 132 92
137 141 140 84
73 45 132 128
76 45 132 128
77 49 132 128
80 49 132 128
137 140 132 128
140 141 132 128
141 137 132 128
49 49 133 128
137 132 132 44
45 45 133 128
49 45 133 128
137 132 132 44
7 144 132 128
137 144 132 -1
1001000142 136 132 128
112 112 133 128
144 144 134 -1
137 132 132 108
137 132 132 128
0
-1
1
48
49

Per eseguire quanto sopra, è necessario commentare #define OPTIMIZEDe aggiungere #define PER_SPECSin pqrs.ce ricompilare.

Questa è stata una grande sfida - mi è piaciuto molto l'allenamento mentale! Mi ha riportato ai miei vecchi giorni di assemblatore 6502 ...

Se dovessi implementare PQRS come un linguaggio di programmazione "reale", aggiungerei probabilmente ulteriori modalità per l'accesso diretto e doppiamente indiretto oltre all'accesso indiretto, nonché la posizione relativa e la posizione assoluta, entrambe con opzioni di accesso indiretto per la diramazione!


3
Dovresti avere una soluzione preparata prima di pubblicare.
feersum

1
Sì, la soluzione è pronta. Capisco che ci siano dei dubbi perché il linguaggio è davvero piuttosto difficile da usare. Per coloro che desiderano un'anteprima, posso inviarlo a te, a condizione che tu prometta di non rivelarlo prima della fine della sfida!

6

Zinco, incrinato! di @Zgarb

Disponibile anche su GitHub .

Hai bisogno di Dart 1.12 e Pub. Basta eseguire pub getper scaricare l'unica dipendenza, una libreria di analisi.

Speriamo che questo duri più di 30 minuti! : O

La lingua

Lo zinco è orientato alla ridefinizione degli operatori. Puoi ridefinire facilmente tutti gli operatori nella lingua!

La struttura di un tipico programma di zinco è simile a:

let
<operator overrides>
in <expression>

Esistono solo due tipi di dati: numeri interi e set. Non esiste un set letterale e gli insiemi vuoti non sono consentiti.

espressioni

Le seguenti sono espressioni valide in zinco:

letterali

Lo zinco supporta tutti i normali letterali interi, come 1e -2.

variabili

Lo zinco ha variabili (come la maggior parte delle lingue). Per fare riferimento a loro, basta usare il nome. Ancora una volta come la maggior parte delle lingue!

Tuttavia, esiste una variabile speciale chiamata Sche si comporta in qualche modo come quella di Pyth Q. Quando lo usi per la prima volta, leggerà in una riga dall'input standard e lo interpreterà come un insieme di numeri. Ad esempio, la riga di input 1234231si trasformerebbe nel set {1, 2, 3, 4, 3, 2, 1}.

NOTA IMPORTANTE!!! In alcuni casi, un valore letterale alla fine di una sostituzione dell'operatore viene analizzato in modo errato, quindi è necessario racchiuderlo tra parentesi.

Operazioni binarie

Sono supportate le seguenti operazioni binarie:

  • Aggiunta via +: 1+1.
  • Sottrazione via -: 1-1.
  • MOLTIPLICAZIONE via *: 2*2.
  • Divisione via /: 4/2.
  • Parità con =: 3=3.

Inoltre, è supportata anche la seguente operazione unaria:

  • Lunghezza con #: #x.

La precedenza è sempre associativa giusta. È possibile utilizzare le parentesi per ignorare questo.

Solo l'uguaglianza e la lunghezza funzionano sui set. Quando si tenta di ottenere la lunghezza di un numero intero, si otterrà il numero di cifre nella sua rappresentazione di stringa.

Imposta le comprensioni

Al fine di manipolare i set, lo zinco ha impostato le comprensioni. Sembrano così:

{<variable>:<set><clause>}

Una clausola è una clausola when o una clausola di ordinamento.

Una clausola di quando assomiglia ^<expression>. L'espressione che segue il punto di inserimento deve comportare un numero intero. L'uso della clausola when prenderà solo gli elementi nel set per i quali expressionè diverso da zero. All'interno dell'espressione, la variabile _verrà impostata sull'indice corrente nel set. È approssimativamente equivalente a questo Python:

[<variable> for _, <variable> in enumerate(<set>) when <expression> != 0]

Una clausola di ordinamento , che assomiglia $<expression>, ordina l'insieme in ordine decrescente per valore di <expression>. È uguale a questo Python:

sorted(<set>, key=lambda <variable>: <expression>)[::-1]

Ecco alcuni esempi di comprensione:

  • Prendi solo gli elementi di set suguali a 5:

    {x:s^x=5}
    
  • Ordina l'insieme in sbase al valore se i suoi elementi sono al quadrato:

    {x:s$x*x}
    

Sostituzioni

Le sostituzioni operatore consentono di ridefinire gli operatori. Sembrano così:

<operator>=<operator>

o:

<variable><operator><variable>=<expression>

Nel primo caso, è possibile definire un operatore per eguagliare un altro operatore. Ad esempio, posso definire +di sottrarre effettivamente tramite:

+=-

Quando lo fai, puoi ridefinire un operatore per essere un operatore magico . Esistono due operatori magici:

  • joinprende un set e un numero intero e unisce il contenuto del set. Ad esempio, l'unione {1, 2, 3}con 4comporterà il numero intero 14243.

  • cutaccetta anche un set e un numero intero e partizionerà il set ad ogni occorrenza del numero intero. Usando cuton {1, 3, 9, 4, 3, 2}e 3creerai {{1}, {9, 4}, {2}}... MA tutti i set di elementi singoli sono appiattiti, quindi il risultato sarà effettivamente {1, {9, 4}, 2}.

Ecco un esempio che ridefinisce l' +operatore nel senso join:

+=join

Per quest'ultimo caso, è possibile ridefinire un operatore per l'espressione data. Ad esempio, questo definisce l'operazione più per aggiungere i valori e quindi aggiungere 1:

x+y=1+:x+:y

Ma che cos'è +:? È possibile aggiungere i due punti :a un operatore per utilizzare sempre la versione integrata. Questo esempio usa l'integrato +via +:per sommare i numeri, quindi aggiunge un 1 (ricorda, tutto è associativo a destra).

L'override dell'operatore lunghezza sembra un po 'come:

#x=<expression>

Si noti che quasi tutte le operazioni integrate (tranne l'uguaglianza) useranno questo operatore di lunghezza per determinare la lunghezza dell'insieme. Se lo hai definito come:

#x=1

ogni parte di zinco che funziona sui set tranne che =opererebbe solo sul primo elemento del set che gli è stato dato.

Sostituzioni multiple

È possibile ignorare più operatori separandoli con virgole:

let
+=-,
*=/
in 1+2*3

Stampa

Non è possibile stampare direttamente nulla in zinco. inVerrà stampato il risultato dell'espressione seguente . I valori di un set verranno concatenati al separatore. Ad esempio, prendi questo:

let
...
in expr

Se exprè impostato {1, 3, {2, 4}}, 1324verrà stampato sullo schermo al termine del programma.

Mettere tutto insieme

Ecco un semplice programma di zinco che sembra aggiungere 2+2ma fa sì che il risultato sia 5:

let
x+y=1+:x+:y
in 1+2

L'interprete

Questo va in bin/zinc.dart:

import 'package:parsers/parsers.dart';
import 'dart:io';

// An error.
class Error implements Exception {
  String cause;
  Error(this.cause);
  String toString() => 'error in Zinc script: $cause';
}


// AST.
class Node {
  Obj interpret(ZincInterpreter interp) => null;
}

// Identifier.
class Id extends Node {
  final String id;
  Id(this.id);
  String toString() => 'Id($id)';
  Obj interpret(ZincInterpreter interp) => interp.getv(id);
}

// Integer literal.
class IntLiteral extends Node {
  final int value;
  IntLiteral(this.value);
  String toString() => 'IntLiteral($value)';
  Obj interpret(ZincInterpreter interp) => new IntObj(value);
}

// Any kind of operator.
class Anyop extends Node {
  void set(ZincInterpreter interp, OpFuncType func) {}
}

// Operator.
class Op extends Anyop {
  final String op;
  final bool orig;
  Op(this.op, [this.orig = false]);
  String toString() => 'Op($op, $orig)';
  OpFuncType get(ZincInterpreter interp) =>
    this.orig ? interp.op0[op] : interp.op1[op];
  void set(ZincInterpreter interp, OpFuncType func) { interp.op1[op] = func; }
}

// Unary operator (len).
class Lenop extends Anyop {
  final bool orig;
  Lenop([this.orig = false]);
  String toString() => 'Lenop($orig)';
  OpFuncType get(ZincInterpreter interp) =>
    this.orig ? interp.op0['#'] : interp.op1['#'];
  void set(ZincInterpreter interp, OpFuncType func) { interp.op1['#'] = func; }
}

// Magic operator.
class Magicop extends Anyop {
  final String op;
  Magicop(this.op);
  String toString() => 'Magicop($op)';
  Obj interpret_with(ZincInterpreter interp, Obj x, Obj y) {
    if (op == 'cut') {
      if (y is! IntObj) { throw new Error('cannot cut int with non-int'); }
      if (x is IntObj) {
        return new SetObj(x.value.toString().split(y.value.toString()).map(
          int.parse));
      } else {
        assert(x is SetObj);
        List<List<Obj>> res = [[]];
        for (Obj obj in x.vals(interp)) {
          if (obj == y) { res.add([]); }
          else { res.last.add(obj); }
        }
        return new SetObj(new List.from(res.map((l) =>
          l.length == 1 ? l[0] : new SetObj(l))));
      }
    } else if (op == 'join') {
      if (x is! SetObj) { throw new Error('can only join set'); }
      if (y is! IntObj) { throw new Error('can only join set with int'); }
      String res = '';
      for (Obj obj in x.vals(interp)) {
        if (obj is! IntObj) { throw new Error('joining set must contain ints'); }
        res += obj.value.toString();
      }
      return new IntObj(int.parse(res));
    }
  }
}

// Unary operator (len) expression.
class Len extends Node {
  final Lenop op;
  final Node value;
  Len(this.op, this.value);
  String toString() => 'Len($op, $value)';
  Obj interpret(ZincInterpreter interp) =>
    op.get(interp)(interp, value.interpret(interp), null);
}

// Binary operator expression.
class Binop extends Node {
  final Node lhs, rhs;
  final Op op;
  Binop(this.lhs, this.op, this.rhs);
  String toString() => 'Binop($lhs, $op, $rhs)';
  Obj interpret(ZincInterpreter interp) =>
    op.get(interp)(interp, lhs.interpret(interp), rhs.interpret(interp));
}

// Clause.
enum ClauseKind { Where, Sort }
class Clause extends Node {
  final ClauseKind kind;
  final Node expr;
  Clause(this.kind, this.expr);
  String toString() => 'Clause($kind, $expr)';
  Obj interpret_with(ZincInterpreter interp, SetObj set, Id id) {
    List<Obj> res = [];
    List<Obj> values = set.vals(interp);
    switch (kind) {
    case ClauseKind.Where:
      for (int i=0; i<values.length; i++) {
        Obj obj = values[i];
        interp.push_scope();
        interp.setv(id.id, obj);
        interp.setv('_', new IntObj(i));
        Obj x = expr.interpret(interp);
        interp.pop_scope();
        if (x is IntObj) {
          if (x.value != 0) { res.add(obj); }
        } else { throw new Error('where clause condition must be an integer'); }
      }
      break;
    case ClauseKind.Sort:
      res = values;
      res.sort((x, y) {
        interp.push_scope();
        interp.setv(id.id, x);
        Obj x_by = expr.interpret(interp);
        interp.setv(id.id, y);
        Obj y_by = expr.interpret(interp);
        interp.pop_scope();
        if (x_by is IntObj && y_by is IntObj) {
          return x_by.value.compareTo(y_by.value);
        } else { throw new Error('sort clause result must be an integer'); }
      });
      break;
    }
    return new SetObj(new List.from(res.reversed));
  }
}

// Set comprehension.
class SetComp extends Node {
  final Id id;
  final Node set;
  final Clause clause;
  SetComp(this.id, this.set, this.clause);
  String toString() => 'SetComp($id, $set, $clause)';
  Obj interpret(ZincInterpreter interp) {
    Obj setobj = set.interpret(interp);
    if (setobj is SetObj) {
      return clause.interpret_with(interp, setobj, id);
    } else { throw new Error('set comprehension rhs must be set type'); }
  }
}

// Operator rewrite.
class OpRewrite extends Node {
  final Anyop op;
  final Node value;
  final Id lid, rid; // Can be null!
  OpRewrite(this.op, this.value, [this.lid, this.rid]);
  String toString() => 'OpRewrite($lid, $op, $rid, $value)';
  Obj interpret(ZincInterpreter interp) {
    if (lid != null) {
      // Not bare.
      op.set(interp, (interp,x,y) {
        interp.push_scope();
        interp.setv(lid.id, x);
        if (rid == null) { assert(y == null); }
        else { interp.setv(rid.id, y); }
        Obj res = value.interpret(interp);
        interp.pop_scope();
        return res;
      });
    } else {
      // Bare.
      if (value is Magicop) {
        op.set(interp, (interp,x,y) => value.interpret_with(interp, x, y));
      } else {
        op.set(interp, (interp,x,y) => (value as Anyop).get(interp)(x, y));
      }
    }
    return null;
  }
}

class Program extends Node {
  final List<OpRewrite> rws;
  final Node expr;
  Program(this.rws, this.expr);
  String toString() => 'Program($rws, $expr)';
  Obj interpret(ZincInterpreter interp) {
    rws.forEach((n) => n.interpret(interp));
    return expr.interpret(interp);
  }
}


// Runtime objects.
typedef Obj OpFuncType(ZincInterpreter interp, Obj x, Obj y);

class Obj {}

class IntObj extends Obj {
  final int value;
  IntObj(this.value);
  String toString() => 'IntObj($value)';
  bool operator==(Obj rhs) => rhs is IntObj && value == rhs.value;
  String dump() => value.toString();
}

class SetObj extends Obj {
  final List<Obj> values;
  SetObj(this.values) {
    if (values.length == 0) { throw new Error('set cannot be empty'); }
  }
  String toString() => 'SetObj($values)';
  bool operator==(Obj rhs) => rhs is SetObj && values == rhs.values;
  String dump() => values.map((x) => x.dump()).reduce((x,y) => x+y);
  List<Obj> vals(ZincInterpreter interp) {
    Obj lenobj = interp.op1['#'](interp, this, null);
    int len;
    if (lenobj is! IntObj) { throw new Error('# operator must return an int'); }
    len = lenobj.value;
    if (len < 0) { throw new Error('result of # operator must be positive'); }
    return new List<Obj>.from(values.getRange(0, len));
  }
}


// Parser.
class ZincParser extends LanguageParsers {
  ZincParser(): super(reservedNames: ['let', 'in', 'join', 'cut']);
  get start => prog().between(spaces, eof);
  get comma => char(',') < spaces;
  get lp => symbol('(');
  get rp => symbol(')');
  get lb => symbol('{');
  get rb => symbol('}');
  get colon => symbol(':');
  get plus => symbol('+');
  get minus => symbol('-');
  get star => symbol('*');
  get slash => symbol('/');
  get eq => symbol('=');
  get len => symbol('#');
  get in_ => char(':');
  get where => char('^');
  get sort => char('\$');

  prog() => reserved['let'] + oprw().sepBy(comma) + reserved['in'] + expr() ^
            (_1,o,_2,x) => new Program(o,x);
  oprw() => oprw1() | oprw2() | oprw3();
  oprw1() => (basicop() | lenop()) + eq + (magicop() | op()) ^
             (o,_,r) => new OpRewrite(o,r);
  oprw2() => (id() + op() + id()).list + eq + expr() ^
             (l,_,x) => new OpRewrite(l[1], x, l[0], l[2]);
  oprw3() => lenop() + id() + eq + expr() ^ (o,a,_,x) => new OpRewrite(o, x, a);
  magicop() => (reserved['join'] | reserved['cut']) ^ (s) => new Magicop(s);
  basicop() => (plus | minus | star | slash | eq) ^ (op) => new Op(op);
  op() => (basicop() + colon ^ (op,_) => new Op(op.op, true)) | basicop();
  lenop() => (len + colon ^ (_1,_2) => new Lenop(true)) |
             len ^ (_) => new Lenop();
  expr() => setcomp() | unop() | binop() | prim();
  setcomp() => lb + id() + in_ + rec(expr) + clause() + rb ^
               (_1,i,_2,x,c,_3) => new SetComp(i,x,c);
  clausekind() => (where ^ (_) => ClauseKind.Where) |
                  (sort  ^ (_) => ClauseKind.Sort);
  clause() => clausekind() + rec(expr) ^ (k,x) => new Clause(k,x);
  unop() => lenop() + rec(expr) ^ (o,x) => new Len(o,x);
  binop() => prim() + op() + rec(expr) ^ (l,o,r) => new Binop(l,o,r);
  prim() => id() | intlit() | parens(rec(expr));
  id() => identifier ^ (i) => new Id(i);
  intlit() => intLiteral ^ (i) => new IntLiteral(i);
}


// Interpreter.
class ZincInterpreter {
  Map<String, OpFuncType> op0, op1;
  List<Map<String, Obj>> scopes;
  ZincInterpreter() {
    var beInt = (v) {
      if (v is IntObj) { return v.value; }
      else { throw new Error('argument to binary operator must be integer'); }
    };
    op0 = {
      '+': (_,x,y) => new IntObj(beInt(x)+beInt(y)),
      '-': (_,x,y) => new IntObj(beInt(x)-beInt(y)),
      '*': (_,x,y) => new IntObj(beInt(x)*beInt(y)),
      '/': (_,x,y) => new IntObj(beInt(x)/beInt(y)),
      '=': (_,x,y) => new IntObj(x == y ? 1 : 0),
      '#': (i,x,_2) =>
        new IntObj(x is IntObj ? x.value.toString().length : x.values.length)
    };
    op1 = new Map<String, OpFuncType>.from(op0);
    scopes = [{}];
  }

  void push_scope() { scopes.add({}); }
  void pop_scope() { scopes.removeLast(); }
  void setv(String name, Obj value) { scopes[scopes.length-1][name] = value; }
  Obj getv(String name) {
    for (var scope in scopes.reversed) {
      if (scope[name] != null) { return scope[name]; }
    }
    if (name == 'S') {
      var input = stdin.readLineSync() ?? '';
      var list = new List.from(input.codeUnits.map((c) =>
        new IntObj(int.parse(new String.fromCharCodes([c])))));
      setv('S', new SetObj(list));
      return getv('S');
    } else throw new Error('undefined variable $name');
  }
}


void main(List<String> args) {
  if (args.length != 1) {
    print('usage: ${Platform.script.toFilePath()} <file to run>');
    return;
  }
  var file = new File(args[0]);
  if (!file.existsSync()) {
    print('cannot open ${args[0]}');
    return;
  }
  Program root = new ZincParser().start.parse(file.readAsStringSync());
  ZincInterpreter interp = new ZincInterpreter();
  var res = root.interpret(interp);
  print(res.dump());
}

E questo va dentro pubspec.yaml:

name: zinc
dependencies:
  parsers: any

Soluzione prevista

let
#x=((x=S)*(-2))+#:x,
/=cut
in {y:{x:S/0$#:x}^_=2}

1
Comprendo correttamente che i set sono ordinati e possono avere duplicati, quindi sono fondamentalmente elenchi? Inoltre, se mi joinpiace un set misto {1,{3,2}}, ci sarà un errore? Non riesco a installare Dart in questo momento, quindi non posso controllare me stesso.
Zgarb,

@Zgarb Sì, in questo caso i set sono essenzialmente elenchi. Partecipare a set misti dovrebbe essere un errore, ma l'interprete in realtà blocca ATM ...
kirbyfan64sos

Come eseguo l'interprete? Se provo solo dart bin/zinc.dart test.zncottengo un errore di sintassi: 'file:///D:/Development/languages/zinc/bin/zinc.dart': error: line 323 pos 41: unexpected token '?'...var input = stdin.readLineSync() ?? '';
Martin Ender,


1
@Zgarb Ricordi quando, nelle specifiche, ho detto che tutte le operazioni integrate tranne l'uguaglianza usano l'operatore lunghezza? L'ho scavalcato per tornare -2+#:Squando è stato dato S, il che ha tagliato i due zeri finali. Speravo fosse risolto così. E ^non dovrebbe invertire il set ... quello era un bug ...
kirbyfan64sos

5

Zuppa di bussola ( incrinata dabox_box )

Interprete: C ++

Compass Soup è un po 'come una macchina di Turing con un infinito nastro bidimensionale. Il problema principale è che la memoria delle istruzioni e la memoria dei dati si trovano nello stesso spazio e l'output del programma è l'intero contenuto di quello spazio.

inserisci qui la descrizione dell'immagine

Come funziona

Un programma è un blocco di testo bidimensionale. Lo spazio del programma inizia con l'intero codice sorgente posizionato con il primo carattere in (0,0). Il resto dello spazio del programma è infinito ed è inizializzato con caratteri null (ASCII 0).

Esistono due puntatori che possono spostarsi nello spazio del programma:

  • Il puntatore di esecuzione ha una posizione e una direzione (nord, sud, est o ovest). Ogni segno di spunta, viene eseguita l'istruzione sotto il puntatore di esecuzione, quindi il puntatore di esecuzione si sposta nella sua direzione corrente. Il puntatore di esecuzione inizia a spostarsi verso est (positivo x), nella posizione del !personaggio o in (0,0) se non esiste.
  • Il puntatore ai dati ha solo una posizione. Si è mosso le istruzioni x, X, y, e Y. Inizia nella posizione del @personaggio o in (0,0) se non esiste.

Ingresso

Il contenuto di stdin viene stampato nello spazio del programma a partire dalla posizione del >carattere o in (0,0) se non esiste.

Produzione

Il programma termina quando il puntatore di esecuzione viene eseguito irrimediabilmente fuori dai limiti. L'output è l'intero contenuto dello spazio del programma in quel momento. Viene inviato a stdout e 'result.txt'.

Istruzioni

  • n - reindirizza il puntatore di esecuzione Nord (negativo y)
  • e - reindirizza il puntatore di esecuzione Est (positivo x)
  • s - reindirizza il puntatore di esecuzione Sud (positivo y)
  • w - reindirizza il puntatore di esecuzione Ovest (negativo x)
  • y - sposta il puntatore dei dati verso nord (negativo y)
  • X - sposta il puntatore dati verso est (positivo x)
  • Y - sposta il puntatore dati verso sud (positivo y)
  • x - sposta il puntatore dati verso ovest (negativo x)
  • p- scrive il carattere successivo rilevato dal puntatore di esecuzione sul puntatore dati. Quel personaggio non viene eseguito come istruzione.
  • j- verifica il carattere successivo rilevato dal puntatore di esecuzione rispetto al carattere sotto il puntatore dati. Quel personaggio non viene eseguito come un'istruzione. Se sono uguali, il puntatore di esecuzione passa al carattere successivo.
  • c - scrive il carattere null nel puntatore dei dati.
  • * - breakpoint: provoca l'interruzione dell'interprete.

Tutti gli altri caratteri vengono ignorati dal puntatore di esecuzione.

Interprete

L'interprete accetta il file sorgente come argomento e input su stdin. Ha un debugger steppable, che puoi invocare con un'istruzione breakpoint nel codice ( *). Se rotto, il puntatore di esecuzione viene mostrato come ASCII 178 (blocco con ombreggiatura più scura) e il puntatore dati viene mostrato come ASCII 177 (blocco con ombreggiatura più chiara).

#include <stdio.h>
#include <iostream>
#include <fstream>
#include <string>
#include <stdio.h>

// Compass Soup programming language interpreter
// created by Brian MacIntosh (BMacZero)
// for https://codegolf.stackexchange.com/questions/61804/create-a-programming-language-that-only-appears-to-be-unusable
//
// 31 October 2015

struct Point
{
    int x, y;
    Point(int ix, int iy) { x = ix; y = iy; };
    bool operator==(const Point &other) const
    {
        return other.x == x && other.y == y;
    }
    bool operator!=(const Point &other) const
    {
        return other.x != x || other.y != y;
    }
};

struct Bounds
{
    int xMin, xMax, yMin, yMax;
    Bounds(int xmin, int ymin, int xmax, int ymax)
    {
        xMin = xmin; yMin = ymin; xMax = xmax; yMax = ymax;
    }
    bool contains(Point pt)
    {
        return pt.x >= xMin && pt.x <= xMax && pt.y >= yMin && pt.y <= yMax;
    }
    int getWidth() { return xMax - xMin + 1; }
    int getHeight() { return yMax - yMin + 1; }
    bool operator==(const Bounds &other) const
    {
        return other.xMin == xMin && other.xMax == xMax && other.yMin == yMin && other.yMax == yMax;
    }
    bool operator!=(const Bounds &other) const
    {
        return other.xMin != xMin || other.xMax != xMax || other.yMin != yMin || other.yMax != yMax;
    }
};

int max(int a, int b) { return a > b ? a : b; }
int min(int a, int b) { return a < b ? a : b; }

Bounds hull(Point a, Bounds b)
{
    return Bounds(min(a.x, b.xMin), min(a.y, b.yMin), max(a.x, b.xMax), max(a.y, b.yMax));
}

Bounds hull(Bounds a, Bounds b)
{
    return Bounds(min(a.xMin, b.xMin), min(a.yMin, b.yMin), max(a.xMax, b.xMax), max(a.yMax, b.yMax));
}

Bounds programBounds(0,0,0,0);
char** programSpace;

Point execPtr(0,0);
Point execPtrDir(1,0);
Point dataPtr(0,0);
Point stdInPos(0,0);

bool breakpointHit = false;
char breakOn = 0;

/// reads the character from the specified position
char read(Point pt)
{
    if (programBounds.contains(pt))
        return programSpace[pt.x - programBounds.xMin][pt.y - programBounds.yMin];
    else
        return 0;
}

/// read the character at the data pointer
char readData()
{
    return read(dataPtr);
}

/// read the character at the execution pointer
char readProgram()
{
    return read(execPtr);
}

/// gets the bounds of the actual content of the program space
Bounds getTightBounds(bool debug)
{
    Bounds tight(0,0,0,0);
    for (int x = programBounds.xMin; x <= programBounds.xMax; x++)
    {
        for (int y = programBounds.yMin; y <= programBounds.yMax; y++)
        {
            if (read(Point(x, y)) != 0)
            {
                tight = hull(Point(x, y), tight);
            }
        }
    }
    if (debug)
    {
        tight = hull(dataPtr, tight);
        tight = hull(execPtr, tight);
    }
    return tight;
}

/// ensure that the program space encompasses the specified rectangle
void fitProgramSpace(Bounds bounds)
{
    Bounds newBounds = hull(bounds, programBounds);

    if (newBounds == programBounds) return;

    // allocate new space
    char** newSpace = new char*[newBounds.getWidth()];

    // copy content
    for (int x = 0; x < newBounds.getWidth(); x++)
    {
        newSpace[x] = new char[newBounds.getHeight()];
        for (int y = 0; y < newBounds.getHeight(); y++)
        {
            Point newWorldPos(x + newBounds.xMin, y + newBounds.yMin);
            newSpace[x][y] = read(newWorldPos);
        }
    }

    // destroy old space
    for (int x = 0; x < programBounds.getWidth(); x++)
    {
        delete[] programSpace[x];
    }
    delete[] programSpace;

    programSpace = newSpace;
    programBounds = newBounds;
}

/// outputs the current program space to a file
void outputToStream(std::ostream &stream, bool debug)
{
    Bounds tight = getTightBounds(debug);
    for (int y = tight.yMin; y <= tight.yMax; y++)
    {
        for (int x = tight.xMin; x <= tight.xMax; x++)
        {
            char at = read(Point(x, y));
            if (debug && x == execPtr.x && y == execPtr.y)
                stream << (char)178;
            else if (debug && x == dataPtr.x && y == dataPtr.y)
                stream << (char)177;
            else if (at == 0)
                stream << ' ';
            else
                stream << at;
        }
        stream << std::endl;
    }
}

/// writes a character at the specified position
void write(Point pt, char ch)
{
    fitProgramSpace(hull(pt, programBounds));
    programSpace[pt.x - programBounds.xMin][pt.y - programBounds.yMin] = ch;
}

/// writes a character at the data pointer
void write(char ch)
{
    write(dataPtr, ch);
}

/// writes a line of text horizontally, starting at the specified position
void writeLine(Point loc, std::string str, bool isSource)
{
    fitProgramSpace(Bounds(loc.x, loc.y, loc.x + str.size(), loc.y));
    for (unsigned int x = 0; x < str.size(); x++)
    {
        programSpace[x + loc.x][loc.y] = str[x];

        // record locations of things
        if (isSource)
        {
            switch (str[x])
            {
            case '>':
                stdInPos = Point(loc.x + x, loc.y);
                break;
            case '!':
                execPtr = Point(loc.x + x, loc.y);
                break;
            case '@':
                dataPtr = Point(loc.x + x, loc.y);
                break;
            }
        }
    }
}

void advanceExecPtr()
{
    execPtr.x += execPtrDir.x;
    execPtr.y += execPtrDir.y;
}

void breakpoint()
{
    breakpointHit = true;
    outputToStream(std::cout, true);
    std::cout << "[Return]: step | [Space+Return]: continue | [<char>+Return]: continue to <char>" << std::endl;
    while (true)
    {
        std::string input;
        std::getline(std::cin, input);
        if (input.size() == 0)
        {
            break;
        }
        else if (input.size() == 1)
        {
            if (input[0] == ' ')
            {
                breakpointHit = false;
                break;
            }
            else
            {
                breakOn = input[0];
                breakpointHit = false;
                break;
            }
        }
    }
}

int main(int argc, char** argv)
{
    if (argc != 2)
    {
        printf("Usage: CompassSoup <source-file>");
        return 1;
    }

    // open source file
    std::ifstream sourceIn(argv[1]);

    if (!sourceIn.is_open())
    {
        printf("Error reading source file.");
        return 1;
    }

    programSpace = new char*[1];
    programSpace[0] = new char[1];
    programSpace[0][0] = 0;

    // read starting configuration
    std::string line;
    int currentLine = 0;
    while (std::getline(sourceIn, line))
    {
        writeLine(Point(0, currentLine), line, true);
        currentLine++;
    }

    sourceIn.close();

    // take stdin
    std::string input;
    std::cout << ">";
    std::cin >> input;
    std::cin.ignore();
    writeLine(stdInPos, input, false);

    // execute
    while (programBounds.contains(execPtr))
    {
        if (execPtrDir.x == 0 && execPtrDir.y == 0)
        {
            printf("Implementation error: execPtr is stuck.");
            break;
        }

        advanceExecPtr();

        char command = readProgram();

        // breakpoint control code
        if (breakpointHit || (breakOn != 0 && command == breakOn))
        {
            breakOn = 0;
            breakpoint();
        }

        switch (command)
        {
        case 'n':
            execPtrDir = Point(0,-1);
            break;
        case 'e':
            execPtrDir = Point(1,0);
            break;
        case 's':
            execPtrDir = Point(0,1);
            break;
        case 'w':
            execPtrDir = Point(-1,0);
            break;
        case 'x':
            dataPtr.x--;
            break;
        case 'X':
            dataPtr.x++;
            break;
        case 'y':
            dataPtr.y--;
            break;
        case 'Y':
            dataPtr.y++;
            break;
        case 'p':
            advanceExecPtr();
            write(readProgram());
            break;
        case 'j':
            advanceExecPtr();
            if (readData() == readProgram())
            {
                advanceExecPtr();
            }
            break;
        case 'c':
            write(0);
            break;
        case '*':
            breakpoint();
            break;
        }
    }

    std::ofstream outputFile("result.txt");
    outputToStream(outputFile, false);
    outputToStream(std::cout, false);
    outputFile.close();
}

Esempi

Ciao mondo

Hello, World!

Gatto

>

Parità: accetta una stringa di caratteri terminata da uno zero ('0'). Emette yessulla prima riga dell'uscita se il numero di 1 nell'input è dispari, altrimenti emette |.

|>
!--eXj1s-c-eXj0s-c-exj|s-pyXpeXps
   c   |   c   |   |   |
  cn0j-w---n1j-w   n---w

Suggerimenti

Dovresti usare un buon editor di testo e fare un uso giudizioso della funzionalità del tasto 'Inserisci', e usare 'Alt-Drag' per aggiungere o eliminare il testo su più righe contemporaneamente.

Soluzione

Ecco la mia soluzione Non è bello come cartone_box perché ho dovuto cancellare il codice sorgente stesso. Speravo anche di trovare un modo per eliminare tutto il codice e lasciare solo la risposta, ma non ci sono riuscito.

Il mio approccio era quello di dividere le diverse sequenze di 1s su linee diverse, quindi ordinarle facendo 1"s" cadere tutte le s fino a quando non ne colpiscono un'altra 1, e infine cancellare tutto tranne la terza linea dopo l'input.

  • Il grande blocco in basso a destra #A#legge 1s e li copia sull'ultima riga della divisione fino a quando non 0viene letto a.
  • #B#controlla per un secondo 0e va a nord fino a che #D#ce n'è uno. Altrimenti, #C#avvia una nuova linea di divisione inserendo |dopo l'ultima e torna a #A#.
  • Il blocco sopra e sopra #F#è il codice di gravità. Cammina fino all'ultima 1della prima fila e la sposta verso l'alto fino a quando non colpisce 1o -. Se non può farlo, segna la riga come finita mettendola +prima.
  • #G#sta cancellando tutte le divisioni non necessarie e #H#sta cancellando lo stdin e tutto il codice tra parentesi.

Codice:

 s-----------------------w
 s-c-w  s-c-w  s---w    e+-
 eXj)nc-eXj)nc-exj(ncyj(nn
(n-----------------------------------------w                      ))
(  #H#                             s---w   |                      ))
(                                  exj+ncyxn                      ))
(                                  |                              ))
(                      s---w   s-c-+w                             ))
(                      exj+ncy-eXj1nn                             ))
(                      |                                          ))
(         s---w    s-c-+w    s+pxw                                ))
(         eyj-n-YY-eXj1nn    |  sn1jX--w           e----s         ))
(         |                  Y  x     e+---s e---s ns1jyw         ))
(      ej+n------s           j  |     nn+jYw-n+jxw-Yw   |         ))
(      Y   ec----s      e---s|  |                       1         ))
(      c   ns1jX-wYcYYY-n-jyww  |                       p         ))
(      ns+jxw      #G#       e--s                       Y         ))
(       e---n                   |               s------w|         ))
(                               |               |   ej-nn         ))
(             s--w              e----s   exc----eyj1n---n         ))
(#A#          p e+---s   s---w       |#F#|                        ))
(e----se---s  1 ||   |   exj|n----p+YeXj1ns                       ))
(ns-jXwn-jyw--w-nn1jXw   c #D#       n----w                       ))
( |        |         |   |                                        ))
( |        n---------+---+-------------|pw            s---w s---w ))
( |                  |   |     exp)XYs   |            eyj-nYeXj0ns)
( |         s---ws---+w  n-----+-----+---+------------+----------w))
( |         |   ||   ||  e--yp)n     e---+--s         |           )
( |     e-c-exj|neYj|nn  |     #C#       |  |         p           ))
( |     |                |     s---w s---+w s---w s---+w          ))
( |     |          #B#  e+s    |   | |   || |   | |   ||          ))
(!ep-Yj0n-c----------Xj0nne----exj|n-eYj|nn exj|n-eYj|nn          ))
(-@
 |>


Accidenti, così vicino! Condividerò la mia soluzione quando torno a casa stasera.
BMac,

Non riesco a far funzionare il programma di parità. Dovrebbe esserci un'istruzione di debug all'inizio? Se lo passo, rimane bloccato in un ciclo infinito, hai idea di cosa potrei fare di sbagliato?
febbraio

Sembra che all'inizio ci fosse un extra cche non avrebbe dovuto essere lì. L'ho riparato. Ho anche aggiunto la mia soluzione al problema.
BMac,

4

Acc! , Cracc'd di Ppperry

Questo linguaggio ha una struttura ad anello, matematica di numeri interi di base, I / O di caratteri e un accumulatore (da cui il nome). Solo un accumulatore. Quindi, il nome.

dichiarazioni

I comandi vengono analizzati riga per riga. Esistono tre tipi di comando:

  1. Count <var> while <cond>

Conti <var>up da 0 a patto che <cond>è diverso da zero, equivalente a C-style for(<var>=0; <cond>; <var>++). Il contatore di loop può essere una singola lettera minuscola. La condizione può essere qualsiasi espressione, non necessariamente implicando la variabile loop. Il ciclo si interrompe quando il valore della condizione diventa 0.

I loop richiedono parentesi graffe in stile K & R (in particolare, la variante Stroustrup ):

Count i while i-5 {
 ...
}
  1. Write <charcode>

Emette un singolo carattere con il dato valore ASCII / Unicode su stdout. Il charcode può essere qualsiasi espressione.

  1. Espressione

Qualsiasi espressione in piedi da sola viene valutata e assegnata nuovamente all'accumulatore (che è accessibile come _). Pertanto, ad esempio, 3è un'istruzione che imposta l'accumulatore su 3; _ + 1incrementa l'accumulatore; e _ * Nlegge un personaggio e moltiplica l'accumulatore per il suo codice.

Nota: l'accumulatore è l'unica variabile a cui è possibile assegnare direttamente; variabili loop e Npuò essere utilizzato nei calcoli ma non modificato.

L'accumulatore è inizialmente 0.

espressioni

Un'espressione può includere valori letterali interi, variabili di ciclo ( a-z), _per l'accumulatore e il valore speciale N, che legge un carattere e valuta il suo codice ogni volta che viene utilizzato. Nota: questo significa che ottieni solo un colpo per leggere ogni personaggio; la prossima volta che utilizzerai N, leggerai il prossimo.

Gli operatori sono:

  • +, inoltre
  • -, sottrazione; negazione unaria
  • *, moltiplicazione
  • /, divisione intera
  • %, modulo
  • ^, esponenziazione

Le parentesi possono essere utilizzate per imporre la precedenza delle operazioni. Qualsiasi altro carattere in un'espressione è un errore di sintassi.

Spazio bianco e commenti

Gli spazi bianchi iniziali e finali e le righe vuote vengono ignorati. Lo spazio bianco nelle intestazioni del loop deve essere esattamente come mostrato, con un singolo spazio tra l'intestazione del loop e l'apertura del parentesi graffa. Lo spazio bianco all'interno delle espressioni è facoltativo.

# inizia un commento a riga singola.

Input Output

Acc! si aspetta una singola riga di caratteri come input. Ogni carattere di input può essere recuperato in sequenza e il suo codice di accesso può essere elaborato utilizzando N. Il tentativo di leggere oltre l'ultimo carattere della riga provoca un errore. Un carattere può essere emesso passando il suo charcode Writeall'istruzione.

Interprete

L'interprete (scritto in Python 3) traduce Acc! codice in Python e execs.

import re, sys

def main():
    if len(sys.argv) != 2:
        print("Please supply a filename on the command line.", file=sys.stderr)
        return
    codeFile = sys.argv[1]
    with open(codeFile) as f:
        code = f.readlines()
    code = translate(code)
    exec(code, {"inputStream": (ord(char) for char in input())})

def translate(accCode):
    indent = 0
    loopVars = []
    pyCode = ["_ = 0"]
    for lineNum, line in enumerate(accCode):
        if "#" in line:
            # Strip comments
            line = line[:line.index("#")]
        line = line.strip()
        if not line:
            continue
        lineNum += 1
        if line == "}":
            if indent:
                loopVar = loopVars.pop()
                if loopVar is not None:
                    pyCode.append(" "*indent + loopVar + " += 1")
                indent -= 1
            else:
                raise SyntaxError("Line %d: unmatched }" % lineNum)
        else:
            m = re.fullmatch(r"Count ([a-z]) while (.+) \{", line)
            if m:
                expression = validateExpression(m.group(2))
                if expression:
                    loopVar = m.group(1)
                    pyCode.append(" "*indent + loopVar + " = 0")
                    pyCode.append(" "*indent + "while " + expression + ":")
                    indent += 1
                    loopVars.append(loopVar)
                else:
                    raise SyntaxError("Line %d: invalid expression " % lineNum
                                      + m.group(2))
            else:
                m = re.fullmatch(r"Write (.+)", line)
                if m:
                    expression = validateExpression(m.group(1))
                    if expression:
                        pyCode.append(" "*indent
                                      + "print(chr(%s), end='')" % expression)
                    else:
                        raise SyntaxError("Line %d: invalid expression "
                                          % lineNum
                                          + m.group(1))
                else:
                    expression = validateExpression(line)
                    if expression:
                        pyCode.append(" "*indent + "_ = " + expression)
                    else:
                        raise SyntaxError("Line %d: invalid statement "
                                          % lineNum
                                          + line)
    return "\n".join(pyCode)

def validateExpression(expr):
    "Translates expr to Python expression or returns None if invalid."
    expr = expr.strip()
    if re.search(r"[^ 0-9a-z_N()*/%^+-]", expr):
        # Expression contains invalid characters
        return None
    elif re.search(r"[a-zN_]\w+", expr):
        # Expression contains multiple letters or underscores in a row
        return None
    else:
        # Not going to check validity of all identifiers or nesting of parens--
        # let the Python code throw an error if problems arise there
        # Replace short operators with their Python versions
        expr = expr.replace("^", "**")
        expr = expr.replace("/", "//")
        # Replace N with a call to get the next input character
        expr = expr.replace("N", "inputStream.send(None)")
        return expr

if __name__ == "__main__":
    main()


3

GoToTape (sicuro)

(Precedentemente noto come Simp-plex.)

Questa lingua è semplice Il controllo di flusso principale è goto, la forma di controllo più naturale e utile.

Specifica del linguaggio

I dati sono memorizzati su un nastro e in un accumulatore. Funziona interamente con integrati non firmati. Ogni personaggio è il comando. Di seguito sono riportati tutti i comandi:

  • Lettere: a- zsono goto statement, andando a A- Z, rispettivamente.
  • :: imposta l'accumulatore sul valore ASCII su char dall'input.
  • ~: emette il carattere per il valore ASCII nell'accumulatore.
  • &: sottrarre uno dall'accumulatore se è 1 o più, altrimenti aggiungerne uno.
  • |: aggiungine uno all'accumulatore.
  • <: imposta il puntatore dati su 0.
  • +: incrementa la cella dati nel puntatore dati; sposta il puntatore +1.
  • -: sottrarre uno dalla cella di dati nel puntatore dei dati se è positivo; sposta il puntatore +1.
  • [...]: esegue il codice n volte dove n è il numero sul nastro nel puntatore dei dati (non può essere nidificato).
  • /: salta la prossima istruzione se l'accumulatore è 0.

Interprete (C ++)

#include <iostream>
#include <memory.h>
#include <fstream>
#include <iostream>
#include <string>
#include <sstream>
using namespace std;

int serch(char* str,char ch){
    for(int i = 0;str[i];i++){
        if(str[i]==ch)
            return i;
    }
    return -1;
}

void runCode(char* code){
    int i = 0;
    char c;
    unsigned int* tape;
    tape = new unsigned int[1000003];
    memset(tape,0,1000003*sizeof(int));
    unsigned int p=0;
    unsigned int a=0;
    unsigned int n;
    unsigned int s;

    while(c=code[i]){
        if('A'<=c && c<='Z');
        if('a'<=c && c<='z')i=serch(code, c+'A'-'a');
        if(':'==c)a=cin.get();
        if('+'==c)tape[p++]++;
        if('-'==c)tape[p++] += tape[p]?-1:0;
        if('|'==c)a++;
        if('&'==c)a=a?a-1:1;
        if('<'==c)p=0;
        if('['==c){if(tape[p]){n=tape[p];s=i;}else i+=serch(code+i,']');};
        if(']'==c)i=--n?i:s;
        if('~'==c)cout<<(char)a;
        if('/'==c)i+=a?0:1;
        if('$'==c)p=a;
        i++;
    }
    delete[](tape);
}

int main(int argc, char* argv[]) {
    if(argc == 2){

        ifstream sorceFile (argv[1]);
        string code(static_cast<stringstream const&>(stringstream() << sorceFile.rdbuf()).str());
        runCode((char*)code.c_str());
    }else
        cout << "Code file must be included as a command-line argument \n";
    return 0;
}

Divertiti!

Soluzione

A:+&&&&&&&&&&/gbG&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&/a<aB<[|]C[&]-[|]/c<[|]D[&]-[|]/d<[|]+E[&]|||||||||||||||||||||||||||||||||||||||||||||||||~X&/x-[|]/e


2
La tua codifica C ++ mi sta uccidendo! C'è un motivo che hai usato callocinvece di new char, hai scritto un ciclo while in stile C, usato la gestione della memoria in stile C, ci ha fatto ricompilare il file C ++ ogni volta che cambiamo il codice e hai usato 20 if invece di a switch? Non mi lamento, ma i miei occhi sanguinano in questo momento ...: O
kirbyfan64sos

3
Ho riparato lo speck per carne l'interprete.
MegaTom,

@ kirbyfan64sos Il codice non è valido. Ho un po 'messo insieme tutto questo, e forse non avrei dovuto farlo come avrei dovuto. la funzione principale può essere cambiata per prendere il codice come input. in effetti penso che lo farò ora ...
MegaTom,

1
La domanda dice che gli interpreti dovrebbero prendere un nome di file nella riga di comando per il programma .
Dennis,

Ecco alcuni modi brevi per leggere un file in una stringa . Quindi chiama str.c_str()per ottenere un char*.
feersum

0

Questa è stata una cattiva idea poiché quasi tutte le lingue esoteriche sembrano illeggibili (guarda Jelly).
Ma ecco qui:

Pylongolf2 beta6

Spingendo in pila

Spingendo in pila agisce diversamente che in altre lingue.
Il codice 78spinge 7e 8nello stack, tuttavia in Pylongolf spinge 78.
In Pylongolf2 questo è commutabile con Ü.

comandi

) Print the stack.
Ü Toggle the method Pylongolf2 uses for pushing to stack.
a The useless command, removes and adds the selected item in the same place.
c Ask for input.
n Convert string to a number.
" Toggle string mode for pushing text to the stack.
s Convert a number to a string. ╨ Encode the selected item (it must be a string).
_ Duplicate the selected item next to itself.
b Swap places between the selected item and the one before.
d Set the selected item to the last one.
m Move the selected item to the end of the stack.
@ Select an item. (Number required after this command as an argument).
w Wait a specified amount of time (the time is taken from the stack).
= Compare the selected item to the one before. (WARNING. THIS DELETES THE 2 ITEMS AND PLACES A true OR A false) (V2 beta)
~ Print the stack nicely. (V2 beta)
² Square a number. (V3 beta)
| Split a string to an array by the character after |. (V4 beta)
♀ Pop the array. (the contents are left in the stack) (V4 beta)
> Begin a while statement. (V5 beta)
< Loop back to the beginning of the while statement. (V5 beta)
! Break out of the while statements. (V5 beta)
? An if statement, does nothing if the selected item is a `true` boolean. (V6 beta)
¿ If an if statement is `false`, the interpreter skips everything to this character. (V6 beta)

Concatenazione di stringhe e rimozione di un modello Regex da una stringa

Il simbolo + concatena le stringhe.
È possibile utilizzare il simbolo - per rimuovere i caratteri che seguono un motivo regex da una stringa:

c╨2"[^a-zA-Z]"-~

Questo codice accetta input e rimuove tutti i caratteri non alfabetici rimuovendo tutti gli schemi corrispondenti [^a-zA-Z].
L'elemento selezionato deve essere la regex e quello precedente deve essere la stringa da modificare.

Se dichiarazioni

Per fare le istruzioni if, metti a =per confrontare l'elemento selezionato e quello successivo.
Questo colloca a trueo a falseal suo posto.
Il comando ?controlla questo valore booleano.
Se lo è, trueallora non fa nulla e l'interprete continua.
Se è falseallora l'interprete salta al ¿personaggio più vicino .

Tratto dalla pagina di Github.

Interprete per Pylongolf2 (Java):

package org.midnightas.pylongolf2;

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
import java.util.Scanner;

public class Pylongolf {

    public static final void main(String[] args) throws Exception {
        String content = new String(Files.readAllBytes(Paths.get(new File(args[0]).toURI()))) + " ";
        boolean fullreadMode = true;
        List<Object> stack = new ArrayList<Object>();
        List<Integer> whileStatements = new ArrayList<Integer>();
        HashMap<String, Object> vars = new HashMap<String, Object>();
        int ifStatements = 0;
        Scanner scanner = new Scanner(new UnclosableDecorator(System.in));
        int selectedIndex = 0;
        for (int cl = 0; cl < content.length(); cl++) {
            char c = content.charAt(cl);
            if (isNumber(c)) {
                if (!fullreadMode) {
                    stack.add(Double.parseDouble(c + ""));
                } else {
                    String number = "";
                    for (int cl0 = cl; cl0 < content.length(); cl0++) {
                        if (isNumber(content.charAt(cl0))) {
                            number += content.charAt(cl0);
                        } else {
                            cl = cl0 - 1;
                            stack.add(Double.parseDouble(number));
                            break;
                        }
                    }
                }
            } else if (c == ')') {
                System.out.println(Arrays.toString(stack.toArray()));
            } else if (c == 'Ü') {
                fullreadMode = !fullreadMode;
            } else if (c == '+') {
                if (stack.get(selectedIndex) instanceof Object[]) {
                    Object[] obj = (Object[]) stack.remove(selectedIndex);
                    Double dbl = new Double(0d);
                    for (Object o : obj) {
                        dbl += (Double) o;
                    }
                    stack.add(selectedIndex, dbl);
                } else {
                    Object obj0 = stack.remove(selectedIndex);
                    Object obj1 = stack.remove(selectedIndex);
                    if (obj0 instanceof Number && obj1 instanceof Number)
                        stack.add(((Number) obj0).doubleValue() + ((Number) obj1).doubleValue());
                    else if (obj0 instanceof String) {
                        stack.add(obj0.toString() + obj1.toString());
                    }
                }
            } else if (c == '-') {
                Object obj0 = stack.remove(selectedIndex);
                Object obj1 = stack.remove(selectedIndex);
                if (obj0 instanceof Number && obj1 instanceof Number)
                    stack.add(((Number) obj0).doubleValue() - ((Number) obj1).doubleValue());
                else if (obj0 instanceof String && obj1 instanceof String) {
                    stack.add(obj0.toString().replaceAll(obj1.toString(), ""));
                }
            } else if (c == '*') {
                Object obj0 = stack.remove(selectedIndex);
                Object obj1 = stack.remove(selectedIndex);
                if (obj0 instanceof Number && obj1 instanceof Number)
                    stack.add(((Number) obj0).doubleValue() * ((Number) obj1).doubleValue());
            } else if (c == '/') {
                Object obj0 = stack.remove(selectedIndex);
                Object obj1 = stack.remove(selectedIndex);
                if (obj0 instanceof Number && obj1 instanceof Number)
                    stack.add(((Number) obj0).doubleValue() / ((Number) obj1).doubleValue());
            } else if (c == 'a') {
                stack.add(selectedIndex, stack.remove(selectedIndex));
            } else if (c == 'c') {
                stack.add(scanner.nextLine());
            } else if (c == 'n') {
                if (stack.get(selectedIndex) instanceof String) {
                    stack.add(selectedIndex, Double.parseDouble(stack.remove(selectedIndex).toString()));
                } else if (stack.get(selectedIndex) instanceof Object[]) {
                    Object[] oldArray = (Object[]) stack.remove(selectedIndex);
                    Object[] newArray = new Object[oldArray.length];
                    for (int i = 0; i < oldArray.length; i++) {
                        newArray[i] = Double.parseDouble(oldArray[i].toString());
                    }
                    stack.add(selectedIndex, newArray);
                }
            } else if (c == '"') {
                String string = "\"";
                for (int cl0 = cl + 1; cl0 < content.length(); cl0++) {
                    string = string + content.charAt(cl0);
                    if (content.charAt(cl0) == '"') {
                        stack.add(string.substring(1, string.length() - 1));
                        cl = cl0;
                        break;
                    }
                }
            } else if (c == 's') {
                Object obj = stack.remove(selectedIndex);
                if (obj instanceof Double) {
                    Double dbl = (Double) obj;
                    if (dbl.doubleValue() == Math.floor(dbl)) {
                        stack.add(selectedIndex, "" + dbl.intValue() + "");
                    } else {
                        stack.add(selectedIndex, "" + dbl + "");
                    }
                }
            } else if (c == '╨') {
                cl++;
                char editmode = content.charAt(cl);
                if (editmode == '0') {
                    stack.add(selectedIndex, rot13(stack.remove(selectedIndex).toString()));
                } else if (editmode == '1') {
                    stack.add(selectedIndex,
                            new StringBuilder(stack.remove(selectedIndex).toString()).reverse().toString());
                } else if (editmode == '2') {
                    stack.add(selectedIndex, stack.remove(selectedIndex).toString().toLowerCase());
                } else if (editmode == '3') {
                    stack.add(selectedIndex, stack.remove(selectedIndex).toString().toUpperCase());
                }
            } else if (c == '_') {
                stack.add(selectedIndex, stack.get(selectedIndex));
            } else if (c == 'b') {
                stack.add(selectedIndex + 1, stack.remove(selectedIndex));
            } else if (c == 'd') {
                selectedIndex = stack.size() == 0 ? 0 : stack.size() - 1;
            } else if (c == 'm') {
                stack.add(stack.remove(selectedIndex));
            } else if (c == '@') {
                String number = "";
                for (int cl0 = cl + 1; cl0 < content.length(); cl0++) {
                    if (isNumber(content.charAt(cl0)))
                        number += content.charAt(cl0);
                    else {
                        cl = cl0 - 1;
                        selectedIndex = Integer.parseInt(number);
                        break;
                    }
                }
            } else if (c == 'w') {
                String number = "";
                for (int cl0 = cl + 1; cl0 < content.length(); cl0++) {
                    if (isNumber(content.charAt(cl0)))
                        number += content.charAt(cl0);
                    else {
                        cl = cl0 - 1;
                        Thread.sleep(Long.parseLong(number));
                        break;
                    }
                }
            } else if (c == '=') {
                Object obj0 = stack.remove(selectedIndex);
                Object obj1 = stack.remove(selectedIndex);
                stack.add(new Boolean(obj0.equals(obj1)));
            } else if (c == '~') {
                for (Object o : stack)
                    System.out.print(o);
                System.out.println();
            } else if (c == '²') {
                if (stack.get(selectedIndex) instanceof Double) {
                    Double dbl = (Double) stack.remove(selectedIndex);
                    stack.add(selectedIndex, dbl * dbl);
                } else if (stack.get(selectedIndex) instanceof Object[]) {
                    Object[] obj = (Object[]) stack.remove(selectedIndex);
                    Object[] newArray = new Object[obj.length];
                    for (int i = 0; i < obj.length; i++) {
                        newArray[i] = Math.pow((Double) obj[i], 2);
                    }
                    stack.add((Object[]) newArray);
                }
            } else if (c == '|') {
                String string = (String) stack.remove(selectedIndex);
                cl++;
                char splitChar = content.charAt(cl);
                stack.add((Object[]) string.split(splitChar + ""));
            } else if (c == '♀') {
                for (Object obj : (Object[]) stack.remove(selectedIndex)) {
                    stack.add(selectedIndex, obj);
                }
            } else if (c == '>') {
                whileStatements.add(new Integer(cl));
            } else if (c == '<') {
                cl = whileStatements.get(whileStatements.size() - 1);
            } else if (c == '!') {
                whileStatements.remove(whileStatements.size() - 1);
            } else if (c == '?') {
                if (stack.get(selectedIndex) instanceof Boolean) {
                    Boolean bool = (Boolean) stack.remove(selectedIndex);
                    if (bool == false) {
                        ifStatements++;
                        for (int cl0 = cl; cl0 < content.length(); cl0++) {
                            if (content.charAt(cl0) == '¿') {
                                ifStatements--;
                                cl = cl0;
                            }
                        }
                    }
                }
            } else if (c == 't') {
                break;
            } else if (c == '(') {
                stack.remove(selectedIndex);
            } else if (c == ':') {
                cl++;
                char charToVar = content.charAt(cl);
                vars.put(charToVar + "", stack.remove(selectedIndex));
            } else if (c >= 'A' && c <= 'Z') {
                stack.add(vars.get(c + ""));
            } else if (c == 'r') {
                stack.add(selectedIndex,
                        (double) new Random().nextInt(((Double) stack.remove(selectedIndex)).intValue() + 1));
            }
        }
        scanner.close();
    }

    public static String rot13(String input) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < input.length(); i++) {
            char c = input.charAt(i);
            if (c >= 'a' && c <= 'm')
                c += 13;
            else if (c >= 'A' && c <= 'M')
                c += 13;
            else if (c >= 'n' && c <= 'z')
                c -= 13;
            else if (c >= 'N' && c <= 'Z')
                c -= 13;
            sb.append(c);
        }
        return sb.toString();
    }

    public static boolean isNumber(char c) {
        return c >= '0' && c <= '9';
    }

}

Questo dovrebbe essere difficile da usare? : /
CalculatorFeline

0

Rainbow (Nota: l'interprete sarà presto disponibile)

So che questa sfida è scaduta.

Rainbow è un mix di ... molte cose.

Rainbow è un linguaggio basato su stack 2D con due stack (Like Brain-Flak) e 8 direzioni ( N NE E SE S SW W NW). Ci sono 8 comandi:

  • 1, +, *, "Fare esattamente quello che fanno in 1+.
  • ! attiva / disattiva lo stack attivo.
  • > ruotare l'IP in senso orario.
  • , inserisci un personaggio e spingilo.
  • . pop e output di un personaggio.

Tuttavia, i caratteri nel codice sorgente non vengono immediatamente eseguiti. Invece, [The Character in the source code]^[Top Of Stack]viene inserito nella cosa delle congetture di Collatz e il numero di passi necessari per raggiungere 1 viene convertito in un carattere dalla tabella ASCII. Questo personaggio viene quindi eseguito.

  • Se per raggiungere 1 sono necessari più di 127 passaggi, il conteggio totale dei passaggi viene diviso per 127, accetta il promemoria e quindi aggiungi il promemoria al quoziente.

All'inizio del programma, il codice sorgente (tranne l'ultimo carattere) viene inserito nello stack.

Quando l'IP raggiunge il limite del codice sorgente, termina.

Apocalisse

n e m sono due registri. Quando >viene eseguita un'istruzione, m viene incrementato. L'apocalisse viene attivata solo se m supera n. Quando succede Apocalypse:

  • Ruotare in senso antiorario anziché in senso orario.
  • m diventa 0.
  • n diventa il primo della pila. E poi, la pila viene spuntata.

m è inizialmente zero e n è inizialmente l'ultimo carattere del codice sorgente.

crittografia

Dopo aver eseguito qualsiasi esecuzione, il codice sorgente viene crittografato. L'ASCII del 1 ° carattere viene incrementato di uno, il 2 ° viene decrementato di uno, il terzo viene incrementato di 2, il 4 ° viene decrementato di due, ecc.


1
abbastanza sicuro che hai bisogno di un interprete per avere una risposta valida ...
Conor O'Brien,

@ ConorO'Brien Poiché questa sfida è già scaduta, è solo per divertimento. Fornirò comunque l'interprete.
Altamente radioattivo il

@HighlyRadioactive ... hai detto quasi un mese fa.
pppery
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.