// Copyright (c) 2010-2023, Lawrence Livermore National Security, LLC. Produced // at the Lawrence Livermore National Laboratory. All Rights reserved. See files // LICENSE and NOTICE for details. LLNL-CODE-806117. // // This file is part of the MFEM library. For more information and source code // availability visit https://mfem.org. // // MFEM is free software; you can redistribute it and/or modify it under the // terms of the BSD-3 license. We welcome feedback and contributions, see file // CONTRIBUTING.md for details. // // ---------------------------------------- // Life Miniapp: Model of the Game of Life // ---------------------------------------- // // This miniapp implements Conway's Game of Life. A few simple starting // positions are available as well as a random initial state. The game will // terminate only if two successive iterations are identical. // // See the output of 'life -h' for more options. // // Compile with: make life // // Sample runs: life // life -nx 30 // life -nx 100 -ny 100 -r 0.3 // life -g '2 3 0' // life -b '10 10 0' -g '2 2 0' // life -b '10 10 1' -g '2 2 0' // life -sp '8 10 0 1 1 1 2 1 1 1' // life -nx 30 -sp '11 11 1 1 1 1 1 1 1 1 2 1 0 1 1 1 1 0 1 2 1 1 1 1 1 1 1 1' #include "mfem.hpp" #include #include #include #include #include #include using namespace std; using namespace mfem; bool GameStep(vector * b[], int nx, int ny); void ProjectStep(const vector & b, GridFunction & x, int n); bool InitSketchPad(vector & b, int nx, int ny, const Array & params); bool InitBlinker(vector & b, int nx, int ny, const Array & params); bool InitGlider(vector & b, int nx, int ny, const Array & params); bool InitMFEM(vector & b, int nx, int ny); int main(int argc, char *argv[]) { // 1. Parse command-line options. int nx = 20; int ny = 20; int rs = -1; double r = -1.0; Array sketch_pad_params(0); Array blinker_params(0); Array glider_params(0); bool visualization = 1; OptionsParser args(argc, argv); args.AddOption(&nx, "-nx", "--num-elems-x", "Number of elements in the x direction."); args.AddOption(&ny, "-ny", "--num-elems-y", "Number of elements in the y direction."); args.AddOption(&r, "-r", "--random-fraction", "Fraction of randomly chosen live cells."); args.AddOption(&rs, "-rs", "--random-seed", "Seed for the random number generator."); args.AddOption(&sketch_pad_params, "-sp", "--sketch-pad", "Specify the starting coordinates and values on a grid" " of cells. The values can be 0, 1, or 2. Where 0 and 1" " indicate cells that are off or on and 2 represents a" " newline character."); args.AddOption(&blinker_params, "-b", "--blinker", "Specify the starting coordinates and orientation (0 or 1)" " of the blinker. Multiple blinkers can be specified as " "'x0 y0 o0 x1 y1 o1 ...'."); args.AddOption(&glider_params, "-g", "--glider", "Specify the starting coordinates and " "orientation (0,1,2, or 3) of the glider. " "Multiple gliders can be specified as " "'x0 y0 o0 x1 y1 o1 ...'."); args.AddOption(&visualization, "-vis", "--visualization", "-no-vis", "--no-visualization", "Enable or disable GLVis visualization."); args.Parse(); if (!args.Good()) { args.PrintUsage(cout); return 1; } args.PrintOptions(cout); // 2. Build a rectangular mesh of quadrilateral elements. Mesh mesh = Mesh::MakeCartesian2D(nx, ny, Element::QUADRILATERAL, 0, nx, ny, false); // 3. Define a finite element space on the mesh. Here we use discontinuous // Lagrange finite elements of order zero i.e. piecewise constant basis // functions. L2_FECollection fec(0, 2); FiniteElementSpace fespace(&mesh, &fec); // 4. Initialize a pair of bit arrays to store two copies of the // playing field. int len = nx * ny; vector * vbp[2]; vector vb0(len); vector vb1(len); vbp[0] = &vb0; vbp[1] = &vb1; if ( r > 0.0 ) { unsigned int seed; if ( rs < 0 ) { srand(time(NULL)); seed = (unsigned int)rand(); } else { seed = (unsigned int)rs; } cout << "Using random seed: " << seed << endl; srand(seed); } bool init = false; if (r > 0) { for (int i=0; i 2 ) { init = InitSketchPad(vb0, nx, ny, sketch_pad_params); } if ( blinker_params.Size() > 0 && (blinker_params.Size() % 3 == 0 ) ) { init = InitBlinker(vb0, nx, ny, blinker_params); } if ( glider_params.Size() > 0 && (glider_params.Size() % 3 == 0 ) ) { init = InitGlider(vb0, nx, ny, glider_params); } if (!init) { init = InitMFEM(vb0, nx, ny); } // 5. Define the vector x as a finite element grid function corresponding // to fespace which will be used to visualize the playing field. // Initialize x with the starting layout set above. GridFunction x(&fespace); ProjectStep(*vbp[0], x, len); // 6. Open a socket to GLVis socketstream sol_sock; if (visualization) { char vishost[] = "localhost"; int visport = 19916; sol_sock.open(vishost, visport); } // 7. Apply the rule iteratively cout << endl << "Running the Game of Life..." << flush; bool is_good = true; bool is_stable = false; while ( is_good && visualization && !is_stable ) { is_stable = GameStep(vbp, nx, ny); ProjectStep(*vbp[1], x, len); // Swap bit arrays std::swap(vbp[0], vbp[1]); // 8. Send the solution by socket to a GLVis server. is_good = sol_sock.good(); if (visualization && is_good ) { sol_sock << "solution\n" << mesh << x << flush; { static int once = 1; if (once) { sol_sock << "keys Ajlm\n"; sol_sock << "view 0 0\n"; sol_sock << "zoom 1.9\n"; sol_sock << "palette 24\n"; once = 0; } if (is_stable) { sol_sock << "valuerange 0 1\n"; } } } } cout << "done." << endl; // 9. Save the mesh and the final state of the game. This output can be // viewed later using GLVis: "glvis -m life.mesh -g life.gf". ofstream mesh_ofs("life.mesh"); mesh_ofs.precision(8); mesh.Print(mesh_ofs); ofstream sol_ofs("life.gf"); sol_ofs.precision(8); x.Save(sol_ofs); return 0; } inline int index(int i, int j, int nx, int ny) { return ((j + ny) % ny) * nx + ((i + nx) % nx); } bool GameStep(vector * b[], int nx, int ny) { bool is_stable = true; for (int j=0; j & b, GridFunction & x, int n) { for (int i=0; i & b, int nx, int ny, const Array & params) { for (int i=0; i & b, int nx, int ny, const Array & params) { for (int i=0; i & b, int nx, int ny, const Array & params) { int cx = params[0]; int cy = params[1]; int ox = 0; int oy = 0; for (int i=2; i & b, int nx, int ny) { int ox = 0; int oy = 0; int wx = (nx >= 23) ? 23 : 5; int hy = (ny >= 7) ? 7 : 5; if (wx == 23) { // Write out "MFEM" ox = (nx - 23) / 2; oy = (ny - hy) / 2; for (int j=0; j