Task: Brownian tree


C++: #include <windows.h>
#include <iostream>
#include <string>


using namespace std;


enum states { SEED, GROWING, MOVING, REST };
enum treeStates { NONE, MOVER, TREE };
const int MAX_SIDE = 480, MAX_MOVERS = 511, MAX_CELLS = 15137;


class point
{
public:
    point()                  { x = y = 0; }
    point( int a, int b )    { x = a; y = b; }
    void set( int a, int b ) { x = a; y = b; }

    int x, y;
};

class movers
{
public:
    point pos;
    bool moving;
    movers() : moving( false ){}
};

class myBitmap
{
public:
    myBitmap() : pen( NULL ) {}
    ~myBitmap()
    {
	DeleteObject( pen );
	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()
    {
	ZeroMemory( pBits, width * height * sizeof( DWORD ) );
    }

    void setPenColor( DWORD clr )
    {
	if( pen ) DeleteObject( pen );
	pen = CreatePen( PS_SOLID, 1, clr );
	SelectObject( hdc, pen );
    }

    void saveBitmap( string path )
    {
	BITMAPFILEHEADER fileheader;
	BITMAPINFO	 infoheader;
	BITMAP		 bitmap;
	DWORD*		 dwpBits;
	DWORD		 wb;
	HANDLE		 file;

	GetObject( bmp, sizeof( bitmap ), &bitmap );

	dwpBits = new DWORD[bitmap.bmWidth * bitmap.bmHeight];
	ZeroMemory( dwpBits, bitmap.bmWidth * bitmap.bmHeight * sizeof( DWORD ) );
	ZeroMemory( &infoheader, sizeof( BITMAPINFO ) );
	ZeroMemory( &fileheader, sizeof( BITMAPFILEHEADER ) );

	infoheader.bmiHeader.biBitCount = sizeof( DWORD ) * 8;
	infoheader.bmiHeader.biCompression = BI_RGB;
	infoheader.bmiHeader.biPlanes = 1;
	infoheader.bmiHeader.biSize = sizeof( infoheader.bmiHeader );
	infoheader.bmiHeader.biHeight = bitmap.bmHeight;
	infoheader.bmiHeader.biWidth = bitmap.bmWidth;
	infoheader.bmiHeader.biSizeImage = bitmap.bmWidth * bitmap.bmHeight * sizeof( DWORD );

	fileheader.bfType    = 0x4D42;
	fileheader.bfOffBits = sizeof( infoheader.bmiHeader ) + sizeof( BITMAPFILEHEADER );
	fileheader.bfSize    = fileheader.bfOffBits + infoheader.bmiHeader.biSizeImage;

	GetDIBits( hdc, bmp, 0, height, ( LPVOID )dwpBits, &infoheader, DIB_RGB_COLORS );

	file = CreateFile( path.c_str(), GENERIC_WRITE, 0, NULL,
                           CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
	WriteFile( file, &fileheader, sizeof( BITMAPFILEHEADER ), &wb, NULL );
	WriteFile( file, &infoheader.bmiHeader, sizeof( infoheader.bmiHeader ), &wb, NULL );
	WriteFile( file, dwpBits, bitmap.bmWidth * bitmap.bmHeight * 4, &wb, NULL );
	CloseHandle( file );

	delete [] dwpBits;
    }

    HDC getDC()     { return hdc; }
    int getWidth()  { return width; }
    int getHeight() { return height; }

private:
    HBITMAP bmp;
    HDC	    hdc;
    HPEN    pen;
    void    *pBits;
    int	    width, height;
};

class brownianTree
{
public:
    brownianTree()         
    { 
	_bmp.create( MAX_SIDE, MAX_SIDE );
	init(); 
    }

    void init()
    {
	_cellCount = 0;
	ZeroMemory( _grid, sizeof( _grid ) );
	_bmp.clear();
	_state = SEED;
    }

bool mainLoop()
    {
	switch( _state )
	{
	    case REST:    saveTree(); return false;
	    case SEED:    doSeed(); break;
	    case GROWING: startMovers(); break;
	    case MOVING:  moveMovers();
	}
	    return true;
	}

    myBitmap* getBmp() { return &_bmp; }

private:
    void saveTree()
    {
	for( int y = 0; y < MAX_SIDE; y++ )
	    for( int x = 0; x < MAX_SIDE; x++ )
		if( _grid[x][y] == TREE )
		    SetPixel( _bmp.getDC(), x, y, RGB( 255, 120, 0 ) );

        _bmp.saveBitmap( "f:\\rc\\tree.bmp" );
    }

    void doSeed()
    {
	int x = MAX_SIDE - MAX_SIDE / 2, y = MAX_SIDE / 4;
	_grid[rand() % x + y][rand() % x + y] = TREE;
	_cellCount++;
	_state = GROWING;
    }

    void addMover( movers* m )
    {
	m->moving = true;
	int x = MAX_SIDE - MAX_SIDE / 2, y = MAX_SIDE / 4, a, b;
	while( true )
	{
	    a = rand() % x + y; b = rand() % x + y;
	    if( _grid[a][b] == NONE ) break;
	}

	m->pos.set( a, b );
	_grid[a][b] = MOVER;
    }

    void startMovers()
    {
	movers* m;
	for( int c = 0; c < MAX_MOVERS; c++ )
	{
	    m = &_movers[c];
	    addMover( m );
	}
	_state = MOVING;
    }

    void addToTree( movers* m )
    {
	m->moving = false;
	_grid[m->pos.x][m->pos.y] = TREE;
	if( ++_cellCount >= MAX_CELLS ) _state = REST;

	COORD c = { 0, 1 };
	SetConsoleCursorPosition( GetStdHandle( STD_OUTPUT_HANDLE ), c );
	cout << "Cells added: " << _cellCount
             << " from " << MAX_CELLS << " => "
             <<  static_cast<float>( 100 * _cellCount ) /
                 static_cast<float>( MAX_CELLS )
             << "%              ";
    }

    bool moveIt( movers* m )
    {
	point f[8]; int ff = 0;
	for( int y = -1; y < 2; y++ )
	{
	    for( int x = -1; x < 2; x++ )
	    {
		if( !x && !y ) continue;
		int a = m->pos.x + x, b = m->pos.y + y;
		if( a < 0 || b < 0 || a >= MAX_SIDE || b >= MAX_SIDE )
		{
		    addToTree( m );
		    return true;
		}
		switch( _grid[a][b] )
		{
		    case TREE:
			addToTree( m );
			return true;
		    case NONE:
			f[ff++].set( a, b );
		}
	    }
        }

	if( ff < 1 ) return false;

	_grid[m->pos.x][m->pos.y] = NONE;
	m->pos = f[rand() % ff];
	_grid[m->pos.x][m->pos.y] = MOVER;

	return false;
    }

    void moveMovers()
    {
	movers* mm;
	for( int m = 0; m < MAX_MOVERS; m++ )
	{
	    mm = &_movers[m];
	    if( !mm->moving ) continue;
	    if( moveIt( mm ) && _cellCount < MAX_CELLS ) addMover( mm );
	}
    }

    states   _state;
    BYTE     _grid[MAX_SIDE][MAX_SIDE];
    myBitmap _bmp;
    int      _cellCount;
    movers   _movers[MAX_MOVERS];
};

int main( int argc, char* argv[] )
{
    ShowWindow( GetConsoleWindow(), SW_MAXIMIZE );
    srand( GetTickCount() );

    brownianTree tree;

    DWORD now = GetTickCount();
    while( tree.mainLoop() );

    now = GetTickCount() - now;
    cout << endl << endl << "It took "
         << now / 1000
         << " seconds to complete the task!" << endl << endl;

    BitBlt( GetDC( GetConsoleWindow() ), 20, 90, MAX_SIDE, MAX_SIDE,
            tree.getBmp()->getDC(), 0, 0, SRCCOPY );

    system( "pause" );
    return 0;
}











Ada: with Ada.Numerics.Discrete_Random;

with SDL.Video.Windows.Makers;
with SDL.Video.Renderers.Makers;
with SDL.Events.Events;

procedure Brownian_Tree is

   Width   : constant := 800;
   Height  : constant := 600;
   Points  : constant := 50_000;

   subtype Width_Range  is Integer range 1 .. Width;
   subtype Height_Range is Integer range 1 .. Height;
   type    Direction    is (N, NE, E, SE, S, SW, W, NW);
   package Random_Width   is new Ada.Numerics.Discrete_Random (Width_Range);
   package Random_Height  is new Ada.Numerics.Discrete_Random (Height_Range);
   package Random_Direc   is new Ada.Numerics.Discrete_Random (Direction);

   Window   : SDL.Video.Windows.Window;
   Renderer : SDL.Video.Renderers.Renderer;
   Event    : SDL.Events.Events.Events;

   Width_Gen  : Random_Width.Generator;
   Height_Gen : Random_Height.Generator;
   Direc_Gen  : Random_Direc.Generator;

   function Poll_Quit return Boolean is
      use type SDL.Events.Event_Types;
   begin
      while SDL.Events.Events.Poll (Event) loop
         if Event.Common.Event_Type = SDL.Events.Quit then
            return True;
         end if;
      end loop;
      return False;
   end Poll_Quit;

   procedure Draw_Brownian_Tree is
      Field : array (Width_Range, Height_Range) of Boolean := (others => (others => False));
      X     : Width_Range;
      Y     : Height_Range;
      Direc : Direction;

      procedure Random_Free (X : out Width_Range; Y : out Height_Range) is
      begin
         
         loop
            X := Random_Width.Random (Width_Gen);
            Y := Random_Height.Random (Height_Gen);
            exit when Field (X, Y) = False;
         end loop;
      end Random_Free;

   begin
      
      Field (Random_Width.Random  (Width_Gen),
             Random_Height.Random (Height_Gen)) := True;

      for I in 0 .. Points loop
         Random_Free (X, Y);
         loop
            
            while
              X = Width_Range'First  or X = Width_Range'Last or
              Y = Height_Range'First or Y = Height_Range'Last
            loop
               Random_Free (X, Y);
            end loop;
            exit when Field (X - 1, Y - 1) or Field (X, Y - 1) or Field (X + 1, Y - 1);
            exit when Field (X - 1, Y)     or                     Field (X + 1, Y);
            exit when Field (X - 1, Y + 1) or Field (X, Y + 1) or Field (X + 1, Y + 1);
            Direc := Random_Direc.Random (Direc_Gen);
            case Direc is
               when NW | N | NE => Y := Y - 1;
               when SW | S | SE => Y := Y + 1;
               when others => null;
            end case;
            case Direc is
               when NW | W | SW => X := X - 1;
               when SE | E | NE => X := X + 1;
               when others => null;
            end case;
         end loop;
         Field (X, Y) := True;
         Renderer.Draw (Point => (SDL.C.int (X), SDL.C.int (Y)));

         if I mod 100 = 0 then
            if Poll_Quit then
               return;
            end if;
            Window.Update_Surface;
         end if;
      end loop;
   end Draw_Brownian_Tree;

begin
   Random_Width.Reset  (Width_Gen);
   Random_Height.Reset (Height_Gen);
   Random_Direc.Reset  (Direc_Gen);

   if not SDL.Initialise (Flags => SDL.Enable_Screen) then
      return;
   end if;

   SDL.Video.Windows.Makers.Create (Win      => Window,
                                    Title    => "Brownian tree",
                                    Position => SDL.Natural_Coordinates'(X => 10, Y => 10),
                                    Size     => SDL.Positive_Sizes'(Width, Height),
                                    Flags    => 0);
   SDL.Video.Renderers.Makers.Create (Renderer, Window.Get_Surface);
   Renderer.Set_Draw_Colour ((0, 0, 0, 255));
   Renderer.Fill (Rectangle => (0, 0, Width, Height));
   Renderer.Set_Draw_Colour ((200, 200, 200, 255));

   Draw_Brownian_Tree;
   Window.Update_Surface;

   loop
      exit when Poll_Quit;
      delay 0.050;
   end loop;
   Window.Finalize;
   SDL.Finalise;
end Brownian_Tree;