16
\$\begingroup\$

Your program has to print out a number of spaces, followed by a dot and a newline. The number of spaces is the x position of your dot defined with 0 < x < 30

Every new line is a turn. Your program runs for 30 turns. Your program starts with a random x position and every turn shifts this position randomly by 1 to the left or right, while staying inside the defined area. Every turn your dot has to change its position by 1.

Your score is the number of characters. You get 10 bonus points, if every printed line consists of exactly 30 characters (and newline). You get 50 bonus points, if, while random, your program tends to stay in the middle of the defined area.

Edit: The 50 bonus points are intended to pull your dot to the middle. For example, this apply's if your dot is at x=20 and has a chance of 66% to go left and 33% to go right. This has to be independent from starting point and should only happen by altering the percentage value of left/right dynamically.

No input of any kind allowed, output has to be on the executing console!

For better understanding, here is a readable example in java, that would give you a score of 723:

public class DotJumper{
    public static void main(String[] args){
        int i = (int)(Math.random()*30);
        int max = 29;
        int step = 1;
        int count = 30;
        while(count>0){
            if(i<=1){
                i+=step;
            }else if(i>=max){
                i-=step;
            }else{
                if(Math.random() > 0.5){
                    i+=step;
                }else{
                    i-=step;
                }
            }
            print(i);
            count--;
        }
    }
    public static void print(int i){
        while(i>0){
            System.out.print(' ');
            i--;
        }
        System.out.println('.');
    }
}
\$\endgroup\$
6
  • \$\begingroup\$ In your example, I think that the int i = (int)(Math.random()*30); should be int i = 1 + (int)(Math.random()*29); instead. As is, it generates a number 0 >= x > 30 instead of 0 > x > 30. \$\endgroup\$ Feb 18, 2014 at 17:26
  • \$\begingroup\$ I think original code is correct. \$\endgroup\$ Feb 18, 2014 at 18:15
  • \$\begingroup\$ it is correct, because i shift first and the print. so even if the random value exceeds the border, if will first correct and then print. \$\endgroup\$ Feb 18, 2014 at 18:17
  • \$\begingroup\$ @Kostronor But this implies that the starting dot position does not follows an uniform distribution, the first position is twice as probably than the other positions. OTOH, being uniformly distributed was not a requirement either. \$\endgroup\$ Feb 18, 2014 at 21:41
  • \$\begingroup\$ Seems like it would be harder to create a program where the dot jumps around more. So why is there a bonus for restricting its movement? \$\endgroup\$ Feb 18, 2014 at 23:12

28 Answers 28

18
\$\begingroup\$

APL, 39 – 10 – 50 = –21

0/{⎕←30↑'.',⍨⍵↑''⋄⍵+¯1*⍵>.5+?28}/31/?29

Tested on Dyalog with ⎕IO←1 and ⎕ML←3 but it should be quite portable.

Explanation

                   ?29  take a random natural from 1 to 29
                31/     repeat it 31 times
              }/        reduce (right-fold) the list using the given function:
          ⍵↑''          . make a string of ⍵ (the function argument) spaces
     '.',⍨              . append a dot to its right
⎕←30↑                   . right-pad it with spaces up to length 30 and output
                ◇       . then
             ?28        . take a random natural from 1 to 28
          .5+           . add 0.5, giving a number ∊ (1.5 2.5 ... 27.5 28.5)
        ⍵>              . check whether the result is <⍵ (see explanation below)
     ¯1*                . raise -1 to the boolean result (0 1 become resp. 1 -1)
   ⍵+                   . add it to ⍵ and return it as the new accumulator value
0/{                     finally ignore the numeric result of the reduction

At every step, this code decides whether to move the dot to the left or to the right depending on the probability that a random number chosen among (1.5 2.5 ... 27.5 28.5) is less than the current dot position.

Therefore, when the current dot position (number of spaces to the left) is 1 the increment is always +1 (all of those numbers 1.5 ... 28.5 are > 1), when it's 29 it's always -1 (all of those numbers are < 29); otherwise it's chosen randomly between +1 and -1, with a probability that's a linear interpolation between those extremes. So the dot is always moving and always more likely to move towards the center than towards the sides. If it's exactly in the middle, it has a 50% chance to move to either side.

The reduction (right-fold) of a replicated value {...}/a/b is just a trick I came up with to repeat a function a-1 times, starting with value b and having the result of each iteration be the accumulator () argument to the next. The second and next input arguments () as well as the final result are ignored. It turns out to be way shorter than a regular recursive call with guard.

Example run

0/{⎕←30↑'.',⍨⍵↑''⋄⍵+¯1*⍵>.5+?28}/31/?29
                   .          
                    .         
                   .          
                  .           
                 .            
                .             
               .              
                .             
               .              
                .             
               .              
              .               
             .                
              .               
             .                
              .               
               .              
              .               
               .              
                .             
                 .            
                  .           
                 .            
                  .           
                 .            
                  .           
                 .            
                .             
               .              
                .             
\$\endgroup\$
4
  • \$\begingroup\$ apl is a great language for golf (hard to read for me, though); UP! \$\endgroup\$
    – blabla999
    Feb 18, 2014 at 0:27
  • \$\begingroup\$ @blabla999 APL has many common symbols and some strange ones, but once you learn their function the language is easy to read and write. Certainly easier that the ASCII noise of 'golfing' languages, but I'd venture to say it's even easier than regular languages. The syntax is very regular, you only have one rule: the expressions are executed from right to left, so that they can be read from left to right: +/2×⍳9 is read "the sum of: two times: the naturals up to 9" but is executed in the opposite way. \$\endgroup\$
    – Tobia
    Feb 18, 2014 at 9:27
  • \$\begingroup\$ Is it me or is there 31 characters per line here? \$\endgroup\$
    – Gusdor
    Feb 18, 2014 at 16:33
  • \$\begingroup\$ I get 30, both by copy-pasting a line from the example run above into an editor, and by checking the code again. ⎕←30↑... will print 30 characters plus a newline, no matter what string is in ... \$\endgroup\$
    – Tobia
    Feb 18, 2014 at 16:39
12
\$\begingroup\$

Mathematica 138 - 10 - 50 = 78

I'm not posting this because I think it's particularly well golfed, but for other reasons. It uses a Markov Process definition with a transition matrix designed to get the ball "centered".

The use of a Markov process in Mathematica allows us to calculate some useful statistics, as you will see below.

First the code (spaces not needed):

r = Range@28/28;
s = DiagonalMatrix;
ListPlot[RandomFunction[DiscreteMarkovProcess[RandomInteger@#, r~ s ~ -1 + s[29/28- r, 1]], 
                                              #], PlotRange -> #] &@{1, 29}

Some outputs:

Mathematica graphics

The transition matrix I used is:

MatrixPlot[s[r, -1] + s[29/28 - r, 1]]

Mathematica graphics

But as I said, the interesting part is that using DiscreteMarkovProcess[] allows us to grab a good picture of what's happening.

Let's see the probability of the ball being at 15 at any time t starting from a particular random state:

d = DiscreteMarkovProcess[RandomInteger@29, s[r, -1] + s[29/28 - r, 1]];
k[t_] := Probability[x[t] == 15, x \[Distributed] d]
ListLinePlot[Table[k[t], {t, 0, 50}], PlotRange -> All]

Mathematica graphics

You can see it fluctuates between 0 and a value near 0.3, that is because depending on the starting state you can only reach 15 on an odd or even number of steps :)

Now we can do the same thing, but telling Mathematica to consider the statistic starting from all possible initial states. What is the probability of being at 15 after a time t?:

d = DiscreteMarkovProcess[Array[1 &, 29]/29, s[r, -1] + s[29/28 - r, 1]];
k[t_] := Probability[x[t] == 15, x \[Distributed] d]
ListLinePlot[Table[k[t], {t, 0, 100}], PlotRange -> All]

Mathematica graphics

You can see it also oscillates ... why? The answer is simple: in the interval [1, 29] there are more odd than even numbers :)

The oscillation is almost gone if we ask for the probability of the ball being at 14 OR 15:

Mathematica graphics

And you could also ask for the limit (in the Cesaro sense) of the state probabilities:

ListLinePlot@First@MarkovProcessProperties[d, "LimitTransitionMatrix"]

Mathematica graphics

Oh, well, perhaps I deserve some downvotes for such an off-topic answer. Feel free.

\$\endgroup\$
3
  • 2
    \$\begingroup\$ This isn't the shortest but it's really cool. \$\endgroup\$ Feb 19, 2014 at 6:02
  • \$\begingroup\$ Upvoted, and now your score is no longer 2,222... \$\endgroup\$
    – cormullion
    Feb 19, 2014 at 15:44
  • \$\begingroup\$ @cormullion Thanks! You can undo that by downvoting hard:) \$\endgroup\$ Feb 19, 2014 at 15:46
7
\$\begingroup\$

Bash, score 21 (81 bytes - 50 bonus - 10 bonus)

o=$[RANDOM%28];for i in {D..a};{ printf " %$[o+=1-2*(RANDOM%29<o)]s.%$[28-o]s
";}

In this answer, the dot is "pulled" back to the middle. This can be tested by hardcoding the starting point at 0 or 30.

\$\endgroup\$
7
  • \$\begingroup\$ Good solution! But please do not hardcode the starting point ;) \$\endgroup\$ Feb 18, 2014 at 0:12
  • 1
    \$\begingroup\$ @Kostronor - oops - I missed that, and have now fixed it. \$\endgroup\$ Feb 18, 2014 at 0:23
  • 2
    \$\begingroup\$ save a char by replacing {1..30} by {P..m} \$\endgroup\$ Feb 18, 2014 at 5:37
  • \$\begingroup\$ What if o is 1 and RANDOM%30 returns 0? And on the next iteration, too? \$\endgroup\$ Feb 18, 2014 at 13:31
  • \$\begingroup\$ @VadimR - I think the boundary conditions are fixed now. \$\endgroup\$ Feb 18, 2014 at 19:12
5
\$\begingroup\$

Ruby 69 66 64-60 = 4

i=rand(30);30.times{a=' '*30;a[i]=?.;puts a;i+=rand>i/29.0?1:-1}

Sample:

            .             
           .              
            .             
           .              
          .               
           .              
            .             
             .            
              .           
             .            
              .           
               .          
              .           
               .          
              .           
             .            
            .             
             .            
              .           
             .            
              .           
             .            
            .             
             .            
            .             
             .            
            .             
           .              
          .               
           .              
\$\endgroup\$
1
  • \$\begingroup\$ You can save a byte with i=rand 30; instead of i=rand(30);. \$\endgroup\$
    – Jordan
    Oct 5, 2016 at 4:47
5
\$\begingroup\$

Smalltalk, 161 159 145-60 = 85

all columns are 30chars long (operating in mutable string b);

random movement chance is adjusted by biasing rnd value with p (rnd(0..29)-p), taking the sign (-1/0/1) and then adjusting to (-1/+1) via (-1|1), which is taken as move delta (effectively computes: x sign<=0 ifTrue:-1 ifFalse:1). As ST uses 1-based indexing, I have to adjust all string refs by +1 (plz appreciate the -1|1 bit fiddling hack ;-) ).

p:=((r:=Random)next*29)rounded.(b:=String new:30)at:p+1put:$..0to:29do:[:i|b at:p+1put:$ .p:=(p+(((r next*29)-p)sign-1|1))min:29max:0.b at:p+1put:$..b printCR]

stealing an idea from Ruby version (thanx&Up @fipgr), I can get rid of the min/max check:

p:=((r:=Random)next*29)rounded.(b:=String new:30)at:p+1put:$..1to:30 do:[:i|b at:p+1put:$ .p:=p+((r next-(p/29))sign-1|1).b at:p+1put:$.;printCR]

output: (I have manually added the col-numbers and vertical bars afterwards; the code above does not generate them)

 012345678901234567890123456789
|                     .        |
|                    .         |
|                   .          |
|                  .           |
|                 .            |
|                  .           |
|                 .            |
|                  .           |
|                 .            |
|                .             |
|                 .            |
|                .             |
|               .              |
|                .             |
|                 .            |
|                  .           |
|                 .            |
|                  .           |
|                 .            |
|                  .           |
|                 .            |
|                .             |
|               .              |
|                .             |
|               .              |
|                .             |
|               .              |
|              .               |
|               .              |
|              .               |
 012345678901234567890123456789
\$\endgroup\$
4
\$\begingroup\$

C, 86

Assuming that seeding the rand() function is not required.

k=30;main(i){i=rand()%k;while(k--){printf("%*c\n",i+=i==30?-1:i==1||rand()%2?1:-1,46);}}

Explanation:

In C, in "%*c" the * means that the length of the output will have a minimum length, and this minimum length is determined by the argument of the function call (in this case, it is i+=i==30?-1:i==1||rand()%2?1:-1. The c means the next argument (46) is a character (the dot).

As for the boundary check, I apologise that I forgot about that. I have now added this to the answer, at the cost of 15 chars. The ternary operator works as follows: boolean_condition?value_if_true:value_if_false. Note that in C true is 1 and false is 0.

\$\endgroup\$
8
  • \$\begingroup\$ Can you expand on what is going on in your code? I'm having trouble understanding how printf("%*c\n",i+=rand()%2?1:-1,46) prints the spaces, as well as how it keeps the dot from possibly moving past 29. Thanks in advance. (Sorry, I'm not a C programmer.) \$\endgroup\$ Feb 17, 2014 at 23:33
  • \$\begingroup\$ @fireeyedboy done, hope you understand :) \$\endgroup\$
    – user12205
    Feb 17, 2014 at 23:53
  • \$\begingroup\$ Aaaah, I kind of had the feeling you were cheating a little bit there. ;-) But the rest is clear indeed now. Thank you! And nice solution! Does C also have the strange behavior with rand()%2 in that it is very predictable (odd/even turns)? I tried your rand()%2 in my PHP solution, and it exhibited this very predictable behavior (as opposed to rand(0,1). Since PHP makes a lot of use of C libraries (if I'm correct) I was wondering whether your C program has the same 'flaw'. \$\endgroup\$ Feb 18, 2014 at 0:02
  • \$\begingroup\$ @fireeyedboy I did not seed the rand() function. In C if rand() is not seeded explicitly, it always uses the same seed every time. That's why it is predictable. If I had to seed it I can do srand(time()); which costs 14 chars \$\endgroup\$
    – user12205
    Feb 18, 2014 at 0:05
  • \$\begingroup\$ But is it so predictable that it switches from odd to even on each subsequent call as well? PHP claims rand() doesn't need to be seeded with srand() anymore, but still shows this odd behavior. \$\endgroup\$ Feb 18, 2014 at 0:27
4
\$\begingroup\$

Java: 204 183 182 176 175 characters - 10 - 50 = 115

class K{public static void main(String[]y){int k,j=0,i=(int)(29*Math.random());for(;j++<30;i+=Math.random()*28<i?-1:1)for(k=0;k<31;k++)System.out.print(k>29?10:k==i?'.':32);}}

First, the dot position must be 0 < x < 30, i.e. [1-29]. This generates a number between 0 and 28 uniformly distributed, and for the purposes of this program [0-28] has the same effect as [1-29]:

i=(int)(29*Math.random());

I personally preferred if it would be normally distributed around 14, but my answer would be longer:

i=0;for(;j<29;j++)i+=(int)(2*Math.random());

Second, this code ensures that it tends to be in the middle:

i+=Math.random()*28<i?-1:1

The probability to get +1 is larger as smaller is the value of i, and we have the opposite for -1. If i is 0, the probability of getting +1 is 100% and the probability of getting -1 is 0%. If i is 28, the opposite to that will happen.

Third, by replacing the 32 at the end by '_' to see the output easier, we see that each line has 30 characters plus a new line:

__________.___________________
_________.____________________
________._____________________
_________.____________________
__________.___________________
___________.__________________
__________.___________________
_________.____________________
________._____________________
_________.____________________
________._____________________
_________.____________________
__________.___________________
_________.____________________
__________.___________________
___________.__________________
__________.___________________
_________.____________________
__________.___________________
___________.__________________
____________._________________
_____________.________________
____________._________________
_____________.________________
______________._______________
_____________.________________
______________._______________
_______________.______________
______________._______________
_____________.________________

Thanks to @VadimR (now, user2846289) for pointing out a misunderstanding in a prior version.

Thanks to @KevinCruijssen for shaving out 6 characters, even after more than two and a half years after this answer was initially posted.

\$\endgroup\$
8
  • \$\begingroup\$ But i getting to 0 is illegal, isn't it? \$\endgroup\$ Feb 18, 2014 at 16:13
  • \$\begingroup\$ @VadimR, for me i is in the range [0-29]. This is equivalent to [1-30] or [288-317], the output would be the same. What matters is that there are 30 integer numbers in the interval [0-29]. \$\endgroup\$ Feb 18, 2014 at 16:46
  • \$\begingroup\$ "The number of spaces is the x position of your dot defined with 0 < x < 30" I.e. the number of spaces (or 0-based dot position) is 1..29. i can't be 0. I understand it's all about having fun but still it is sad. \$\endgroup\$ Feb 18, 2014 at 17:08
  • \$\begingroup\$ @VadimR, Oh, thanks. Fixed. This means that the dot will never be in the rightmost posistion, but anyway, that is what was specified. \$\endgroup\$ Feb 18, 2014 at 17:22
  • \$\begingroup\$ I'm sorry, but it didn't fix anything. Imagine i gets 1 initially, and on first iteration Math.random() is 0, then i gets 0. Please don't get me wrong, it's not about your answer. Rather about my inability to read most languages other than C-like. Then with no reaction (except for upvotes) about errors, how can I know they are right or not? \$\endgroup\$ Feb 18, 2014 at 17:40
3
\$\begingroup\$

Mathematica 157-10-50 = 97

A random number from 1-30 is used for starting. All the remaining column numbers from the dot are chosen via RandomChoice[If[c > 15, {2, 1}, {1, 2}] -> {-1, 1}] + c, which translates to: "If the prior column number was greater than 15, select one number from the set {-1,1}, with -1 weighted 2:1 with respect to 1; otherwise, flip the weights and choose from the same set.

ReplacePart replaces the element in a list of 30 blank spaces that corresponds to the column of interest.

f@c_ := Switch[d = RandomChoice[If[c > 15, {2, 1}, {1, 2}] -> {-1, 1}] + c, 1, 2, 30, 29, d, d]
Row@ReplacePart[Array["_" &, 29], # -> "."] & /@ NestList[f, RandomInteger@29+1, 30] // TableForm

dot

\$\endgroup\$
1
  • \$\begingroup\$ Nice use of RandomChoice[] \$\endgroup\$ Feb 18, 2014 at 17:57
3
\$\begingroup\$

><>, 358 - 10 = 348

This won't win at codegolf, but it works. (On windows 7 with this interpreter, which implements the "p" instruction differently than the esolang page defines it)

1v        >a"                              "v
v<      0<} vooooooooooooooooooooooooooooooo<
&  _>   v : >$" "@p1+:2f*(?v;
  |x1>  v^}!               <
  |xx2> v p
  |xxx3>v  
  |xxxx4v $
>!|xxx< v }
  |xxxx6v }
  |xxx7>v @
  |xx8> v :
  |x9v  < @>  5)?v$:67*)?vv
   _>>&?v!@^     >$:b(?v v
  v }"."< :        v+ 1<  <
  >a+b+00}^0}}${"."< <- 1<

The name of this language can't be googled, so here's its esolang article for the curious.

\$\endgroup\$
3
  • \$\begingroup\$ Can you code the requirement for the -50 in less than 50 characters? \$\endgroup\$ Feb 18, 2014 at 15:43
  • 1
    \$\begingroup\$ @Victor Probably, but ><> is a giant pain to code in (a fun giant pain), so I need to take a break from it. \$\endgroup\$
    – SirCxyrtyx
    Feb 18, 2014 at 21:19
  • \$\begingroup\$ @SirCxyrtyx this made me chuckle. Well golfed sir. \$\endgroup\$
    – Gusdor
    Feb 19, 2014 at 21:17
3
\$\begingroup\$

PHP, 118 113 112 111 (, -10 bonus points = 101)

(second try, with horribly predictable rand() behavior, and a little more efficiency)

for($n=30,$i=rand(1,29),$t=$s=pack("A$n",'');$n--;$i+=$i<2|rand()%2&$i<28?1:-1,$t=$s){$t[$i-1]='.';echo"$t\n";}

Possible result:

______._______________________
_____.________________________
______._______________________
_____.________________________
______._______________________
_____.________________________
______._______________________
_____.________________________
______._______________________
_____.________________________
______._______________________
_____.________________________
______._______________________
_____.________________________
______._______________________
_____.________________________
______._______________________
_____.________________________
______._______________________
_____.________________________
______._______________________
_____.________________________
______._______________________
_____.________________________
______._______________________
_____.________________________
______._______________________
_____.________________________
______._______________________
_____.________________________

PHP, 130 (, -10 bonus points = 120)

(first try)

This could probably still be much more efficient:

for($n=30,$i=rand(1,29),$t=$s=str_repeat(' ',$n)."\n";$n--;$i=$i<2?2:($i>28?28:(rand(0,1)?$i+1:$i-1)),$t=$s){$t[$i-1]='.';echo$t;}

If I replace the space with an underscore (for display purposes), this is a possible result:

________._____________________
_______.______________________
______._______________________
_______.______________________
________._____________________
_______.______________________
______._______________________
_______.______________________
______._______________________
_______.______________________
________._____________________
_______.______________________
________._____________________
_________.____________________
__________.___________________
___________.__________________
__________.___________________
___________.__________________
__________.___________________
_________.____________________
________._____________________
_________.____________________
________._____________________
_______.______________________
______._______________________
_____.________________________
____._________________________
_____.________________________
____._________________________
___.__________________________

Oddly enough, if I replace rand(0,1) with rand()%2 (PHP 5.4, on Windows XP), the random result always switches from odd to even, and vice versa, on each next iteration, making rand() worryingly predictable, in that sense, all of a sudden. This 'bug' appears to be one that has been known since 2004. Not entirely sure whether it's exactly the same 'bug' though.

\$\endgroup\$
3
\$\begingroup\$

J 42 characters - 50 -10 = -18

'_.'{~(|.~((I.%<:@#)*@-?@0:))^:(<@#)1=?~30

Explanation, starting from the right (some knowledge about trains comes in handy):

init=: 0=?~30          NB. where is the 0 in the random permutation of [0,29]
rep =: ^:(<@#)         NB. repeat as many times as the array is long, showing each step

rnd =: ?@0:            NB. discards input, generates a random number between 0 and 1

signdiff =: *@-        NB. sign of the difference (because this works nicely with
                       NB. the shift later on).

left_prob =: (I.%<:@#) NB. probability of shifting left. The position of the one (I.) divided by the length -1.

shift =: |.~           NB. x shift y , shifts x by y positions to the left.

output =: {&'_.'       NB. for selecting the dots and bars.

NB. Piecing things together:
output (shift (left_prob signdiff rnd))rep init

Center tendency, -50, example over 1000 runs:

NB. Amounts of ones in each column (sum)
   ]a=:+/ (|.~((I.%<:@#)*@-?@0:))^:(<1000)0=?30
0 0 0 0 0 0 2 6 10 12 25 60 95 121 145 161 148 99 49 27 19 13 6 1 1 0 0 0 0 0
   +/a NB. check the number of ones in total
1000
   |. |:(<.a%10) #"0 1] '*' NB. plot of those values
           *              
           *              
          ***             
          ***             
         ****             
         ****             
         ****             
        ******            
        ******            
        ******            
       *******            
       *******            
       ********           
       ********           
      **********          
    **************        

Example run, outputting exactly 30 bytes each line

_______________.______________
________________._____________
_______________.______________
________________._____________
_______________.______________
________________._____________
_______________.______________
________________._____________
_______________.______________
________________._____________
_______________.______________
______________._______________
_______________.______________
________________._____________
_________________.____________
________________._____________
_______________.______________
______________._______________
_____________.________________
____________._________________
___________.__________________
__________.___________________
___________.__________________
__________.___________________
___________.__________________
__________.___________________
___________.__________________
____________._________________
_____________.________________
______________._______________
\$\endgroup\$
2
  • \$\begingroup\$ A very good solution! \$\endgroup\$ Feb 19, 2014 at 8:56
  • \$\begingroup\$ A very good challenge too! \$\endgroup\$
    – jpjacobs
    Feb 19, 2014 at 9:19
3
\$\begingroup\$

Python 2.7: 126 109 -10-50 = 49

Got rid of the hard-coded starting point - now starts at random point. Because of this, I needed randint, so I decided to use that instead of choice for the offset. Used the (-1)**bool trick for that.

from random import randint as r;p=r(0,29)
for i in range(30):
 print' '*p+'.'+' '*(29-p);p+=(-1)**(r(0,29)<p)

Some great answers here. First attempt in Python, thinking about improvements. Not helped by the need for an import.

-10 - yes 30 chars + \n on each line

-50 - the further away from the centre, the more likely a move the other way (accomplished by building a list with a different number of +/i offsets)

Previous attempt:

from random import choice;p,l=15,[]
for i in range(30):
 q=29-p;l+=[' '*p+'.'+' '*q];p+=choice([1]*q+[-1]*p)
print'\n'.join(l)
\$\endgroup\$
1
  • \$\begingroup\$ Your for loop can all be on one line, but even better is for i in[0]*30: and better still is eval"..."*30. \$\endgroup\$
    – mbomb007
    Oct 5, 2016 at 20:20
2
\$\begingroup\$

Java - 198 183 characters

This is just a plain, simple, direct and uncreative golf of the example that you had given in the question.

class A{public static void main(String[]y){int c,j,i=(int)(Math.random()*30);for(c=30;c>0;c--)for(j=i+=i<2?1:i>28?-1:Math.random()>0.5?1:-1;j>=0;j--)System.out.print(j>0?" ":".\n");}}
\$\endgroup\$
2
\$\begingroup\$

Batch - (288 Bytes - 10) 278

@echo off&setlocal enabledelayedexpansion&set/ap=%random%*30/32768+1&for /l %%b in (1,1,30)do (set/ar=!random!%%2&if !r!==1 (if !p! GTR 1 (set/ap-=1)else set/ap+=1)else if !p! LSS 30 (set/ap+=1)else set/ap-=1
for /l %%c in (1,1,30)do if %%c==!p! (set/p"=."<nul)else set/p"=_"<nul
echo.)

Un-golfed:

@echo off
setlocal enabledelayedexpansion
set /a p=%random%*30/32768+1
for /l %%b in (1,1,30) do (
    set /a r=!random!%%2
    if !r!==1 (
        if !p! GTR 1 (set /a p-=1) else set /a p+=1
    ) else if !p! LSS 30 (set /a p+=1) else set /a p-=1
    for /l %%c in (1,1,30) do if %%c==!p! (set /p "=."<nul) else set /p "=_"<nul
    echo.
)

To output spaces instead of underscores - 372 Bytes -

@echo off&setlocal enabledelayedexpansion&for /f %%A in ('"prompt $H &echo on&for %%B in (1)do rem"')do set B=%%A
set/ap=%random%*30/32768+1&for /l %%b in (1,1,30)do (set/ar=!random!*2/32768+1&if !r!==1 (if !p! GTR 1 (set/ap-=1)else set/ap+=1)else if !p! LSS 30 (set/ap+=1)else set/ap-=1
for /l %%c in (1,1,30)do if %%c==!p! (set/p"=."<nul)else set/p"=.%B% "<nul
echo.)

Looking for some help with the following logic, surely this isn't the most space efficient method (!r! will expand to either 1 or 2) -

if !r!==1 (
    if !p! GTR 1 (set /a p-=1) else set /a p+=1
) else if !p! LSS 30 (set /a p+=1) else set /a p-=1

It golfs down to: if !r!==1 (if !p! GTR 1 (set/ap-=1)else set/ap+=1)else if !r! LSS 30 (set/ap+=1)else set/ap-=1

\$\endgroup\$
2
\$\begingroup\$

J, 42 characters, no bonuses

' .'{~(i.30)=/~29<.0>.+/\(-0&=)?(,2#~<:)30

Example run:

          ' .'{~(i.30)=/~29<.0>.+/\(-0&=)?(,2#~<:)30
                       .
                      .
                     .
                      .
                     .
                      .
                     .
                      .
                       .
                      .
                     .
                    .
                     .
                    .
                     .
                    .
                     .
                      .
                       .
                        .
                       .
                        .
                       .
                      .
                     .
                      .
                       .
                      .
                     .
                    .
\$\endgroup\$
2
\$\begingroup\$

Python 2.7 (126 - 10 (fix length) - 50 (Center Tendency) = 66)

The following program have a center tendency over a larger sample

s=id(9)%30
for e in ([[0,2][s>15]]*abs(s-15)+map(lambda e:ord(e)%2*2,os.urandom(30)))[:30]:
    s+=1-e;print" "*s+"."+" "*(28-s)

Demo

         .           
        .            
       .             
      .              
     .               
      .              
       .             
        .            
         .           
          .          
           .         
          .          
           .         
          .          
         .           
        .            
       .             
        .            
       .             
      .              
     .               
      .              
       .             
      .              
     .               
    .                
   .                 
    .                
   .                 
    .              
\$\endgroup\$
1
  • \$\begingroup\$ Like the s=id(9)%30 for the seeding. os needs importing, though? And does this cover the full 1-30 range? Oh wait...*rereads the inequality at top of page* \$\endgroup\$
    – psion5mx
    Feb 18, 2014 at 18:24
2
\$\begingroup\$

Javascript 125 73 72 60 (120 - 50 - 10)

i=0;r=Math.random;s=r()*30|0;do{a=Array(30);a[s=s>28?28:s?r()<s/30?s-1:s+1:1]='.';console.log(a.join(' '))}while(++i<30)

EDIT: Fix for 50 point bonus and 10 point bonus.

EDIT 2: Even shorter!

\$\endgroup\$
4
  • \$\begingroup\$ can you explain, where you get the 50 points bonus? I am not getting it at the moment... \$\endgroup\$ Feb 18, 2014 at 0:15
  • \$\begingroup\$ @Kostronor I just saw the edit for the requirement. Looks like I don't get the 50 points yet. \$\endgroup\$
    – aebabis
    Feb 18, 2014 at 2:51
  • \$\begingroup\$ @acbabis, well done ! You can save some bytes (116) : r=Math.random;s=r()*30|0;for(i=0;i++<30;a=Array(30)){a[s=s>28?28:s?r()<s/30?s-1:s+1:1]='.';console.log(a.join(' '))} \$\endgroup\$
    – Michael M.
    Feb 18, 2014 at 9:27
  • \$\begingroup\$ @Michael Thanks for the tips. Couldn't get the array init inside the for working, though; had to use a do while. \$\endgroup\$
    – aebabis
    Feb 18, 2014 at 16:58
2
\$\begingroup\$

D - 167, 162, 144 ( 154 - 10 )

Golfed:

import std.stdio,std.random;void main(){int i=uniform(1,30),j,k;for(;k<30;++k){char[30]c;c[i]='.';c.writeln;j=uniform(0,2);i+=i==29?-1:i==0?1:j==1?1:-1;}}

Un-golfed:

import std.stdio, std.random;

void main()
{
    int i = uniform( 1, 30 ), j, k;

    for(; k < 30; ++k )
    {
        char[30] c;
        c[i] = '.';
        c.writeln;
        j = uniform( 0, 2 );
        i += i == 29 ? -1 : i == 0 ? 1 : j == 1 ? 1 : -1;
    }
}

EDIT 1 - I'm not quite sure if my code qualifies for the -50 bonus or not. i doesn't always start in the middle, but during the for loop, the dot never moves more than like 3 places either direction, so when i does start near the middle, the whole thing tends to stay there as well.

EDIT 2 - Code now qualifies for the -10 bonus, as it prints an array of 29 characters followed by LF for a total of exactly 30 chars per line.

\$\endgroup\$
2
  • \$\begingroup\$ as it prints an array of 29 characters followed by LF - It should be 30 characters followed by a LF. \$\endgroup\$ Feb 18, 2014 at 15:30
  • \$\begingroup\$ @Victor ahh, thank you for the correction, I misinterpreted the main post. \$\endgroup\$
    – Tony Ellis
    Feb 18, 2014 at 17:16
2
\$\begingroup\$

PowerShell, 77 - 10 - 50 = 17

$x=random 30
1..30|%{$x+=random(@(-1)*$x+@(1)*(29-$x))
'_'*$x+'.'+'_'*(29-$x)}

Output

_.____________________________
__.___________________________
___.__________________________
____._________________________
___.__________________________
____._________________________
_____.________________________
______._______________________
_______.______________________
______._______________________
_____.________________________
______._______________________
_______.______________________
________._____________________
_________.____________________
__________.___________________
___________.__________________
__________.___________________
___________.__________________
____________._________________
___________.__________________
__________.___________________
_________.____________________
__________.___________________
_________.____________________
________._____________________
_________.____________________
________._____________________
_________.____________________
__________.___________________
\$\endgroup\$
1
  • \$\begingroup\$ Smart random. You could to use a golfed version $x=random 30;1..30|%{' '*($x+=,-1*$x+,1*(29-$x)|random)+'.'|% *ht 30}. 66 bytes - 10 - 50 = 6 score points \$\endgroup\$
    – mazzy
    Dec 11, 2018 at 9:34
2
\$\begingroup\$

R, 107 characters - 60 points bonus=47

s=sample;i=s(29,1);for(j in 1:30){a=rep(' ',30);i=i+s(c(-1,1),1,p=c(i-1,29-i));a[i]='.';cat(a,'\n',sep='')}

i is the index of the dot. a is the array of 30 spaces. Starting point is random (uniformly from 1 to 29). At each iteration we randomly add -1 or +1 to i with weighted probabilities: i-1 for -1 and 29-i for +1 (values fed as probabilities don't need to sum to one), meaning that it tends to orient the dot towards the center while preventing it from below 1 or above 29 (since their probability fall to 0 in both case).

Example run with _ instead of spaces for legibility:

> s=sample;i=s(1:30,1);for(j in 1:30){a=rep('_',30);i=i+s(c(-1,1),1,p=c(i-1,29-i));a[i]='.';cat(a,'\n',sep='')}
_______________________.______
______________________._______
_____________________.________
______________________._______
_____________________.________
______________________._______
_____________________.________
______________________._______
_____________________.________
____________________._________
_____________________.________
______________________._______
_____________________.________
______________________._______
_____________________.________
____________________._________
___________________.__________
____________________._________
___________________.__________
__________________.___________
_________________.____________
________________._____________
_______________.______________
______________._______________
_______________.______________
________________._____________
_________________.____________
________________._____________
_______________.______________
______________._______________
\$\endgroup\$
5
  • \$\begingroup\$ If I'm not mistaken reading your code, i can become either 0 or 30, no? \$\endgroup\$ Feb 18, 2014 at 9:27
  • \$\begingroup\$ Yes you're right it can be 30 (not more though), i ll change that. \$\endgroup\$
    – plannapus
    Feb 18, 2014 at 9:29
  • 1
    \$\begingroup\$ It's fixed now. The probability to go from 1 to 0 or to go from 29 to 30 is now 0. The random starting point is now between 1 and 29. \$\endgroup\$
    – plannapus
    Feb 18, 2014 at 9:35
  • \$\begingroup\$ Hint: You can save 2 more characters by replacing s(1:29,1) with s(29,1). \$\endgroup\$ Feb 18, 2014 at 17:12
  • \$\begingroup\$ @SvenHohenstein you're right I always forget about that one \$\endgroup\$
    – plannapus
    Feb 19, 2014 at 7:56
2
\$\begingroup\$

C# 184 - 10 - 50 = 123

using System;namespace d{class P{static void Main(){var r=new Random();int p=r.Next(30);for(int i=0;i<30;i++){Console.WriteLine(".".PadLeft(p+1).PadRight(29));p+=r.Next(30)<p?-1:1;}}}}

Output

space replaced with _ for legibility.

____________.________________
___________._________________
__________.__________________
___________._________________
____________.________________
_____________._______________
______________.______________
_____________._______________
______________.______________
_______________._____________
______________.______________
_______________._____________
______________.______________
_______________._____________
______________.______________
_____________._______________
____________.________________
___________._________________
____________.________________
_____________._______________
______________.______________
_____________._______________
____________.________________
___________._________________
____________.________________
___________._________________
____________.________________
___________._________________
__________.__________________
___________._________________
\$\endgroup\$
5
  • \$\begingroup\$ I am pretty sure that it should be possible to get a smaller code in the if...else if...else at the end of you code. Further, your output makes me get some doubt that it tends to be in the middle, but your code seems to be right. \$\endgroup\$ Feb 18, 2014 at 15:42
  • \$\begingroup\$ Forgot to update the output when i edited the code. The r.Next(30)<p?-1:1; makes it happen. Not sure you can go smaller with the if statements. switch is big because of the mandatory break / return and the final else requires a default:{} case and that is also long. \$\endgroup\$
    – Gusdor
    Feb 18, 2014 at 16:15
  • \$\begingroup\$ @Victor thanks for the input. i made some edits. \$\endgroup\$
    – Gusdor
    Feb 18, 2014 at 16:20
  • \$\begingroup\$ If p is zero, the p+=r.Next(30)<p?-1:1; will always get 1, so no need for the if(p==0). Ditto for p==29. p will never be 30, so you can get rid of the else if. \$\endgroup\$ Feb 18, 2014 at 16:43
  • \$\begingroup\$ @Victor grand. ill make those changes. Ta. \$\endgroup\$
    – Gusdor
    Feb 19, 2014 at 8:02
1
\$\begingroup\$

PHP

With the centring bonus: 82 - 50 = 32

$i=rand(0,29);for($c=0;$c++<30;rand(0,28)<$i?$i--:$i++)echo pack("A$i",'').".\n";

For this version (old versions below), removed the min/max checking as that's taken care of in the centring code. rand(1,28) becomes important here as it allows for the $i++ to push itself up to 29 (actual max).

edit: unnecessary parenthesis, moved shifting code


Simple algorithm for centring: generates a new number between 0 and 29 and compares it to the current one. Takes advantage of the "probability" of getting a number on the bigger side to draw towards the centre.

Actual result: (line numbering added afterwards)

01|        .
02|       .
03|        .
04|         .
05|          .
06|           .
07|            .
08|           .
09|            .
10|             .
11|              .
12|             .
13|            .
14|             .
15|            .
16|             .
17|            .
18|             .
19|              .
20|               .
21|              .
22|             .
23|              .
24|               .
25|                .
26|               .
27|                .
28|                 .
29|                .
30|               .

Archived:

$i=rand(0,29);for($c=0;$c++<30;){($i<1?$j=1:($i>28?$j=28:$j=rand(0,29)));($j<$i?$i--:$i++);echo pack("A$i",'').".\n";} 119 characters

$i=rand(0,29);for($c=0;$c++<30;){($i<1?$i++:($i>28?$i--:(rand(0,29)<$i?$i--:$i++)));echo pack("A$i",'').".\n";} 112 characters

\$\endgroup\$
2
  • \$\begingroup\$ I'm a little impressed that I shaved off 49 characters since my first version... \$\endgroup\$
    – Eligos
    Feb 19, 2014 at 16:40
  • \$\begingroup\$ shaved 44 characters now. Which must mean it was 39 last time. \$\endgroup\$
    – Eligos
    Feb 20, 2014 at 10:24
1
\$\begingroup\$

JavaScript ES6 125 - 10 (30 character lines) - 50 (shifts toward middle) = 65

I had an epiphany going up the lift to my unit, so I had to get it down before it left my memory...

z=(j=Array(t=29).join`_`)+"."+j;x=(r=Math.random)()*t;for(i=30;i--;)console.log(z.substr(x=(x+=r()<x/t?-1:1)>t?t:x<0?0:x,30))

A little variable positional shuffling and a little creativity for calculating the shift probability indicated by x/t... (Thanks Kostronor for pointing it out!) I now gain the -50 bonus for shift to the middle, and I also made the starting position within the full range of the line, which allowed me to shave two bytes!

....5....0....5....0....5....0 <-- Ruler
_.____________________________
__.___________________________
___.__________________________
__.___________________________
___.__________________________
__.___________________________
_.____________________________
__.___________________________
___.__________________________
__.___________________________
___.__________________________
____._________________________
_____.________________________
______._______________________
_______.______________________
________._____________________
_______.______________________
______._______________________
_______.______________________
________._____________________
_______.______________________
________._____________________
_________.____________________
__________.___________________
_________.____________________
__________.___________________
___________.__________________
____________._________________
_____________.________________
______________._______________
\$\endgroup\$
5
  • \$\begingroup\$ Isn't it that randomness of initial dot position here is limited to 2 or 3 possibilities? And that's what keeping dot in the middle (because of very short number of runs = 30) and therefore worth -50? \$\endgroup\$ Feb 18, 2014 at 10:34
  • \$\begingroup\$ Quoting the OP's text: "Your program starts with a random x position and every turn shifts this position randomly by 1 to the left or right" My code initially is defined at 15+r()*2 which could be anything from 15 to 16.9999999998 or so which could round off to 17. the additional x+=r()<.5?-1:1 throws a little more randomness by bringing it to a range of 14 to 18, so technically a random number which is within the definition of what was asked... By bending that rule, the flip (+1,-1) will in most cases bring it back toward the middle... ;) \$\endgroup\$ Feb 18, 2014 at 21:26
  • \$\begingroup\$ Well, on the random thing, you got me... It was meant to be 'a random position from all possible positions' but it gives you not much advantage, as the 50 points clearly not apply! Please reread the explanation on this bonus, a fixed 0,5 percent do not get it! \$\endgroup\$ Feb 19, 2014 at 9:01
  • \$\begingroup\$ Valid point, I'll redo my score accordingly... \$\endgroup\$ Feb 20, 2014 at 0:39
  • \$\begingroup\$ @Kostonor Code updated with proper solution, updated score accordingly! \$\endgroup\$ Feb 20, 2014 at 7:17
1
\$\begingroup\$

k, 53 - 10 - 50 = -7

Solution 1

{{a:30#" ";a[x]:".";a}'{x+$[*x<1?!30;1;-1]}\[x;*1?x]}

Usage

{{a:30#" ";a[x]:".";a}'{x+$[*x<1?!30;1;-1]}\[x;*1?x]}30

"      .                       "
"       .                      "
"      .                       "
"       .                      "
"      .                       "
"       .                      "
"        .                     "
"         .                    "
"        .                     "
"         .                    "
"        .                     "
"         .                    "
"          .                   "
"           .                  "
"            .                 "
"             .                "
"              .               "
"               .              "
"              .               "
"             .                "
"            .                 "
"           .                  "
"          .                   "
"         .                    "
"        .                     "
"       .                      "
"        .                     "
"         .                    "
"          .                   "
"         .                    "
"          .                   "

Solution 2

{r::x;{a:r#" ";a[x]:".";a}'{a:r#0b;a[x?r]:1b;x+$[a@*1?r;-1;1]}\[x;*1?x]}[30]
\$\endgroup\$
1
\$\begingroup\$

Scala, 95 - 10 = 85 bytes

def r=math.random
Seq.iterate(r*30,30){n=>println(("#"*30)updated(n.toInt,'.'))
(r*2-1+n)round}

I'm still thinking about the 50 byte bonus.

Explanation:

def r=math.random //define a shortcut for math.random, which returns a number 0 <= n < 1
Seq.iterate(      //build a sequence,
  r*30,             //starting with a random number bewteen 0 and 29
  30                //and containing 30 elements.
){n=>             //Calculate each element by applying this function to the previous element
  println(        //print...
    (" "*30)             //30 spaces
    updated(n.toInt,'.') //with the n-th char replaced with a dot
  )               //and a newline.
                  //The next element is
  (r*2-1+n)       //an random number between -1 and 1 plus n
  round           //rounded to the nearest integer.
}
\$\endgroup\$
1
\$\begingroup\$

Javascript, 125 (135 - 10)

q=Math.random,p=~~(q()*31)+1;for(i=0;i++<30;){s='',d=j=1;for(;j++<31;)s+=j==p?'.':" ";p+=q()<.5?1:-1;p-=p>28?2:p<2?-2:0;console.log(s)}

Comments and advice is welcome.

\$\endgroup\$
3
  • \$\begingroup\$ Sadly, your solution does not qualify, compare your output with the output of other solutions. Shift the dot by one character. \$\endgroup\$ Feb 18, 2014 at 12:23
  • \$\begingroup\$ @Kostronor Oh! oh! I am so sorry! I forgot to read that part of the question! I will try to develop a new version soon. Thanks for pointing out! \$\endgroup\$ Feb 18, 2014 at 12:25
  • \$\begingroup\$ @Kostronor Program edited. \$\endgroup\$ Feb 18, 2014 at 12:39
1
\$\begingroup\$

JavaScript

114 chars - 10 (30 char lines) - 50 (pull dot towards the middle) = 54

for(f=Math.random,a=[],i=30,j=k=f()*i|0;i--;a[j]='.',a[j+=29-k]='\n',j+=k+=f()>k/29?1:-1);console.log(a.join('-'))

However, I noticed that a 10 character reward for filling up the lines to 30 chars may be a bad deal; so:

102 chars - 50 (pull dot towards the middle) = 52

for(f=Math.random,a=[],i=30,j=k=f()*i|0;i;i--,a[j]='.\n',j+=k+=f()>k/29?1:-1);console.log(a.join('-'))

Kudos to @WallyWest for the simplified pull direction conditional f()>k/29?1:-1, my first draft used two nested conditionals.

\$\endgroup\$
1
\$\begingroup\$

Racket 227 bytes (-10 for 30 characters, -50 for shift to midline = 167)

At each step, dot is twice more likely to move towards midline than away from it:

(let lp((r(random 1 31))(c 0)(g(λ(n)(make-string n #\space))))(set! r
(cond[(< r 1)1][(> r 30)30][else r]))(printf"~a~a~a~n"(g r)"*"(g(- 29 r)))
(when(< c 30)(lp(+ r(first(shuffle(if(> r 15)'(-1 -1 1)'(1 1 -1)))))(add1 c)g)))

Ungolfed:

(define (f)
    (let loop ((r (random 1 31))
               (c 0)
               (g (λ (n) (make-string n #\space))))
      (set! r (cond
                [(< r 1) 1]
                [(> r 30) 30]
                [else r] ))
      (printf "~a~a~a~n" (g r) "*" (g (- 29 r)))
      (when (< c 30)
        (loop (+ r
                 (first
                  (shuffle
                   (if (> r 15)
                       '(-1 -1 1)
                       '(1 1 -1)))))
              (add1 c)
              g))))

Testing:

(println "012345678901234567890123456789")
(f)

Output:

"012345678901234567890123456789"
                       *       
                      *        
                       *       
                        *      
                         *     
                        *      
                       *       
                      *        
                     *         
                      *        
                     *         
                    *          
                     *         
                      *        
                     *         
                    *          
                   *           
                  *            
                 *             
                *              
               *               
                *              
                 *             
                *              
                 *             
                *              
                 *             
                *              
                 *             
                *              
               *               
\$\endgroup\$
1
  • \$\begingroup\$ Nice solution! Racket is really interesting. You can give yourself the 50 bonus points and check if you apply for the extra 10 :-) \$\endgroup\$ Oct 5, 2016 at 6:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.