One Line Aquarium

31

8

My parents-in-law have a fishpond in their yard, filled with koi. It didn't really cross my mind until I noticed the following extract from my code.

',') & '_'

... I had fish-faces looking back at me from my screen...

Which has given me a great idea...

My quest for you, my dear golfers is to create a one-line ASCII aquarium, with the following rules:

  • The size of the aquarium from side of the tank to the other side of the tank should be no smaller than 60 characters and no bigger than 80 characters. Sides of the tank need to be indicated using the pipe/bar (|) character.
  • Fish must be able to "swim" left to right between the sides of the tank. As they will be turning, a front view is required for the transition from one direction to the other.
  • The fish must look like the following:

    • }}< }} ',') Fish swimming right
    • (',' {{ >{{ Fish swimming left
    • }}('_'){{ Fish facing the front
  • When a fish turns to change direction (30% chance), it must start from one direction, face the front, then face the other direction... Make sure there is ample room for the fish to turn, i.e. requires six spaces before the edge of the tank minimum...

  • Unless a fish is blowing a bubble or changing direction, it will continue in the direction it is facing, if it gets within six spaces of the side of the tank, it will change direction.
  • The fish (facing left or right) can on occasion (10% chance) stop to blow bubbles in succession (.oO*), so the fish is required to have a space available next to them in order for the bubble to exist, so a fish can only swim as close to the sides, save one space. The bubble must disappear before the fish can move on...

A series of example lines of the fish's behavior, with - characters indicating the spaces, as the code display feature on here is a little strict... I will expect these dashes to be replaced with spaces when you code this...

Each line here could be considered a frame in time-lapse.

|-}}< }} ',')----------|
|--}}< }} ',')---------|
|---}}< }} ',')--------|
|----}}< }} ',')-------|
|-----}}< }} ',')------|
|-----}}< }} ',').-----|
|-----}}< }} ',')o-----|
|-----}}< }} ',')O-----|
|-----}}< }} ',')*-----|
|---------}}('_'){{----|
|-----------(',' {{ >{{|
|----------(',' {{ >{{-|
|---------(',' {{ >{{--|
|--------.(',' {{ >{{--|
|--------o(',' {{ >{{--|
|--------O(',' {{ >{{--|
|--------*(',' {{ >{{--|
|--------(',' {{ >{{---|
|-------(',' {{ >{{----|
|------(',' {{ >{{-----|

etc. The above example is, as I said, small, but you get the general idea...

Shortest code wins...

I am expecting the output on the same line (if possible), if not, displaying frame after frame in succession is fine... Whether you go one line or multiple lines is up to you. If you are doing multiple lines, they must be separated by a newline.

Also a timer is imposed between frames, 2000ms . This is mandatory.

Let's see what you've got!

WallyWest

Posted 2014-01-16T05:59:22.713

Reputation: 6 949

If the fish gets too close to the edge of the aquarium, is it required to turn, or it may phase out of the screen? Must it be possible that the fish blows bubbles in this situation? Also, must it be possible (or is it forbidden, or is allowed but not required) for the fish to blow bubbles twice in succession? – John Dvorak – 2014-01-16T06:04:52.857

Are we allowed to leave old frames on the screen? If not, then the set of languages able to compete is rather limited. – John Dvorak – 2014-01-16T06:07:43.623

@JanDvorak Just quoting the bullet point I made: "Unless a fish is blowing a bubble or changing direction, it will continue in the direction it is facing, if it gets within six spaces of the side of the tank, it will change direction."

So if it gets within six spaces of the side of the tank it is required to change direction. It is also possible that a fish may blow bubbles twice... – WallyWest – 2014-01-16T06:09:00.083

OK, thanks. Golfscript is out due to the timing requirements, so, which language should I choose? :-) – John Dvorak – 2014-01-16T06:10:30.203

@JanDvorak Old frames can be left on the screen, I have updated my OP accordingly... I would suggest anything with a timing capability ;) JavaScript, PHP, PERL, C, etc. – WallyWest – 2014-01-16T06:12:52.770

Javascript was my first idea, but the timing makes a bit of overhead... – John Dvorak – 2014-01-16T06:14:04.707

Okay, I may have to alter this considerably... – WallyWest – 2014-01-16T06:14:59.810

@JanDvorak Okay, I have changed this to 100 frames of the aquarium's output...

You reckon GolfScript can still be used? – WallyWest – 2014-01-16T06:17:12.103

1Wait, no, don't do changes like this! I've already started coding! Would you mind rolling back? – John Dvorak – 2014-01-16T06:29:37.173

5

Please use the sandbox the next time.

– John Dvorak – 2014-01-16T06:32:16.673

Really? I wanted this to be simpler for everyone... You were right, setting a timer is too much overhead...

I can't leave GolfScript in the corner... – WallyWest – 2014-01-16T06:32:35.933

@JanDvorak I'll remember the sandbox for next time, promise. – WallyWest – 2014-01-16T06:33:44.030

Uh, no, most languages can just do sleep(2) or a similar (and this would be the first time I use a function while golfing, and i like it). And Golfscript deserves to be left out. – John Dvorak – 2014-01-16T06:34:04.667

Dammit... Okay, I'll roll it back... About time we had a real code golf challenge... – WallyWest – 2014-01-16T06:35:24.227

Thanks :-) (be sure to include the information that old frames can be left on the screen but they must be separated by a newline) – John Dvorak – 2014-01-16T06:37:11.420

Done! You're right, I should have sandboxed this first! LOL – WallyWest – 2014-01-16T06:39:02.723

1It's not explicitly stated, but I guess there must be exactly one fish in the aquarium? – John Dvorak – 2014-01-16T07:08:11.787

How is the size of the aquarium counted? The count of the hyphens + fish-chars in the middle? Wall-to-wall distance? 60..80 characters including the walls? – John Dvorak – 2014-01-16T07:12:29.110

@JanDvorak I assume including the walls. – Justin – 2014-01-16T07:20:10.353

@JanDvorak One fish only; it's easier ;)

Also one side of the tank counts as character 1, and the other side of the tank is x where x is a number between 60 and 80 (inclusive) – WallyWest – 2014-01-16T07:21:40.867

Amazing, so my aquarium is within tolerance :-) Now to get the fish moving :-) – John Dvorak – 2014-01-16T07:23:33.737

So yes, including walls! ;) – WallyWest – 2014-01-16T07:24:51.223

1Is it required that the fish may bubble twice in succession, or are we allowed to let it happen? It makes a three-character difference for me. – John Dvorak – 2014-01-16T07:42:56.537

Hmmm, it's okay if it bubbles twice, but like I said, but if it bubbles once, then moves and bubbles again, that's okay too. Just go with your gut @JanDvorak – WallyWest – 2014-01-16T07:57:02.777

"the bubble must disappear before the fish can move on" but in your example there's no frame where the bubble is gone and the fish hasn't moved. Which one is true, or are we allowed to do either? – John Dvorak – 2014-01-16T08:21:54.667

@Eliseod'Annunzio Would you consider having either the timer or n frames of output? I'm working on a SQL version. – SQB – 2014-01-16T08:40:34.473

@JanDvorak I'd say the bubble must have completed before the fish can move. – SQB – 2014-01-16T08:42:15.557

Answers

11

Python 3 (278)

Previously: 334, 332, 325, 302, 300, 299, 291, 286, 284, 281

import random,time
r=random.random
F="}}('_'){{%s","}}< }} ',')%s","%s(',' {{ >{{"
p,d=9,1
c=b=0
while 1:
 if c:p-=c+c*3*(2*d+c==1);d=c-c*d*d;c-=d
 elif b%5:b+=1
 elif.3>r()or{p*d}<{-5,53}:c=-d
 elif.1>r():b=1
 else:p+=d
 print('|%-70s|'%(' '*p+F[d])%' .oO*'[b%5]);time.sleep(2)

Golfing in Python is always difficult due to the indentation requirements of statements, but despite that, this went incredibly well!

Big thanks to Volatility and DSM for helping me golf this so much further.

Clean version

from random import random as r
from time import sleep as s
F = { 1: "}}< }} ',')%s", 0: "}}('_'){{%s", -1: "%s(',' {{ >{{" }

# p: position (from left)
# d: direction (-1: left, +1: right)
# c: changing direction (0: not changing, +1: to right, -1: to left)
# b: bubble (0)
p, d, c, b = 9, 1, 0, 0

while 1:
    if c:
        p -= c*[1,4][2*d+c==1]
        if d:
            d = 0
        else:
            d, c = c, 0
    elif b % 5:
        b += 1
    else:
        # change direction
        if r() < .3 or p * d in (-5,53):
            c = -d
        # start bubbling
        elif r() < .1:
            b = 1
        # move forward
        else:
            p += d

    # print fish and sleep
    print('|{:<70}|'.format(' '*p+(F[d]%' .oO*'[b%5])))
    s(2)

poke

Posted 2014-01-16T05:59:22.713

Reputation: 294

7

Ruby, 291 289

l="(',' {{ >{{";m="    }}('_'){{    ";r="}}< }} ',')";w=?\s;s=w*6;q="|#{r+s*9}|"
f=->*s{(puts q;sleep 2)if q.sub! *s}
loop{rand>0.1||(f[") ",")."]||f[" (",".("];f[?.,?o];f[?o,?O];f[?O,?*];f[?*,w])
q[7]==?(||q[-8]==?)||rand<0.3?f[s+l,m]&&f[m,r+s]||f[r+s,m]&&f[m,s+l]:f[w+l,l+w]||f[r+w,w+r]}

The fish is eleven characters long, making the aquarium 6*9+11+2 = 67 characters wide, which fits neatly in the required tolerance.

The f lambda function does all the heavy lifting: it accepts a substitution as a pair of arguments, then attempts to apply the substitution on the aquarium. If it succeeds, it paints one frame of the animation. It then reports the success value as 2 (the delay taken) or nil (delay not executed).

Fish will not blow bubbles twice in succession. (Fix: ...while rand>0.1 - 4 characers)

Fish may blow bubbles even before a forced turn. (Fix: rearrange the branching structure)

There is a frame where the bubble is completely gone (including the *) but the fish has not moved into the resulting void. This follows the letter, but not the example. (Fix: replace f[?*,w] with q[?*]=w - free)

Does not clear the console. Fix: add `clear` (Unix) or system 'cls' (Windows console) before puts q to fix (Ref.) or use print instead of puts and prepend \r to the aquarium (suggested by @manatwork).

Readable version:

# l - left fish; m - middle fish + space; r - right fish
# w - single space character; s - six spaces; q - the aquarium
l="(',' {{ >{{"; m="    }}('_'){{    "; r="}}< }} ',')";
w=" "; s=w*6; q="|#{r+s*9}|"
f = ->k,v do
  if q.sub! k,v
    puts q
    sleep 2
    return 2
  else
    return nil
  end
end

loop do
  if rand < 0.1
    f[") ",")."] || f[" (",".("]
    f[?.,?o]; f[?o,?O]; f[?O,?*]; f[?*,' ']
  end
  if q[7] == "(" || q[-8] == ")" || rand < 0.3
    (f[s+l,m] && f[m,r+s]) || (f[r+s,m] && f[m,s+l])
  else
    f[w+l,l+w] || f[r+w,w+r]
  end
end

John Dvorak

Posted 2014-01-16T05:59:22.713

Reputation: 9 048

I would add \r to the beginning of q: q="\r|#{r+s*9}|". Then use $><<q instead of puts q. Is 1 character longer, but looks better without using external commands. – manatwork – 2014-01-16T10:29:46.173

\r clears the screen? It doesn't in my case. Or, why don't you like puts? – John Dvorak – 2014-01-16T10:35:00.157

1No, \r is carriage return. By using $><<q for output the cursor will stay in the same line (puts forces a \n after the output) so starting the next output with \r will jump back to the beginning of the current line and start to output from there. Thus overwrites the aquarium line's previous “image”. – manatwork – 2014-01-16T10:40:53.083

@manatwork unfortunately, this completely messes up the jRuby interactive window, which ignores the \r. – John Dvorak – 2014-01-16T10:58:08.887

Oh. Sorry to hear that. And thank you for the information. Sadly this is the first fact I learn about jRuby. :( – manatwork – 2014-01-16T11:07:08.263

3

R, 451 characters

A first attempt:

f=function(j,p){cat("\r|",rep("-",j),p,rep("-",50-j),"|",sep="");Sys.sleep(2)};d=F;j=5;P=c("--}}(\'_\'){{--","-}}< }} \',\')-","-(\',\' {{ >{{-");B=c("-",".","o","O","*","-");g=Vectorize(gsub,v="replacement");b=list(g("-$",B,P[2]),g("^-",B,P[3]));repeat{if(j<5 & d){d=!d;j=j+1;f(j,P[1])};if(j>44 & !d){d=!d;f(j,P[1]);j=j-1};if(runif(1)<.1){for(i in b[[d+1]])f(j,i)}else{f(j,P[d+2])};if(runif(1)<.3){d=!d;f(j,P[1]);f(j,P[d+2])};if(d){j=j-1}else{j=j+1}}

Indented:

f=function(j,p){ #Printing function (depends of buffer and kind of fish)
    cat("\r|",rep("-",j),p,rep("-",50-j),"|",sep="")
    Sys.sleep(2)
}
d=F   #Direction: if FALSE left to right, if TRUE right to left.
j=5   #Buffer from left side of tank
P=c("--}}(\'_\'){{--","-}}< }} \',\')-","-(\',\' {{ >{{-") #The fish
B=c("-",".","o","O","*","-") #The bubble sequence
g=Vectorize(gsub,v="replacement")
b=list(g("-$",B,P[2]),g("^-",B,P[3])) #Fish+bubble
repeat{
    if(j<5 & d){ #What happens if too close from left side
        d=!d
        j=j+1
        f(j,P[1])
        }
    if(j>44 & !d){ #What happens if too close from right side
        d=!d
        f(j,P[1])
        j=j-1}
    if(runif(1)<.1){ #If bubble sequence initiated
        for(i in b[[d+1]])f(j,i)
        }else{f(j,P[d+2])} #Otherwise
    if(runif(1)<.3){ #If fish decide to turn
        d=!d
        f(j,P[1])
        f(j,P[d+2])
    }
    if(d){j=j-1}else{j=j+1} #Increment or decrement j depending on direction
}

It prints the aquarium as stdout on a single line (then 2s break and carriage return before the aquarium at t+1 is printed).

plannapus

Posted 2014-01-16T05:59:22.713

Reputation: 8 610

3

Perl, 281

@f=("s O(',' {{ >{{","s}}('_'){{s","}}< }} ',')O s");$d=1;@o=split//," *Oo. ";{$_="|".$"x$x.$f[$d+1].$"x(44-$x).'|
';s/O/$o[$b]/;s/s/    /g;print;if($b||$t){$b--if$b;if($t){$d+=$t;$t=0if$d}}else{$x+=$d;$t=($x<1)-($x>43);if(!$t){$b=5if.9<rand;if(.7<rand){$t=-$d;$b=0}}}sleep 2;redo}

or more clearly

@f = ( "s O(',' {{ >{{", "s}}('_'){{s", "}}< }} ',')O s" );
$d = 1;
@o = split //, " *Oo. ";
{
    $_ = "|" . $" x $x . $f[ $d + 1 ] . $" x ( 44 - $x ) . '|
';
    s/O/$o[$b]/;
    s/s/    /g;
    print;
    if ( $b || $t ) {
        $b-- if $b;
        if ($t) { $d += $t; $t = 0 if $d }
    }
    else {
        $x += $d;
        $t = ( $x < 1 ) - ( $x > 43 );
        if ( !$t ) {
            $b = 5 if .9 < rand;
            if ( .7 < rand ) { $t = -$d; $b = 0 }
        }
    }
    sleep 2;
    redo
}

Fish turning correctly. Bubbles blowing.

285 - if you like the real aquarium feel and not the scrolling version:

$|=@f=("s O(',' {{ >{{","s}}('_'){{s","}}< }} ',')O s");$d=1;@o=split//," *Oo. ";{$_="\r|".$"x$x.$f[$d+1].$"x(44-$x).'|';s/O/$o[$b]/;s/s/    /g;print;if($b||$t){$b--if$b;if($t){$d+=$t;$t=0if$d}}else{$x+=$d;$t=($x<1)-($x>43);if(!$t){$b=5if.9<rand;if(.7<rand){$t=-$d;$b=0}}}sleep 2;redo}

KevinColyer

Posted 2014-01-16T05:59:22.713

Reputation: 171

Where do you do turning? I can't see you setting $d=0 in the "required turning" case. – John Dvorak – 2014-01-16T21:19:56.643

1sleep2 (without space) is not working for me; also when you are turning, you don’t keep the fish face aligned. And the bubbles seem to never appear. – poke – 2014-01-16T21:25:26.000

"Also a timer is imposed between frames, 2000ms . This is mandatory." - this doesn't happen at all for me. Also, bubbles don't appear, the fish just sits in place. Also, the head should stay in place while the fish is turning, not the body center (at least that's what the example shows) – John Dvorak – 2014-01-16T21:28:23.927

Drat! Golfed too Hard. Need an extra space with sleep! Fish was bubbling nicely too! hang on... – KevinColyer – 2014-01-16T21:38:44.143

Oops - forgot the head in the middle when turning at the edges - need some rework... – KevinColyer – 2014-01-16T21:50:50.100

OK. Think I have it this time. Thanks for pointing out the horrendous errors. Going to bed! – KevinColyer – 2014-01-16T22:16:20.833

The head is still not aligned correctly when turning; and I believe you should pause once before starting the bubble. – poke – 2014-01-16T22:20:17.807

$b=5 sorts out the bubbles. Not too sure how to turn the fish to align the tail properly. Do all the other entries do this? – KevinColyer – 2014-01-16T22:35:47.813

Ruby, Python and C do this, yes, as does the fish in OP’s example. – poke – 2014-01-16T22:47:15.750

Hmmmm probably need to go back to the drawing board on this one! – KevinColyer – 2014-01-16T22:55:33.567

@KevinColyer my entry doesn't do that either: the pivot when turning is the mid-lenth of the fish so that it occupy the same space before and after the turn. Didn't thought it was a specification. – plannapus – 2014-01-17T11:44:37.820

@plannapus - works now though! – KevinColyer – 2014-01-17T11:51:25.633

2

C, 400 394 373 characters

#define p printf
#define a(x) l[5]=r[11]=x;d();
char *l="      (',' {{ >{{",*f="    }}('_'){{    ",*r="}}< }} ',')      ",*c,s=7,i,*T;d(){p("|");for(i=0;i<s;i++)p(" ");p(c);for(i=0;i<70-s;i++)p(" ");puts("|");sleep(2);}b(){a(46)a(111)a(79)a(42)a(32)}t(){T=c;c=f;d();c=T==r?l:r;d();}m(){c==l?s--:s++;d();s>69||s<1?t():0;}main(){c=r;for(d();;)i=rand()%10,i?i>6?t():m():b();} 

With whitespace:

#define p printf
#define a(x) l[5]=r[11]=x;d();
char *l="      (',' {{ >{{",
     *f="    }}('_'){{    ",
     *r="}}< }} ',')      ",
     *c, 
     s=7,
     i,  
    *T; 
d(){
  p("|");
  for(i=0;i<s;i++)
    p(" ");
  p(c);
  for(i=0;i<70-s;i++)
    p(" ");
  puts("|");
  sleep(2);
}
b(){
  a(46)
  a(111)
  a(79)
  a(42)
  a(32)
}
t(){
  T=c;
  c=f;
  d();
  c=T==r?l:r;
  d();
}
m(){
  c==l?s--:s++;
  d();
  s>69||s<1?t():0;
}
main(){
  c=r;
  for(d();;)
    i=rand()%10,
    i?i>6?t():m():b();
}

Josh

Posted 2014-01-16T05:59:22.713

Reputation: 2 783

1Can i==l?(c=r):(c=l) be shortened to c=i==l?r:l? – John Dvorak – 2014-01-16T18:43:31.217

Looks like it can be. Thanks! – Josh – 2014-01-16T18:45:08.447

You can save a few characters by #define p printf( – user12205 – 2014-01-16T22:06:32.960

@Ace gcc doesn't like it when I do that. – Josh – 2014-01-16T22:11:48.287

it works for me. http://oi41.tinypic.com/15fqbvb.jpg

– user12205 – 2014-01-16T22:19:20.347

Looks like it will stop after 8 lines, generating a "Bus error: 10" on my iMac (using gcc -o to compile). – Giulio Muscarello – 2014-01-17T16:22:09.697

It looks like I'm going to have to change the char* to char[] to increase cross-system compatibility. I was hoping to golf this down to 349 characters, but we will see if that materializes. – Josh – 2014-01-17T18:28:17.217