Time-Sensitive Echo

40

3

Background

The echo program is so neat. You can say anything to it, and it repeats your words perfectly, every time! How cool is that! Disappointingly, it repeats the input all at once, regardless of your typing speed, which is not very realistic. We'll have to fix that.

The Task

Your program shall take its input from STDIN or closest equivalent. It shall read lines from the user one by one, possibly displaying some prompt, until they enter an empty line. After that, it shall print the lines to STDOUT or closest equivalent, in the same order as they were given. The last (empty) line is not printed, and the last printed line doesn't need to have a trailing newline.

Additionally, the program shall preserve the time intervals between each line: if it took the user x seconds to enter a line, it shall take x seconds for the program to print it. This applies to the first and last lines too; the empty line is not printed, but the program waits anyway before terminating.

Example

Here's an example session with the program. All actions that don't produce text are described in brackets, and the (optional) prompt is displayed as >.

[begin program]
> fhtagn[enter; 1.48s passed since starting program]
> yum yum[enter; 3.33s passed since previous enter]
> so cool![enter; 2.24s passed since previous enter]
> [enter; 0.23s passed since previous enter]
[wait 1.48s]fhtagn
[wait 3.33s]yum yum
[wait 2.24s]so cool!
[wait 0.23s, then end program]

Without the actions, the session looks like this:

> fhtagn
> yum yum
> so cool!
> 
fhtagn
yum yum
so cool!

Rules and Scoring

The waiting times should be accurate to within 0.01 seconds (in practice, if the average human can't tell the difference, you're fine). The lowest byte count wins, and standard loopholes are disallowed. If your language has a built-in function for precisely this task, you may not use it.

Zgarb

Posted 2015-10-09T14:44:32.810

Reputation: 39 083

10Next step up: Golf a program that plays a basic rhythm game :P – Sp3000 – 2015-10-09T14:49:41.750

Can we disregard the time the program takes to output characters? I mean, If I can measure that my language takes 0.1 sec to output a char, should I take it into account? Full disclosure, I'm planning to use ><> interpreters' tick time to implement the delay ; in this case, can I have a loop elapsing the input time then disregard the time elapsed by my display loop? – Aaron – 2015-10-09T14:56:51.293

1@AaronGOUZIT I'll allow that, as long as you're consistent: either the time intervals between the moments your program begins to print a line are all taken from the user, OR the waiting times between finishing printing a line and beginning to print the next are all taken from the user. – Zgarb – 2015-10-09T15:00:58.400

if it took the user x seconds to enter a line, it shall take x seconds for the program to print it. - does this mean character by character with pauses in between, or print line / pause / print line / pause ? – TessellatingHeckler – 2015-10-09T15:21:39.217

1@TessellatingHeckler The latter; see the example session. – Zgarb – 2015-10-09T15:24:15.057

How is typing so cool! shorter than typing yum yum? :) – user41805 – 2015-10-09T19:06:10.873

1@KritixiLithos I only used my right hand for yum yum, which was rather cumbersome. – Zgarb – 2015-10-09T19:07:57.510

Answers

16

CJam, 45 41 39 36 34 bytes

{eslN1$}g;es](es-fm3/{){_es>}g;o}/

This doesn't really make sense in the online interpreter of course, but it works in the Java interpreter.

It doesn't display a prompt.

Explanation

{        e# Do while... (popping the condition from the stack)
  es     e#   Get the current timestamp.
  l      e#   Wait for a line to be entered and read it.
  N      e#   Push a linefeed.
  1$     e#   Copy the line we read - this terminates if the line is empty, because
         e#   empty strings/arrays are falsy.
}g
;        e# Discard the last linefeed (the one after the empty input).
es       e# Push the current timestamp (corresponding to the last, empty, input).
]        e# Wrap everything in an array. This is now a flat array containing:
         e#   - The initial timestamp.
         e#   - Three elements for each line: the line, a linefeed, the timestamp.
         e#   - Two elements for the last line: the empty string and the timestamp.
(        e# Pull off the initial time.
es-      e# Subtract the current time, which gives (minus) the difference between
         e# when a line was entered and when it should be printed back.
fm       e# This maps "minus that value" onto each element in the array. Now the lines
         e# and linefeeds are strings (arrays) - so minus is set difference, but they
         e# only contain characters, not any integers (like the difference value), so
         e# none of the strings will be affected.
         e# The timestamps on the other hand will be incremented by the time difference
         e# between reading and printing, giving the time at which each line should be
         e# printed back.
3/       e# Split the array into chunks of 3 (where the remaining two elements are
         e# just grouped as a pair).
{        e# For each of those chunks...
  )      e#   Pull off the timestamp.
  {      e#   Do while... (popping the condition from the stack)
    _    e#     Duplicate the target time.
    es>  e#     Check if it's still greater than the current time.
  }g
  ;o     e# Discard the target time and print the rest of the current chunk, which will
         e# automatically be flattened/concatenated into a single string.
}/

Martin Ender

Posted 2015-10-09T14:44:32.810

Reputation: 184 808

9

JavaScript, 119 112 bytes

k=(d=Date.now)(i=j=[]);do{i[++j]=[prompt(),d()-k]}while(i[j][0]);i.map(a=>setTimeout(b=>console.log(a[0]),a[1]))

Hoping to find a couple more bytes to cut out.

Mwr247

Posted 2015-10-09T14:44:32.810

Reputation: 3 494

1You could save a couple of bytes with j=i=[] (++ will still work!) also, your while doesn't need !='' as it's falsy! So disappointed I missed map! +1 – Dom Hastings – 2015-10-09T15:25:40.910

1Good note on the !=''. Was concerned if the input was 0, but it seems to handle that fine. I had noticed the [] increment possibility before, but I had been silly and tried to do j++ with it. Doing ++j works, since []++ is apparently 0 XD Thanks! – Mwr247 – 2015-10-09T15:31:26.593

1Mark this the day that I learnt that there were do...while loops in JS – Conor O'Brien – 2015-10-09T16:26:48.200

6

JavaScript, 120 bytes

No chance of getting near to CJam with this approach, but a straightforward script.

a=[];t=+new Date;while(s=prompt()){a.push({s:s,t:+new Date})}while(v=a.pop()){setTimeout(`console.log('${v.s}')`,v.t-t)}

Dom Hastings

Posted 2015-10-09T14:44:32.810

Reputation: 16 415

1Seems we both went for JS at the same time haha, though you got yours in a little before mine. Still, different approaches. – Mwr247 – 2015-10-09T15:24:00.797

@Mwr247 Indeed, yours is more elegant though! – Dom Hastings – 2015-10-09T15:24:25.103

6

Ruby, 74

t,*a=Time.now
a<<[$_,t-t=Time.now]while$/<gets
a.map{|l,i|sleep -i;puts l}

Tricks: *a on the first line initalizes an empty array. I could use $* instead but it's mildly sketchy since it's populated with some invocations and only saves me a byte. $/ is a newline, and $_ is the last line retrieved by gets.

Edit: Sleeping at the end costs 20 more bytes, probably a way to golf it down

t,*a=Time.now
a<<[$_,t-t=Time.now]while$/<gets
t-=Time.now
a.map{|l,i|sleep -i;puts l}
sleep -t

histocrat

Posted 2015-10-09T14:44:32.810

Reputation: 20 600

I think you need to sleep on the last line, depending on how long it took the user to provide an empty line. – Konrad Borowski – 2015-10-10T15:22:32.137

For sleeping at the end (solution 2), you call Time.now enough times that using def n;Time.now;end, saving a whole 2 bytes – Value Ink – 2017-01-26T23:10:39.363

6

Python 3, 124

Only works on Windows platforms

from time import*
s=[(1,clock())]
while s[-1][0]:s+=[(input(),clock()-s[-1][1])]
[sleep(y)or x and print(x)for x,y in s[1:]]

Keeping the input and times in separate lists cost me 3 more bytes. Probably not the best approach.

An 129 byte Unix friendly version, with credit to Mego:

from time import*
t=time
s=[(1,t())]
while s[-1][0]:s+=[(input(),t(),t()-s[-1][1])]
[sleep(y)or x and print(x)for x,z,y in s[1:]]

FryAmTheEggman

Posted 2015-10-09T14:44:32.810

Reputation: 16 206

Can't you use time() instead of clock() to save 2 bytes? – kirbyfan64sos – 2015-10-09T18:01:03.447

6

Pyth, 68 bytes

M&p+Gb$__import__('time').sleep(H)$J].dZWeaYwaJ.dZ)aJ.dZp&gVPY-VtJJk

Wasted a lot of bytes on the call to sleep, since Pyth has no sleep function.

kirbyfan64sos

Posted 2015-10-09T14:44:32.810

Reputation: 8 730

3Maybe you should suggest that as an addition to Pyth. – mbomb007 – 2015-10-09T18:26:56.230

I believe you have an off-by-one error in the waiting. Try starting the program, waiting around, then typing something and press enter twice rapidly. It will immediately print the first line, then wait a while before terminating. – FryAmTheEggman – 2015-10-09T20:57:01.603

5

SWI-Prolog, 185 bytes

a:-b([],S),reverse(S,T),c(T),!.
b(R,S):-get_time(X),read_string(user_input,"\n","",_,A),get_time(Y),Z is Y-X,(A="",S=[A:Z|R];b([A:Z|R],S)).
c([A:Z|T]):-sleep(Z),T=[];(write(A),nl,c(T)).

There is probably a lot to golf here but this will do for now...

Fatalize

Posted 2015-10-09T14:44:32.810

Reputation: 32 976

4

PowerShell, 261 190 121 95 Bytes

$(do{Measure-Command{$l=read-host};$l}while($l))|%{($_,(sleep -m($_.Ticks/1e4)))[($b=!$b+!$_)]}

Props to TessellatngHeckler and tomkandy for the golfing assistance and inspiration

This is very similar in concept to the 121-byte version below, we're just dynamically creating and building a list of objects, instead of going through a while loop to store them into an explicit array $a. In both cases, that list of objects gets pipelined into the same foreach loop |%{...}. The indexing into the result-array-selector ($b=!$b+!$_) is this time formulated to eliminate the if($_){$_} of the below iterations, which saves a few more bytes.


Previous, 121 Bytes

$l,$a=1,@();while($l){$t=Measure-Command{$l=read-host};$a+=$t,$l}$a|%{($(if($_){$_}),(sleep -m($_.Ticks/1e4)))[($b=!$b)]}

Expanded and explained:

$l,$a=1,@()                        # Set variable $l and create array $a
while($l){                         # So long as we don't have a blank line
  $t=Measure-Command{$l=read-host} # Read the input and measure time to input
  $a+=$t,$l                        # Add those values into the array
}
$a|%{                              # For each item in $a, do
  ($(if($_){$_}),(sleep -m($_.Ticks/1e4)))[($b=!$b)]
  # Magic happens here ... first, we set $b to the NOT of it's uninitialized
  # value, so $b is initially set to truthy
  # This value in [...] selects which of the two elements ( , ) get selected
  # Truthy to start means the second command, sleep, gets chosen first, and
  # then it alternates every next item, so it sleeps, then prints, then
  # sleeps, then prints, etc., until we run out of $a
}

Previous-er, 190 Bytes

function f {param($m)sleep -m $a[$m].totalmilliseconds}$a=1,1;while($a[-1]-ne""){$a+=Measure-Command{$b=read-host};$a+=$b}if(!($a[3])){f 2;exit}$i=2;while($i-lt$a.length){f($i++);$a[($i++)]}

function f {                        # Define a new function
  param($m)                         # with $m as input
  sleep -m $a[$m].totalmilliseconds # sleep for $a[$m] milliseconds
}
$a=1,1                              # Create new array with two elements
while($a[-1]-ne""){                 # While the last element isn't empty
  $a+=Measure-Command{$b=read-host} # Read into $b and measure how long that took,
                                    # and add the time into $a
  $a+=$b                            # Then add the input into $a
}
if(!($a[3])){                       # If the third element is empty, the user entered
                                    # a blank as the only input, so...
  f 2                               # sleep for $a[2] ms (how long it took them to hit enter)...
  exit                              # and exit the script
}                                   # Else ...
$i=2                                # Set a counter variable
while($i-lt$a.length){              # While we haven't reached the end of $a
  f($i++)                           # Sleep
  $a[($i++)]                        # Write the output
}

Previous-er-er, 261 Bytes

$a=$d=@();$d+=,@(date);$x=Read-Host
while($x){$a+=,@($x);$d+=,@(date);$x=Read-Host}
if($x){0..($a.Length-1)|%{sleep -m((($d[$_+1]).ticks-($d[$_]).ticks)/1e4);$a[$_]};sleep -m((($d[-1]).ticks-($d[-2]).ticks)/1e4)}
else{sleep -m(((date).Ticks-($d[0]).Ticks)/1e4)}

Holy verbosity, Batman! Let's break it down:

$a=$d=@()                  # Create two empty arrays
$d+=,@(date)               # Add the current time into $d
$x=Read-Host               # Read the first line
while($x){                 # So long as it's not empty
  $a+=,@($x)               # Add it into our output array
  $d+=,@(date)             # Add the current time into $d
  $x=Read-Host             # Get the next line
}
if($a){                    # So long as $a exists (i.e., the first input wasn't blank)
  0..($a.Length-1)|%{      # For-loop over the length
                           # Sleep for how long it took to do input
    sleep -m((($d[$_+1]).ticks-($d[$_]).ticks)/1e4)
    $a[$_]                 # Print out the input
  }
                           # Sleep the length it took for the final blank
  sleep -m((($d[-1]).ticks-($d[-2]).ticks)/1e4)
}
else{
                           # If we're here, the initial input was blank, so just sleep
  sleep -m(((date).Ticks-($d[0]).Ticks)/1e4)
}

AdmBorkBork

Posted 2015-10-09T14:44:32.810

Reputation: 41 581

144 $a=1,1;while($a[-1]-ne""){$a+=Measure-Command{$b=read-host};$a+=$b};$i=2;while($i-lt$a.length){sleep -m $a[($i++)].totalmilliseconds;$a[($i++)]} – tomkandy – 2015-10-09T17:32:04.017

@tomkandy Thanks! Updated with improvements. – AdmBorkBork – 2015-10-09T20:06:06.657

@TessellatingHeckler Excellent! I was struggling with a way to control the alternating effectively, and indexing into an array like that is the obvious choice now that I see it. Incidentally, I golfed another byte by removing the @ from that array, as it's not needed in this context, so down to 121. – AdmBorkBork – 2015-10-10T06:29:32.923

@TimmyD what I was trying for yesterday was to put ($t,$l) pairs into $a making a nested array. I couldn't make it work, but today I could and it helps a bit because there's no need to toggle, just read every pair and use them. Then I realised - we have a perfectly good pipeline which can queue things, why keep an array at all? $($l=1;while($l){Measure-Command{$l=read-host};$l})|%{($_,(sleep -m($_.Ticks/1e4)))[($b=!$b+!$_)]} - and with a change to the toggle, so that when the string is empty it doesn't toggle, and sleeps instead - 98 – TessellatingHeckler – 2015-10-10T17:53:33.503

(Make it a do{...}while($l) loop and drop $l=1; to get 95) – TessellatingHeckler – 2015-10-10T19:08:40.857

3

Perl 6, 70 characters

repeat {$/=now;.push($!=get,now -$/)}while $!;.map:{sleep $^b;say $^a}

Perl 6 interpreter only defines three symbolic variables (unlike the craziness of Perl 5). To be exact, $/, $!, and $_. This program uses them all, to avoid the cost of declaring variables using my.

get reads a line from STDIN. It doesn't contain a newline, unlike Perl 5.

now builtin returns a current time. When subtracted, it gives an interval that can be passed to a string.

A method with nothing on the left of it (like .push and .map in this code) works on $_.

Using repeat while loop (known as do while in other programming languages), Perl 6 is writing current timestamp to $/, and pushes the received line (which it also stores to $!), and difference between current time and timestamp in $/. Because of parameter order, now is not calculated until a line is received.

The while condition checks if the line is not empty (in Perl 6, "0" is a true value, unlike Perl 5).

After I get all timestamps and lines, I just provide those to map callback which sleeps a bit and says what was said.

Konrad Borowski

Posted 2015-10-09T14:44:32.810

Reputation: 11 185

2

MATLAB, 107 99

tic;a={};i=1;while nnz(i);i=input('','s');a=[a;{i,toc}];tic;end;for b=a';pause(b{2});disp(b{1});end

And ungolfed:

tic; %Start timer
a={};
i=1; %Make us enter the while loop
while nnz(i); %While i has some non-zero elements (this is used to detect a zero length input where we end)
    i=input('','s'); %Get an input string
    a=[a;{i,toc}]; %Append the string and current time as a new cell in a
    tic; %Restart timer
end
for b=a' %For each input
    pause(b{2}); %Wait for the required time
    disp(b{1}); %Then print the string
end

This won't be 100% accurate in timing as it doesn't account for the time taken to display each string but that should be pretty quick so timing-wise it should be pretty close.


After a quick revisit, I've saved a few bytes by removing the double layer deep cell array. Turns out all I needed was a ; to get it to split up correctly when packing.

Tom Carpenter

Posted 2015-10-09T14:44:32.810

Reputation: 3 990

1Maybe you could make a version that's golfed in MATL. – ckjbgames – 2017-01-26T20:07:16.713

2

Groovy, 202 bytes

def b={System.currentTimeMillis()};def h=[];for(;;){def t=b();def s=System.console().readLine();h.add(s+" "+(b()-t));if(s=="")break};for(def s:h){Thread.sleep((s=s.split(" "))[1].toLong());println s[0]}

Radical.

Ungolfed version:

def b = {System.currentTimeMillis()}; // Creates a closure (short function) b that returns the current time since the epoch in milliseconds.
def h = []; // Makes an empty list
for(;;) { // Infinite loop
  def t = b(); // Get the time
  def s = System.console().readLine(); // Read a line
  h.add(s + " " + b()-t); // Add the string plus the amount of time elapsed to the list
  if(s=="") // If the string is blank
    break; // Exit loop
}
for(def s : h) { // Iterate through array
  Thread.sleep((s=s.split(" "))[1].toLong()); // Splits s into an array and puts the value in s, then takes the second element (the time), converts into a long and sleeps for that time.
  println s[0] // Print the first element (text)
}

a spaghetto

Posted 2015-10-09T14:44:32.810

Reputation: 10 647

2

JavaScript (ES6) 102

Putting together the efforts of Mwr247 and Dom Hastings (CW)

/* for TEST */ console.log=x=>O.innerHTML+=x+'\n'

for(k=new Date,i=[];p=prompt();i.push([p,new Date]));i.map(a=>setTimeout(b=>console.log(a[0]),a[1]-k))
<pre id=O></pre>

edc65

Posted 2015-10-09T14:44:32.810

Reputation: 31 086

1

Bash, 22 bytes

nc -l 3|nc localhost 3

Then type in the input and press enter.

Fabricio

Posted 2015-10-09T14:44:32.810

Reputation: 1 605

1

Java, using version 1.04 of this library, 385 bytes

import sj224.lib.util.*;import java.util.*;class E{static long t(){return System.currentTimeMillis();}public static void main(String[]a) throws Exception{List<Pair<?,Long>>l=new ArrayList();Scanner i=new Scanner(System.in);while(true){long t=t();String s=i.nextLine();if(s.isEmpty())break;l.add(new Pair(s,t()-t));}for(Pair<?,Long>p:l){Thread.sleep(p.two);System.out.println(p.one);}}}

SuperJedi224

Posted 2015-10-09T14:44:32.810

Reputation: 11 342

1

Caché ObjectScript, 123 bytes

w() q $P($ZTS,",",2)
r f  s i=i+1,t=$$w() r x,! q:x=""  s g(i,x)=$$w()-t
    f i=1:1 s s=$O(g(i,"")) q:s=""  w s,! h g(i,s)
    q

As usual, this assumes a clean symbol table before running d r.

This problem cannot be solved in ANSI MUMPS, since the ANSI standard only requires second-level resolution for the time intrinsic $H[OROLOG]. Luckily, Intersystems Caché, which is currently the industry-leading platform for MUMPS, provides the implementation-defined $ZT[IME]S[TAMP] intrinsic, which provides microsecond-level resolution.

(Score was formerly 105 bytes, but there was a bug.)

senshin

Posted 2015-10-09T14:44:32.810

Reputation: 641

1

C++ 11, 343 338 bytes

Wanted to see how many bytes a code for that in c++ would need. A lot more than I expected. Maybe I over complicated the solution.

#include<iostream>
#include<vector>
#include<chrono>
int i;using namespace std;int main(){auto n=chrono::system_clock::now;auto t=n();string s{1};vector<string>r;vector<decltype(t-t)>w;while(s.size())getline(cin,s),r.push_back(s),w.push_back(n()-t),t=n();while(i<r.size()){while((n()-t)<w[i]);t=n();cout<<r[i++]<<(i<r.size()-1?"\n":0);}}  

Lets see if I can reduce this somehow.

wendelbsilva

Posted 2015-10-09T14:44:32.810

Reputation: 411

You can remove the spaces in the #includes and the type declaration for main. That's 7 bytes--not much, but a start. You might also be able to use auto rather than string for s. – Alex A. – 2015-10-09T19:35:59.133

Thanks for the feedback. I will keep the return type for the main. If I remember correctly, only for c that we dont have to specify it. I tried initially to use auto s... but looks like it is converted to const char * and not std::string. I wonder if I can create an alias for while. – wendelbsilva – 2015-10-09T20:39:30.090

Removing the return type works for C++ even though it "shouldn't" per the standard. You could try creating an alias for while using a #define maybe. – Alex A. – 2015-10-09T20:50:03.793

1

VBA , 233 228bytes

I'm Sure this can be golfed a lot. they didn't specify how many inputs so i hard coded my array lengths because its shorter then Redim preserve.

Input is via popup, output is debug.print because msgbox produces a MODAL and halts code.

I don't know how to test if this is accurate to the 0.01s. Maybe someone can test but I am giving the wait command the number in a way that it SHOULD use the milliseconds, But VBA isn't known for doing what it should.

The If goto may be able to be substituted by a well golfed Do Loop While.

Sub a()
Dim k(99) As String
Dim h(99) As Date
b:
t=Now()
i=i+1
k(i)=InputBox("")
h(i)=Now()-t
If k(i)<>"" Then GoTo b
For u=1 To i
Application.Wait (Now()+(Format(h(u),"s")&Format(h(u),"ms"))/10^8)
Debug.Print k(u)
Next
End Sub

Will not work in Access VBA, because access doesn't have a wait command because Microsoft hates consistency

JimmyJazzx

Posted 2015-10-09T14:44:32.810

Reputation: 691

1

Bash, 91 90 bytes

while r=`\time -fsleep\ %e head -1`
[[ $r ]]
do printf{,\ %%b\ %q\;} "$r
"
done>t 2>&1
. t

This creates a temporary file t. It will overwrite an existing file with the same name.

The idea itself is pretty short, but dealing with special characters in the input adds around 15 bytes...

Dennis

Posted 2015-10-09T14:44:32.810

Reputation: 196 637

0

SmileBASIC, 122 bytes

DIM A$[0],T[0]@L
C=MAINCNT
LINPUT S$PUSH A$,S$PUSH T,MAINCNT-C
IF""<S$GOTO@L@P
WAIT SHIFT(T)IF""<A$[0]THEN?SHIFT(A$)GOTO@P

I think this could be made slightly shorter.

12Me21

Posted 2015-10-09T14:44:32.810

Reputation: 6 110

0

C UNIX, 272 bytes

#include <stdio.h>
#include <unistd.h>
#define P printf
i;r;c;main(){char*L[99]={0};size_t s;long T[99]={0};while(1){P(">  ");T[c]=time(0);r=getline(&L[c],&s,stdin);T[c]=time(0)-T[c];if(r==-1|!(*L[c]-10))break;c++;}while(i<c){P("> ");usleep(T[i]*1000);P("%s", L[i]);i++;}}

Detailed

#include <stdio.h>
#include <unistd.h>

int main(void)
{
    int i = 0, c = 0, r;
    char * L[99] = {0};
    size_t size;
    long T[99] = {0L};

    while(1)
    {
        printf("> ");
        T[c] = time(0);
        r = getline(&L[c], &size, stdin);
        T[c] = time(0) - T[c];
        if(r == (-1)) break;
        if(*L[c]=='\0' || *L[c]=='\n') break;
        c = c + 1;
    }

    while(i < c)
    {
        printf(" %ld > ",T[i]);
        usleep(T[i]*1000);
        printf("%s", L[i]);
        i = i + 1;
    }

    return 0;
}

Khaled.K

Posted 2015-10-09T14:44:32.810

Reputation: 1 435