Erstellen Sie ein "Hacker-Typer" -Programm, das seinen eigenen Quellcode rendert

25

Wenn Sie mit Hackertyper nicht vertraut sind, lesen Sie hackertyper.net . Kurz gesagt, es ist ein Programm, das einen Teil einer Codebasis pro Tastendruck für komödiantische Effekte ausgibt. ABER die hackertyper.net Version ist viel zu einfach zu implementieren. Es werden einfach drei Zeichen gleichzeitig aus einem beliebigen Code ausgegeben. Für diese Herausforderung muss ein Programm seinen eigenen Quellcode ausgeben und einen durch Leerzeichen getrennten Codeabschnitt pro Tastendruck drucken .

Einzelheiten

  • Man kann einen Dateinamen für das Programm nicht hart codieren; es muss seinen Namen dynamisch bestimmen. Wenn das Programm zu einer ausführbaren Datei kompiliert, hängt es möglicherweise die Standarddateierweiterung an den Namen der ausführbaren Datei an (mit Ausnahme der EXE-Datei, wenn Windows verwendet wird) und geht davon aus, dass sich die Quelldatei im Verzeichnis der ausführbaren Datei befindet. Wenn eine ausführbare C-Datei beispielsweise "hacker" heißt, sollte sie ihren Quellcode aus einer Datei mit dem Namen "hacker.c" in demselben Verzeichnis abrufen. Wenn ein kompiliertes Programm eine Erweiterung hat, sollte diese gelöscht werden, bevor der Name seines Quellcodes ermittelt wird ("typer.exe" -> "typer.cs").
  • Programme müssen mindestens 5 Leerzeichen enthalten, wobei zwischen jedem Leerzeichen mindestens ein Zeichen stehen muss. Dies bedeutet, dass die kleinstmögliche Größe für diese Herausforderung 9 Byte beträgt. Die Räume müssen nicht entscheidend für das Funktionieren des Programms sein.
  • Alle Formatierungen (Einrückungen, neue Zeilen usw.) müssen in der Ausgabe beibehalten werden. Diese Formatierung kann entweder mit dem Code gedruckt werden, der darauf folgt, oder es kommt darauf an, dass die Formatierung beibehalten wird.
  • Verwenden Sie keine Kommentare, um die 5 Speicherplatzanforderungen zu erfüllen, es sei denn, es gibt keine andere Möglichkeit, die Leerzeichen in der Sprache Ihrer Wahl zu implementieren.

BEARBEITEN : Neue Zeilen können anstelle von oder zusätzlich zu Leerzeichen als Blocktrennzeichen verwendet werden.

DrJPepper
quelle
1
Ich bin ein wenig verwirrt. Sollte das Programm ein Quine sein oder nicht?
Orby
8
Die Art und Weise, wie Sie es beschrieben haben, klingt so, als ob es akzeptabel wäre, den Code aus der ursprünglichen Quelldatei zu lesen, was kein Quine wäre. Ich denke, es wäre ein besserer Wettbewerb, wenn das Programm ein echtes Quine wäre.
Orby
1
@Orby Ich würde sagen, das Programm ist kein Quine im herkömmlichen Sinne, unabhängig davon, ob das Lesen der Quelle erlaubt ist oder nicht. Quines haben keine Eingabe, aber diese Programme haben eindeutig eine Eingabe.
Calvins Hobbys
@DrJPepper Ihr dritter Aufzählungspunkt lässt es so klingen, als ob eine beliebige Folge von Leerzeichen als Begrenzer zählt, aber Sie sagen ausdrücklich, dass nur Leerzeichen vorhanden sind. Könntest Du das erläutern?
Calvins Hobbys
2
Diese Herausforderung ermutigt zum Lesen des programmeigenen Quellcodes, was bei der Konstruktion von Quines normalerweise verboten ist.
Feersum

Antworten:

13

Bash, 51 58

for w in $(<$0);do read -sn 1;printf -- "$w ";done
Wille
quelle
2
Es ist bash, nicht Shell: Dies funktioniert nicht unter Dash, ( 2: read: Illegal option -s)
F. Hauri
1
Vorausgesetzt, bash, kann ersetzen cat $0und tildes mit$(<$0)
@broslow thx für feedback; markierte Bash, gleiche Länge
wird
1
@ Wird kein Problem sein. Wird das IFS=\ eigentlich benötigt, wenn man den shebang weglässt? Standard-IFS ist so etwas wie IFS=$'\n\t ', und da Sie keinen Zeilenumbruch mehr haben, müssen Sie ihn meines Erachtens nicht auf Leerzeichen beschränken.
1
for w in `<$0`;{ read \-sn1;printf $w\ ;}
Jimmy23013
21

HTML & JavaScript, 123

<head></head><body onload="s=(a=document.all)[i=0].innerHTML" onkeyup="a[2].textContent += s.split(/(?= )/)[i++%6]"></body>

Dies funktioniert ähnlich wie bei Hacker Typer, jedoch mit einem eigenen Quellcode. Lassen Sie mich wissen, wenn ich die Regeln missverstanden habe.

Und hier ist eine gestaltete Version (170 Zeichen):

<head></head>
<body style="background:#000;color:lime" onload="s=(a=document.all)[i=0].innerHTML" onkeyup="a[3].textContent+=s.split(/(?=\s)/)[i++%6]">
<pre></pre></body>

Ich habe eine Demo gemacht . Es wurde geändert, weil JS Bin viel zusätzlichen Code hinzufügt, aber die allgemeine Idee ist dieselbe.

grc
quelle
2
Ich wäre überrascht, wenn dies ohne die Tags <html> und <head> und ohne Abschluss </ body> nicht korrekt dargestellt würde. Sie wären überrascht, wie sehr alle Browser diesbezüglich verzeihen.
Will
2
@ Will Dank. Ich <head>habe den Grund angegeben, dass der Browser ihn hinzufügt, wenn er nicht vorhanden ist, sodass er immer angezeigt wird. Ich habe es jedoch vergessen <html>.
Grc
12

Perl + Term :: ReadKey, 56 Byte

use
Term'ReadKey;ReadMode
4;open
0;ReadKey,print
for
<0>

Vielen Dank an ThisSuitIsBlackNot für die ursprüngliche Inspiration und an primo für die Anregung open 0und <0>.

Beachten Sie, dass die Zeile danach foreigentlich nicht erforderlich ist, außer dass ich irgendwo eine zusätzliche Zeile einfügen muss , um die Anzahl der Leerzeichen auf das angegebene Minimum von fünf zu erhöhen .

Beachten Sie auch, dass dieses Programm wie ThisSuitIsBlackNot das Modul Term :: ReadKey von CPAN benötigt. Unter Debian / Ubuntu Linux kann dieses Modul, falls es noch nicht vorhanden ist, einfach mit dem Befehl installiert werden sudo apt-get install libterm-readkey-perl.

Um einige Zeichen zu speichern, stellt dieses Programm den Eingabemodus beim Beenden nicht auf den normalen Wert zurück, sodass Sie möglicherweise nicht sehen können, was Sie danach eingeben. Ausführen des Shell-Befehls stty saneoder resetsollte das beheben. Dieses Problem konnte auf Kosten von 10 zusätzlichen Bytes behoben werden mit:

use
Term'ReadKey;ReadMode
4;open
0;ReadKey,print
for<0>;ReadMode
0

Bonus: Pure Quine, 81 Bytes

$_=q{use
Term'ReadKey;ReadMode
4;ReadKey,say
for
split$/,
"\$_=q{$_};eval"};eval

Auch hier wird der Zeilenumbruch nach dem Komma nur benötigt, um das Minimum von fünf Leerzeichen zu erreichen.

Im Gegensatz zum obigen 56-Byte-Programm muss diese Version keinen eigenen Quellcode lesen, da sie auf einem Quine basiert - speziell auf diesem Quine:

$_=q{say"\$_=q{$_};eval"};eval

Das Schöne an dieser Quine ist, dass sie problemlos eine beliebige "Nutzlast" innerhalb des q{ }Blocks transportieren kann, ohne sie wiederholen zu müssen. Es ist zwar nicht ganz kurz zu schlagen <0>, aber es kommt ziemlich nahe.

Hinweis: Dieses Programm verwendet die Perl 5.10+ say-Funktion und muss daher mit der Befehlszeilenoption -M5.010(oder -E) aufgerufen werden. Gemäß dem bestehenden Metakonsens zählen solche Schalter, die zur Aktivierung moderner Sprachfunktionen verwendet werden, nicht als zusätzliche Zeichen . Die kürzeste Lösung, die ich finden kann, sayist 83 Bytes:

$_=q{use
Term'ReadKey;ReadMode
4;ReadKey,print
for
split/^/,
"\$_=q{$_};eval"};eval

Beide können auch terminalfreundlicher gestaltet werden, indem Sie (die letzten beiden Zeilen verbinden und) Folgendes einfügen:

;ReadMode
0

vor dem letzten }.

Ilmari Karonen
quelle
Wow. Einfach wow. Sehr cool.
ThisSuitIsBlackNot
+1, aber ich empfehle, die Gewohnheit zu haben, stty saneanstatt zu schreiben reset(was auf einigen Betriebssystemen manchmal etwas mehr als nur das Zurücksetzen einiger Terminalparameter sein könnte ^^)
Olivier Dulac
Sehr schöne Lösung. FWIW open F,$0und <F>könnte durch open 0und ersetzt werden <0>. Außerdem würde ich argumentieren, dass ein Beitrag in Meta nicht wirklich einen Konsens darstellt. Die Option -M5.01nicht „bringt die Sprache zu einem bestimmten Punkt“ , wie der Autor sagt, es ermöglicht Funktionen. Es gibt keine Perl-Version, für die diese Funktionen standardmäßig aktiviert sind.
Primo
3
@primo: Bitte posten Sie Ihre eigene Antwort im Meta-Thread, wenn Sie mit der vorhandenen nicht einverstanden sind. Die Tatsache, dass dies in dreieinhalb Jahren noch niemand getan hat, deutet auf ein angemessenes Maß an Konsens hin, zumindest unter den Stammgästen, die Meta aktiv besuchen, aber der Konsens kann sich immer ändern. (So ​​wie ich es sehe, sollte ruby golfscript.rb foo.gsein gültiger Befehl zum Ausführen eines in GolfScript geschriebenen Programms perl -M5.010 foo.plals gültiger Befehl zum Ausführen eines in "Perl 5.10" geschriebenen Programms gelten. Aber solche Argumente gehören wirklich zum Meta, nicht zum Meta hier.)
Ilmari Karonen
5

Python 3 - 124 Bytes - 7 Leerzeichen


Code:

from curses import*
s=initscr();noecho()
for x in open(__file__).read().split(" "):s.getch();s.addstr(x+" ")
echo();endwin()

Ungolfed:

from curses import*
# init curses
screen=initscr()
noecho()
# split code into spaces
code = open(__file__).read().split(" ")
for x in code:
    # wait for keypress
    screen.getch()
    # print a bit
    screen.addstr(x+" ")
# deactivate curses
echo()
endwin()

Gestaltete Version:

from curses import*
s=initscr();noecho();start_color();init_pair(2,COLOR_GREEN,COLOR_BLACK)
for x in open(__file__).read().split(" "):s.getch();s.addstr(x+" ",color_pair(2))
echo();endwin()
Matsjoyce
quelle
4

Ruby, 85 , 71

require"io/console";f=File.open __FILE__;loop{STDIN.raw &:getc;print f.read(3)||exit}

Schade, dass IO#rawdas nicht Teil der Standardbibliothek ist.

Verbesserung

require"io/console";24.times{|q|STDIN.raw &:getc;$><<IO.read($0,3,q*3)}

Dieser beseitigt den Aufruf von Kernel # exit und verwendet globale Variablen, um den Code zu verkürzen.

ferdinand808
quelle
4

Befunge - 21

~ $ g , 1 +:54*`#@_:0

Ich bin ziemlich zufrieden damit, wie ich gerade über Befunge herausgefunden habe. Wenn es Ihnen nichts ausmacht, in ein Popup-Fenster zu "tippen", können Sie es hier oder hier ausführen, bis ich einen besseren Online-Dolmetscher finde.

Yann
quelle
2

Powershell, 89

(gc $MyInvocation.MyCommand.Path).split(" ")|%{$l+="$_ ";write-host "$l";read-host ;cls}
Tomkandy
quelle
2

Python 3 - 299

a="""from curses import*
s=initscr()
raw()
noecho()
for x in e:
 s.getch()
 s.addstr(x+' ')
nocbreak()
echo()
endwin()
""";b="""e=(a+'a=#%s#;b=#%s#;%s'%(a,b,b.replace('#','""''"',4))+'exec(a)').split(' ')
""";e=('a="""%s""";b="""%s""";%s'%(a,b,b.replace('#','""''"',4))+'exec(a)').split(' ')
exec(a)

Es ist ein Quine. Verkürzt von 507 durch Verwenden execund Verschieben einiger Anweisungen.

faubi
quelle
2

C 211 186 Bytes

Meine Lösung in C mit der Curses-Bibliothek. Es kann länger sein als die andere C-Lösung, aber es ist eine Quine. Obwohl von der Frage nicht verlangt, ist es immer noch ziemlich nett. Es funktioniert auch ganz gut:

#define R(x)#x
#define T(x)R(x)
#define S(p)char*s="#define R(x)#x\n#define T(x)R(x)\n#define S(p)"p"\nS(T(S(p)))";main(){initscr();noecho();while(*s)if(~getch())addch(*s++);}
S(T(S(p)))

Eine lesbarere Version mit einigen Kommentaren und Sachen:

#define R(x)#x /* macros to convert the source code in S into a C-string */
#define T(x)R(x)
#define S(p) char*s="#define R(x)#x\n" \
                    "#define T(x)R(x)\n" \
                    "#define S(p) " p "\n" \
                    "S(T(S(p)))";\
    main(){\
        initscr();\
        noecho(); /* don't echo input */ \
        while(*s)\
            if(~getch()) /*true if character has been typed */ \
                addch(*s++);\
}
S(T(S(p)))

kompilieren mit:

gcc -o h h.c -lncurses
MarcDefiant
quelle
2

C - 136 135 132 Bytes (nur Windows)

*fopen();**v;b[ 1<<20];main(p,q){v=q; strcpy(b,*v);strcat(b,".c") ;for(*v=fopen(b,"r");~fscanf(*v,"%s",b);printf("%s ",b))getch();} 

Hinweis: Am Ende des Programms befindet sich ein Leerzeichen, das wahrscheinlich nicht angezeigt wird.

Ich kann nicht garantieren, dass dieses Programm auf einem anderen Computer als meinem funktioniert, da es furchtbar hackig ist. Früher wäre es viel einfacher gewesen, wenn alle nur 32-Bit-Computer hatten. Dann brauche ich mir keine Sorgen um sizeof(int*)8 zu machen (was es definitiv ist; ich habe es ausgedruckt, um sicherzugehen), während sizeof(int)es 4 ist.

Glücklicherweise wird der Name der ausführbaren Datei in der ersten Zeichenfolge in argv gespeichert. Das Setzen eines Zeigers als Argument für eine Funktion bedeutet jedoch, dass ich den Typ ALLER Argumente für die Funktion explizit angeben muss - was bedeutet, dass ich intzweimal eingeben müsste -, was eine enorme Zeichenverschwendung darstellt. Zum Glück habe ich einen Workaround gefunden. Ich hatte das zweite Argument, main, qnur ein weiteres int. Zu sein. Die Zuweisung qzu einer Variablen vom Typ hat es dann int**irgendwie geschafft, alle erforderlichen Bytes vom Stapel zu holen.

Es gelang mir nicht, solche Tricks zu finden, um den Rückgabetyp von fopenals Zeiger zu interpretieren, ohne die Funktion zu deklarieren.

Edit: Bemerkt sollte ich ~fscanf(*v,"%s",b)statt verwenden, fscanf(*v,"%s",b)>0da die Rückgabe -1 ist, wenn EOF erreicht ist.

Feersum
quelle
Dieser Abschnitt ist für mich fehlerhaft, daher kann ich ihn nicht testen, aber Sie sollten in der Lage sein, einen ungültigen Zeiger ( void **v;) zu deklarieren, anstatt einen Prototyp zu erstellen fopen().
Comintern
@Comintern Diese Änderung hat mir nicht geholfen, das Ergebnis von korrekt zu speichern fopen. Ich verstehe nicht, warum das Ersetzen von int durch void einen Unterschied machen sollte, da alle Zeiger sowieso die gleiche Größe haben.
Feersum
Guter Punkt. Noch kürzer und stabiler nur einen Zeiger zu deklarieren , obwohl - das ist für mich tatsächlich läuft: b[1<<20];main(int *c,char **v){strcpy(b,*v);strcat(b,".c");c=fopen(b,"r");for(;fscanf(c,"%s",b)>0;printf("%s ",b))getch();}(ich hatte als Ersatz getchar()für getch()though).
Comintern
@Comintern Ihr Code stürzt immer noch auf meinem System ab, aber es macht Spaß, ihn zum Laufen zu bringen. Ich denke, es ist wie gesagt - jede Version des Programms läuft auf 1 Computer.
Feersum
Warum verwenden Sie keine K & R-Prototypen? ZB *fopen()statt *fopen(a,b)?
FUZxxl
1

Perl - 87 Bytes

#!/usr/bin/perl -040
use Term::ReadKey;open F,$0;ReadMode 3;print''.<F>while ReadKey 0

Ich habe in den Regeln nichts darüber gesehen, was zu tun ist, wenn die Datei bis zum Ende gelesen wurde, und sie wartet einfach auf Eingaben, nachdem der letzte Block gedruckt wurde.

ThisSuitIsBlackNot
quelle
1

node.js mit LiveScript:

#!/usr/local/bin/lsc
console.log <| require \fs .readFileSync __filename, encoding: \utf8

asynchrone Version:

#!/usr/local/bin/lsc
require \fs .readFile __filename, encoding: \utf8, -> console.log &1
homam
quelle
1

Cobra - 147

class P
    def main
        while 1,for a in File.readLines(CobraCore.exePath[:-4]+'.cobra'),print if('[Console.readKey]'and (Console.cursorLeft=0)<1,a,'')*

CobraCore.exePath ist so nützlich!

Οurous
quelle
1

Javascript ES6, 154

Firefox 154 :

(a= (i=1,b="(a= "+a+")()",s="") => {window.onkeydown=()=>{clear();i=b.indexOf(" ",i+1),d=b.slice(0,i<0?b.length:i);console.log(s+d);if(i<0){i=0,s+=d}}})()

Chrome 175 :

( a= function (i,s){b="( a= "+a+")()";c=console,window.onkeydown=function(){c.clear();s=s||"",i=b.indexOf(" ",i+1),d=b.slice(0,i<0?b.length:i);c.log(s+d);if(i<0){i=0,s+=d}}})()

Beide 274 :

( a= function (i,s){b="( a= "+a+")()";c=console,window.onkeydown=function(){(clear)?clear():c.clear?c.clear():0;s=s||"",i=b.indexOf(" ",i+1),d=b.slice(0,i<0?b.length:i);c.log(s+d);if(i<0){i=0,s+=d}}})()

Ungolfed (Chrom):

( a= function (i,s){        // starting index | undefined, output string
    b="( a= "+a+")()";      // get a string representation of the function
    c=console,
    window.onkeydown=function(){    // on each key down event
        c.clear();                  // clear the output 
        s=s||"";
        i=b.indexOf(" ",i+1);       // get the index of next space
        d=b.slice(0,i<0?b.length:i);// get the string part wanted
        c.log(s+d);                 // print the string
        if(i<0){
            i=0,                    // reset counters
            s+=d                    // adding the string to the main output
        }
    }
})()

Hat zwei Versionen, da Chrome die Pfeilfunktion nicht unterstützt und die Konsole nicht mit derselben Methode gelöscht wird

Der Firefox arbeitet mit Firebug, anscheinend kann die Standard-Entwicklerkonsole nicht aus einem Skript gelöscht werden.

Hacketo
quelle
Haben Sie die Anforderung übersehen, dass der Benutzer zufällige Tasten drücken muss, um die Ausgabe zu drucken?
Optimierer
na klar! werde das umschreiben.
Hacketo
0

Groovy - 379

import java.nio.file.*
Path p = Paths.get(System.getProperty("user.dir"))
DirectoryStream<Path> f = Files.newDirectoryStream(p,"*.groovy")
try{for(e in f){read(e.toAbsolutePath().toString())}}
catch(Exception e){ }
finally{f.close()}

void read(String x){
    def s = new File(x).text
    for(e in s.replace("%n"," %n").split(" ")) 
        print e + " " 
    Thread.sleep(200)
}   

Da es getch()in Java und Java-ähnlichen Sprachen wie Groovy kein oder ein gleichwertiges Programm gibt, kann mein Code Tastendrücke nicht verarbeiten. Das ist alles: D

Kleines Kind
quelle
0

C 248 Zeichen

Wahres Quine

Funktioniert nur unter Unix, unter Windows würde es mit _getch implementiert.

main(){char *p="main(){char *p=\"%s\",s[400];sprintf(s,p,p);system(\"stty raw\");for(p=s;*p!=0;putchar(*p++))getchar();system(\"stty cooked\");}",s[400];sprintf(s,p,p);system("stty raw");for(p=s;*p!=0;putchar(*p++))getchar();system("stty cooked");}
rorlork
quelle
0

HTML und Javascript, 232 Bytes

<body><script>var n=0;var f=function (){document.onkeypress=function(){document.body.innerHTML+=("&lt;body>&lt;script>var n=0;var f="+f.toString()+"f()&lt;/script>&lt;/body>").split(" ")[n]+" ";n++;}};f()</script></body>

Das traditionelle Javascript quine, aber modifiziert.

JSFiddle hier .

BobTheAwesome
quelle
0

SmileBASIC, 79 75 Bytes

LOAD"PRG1:"+PRGNAME$()
PRGEDIT 1
@L
IF BUTTON(2)THEN?PRGGET$();
WAIT
GOTO@L

Es ist sehr einfach, eine bestimmte Zeile eines Programms in SmileBASIC abzurufen, daher setze ich die Leerzeichen vor jedem Zeilenumbruch. Ich fand es so clever, die Leerzeichen vor jeden Zeilenumbruch zu setzen, aber anscheinend dürfen wir Zeilenumbrüche anstelle von Leerzeichen verwenden ...

Erläuterung:

LOAD "PRG1:"+PRGNAME$() 'load the code into slot 1 so we can easily read 1 line at a time
PRGEDIT 1 'Edit slot 1
@LOOP
IF BUTTON(2) THEN 'When a button is pressed...
                   PRINT PRGGET$(); 'get a line of code and print it
WAIT 'delay so we don't detect the same press multiple times in a single frame.
GOTO @LOOP 
12Me21
quelle
-1

Haskell

{-# LANGUAGE CPP #-}
main = readFile __FILE__ >>= putStrLn
homam
quelle
Dies gibt nur die Quelle aus.
Carcigenicate