Create a "hacker typer" program that renders its own source code

25

2

If you are unfamiliar with hacker typer, see hackertyper.net. In short, it is a program that outputs one chunk of a code base per keystroke for comedic effect. BUT, the hackertyper.net version is far too easy to implement. It simply outputs three characters at a time from an arbitrary piece of code. For this challenge, a program must output its own source code, and print one space delimited chunk of code per keystroke.

Details

  • One cannot hard code a file name for the program; it must determine its name dynamically. If the program compiles to an executable, it may append the standard file extension to the name of the executable (excluding the .exe if using Windows) and assume that the source file is within the executable's directory. For example, if a C executable is named "hacker", it should pull its source code from a file named "hacker.c" in its same directory. If a compiled program has an extension, it should be dropped before determining the name of its source code ("typer.exe" -> "typer.cs").
  • Programs must contain at least 5 spaces, with at least one character between each space. This means that the smallest possible size for this challenge is 9 bytes. The spaces do not have to be crucial to the functioning of the program.
  • Any formatting (indentation, new lines, etc.) must be maintained in the output. This formatting may either be printed with the code that proceeds it or follows it, what matters is that the formatting be maintained.
  • Avoid using comments to satisfy the 5 space requirement unless there is no other way to implement the spaces in your language of choice.

EDIT: New lines can be used in place of, or in addition to, spaces as chunk separators.

DrJPepper

Posted 2014-09-05T03:54:41.193

Reputation: 499

I have not enough rep to answer, but this (http://esolangs.org/wiki/Help,_WarDoq%21) language can solve it in 9 bytes, using the Q command.

– Yotam Salmon – 2016-06-13T12:33:25.317

1I'm a little confused. Should the program be a quine, or not? – Orby – 2014-09-05T04:24:41.677

8The way you've described it makes it sound as if its acceptable to read the code from the original source file, which would not be a quine. I think it would be a better contest if the program had to be an actual quine. – Orby – 2014-09-05T04:32:33.460

1

@Orby I'd say the program is not a quine in the traditional sense, regardless of if reading the source is allowed or not. Quines don't have input but these programs clearly do.

– Calvin's Hobbies – 2014-09-05T05:14:02.743

@DrJPepper Your third bullet point makes it sound like any sequence of whitespace counts as a delimiter but you specifically say that only space is. Can you clarify? – Calvin's Hobbies – 2014-09-05T05:15:32.360

@Calvin's Hobbies Yes, new lines between chunks are fine as well, I'll edit it in a minute to clarify. – DrJPepper – 2014-09-05T12:58:20.703

2This challenge encourages reading of the program's own source code, a practice typically verboten in the construction of quines. – feersum – 2014-09-05T13:01:54.300

@Orby Seeing as you are allowed to read the source file, it seems you are correct in this not being a Quine. The main point of the challenge was the staggering of the output. As for the quality of the contest, I feel making such a drastic change at this point would be disrespectful to the extant answers. – DrJPepper – 2014-09-05T13:03:27.017

I'm disapppointed that newlines are now allowed to be delimiters; it simplifies the problem greatly. Most languages have easy way to read files by line, and print a whole line. My bash entry could be 12 shorter, for example, possibly shorter using a while read. – Will – 2014-09-05T17:31:57.060

@Will I didn't mean in place of, I meant in addition too. But since it was an edit, I thought honoring them should be optional. Honoring spaces is not optional. – DrJPepper – 2014-09-05T18:21:18.567

Does that mean that we can ignore newlines, as it'll make my program shorter. – matsjoyce – 2014-09-06T08:06:49.920

@matsjoyce Yes you can ignore newlines. You still need 5 spaces, but since I was not clear new lines can either be factored in or not. It was my original intention for them to count, it just slipped my mind to clarify. – DrJPepper – 2014-09-06T16:28:24.817

Answers

13

bash, 51 58

for w in $(<$0);do read -sn 1;printf -- "$w ";done

Will

Posted 2014-09-05T03:54:41.193

Reputation: 1 143

for w in `<$0`;do read \-sn1;printf $w\ ;done – jimmy23013 – 2015-01-06T11:01:38.440

1for w in `<$0`;{ read \-sn1;printf $w\ ;} – jimmy23013 – 2015-01-06T11:14:54.400

2It's bash, not shell: This won't work under dash, (2: read: Illegal option -s) – F. Hauri – 2014-09-06T20:19:01.733

1Assuming bash, can replace cat $0 and tildes with $(<$0) – None – 2014-09-07T01:04:42.897

@broslow thx for feedback; labelled bash, same length – Will – 2014-09-08T05:13:42.773

1@Will No problem. Is the IFS=\ actually needed if you omit the shebang? Default IFS is something like IFS=$'\n\t ', and since you no longer have a newline, I don't think you need to limit it to just space. – None – 2014-09-08T06:44:23.050

@BroSlow how on earth did I miss that? lol you really ought enter yourself ;) Thousand thanks – Will – 2014-09-08T06:50:30.630

22

HTML & JavaScript, 123

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

This works similarly to hacker typer, but with its own source code. Let me know if I've misunderstood the rules.

And here's a styled version (170 characters):

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

I've made a demo. It's modified because JS Bin adds a lot of extra code, but the general idea is the same.

grc

Posted 2014-09-05T03:54:41.193

Reputation: 18 565

2I'd be surprised if this didn't render correctly without the <html> and <head> tags, and without a closing </body>. You'd be surprised how very forgiving all browsers are in this regard. – Will – 2014-09-05T17:46:59.910

2@Will Thanks. The reason I included <head> was that the browser will add it if it's not there, so it will always get displayed. I forgot about <html> though. – grc – 2014-09-06T01:23:47.037

12

Perl + Term::ReadKey, 56 bytes

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

Thanks to ThisSuitIsBlackNot for the original inspiration, and to primo for suggesting open 0 and <0>.

Note that the newline after for is actually unnecessary, except that I need to include one extra newline somewhere to bring the whitespace count up to the specified minimum of five.

Also note that, like ThisSuitIsBlackNot's submission, this program requires the Term::ReadKey module from CPAN. On Debian / Ubuntu Linux, this module, if not already present, can be easily installed with the command sudo apt-get install libterm-readkey-perl.

Also, to save a few characters, this program does not restore the input mode to normal on exit, so you may find yourself unable to see what you're typing afterwards. Executing the shell command stty sane or reset should fix that. This issue could be fixed, at the cost of 10 extra bytes, with:

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

Again, the newline after the comma is only needed to meet the five whitespace minimum.

Unlike the 56-byte program above, this version doesn't actually need to read its own source code, since it's based on a quine — specifically, on this quine:

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

The nice thing about this quine is that it can easily carry an arbitrary "payload" within the q{ } block, without having to repeat it. While it can't quite beat <0> in shortness, it does get pretty close.

Note: This program uses the Perl 5.10+ say feature, and thus needs to be invoked with the -M5.010 (or -E) command line switch. Per established consensus on meta, such switches used to enable modern language features do not count as extra characters. The shortest solution I can find without say is 83 bytes:

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

Both of these can also be made more terminal-friendly by (joining the last two lines and) inserting:

;ReadMode
0

before the last }.

Ilmari Karonen

Posted 2014-09-05T03:54:41.193

Reputation: 19 513

Wow. Just wow. Very cool. – ThisSuitIsBlackNot – 2014-09-06T05:38:31.663

+1, but I recommend to have the habit to type stty sane instead of reset (which could, on some os, sometimes be doing something more than just resetting some terminal parameters ^^) – Olivier Dulac – 2014-09-06T08:44:27.317

Very nice solution. FWIW, open F,$0 and <F> could be replaced with open 0 and <0>. Also, I would argue that one post in meta doesn't really constitute a consensus. The option -M5.01 doesn't "bring the language to a specific point", as the author suggests, it enables features. There is no version of perl for which these features are enabled by default. – primo – 2014-09-07T07:01:17.113

3@primo: Please do post your own answer to the meta thread, if you disagree with the existing one. The fact that nobody's done so in three and a half years, so far, does suggest a reasonable degree of consensus, at least among the regulars here who actively visit meta, but consensus can always change. (Anyway, the way I see it, if ruby golfscript.rb foo.gs counts as a valid command to run a program written in GolfScript, then perl -M5.010 foo.pl should count as a valid command to run a program written in "Perl 5.10". But such arguments really belong on meta, not here.) – Ilmari Karonen – 2014-09-07T07:28:16.847

5

Python 3 - 124 bytes - 7 spaces


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

Styled 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

Posted 2014-09-05T03:54:41.193

Reputation: 1 319

4

Ruby, 85, 71

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

Too bad that IO#raw is not part of the standard library.

Improvement

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

This one eliminates the call to Kernel#exit and uses global variables to shorten the code.

ferdinand808

Posted 2014-09-05T03:54:41.193

Reputation: 41

4

Befunge - 21

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

I'm fairly pleased with this, as I just found out about Befunge. If you don't mind "typing" into a popup window, you can run it here or here until I find a better online interpreter.

Yann

Posted 2014-09-05T03:54:41.193

Reputation: 151

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)

Its a quine. Shortened from 507 by using exec and moving some statements around.

faubi

Posted 2014-09-05T03:54:41.193

Reputation: 2 599

2

Powershell, 89

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

tomkandy

Posted 2014-09-05T03:54:41.193

Reputation: 167

2

C - 136 135 132 bytes (Windows only)

*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();} 

Note: there is a space at the end of the program, which probably won't show up.

I can't guarantee this program will work on a single computer other than my own as it is awesomely hacky. Things would have been a lot simpler back when everyone only had 32-bit machines. Then I would not need to worry about sizeof(int*) being 8 (which it definitely is; I printed it out to make sure) while sizeof(int) is 4.

Happily, the name of the executable is stored in the first string in argv. However, putting a pointer as an argument to a function means that I have to explicitly specify the type of ALL the arguments to the function--meaning I would have to type int twice--a huge waste of characters. Fortunately I found a workaround. I had the second argument to main, q, be just another int. Then assigning q to a variable of type int** somehow managed to grab all the necessary bytes from the stack.

I was unsuccessful in finding any such tricks to interpret the return type of fopen as a pointer without declaring the function.

Edit: Noticed I should use ~fscanf(*v,"%s",b) instead of fscanf(*v,"%s",b)>0 since the return is -1 when EOF is reached.

feersum

Posted 2014-09-05T03:54:41.193

Reputation: 29 566

Why don't you use K&R prototypes? E.g. *fopen() instead of *fopen(a,b)? – FUZxxl – 2015-02-17T12:13:21.280

@FUZxxl Yep, that works. Evidently I did not know the rules of K&R declarations when I wrote this program, as I also complained about having to declare return types of all the parameters of a function. – feersum – 2015-02-18T07:15:57.560

This segfaults for me so I can't test it, but you should be able to declare a void pointer (void **v;) instead of prototyping fopen(). – Comintern – 2014-09-06T03:34:25.157

@Comintern this change didn't help me to correctly store the result of fopen. I don't see why substituting void for int should make a difference, as all pointers are the same size anyway. – feersum – 2014-09-06T04:11:11.680

Good point. Still shorter and more stable to just declare a pointer though - this actually runs for me: 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();} (I had to substitute getchar() for getch() though). – Comintern – 2014-09-06T04:42:32.630

@Comintern your code still crashes on my system, but nice job getting it to work. I guess it is like I said--each version of the program will run on 1 computer. – feersum – 2014-09-06T05:30:41.350

2

C, 211 186 bytes

My solution in C using the curses library. It may be longer than the other C solution, but it is a quine. Although not required by the question, it's still pretty nice. It also works quite nicely:

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

A more readable version with some comments and stuff:

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

compile with:

gcc -o h h.c -lncurses

MarcDefiant

Posted 2014-09-05T03:54:41.193

Reputation: 996

1

Perl - 87 bytes

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

I didn't see anything in the rules about what to do once the file has been read to the end, so it simply sits waiting for input after printing the last chunk.

ThisSuitIsBlackNot

Posted 2014-09-05T03:54:41.193

Reputation: 1 050

1

node.js with LiveScript:

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

asynchronous version:

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

homam

Posted 2014-09-05T03:54:41.193

Reputation: 109

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 is so useful!

Οurous

Posted 2014-09-05T03:54:41.193

Reputation: 7 916

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}}})()

Both 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 (chrome):

( 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
        }
    }
})()

Has two versions, because Chrome does not handle arrow function and the console is not cleared with the same method

The Firefox one work with firebug, it seem that the default developer console can't be cleared from a script.

Hacketo

Posted 2014-09-05T03:54:41.193

Reputation: 151

Did you miss the requirement that the user has to press random keys to get the output printed ? – Optimizer – 2015-01-09T17:03:26.933

sure !, gonna rewrite this. – Hacketo – 2015-01-09T17:05:29.917

0

SmileBASIC, 79 75 bytes

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

It's very easy to get a specific LINE of a program in SmileBASIC, so I just put the spaces before each line break. I thought I was so clever, putting the spaces before each line break, but apparently we're allowed to use line breaks instead of spaces...

Explanation:

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

Posted 2014-09-05T03:54:41.193

Reputation: 6 110

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

Since there is no getch() or equivalent in Java and Java-esque languages like Groovy... basically my code doesn't handle keypresses. That's all :D

Little Child

Posted 2014-09-05T03:54:41.193

Reputation: 293

0

HTML and 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>

The traditional Javascript quine, but modified.

JSFiddle here.

BobTheAwesome

Posted 2014-09-05T03:54:41.193

Reputation: 509

0

C, 248 characters

True quine

Only works in unix, in windows it would be implemented using _getch.

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

Posted 2014-09-05T03:54:41.193

Reputation: 1 421

-1

Haskell

{-# LANGUAGE CPP #-}
main = readFile __FILE__ >>= putStrLn

homam

Posted 2014-09-05T03:54:41.193

Reputation: 109

This just prints its source. – Carcigenicate – 2017-02-19T21:59:47.317