Galton box animation: Difference between revisions

Improved D code
(+ simplified textual D entry)
(Improved D code)
Line 396:
<lang d>import std.stdio, std.algorithm, std.random, std.array;
 
enum int boxW = 41, boxH = 37; // Galton box width and height.
enum int pegsBaseWpinsBaseW = 19; // Pins triangle base size.
enum int nBallsnMaxBalls = 55; // Number of balls.
 
static assert(boxW >= 2 && boxH >= 2);
static assert((boxW - 4) >= (pegsBaseWpinsBaseW * 2 - 1));
static assert((boxH - 3) >= pegsBaseWpinsBaseW);
enum centercenterH = pegsBaseWpinsBaseW + (boxW - (pegsBaseWpinsBaseW * 2 - 1)) / 2 - 1;
 
enum center = pegsBaseW + (boxW - (pegsBaseW * 2 - 1)) / 2 - 1;
 
alias CellBaseType = dchar;
 
alias CellBaseType = dcharchar;
enum Cell : CellBaseType { empty = ' ',
ball = 'o',
Line 413 ⟶ 411:
corner = '+',
floor = '-',
pegpin = '.' }
 
Cell[boxW][boxH] box; // Galton box. Will be printed upside-down.
 
struct Ball {
int x, y; // Position.
 
this(in int x_, in int y_) { this.x = x_; this.y = y_; }nothrow
in }{
assert(box[$ - 2y_][centerx_] == Cell.empty);
} body {
assert(0)this.x = x_;
this.y = elsey_;
box[y][x] = Cell.ball;
}
 
boolnothrow isFrozenconst invariant() {
assert(x >= 0 && x < boxW && y >= 0 && y < boxH);
with (Cell) {
if assert(box[y - 1][x] == Cell.emptyball) {;
// It always piles on other balls.
static immutable obstacles = [ball, wall, corner, floor];
return y <= 0 || obstacles.canFind(box[y - 1][x]);
}
}
 
void doFallingStepdoStep() {
if (isFrozen()y <= 0)
return; // Reached the bottom of the box.
 
if (box[y - 1][x] == Cell.empty) {
with (Cell) box[y][x] = Cell.empty;{
final switch (box[y --; 1][x]) {
box[y][x] = Cell.ball; case empty:
return box[y][x] = Cell.empty;
} y--;
if ( box[y - 1][x] == Cell.peg) {ball;
box[y][x] = Cell.empty break;
y--; case ball, wall, corner, floor:
if (box[y][x - 1] == Cell // It's frozen.empty &&(It always piles on other balls).
box[y][x + 1] == Cell.empty) {break;
xcase += uniform(0, 2) * 2 - 1;pin:
box[y][x] = Cell.ballempty;
return y--;
if (box[y][x - 1] == Cell.empty) &&
box[y][x + 1] == Cell.empty) {
x += uniform(0, 2) * 2 - 1;
box[b.y][b.x] = Cell.ball;
return;
} else if (box[y][x - 1] == Cell.empty)
x++;
x--; else
addBall() x--;
box[y][x] = Cell.ball;
break;
}
if (box[y][x - 1] == Cell.empty)
x++;
else
x--;
box[y][x] = Cell.ball;
return;
}
assert(0);
}
}
 
void initializeBox() {
// Set ceiling and floor:
box[0] = Cell.corner ~ [Cell.floor].replicate(boxW - 2)
~ Cell.corner;
box[$ - 1] = box[0][];
 
// Set walls:
foreach (r; 1 .. boxH - 1)
box[r][0] = box[r][$ - 1] = Cell.wall;
 
// Set pins:
foreach (nPegs; 1 .. pegsBaseW + 1)
foreach (pegnPins; 01 .. nPegspinsBaseW + 1)
foreach (nPegspin; 10 .. pegsBaseW + 1nPins)
box[boxH - 2 - nPegs][center + 1 - nPegs + peg * 2]
box[boxH - 2 - =nPins][centerH Cell.peg;+ 1 - nPins + pin * 2]
= Cell.pin;
}
 
Line 476 ⟶ 485:
foreach_reverse (ref row; box)
writeln(cast(CellBaseType[])row);
}
 
Ball[] balls;
 
void addBall() {
assert(box[$ - 2][center] == Cell.empty);
const b = Ball(center, boxH - 2);
balls ~= b;
box[b.y][b.x] = Cell.ball;
}
 
void main() {
initializeBox();
Ball[] balls;
 
foreach (const i; 0 .. nBallsnMaxBalls + boxH) {
if (i < nBalls)
addBall();
writefln("\nStep %d:", i);
if (i < nBallsnMaxBalls)
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.doFallingStepdoStep();
}
}</lang>
Line 607 ⟶ 611:
| |
| |
| o o o |
| o o o |
| o o o |
| o o o |
| o o o |
| o o o |
| o o o o |
| o o o o |
| o o o o |
| o o o o o o |
| o o o o o 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>