Galton box animation: Difference between revisions

m
 
(80 intermediate revisions by 32 users not shown)
Line 1:
{{task|Animation}}
[[Category:Randomness]]
Generate an animated simulation of [[wp:Bean_machine|Sir Francis Galton's device]]. An example can be found to the right. [[File:Galtonbox-Unicon.PNG|thumb|Example of a Galton Box at the end of animation.]]
{{omit from|GUISS}}
[[File:Galtonbox-Unicon.PNG|thumb|Example of a Galton Box at the end of animation.]]
 
 
A   '''Galton device'''   [[wp:Bean_machine|Sir Francis Galton's device]]   is also known as a   '''bean machine''',   a   '''Galton Board''',   or a   '''quincunx'''.
 
 
;Description of operation:
In a Galton box, there are a set of pins arranged in a triangular pattern.   A number of balls are dropped so that they fall in line with the top pin, deflecting to the left or the right of the pin.   The ball continues to fall to the left or right of lower pins before arriving at one of the collection points between and to the sides of the bottom row of pins.
 
Eventually the balls are collected into bins at the bottom   (as shown in the image),   the ball column heights in the bins approximate a   [https://mathworld.wolfram.com/NormalDistribution.html bell curve].   Overlaying   [https://en.wikipedia.org/wiki/Pascal%27s_triangle Pascal's triangle]   onto the pins shows the number of different paths that can be taken to get to each bin.
In a Galton box, there are a set of pins arranged in a triangular pattern. A number of balls are dropped so that they fall in line with the top pin, deflecting to the left or the right of the pin. The ball continues to fall to the left or right of subsequent pins before arriving at one of the collection points between and to the sides of the bottom row of pins.
 
For the purpose of this task the box should have at least 5 pins on the bottom row. Your solution can use graphics or ASCII animation. Provide a sample of the output/display such as a screenshot.
 
;Task:
Your solution can have either one or more balls in flight at the same time. If multiple balls are in flight, ensure they don't interfere with each other.
Generate an animated simulation of a Galton device.
 
Your solution should allow users to specify the number of balls or it should run until full or a preset limit. Optionally, display the number of balls.
 
;Task requirements:
::*   The box should have at least 5 pins on the bottom row.
::*   A solution can use graphics or ASCII animation.
::*   Provide a sample of the output/display such as a screenshot.
::*   There can be one or more balls in flight at the same time.
::*   If multiple balls are in flight, ensure they don't interfere with each other.
::*   A solution should allow users to specify the number of balls, or it should run until full or a preset limit.
::*   Optionally,   display the number of balls.
<br><br>
 
=={{header|AutoHotkey}}==
Uses an edit box for the (text based) animation
<langsyntaxhighlight AutoHotkeylang="autohotkey">AutoTrim Off
; User settings
bottompegs := 6
Line 101 ⟶ 118:
StringTrimRight, out, out, 1 ; removes the last newline
return out
}</langsyntaxhighlight>While the number of pegs, and falling space are configurable, here's output shortly after starting one configuration:
<pre>
*
Line 119 ⟶ 136:
=={{header|BASIC256}}==
[[File:Galton box BASIC-256.gif|right|150px|thumb|Galton box animation created with BASIC-256]]
<langsyntaxhighlight lang="basic256">graphsize 150,125
fastgraphics
color black
Line 234 ⟶ 251:
iters = iters + 1
refresh
return</langsyntaxhighlight>
 
=={{header|BBC BASIC}}==
{{works with|BBC BASIC for Windows}}
[[Image:quincunx_bbc.gif|right]]
<langsyntaxhighlight lang="bbcbasic"> maxBalls% = 10
DIM ballX%(maxBalls%), ballY%(maxBalls%)
Line 287 ⟶ 304:
NEXT
tick% += 1
UNTIL FALSE</langsyntaxhighlight>
 
=={{header|C}}==
<langsyntaxhighlight lang="c">#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Line 379 ⟶ 396:
 
return 0;
}</langsyntaxhighlight>
Sample out put at begining of a run:<pre>
*
Line 390 ⟶ 407:
 
* * * * *
</pre>
 
=={{header|C++}}==
Windows GDI version.
<syntaxhighlight lang="cpp">
#include "stdafx.h"
#include <windows.h>
#include <stdlib.h>
 
const int BMP_WID = 410, BMP_HEI = 230, MAX_BALLS = 120;
 
class myBitmap {
public:
myBitmap() : pen( NULL ), brush( NULL ), clr( 0 ), wid( 1 ) {}
~myBitmap() {
DeleteObject( pen ); DeleteObject( brush );
DeleteDC( hdc ); DeleteObject( bmp );
}
bool create( int w, int h ) {
BITMAPINFO bi;
ZeroMemory( &bi, sizeof( bi ) );
bi.bmiHeader.biSize = sizeof( bi.bmiHeader );
bi.bmiHeader.biBitCount = sizeof( DWORD ) * 8;
bi.bmiHeader.biCompression = BI_RGB;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biWidth = w;
bi.bmiHeader.biHeight = -h;
 
HDC dc = GetDC( GetConsoleWindow() );
bmp = CreateDIBSection( dc, &bi, DIB_RGB_COLORS, &pBits, NULL, 0 );
if( !bmp ) return false;
hdc = CreateCompatibleDC( dc );
SelectObject( hdc, bmp );
ReleaseDC( GetConsoleWindow(), dc );
width = w; height = h;
return true;
}
void clear( BYTE clr = 0 ) {
memset( pBits, clr, width * height * sizeof( DWORD ) );
}
void setBrushColor( DWORD bClr ) {
if( brush ) DeleteObject( brush );
brush = CreateSolidBrush( bClr );
SelectObject( hdc, brush );
}
void setPenColor( DWORD c ) {
clr = c; createPen();
}
void setPenWidth( int w ) {
wid = w; createPen();
}
HDC getDC() const { return hdc; }
int getWidth() const { return width; }
int getHeight() const { return height; }
private:
void createPen() {
if( pen ) DeleteObject( pen );
pen = CreatePen( PS_SOLID, wid, clr );
SelectObject( hdc, pen );
}
HBITMAP bmp;
HDC hdc;
HPEN pen;
HBRUSH brush;
void *pBits;
int width, height, wid;
DWORD clr;
};
class point {
public:
int x; float y;
void set( int a, float b ) { x = a; y = b; }
};
typedef struct {
point position, offset;
bool alive, start;
}ball;
class galton {
public :
galton() {
bmp.create( BMP_WID, BMP_HEI );
initialize();
}
void setHWND( HWND hwnd ) { _hwnd = hwnd; }
void simulate() {
draw(); update(); Sleep( 1 );
}
private:
void draw() {
bmp.clear();
bmp.setPenColor( RGB( 0, 255, 0 ) );
bmp.setBrushColor( RGB( 0, 255, 0 ) );
int xx, yy;
for( int y = 3; y < 14; y++ ) {
yy = 10 * y;
for( int x = 0; x < 41; x++ ) {
xx = 10 * x;
if( pins[y][x] )
Rectangle( bmp.getDC(), xx - 3, yy - 3, xx + 3, yy + 3 );
}
}
bmp.setPenColor( RGB( 255, 0, 0 ) );
bmp.setBrushColor( RGB( 255, 0, 0 ) );
ball* b;
for( int x = 0; x < MAX_BALLS; x++ ) {
b = &balls[x];
if( b->alive )
Rectangle( bmp.getDC(), static_cast<int>( b->position.x - 3 ), static_cast<int>( b->position.y - 3 ),
static_cast<int>( b->position.x + 3 ), static_cast<int>( b->position.y + 3 ) );
}
for( int x = 0; x < 70; x++ ) {
if( cols[x] > 0 ) {
xx = 10 * x;
Rectangle( bmp.getDC(), xx - 3, 160, xx + 3, 160 + cols[x] );
}
}
HDC dc = GetDC( _hwnd );
BitBlt( dc, 0, 0, BMP_WID, BMP_HEI, bmp.getDC(), 0, 0, SRCCOPY );
ReleaseDC( _hwnd, dc );
}
void update() {
ball* b;
for( int x = 0; x < MAX_BALLS; x++ ) {
b = &balls[x];
if( b->alive ) {
b->position.x += b->offset.x; b->position.y += b->offset.y;
if( x < MAX_BALLS - 1 && !b->start && b->position.y > 50.0f ) {
b->start = true;
balls[x + 1].alive = true;
}
int c = ( int )b->position.x, d = ( int )b->position.y + 6;
if( d > 10 || d < 41 ) {
if( pins[d / 10][c / 10] ) {
if( rand() % 30 < 15 ) b->position.x -= 10;
else b->position.x += 10;
}
}
if( b->position.y > 160 ) {
b->alive = false;
cols[c / 10] += 1;
}
}
}
}
void initialize() {
for( int x = 0; x < MAX_BALLS; x++ ) {
balls[x].position.set( 200, -10 );
balls[x].offset.set( 0, 0.5f );
balls[x].alive = balls[x].start = false;
}
balls[0].alive = true;
for( int x = 0; x < 70; x++ )
cols[x] = 0;
for( int y = 0; y < 70; y++ )
for( int x = 0; x < 41; x++ )
pins[x][y] = false;
int p;
for( int y = 0; y < 11; y++ ) {
p = ( 41 / 2 ) - y;
for( int z = 0; z < y + 1; z++ ) {
pins[3 + y][p] = true;
p += 2;
}
}
}
myBitmap bmp;
HWND _hwnd;
bool pins[70][40];
ball balls[MAX_BALLS];
int cols[70];
};
class wnd {
public:
int wnd::Run( HINSTANCE hInst ) {
_hInst = hInst;
_hwnd = InitAll();
_gtn.setHWND( _hwnd );
ShowWindow( _hwnd, SW_SHOW );
UpdateWindow( _hwnd );
MSG msg;
ZeroMemory( &msg, sizeof( msg ) );
while( msg.message != WM_QUIT ) {
if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) != 0 ) {
TranslateMessage( &msg );
DispatchMessage( &msg );
} else _gtn.simulate();
}
return UnregisterClass( "_GALTON_", _hInst );
}
private:
static int WINAPI wnd::WndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) {
switch( msg ) {
case WM_DESTROY: PostQuitMessage( 0 ); break;
default:
return static_cast<int>( DefWindowProc( hWnd, msg, wParam, lParam ) );
}
return 0;
}
HWND InitAll() {
WNDCLASSEX wcex;
ZeroMemory( &wcex, sizeof( wcex ) );
wcex.cbSize = sizeof( WNDCLASSEX );
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = ( WNDPROC )WndProc;
wcex.hInstance = _hInst;
wcex.hCursor = LoadCursor( NULL, IDC_ARROW );
wcex.hbrBackground = ( HBRUSH )( COLOR_WINDOW + 1 );
wcex.lpszClassName = "_GALTON_";
RegisterClassEx( &wcex );
RECT rc;
SetRect( &rc, 0, 0, BMP_WID, BMP_HEI );
AdjustWindowRect( &rc, WS_CAPTION, FALSE );
return CreateWindow( "_GALTON_", ".: Galton Box -- PJorente :.", WS_SYSMENU, CW_USEDEFAULT, 0, rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, _hInst, NULL );
}
HINSTANCE _hInst;
HWND _hwnd;
galton _gtn;
};
int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow ) {
srand( GetTickCount() );
wnd myWnd;
return myWnd.Run( hInstance );
}
</syntaxhighlight>
 
=={{header|Clojure}}==
<syntaxhighlight lang="clojure">(def n 8)
(def balls* (atom [{:x n :y 0}]))
(def board* (atom (vec (repeat (inc n) (vec (repeat (inc (* 2 n)) " "))))))
(doseq [y (range (inc n))
i (range y)]
(swap! board* assoc-in [y (+ (- n y) (* 2 i) 1)] "^"))
(def histogram* (atom (vec (repeat (inc (* 2 n)) 0))))
 
(loop [frame 0]
(print "\033[0;0f\033[2J")
(doseq [row @board*] (println (apply str row)))
(let [depth (inc (apply max (map #(quot % 8) @histogram*)))]
(dotimes [y depth]
(doseq [i @histogram*]
(print (nth " ▁▂▃▄▅▆▇█" (min 8 (max 0 (- i (* (- depth y 1) 8)))))))
(print "\n")))
(println "\n")
(flush)
(doseq [[i {:keys [x y]}] (map-indexed vector @balls*)]
(swap! board* assoc-in [y x] " ")
(let [[new-x new-y] [(if (< 0.5 (rand)) (inc x) (dec x)) (inc y)]]
(if (> new-y n)
(do (swap! histogram* update x inc)
(swap! balls* assoc i {:x n :y 0}))
(do (swap! board* assoc-in [new-y new-x] "*")
(swap! balls* assoc i {:x new-x :y new-y})))))
(Thread/sleep 200)
(when (< (count @balls*) n) (swap! balls* conj {:x n :y 0}))
(when (< frame 200) (recur (inc frame))))
</syntaxhighlight>
 
Sample output:
<pre>
^*
^*^
^ ^ ^
^ ^ ^*^
^*^ ^ ^ ^
^ ^ ^*^ ^ ^
^ ^ ^ ^*^ ^ ^
^ ^ ^*^ ^ ^ ^ ^
▅ ▅
█ █ ▂
█ █ █
▃ █ █ █
█ █ █ █ █
▁ ▇ █ █ █ █ █ ▃ ▁
</pre>
 
=={{header|D}}==
To keep the code simpler some corner cases are ignored.
<langsyntaxhighlight lang="d">import std.stdio, std.algorithm, std.random, std.array;
 
enum int boxW = 41, boxH = 37; // Galton box width and height.
Line 405 ⟶ 695:
enum centerH = pinsBaseW + (boxW - (pinsBaseW * 2 - 1)) / 2 - 1;
 
enum Cell : char { empty = ' ',
alias CellBaseType = char;
enum Cell : CellBaseType { empty ball = ' o',
ballwall = 'o|',
wall corner = '|+',
floor corner = '+-',
pin floor = '-.', }
pin = '.' }
 
Cell[boxW][boxH] box; // Galton box. Will be printed upside-down.
Line 418 ⟶ 707:
int x, y; // Position.
 
this(in int x_, in int y_) nothrow @safe @nogc
in {
assert(box[y_][x_] == Cell.empty);
Line 427 ⟶ 716:
}
 
nothrow const @safe @nogc invariant() {
assert(x >= 0 && x < boxW && y >= 0 && y < boxH);
assert(box[y][x] == Cell.ball);
Line 436 ⟶ 725:
return; // Reached the bottom of the box.
 
final switch (box[y - 1][x]) with (Cell) {
finalcase switch (box[y - 1][x]) {empty:
casebox[y][x] = Cell.empty:;
box[y][x] = Cell.empty--;
box[y][x] = y--Cell.ball;
break;
case ball, wall, corner, floor:
// It's frozen. (It always piles on other balls).
break;
case pin:
box[y][x] = Cell.empty;
y--;
if (box[y][x - 1] == Cell.empty && box[y][x + 1] == Cell.empty) {
x += uniform(0, 2) * 2 - 1;
box[y][x] = Cell.ball;
breakreturn;
case} ball,else wall,if corner,(box[y][x floor:- 1] == Cell.empty) {
// It's frozen. (It always piles on other balls).x++;
} else break;{
case pin: x--;
box[y][x] = Cell.empty;}
box[y][x] = y--Cell.ball;
if (box[y][x - 1] == Cell.empty &&break;
box[y][x + 1] == Cell.empty) {
x += uniform(0, 2) * 2 - 1;
box[y][x] = Cell.ball;
return;
} else if (box[y][x - 1] == Cell.empty)
x++;
else
x--;
box[y][x] = Cell.ball;
break;
}
}
}
Line 467 ⟶ 754:
void initializeBox() {
// Set ceiling and floor:
box[0][] = Cell.corner ~ [Cell.floor].replicate(boxW - 2) ~ Cell.corner;
box[$ - 1][] = ~ Cell.cornerbox[0][];
box[$ - 1] = box[0][];
 
// Set walls:
foreach (immutable r; 1 .. boxH - 1)
box[r][0] = box[r][$ - 1] = Cell.wall;
 
// Set pins:
foreach (immutable nPins; 1 .. pinsBaseW + 1)
foreach (pin; 0 .. nPins)
box[boxH - 2 - nPins][centerH + 1 - nPins + pin * 2] = Cell.pin;
= Cell.pin;
}
 
void drawBox() {
foreach_reverse (const ref row; box)
writelnwritefln(cast"%(CellBaseType[]%c%)", row);
}
 
void main() {
initializeBox();
Ball[] balls;
 
Line 495 ⟶ 780:
if (i < nMaxBalls)
balls ~= Ball(centerH, boxH - 2); // Add ball.
drawBox();
 
// Next step for the simulation.
// Frozen balls are kept in balls array for simplicity.
foreach (ref b; balls)
b.doStep();
}
}</langsyntaxhighlight>
{{out}}
<pre>
Line 625 ⟶ 910:
| o o o o o o o o o o o |
+---------------------------------------+</pre>
 
=={{header|EasyLang}}==
[https://easylang.dev/show/#cod=dVNdc4IwEHzPr9iZvqgMlCDYOlP8Ix0f+IhtFBMnYIV/3wkEJIAvcLcc2d27S9mUqOStYKeKvIHclMyQq+Rx46KEB48AOEkFjkpirzMDnDXADQDgKv8YVjSCC77GBls4OGODHXj7dBA8izOusoLB9z4M1vJ4xCOZLKRCFEWkV0HSJLv8KHkXOVyqRRZMIJX19xHUb5NR2HRhb6X8lY9U1jMn1DfENeJBII2m/jQLP05M1qC+D7d1Fy76CqauWi1c8Oq1kI4KMQZlo1Sf8Ya+OeGWSIGKX5nSH/kJ6b1sdC0SkUMlIueiQogYtD+9K6AjgzZ9d85EgiUk/LTQpkMDC0wVSy4DS9+B/s2Kktl6jDnduYIlrRszsdfTGk1EK8YBtodW2FeMaDfX60wF9/VXmWM3s9MyOjG22GDVtzXABgFcbNez2q4ltYV7z2zk37Dn2tCqhgsarfGOZcnhouRDjP0Obrc4eX3U2/hCkT/Fh3+ceDytgWD4frA25FkieLUAz8Y/a4GdjUJzsxrrui1fJ6DbffgeJR4xMSH/ Run it]
 
<syntaxhighlight>
sys topleft
#
proc drawpins . .
for i to 9
for j to i
move (15 - i) * 3 + j * 6 i * 6 + 2
circle 0.7
.
.
.
color 555
drawpins
background -1
#
len box[] 10
len x[] 10
len y[] 10
#
proc showbox . .
for i to 10
x = i * 6 + 15
for j to box[i]
move x 100 - j * 4 + 2
circle 2
.
.
.
proc init . .
for i to 10
box[i] = 0
x[i] = 0
.
.
#
color 543
on timer
if busy = 0 and randint 4 = 1
busy = 1
for i to 10
if x[i] = 0
x[i] = 48
y[i] = 2
break 1
.
.
else
busy = 0
.
clear
showbox
for i to 10
x = x[i]
if x > 0
if y[i] <= 56
y[i] += 2
if y[i] mod 6 = 2
x += 3 * (randint 2 * 2 - 3)
x[i] = x
.
else
idx = (x - 15) / 6
y[i] += 4
if y[i] >= 96 - box[idx] * 4
x[i] = 0
box[idx] += 1
if box[idx] > 10
init
break 1
.
.
.
move x y[i]
circle 2
.
.
timer 0.1
.
timer 0
</syntaxhighlight>
 
=={{header|Elm}}==
<syntaxhighlight lang="elm">import Html.App exposing (program)
import Time exposing (Time, every, millisecond)
import Color exposing (Color, black, red, blue, green)
import Collage exposing (collage)
import Collage exposing (collage,polygon, filled, move, Form, circle)
import Element exposing (toHtml)
import Html exposing (Attribute, Html, text, div, input, button)
import Html.Attributes as A exposing (type', min, placeholder, value, style, disabled)
import Html.Events exposing (onInput, targetValue, onClick)
import Dict exposing (Dict, get, insert)
import String exposing (toInt)
import Result exposing (withDefault)
import Random.Pcg as Random exposing (Seed, bool, initialSeed, independentSeed, step, map)
 
width = 500
height = 600
hscale = 10.0
vscale = hscale * 2
margin = 30
levelCount = 12
radius = hscale/ 2.0
 
type State = InBox Int Int Seed | Falling Int Float Float Float | Landed Int Float
 
type Coin = Coin State Color
 
colorCycle : Int -> Color
colorCycle i =
case i % 3 of
0 -> red
1 -> blue
_ -> green
 
initCoin : Int -> Seed -> Coin
initCoin indx seed = Coin (InBox 0 0 seed) (colorCycle indx)
 
drawCoin : Coin -> Form
drawCoin (Coin state color) =
let dropLevel = toFloat (height//2 - margin)
(level, shift, distance) =
case state of
InBox level shift seed -> (level, shift, 0)
Falling shift distance _ _-> (levelCount, shift, distance)
Landed shift distance -> (levelCount, shift, distance)
position =
( hscale * toFloat shift
, dropLevel - vscale * (toFloat level) - distance + radius / 2.0)
 
in radius |> circle |> filled color |> move position
 
drawGaltonBox : List Form
drawGaltonBox =
let levels = [0..levelCount-1]
-- doubles :
-- [0,2,4,6,8...]
doubles = List.map (\n -> 2 * n) levels
 
-- sequences :
-- [[0], [0,2], [0,2,4], [0,2,4,6], [0,2,4,6,8],...]
sequences = case List.tail (List.scanl (::) [] (doubles)) of
Nothing -> []
Just ls -> ls
 
-- galtonCoords :
-- [ (0,0),
-- (-1,1), (1,1),
-- (-2,2), (0,2), (2,2),
-- (-3,3), (-1,3), (1,3), (3,3),
-- (-4,4), (-2,4), (0,4), (2,4), (4,4), ...]
galtonCoords =
List.map2
(\ls level -> List.map (\n -> (n - level, level)) ls)
sequences
levels
|> List.concat
 
peg = polygon [(0,0), (-4, -8), (4, -8)] |> filled black
 
apex = toFloat (height//2 - margin)
 
in List.map (\(x,y) -> move (hscale*toFloat x, apex - vscale*toFloat y) peg) galtonCoords
 
coinsInBin : Int -> Dict Int Int -> Int
coinsInBin binNumber bins =
case get binNumber bins of
Nothing -> 0
Just n -> n
 
addToBins : Int -> Dict Int Int -> Dict Int Int
addToBins binNumber bins =
insert binNumber (coinsInBin binNumber bins + 1) bins
 
updateCoin : (Coin, Dict Int Int) -> (Coin, Dict Int Int)
updateCoin (Coin state color as coin, bins) =
case state of
InBox level shift seed ->
let deltaShift = map (\b -> if b then 1 else -1) bool
(delta, newSeed) = step deltaShift seed
newShift = shift+delta
newLevel = (level)+1
in if (newLevel < levelCount) then
(Coin (InBox newLevel newShift newSeed) color, bins)
else -- transition to falling
let maxDrop = toFloat (height - 2 * margin) - toFloat (levelCount) * vscale
floor = maxDrop - toFloat (coinsInBin newShift bins) * (radius*2 + 1)
in (Coin (Falling newShift -((vscale)/2.0) 10 floor) color, addToBins newShift bins)
 
Falling shift distance velocity floor ->
let newDistance = distance + velocity
in if (newDistance < floor) then
(Coin (Falling shift newDistance (velocity + 1) floor) color, bins)
else -- transtion to landed
(Coin (Landed shift floor) color, bins)
 
Landed _ _ -> (coin, bins) -- unchanged
 
type alias Model =
{ coins : List Coin
, bins : Dict Int Int
, count : Int
, started : Bool
, seedInitialized : Bool
, seed : Seed
}
 
init : (Model, Cmd Msg)
init =
( { coins = []
, bins = Dict.empty
, count = 0
, started = False
, seedInitialized = False
, seed = initialSeed 45 -- This will not get used. Actual seed used is time dependent and set when the first coin drops.
}, Cmd.none)
 
type Msg = Drop Time | Tick Time | SetCount String | Go
 
update : Msg -> Model -> (Model, Cmd Msg)
update action model =
case action of
Go ->
({model | started = model.count > 0}, Cmd.none)
 
SetCount countString ->
({ model | count = toInt countString |> withDefault 0 }, Cmd.none)
 
Drop t ->
if (model.started && model.count > 0) then
let newcount = model.count - 1
seed' = if model.seedInitialized then model.seed else initialSeed (truncate t)
(seed'', coinSeed) = step independentSeed seed'
in ({ model
| coins = initCoin (truncate t) coinSeed :: model.coins
, count = newcount
, started = newcount > 0
, seedInitialized = True
, seed = seed''}, Cmd.none)
else
(model, Cmd.none)
 
Tick _ ->
-- foldr to execute update, append to coins, replace bins
let (updatedCoins, updatedBins) =
List.foldr (\coin (coinList, bins) ->
let (updatedCoin, updatedBins) = updateCoin (coin, bins)
in (updatedCoin :: coinList, updatedBins))
([], model.bins)
model.coins
in ({ model | coins = updatedCoins, bins = updatedBins}, Cmd.none)
 
view : Model -> Html Msg
view model =
div []
[ input
[ placeholder "How many?"
, let showString = if model.count > 0 then model.count |> toString else ""
in value showString
, onInput SetCount
, disabled model.started
, style [ ("height", "20px") ]
, type' "number"
, A.min "1"
]
[]
 
, button
[ onClick Go
, disabled model.started
, style [ ("height", "20px") ]
]
[ Html.text "GO!" ]
 
, let coinForms = (List.map (drawCoin) model.coins)
in collage width height (coinForms ++ drawGaltonBox) |> toHtml
]
 
subscriptions model =
Sub.batch
[ every (40*millisecond) Tick
, every (200*millisecond) Drop
]
 
main =
program
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}</syntaxhighlight>
 
Link to live demo: http://dc25.github.io/galtonBoxAnimationElm/ . Follow the link, enter a number and press the GO button.
 
=={{header|Factor}}==
{{works with|Factor|0.99 development release 2019-03-17}}
<syntaxhighlight lang="factor">USING: accessors arrays calendar colors combinators
combinators.short-circuit fonts fry generalizations kernel
literals locals math math.ranges math.vectors namespaces opengl
random sequences timers ui ui.commands ui.gadgets
ui.gadgets.worlds ui.gestures ui.pens.solid ui.render ui.text ;
IN: rosetta-code.galton-box-animation
 
CONSTANT: pegs $[ 20 300 40 <range> ]
CONSTANT: speed 90
CONSTANT: balls 140
CONSTANT: peg-color T{ rgba f 0.60 0.4 0.60 1.0 }
CONSTANT: ball-color T{ rgba f 0.80 1.0 0.20 1.0 }
CONSTANT: slot-color T{ rgba f 0.00 0.2 0.40 1.0 }
CONSTANT: bg-color T{ rgba f 0.02 0.0 0.02 1.0 }
 
CONSTANT: font $[
monospace-font
t >>bold?
T{ rgba f 0.80 1.0 0.20 1.0 } >>foreground
T{ rgba f 0.02 0.0 0.02 1.0 } >>background
]
 
TUPLE: galton < gadget balls { frame initial: 1 } ;
 
DEFER: on-tick
 
: <galton-gadget> ( -- gadget )
galton new bg-color <solid> >>interior V{ } clone >>balls
dup [ on-tick ] curry f speed milliseconds <timer>
start-timer ;
 
: add-ball ( gadget -- )
dup frame>> balls <
[ { 250 -20 } swap balls>> [ push ] keep ] when drop ;
 
: draw-msg ( -- )
{ 10 10 }
[ font "Press <space> for new animation" draw-text ]
with-translation ;
 
: draw-slots ( -- )
slot-color gl-color { 70 350 } { 70 871 }
10 [ 2dup gl-line [ { 40 0 } v+ ] bi@ ] times 2drop
{ 70 871 } { 430 871 } gl-line ;
 
: diamond-side ( loc1 loc2 loc3 -- )
[ v+ dup ] [ v+ gl-line ] bi* ;
 
: draw-diamond ( loc color -- )
gl-color {
[ { 0 -10 } { 10 10 } ]
[ { 10 0 } { -10 10 } ]
[ { 0 10 } { -10 -10 } ]
[ { -10 0 } { 10 -10 } ]
} [ diamond-side ] map-compose cleave ;
 
: draw-peg-row ( loc n -- )
<iota> [ 40 * 0 2array v+ peg-color draw-diamond ] with
each ;
 
: draw-peg-triangle ( -- )
{ 250 40 } 1
8 [ 2dup draw-peg-row [ { -20 40 } v+ ] dip 1 + ] times
2drop ;
 
: draw-balls ( gadget -- )
balls>> [ ball-color draw-diamond ] each ;
 
: rand-side ( loc -- loc' ) { { 20 20 } { -20 20 } } random v+ ;
 
:: collide? ( GADGET BALL -- ? )
BALL second :> y
BALL { 0 20 } v+ :> tentative
{ [ y 860 = ] [ tentative GADGET balls>> member? ] } 0|| ;
 
:: update-ball ( GADGET BALL -- BALL' )
{
{ [ BALL second pegs member? ] [ BALL rand-side ] }
{ [ GADGET BALL collide? ] [ BALL ] }
[ BALL { 0 20 } v+ ]
} cond ;
 
: update-balls ( gadget -- )
dup '[ [ _ swap update-ball ] map ] change-balls drop ;
 
: on-tick ( gadget -- )
{
[ dup frame>> odd? [ add-ball ] [ drop ] if ]
[ relayout-1 ] [ update-balls ]
[ [ 1 + ] change-frame drop ]
} cleave ;
 
M: galton pref-dim* drop { 500 900 } ;
 
M: galton draw-gadget*
draw-peg-triangle draw-msg draw-slots draw-balls ;
 
: com-new ( gadget -- ) V{ } clone >>balls 1 >>frame drop ;
 
galton "gestures" f {
{ T{ key-down { sym " " } } com-new }
} define-command-map
 
MAIN-WINDOW: galton-box-animation
{
{ title "Galton Box Animation" }
{ window-controls
{ normal-title-bar close-button minimize-button } }
} <galton-gadget> >>gadgets ;</syntaxhighlight>
{{out}}
Image taken from the program mid-animation: [https://i.imgur.com/E2ge7LE.png]
 
=={{header|Go}}==
{{trans|D}}
<syntaxhighlight lang="go">package main
 
import (
"fmt"
"math/rand"
"time"
)
 
const boxW = 41 // Galton box width
const boxH = 37 // Galton box height.
const pinsBaseW = 19 // Pins triangle base.
const nMaxBalls = 55 // Number of balls.
 
const centerH = pinsBaseW + (boxW-pinsBaseW*2+1)/2 - 1
 
const (
empty = ' '
ball = 'o'
wall = '|'
corner = '+'
floor = '-'
pin = '.'
)
 
type Ball struct{ x, y int }
 
func newBall(x, y int) *Ball {
if box[y][x] != empty {
panic("Tried to create a new ball in a non-empty cell. Program terminated.")
}
b := Ball{x, y}
box[y][x] = ball
return &b
}
 
func (b *Ball) doStep() {
if b.y <= 0 {
return // Reached the bottom of the box.
}
cell := box[b.y-1][b.x]
switch cell {
case empty:
box[b.y][b.x] = empty
b.y--
box[b.y][b.x] = ball
case pin:
box[b.y][b.x] = empty
b.y--
if box[b.y][b.x-1] == empty && box[b.y][b.x+1] == empty {
b.x += rand.Intn(2)*2 - 1
box[b.y][b.x] = ball
return
} else if box[b.y][b.x-1] == empty {
b.x++
} else {
b.x--
}
box[b.y][b.x] = ball
default:
// It's frozen - it always piles on other balls.
}
}
 
type Cell = byte
 
/* Galton box. Will be printed upside down. */
var box [boxH][boxW]Cell
 
func initializeBox() {
// Set ceiling and floor
box[0][0] = corner
box[0][boxW-1] = corner
for i := 1; i < boxW-1; i++ {
box[0][i] = floor
}
for i := 0; i < boxW; i++ {
box[boxH-1][i] = box[0][i]
}
 
// Set walls
for r := 1; r < boxH-1; r++ {
box[r][0] = wall
box[r][boxW-1] = wall
}
 
// Set rest to empty initially
for i := 1; i < boxH-1; i++ {
for j := 1; j < boxW-1; j++ {
box[i][j] = empty
}
}
 
// Set pins
for nPins := 1; nPins <= pinsBaseW; nPins++ {
for p := 0; p < nPins; p++ {
box[boxH-2-nPins][centerH+1-nPins+p*2] = pin
}
}
}
 
func drawBox() {
for r := boxH - 1; r >= 0; r-- {
for c := 0; c < boxW; c++ {
fmt.Printf("%c", box[r][c])
}
fmt.Println()
}
}
 
func main() {
rand.Seed(time.Now().UnixNano())
initializeBox()
var balls []*Ball
for i := 0; i < nMaxBalls+boxH; i++ {
fmt.Println("\nStep", i, ":")
if i < nMaxBalls {
balls = append(balls, newBall(centerH, boxH-2)) // add ball
}
drawBox()
 
// Next step for the simulation.
// Frozen balls are kept in balls slice for simplicity
for _, b := range balls {
b.doStep()
}
}
}</syntaxhighlight>
 
{{out}}
Sample output (showing last step only):
<pre>
Step 91 :
+---------------------------------------+
| |
| . |
| . . |
| . . . |
| . . . . |
| . . . . . |
| . . . . . . |
| . . . . . . . |
| . . . . . . . . |
| . . . . . . . . . |
| . . . . . . . . . . |
| . . . . . . . . . . . |
| . . . . . . . . . . . . |
| . . . . . . . . . . . . . |
| . . . . . . . . . . . . . . |
| . . . . . . . . . . . . . . . |
| . . . . . . . . . . . . . . . . |
| . . . . . . . . . . . . . . . . . |
| . . . . . . . . . . . . . . . . . . |
| . . . . . . . . . . . . . . . . . . . |
| |
| |
| o |
| o |
| o o |
| o o |
| o o |
| o o o |
| o o o |
| o o o o o |
| o o o o o |
| o o o o o o o |
| o o o o o o o |
| o o o o o o o o |
| o o o o o o o o o |
+---------------------------------------+
</pre>
 
=={{header|Haskell}}==
<syntaxhighlight lang="haskell">import Data.Map hiding (map, filter)
import Graphics.Gloss
import Control.Monad.Random
 
data Ball = Ball { position :: (Int, Int), turns :: [Int] }
 
type World = ( Int -- number of rows of pins
, [Ball] -- sequence of balls
, Map Int Int ) -- counting bins
 
updateWorld :: World -> World
updateWorld (nRows, balls, bins)
| y < -nRows-5 = (nRows, map update bs, bins <+> x)
| otherwise = (nRows, map update balls, bins)
where
(Ball (x,y) _) : bs = balls
 
b <+> x = unionWith (+) b (singleton x 1)
 
update (Ball (x,y) turns)
| -nRows <= y && y < 0 = Ball (x + head turns, y - 1) (tail turns)
| otherwise = Ball (x, y - 1) turns
drawWorld :: World -> Picture
drawWorld (nRows, balls, bins) = pictures [ color red ballsP
, color black binsP
, color blue pinsP ]
where ballsP = foldMap (disk 1) $ takeWhile ((3 >).snd) $ map position balls
binsP = foldMapWithKey drawBin bins
pinsP = foldMap (disk 0.2) $ [1..nRows] >>= \i ->
[1..i] >>= \j -> [(2*j-i-1, -i-1)]
 
disk r pos = trans pos $ circleSolid (r*10)
drawBin x h = trans (x,-nRows-7)
$ rectangleUpperSolid 20 (-fromIntegral h)
trans (x,y) = Translate (20 * fromIntegral x) (20 * fromIntegral y)
 
startSimulation :: Int -> [Ball] -> IO ()
startSimulation nRows balls = simulate display white 50 world drawWorld update
where display = InWindow "Galton box" (400, 400) (0, 0)
world = (nRows, balls, empty)
update _ _ = updateWorld
 
main = evalRandIO balls >>= startSimulation 10
where balls = mapM makeBall [1..]
makeBall y = Ball (0, y) <$> randomTurns
randomTurns = filter (/=0) <$> getRandomRs (-1, 1)</syntaxhighlight>
 
=={{header|Icon}} and {{header|Unicon}}==
Line 630 ⟶ 1,549:
[[File:Galtonbox-Unicon.PNG|thumb|right]]
 
<langsyntaxhighlight Iconlang="icon">link graphics
 
global pegsize, pegsize2, height, width, delay
Line 694 ⟶ 1,613:
initial ballcounts := table(0)
FillArc(x, height-(ballcounts[x] +:= 1)*pegsize, pegsize, pegsize)
end</langsyntaxhighlight>
 
=={{header|J}}==
 
First, we need to a representation forrepresent our pins:
 
<langsyntaxhighlight lang="j">initpins=: '* ' {~ '1'&i.@(-@|. |."_1 [: ":@-.&0"1 <:~/~)@i.</langsyntaxhighlight>
 
For example:
 
<langsyntaxhighlight lang="j"> initpins 4
*
* *
* * *
* * * *</langsyntaxhighlight>
 
Note that we could introduce other pin arrangements, for example a Sierpinski triangle:
 
<langsyntaxhighlight lang="j">initSpins=: [: }.@|. (1- 2&^@>:) ]\ [: ,] (,~ ,.~)@]^:[ ,: bind '* '</langsyntaxhighlight>
 
... but this will not be too interesting to use, because of the lack of interior pins for the balls to bounce off of.
Line 718 ⟶ 1,637:
Anyways, once we have that, we can add balls to our picture:
 
<langsyntaxhighlight lang="j">init=: ' ',. ' ',.~ ] ,~ ' ',~ ' o' {~ (# ' ' ~: 1&{.)</langsyntaxhighlight>
 
For example:
 
<langsyntaxhighlight lang="j"> 3 (init initpins) 4
o
o
Line 730 ⟶ 1,649:
* *
* * *
* * * * </langsyntaxhighlight>
 
Now we just need some way of updating our datastructure.
Line 736 ⟶ 1,655:
We will need a mechanism to shift a ball left or right if it's above a pin:
 
<syntaxhighlight lang="text">bounce=: (C.~ ] <"1@:+ 0 1 -~/~ ? @: (2"0))"1 [: I. 'o*'&E."1&.|:</langsyntaxhighlight>
 
And, a mechanism to make the balls fall:
 
<syntaxhighlight lang="text">shift=: 4 :0
fill=. {.0#,y
x |.!.fill y
)</langsyntaxhighlight>
 
And then we need to separate out the balls from the pins, so the balls fall and the pins do not. Note also that in this representation, balls will have to fall when they bounce because they cannot occupy the same space that a pin occupies.
 
We will also want some way of preventing the balls from falling forever. For this task it's probably sufficient to introduce a baseline sufficientlyjust deep enough to hold the stacks (which have passed through the pins) and have later balls instantly fall as close as they can to the baseline once they are pastpassed the pins.
 
<langsyntaxhighlight lang="j">pins=: '*'&=
balls=: 'o'&=
 
Line 758 ⟶ 1,677:
clean2=: ({. , -.&' '"1&.|:&.|.@}.)~ 1 + >./@(# | '*' i:~"1 |:)
clean1=: #~ 1 1 -.@E. *./"1@:=&' '
clean=: clean1@clean2</langsyntaxhighlight>
 
For example:
 
<langsyntaxhighlight lang="j"> nxt nxt 3 (init initpins) 4
o
Line 770 ⟶ 1,689:
* * *
* * * *
</langsyntaxhighlight>
 
Or, showing an entire animation sequence:
 
<langpre jstyle="overflow-x: scroll; white-space: pre; width: 100%"> nxt&.>^:a: <7 (init ' ',.' ',.~ initpins) 5
┌─────────────┬─────────────┬─────────────┬─────────────┬─────────────┬─────────────┬─────────────┬─────────────┬─────────────┬─────────────┬─────────────┬─────────────┬─────────────┬─────────────┐
│ o │ │ │ │ │ │ │ │ │ │ │ │ │ │
Line 790 ⟶ 1,709:
│ * * * * * │ * * * * * │ │ │ │ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │
└─────────────┴─────────────┴─────────────┴─────────────┴─────────────┴─────────────┴─────────────┴─────────────┴─────────────┴─────────────┴─────────────┴─────────────┴─────────────┴─────────────┘</langpre>
 
=={{header|Java}}==
The balls keep track of where they are, and we just have to move them down and print. You might easily adjust this to take command line input for the numbers of pins and balls. I'm sure that this could be a lot shorter...
<syntaxhighlight lang="java">import java.util.Random;
import java.util.List;
import java.util.ArrayList;
 
public class GaltonBox {
=={{header|Perl 6}}==
public static void main( final String[] args ) {
[[File:Galton_box_perl6.gif|thumb|UPPER HALF BLOCK and LOWER HALF BLOCK alternate to give a somewhat smooth animation.]]
new GaltonBox( 8, 200 ).run();
<lang Perl6>my $row-count = 6;
}
 
private final int m_pinRows;
constant $peg = "*";
private final int m_startRow;
constant @coin-icons = "\c[UPPER HALF BLOCK]", "\c[LOWER HALF BLOCK]";
private final Position[] m_balls;
private final Random m_random = new Random();
 
public GaltonBox( final int pinRows, final int ballCount ) {
sub display-board(@positions, @stats is copy, $halfstep) {
m_pinRows = pinRows;
my $coin = @coin-icons[$halfstep.Int];
m_startRow = pinRows + 1;
m_balls = new Position[ ballCount ];
 
for ( int ball = 0; ball < ballCount; ball++ )
state @board-tmpl = {
m_balls[ ball ] = new Position( m_startRow, 0, 'o' );
# precompute a board
my @tmpl;}
 
sub out(*@stuff) {
private static class Position {
@tmpl.push: @stuff>>.ords.item;
int m_row;
int m_col;
char m_char;
 
Position( final int row, final int col, final char ch ) {
m_row = row;
m_col = col;
m_char = ch;
}
}
# three lines of space above
 
for (1..3) {
public void run() {
out " ", " " x (2 * $row-count);
for ( int ballsInPlay = m_balls.length; ballsInPlay > 0; ) {
ballsInPlay = dropBalls();
print();
}
}
# $row-count lines of pegs
 
for ($row-count...1) Z (1...$row-count) -> $spaces, $pegs {
private int dropBalls() {
out " ", " " x $spaces, ($peg xx $pegs).join(" "), " " x $spaces;
int ballsInPlay = 0;
int ballToStart = -1;
 
// Pick a ball to start dropping
for ( int ball = 0; ball < m_balls.length; ball++ )
if ( m_balls[ ball ].m_row == m_startRow )
ballToStart = ball;
 
// Drop balls that are already in play
for ( int ball = 0; ball < m_balls.length; ball++ )
if ( ball == ballToStart ) {
m_balls[ ball ].m_row = m_pinRows;
ballsInPlay++;
}
else if ( m_balls[ ball ].m_row > 0 && m_balls[ ball ].m_row != m_startRow ) {
m_balls[ ball ].m_row -= 1;
m_balls[ ball ].m_col += m_random.nextInt( 2 );
if ( 0 != m_balls[ ball ].m_row )
ballsInPlay++;
}
 
return ballsInPlay;
}
 
private void print() {
for ( int row = m_startRow; row --> 1; ) {
for ( int ball = 0; ball < m_balls.length; ball++ )
if ( m_balls[ ball ].m_row == row )
printBall( m_balls[ ball ] );
System.out.println();
printPins( row );
}
# four lines of space belowprintCollectors();
for (1System.out.4println() {;
}
out " ", " " x (2 * $row-count);
 
private static void printBall( final Position pos ) {
for ( int col = pos.m_row + 1; col --> 0; )
System.out.print( ' ' );
for ( int col = 0; col < pos.m_col; col++ )
System.out.print( " " );
System.out.print( pos.m_char );
}
 
private void printPins( final int row ) {
for ( int col = row + 1; col --> 0; )
System.out.print( ' ' );
for ( int col = m_startRow - row; col --> 0; )
System.out.print( ". " );
System.out.println();
}
 
private void printCollectors() {
final List<List<Position>> collectors = new ArrayList<List<Position>>();
 
for ( int col = 0; col < m_startRow; col++ ) {
final List<Position> collector = new ArrayList<Position>();
 
collectors.add( collector );
for ( int ball = 0; ball < m_balls.length; ball++ )
if ( m_balls[ ball ].m_row == 0 && m_balls[ ball ].m_col == col )
collector.add( m_balls[ ball ] );
}
@tmpl
}();
 
for ( int row = 0, rows = longest( collectors ); row < rows; row++ ) {
my $midpos = $row-count + 2;
for ( int col = 0; col < m_startRow; col++ ) {
final List<Position> collector = collectors.get( col );
final int pos = row + collector.size() - rows;
 
System.out.print( '|' );
my @output;
if ( pos >= 0 )
{
System.out.print( collector.get( pos ).m_char );
# collect all the output and output it all at once at the end
sub say(Str $foo) { else
@output System.push:out.print( ' $foo,' "\n");
}
sub print System.out.println(Str $foo)'|' {);
@output.push: $foo;
}
}
 
private static final int longest( final List<List<Position>> collectors ) {
# make some space above the picture
sayint ""result for= ^100;
 
for ( final List<Position> collector : collectors )
my @output-lines = map { [map *.clone, @$_].item }, @board-tmpl;
result = Math.max( collector.size(), result );
# place the coins
 
for @positions.kv -> $line, $pos {
return next unless $pos.definedresult;
}
@output-lines[$line][$pos + $midpos] = $coin.ord;
}</syntaxhighlight>
}
{{out}}
# output the board with its coins
When only five balls have begun to fall through the pins:
for @output-lines -> @line {
<pre> say @line>>.chr.join("");o
.
o
. .
o
. . .
o
. . . .
o
. . . . .
 
. . . . . .
 
. . . . . . .
 
. . . . . . . . </pre>
 
Later, some balls have arrived in the collectors:
<pre> o
.
o
. .
o
. . .
o
. . . .
o
. . . . .
o
. . . . . .
o
. . . . . . .
o
. . . . . . . .
| | | | | |o| | | |
| | | |o|o|o| | | |</pre>
Note that the collectors are as deep as required.
 
Finally, all the balls are in the collectors:
<pre> .
 
. .
 
. . .
 
. . . .
 
. . . . .
 
. . . . . .
 
. . . . . . .
 
. . . . . . . .
| | | | |o| | | | |
| | | | |o| | | | |
| | | | |o| | | | |
| | | | |o| | | | |
| | | | |o| | | | |
| | | | |o| | | | |
| | | | |o| | | | |
| | | | |o| | | | |
| | | |o|o| | | | |
| | | |o|o| | | | |
| | | |o|o| | | | |
| | | |o|o| | | | |
| | | |o|o| | | | |
| | | |o|o| | | | |
| | | |o|o|o| | | |
| | | |o|o|o| | | |
| | | |o|o|o| | | |
| | | |o|o|o| | | |
| | | |o|o|o| | | |
| | | |o|o|o| | | |
| | | |o|o|o| | | |
| | | |o|o|o| | | |
| | | |o|o|o| | | |
| | | |o|o|o| | | |
| | | |o|o|o| | | |
| | | |o|o|o| | | |
| | | |o|o|o| | | |
| | | |o|o|o| | | |
| | | |o|o|o| | | |
| | | |o|o|o| | | |
| | | |o|o|o|o| | |
| | | |o|o|o|o| | |
| | | |o|o|o|o| | |
| | | |o|o|o|o| | |
| | | |o|o|o|o| | |
| | | |o|o|o|o| | |
| | | |o|o|o|o| | |
| | |o|o|o|o|o| | |
| | |o|o|o|o|o| | |
| | |o|o|o|o|o| | |
| | |o|o|o|o|o| | |
| | |o|o|o|o|o| | |
| | |o|o|o|o|o| | |
| | |o|o|o|o|o| | |
| | |o|o|o|o|o| | |
| | |o|o|o|o|o| | |
| | |o|o|o|o|o| | |
| | |o|o|o|o|o| | |
| | |o|o|o|o|o| | |
| | |o|o|o|o|o| | |
| | |o|o|o|o|o|o| |
| | |o|o|o|o|o|o| |
| | |o|o|o|o|o|o| |
| |o|o|o|o|o|o|o| |
| |o|o|o|o|o|o|o| |
| |o|o|o|o|o|o|o| |</pre>
 
=={{header|JavaScript}}==
Works with NodeJs
<syntaxhighlight lang="javascript">const readline = require('readline');
 
/**
* Galton Box animation
* @param {number} layers The number of layers in the board
* @param {number} balls The number of balls to pass through
*/
const galtonBox = (layers, balls) => {
const speed = 100;
const ball = 'o';
const peg = '.';
const result = [];
 
const sleep = ms => new Promise(resolve => {
setTimeout(resolve,ms)
});
 
/**
* The board is represented as a 2D array.
* @type {Array<Array<string>>}
*/
const board = [...Array(layers)]
.map((e, i) => {
const sides = Array(layers - i).fill(' ');
const a = Array(i + 1).fill(peg).join(' ').split('');
return [...sides, ...a, ...sides];
});
 
/**
* @return {Array<string>}
*/
const emptyRow = () => Array(board[0].length).fill(' ');
 
/**
* @param {number} i
* @returns {number}
*/
const bounce = i => Math.round(Math.random()) ? i - 1 : i + 1;
 
/**
* Prints the current state of the board and the collector
*/
const show = () => {
readline.cursorTo(process.stdout, 0, 0);
readline.clearScreenDown(process.stdout);
board.forEach(e => console.log(e.join('')));
result.reverse();
result.forEach(e => console.log(e.join('')));
result.reverse();
};
 
 
/**
* Collect the result.
* @param {number} idx
*/
const appendToResult = idx => {
const row = result.find(e => e[idx] === ' ');
if (row) {
row[idx] = ball;
} else {
const newRow = emptyRow();
newRow[idx] = ball;
result.push(newRow);
}
};
 
/**
* Move the balls through the board
* @returns {boolean} True if the there are balls in the board.
*/
const iter = () => {
let hasNext = false;
[...Array(bordSize)].forEach((e, i) => {
const rowIdx = (bordSize - 1) - i;
const idx = board[rowIdx].indexOf(ball);
if (idx > -1) {
board[rowIdx][idx] = ' ';
const nextRowIdx = rowIdx + 1;
if (nextRowIdx < bordSize) {
hasNext = true;
const nextRow = board[nextRowIdx];
nextRow[bounce(idx)] = ball;
} else {
appendToResult(idx);
}
}
});
return hasNext;
};
 
/**
# show the statistics
* Add a ball to mythe $padding = 0;board.
* @returns {number} The number of balls left to add.
while any(@stats) > 0 {
*/
$padding++;
const addBall = () => {
print " ";
board[0][apex] = ball;
@stats = do for @stats -> $stat {
balls = balls - 1;
given $stat {
return balls;
when 1 {
};
print "\c[UPPER HALF BLOCK]";
 
$stat - 1;
board.unshift(emptyRow());
}
result.unshift(emptyRow());
when * <= 0 {
 
print " ";
const bordSize = board.length;
0
const apex = board[1].indexOf(peg);
}
 
default {
/**
print "\c[FULL BLOCK]";
* Run the animation
$stat - 2;
*/
}
(async () => {
while (addBall()) {
await sleep(speed).then(show);
iter();
}
await sleep(speed).then(show);
while (iter()) {
await sleep(speed).then(show);
}
await sleep(speed).then(show);
})();
 
 
};
 
galtonBox(12, 50);</syntaxhighlight>
{{out}}
<pre>
.
. .
. . .
. . . .
. . . . .
. . . . . .
. . . . . . .
. . . . . . . .
. . . . . . . . .
. . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . .
o
o
o
o
o o
o o
o o
o o o
o o o
o o o o
o o o o
o o o o o
o o o o o o
o o o o o o o
o o o o o o o o </pre>
 
=={{header|Julia}}==
This is a proof of concept code. It does not use external packages. The ASCII animation is running in the stdout terminal, which has to be at least 28 character wide, and 40 character high, and accept ANSI control sequences for positioning the cursor, and clearing the screen (e.g. the Windows console, or ConEmu).
 
6 pins in 6 rows are hard coded. The next ball is released at the press of the Enter key. Keep it depressed for continuous running.
The balls are randomly deflected left or right at hitting the pins, and they fall to the bins at the bottom, which extend downwards.
The timer function sets the speed of the animation. Change the "interval" parameter to larger values for slower movement.
Pressing x then Enter exits, other keys are ignored.
{{works with|Julia|1.0}}
 
<syntaxhighlight lang="julia">using Random
function drawball(timer)
global r, c, d
print("\e[$r;$(c)H ") # clear last ball position (r,c)
if (r+=1) > 14
close(timer)
b = (bin[(c+2)>>2] += 1)# update count in bin
print("\e[$b;$(c)Ho") # lengthen bar of balls in bin
else
r in 3:2:13 && c in 17-r:4:11+r && (d = 2bitrand()-1)
print("\e[$r;$(c+=d)Ho")# show ball moving in direction d
end
end
 
print("\e[2J") # clear screen
for r = 3:2:13, c = 17-r:4:11+r # 6 pins in 6 rows
print("\e[$r;$(c)H^") # draw pins
end
print("\e[15;2H-------------------------")
 
bin = fill(15,7) # positions of top of bins
while "x" != readline() >= "" # x-Enter: exit, {keys..}Enter: next ball
global r,c,d = 0,14,0
t = Timer(drawball, 0, interval=0.1)
while r < 15 sleep(0.01) end
print("\e[40;1H") # move cursor far down
end</syntaxhighlight>
{{out}}
<pre>
^
 
^ ^
 
^ ^ ^
 
^ ^ ^ ^
 
^ ^ ^ ^ ^
 
^ ^ ^ ^ ^ ^
 
--------------------------
o o o o o o o
o o o o o
o o o o
o o o o
o o o o
o o o
o o
o o
o o
o o
o
o
o
o
o</pre>
 
=={{header|Kotlin}}==
{{trans|D}}
<syntaxhighlight lang="scala">// version 1.2.10
 
import java.util.Random
 
val boxW = 41 // Galton box width.
val boxH = 37 // Galton box height.
val pinsBaseW = 19 // Pins triangle base.
val nMaxBalls = 55 // Number of balls.
 
val centerH = pinsBaseW + (boxW - pinsBaseW * 2 + 1) / 2 - 1
val rand = Random()
 
enum class Cell(val c: Char) {
EMPTY(' '),
BALL('o'),
WALL('|'),
CORNER('+'),
FLOOR('-'),
PIN('.')
}
 
/* Galton box. Will be printed upside down. */
val box = List(boxH) { Array<Cell>(boxW) { Cell.EMPTY } }
 
class Ball(var x: Int, var y: Int) {
 
init {
require(box[y][x] == Cell.EMPTY)
box[y][x] = Cell.BALL
}
 
fun doStep() {
if (y <= 0) return // Reached the bottom of the box.
val cell = box[y - 1][x]
when (cell) {
Cell.EMPTY -> {
box[y][x] = Cell.EMPTY
y--
box[y][x] = Cell.BALL
}
 
Cell.PIN -> {
box[y][x] = Cell.EMPTY
y--
if (box[y][x - 1] == Cell.EMPTY && box[y][x + 1] == Cell.EMPTY) {
x += rand.nextInt(2) * 2 - 1
box[y][x] = Cell.BALL
return
}
else if (box[y][x - 1] == Cell.EMPTY) x++
else x--
box[y][x] = Cell.BALL
}
 
else -> {
// It's frozen - it always piles on other balls.
}
say "";
}
say "" for $padding...^10;
}
say @output.join("");
}
 
fun initializeBox() {
sub simulate($coins is copy) {
my// $aliveSet =ceiling True;and floor:
box[0][0] = Cell.CORNER
box[0][boxW - 1] = Cell.CORNER
for (i in 1 until boxW - 1) box[0][i] = Cell.FLOOR
for (i in 0 until boxW) box[boxH - 1][i] = box[0][i]
 
sub// hits-peg($x,Set $y) {walls:
for (r in 1 until boxH - 1) {
if 3 <= $y < 3 + $row-count and -($y - 2) <= $x <= $y - 2 {
box[r][0] = return not ($x - $y) %% 2;Cell.WALL
box[r][boxW - 1] = Cell.WALL
}
 
// Set pins:
for (nPins in 1..pinsBaseW) {
for (pin in 0 until nPins) {
box[boxH - 2 - nPins][centerH + 1 - nPins + pin * 2] = Cell.PIN
}
return False;
}
}
 
fun drawBox() {
my @coins = Int xx (3 + $row-count + 4);
my @stats = 0 xxfor ($row-count *in 2box.reversed();) {
for (i in row.indices) print(row[i].c)
# this line will dispense coins until turned off.
@coins[0] = 0; println()
while $alive {}
}
$alive = False;
 
# if a coin falls through the bottom, count it
fun main(args: Array<String>) {
given @coins[*-1] {
initializeBox()
when *.defined {
val balls = mutableListOf<Ball>()
@stats[$_ + $row-count]++;
for (i in 0 until nMaxBalls + boxH) }{
println("\nStep $i:")
if (i < nMaxBalls) balls.add(Ball(centerH, boxH - 2)) // Add ball.
drawBox()
 
// Next step for the simulation.
// Frozen balls are kept in balls list for simplicity
for (b in balls) b.doStep()
}
}</syntaxhighlight>
 
Sample output (showing final step only):
<pre>
Step 91:
+---------------------------------------+
| |
| . |
| . . |
| . . . |
| . . . . |
| . . . . . |
| . . . . . . |
| . . . . . . . |
| . . . . . . . . |
| . . . . . . . . . |
| . . . . . . . . . . |
| . . . . . . . . . . . |
| . . . . . . . . . . . . |
| . . . . . . . . . . . . . |
| . . . . . . . . . . . . . . |
| . . . . . . . . . . . . . . . |
| . . . . . . . . . . . . . . . . |
| . . . . . . . . . . . . . . . . . |
| . . . . . . . . . . . . . . . . . . |
| . . . . . . . . . . . . . . . . . . . |
| |
| |
| o |
| o |
| o o |
| o o |
| o o o |
| o o o |
| o o o |
| o o o |
| o o o o |
| o o o o o |
| o o o o o o o |
| o o o o o o o o o |
| o o o o o o o o o o o o |
+---------------------------------------+
</pre>
 
=={{header|Liberty BASIC}}==
User can choose the number of balls to run through the simulation.
<syntaxhighlight lang="lb">
[setup]
nomainwin
 
speed=50
 
prompt "Number of balls to drop: ";cycleMax
cycleMax=abs(int(cycleMax))
 
'create window
WindowWidth=400
WindowHeight=470
UpperLeftX=1
UpperLeftY=1
graphicbox #1.gb, 10, 410,370,25
open "Galton Machine" for graphics_nf_nsb as #1
#1 "trapclose [q];down;fill black;flush"
#1.gb "font courier_new 12"
 
'Create graphical sprites
#1 "getbmp bg 1 1 400 600"
#1 "place 0 0; color white;backcolor white;boxfilled 17 17;place 8 8;color black;backcolor black;circlefilled 8;"
#1 "place 8 25;color white;backcolor white;circlefilled 8;"
#1 "getbmp ball 0 0 17 34"
#1 "place 8 25;color red;backcolor red;circlefilled 8;"
#1 "getbmp pin 0 0 17 34"
#1 "background bg"
 
'add sprites to program
for pinCount = 1 to 28
#1 "addsprite pin";pinCount;" pin;spriteround pin";pinCount
next pinCount
 
for ballCount = 1 to 7
#1 "addsprite ball";ballCount;" ball;spriteround ball";ballCount
next ballCount
 
'place pins on page
for y = 1 to 7
for x = 1 to y
pin=pin+1
xp=200-x*50+y*25
yp=y*35+100
#1 "spritexy pin";pin;" ";xp;" ";yp
#1 "drawsprites"
next x
next y
 
'set balls in motion
for a = 1 to 7
#1 "spritexy ball";a;" 174 ";a*60-350
#1 "spritemovexy ball";a;" 0 5"
next a
 
[start] 'update every 50ms - lower number means faster updates
timer speed, [move]
wait
 
[move] 'cycle through the sprites to check for contact with pins or dropping off board
#1 "drawsprites"
for ballNum = 1 to 7
gosub [checkCollide]
next ballNum
timer speed, [move]
wait
 
[checkCollide] 'check for contact with pins or dropping off board
timer 0
#1 "spritexy? ball";ballNum;" x y" 'get current ball position
#1 "spritecollides ball";ballNum;" hits$" 'collect any collisions
if hits$<>"" then 'any collisions? if so...
direction = rnd(1)
'randomly bounce either left or right
if direction >0.4999999 then #1 "spritexy ball";ballNum;" ";x+25;" ";y else #1 "spritexy ball";ballNum;" ";x-25;" ";y
#1 "spritemovexy ball";ballNum;" 0 5"'set ball in motion again
end if
#1 "spritexy? ball";ballNum;" x y" 'get current ball position
if y > 400 then 'if ball has dropped off board, then...
select case 'figure out which slot it has landed in and increment the counter for that slot
case x<49
slot(1)=slot(1)+1
case x=49
slot(2)=slot(2)+1
case x=99
slot(3)=slot(3)+1
case x=149
slot(4)=slot(4)+1
case x=199
slot(5)=slot(5)+1
case x=249
slot(6)=slot(6)+1
case x=299
slot(7)=slot(7)+1
case x>299
slot(8)=slot(8)+1
end select
for a = 1 to 8 'write the slot counts in the small graphic box
update$="place "+str$((a-1)*48+1)+" 20;\"+str$(slot(a))
#1.gb, update$
next a
#1 "spritexy ball";ballNum;" 174 ";0-10 'reposition the sprite just off the top of the screen
#1 "spritemovexy ball";ballNum;" 0 5" 'set the ball in motion again
cycles = cycles + 1 'increment the fallen ball count
if cycles >= cycleMax then
timer 0 'stop animation
'make the visible balls go away
for a = 1 to 7
#1 "spritexy ball";a;" 174 700"
#1 "spritemovexy ball";a;" 0 0"
next a
#1 "drawsprites"
notice "Complete"
wait
end if
end if
return
 
[q]
close #1
'It is IMPORTANT to unload the bitmaps and clear memory
unloadbmp "pin"
unloadbmp "ball"
unloadbmp "bg"
end
</syntaxhighlight>
 
=={{header|Lua}}==
Uses Bitmap class [[Bitmap#Lua|here]], with an ASCII pixel representation, then extending..
<syntaxhighlight lang="lua">Bitmap.render = function(self)
for y = 1, self.height do
print(table.concat(self.pixels[y], " "))
end
end
 
-- globals (tweak here as desired)
math.randomseed(os.time())
local W, H, MIDX = 15, 40, 7
local bitmap = Bitmap(W, H)
local AIR, PIN, BALL, FLOOR = ".", "▲", "☻", "■"
local nballs, balls = 60, {}
local frame, showEveryFrame = 1, false
 
-- the game board:
bitmap:clear(AIR)
for row = 1, 7 do
for col = 0, row-1 do
bitmap:set(MIDX-row+col*2+1, 1+row*2, PIN)
end
end
for col = 0, W-1 do
bitmap:set(col, H-1, FLOOR)
end
 
-- ball class
Ball = {
new = function(self, x, y, bitmap)
local instance = setmetatable({ x=x, y=y, bitmap=bitmap, alive=true }, self)
return instance
end,
update = function(self)
if not self.alive then return end
self.bitmap:set(self.x, self.y, AIR)
local newx, newy = self.x, self.y+1
local below = self.bitmap:get(newx, newy)
if below==PIN then
newx = newx + (math.random(2)-1)*2-1
end
local there = self.bitmap:get(newx, newy)
if there==AIR then
self.x, self.y = newx, newy
else
self.alive = false
end
self.bitmap:set(self.x, self.y, BALL)
end,
}
Ball.__index = Ball
setmetatable(Ball, { __call = function (t, ...) return t:new(...) end })
 
-- simulation:
local function spawn()
if nballs > 0 then
balls[#balls+1] = Ball(MIDX, 0, bitmap)
nballs = nballs - 1
end
end
 
spawn()
while #balls > 0 do
if frame%2==0 then spawn() end
alive = {}
for _,ball in ipairs(balls) do
ball:update()
if ball.alive then alive[#alive+1]=ball end
end
balls = alive
if frame%50==0 or #alive==0 or showEveryFrame then
print("FRAME "..frame..":")
bitmap:render()
end
frame = frame + 1
end</syntaxhighlight>
{{out}}
<pre>FRAME 50:
. . . . . . . . . . . . . . .
. . . . . . . ☻ . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . ☻ ▲ . . . . . . .
. . . . . . . . . . . . . . .
. . . . . ☻ ▲ . ▲ . . . . . .
. . . . . . . . . . . . . . .
. . . . . ▲ ☻ ▲ . ▲ . . . . .
. . . . . . . . . . . . . . .
. . . . ▲ . ▲ . ▲ ☻ ▲ . . . .
. . . . . . . . . . . . . . .
. . . ▲ . ▲ ☻ ▲ . ▲ . ▲ . . .
. . . . . . . . . . . . . . .
. . ▲ . ▲ . ▲ ☻ ▲ . ▲ . ▲ . .
. . . . . . . . . . . . . . .
. ▲ . ▲ . ▲ . ▲ ☻ ▲ . ▲ . ▲ .
. . . . . . . . . . . . . . .
. . . . ☻ . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . ☻ . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . ☻ . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . ☻ . . . .
. . . . . . . . . . . . . . .
. . . . . . ☻ . . . . . . . .
. . . . . . . . . . . . . . .
. . . . ☻ . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . ☻ . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . ☻ . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . ☻
. . . . . . . . . . . . . . .
. . . . . . ☻ . . . . . . . .
. . . . . . ☻ . . . . . . . .
. . ☻ . . . ☻ . . . ☻ . . . .
. . ☻ . . . ☻ . ☻ . ☻ . . . .
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
FRAME 100:
. . . . . . . . . . . . . . .
. . . . . . . ☻ . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . ☻ ▲ . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . ▲ ☻ ▲ . . . . . .
. . . . . . . . . . . . . . .
. . . . . ▲ ☻ ▲ . ▲ . . . . .
. . . . . . . . . . . . . . .
. . . . ▲ . ▲ . ▲ ☻ ▲ . . . .
. . . . . . . . . . . . . . .
. . . ▲ . ▲ . ▲ ☻ ▲ . ▲ . . .
. . . . . . . . . . . . . . .
. . ▲ . ▲ ☻ ▲ . ▲ . ▲ . ▲ . .
. . . . . . . . . . . . . . .
. ▲ ☻ ▲ . ▲ . ▲ . ▲ . ▲ . ▲ .
. . . . . . . . . . . . . . .
. . . . . . ☻ . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . ☻ . . . . . . . .
. . . . . . . . . . . . . . .
. . . . ☻ . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . ☻ . . . . . .
. . . . . . . . . . . . . . .
. . . . . . ☻ . ☻ . . . . . .
. . . . . . ☻ . . . . . . . .
. . . . ☻ . ☻ . . . . . . . .
. . . . . . ☻ . . . . . . . .
. . . . . . ☻ . ☻ . . . . . .
. . . . . . ☻ . . . . . . . .
. . . . ☻ . ☻ . . . . . . . .
. . . . . . ☻ . ☻ . . . . . .
. . . . . . ☻ . ☻ . . . . . .
. . . . . . ☻ . ☻ . ☻ . . . .
. . . . ☻ . ☻ . ☻ . ☻ . . . .
. . ☻ . ☻ . ☻ . ☻ . ☻ . . . .
. . ☻ . ☻ . ☻ . ☻ . ☻ . . . .
☻ . ☻ . ☻ . ☻ . ☻ . ☻ . . . ☻
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
FRAME 147:
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . ▲ . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . ▲ . ▲ . . . . . .
. . . . . . . . . . . . . . .
. . . . . ▲ . ▲ . ▲ . . . . .
. . . . . . . . . . . . . . .
. . . . ▲ . ▲ . ▲ . ▲ . . . .
. . . . . . . . . . . . . . .
. . . ▲ . ▲ . ▲ . ▲ . ▲ . . .
. . . . . . . . . . . . . . .
. . ▲ . ▲ . ▲ . ▲ . ▲ . ▲ . .
. . . . . . . . . . . . . . .
. ▲ . ▲ . ▲ . ▲ . ▲ . ▲ . ▲ .
. . . . . . ☻ . . . . . . . .
. . . . . . ☻ . . . . . . . .
. . . . . . ☻ . . . . . . . .
. . . . . . ☻ . . . . . . . .
. . . . . . ☻ . . . . . . . .
. . . . . . ☻ . . . . . . . .
. . . . . . ☻ . . . . . . . .
. . . . . . ☻ . . . . . . . .
. . . . . . ☻ . . . . . . . .
. . . . . . ☻ . ☻ . . . . . .
. . . . . . ☻ . ☻ . . . . . .
. . . . . . ☻ . ☻ . . . . . .
. . . . . . ☻ . ☻ . . . . . .
. . . . . . ☻ . ☻ . . . . . .
. . . . ☻ . ☻ . ☻ . . . . . .
. . . . ☻ . ☻ . ☻ . . . . . .
. . . . ☻ . ☻ . ☻ . . . . . .
. . . . ☻ . ☻ . ☻ . ☻ . . . .
. . ☻ . ☻ . ☻ . ☻ . ☻ . . . .
. . ☻ . ☻ . ☻ . ☻ . ☻ . . . .
. . ☻ . ☻ . ☻ . ☻ . ☻ . . . .
. . ☻ . ☻ . ☻ . ☻ . ☻ . . . .
☻ . ☻ . ☻ . ☻ . ☻ . ☻ . ☻ . ☻
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
</pre>
 
=={{header|Mathematica}} / {{header|Wolfram Language}}==
<syntaxhighlight lang="mathematica">ClearAll[MakePathFunction]
MakePathFunction[{path_, acumpath_}] :=
Module[{f1, f2, f3, pf, n = Length[path]},
f1 = MapThread[{#1/2, #2 + 0.5 < z <= #2 + 1} &, {acumpath,
n - Range[n + 1]}];
f2 = MapThread[{#1/2 + #2 Sqrt[1/4 - (z - #3)^2], #3 <
z <= #3 + 1/2} &, {acumpath // Most, path, n - Range[n]}];
f3 = {{acumpath[[-1]]/2, z <= 0}};
pf = Piecewise[Evaluate[Join[f1, f2, f3]], 0];
pf
]
MakeScene[pfs_List, zfinals_List, n_Integer, t_] :=
Module[{durations, accumduration, if, part, fixed, relt},
durations = n - zfinals;
accumduration = Accumulate[Prepend[durations, 0]];
if = Interpolation[{accumduration, Range[Length[zfinals] + 1]} //
Transpose, InterpolationOrder -> 1];
part = Floor[if[t]];
If[part > 0,
fixed = Table[{pfs[[i]], z} /. z -> zfinals[[i]], {i, part - 1}];
,
fixed = {};
];
relt = t - accumduration[[part]];
relt = n - relt;
Append[fixed, {pfs[[part]] /. z -> relt, relt}]
]
SeedRandom[1234];
n = 6;
m = 150;
r = 0.25; (* fixed *)
dots =
Catenate@Table[{# - i/2 - 1/2, n - i} & /@ Range[i], {i, n}];
g = Graphics[Disk[#, r] & /@ dots, Axes -> True];
 
paths = RandomChoice[{-1, 1}, {m, n}];
paths = {#, Accumulate[Prepend[#, 0]]} & /@ paths;
xfinals = paths[[All, 2, -1]];
types = DeleteDuplicates[xfinals];
zfinals = ConstantArray[0, Length[paths]];
Do[
pos = Flatten[Position[xfinals, t]];
zfinals[[pos]] += 0.5 Range[Length[pos]];
,
{t, types}
];
max = Max[zfinals] + 1;
zfinals -= max;
pfs = MakePathFunction /@ paths;
Manipulate[
Graphics[{Disk[#, r] & /@ dots, Red,
Disk[#, r] & /@ MakeScene[pfs, zfinals, n, t]},
PlotRange -> {{-n, n}, {Min[zfinals] - 1, n + 2}},
ImageSize -> 150], {t, 0, Total[n - zfinals] - 0.001}]</syntaxhighlight>
 
=={{header|Nim}}==
{{trans|Go}}
<syntaxhighlight lang="nim">import random, strutils
 
const
BoxW = 41 # Galton box width.
BoxH = 37 # Galton box height.
PinsBaseW = 19 # Pins triangle base.
NMaxBalls = 55 # Number of balls.
 
const CenterH = PinsBaseW + (BoxW - (PinsBaseW * 2 - 1)) div 2 - 1
 
type
 
Cell = enum
cEmpty = " "
cBall = "o"
cWall = "|"
cCorner = "+"
cFloor = "-"
cPin = "."
 
# Galton box. Will be printed upside-down.
Box = array[BoxH, array[BoxW, Cell]]
 
Ball = ref object
x, y: int
 
 
func initBox(): Box =
 
# Set ceiling and floor.
result[0][0] = cCorner
result[0][^1] = cCorner
for i in 1..(BoxW - 2):
result[0][i] = cFloor
result[^1] = result[0]
 
# Set walls.
for i in 1..(BoxH - 2):
result[i][0] = cWall
result[i][^1] = cWall
 
# Set rest to Empty initially.
for i in 1..(BoxH - 2):
for j in 1..(BoxW - 2):
result[i][j] = cEmpty
 
# Set pins.
for nPins in 1..PinsBaseW:
for p in 0..<nPins:
result[BoxH - 2 - nPins][CenterH + 1 - nPins + p * 2] = cPin
 
 
func newBall(box: var Box; x, y: int): Ball =
 
doAssert box[y][x] == cEmpty, "Tried to create a new ball in a non-empty cell"
result = Ball(x: x, y: y)
box[y][x] = cBall
 
 
proc doStep(box: var Box; b: Ball) =
 
if b.y <= 0:
return # Reached the bottom of the box.
 
case box[b.y-1][b.x]
 
of cEmpty:
box[b.y][b.x] = cEmpty
dec b.y
box[b.y][b.x] = cBall
 
of cPin:
box[b.y][b.x] = cEmpty
dec b.y
if box[b.y][b.x-1] == cEmpty and box[b.y][b.x+1] == cEmpty:
inc b.x, 2 * rand(1) - 1
elif box[b.y][b.x-1] == cEmpty:
inc b.x
else:
dec b.x
box[b.y][b.x] = cBall
 
else:
# It's frozen - it always piles on other balls.
discard
 
 
proc draw(box: Box) =
for r in countdown(BoxH - 1, 0):
echo box[r].join()
 
 
#———————————————————————————————————————————————————————————————————————————————————————————————————
 
randomize()
var box = initBox()
var balls: seq[Ball]
 
for i in 0..<(NMaxBalls + BoxH):
 
echo "Step ", i, ':'
if i < NMaxBalls:
balls.add box.newBall(CenterH, BoxH - 2)
box.draw()
 
# Next step for the simulation.
# Frozen balls are kept in balls slice for simplicity.
for ball in balls:
box.doStep(ball)</syntaxhighlight>
 
{{out}}
Sample output (showing last step only):
<pre>Step 91:
+---------------------------------------+
| |
| . |
| . . |
| . . . |
| . . . . |
| . . . . . |
| . . . . . . |
| . . . . . . . |
| . . . . . . . . |
| . . . . . . . . . |
| . . . . . . . . . . |
| . . . . . . . . . . . |
| . . . . . . . . . . . . |
| . . . . . . . . . . . . . |
| . . . . . . . . . . . . . . |
| . . . . . . . . . . . . . . . |
| . . . . . . . . . . . . . . . . |
| . . . . . . . . . . . . . . . . . |
| . . . . . . . . . . . . . . . . . . |
| . . . . . . . . . . . . . . . . . . . |
| |
| |
| |
| o |
| o |
| o |
| o o |
| o o |
| o o o |
| o o o o o |
| o o o o o o o |
| o o o o o o o |
| o o o o o o o o |
| o o o o o o o o o |
| o o o o o o o o o |
+---------------------------------------+</pre>
 
=={{header|Perl}}==
=== translated from Raku ===
Output shows of final state for a run with 50 coins.
{{trans|Raku}}
<syntaxhighlight lang="perl">use strict;
use warnings;
 
use List::Util 'any';
use Time::HiRes qw(sleep);
use List::AllUtils <pairwise pairs>;
 
use utf8;
binmode STDOUT, ':utf8';
 
my $coins = shift || 100;
my $peg_lines = shift || 13;
my $row_count = $peg_lines;
my $peg = '^';
my @coin_icons = ("\N{UPPER HALF BLOCK}", "\N{LOWER HALF BLOCK}");
 
my @coins = (undef) x (3 + $row_count + 4);
my @stats = (0) x ($row_count * 2);
$coins[0] = 0; # initialize with first coin
 
while (1) {
my $active = 0;
# if a coin falls through the bottom, count it
$stats[$coins[-1] + $row_count]++ if defined $coins[-1];
 
# move every coin down one row
for my $line (reverse 1..(3+$row_count+3) ) {
my $coinpos = $coins[$line - 1];
 
#$coins[$line] = do if (! defined $coinpos) x
if (! defined $coinpos) {
$coins[$line] = undef
} elsif (hits_peg($coinpos, $line)) {
# when a coin from above hits a peg, it will bounce to either side.
$active = 1;
$coinpos += rand() < .5 ? -1 : 1;
$coins[$line] = $coinpos
} else {
# if there was a coin above, it will fall to this position.
$active = 1;
$coins[$line] = $coinpos;
}
}
# let the coin dispenser blink and turn it off if we run out of coins
if (defined $coins[0]) {
$coins[0] = undef;
} elsif (--$coins > 0) {
$coins[0] = 0
}
 
for (<0 1>) {
# move every coin down one row
display_board(\@coins, \@stats, $_);
for ( 3 + $row-count + 3 )...1 -> $line {
sleep my $coinpos = @coins[$line - .1];
}
exit unless $active;
}
 
sub display_board {
@coins[$line] = do if not $coinpos.defined {
my($p_ref, $s_ref, $halfstep) = @_;
Nil
my @positions = @$p_ref;
} elsif hits-peg($coinpos, $line) {
my @stats = @$s_ref;
# when a coin from above hits a peg, it will bounce to either side.
my $coin = $alive = Truecoin_icons[$halfstep];
 
($coinpos - 1, $coinpos + 1).pick;
my @board = } elsedo {
my @tmpl;
# if there was a coin above, it will fall to this position.
 
$alive = True;
sub out $coinpos;{
}my(@stuff) = split '', shift;
my @line;
push @line, ord($_) for @stuff;
[@line];
}
 
# let the coin dispenser blink and turn it off if we run out of coins
push @tmpl, out(" " . " "x(2 * $row_count)) for 1..3;
if @coins[0].defined {
my @coins[0]a = Nilreverse 1..$row_count;
}my elsif@b --= 1..$coins > 0 {row_count;
my @pairs = pairwise @coins[0]{ =($a, 0$b) } @a, @b;
for ( pairs @pairs ) {
my ( $spaces, $pegs ) = @$_;
push @tmpl, out(" " . " "x$spaces . join(' ',($peg) x $pegs) . " "x$spaces);
}
push @tmpl, out(" " . " "x(2 * $row_count)) for 1..4;
@tmpl;
};
 
my $midpos = $row_count + 2;
# smooth out the two halfsteps of the animation
my $start-time;
ENTER { $start-time = now }
my $wait-time = now - $start-time;
 
our @output;
sleep 0.1 - $wait-time if $wait-time < 0.1;
{
for @coin-icons.keys {
# collect all the output and output it all at once at the end
sleep $wait-time max 0.1;
sub printnl { display-boardmy($foo) = @coins,_; push @statsoutput, $_);foo . "\n" }
sub printl { my($foo) = @_; push @output, $foo }
 
# make some space above the picture
printnl("") for 0..9;
 
# place the coins
for my $line (0..$#positions) {
my $pos = $positions[$line];
next unless defined $pos;
$board[$line][$pos + $midpos] = ord($coin);
}
# output the board with its coins
for my $line (@board) {
printnl join '', map { chr($_) } @$line;
}
 
# show the statistics
my $padding = 0;
while (any {$_> 0} @stats) {
$padding++;
printl " ";
for my $i (0..$#stats) {
if ($stats[$i] == 1) {
printl "\N{UPPER HALF BLOCK}";
$stats[$i]--;
} elsif ($stats[$i] <= 0) {
printl " ";
$stats[$i] = 0
} else {
printl "\N{FULL BLOCK}";
$stats[$i]--; $stats[$i]--;
}
}
printnl("");
}
printnl("") for $padding..(10-1);
}
 
print join('', @output) . "\n";
}
 
sub hits_peg {
sub MAIN($coins = 20, $peg-lines = 6) {
my($row-countx, $y) = $peg-lines@_;
3 <= $y && $y < (3 + $row_count) and -($y - 2) <= $x && $x <= $y - 2
simulate($coins);
? not 0 == ($x - $y) % 2
}</lang>
: 0
}</syntaxhighlight>
{{out}}
<pre> ^
^ ^
^ ^ ^
^ ^ ^ ^
^ ^ ^ ^ ^
^ ^ ^ ^ ^ ^
^ ^ ^ ^ ^ ^ ^
 
 
 
 
█ █ █ █ █ █
█ █ █ █
█ █ █ █
█ █ █ █
█ █ █ ▀
▀ █ █
</pre>
 
=== native Perl ===
Runs until a bottom column overflows.
<syntaxhighlight lang="perl">#!/usr/bin/perl
 
use strict; # https://rosettacode.org/wiki/Galton_box_animation
use warnings;
$| = 1;
 
my $width = shift // 7;
my $bottom = 15;
my $blank = ' ' x ( 2 * $width + 1 ) . "\n";
my $line = ' ' x $width . '*' . ' ' x $width . "\n";
local $_ = join '', $blank x 5 . $line,
map({ $line =~ s/ \* (.*) /* * $1/; ($blank, $line) } 2 .. $width ),
$blank x $bottom;
 
my $gap = / \n/ && $-[0];
my $gl = qr/.{$gap}/s;
my $g = qr/.$gl/s;
my $center = $gap >> 1;
my %path = ('O* ' => 'O*X', ' *O' => 'X*O');
 
print "\e[H\e[J$_";
while( not /(?:O$g){$bottom}/ )
{
my $changes = s!O($gl)( \* |O\* | \*O)! " $1" .
($path{$2} // (rand(2) < 1 ? "X* " : " *X")) !ge +
s/O($g) / $1X/g +
s/^ {$center}\K ($g $g) /O$1 /;
tr/X/O/;
print "\e[H$_";
$changes or last;
select undef, undef, undef, 0.05;
}</syntaxhighlight>
{{out}}
<pre>
O
*O
* *
O
* * *
*O* * *
* * * * *
O
* * * * * *
* * *O* * * *
O
O
O O
O
O
O
O
O O
O O
O O O
O O O O
O O O O
O O O O
O O O O
O O O O O O
</pre>
 
=={{header|Phix}}==
=== console ===
First, a console version:
<!--<syntaxhighlight lang="phix">(phixonline)-->
<span style="color: #008080;">without</span> <span style="color: #008080;">js</span> <span style="color: #000080;font-style:italic;">-- clear_screen(), text_color(), position(), sleep(), get_key()...</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">balls</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">80</span>
<span style="color: #7060A8;">clear_screen</span><span style="color: #0000FF;">()</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">screen</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #008000;">' '</span><span style="color: #0000FF;">,</span><span style="color: #000000;">23</span><span style="color: #0000FF;">),</span><span style="color: #000000;">12</span><span style="color: #0000FF;">)</span>
<span style="color: #0000FF;">&</span> <span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">join</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #008000;">':'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">12</span><span style="color: #0000FF;">)),</span><span style="color: #000000;">12</span><span style="color: #0000FF;">)</span>
<span style="color: #0000FF;">&</span> <span style="color: #0000FF;">{</span><span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #008000;">'.'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">23</span><span style="color: #0000FF;">)},</span>
<span style="color: #000000;">Pxy</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">({</span><span style="color: #000000;">12</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">},</span><span style="color: #000000;">balls</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">peg</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #000000;">10</span> <span style="color: #008080;">do</span>
<span style="color: #000000;">screen</span><span style="color: #0000FF;">[</span><span style="color: #000000;">peg</span><span style="color: #0000FF;">+</span><span style="color: #000000;">2</span><span style="color: #0000FF;">][</span><span style="color: #000000;">13</span><span style="color: #0000FF;">-</span><span style="color: #000000;">peg</span><span style="color: #0000FF;">..</span><span style="color: #000000;">11</span><span style="color: #0000FF;">+</span><span style="color: #000000;">peg</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">join</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #008000;">'.'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">peg</span><span style="color: #0000FF;">))</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #7060A8;">puts</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">join</span><span style="color: #0000FF;">(</span><span style="color: #000000;">screen</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"\n"</span><span style="color: #0000FF;">))</span>
<span style="color: #000000;">text_color</span><span style="color: #0000FF;">(</span><span style="color: #000000;">BRIGHT_RED</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">bool</span> <span style="color: #000000;">moved</span> <span style="color: #0000FF;">=</span> <span style="color: #004600;">true</span>
<span style="color: #004080;">integer</span> <span style="color: #7060A8;">top</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">' '</span> <span style="color: #000080;font-style:italic;">-- (new drop every other iteration)</span>
<span style="color: #008080;">while</span> <span style="color: #000000;">moved</span> <span style="color: #008080;">or</span> <span style="color: #7060A8;">top</span><span style="color: #0000FF;">!=</span><span style="color: #008000;">' '</span> <span style="color: #008080;">do</span>
<span style="color: #000000;">moved</span> <span style="color: #0000FF;">=</span> <span style="color: #004600;">false</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #000000;">balls</span> <span style="color: #008080;">do</span>
<span style="color: #004080;">integer</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">Px</span><span style="color: #0000FF;">,</span><span style="color: #000000;">Py</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">Pxy</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">Py</span><span style="color: #0000FF;">!=</span><span style="color: #000000;">1</span> <span style="color: #008080;">or</span> <span style="color: #7060A8;">top</span><span style="color: #0000FF;">=</span><span style="color: #008000;">' '</span> <span style="color: #008080;">then</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">Dx</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">Dy</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">screen</span><span style="color: #0000FF;">[</span><span style="color: #000000;">Py</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">Px</span><span style="color: #0000FF;">]=</span><span style="color: #008000;">' '</span> <span style="color: #008080;">then</span> <span style="color: #000080;font-style:italic;">-- can vertical?</span>
<span style="color: #000000;">Dy</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">1</span>
<span style="color: #008080;">else</span>
<span style="color: #000000;">Dx</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">}[</span><span style="color: #7060A8;">rand</span><span style="color: #0000FF;">(</span><span style="color: #000000;">2</span><span style="color: #0000FF;">)]</span> <span style="color: #000080;font-style:italic;">-- try l;r or r;l</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">screen</span><span style="color: #0000FF;">[</span><span style="color: #000000;">Py</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">Px</span><span style="color: #0000FF;">+</span><span style="color: #000000;">Dx</span><span style="color: #0000FF;">]!=</span><span style="color: #008000;">' '</span> <span style="color: #008080;">then</span> <span style="color: #000000;">Dx</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">-</span><span style="color: #000000;">Dx</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">screen</span><span style="color: #0000FF;">[</span><span style="color: #000000;">Py</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">Px</span><span style="color: #0000FF;">+</span><span style="color: #000000;">Dx</span><span style="color: #0000FF;">]==</span><span style="color: #008000;">' '</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">Dy</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">1</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">Dy</span> <span style="color: #008080;">then</span>
<span style="color: #7060A8;">position</span><span style="color: #0000FF;">(</span><span style="color: #000000;">Py</span><span style="color: #0000FF;">,</span><span style="color: #000000;">Px</span><span style="color: #0000FF;">)</span> <span style="color: #7060A8;">puts</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">" "</span><span style="color: #0000FF;">)</span> <span style="color: #000000;">screen</span><span style="color: #0000FF;">[</span><span style="color: #000000;">Py</span><span style="color: #0000FF;">,</span><span style="color: #000000;">Px</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">' '</span>
<span style="color: #000000;">Px</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">Dx</span>
<span style="color: #000000;">Py</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">Dy</span>
<span style="color: #7060A8;">position</span><span style="color: #0000FF;">(</span><span style="color: #000000;">Py</span><span style="color: #0000FF;">,</span><span style="color: #000000;">Px</span><span style="color: #0000FF;">)</span> <span style="color: #7060A8;">puts</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"o"</span><span style="color: #0000FF;">)</span> <span style="color: #000000;">screen</span><span style="color: #0000FF;">[</span><span style="color: #000000;">Py</span><span style="color: #0000FF;">,</span><span style="color: #000000;">Px</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">'o'</span>
<span style="color: #000000;">Pxy</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">Px</span><span style="color: #0000FF;">,</span><span style="color: #000000;">Py</span><span style="color: #0000FF;">}</span>
<span style="color: #000000;">moved</span> <span style="color: #0000FF;">=</span> <span style="color: #004600;">true</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">Py</span><span style="color: #0000FF;">=</span><span style="color: #000000;">2</span> <span style="color: #008080;">then</span> <span style="color: #7060A8;">top</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">'o'</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #7060A8;">position</span><span style="color: #0000FF;">(</span><span style="color: #000000;">26</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">sleep</span><span style="color: #0000FF;">(</span><span style="color: #000000;">0.2</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #7060A8;">get_key</span><span style="color: #0000FF;">()!=-</span><span style="color: #000000;">1</span> <span style="color: #008080;">then</span> <span style="color: #008080;">exit</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #7060A8;">top</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">screen</span><span style="color: #0000FF;">[</span><span style="color: #000000;">2</span><span style="color: #0000FF;">][</span><span style="color: #000000;">12</span><span style="color: #0000FF;">]</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">while</span>
<!--</syntaxhighlight>-->
{{out}}
<pre>
.o
. .
.o. .
. . . .
. .o. . .
. . . . . .
. . . . .o. .
. . . . . . . .
. . . . .o. . . .
. . . . . . . . . .
: : : : :o: : : : : : :
: : : : : : : : : : : :
: : : : : : : : :o: : :
: : : : : : : : : : : :
: : : : : : : :o: : : :
: : : : : : : : : : : :
: : : : :o:o:o: : : : :
: : : : :o:o:o: : : : :
: : :o: :o:o:o: : : : :
: : : : :o:o:o:o: : : :
: : : :o:o:o:o:o: : : :
: : :o:o:o:o:o:o: : : :
.......................
</pre>
=== gui ===
Also, here is a slightly nicer and resize-able gui version:
{{libheader|Phix/pGUI}}
{{libheader|Phix/online}}
You can run this online [http://phix.x10.mx/p2js/galtonbox.htm here].
<!--<syntaxhighlight lang="phix">(phixonline)-->
<span style="color: #000080;font-style:italic;">--
-- demo\rosetta\GaltonBox.exw
-- ==========================
--
-- Author Pete Lomax, May 2017
--</span>
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">TITLE</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"Galton Box"</span>
<span style="color: #008080;">include</span> <span style="color: #000000;">pGUI</span><span style="color: #0000FF;">.</span><span style="color: #000000;">e</span>
<span style="color: #004080;">Ihandle</span> <span style="color: #000000;">dlg</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">canvas</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">timershow</span>
<span style="color: #004080;">cdCanvas</span> <span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">cdcanvas</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">brem</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">80</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">balls</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{{</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0</span><span style="color: #0000FF;">}}</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">bins</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #000000;">8</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">redraw_cb</span><span style="color: #0000FF;">(</span><span style="color: #004080;">Ihandle</span> <span style="color: #000080;font-style:italic;">/*ih*/</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">integer</span> <span style="color: #000080;font-style:italic;">/*posx*/</span><span style="color: #0000FF;">,</span> <span style="color: #000080;font-style:italic;">/*posy*/</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">integer</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">w</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">h</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupGetIntInt</span><span style="color: #0000FF;">(</span><span style="color: #000000;">canvas</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"DRAWSIZE"</span><span style="color: #0000FF;">),</span> <span style="color: #000000;">x</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">y</span>
<span style="color: #004080;">atom</span> <span style="color: #000000;">xx</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">yy</span>
<span style="color: #7060A8;">cdCanvasActivate</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">cdCanvasClear</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">)</span>
<span style="color: #000080;font-style:italic;">-- draw the pins, then balls, then bins</span>
<span style="color: #7060A8;">cdCanvasSetForeground</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">,</span> <span style="color: #004600;">CD_DARK_GREEN</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">pinsize</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">min</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">floor</span><span style="color: #0000FF;">(</span><span style="color: #000000;">h</span><span style="color: #0000FF;">/</span><span style="color: #000000;">40</span><span style="color: #0000FF;">),</span><span style="color: #7060A8;">floor</span><span style="color: #0000FF;">(</span><span style="color: #000000;">w</span><span style="color: #0000FF;">/</span><span style="color: #000000;">50</span><span style="color: #0000FF;">))</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">y</span><span style="color: #0000FF;">=</span><span style="color: #000000;">4</span> <span style="color: #008080;">to</span> <span style="color: #000000;">16</span> <span style="color: #008080;">by</span> <span style="color: #000000;">2</span> <span style="color: #008080;">do</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">x</span><span style="color: #0000FF;">=-(</span><span style="color: #000000;">y</span><span style="color: #0000FF;">-</span><span style="color: #000000;">4</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">to</span> <span style="color: #0000FF;">(</span><span style="color: #000000;">y</span><span style="color: #0000FF;">-</span><span style="color: #000000;">4</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">by</span> <span style="color: #000000;">4</span> <span style="color: #008080;">do</span>
<span style="color: #000000;">xx</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">w</span><span style="color: #0000FF;">/</span><span style="color: #000000;">2</span> <span style="color: #0000FF;">+</span> <span style="color: #000000;">x</span><span style="color: #0000FF;">*</span><span style="color: #000000;">w</span><span style="color: #0000FF;">/</span><span style="color: #000000;">32</span>
<span style="color: #000000;">yy</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">h</span> <span style="color: #0000FF;">-</span><span style="color: #000000;">y</span><span style="color: #0000FF;">*</span><span style="color: #000000;">h</span><span style="color: #0000FF;">/</span><span style="color: #000000;">32</span>
<span style="color: #7060A8;">cdCanvasSector</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">xx</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">yy</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">pinsize</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">pinsize</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">0</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">360</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #7060A8;">cdCanvasSetForeground</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">,</span> <span style="color: #004600;">CD_INDIGO</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">balls</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">x</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">y</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">balls</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span>
<span style="color: #000000;">xx</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">w</span><span style="color: #0000FF;">/</span><span style="color: #000000;">2</span> <span style="color: #0000FF;">+</span> <span style="color: #000000;">x</span><span style="color: #0000FF;">*</span><span style="color: #000000;">w</span><span style="color: #0000FF;">/</span><span style="color: #000000;">32</span>
<span style="color: #000000;">yy</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">h</span> <span style="color: #0000FF;">-</span><span style="color: #000000;">y</span><span style="color: #0000FF;">*</span><span style="color: #000000;">h</span><span style="color: #0000FF;">/</span><span style="color: #000000;">32</span>
<span style="color: #7060A8;">cdCanvasSector</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">xx</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">yy</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">pinsize</span><span style="color: #0000FF;">*</span><span style="color: #000000;">4</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">pinsize</span><span style="color: #0000FF;">*</span><span style="color: #000000;">4</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">0</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">360</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #7060A8;">cdCanvasSetLineWidth</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">,</span><span style="color: #000000;">w</span><span style="color: #0000FF;">/</span><span style="color: #000000;">9</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">bins</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #000000;">xx</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">w</span><span style="color: #0000FF;">/</span><span style="color: #000000;">2</span><span style="color: #0000FF;">+(</span><span style="color: #000000;">i</span><span style="color: #0000FF;">*</span><span style="color: #000000;">4</span><span style="color: #0000FF;">-</span><span style="color: #000000;">18</span><span style="color: #0000FF;">)*</span><span style="color: #000000;">w</span><span style="color: #0000FF;">/</span><span style="color: #000000;">32</span>
<span style="color: #000000;">yy</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">bins</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]*</span><span style="color: #000000;">h</span><span style="color: #0000FF;">/</span><span style="color: #000000;">64</span><span style="color: #0000FF;">+</span><span style="color: #000000;">10</span>
<span style="color: #7060A8;">cdCanvasLine</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">,</span><span style="color: #000000;">xx</span><span style="color: #0000FF;">,</span><span style="color: #000000;">10</span><span style="color: #0000FF;">,</span><span style="color: #000000;">xx</span><span style="color: #0000FF;">,</span><span style="color: #000000;">yy</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #7060A8;">cdCanvasFlush</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">return</span> <span style="color: #004600;">IUP_DEFAULT</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">timer_cb</span><span style="color: #0000FF;">(</span><span style="color: #004080;">Ihandle</span> <span style="color: #000000;">ih</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">x</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">y</span><span style="color: #0000FF;">=</span><span style="color: #000000;">9</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">dx</span>
<span style="color: #008080;">if</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">balls</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">x</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">,</span><span style="color: #000000;">dx</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">balls</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">y</span><span style="color: #0000FF;">></span><span style="color: #000000;">20</span> <span style="color: #008080;">then</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">bindx</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">(</span><span style="color: #000000;">x</span><span style="color: #0000FF;">+</span><span style="color: #000000;">18</span><span style="color: #0000FF;">)/</span><span style="color: #000000;">4</span>
<span style="color: #000000;">bins</span><span style="color: #0000FF;">[</span><span style="color: #000000;">bindx</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">1</span>
<span style="color: #000000;">balls</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">balls</span><span style="color: #0000FF;">[</span><span style="color: #000000;">2</span><span style="color: #0000FF;">..$]</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">balls</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">x</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">,</span><span style="color: #000000;">dx</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">balls</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">y</span><span style="color: #0000FF;">></span><span style="color: #000000;">15</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">dx</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span>
<span style="color: #008080;">elsif</span> <span style="color: #7060A8;">and_bits</span><span style="color: #0000FF;">(</span><span style="color: #000000;">y</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)=</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">dx</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">}[</span><span style="color: #7060A8;">rand</span><span style="color: #0000FF;">(</span><span style="color: #000000;">2</span><span style="color: #0000FF;">)]</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">balls</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">x</span><span style="color: #0000FF;">+</span><span style="color: #000000;">dx</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">dx</span><span style="color: #0000FF;">}</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">y</span><span style="color: #0000FF;">></span><span style="color: #000000;">4</span> <span style="color: #008080;">and</span> <span style="color: #000000;">brem</span><span style="color: #0000FF;">!=</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">brem</span> <span style="color: #0000FF;">-=</span> <span style="color: #000000;">1</span>
<span style="color: #000000;">balls</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">append</span><span style="color: #0000FF;">(</span><span style="color: #000000;">balls</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0</span><span style="color: #0000FF;">})</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">brem</span><span style="color: #0000FF;">=</span><span style="color: #000000;">0</span> <span style="color: #008080;">and</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">balls</span><span style="color: #0000FF;">)=</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span>
<span style="color: #7060A8;">IupSetAttribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">timershow</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"RUN"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"NO"</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #7060A8;">IupUpdate</span><span style="color: #0000FF;">(</span><span style="color: #000000;">canvas</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">return</span> <span style="color: #004600;">IUP_IGNORE</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">map_cb</span><span style="color: #0000FF;">(</span><span style="color: #004080;">Ihandle</span> <span style="color: #000000;">ih</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">cdcanvas</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">cdCreateCanvas</span><span style="color: #0000FF;">(</span><span style="color: #004600;">CD_IUP</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">ih</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">cddbuffer</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">cdCreateCanvas</span><span style="color: #0000FF;">(</span><span style="color: #004600;">CD_DBUFFER</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">cdcanvas</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">cdCanvasSetBackground</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">,</span> <span style="color: #004600;">CD_GREY</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">return</span> <span style="color: #004600;">IUP_DEFAULT</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">main</span><span style="color: #0000FF;">()</span>
<span style="color: #7060A8;">IupOpen</span><span style="color: #0000FF;">()</span>
<span style="color: #000000;">canvas</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupCanvas</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"RASTERSIZE=360x600"</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">IupSetCallbacks</span><span style="color: #0000FF;">(</span><span style="color: #000000;">canvas</span><span style="color: #0000FF;">,</span> <span style="color: #0000FF;">{</span><span style="color: #008000;">"MAP_CB"</span><span style="color: #0000FF;">,</span> <span style="color: #7060A8;">Icallback</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"map_cb"</span><span style="color: #0000FF;">),</span>
<span style="color: #008000;">"ACTION"</span><span style="color: #0000FF;">,</span> <span style="color: #7060A8;">Icallback</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"redraw_cb"</span><span style="color: #0000FF;">)})</span>
<span style="color: #000000;">timershow</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupTimer</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">Icallback</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"timer_cb"</span><span style="color: #0000FF;">),</span> <span style="color: #000000;">80</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">dlg</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupDialog</span><span style="color: #0000FF;">(</span><span style="color: #000000;">canvas</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">`TITLE="%s"`</span><span style="color: #0000FF;">,</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">TITLE</span><span style="color: #0000FF;">})</span>
<span style="color: #7060A8;">IupShow</span><span style="color: #0000FF;">(</span><span style="color: #000000;">dlg</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">IupSetAttribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">canvas</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"RASTERSIZE"</span><span style="color: #0000FF;">,</span> <span style="color: #004600;">NULL</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #7060A8;">platform</span><span style="color: #0000FF;">()!=</span><span style="color: #004600;">JS</span> <span style="color: #008080;">then</span>
<span style="color: #7060A8;">IupMainLoop</span><span style="color: #0000FF;">()</span>
<span style="color: #7060A8;">IupClose</span><span style="color: #0000FF;">()</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #000000;">main</span><span style="color: #0000FF;">()</span>
<!--</syntaxhighlight>-->
 
=={{header|PicoLisp}}==
<langsyntaxhighlight PicoLisplang="picolisp">(de galtonBox (Pins Height)
(let (Bins (need (inc (* 2 Pins)) 0) X 0 Y 0)
(until (= Height (apply max Bins))
Line 967 ⟶ 3,318:
(prin (if (>= B H) "o" " ")) )
(prinl) )
(wait 200) ) ) )</langsyntaxhighlight>
Test:
<syntaxhighlight lang PicoLisp="picolisp">(galtonBox 9 11)</langsyntaxhighlight>
{{Out}}
Output:
<pre># Snapshot after a few seconds:
.
Line 1,018 ⟶ 3,369:
=={{header|Prolog}}==
Works with SWI-Prolog and XPCE.[[File:Prolog_Galton_Box_1.png|thumb|Sample display of Prolog solution]]
<langsyntaxhighlight Prologlang="prolog">:- dynamic tubes/1.
:- dynamic balls/2.
:- dynamic stop/1.
Line 1,216 ⟶ 3,567:
send(Ch, append, T),
send(D, display, T))).
</syntaxhighlight>
</lang>
 
=={{header|PureBasic}}==
{{trans|Unicon}}
[[File:PureBasic_galtonbox.png|thumb|Sample display of PureBasic solution]]
<langsyntaxhighlight lang="purebasic">Global pegRadius, pegSize, pegSize2, height, width, delay, histogramSize, ball
 
Procedure eventLoop()
Line 1,347 ⟶ 3,698:
If Not galton(pegRows): Break: EndIf
Next
Repeat: eventLoop(): ForEver</langsyntaxhighlight>
 
=={{header|Python}}==
<syntaxhighlight lang="python">#!/usr/bin/python
 
import sys, os
import random
import time
 
def print_there(x, y, text):
sys.stdout.write("\x1b7\x1b[%d;%df%s\x1b8" % (x, y, text))
sys.stdout.flush()
 
 
class Ball():
def __init__(self):
self.x = 0
self.y = 0
def update(self):
self.x += random.randint(0,1)
self.y += 1
 
def fall(self):
self.y +=1
 
 
class Board():
def __init__(self, width, well_depth, N):
self.balls = []
self.fallen = [0] * (width + 1)
self.width = width
self.well_depth = well_depth
self.N = N
self.shift = 4
def update(self):
for ball in self.balls:
if ball.y < self.width:
ball.update()
elif ball.y < self.width + self.well_depth - self.fallen[ball.x]:
ball.fall()
elif ball.y == self.width + self.well_depth - self.fallen[ball.x]:
self.fallen[ball.x] += 1
else:
pass
def balls_on_board(self):
return len(self.balls) - sum(self.fallen)
def add_ball(self):
if(len(self.balls) <= self.N):
self.balls.append(Ball())
 
def print_board(self):
for y in range(self.width + 1):
for x in range(y):
print_there( y + 1 ,self.width - y + 2*x + self.shift + 1, "#")
def print_ball(self, ball):
if ball.y <= self.width:
x = self.width - ball.y + 2*ball.x + self.shift
else:
x = 2*ball.x + self.shift
y = ball.y + 1
print_there(y, x, "*")
def print_all(self):
print(chr(27) + "[2J")
self.print_board();
for ball in self.balls:
self.print_ball(ball)
 
 
def main():
board = Board(width = 15, well_depth = 5, N = 10)
board.add_ball() #initialization
while(board.balls_on_board() > 0):
board.print_all()
time.sleep(0.25)
board.update()
board.print_all()
time.sleep(0.25)
board.update()
board.add_ball()
 
 
if __name__=="__main__":
main()</syntaxhighlight>
 
=={{header|Racket}}==
This does not use the default #lang racket. Required is advanced student with teachpacks universe and image.
Multiple balls are added each step, but they do not collide.
 
<syntaxhighlight lang="racket">
<lang Racket>
;a ball's position...row is a natural number and col is an integer where 0 is the center
(define-struct pos (row col))
;state of simulation...list of all positions and vector of balls (index = bin)
(define-struct st (poss bins))
;given list of indices, increment those byvector 1@i
(define (vector-inc! v i) (vector-indicesset! isv i (add1 (vector-ref v i))))
(let vector-inc-indices ([is is])
(if (null? is)
v
(begin (vector-set! v (car is) (add1 (vector-ref v (car is))))
(vector-inc-indices (cdr is))))))
 
(define BALL-RADIUS 6)
Line 1,455 ⟶ 3,889:
(+ -1 (* 2 (random 2)) (pos-col p))))
 
;each step, every ball goes to the next row and a new ballballs isare added at the top center
;balls that fall off go into bins
(define (tock height s)
Line 1,463 ⟶ 3,897:
;dead balls have (partition from normal Racket would be useful here...)
[dead (filter (λ (p) (>= (pos-row p) height)) new-ps)]
;mapadjust col tofrom bin[-x,x] to index[0,2x]
[bin-indices (map (λ (p) (quotient (+ (pos-col p) height) 2)) dead)])
;add a new ballballs to the live balls
(make-st (consappend (make-list (random 4) (make-pos 0 0)) live)
;sum dead ball positions into bins
(begin (for-each (λ (i) (vector-inc-indices! bin-indices (st-bins s) i))) bin-indices)
(st-bins s)))))
 
;run simulation with empty list of positions to start, stepping with "tock" and drawing with "draw"
Line 1,475 ⟶ 3,910:
(on-tick (λ (ps) (tock height ps)) 0.5)
(to-draw (λ (ps) (draw height ps)))))
</syntaxhighlight>
</lang>
 
=={{header|Raku}}==
(formerly Perl 6)
[[File:Galton_box_perl6.gif|thumb|UPPER HALF BLOCK and LOWER HALF BLOCK alternate to give a somewhat smooth animation.]]
{{works with|rakudo|2015-09-12}}
<syntaxhighlight lang="raku" line>my $row-count = 6;
constant $peg = "*";
constant @coin-icons = "\c[UPPER HALF BLOCK]", "\c[LOWER HALF BLOCK]";
sub display-board(@positions, @stats is copy, $halfstep) {
my $coin = @coin-icons[$halfstep.Int];
state @board-tmpl = {
# precompute a board
my @tmpl;
sub out(*@stuff) {
@tmpl.push: $[@stuff.join>>.ords.flat];
}
# three lines of space above
for 1..3 {
out " ", " " x (2 * $row-count);
}
# $row-count lines of pegs
for flat ($row-count...1) Z (1...$row-count) -> $spaces, $pegs {
out " ", " " x $spaces, ($peg xx $pegs).join(" "), " " x $spaces;
}
# four lines of space below
for 1..4 {
out " ", " " x (2 * $row-count);
}
@tmpl
}();
my $midpos = $row-count + 2;
my @output;
{
# collect all the output and output it all at once at the end
sub say(Str $foo) {
@output.push: $foo, "\n";
}
sub print(Str $foo) {
@output.push: $foo;
}
# make some space above the picture
say "" for ^10;
my @output-lines = map { [ @$_ ] }, @board-tmpl;
# place the coins
for @positions.kv -> $line, $pos {
next unless $pos.defined;
@output-lines[$line][$pos + $midpos] = $coin.ord;
}
# output the board with its coins
for @output-lines -> @line {
say @line.chrs;
}
# show the statistics
my $padding = 0;
while any(@stats) > 0 {
$padding++;
print " ";
@stats = do for @stats -> $stat {
given $stat {
when 1 {
print "\c[UPPER HALF BLOCK]";
$stat - 1;
}
when * <= 0 {
print " ";
0
}
default {
print "\c[FULL BLOCK]";
$stat - 2;
}
}
}
say "";
}
say "" for $padding...^10;
}
say @output.join("");
}
sub simulate($coins is copy) {
my $alive = True;
sub hits-peg($x, $y) {
if 3 <= $y < 3 + $row-count and -($y - 2) <= $x <= $y - 2 {
return not ($x - $y) %% 2;
}
return False;
}
my @coins = Int xx (3 + $row-count + 4);
my @stats = 0 xx ($row-count * 2);
# this line will dispense coins until turned off.
@coins[0] = 0;
while $alive {
$alive = False;
# if a coin falls through the bottom, count it
given @coins[*-1] {
when *.defined {
@stats[$_ + $row-count]++;
}
}
# move every coin down one row
for ( 3 + $row-count + 3 )...1 -> $line {
my $coinpos = @coins[$line - 1];
@coins[$line] = do if not $coinpos.defined {
Nil
} elsif hits-peg($coinpos, $line) {
# when a coin from above hits a peg, it will bounce to either side.
$alive = True;
($coinpos - 1, $coinpos + 1).pick;
} else {
# if there was a coin above, it will fall to this position.
$alive = True;
$coinpos;
}
}
# let the coin dispenser blink and turn it off if we run out of coins
if @coins[0].defined {
@coins[0] = Nil
} elsif --$coins > 0 {
@coins[0] = 0
}
# smooth out the two halfsteps of the animation
my $start-time;
ENTER { $start-time = now }
my $wait-time = now - $start-time;
sleep 0.1 - $wait-time if $wait-time < 0.1;
for @coin-icons.keys {
sleep $wait-time max 0.1;
display-board(@coins, @stats, $_);
}
}
}
sub MAIN($coins = 20, $peg-lines = 6) {
$row-count = $peg-lines;
simulate($coins);
}</syntaxhighlight>
 
=={{header|REXX}}==
The REXX version displays an ASCII version of a working Galton box.
 
Balls are dropped continuously &nbsp; (up to a number specified or the default), &nbsp; the default is enough rows of
<br>pins to fill the top &nbsp; <big><sup>1</sup>/<sub>3</sub></big> &nbsp; rows of the terminal screen.
<syntaxhighlight lang="rexx">/*REXX pgm simulates Sir Francis Galton's box, aka: Galton Board, quincunx, bean machine*/
trace off /*suppress any messages for negative RC*/
if !all(arg()) then exit /*Any documentation was wanted? Done.*/
signal on halt /*allow the user to halt the program.*/
parse arg rows balls freeze seed . /*obtain optional arguments from the CL*/
if rows =='' | rows=="," then rows= 0 /*Not specified? Then use the default.*/
if balls=='' | balls=="," then balls= 100 /* " " " " " " */
if freeze=='' | freeze=="," then freeze= 0 /* " " " " " " */
if datatype(seed, 'W') then call random ,,seed /*Was a seed specified? Then use seed.*/
pin = '·'; ball = '☼' /*define chars for a pin and a ball.*/
parse value scrsize() with sd sw . /*obtain the terminal depth and width. */
if sd==0 then sd= 40 /*Not defined by the OS? Use a default*/
if sw==0 then sw= 80 /* " " " " " " " " */
sd= sd - 3 /*define the usable screen depth.*/
sw= sw - 1; if sw//2 then sw= sw - 1 /* " " " odd " width.*/
if rows==0 then rows= (sw - 2 ) % 3 /*pins are on the first third of screen*/
call gen /*gen a triangle of pins with some rows*/
do step=1; call drop; call show /*show animation 'til run out of balls.*/
end /*step*/ /* [↑] the dropping/showing " " */
exit 0 /*stick a fork in it, we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
gen: @.=; do r=0 by 2 to rows; $= /*build a triangle of pins for the box.*/
do pins=1 for r%2; $= $ pin /*build a row of pins to be displayed. */
end /*pins*/
@.r= center( strip($, 'T'), sw) /*an easy method to build a triangle. */
end /*r*/; #= 0; return /*#: is the number of balls dropped. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
drop: static= 1 /*used to indicate all balls are static*/
do c=sd-1 by -1 for sd-1; n= c + 1 /*D: current row; N: the next row. */
x= pos(ball, @.c); y= x - 1 /*X: position of a ball on the C line.*/
if x==0 then iterate /*No balls here? Then nothing to drop.*/
do forever; y= pos(ball, @.c, y+1) /*drop most balls down to the next row.*/
if y==0 then iterate c /*down with this row, go look at next. */
z= substr(@.n, y, 1) /*another ball is blocking this fall. */
if z==' ' then do; @.n= overlay(ball, @.n, y) /*drop a ball straight down.*/
@.c= overlay(' ' , @.c, y) /*make current ball a ghost.*/
static= 0 /*indicate balls are moving.*/
iterate /*go keep looking for balls.*/
end
if z==pin then do; ?= random(,999); d= -1 /*assume falling to the left*/
if ?//2 then d= 1 /*if odd random#, fall right*/
if substr(@.n, y+d, 1)\==' ' then iterate /*blocked fall*/
@.n= overlay(ball, @.n, y+d)
@.c= overlay(' ' , @.c, y )
static= 0 /*indicate balls are moving.*/
iterate /*go keep looking for balls.*/
end
end /*forever*/
end /*c*/ /* [↓] step//2 is used to avoid collisions. */
/* [↓] drop a new ball ? */
if #<balls & step//2 then do; @.1= center(ball, sw+1); # = # + 1; end
else if static then exit 2 /*insure balls are static. */
return
/*──────────────────────────────────────────────────────────────────────────────────────*/
show: !cls; do LR=sd by -1 until @.LR\=='' /*LR: last row of data.*/
end /*LR*/; ss= 'step' step /* [↓] display a row. */
do r=1 for LR; _= strip(@.r, 'T'); if r==2 then _= overlay(ss, _, sw-12); say _
end /*r*/; if step==freeze then do; say 'press ENTER ···'; pull; end
return
/*══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════*/
halt: say '***warning*** REXX program' !fn "execution halted by user."; exit 1
!all: !!=!;!=space(!);upper !;call !fid;!nt=right(!var('OS'),2)=='NT';!cls=word('CLS VMFCLEAR CLRSCREEN',1+!cms+!tso*2);if arg(1)\==1 then return 0;if wordpos(!,'? ?SAMPLES ?AUTHOR ?FLOW')==0 then return 0;!call=']$H';call '$H' !fn !;!call=;return 1
!cal: if symbol('!CALL')\=="VAR" then !call=; return !call
!env: !env='ENVIRONMENT'; if !sys=='MSDOS' | !brexx | !r4 | !roo then !env= 'SYSTEM'; if !os2 then !env= 'OS2'!env; !ebcdic= 2=='f2'x; if !crx then !env= 'DOS'; return
!fid: parse upper source !sys !fun !fid . 1 . . !fn !ft !fm .; call !sys; if !dos then do; _= lastpos('\', !fn); !fm= left(!fn, _); !fn= substr(!fn, _+1); parse var !fn !fn '.' !ft; end; return word(0 !fn !ft !fm, 1 + ('0'arg(1) ) )
!rex: parse upper version !ver !vernum !verdate .; !brexx= 'BY'==!vernum; !kexx= 'KEXX'==!ver; !pcrexx= 'REXX/PERSONAL'==!ver | 'REXX/PC'==!ver; !r4= 'REXX-R4'==!ver; !regina= 'REXX-REGINA'==left(!ver, 11); !roo= 'REXX-ROO'==!ver; call !env; return
!sys: !cms= !sys=='CMS'; !os2= !sys=='OS2'; !tso= !sys=='TSO' | !sys=='MVS'; !vse= !sys=='VSE'; !dos= pos('DOS', !sys)\==0 | pos('WIN', !sys)\==0 | !sys=='CMD'; !crx= left(!sys, 6)=='DOSCRX'; call !rex; return
!var: call !fid; if !kexx then return space( dosenv( arg(1) ) ); return space( value( arg(1), , !env) )</syntaxhighlight>
Programming note: &nbsp; the last seven lines of this REXX program are some general purpose (boilerplate code) that, among other things, finds:
::* &nbsp; the REXX program's filename, filetype (file extension), and filemode (and/or path)
::* &nbsp; if the user wants documentation presented (not germane to this REXX program)
::* &nbsp; the environment name (not germane)
::* &nbsp; what command to be used to clear the terminal screen
::* &nbsp; the name of the operating system being used (not germane)
::* &nbsp; the name of the REXX interpreter being used (not germane)
::* &nbsp; various other bits of information (not germane)
 
It is only intended to be used to make this particular REXX program independent of any particular REXX interpreter and/or independent of knowing which program is to be used for clearing the terminal screen. &nbsp; As such, the boilerplate code isn't commented and isn't intended to be a teaching tool.
 
This REXX program makes use of &nbsp; '''SCRSIZE''' &nbsp; REXX program (or
BIF) which is used to determine the screen
<br>width and depth of the terminal (console). &nbsp; Some REXXes don't have this BIF.
 
The &nbsp; '''SCRSIZE.REX''' &nbsp; REXX program is included here &nbsp; ───► &nbsp; [[SCRSIZE.REX]].
 
The terminal size used for this display was &nbsp; '''64'''<small>x</small>'''96'''.
 
{{out|output|text=&nbsp; when the REXX program was "stopped" by using the inputs of &nbsp; &nbsp; <tt> , , 100 </tt> &nbsp; so as to freeze the program to capture a screenshot:}}
 
(Shown at &nbsp; <big>'''<sup>2</sup>/<sub>3</sub>'''</big> &nbsp; size.)
 
<pre style="font-size:67%">
☼· step 100
 
·☼·
 
· ·☼·
 
· ·☼· ·
 
☼· · · · ·
 
· · ·☼· · ·
 
·☼· · · · · ·
 
· · · · ·☼· · ·
 
· · · · · · ·☼· ·
 
· · ·☼· · · · · · ·
 
· · · ·☼· · · · · · ·
 
· · · ·☼· · · · · · · ·
 
· · ·☼· · · · · · · · · ·
 
· · · · · · · ·☼· · · · · ·
 
· · · · · · ·☼· · · · · · · ·
 
 
 
 
 
 
 
 
 
 
 
 
 
 
☼ ☼ ☼
☼ ☼ ☼
☼ ☼ ☼ ☼ ☼ ☼ ☼
☼ ☼ ☼ ☼ ☼ ☼ ☼ ☼ ☼
press ENTER ···
</pre>
{{out|output|text=&nbsp; when using the above inputs; &nbsp; this is the final screenshot:}}
 
(Shown at &nbsp; <big>'''<sup>2</sup>/<sub>3</sub>'''</big> &nbsp; size.)
 
<pre style="font-size:67%">
· step 350
 
· ·
 
· · ·
 
· · · ·
 
· · · · ·
 
· · · · · ·
 
· · · · · · ·
 
· · · · · · · ·
 
· · · · · · · · ·
 
· · · · · · · · · ·
 
· · · · · · · · · · ·
 
· · · · · · · · · · · ·
 
· · · · · · · · · · · · ·
 
· · · · · · · · · · · · · ·
 
· · · · · · · · · · · · · · ·
 
☼ ☼
☼ ☼
☼ ☼ ☼
☼ ☼ ☼
☼ ☼ ☼
☼ ☼ ☼
☼ ☼ ☼ ☼
☼ ☼ ☼ ☼
☼ ☼ ☼ ☼
☼ ☼ ☼ ☼
☼ ☼ ☼ ☼
☼ ☼ ☼ ☼
☼ ☼ ☼ ☼
☼ ☼ ☼ ☼
☼ ☼ ☼ ☼
☼ ☼ ☼ ☼
☼ ☼ ☼ ☼
☼ ☼ ☼ ☼
☼ ☼ ☼ ☼
☼ ☼ ☼ ☼ ☼ ☼
☼ ☼ ☼ ☼ ☼ ☼
☼ ☼ ☼ ☼ ☼ ☼
☼ ☼ ☼ ☼ ☼ ☼ ☼ ☼
☼ ☼ ☼ ☼ ☼ ☼ ☼ ☼
☼ ☼ ☼ ☼ ☼ ☼ ☼ ☼
☼ ☼ ☼ ☼ ☼ ☼ ☼ ☼ ☼
☼ ☼ ☼ ☼ ☼ ☼ ☼ ☼ ☼ ☼
☼ ☼ ☼ ☼ ☼ ☼ ☼ ☼ ☼ ☼
☼ ☼ ☼ ☼ ☼ ☼ ☼ ☼ ☼ ☼
</pre>
 
=={{header|Ruby}}==
{{libheader|Shoes}}
[[File:galtonbox.shoes.png|thumb|Sample display of Ruby solution]]
<langsyntaxhighlight lang="ruby">$rows_of_pins = 12
$width = $rows_of_pins * 10 + ($rows_of_pins+1)*14
 
Line 1,557 ⟶ 4,368:
end
end
end</langsyntaxhighlight>
 
=={{header|Tcl}}==
{{trans|C}}
<langsyntaxhighlight lang="tcl">package require Tcl 8.6
 
oo::class create GaltonBox {
Line 1,661 ⟶ 4,472:
board show
if {[board step]} {after 60} break
}</langsyntaxhighlight>
After a sample run with input parameters <tt>10 55</tt>:
<pre>
Line 1,721 ⟶ 4,532:
</pre>
There is a much more comprehensive solution to this on the [http://wiki.tcl.tk/8825 Tcler's Wiki].<!-- Too long to reproduce here -->
 
=={{header|Wren}}==
{{trans|D}}
{{libheader|Wren-iterate}}
<syntaxhighlight lang="wren">import "random" for Random
import "./iterate" for Reversed
 
var boxW = 41 // Galton box width.
var boxH = 37 // Galton box height.
var pinsBaseW = 19 // Pins triangle base.
var nMaxBalls = 55 // Number of balls.
 
var centerH = pinsBaseW + (boxW - pinsBaseW * 2 + 1) / 2 - 1
var Rand = Random.new()
 
class Cell {
static EMPTY { " " }
static BALL { "o" }
static WALL { "|" }
static CORNER { "+" }
static FLOOR { "-" }
static PIN { "." }
}
 
/* Galton box. Will be printed upside down. */
var Box = List.filled(boxH, null)
for (i in 0...boxH) Box[i] = List.filled(boxW, Cell.EMPTY)
 
class Ball {
construct new(x, y) {
if (Box[x][y] != Cell.EMPTY) Fiber.abort("The cell at (x, y) is not empty.")
Box[y][x] = Cell.BALL
_x = x
_y = y
}
 
doStep() {
if (_y <= 0) return // Reached the bottom of the box.
var cell = Box[_y - 1][_x]
if (cell == Cell.EMPTY) {
Box[_y][_x] = Cell.EMPTY
_y = _y - 1
Box[_y][_x] = Cell.BALL
} else if (cell == Cell.PIN) {
Box[_y][_x] = Cell.EMPTY
_y = _y - 1
if (Box[_y][_x - 1] == Cell.EMPTY && Box[_y][_x + 1] == Cell.EMPTY) {
_x = _x + Rand.int(2) * 2 - 1
Box[_y][_x] = Cell.BALL
return
} else if (Box[_y][_x - 1] == Cell.EMPTY){
_x = _x + 1
} else _x = _x - 1
Box[_y][_x] = Cell.BALL
} else {
// It's frozen - it always piles on other balls.
}
}
}
 
var initializeBox = Fn.new {
// Set ceiling and floor:
Box[0][0] = Cell.CORNER
Box[0][boxW - 1] = Cell.CORNER
for (i in 1...boxW - 1) Box[0][i] = Cell.FLOOR
for (i in 0...boxW) Box[boxH - 1][i] = Box[0][i]
 
// Set walls:
for (r in 1...boxH - 1) {
Box[r][0] = Cell.WALL
Box[r][boxW - 1] = Cell.WALL
}
// Set pins:
for (nPins in 1..pinsBaseW) {
for (pin in 0...nPins) {
Box[boxH - 2 - nPins][centerH + 1 - nPins + pin * 2] = Cell.PIN
}
}
}
 
var drawBox = Fn.new() {
for (row in Reversed.new(Box, 1)) {
for (c in row) System.write(c)
System.print()
}
}
 
initializeBox.call()
var balls = []
for (i in 0...nMaxBalls + boxH) {
System.print("\nStep %(i):")
if (i < nMaxBalls) balls.add(Ball.new(centerH, boxH - 2)) // Add ball.
drawBox.call()
 
// Next step for the simulation.
// Frozen balls are kept in balls list for simplicity
for (b in balls) b.doStep()
}</syntaxhighlight>
 
{{out}}
Sample output, showing the last step only:
<pre>
Step 91:
+---------------------------------------+
| |
| . |
| . . |
| . . . |
| . . . . |
| . . . . . |
| . . . . . . |
| . . . . . . . |
| . . . . . . . . |
| . . . . . . . . . |
| . . . . . . . . . . |
| . . . . . . . . . . . |
| . . . . . . . . . . . . |
| . . . . . . . . . . . . . |
| . . . . . . . . . . . . . . |
| . . . . . . . . . . . . . . . |
| . . . . . . . . . . . . . . . . |
| . . . . . . . . . . . . . . . . . |
| . . . . . . . . . . . . . . . . . . |
| . . . . . . . . . . . . . . . . . . . |
| |
| |
| o |
| o |
| o |
| o o |
| o o |
| o o |
| o o o o |
| o o o o |
| o o o o |
| o o o o o |
| o o o o o o o o |
| o o o o o o o o o |
| o o o o o o o o o o o o |
+---------------------------------------+
</pre>
 
=={{header|XPL0}}==
Line 1,726 ⟶ 4,679:
This ''Peeks'' into some IBM-PC specific locations and hence is not entirely portable.
 
<langsyntaxhighlight XPL0lang="xpl0">include c:\cxpl\codes; \intrinsic code declarations
define Balls = 80; \maximum number of balls
int Bx(Balls), By(Balls), \character cell coordinates of each ball
Line 1,758 ⟶ 4,711:
Sound(0, 3, 1); \delay about 1/6 second
until KeyHit; \continue until a key is struck
]</langsyntaxhighlight>
 
{{Out}}
Example output:
<pre>
o
Line 1,788 ⟶ 4,741:
</pre>
 
=={{header|Yabasic}}==
<syntaxhighlight lang="yabasic">bola$ = "0000ff"
obst$ = "000000"
 
maxBalls = 10
{{omit from|GUISS}}
cx = 1
[[Category:Animation]][[Category:Randomness]]
cy = 2
dim Balls(maxBalls, 2)
 
open window 600,600
window origin "ct"
 
maxh = peek("winheight")
 
REM Draw the pins:
 
FOR row = 1 TO 7
FOR col = 1 TO row
FILL circle 40*col - 20*row, 40*row+80, 10
NEXT col
NEXT row
 
REM Animate
tick = 0
bolas = 0
color 0,0,255
 
do
if (bolas < maxBalls) then
if tick = 3 then
tick = 0
bolas = bolas + 1
Balls(bolas, cx) = 20
Balls(bolas, cy) = 10
end if
tick = tick + 1
end if
for n = 1 to bolas
if Balls(n, cy) then
color$ = right$(getbit$(Balls(n,cx),Balls(n,cy) + 10,Balls(n,cx),Balls(n,cy) + 10),6)
if (color$ = bola$) or (Balls(n,cy) >= maxh - 15) then
Balls(n,cy) = 0
break
end if
clear fill circle Balls(n,cx),Balls(n,cy),10
if color$ = obst$ then
if int(ran(2)) then
Balls(n,cx) = Balls(n,cx) - 20
else
Balls(n,cx) = Balls(n,cx) + 20
end if
end if
Balls(n,cy) = Balls(n,cy)+10
fill circle Balls(n,cx),Balls(n,cy),10
wait .001
else
wait .001
end if
next n
loop
</syntaxhighlight>
 
=={{header|Zig}}==
<syntaxhighlight lang="zig">const std = @import("std");
const rand = std.rand;
const time = std.time;
 
const PEG_LINES = 20;
const BALLS = 10;
 
fn boardSize(comptime peg_lines: u16) u16 {
var i: u16 = 0;
var size: u16 = 0;
inline while (i <= peg_lines) : (i += 1) {
size += i + 1;
}
return size;
}
 
const BOARD_SIZE = boardSize(PEG_LINES);
 
fn stepBoard(board: *[BOARD_SIZE]u1, count: *[PEG_LINES + 1]u8) void {
var prng = rand.DefaultPrng.init(@bitCast(time.timestamp()));
 
var p: u8 = 0;
var sum: u16 = 0;
while (p <= PEG_LINES) : (p += 1) {
const pegs = PEG_LINES - p;
var i: u16 = 0;
while (i < pegs + 1) : (i += 1) {
if (pegs != PEG_LINES and board[BOARD_SIZE - 1 - sum - i] == 1) {
if (prng.random().boolean()) {
board.*[BOARD_SIZE - 1 - sum - i + pegs + 1] = 1;
} else {
board.*[BOARD_SIZE - 1 - sum - i + pegs + 2] = 1;
}
} else if (pegs == PEG_LINES and board[BOARD_SIZE - 1 - sum - i] == 1) {
count.*[pegs - i] += 1;
}
board.*[BOARD_SIZE - 1 - sum - i] = 0;
}
sum += pegs + 1;
}
}
 
fn printBoard(board: *[BOARD_SIZE]u1, count: *[PEG_LINES + 1]u8) !void {
const stdout = std.io.getStdOut();
_ = try stdout.write("\x1B[2J\x1B[1;1H");
var pegs: u16 = 0;
var sum: u16 = 0;
while (pegs <= PEG_LINES) : (pegs += 1) {
var i: u16 = 0;
while (i < (PEG_LINES - pegs)) : (i += 1) _ = try stdout.write(" ");
i = 0;
while (i < pegs + 1) : (i += 1) {
const spot = if (board[i + sum] == 1) "o" else " ";
_ = try stdout.write(spot);
if (i != pegs) _ = try stdout.write("*");
}
sum += pegs + 1;
_ = try stdout.write("\n");
}
for (count) |n| {
const num_char = [2]u8{'0' + n, ' '};
_ = try stdout.write(&num_char);
}
_ = try stdout.write("\n");
}
 
pub fn main() !void {
var board: [BOARD_SIZE]u1 = [_]u1{0} ** BOARD_SIZE;
var bottom_count: [PEG_LINES+1]u8 = [_]u8{0} ** (PEG_LINES + 1);
 
var i: u16 = 0;
while (i < PEG_LINES + BALLS + 1) : (i += 1) {
if (i < BALLS) board[0] = 1;
 
try printBoard(&board, &bottom_count);
stepBoard(&board, &bottom_count);
time.sleep(150000000);
}
}</syntaxhighlight>
2,042

edits