A 2D Traffic Jam

17

1

The Biham-Middleton-Levine traffic model is a self-organizing cellular automaton that models simplified traffic.

It consists of a number of cars represented by points on a lattice with a random starting position, where each car may be one of two types: those that only move downwards (shown as blue in this article), and those that only move towards the right (shown as red in this article). The two types of cars take turns to move. During each turn, all the cars for the corresponding type advance by one step if they are not blocked by another car.

Your task is to visualize this model as an animation. Here are some good demonstrations.

enter image description here

Input

A floating point number between 0 and 1 representing density, and two integers representing the displayed grid height and width. Assume inputs are valid, and parameters to a function or reading from user input are both fine.

Example: 0.38 144 89 (corresponds to image above)

Output

A grid, at least 80x80, that displays the animation of this model running. At the start, cars are randomly placed on the grid until the grid reaches the input density, with half red and half blue (that is density times total number of grid squares, rounded however you like). The density must be this value, which means you cannot fill each cell with density as a probability. For each step, one type of car either moves downwards or right, wrapping around if they go past the edge. The type of car that moves alternates each step. To make the animation viewable, there must be at least 10 ms between each step.

Rules

  • The cars can be any color or symbol as long as they are distinguishable from each other and the background, and each car type is the same color or symbol.

  • Console and graphical output are both allowed. For console output, any printable symbol is fine, but output must be as a grid of characters.

  • Please specify what kind of output you've produced if you don't have a screenshot or gif.

  • The simulation must run forever.

The output is a bit complex, so if you have any questions, please comment.

qwr

Posted 2016-12-27T04:32:06.437

Reputation: 8 929

Any restrictions on how slowly or quickly the animation must run? – xnor – 2016-12-27T05:23:09.777

Perhaps it's worth specifying that the type of car that moves alternates each step. – Greg Martin – 2016-12-27T05:37:17.997

@xnor I was thinking at least 5 or 10 ms per loop, but I'm not really sure if that would be difficult to measure. – qwr – 2016-12-27T05:39:14.843

@GregMartin It says that in the quote, but I'll edit it anyway – qwr – 2016-12-27T05:39:38.263

Can we assume that the dimensions are coprime, like in the linked animation? – None – 2016-12-27T05:40:48.523

How does the density work? if density is 0.38, are 38% of the pixels filled, or is the board 38% red car and 38% blue car? – JungHwan Min – 2016-12-27T05:41:54.313

@ais523 no, they could be not coprime – qwr – 2016-12-27T05:42:01.480

@JungHwanMin 38% of pixels filled. There's some gray area for rounding, but please be reasonable. – qwr – 2016-12-27T05:44:10.977

Is it okay if the dimensions are switched (width <-> height) – JungHwan Min – 2016-12-27T06:58:08.613

3Does the density mean that the density has to be that value, or just that each pixel has a probability d to be filled? Also, do we have to assign the color of the cars randomly or not? If randomly, again is it ok if they just have a 50-50 chance of being either colour? – JAD – 2016-12-27T12:29:46.833

Is it acceptable if the cars move up instead of down? – JAD – 2016-12-27T13:45:46.527

If the density is 1, should it just be gridlocked? – Magic Octopus Urn – 2016-12-27T15:34:58.807

@carusocomputing that is what the linked example does. – JAD – 2016-12-27T17:27:19.103

@LuisMendo waiting for a 40 byte MATL answer. – Magic Octopus Urn – 2016-12-27T17:37:45.040

1@JarkoDubbeldam The density has to be that value. They have a 50-50 chance of being each color. However I answered late so answers may be different. Cars can move up or left. – qwr – 2016-12-27T22:35:12.720

Answers

5

Mathematica, 237 228 203 198 181 bytes

(b=RandomSample@ArrayReshape[Table[{0,i=2},##/2],{1##2},1]~Partition~#2;Dynamic@Colorize[i=-i;b=CellularAutomaton[{193973693,{3,{a=0{,,},{3,9,1},a}},{1,1}},b];If[i>0,b,2-b]])&

The output is a dynamic Image. The background is light green, and the cars are black or magenta, depending on their direction.

Explanation

b=RandomSample@ArrayReshape[Table[{i=1,2},##/2],{1##2},1]~Partition~#2

Create the initial board:

Table[{0,i=2},##/2]

Set i to 2. Create a List of {0, 2}, whose length is floor(density * width * height / 2) (divided by two because {0, 2} is length-2).

ArrayReshape[ ... ,{1##2},1]

Reshape the resulting 2-D List (2 x something) into 1-D List (length = width * height). Pad 1 if there aren't enough values.

RandomSample@ ...

(Pseudo-)randomly sort the result.

... ~Partition~#2

Partition that result into length (width).

b= ...

Store that in b.


Dynamic@Colorize[i=-i;b=CellularAutomaton[{193973693,{3,{a=0{,,},{3,9,1},a}},{1,1}},b];If[i>0,b,2-b]]

Create a Dynamic Image:

i=-i;

Flip the sign of i.

b=CellularAutomaton[{193973693,{3,{a=0{,,},{3,9,1},a}},{1,1}},b]

Apply the cellular automaton with rule 193973693 and neighbor weights {{0, 0, 0}, {3, 9, 1}, {0, 0, 0}} to b transposed. Set b equal to that.

If[i>0,b,2-b]

If i is positive, leave b alone. If not, transpose the b (2- is there because I golfed the CellularAutomaton a bit). Essentially, this transposes b every other iteration (to undo the transposition)

Colorize[ ... ]

Convert the array into a colorful Image.

Dynamic@ ...

Make the expression Dynamic. i.e. the above functions are run repeatedly.

Output

Here's a sample output (inputs: 0.35, 192, 108) for 2000 frames (magnified 2x).

https://i.imgur.com/zmSyRut.mp4

JungHwan Min

Posted 2016-12-27T04:32:06.437

Reputation: 13 290

Huh, using the built-in is longer than not using it?! – Adám – 2017-01-18T16:05:56.853

5

R, 350 338 293 291 273 268 264 bytes

function(d,x,y){f=function(w){v=length(w);for(j in which(w>0&!w[c(2:v,1)]))w[c(j,j%%v+1)]=0:1;w};m=matrix(sample(c(rep(1,q<-floor(d*y*x/2)),rep(-1,q),rep(0,x*y-2*q))),x);p=animation::ani.pause;o=image;a=apply;repeat{o(m<-t(a(m,1,f)));p();o(m<--1*a(-1*m,2,f));p()}}

Ungolfed:

function(d,x,y){
  q=floor(d*y*x/2)

  m=matrix(sample(c(rep(1,q),rep(-1,q),rep(0,x*y-2*q))),x)

  f=function(w){
    v=length(w)
    for(j in which(w>0&!w[c(2:v,1)])){
      w[c(j,j%%v+1)]=0:1
    }
    w
  }


  library(animation)
  repeat{
    m=t(apply(m,1,f))
    image(m)
    m=-1*apply(-1*t(m),2,f))
    ani.pause()
    image(m)  
    ani.pause()
  }
}

Function that takes 3 arguments: d as density, and dimensions x,y. q is the number of cars in each colour. m is the matrix with cars, which is initially filled by taking a random sort of the number of cars and empty spaces. Cars are either 1 or -1, empty space is 0.

f is a function that moves the cars one row, looking at cars coded as 1. It checks if the car can move by checking for 1s followed by 0. We use apply to run f on every row or column, depending on which cars.

f handles the moving of the 1 cars, to move the -1 cars, we transpose the matrix, chaninging the direction of movement, multiplying the matrix by -1, so the -1 cars become 1 cars, and v.v. and the resulting matrix is transformed again.

This uses image to create the plot, using 3 default colours for the three values. Uses the animation package to handle the animations using the default options, which is 1 fps.

0.38, 144, 89:

Link to GIF

0.2, 144, 89:

Link to GIF

0.53, 144, 89:

Link to GIF

JAD

Posted 2016-12-27T04:32:06.437

Reputation: 2 898

Your animation looks really cool - what density did you use? Seems like the whole thing got jammed pretty quickly with a lot of empty space – qwr – 2016-12-29T00:30:41.833

@qwr that actually was something that was bothering me. In my program the whole thing jams at lower densities than in the example you linked. I can't remember the exact parameters used for the plot though, but it could very well be the 0.38 144 89 from the example. – JAD – 2016-12-29T09:52:20.590

Playing around with square grids I got a density of 0.35 to jam https://www.jasondavies.com/bml/#0.35/100/100 but it is almost always one thick 45deg line instead of thin diagonal lines. Since your lines look more vertical I think something with the two types of cars is off

– qwr – 2016-12-29T10:19:46.427

I see the problem now. Cara can only advance if they are not blocked by another car. So in the Wikipedia examples all moving cars have a space in front of them them. But in your animation the cars move as a line. Interesting. – qwr – 2016-12-29T10:21:30.893

Ah, that would do it. – JAD – 2016-12-29T10:28:58.020

I think I am not the only submission that has this flaw. I'll try to fix it, but maybe it would be good to specify this in the challenge. – JAD – 2016-12-29T10:42:34.923

3

Dyalog APL, 190 108 115 112 bytes

Solution

S←{⍉⍣⍺⊢d[⍺]↑d[⍺]↓⍉↑(⍺⊃'(↓+) ' '(→+) ')⎕R' \1'↓(,⍨,⊢)⍉⍣⍺⍉⎕←⍵⊣⎕DL÷4}
{1S 0S⍵}⍣≡' ↓→'[d⍴{⍵[?⍨⍴⍵]}c↑1 2⍴⍨⌊⎕×c←×/d←⎕]

TryAPL online (slightly modified because of online restrictions):

  1. Set ⎕IO←0, define the function S, and then define and display a random 38% 14×29 grid, G.

  2. Make one move down.

  3. Make one move to the right.

  4. Go to step 2.

    Traffic
    Animation of previous algorithm, which did not guarantee density.

Explanation

S←{ define the direct function S (explained here from right to left):

÷4 reciprocal of 4 (0.25)

⎕DL wait that many seconds (returns actual elapsed time)

⍵⊣ discard that in favour of ⍵ (the right argument; the grid)

⎕← output that

 transpose

⍉⍣⍺ transpose back again if ⍺ (the left argument; 0=down, 1=right)

( apply the function train (explained here from left to right):

  ,⍨ the argument appended to itself

  , appended to

   itself

)

 split matrix into list of lists

( search regex (explained here from left to right):

  ⍺⊃ pick one of the following two based on ⍺ (0=down/first, 1=right/second)

  '(↓+) ' '(→+) ' down and left arrow sequences followed by a space

)⎕R' \1' replace with a space followed by the found sequence

 mix list of lists into matrix

 transpose

d[⍺]↓ drop "height" rows if ⍺ (left argument) is 0 (down) or "width" rows if ⍺ is 1 (right)

d[⍺]↑ then take that many rows

 pass through (serves as separator)

⍉⍣⍺ transpose if ⍺ (the left argument; 0=down, 1=right)

}


' ↓→'[ index the string with (explained here from right to left):

 numeric input (dimensions)

d← assign that to d

×/ multiply the dimensions (finds count of cells)

c← assign that to c

⎕× multiply that with numeric input (the density)

 round down

1 2⍴⍨ cyclically repeat one and two until that length

c↑ extend that until length c, padding with zeros

d⍴ use d (the dimensions) to reshape

{ apply this anonymous function to that (explained here from left to right):

  ⍵[ the right argument (the list of zeros, ones, and twos) indexed by

   ?⍨ the shuffled indices up to

   ⍴⍵ the length of the argument

  ]

}

]

{ apply the following anonymous function (explained from right to left):

0S⍵ apply S with 0 (down) as left argument and the grid as right argument

1S with that as right argument, apply S with 1 (right) as left argument

}⍣≡ until two successive iterations are identical (a traffic jam)

Notes

  1. Requires ⎕IO←0, which is default on many systems.

  2. Prompts for (height,width), and then for density.

  3. Does not use any built-in automaton.

  4. Does use built-in regex support.

  5. Stops if there is a traffic jam (no car can move).

  6. Outputs character matrices where represents cars moving right, represents cars moving down, and spaces are empty roads.

  7. As above, it outputs to the session at 4 Hz, but frequency can be adjusted by changing ÷4; e.g ÷3 is 3 Hz and .3 is ³⁄₁₀ Hz.

  8. It is easier to see what is going on if executing ]Box on -s=max -f=on first.

  9. The required distribution is now guaranteed, and the two types of cars occur in exactly 50-50, save for rounding.

Adám

Posted 2016-12-27T04:32:06.437

Reputation: 37 779

Your initial board generation does not guarantee a board that has the input density. I guess it's OP's choice whether to allow that or not. – JungHwan Min – 2016-12-27T16:02:22.257

Oh, @JarkoDubbeldam asked that already. – JungHwan Min – 2016-12-27T16:03:58.933

@JungHwanMin How so? Let the density be d. Every position gets a value between 0 and 1. If between 0 and ᵈ⁄₂ it becomes a ,. If between ᵈ⁄₂ and d it becomes a . If between d and 1 it stays empty. – Adám – 2016-12-27T17:15:55.623

Well, an extreme case would be: every position somehow gets the value 0 (because they're (pseudo)-randomly generated (pseudo)-independently; very improbable but possible). Then your board is full of s. – JungHwan Min – 2016-12-27T17:19:09.800

@JungHwanMin Ah, I see what you mean. – Adám – 2016-12-27T17:25:23.120

Also, your solution is 190 bytes, 108 characters, in UTF-8. (or is it 108 bytes? If so, what encoding did you use?) – JungHwan Min – 2016-12-27T17:40:11.030

@JungHwanMin Notice the link "bytes"...

– Adám – 2016-12-27T17:48:31.160

@JungHwanMin Distribution is now guaranteed. – Adám – 2016-12-28T07:26:42.740

1

Java (624 Bytes + 18 Bytes for Java.awt.* = 642 Bytes)

static void k(double b,final int c,final int d){final int[][]a=new int[c+1][d+1];int i=0,j;for(;i<c;i++){for(j=0;j<d;j++){a[i][j]=Math.random()<b?Math.random()<0.5?1:2:0;}}Frame e=new Frame(){public void paint(Graphics g){setVisible(1>0);int i=0,j;for(;i<c;i++){for(j=0;j<d;j++){g.setColor(a[i][j]==2?Color.BLUE:a[i][j]==1?Color.RED:Color.WHITE);g.drawLine(i,j,i,j);}}for(i=c-1;i>=0;i--){for(j=d-1;j>=0;j--){if(a[i][j]==1&&a[i][(j+1)%d]==0){a[i][(j+1)%d]=1;a[i][j]=0;}else if(a[i][j]>1&&a[(i+1)%c][j]==0){a[(i+1)%c][j]=2;a[i][j]=0;}}}}};e.show();while(1>0){e.setSize(c,d+i++%2);try{Thread.sleep(400L);}catch(Exception f){}}}

Ungolfed:

static void k(double b,final int c,final int d){
        final int[][]a=new int[c+1][d+1];
        int i=0,j;
        for(;i<c;i++) {
            for(j=0;j<d;j++) {
                a[i][j]=Math.random()<b?Math.random()<0.5?1:2:0;
            }
        }

        Frame e=new Frame(){
            public void paint(Graphics g){
                setVisible(1>0);
                int i=0,j;
                for(;i<c;i++) {
                    for(j=0;j<d;j++) {
                        g.setColor(a[i][j]==2?Color.BLUE:a[i][j]==1?Color.RED:Color.WHITE);
                        g.drawLine(i,j,i,j);
                    }
                }
                for(i=c-1;i>=0;i--) {
                    for(j=d-1;j>=0;j--) {
                        if(a[i][j]==1&&a[i][(j+1)%d]==0){
                            a[i][(j+1)%d]=1;a[i][j]=0;
                        }else if(a[i][j]>1&&a[(i+1)%c][j]==0){
                            a[(i+1)%c][j]=2;a[i][j]=0;
                        }
                    }
                }
            }
        };
        e.show();
        while(1>0){e.setSize(c,d+i++%2);try{Thread.sleep(400L);}catch(Exception f){}}
    }

Picture:

enter image description here

Magic Octopus Urn

Posted 2016-12-27T04:32:06.437

Reputation: 19 422

Not familiar with java, but are red, blue and white the shortest names for colours you can use? (maybe grey is an option, saving one byte vs white) – JAD – 2016-12-27T22:43:21.753

The screenshot appears to show the same problem as what I described here http://codegolf.stackexchange.com/questions/104742/a-2d-traffic-jam/104775?noredirect=1#comment255146_104775

– qwr – 2016-12-29T10:27:36.213