Guest User

Cellular automata

a guest
Jan 8th, 2014
1,752
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import 'dart:html';
  2. import 'dart:math';
  3. import 'dart:async';
  4.  
  5. class Main {
  6.     static int cellXQuantity, cellYQuantity, cellSize;
  7.     static final CanvasElement canvas = new CanvasElement();
  8.     static TableCellElement cell;
  9.     static final SelectElement fieldSizeCombobox = new SelectElement();
  10.     static final SelectElement initialConfCombobox = new SelectElement();
  11.     static final SelectElement fpsCombobox = new SelectElement();
  12.     static final ButtonElement pauseButton = new ButtonElement();
  13.     static Timer timer;
  14.  
  15.     static final Map<String, int> fieldSizeMap = { "256x256 field" : 256, "128x128 field" : 128,  "64x64 field" : 64, "512x512 field" : 512 };
  16.  
  17.     static String get configurationName => initialConfCombobox.options[ initialConfCombobox.selectedIndex ].label;
  18.  
  19.     static final Map<String, int> fpsMap = { "Max FPS" : 1, "200 FPS" : 5, "25 FPS" : 40, "5 FPS" : 200 };
  20.  
  21.     static int get time => fpsMap[ fpsCombobox.options[ fpsCombobox.selectedIndex ].label ];
  22.  
  23.     static bool get paused => pauseButton.text == "Resume";
  24.  
  25.     static int mouseCellX( MouseEvent event ) => event.offset.x ~/ cellSize;
  26.     static int mouseCellY( MouseEvent event ) => event.offset.y ~/ cellSize;
  27.  
  28.  
  29.     static int oldMouseX, oldMouseY;
  30.     static bool paint = false, mode;
  31.  
  32.     static void init() {
  33.         canvas.onMouseMove.listen( (event) {
  34.             int x = mouseCellX( event );
  35.             int y = mouseCellY( event );
  36.             if( paint && paused && ( x != oldMouseX || y != oldMouseY ) ) {
  37.                 Cell.set( mouseCellX( event ), mouseCellY( event ), mode );
  38.                 oldMouseX = x;
  39.                 oldMouseY = y;
  40.                 draw();
  41.             }
  42.         } );
  43.         canvas.onMouseDown.listen( (event) {
  44.             paint = true;
  45.             mode = !Cell.getState( mouseCellX( event ), mouseCellY( event ) );
  46.             oldMouseX = 0;
  47.             oldMouseY = 0;
  48.         } );
  49.         canvas.onMouseUp.listen( (event) {
  50.             paint = false;
  51.         } );
  52.  
  53.  
  54.         fieldSizeCombobox.onChange.listen( (event) {
  55.             fieldInit( fieldSizeMap[ fieldSizeCombobox.options[ fieldSizeCombobox.selectedIndex ].label ] );
  56.         } );
  57.  
  58.         initialConfCombobox.onChange.listen( (event) {
  59.             Cell.createConfiguration();
  60.         } );
  61.  
  62.         fpsCombobox.onChange.listen( (event) {
  63.             timerInit();
  64.         });
  65.  
  66.         pauseButton
  67.             ..text = "Pause"
  68.             ..onClick.listen( (event) {
  69.                 pauseButton.text = ( paused ? "Pause" : "Resume" );
  70.             } );
  71.  
  72.  
  73.         TableElement table = new TableElement();
  74.         TableRowElement row = table.insertRow( 0 );
  75.         cell = row.insertCell( 0 )
  76.             ..setAttribute( "align", "center" );
  77.         TableCellElement cell2 = table.insertRow( 1 ).insertCell( 0 )
  78.             ..setAttribute( "align", "center" );
  79.  
  80.         for( String caption in Rules.sets.keys ) {
  81.             ButtonElement button = new ButtonElement()
  82.                 ..text = caption
  83.                 ..onClick.listen( (event) {
  84.                     Group.groups.clear();
  85.                     Group.cellStateRule = null;
  86.                     Rules.sets[ caption ]();
  87.                     Cell.createConfiguration();
  88.                 } );
  89.             cell.append( button );
  90.         }
  91.  
  92.         Map<SelectElement, Map<String, Object>> comboboxMap =
  93.             { fieldSizeCombobox : fieldSizeMap, initialConfCombobox : Cell.configurations, fpsCombobox : fpsMap };
  94.         for( SelectElement combobox in comboboxMap.keys ) {
  95.             for( String name in comboboxMap[ combobox ].keys ) {
  96.                 combobox.append( new OptionElement( data:name ) );
  97.             }
  98.             cell2.append( combobox );
  99.         }
  100.  
  101.         cell2.append( pauseButton );
  102.  
  103.         document.body
  104.             ..append( table )
  105.             ..append( canvas );
  106.  
  107.         fieldInit( 256 );
  108.         timerInit();
  109.     }
  110.  
  111.  
  112.     static void fieldInit( int cellQuantity ) {
  113.         cellXQuantity = cellQuantity;
  114.         cellYQuantity = cellQuantity;
  115.         cellSize = 800 ~/ cellQuantity;
  116.  
  117.         Cell.cellXMask = cellXQuantity - 1;
  118.         Cell.cellYMask = cellYQuantity - 1;
  119.         Cell.xCenter = cellXQuantity ~/ 2;
  120.         Cell.yCenter = cellYQuantity ~/ 2;
  121.  
  122.         canvas
  123.             ..width = cellSize * cellXQuantity + 1
  124.             ..height = cellSize * cellYQuantity + 1;
  125.  
  126.         cell.setAttribute( "width", Main.canvas.width.toString() );
  127.  
  128.         Cell.createConfiguration();
  129.     }
  130.  
  131.     static void timerInit() {
  132.         if( timer != null ) timer.cancel();
  133.         timer = new Timer.periodic( new Duration( milliseconds:time ), Cell.gen );
  134.     }
  135.  
  136.     static void draw( [ bool clear = true ] ) {
  137.         CanvasRenderingContext2D context = canvas.context2D;
  138.         if( clear ) {
  139.             context
  140.                 ..fillStyle = '#444'
  141.                 ..fillRect( 0, 0, canvas.width, canvas.height );
  142.         }
  143.         List<Cell> list = clear ? Cell.cells : Cell.togglingCells;
  144.         int size = cellSize > 1 ? cellSize - 1 :cellSize;
  145.         for( Cell cell in list ) {
  146.             context
  147.                 ..fillStyle = cell.state ? '#FFF' : '#444'
  148.                 ..fillRect( ( cell.x & Cell.cellXMask ) * cellSize + 1, ( cell.y & Cell.cellYMask ) * cellSize + 1, size, size );
  149.         }
  150.     }
  151. }
  152.  
  153.  
  154. class Range {
  155.     Group group;
  156.     int from, to;
  157.  
  158.     Range( this.group, this.from, this.to );
  159. }
  160.  
  161.  
  162.  
  163. class Group {
  164.     static List<Group> groups = new List<Group>();
  165.  
  166.     List<List<int>> positions;
  167.     int k = 1;
  168.  
  169.  
  170.     Group( this.positions ){
  171.         groups.add( this );
  172.     }
  173.  
  174.  
  175.     static List<bool> cellStateRule = null;
  176.  
  177.     static void init() {
  178.         int k = 1;
  179.         for( Group group in groups ) {
  180.             group.k = k;
  181.             k *= ( group.positions.length + 1 );
  182.         }
  183.         cellStateRule = new List<bool>( k );
  184.         for( int n = 0; n < k; n++ ) cellStateRule[ n ] = false;
  185.     }
  186.  
  187.     Range range( int from, [ int to ] ) => new Range( this, from, ( to == null ? from : to ) );
  188. }
  189.  
  190.  
  191.  
  192. class Rules {
  193.     static Group cell, neighbors, hor, center, vert, top, bottom, left, right, first, second, diagonal, horvert;
  194.  
  195.     static final Map<String, Function> sets = {
  196.         "Classic Life" : (){
  197.             groupCellNeighbors();
  198.             set( [ cell.range( 1 ), neighbors.range( 2, 3 ) ] );
  199.             set( [ cell.range( 0 ), neighbors.range( 3 ) ] );
  200.         }, "SandyLifeHV" : (){
  201.             groupCellDiagonalHorvert();
  202.             set( [ cell.range( 0 ), diagonal.range( 2 ), horvert.range( 0, 1 ) ] );
  203.             set( [ cell.range( 0 ), diagonal.range( 0 ), horvert.range( 2 ) ] );
  204.             set( [ cell.range( 1 ), diagonal.range( 1, 2 ), horvert.range( 1, 2 ) ] );
  205.         }, "SandyLifeD" : (){
  206.             groupCellDiagonalHorvert();
  207.             set( [ cell.range( 0 ), diagonal.range( 2 ), horvert.range( 0 ) ] );
  208.             set( [ cell.range( 0 ), diagonal.range( 0, 1 ), horvert.range( 2 ) ] );
  209.             set( [ cell.range( 1 ), diagonal.range( 1, 2 ), horvert.range( 1, 2 ) ] );
  210.         }, "AmoebaLife" : (){
  211.             groupCellDiagonalHorvert();
  212.             set( [ cell.range( 0 ), diagonal.range( 2 ), horvert.range( 0, 2 ) ] );
  213.             set( [ cell.range( 0 ), diagonal.range( 0, 2 ), horvert.range( 2 ) ] );
  214.             set( [ cell.range( 1 ), diagonal.range( 2 ), horvert.range( 0, 2 ) ] );
  215.         }, "ElectronicLife" : (){
  216.             groupCellDiagonalHorvert();
  217.             set( [ cell.range( 0 ), diagonal.range( 2, 3 ), horvert.range( 2 ) ] );
  218.             set( [ cell.range( 0 ), diagonal.range( 0, 1 ), horvert.range( 2, 3 ) ] );
  219.             set( [ cell.range( 1 ), diagonal.range( 1, 2 ), horvert.range( 1, 2 ) ] );
  220.         }, "LiquidLife" : (){
  221.             groupTopCenterBottom();
  222.             set( [ top.range( 0, 1 ), center.range( 1, 3 ), bottom.range( 2, 3 ) ] );
  223.             set( [ top.range( 1, 2 ), center.range( 2, 3 ), bottom.range( 0, 1 ) ] );
  224.             set( [ top.range( 2, 3 ), center.range( 1, 3 ), bottom.range( 1, 2 ) ] );
  225.         }, "ArmadaLife" : (){
  226.             groupTopCenterBottom();
  227.             set( [ top.range( 0, 1 ), center.range( 1, 2 ), bottom.range( 2, 3 ) ] );
  228.             set( [ top.range( 1, 3 ), center.range( 1, 2 ), bottom.range( 1, 3 ) ] );
  229.             set( [ top.range( 2, 3 ), center.range( 1, 2 ), bottom.range( 0, 1 ) ] );
  230.         }, "WireLife" : () {
  231.             groupHorCenterVert();
  232.             set( [ hor.range( 2 ), center.range( 0 ), vert.range( 2, 3 ) ] );
  233.             set( [ hor.range( 1, 3 ), center.range( 1 ), vert.range( 1, 2 ) ] );
  234.         }, "TwirlLife" : () {
  235.             groupHorCenterVert();
  236.             set( [ hor.range( 2 ), center.range( 0 ), vert.range( 2 ) ] );
  237.             set( [ hor.range( 1, 3 ), center.range( 1 ), vert.range( 1, 3 ) ] );
  238.         }, "Fractal 1" : (){
  239.             groupCellDiagonalHorvert();
  240.             set( [ cell.range( 0 ), horvert.range( 1 ) ] );
  241.             set( [ cell.range( 1 ) ] );
  242.         }, "Fractal 2" : (){
  243.             groupCellDiagonalHorvert();
  244.             set( [ cell.range( 0 ), diagonal.range( 1 ) ] );
  245.             set( [ cell.range( 1 ) ] );
  246.         }, "Carpet" : (){
  247.             groupCellDiagonalHorvert();
  248.             set( [ cell.range( 0 ), diagonal.range( 1, 2 ), horvert.range( 0 ) ] );
  249.             set( [ cell.range( 0 ), diagonal.range( 0 ), horvert.range( 1, 2 ) ] );
  250.             set( [ cell.range( 1 ), diagonal.range( 0, 2 ), horvert.range( 0, 2 ) ] );
  251.         }, "Snakes" : (){
  252.             groupCellFirstSecond();
  253.             set( [ cell.range( 0 ), first.range( 2, 3 ), second.range( 0 ) ] );
  254.             set( [ cell.range( 0 ), first.range( 0 ), second.range( 2, 3 ) ] );
  255.             set( [ cell.range( 1 ), first.range( 1, 2 ), second.range( 1, 2 ) ] );
  256.         }, "Mice": () {
  257.             groupTopCenterBottom();
  258.             set( [ top.range( 0, 2 ), center.range( 1, 2 ), bottom.range( 2, 3 ) ] );
  259.             set( [ top.range( 1, 2 ), center.range( 1 ), bottom.range( 0, 2 ) ] );
  260.             set( [ top.range( 2, 3 ), center.range( 1, 2 ), bottom.range( 1, 2 ) ] );
  261.         }, "Fiber" : (){
  262.             groupTopCenterBottom();
  263.             set( [ top.range( 0, 0 ), center.range( 1, 2 ), bottom.range( 2, 3 ) ] );
  264.             set( [ top.range( 1, 2 ), center.range( 1, 3 ), bottom.range( 1, 2 ) ] );
  265.             set( [ top.range( 2, 3 ), center.range( 1, 2 ), bottom.range( 0 ) ] );
  266.         }, "Smoke" : () {
  267.             groupTopLeftCenterBottomRight();
  268.             set( [ center.range( 0 ), left.range( 3, 3 ), right.range( 1, 3 ) ] );
  269.             set( [ center.range( 0 ), left.range( 1, 3 ), right.range( 3, 3 ) ] );
  270.             set( [ center.range( 0 ), top.range( 3, 3 ), bottom.range( 0, 1 ) ] );
  271.             set( [ center.range( 0 ), top.range( 0, 1 ), bottom.range( 3, 3 ) ] );
  272.             set( [ center.range( 1 ), bottom.range( 1, 3 ), top.range( 1, 3 ) ] );
  273.         }, "Fall" : () {
  274.             groupTopLeftCenterBottomRight();
  275.             int k1=2, k2=3, k3=1, k4=2;
  276.             set( [ center.range( 0 ), left.range( k1, k2 ), right.range( k3, k4 ) ] );
  277.             set( [ center.range( 0 ), left.range( k3, k4 ), right.range( k1, k2 ) ] );
  278.             set( [ center.range( 0 ), top.range( k1, k2 ), bottom.range( k3, k4 ) ] );
  279.             set( [ center.range( 0 ), top.range( k3, k4 ), bottom.range( k1, k2 ) ] );
  280.             set( [ center.range( 1 ), bottom.range( 0, 1 ), top.range( 1, 3 ) ] );
  281.         }, "Freezer" : () {
  282.             groupTopLeftCenterBottomRight();
  283.             set( [ center.range( 0 ), left.range( 1, 2 ), bottom.range( 2, 2 ) ] );
  284.             set( [ center.range( 0 ), top.range( 2, 2 ), right.range( 1, 2 ) ] );
  285.             set( [ center.range( 1 ), bottom.range( 1, 3 ), left.range( 0, 2 ) ] );
  286.             set( [ center.range( 1 ), top.range( 0, 2 ), right.range( 1, 3 ) ] );
  287.         }
  288.     };
  289.  
  290.  
  291.     static void groupCellNeighbors() {
  292.         cell = new Group( [ [ 0, 0 ] ] );
  293.         neighbors = new Group( [ [ -1, -1 ], [ 1, -1 ], [ 1, 1 ], [ -1, 1 ], [ 0, -1 ], [ -1, 0 ], [ 1, 0 ], [ 0, 1 ] ] );
  294.     }
  295.  
  296.     static void groupCellFirstSecond() {
  297.         cell = new Group( [ [ 0, 0 ] ] );
  298.         first = new Group( [ [ -1, 0 ], [ -1, -1 ], [ 0, -1 ], [ 1, -1 ] ] );
  299.         second = new Group( [ [ -1, 1 ], [ 0, 1 ], [ 1, 1 ], [ 1, 0 ] ] );
  300.     }
  301.  
  302.     static void groupHorCenterVert() {
  303.         hor = new Group( [ [ -1, -1 ], [ 0, -1 ], [ 1, -1 ], [ -1, 1 ], [ 0, 1 ], [ 1, 1 ] ] );
  304.         center = new Group( [ [ 0, 0 ] ] );
  305.         vert = new Group( [ [ -1, -1 ], [ -1, 0 ], [ -1, 1 ], [ 1, -1 ], [ 1, 0 ], [ 1, 1 ] ] );
  306.     }
  307.  
  308.     static void groupTopCenterBottom() {
  309.         top = new Group( [ [ -1, -1 ], [ 0, -1 ], [ 1, -1 ] ] );
  310.         center = new Group( [ [ -1, 0 ], [ 0, 0 ], [ 1, 0 ] ] );
  311.         bottom = new Group( [ [ -1, 1 ], [ 0, 1 ], [ 1, 1 ] ] );
  312.     }
  313.  
  314.     static void groupTopLeftCenterBottomRight() {
  315.         top = new Group( [ [ -1, -1 ], [ 0, -1 ], [ 1, -1 ] ] );
  316.         left = new Group( [ [ -1, -1 ], [ -1, 0 ], [ -1, 1 ] ] );
  317.         center = new Group( [ [ 0, 0 ] ] );
  318.         bottom = new Group( [ [ -1, 1 ], [ 0, 1 ], [ 1, 1 ] ] );
  319.         right = new Group( [ [ 1, -1 ], [ 1, 0 ], [ 1, 1 ] ] );
  320.     }
  321.  
  322.     static void groupCellDiagonalHorvert() {
  323.         cell = new Group( [ [ 0, 0 ] ] );
  324.         diagonal = new Group( [ [ -1, -1 ], [ 1, -1 ], [ 1, 1 ], [ -1, 1 ] ] );
  325.         horvert = new Group( [ [ 0, -1 ], [ -1, 0 ], [ 1, 0 ], [ 0, 1 ] ] );
  326.     }
  327.  
  328.  
  329.     static void set( List<Range> ranges, [bool cellState = true, int groupIndex = 0, int arrayIndex = 0 ] ) {
  330.         if( Group.cellStateRule == null ) Group.init();
  331.         Group group = Group.groups[ groupIndex ];
  332.         Range range = ranges.firstWhere( (range) => range.group == group, orElse: () => null );
  333.         for( int n = ( range == null ? 0 : range.from ); n <= ( range == null ? group.positions.length : range.to ); n++ ) {
  334.             int index = arrayIndex + group.k * n;
  335.             if( group == Group.groups.last ) {
  336.                 Group.cellStateRule[ index ] = cellState;
  337.             } else {
  338.                 set( ranges, cellState, groupIndex + 1, index );
  339.             }
  340.         }
  341.     }
  342. }
  343.  
  344.  
  345.  
  346. class Cell {
  347.     static List<Cell> cells = new List<Cell>();
  348.     static List<Cell> cellSpace;
  349.  
  350.     static int cellXMask, cellYMask, xCenter, yCenter;
  351.  
  352.     static int cellAddress( x, y ) => ( x & cellXMask ) + Main.cellXQuantity * ( y & cellYMask );
  353.  
  354.     static bool getState( x, y ) {
  355.         Cell cell = cellSpace[ cellAddress( x, y ) ];
  356.         return ( cell == null ? false : cell.state );
  357.     }
  358.  
  359.     static void createCell( x, y ) => Cell.toggle( xCenter + x, yCenter + y );
  360.  
  361.     int x, y, ruleIndex = 0;
  362.     bool state, active = true;
  363.  
  364.     Cell( this.x, this.y, this.state );
  365.  
  366.  
  367.     static Cell get( x, y ) {
  368.         int address = cellAddress( x, y );
  369.         Cell cell = cellSpace[ address ];
  370.         if( cell == null ) {
  371.             cell = new Cell( x, y, false );
  372.             cellSpace[ address ] = cell;
  373.             cells.add( cell );
  374.         }
  375.         return cell;
  376.     }
  377.  
  378.     static int cellsQuantity;
  379.  
  380.     static void set( x, y, bool state, [ bool toggle = false ] ) {
  381.         Cell cell = get( x, y );
  382.         if( toggle ) state = !cell.state;
  383.         if( state == cell.state ) return;
  384.         cellsQuantity += state ? 1 : -1;
  385.         int k = ( state ? 1 : -1 );
  386.         for( Group group in Group.groups ) {
  387.             for( List<int> coords in group.positions ) {
  388.                 Cell cell = get( x - coords[ 0 ], y - coords[ 1 ] );
  389.                 cell.ruleIndex += k * group.k;
  390.                 if( !cell.active ) {
  391.                     cells.add( cell );
  392.                     cell.active = true;
  393.                 }
  394.             }
  395.         }
  396.         cell.state = state;
  397.     }
  398.  
  399.     static void toggle( x, y ) => set( x, y, false, true );
  400.  
  401.  
  402.     static int genNum = 0;
  403.  
  404.     static final List<Cell> togglingCells = new List<Cell>();
  405.  
  406.     static DateTime lastFPStime = new DateTime.now();
  407.     static int fps = 0, fpsCounter = 0;
  408.  
  409.     static void gen( Timer timer ) {
  410.         if( Main.paused || cells.isEmpty ) return;
  411.  
  412.         for( Cell cell in cells ) {
  413.             if( Group.cellStateRule[ cell.ruleIndex ] != cell.state ) togglingCells.add( cell );
  414.             cell.active = false;
  415.         }
  416.  
  417.         cells.clear();
  418.  
  419.         for( Cell cell in togglingCells ) toggle( cell.x, cell.y );
  420.  
  421.         genNum++;
  422.         document.title = "$cellsQuantity / $genNum / $fps";
  423.         Main.draw( false );
  424.  
  425.         togglingCells.clear();
  426.  
  427.         DateTime now = new DateTime.now();
  428.         if( now.isAfter( lastFPStime ) ) {
  429.             lastFPStime = now.add( new Duration( seconds: 1 ) );
  430.             fps = fpsCounter;
  431.             fpsCounter = 0;
  432.         } else {
  433.             fpsCounter++;
  434.         }
  435.     }
  436.  
  437.  
  438.     static final Map<String, Function> configurations = {
  439.         "Random area 1/4" : (){ createArea( Main.cellXQuantity ~/ 2 ); },
  440.         "Random area 1/16" : (){ createArea( Main.cellXQuantity ~/ 4 ); },
  441.         "Random area 1/64" : (){ createArea( Main.cellXQuantity ~/ 8 ); },
  442.         "Random area 1" : (){ createArea( Main.cellXQuantity ); },
  443.         "Pentamino" : (){ createPolyomino( 5 ); },
  444.         "Hexamino" : (){ createPolyomino( 6 ); },
  445.         "Heptamino" : (){ createPolyomino( 7 ); },
  446.         "Single cell" : (){ createCell( 0, 0 ); }
  447.     };
  448.  
  449.     static void createConfiguration() {
  450.         if( Group.groups.isEmpty ) return;
  451.  
  452.         genNum = 0;
  453.         cellsQuantity = 0;
  454.  
  455.         cells.clear();
  456.         cellSpace = new List<Cell>( Main.cellXQuantity * Main.cellYQuantity );
  457.         configurations[ Main.configurationName ]();
  458.         Main.draw();
  459.     }
  460.  
  461.     static void createArea( int size ) {
  462.         Random random = new Random();
  463.         size = ( size - 1 ) ~/ 2;
  464.         for( int y = -size - 1; y <= size; y++ ) {
  465.             for( int x = -size - 1; x <= size; x++ ) {
  466.                 if( random.nextInt( 2 ) == 1 ) createCell( x, y );
  467.             }
  468.         }
  469.     }
  470.  
  471.     static void createPolyomino( int cellsQuantity ) {
  472.         createCell( 0, 0 );
  473.         Random random = new Random();
  474.         for( int n = 1; n < cellsQuantity; n++ ) {
  475.             int x, y;
  476.             while( true ) {
  477.                 Cell cell = cells[ random.nextInt( cells.length ) ];
  478.                 if( !cell.state ) continue;
  479.                 int posIndex = random.nextInt( 4 );
  480.                 x = cell.x + [ 0, -1, 1, 0 ][ posIndex ];
  481.                 y = cell.y + [ -1, 0, 0, 1 ][ posIndex ];
  482.                 if( !get( x, y ).state ) break;
  483.             };
  484.             toggle( x, y );
  485.         }
  486.     }
  487. }
  488.  
  489.  
  490. void main() {
  491.     Main.init();
  492. }
Advertisement
Add Comment
Please, Sign In to add comment