MFEM  v4.3.0
Finite element discretization library
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
life.cpp
Go to the documentation of this file.
1 // Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
2 // at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3 // LICENSE and NOTICE for details. LLNL-CODE-806117.
4 //
5 // This file is part of the MFEM library. For more information and source code
6 // availability visit https://mfem.org.
7 //
8 // MFEM is free software; you can redistribute it and/or modify it under the
9 // terms of the BSD-3 license. We welcome feedback and contributions, see file
10 // CONTRIBUTING.md for details.
11 //
12 // ----------------------------------------
13 // Life Miniapp: Model of the Game of Life
14 // ----------------------------------------
15 //
16 // This miniapp implements Conway's Game of Life. A few simple starting
17 // positions are available as well as a random initial state. The game will
18 // terminate only if two successive iterations are identical.
19 //
20 // See the output of 'life -h' for more options.
21 //
22 // Compile with: make life
23 //
24 // Sample runs: life
25 // life -nx 30
26 // life -nx 100 -ny 100 -r 0.3
27 // life -g '2 3 0'
28 // life -b '10 10 0' -g '2 2 0'
29 // life -b '10 10 1' -g '2 2 0'
30 // life -sp '8 10 0 1 1 1 2 1 1 1'
31 // life -nx 30 -sp '11 11 1 1 1 1 1 1 1 1 2
32 // 1 0 1 1 1 1 0 1 2
33 // 1 1 1 1 1 1 1 1'
34 
35 #include "mfem.hpp"
36 #include <algorithm>
37 #include <cstdlib>
38 #include <fstream>
39 #include <iostream>
40 #include <bitset>
41 #include <vector>
42 
43 using namespace std;
44 using namespace mfem;
45 
46 bool GameStep(vector<bool> * b[], int nx, int ny);
47 void ProjectStep(const vector<bool> & b, GridFunction & x, int n);
48 
49 bool InitSketchPad(vector<bool> & b, int nx, int ny, const Array<int> & params);
50 bool InitBlinker(vector<bool> & b, int nx, int ny, const Array<int> & params);
51 bool InitGlider(vector<bool> & b, int nx, int ny, const Array<int> & params);
52 bool InitMFEM(vector<bool> & b, int nx, int ny);
53 
54 int main(int argc, char *argv[])
55 {
56  // 1. Parse command-line options.
57  int nx = 20;
58  int ny = 20;
59  int rs = -1;
60  double r = -1.0;
61  Array<int> sketch_pad_params(0);
62  Array<int> blinker_params(0);
63  Array<int> glider_params(0);
64  bool visualization = 1;
65 
66  OptionsParser args(argc, argv);
67  args.AddOption(&nx, "-nx", "--num-elems-x",
68  "Number of elements in the x direction.");
69  args.AddOption(&ny, "-ny", "--num-elems-y",
70  "Number of elements in the y direction.");
71  args.AddOption(&r, "-r", "--random-fraction",
72  "Fraction of randomly chosen live cells.");
73  args.AddOption(&rs, "-rs", "--random-seed",
74  "Seed for the random number generator.");
75  args.AddOption(&sketch_pad_params, "-sp", "--sketch-pad",
76  "Specify the starting coordinates and values on a grid"
77  " of cells. The values can be 0, 1, or 2. Where 0 and 1"
78  " indicate cells that are off or on and 2 represents a"
79  " newline character.");
80  args.AddOption(&blinker_params, "-b", "--blinker",
81  "Specify the starting coordinates and orientation (0 or 1)"
82  " of the blinker. Multiple blinkers can be specified as "
83  "'x0 y0 o0 x1 y1 o1 ...'.");
84  args.AddOption(&glider_params, "-g", "--glider",
85  "Specify the starting coordinates and "
86  "orientation (0,1,2, or 3) of the glider. "
87  "Multiple gliders can be specified as "
88  "'x0 y0 o0 x1 y1 o1 ...'.");
89  args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
90  "--no-visualization",
91  "Enable or disable GLVis visualization.");
92  args.Parse();
93  if (!args.Good())
94  {
95  args.PrintUsage(cout);
96  return 1;
97  }
98  args.PrintOptions(cout);
99 
100  // 2. Build a rectangular mesh of quadrilateral elements.
101  Mesh mesh = Mesh::MakeCartesian2D(nx, ny, Element::QUADRILATERAL, 0, nx, ny,
102  false);
103 
104  // 3. Define a finite element space on the mesh. Here we use discontinuous
105  // Lagrange finite elements of order zero i.e. piecewise constant basis
106  // functions.
107  L2_FECollection fec(0, 2);
108  FiniteElementSpace fespace(&mesh, &fec);
109 
110  // 4. Initialize a pair of bit arrays to store two copies of the
111  // playing field.
112  int len = nx * ny;
113 
114  vector<bool> * vbp[2];
115  vector<bool> vb0(len);
116  vector<bool> vb1(len);
117 
118  vbp[0] = &vb0;
119  vbp[1] = &vb1;
120 
121  if ( r > 0.0 )
122  {
123  unsigned int seed;
124  if ( rs < 0 )
125  {
126  srand(time(NULL));
127  seed = (unsigned int)rand();
128  }
129  else
130  {
131  seed = (unsigned int)rs;
132  }
133  cout << "Using random seed: " << seed << endl;
134  srand(seed);
135  }
136 
137  bool init = false;
138  if (r > 0)
139  {
140  for (int i=0; i<len; i++)
141  {
142  double rv = double(rand()) / RAND_MAX;
143  vb0[i] = (rv <= r);
144  vb1[i] = false;
145  }
146  }
147  else
148  {
149  for (int i=0; i<len; i++)
150  {
151  vb0[i] = false;
152  }
153  }
154  if ( sketch_pad_params.Size() > 2 )
155  {
156  init = InitSketchPad(vb0, nx, ny, sketch_pad_params);
157  }
158  if ( blinker_params.Size() > 0 && (blinker_params.Size() % 3 == 0 ) )
159  {
160  init = InitBlinker(vb0, nx, ny, blinker_params);
161  }
162  if ( glider_params.Size() > 0 && (glider_params.Size() % 3 == 0 ) )
163  {
164  init = InitGlider(vb0, nx, ny, glider_params);
165  }
166  if (!init)
167  {
168  init = InitMFEM(vb0, nx, ny);
169  }
170 
171  // 5. Define the vector x as a finite element grid function corresponding
172  // to fespace which will be used to visualize the playing field.
173  // Initialize x with the starting layout set above.
174  GridFunction x(&fespace);
175 
176  ProjectStep(*vbp[0], x, len);
177 
178  // 6. Open a socket to GLVis
179  socketstream sol_sock;
180  if (visualization)
181  {
182  char vishost[] = "localhost";
183  int visport = 19916;
184  sol_sock.open(vishost, visport);
185  }
186 
187  // 7. Apply the rule iteratively
188  cout << endl << "Running the Game of Life..." << flush;
189 
190  bool is_good = true;
191  bool is_stable = false;
192  while ( is_good && visualization && !is_stable )
193  {
194  is_stable = GameStep(vbp, nx, ny);
195  ProjectStep(*vbp[1], x, len);
196 
197  // Swap bit arrays
198  std::swap(vbp[0], vbp[1]);
199 
200  // 8. Send the solution by socket to a GLVis server.
201  is_good = sol_sock.good();
202 
203  if (visualization && is_good )
204  {
205  sol_sock << "solution\n" << mesh << x << flush;
206  {
207  static int once = 1;
208  if (once)
209  {
210  sol_sock << "keys Ajlm\n";
211  sol_sock << "view 0 0\n";
212  sol_sock << "zoom 1.9\n";
213  sol_sock << "palette 24\n";
214  once = 0;
215  }
216  if (is_stable)
217  {
218  sol_sock << "valuerange 0 1\n";
219  }
220  }
221  }
222  }
223  cout << "done." << endl;
224 
225  // 9. Save the mesh and the final state of the game. This output can be
226  // viewed later using GLVis: "glvis -m life.mesh -g life.gf".
227  ofstream mesh_ofs("life.mesh");
228  mesh_ofs.precision(8);
229  mesh.Print(mesh_ofs);
230  ofstream sol_ofs("life.gf");
231  sol_ofs.precision(8);
232  x.Save(sol_ofs);
233 
234  return 0;
235 }
236 
237 inline int index(int i, int j, int nx, int ny)
238 {
239  return ((j + ny) % ny) * nx + ((i + nx) % nx);
240 }
241 
242 bool GameStep(vector<bool> * b[], int nx, int ny)
243 {
244  bool is_stable = true;
245  for (int j=0; j<ny; j++)
246  {
247  for (int i=0; i<nx; i++)
248  {
249  int c =
250  (int)(*b[0])[index(i+0,j+0,nx,ny)] +
251  (int)(*b[0])[index(i+1,j+0,nx,ny)] +
252  (int)(*b[0])[index(i+1,j+1,nx,ny)] +
253  (int)(*b[0])[index(i+0,j+1,nx,ny)] +
254  (int)(*b[0])[index(i-1,j+1,nx,ny)] +
255  (int)(*b[0])[index(i-1,j+0,nx,ny)] +
256  (int)(*b[0])[index(i-1,j-1,nx,ny)] +
257  (int)(*b[0])[index(i+0,j-1,nx,ny)] +
258  (int)(*b[0])[index(i+1,j-1,nx,ny)];
259  switch (c)
260  {
261  case 3:
262  (*b[1])[index(i,j,nx,ny)] = true;
263  break;
264  case 4:
265  (*b[1])[index(i,j,nx,ny)] = (*b[0])[index(i,j,nx,ny)];
266  break;
267  default:
268  (*b[1])[index(i,j,nx,ny)] = false;
269  break;
270  }
271  is_stable &= (*b[1])[index(i,j,nx,ny)] == (*b[0])[index(i,j,nx,ny)];
272  }
273  }
274  return is_stable;
275 }
276 
277 void ProjectStep(const vector<bool> & b, GridFunction & x, int n)
278 {
279  for (int i=0; i<n; i++)
280  {
281  x[i] = (double)b[i];
282  }
283 }
284 
285 bool InitBlinker(vector<bool> & b, int nx, int ny, const Array<int> & params)
286 {
287  for (int i=0; i<params.Size()/3; i++)
288  {
289  int cx = params[3 * i + 0];
290  int cy = params[3 * i + 1];
291  int ornt = params[3 * i + 2];
292 
293  switch (ornt % 2)
294  {
295  case 0:
296  b[index(cx+0,cy+1,nx,ny)] = true;
297  b[index(cx+0,cy+0,nx,ny)] = true;
298  b[index(cx+0,cy-1,nx,ny)] = true;
299  break;
300  case 1:
301  b[index(cx+1,cy+0,nx,ny)] = true;
302  b[index(cx+0,cy+0,nx,ny)] = true;
303  b[index(cx-1,cy+0,nx,ny)] = true;
304  break;
305  }
306  }
307  return true;
308 }
309 
310 bool InitGlider(vector<bool> & b, int nx, int ny, const Array<int> & params)
311 {
312  for (int i=0; i<params.Size()/3; i++)
313  {
314  int cx = params[3 * i + 0];
315  int cy = params[3 * i + 1];
316  int ornt = params[3 * i + 2];
317 
318  switch (ornt % 4)
319  {
320  case 0:
321  b[index(cx-1,cy+0,nx,ny)] = true;
322  b[index(cx+0,cy+1,nx,ny)] = true;
323  b[index(cx+1,cy-1,nx,ny)] = true;
324  b[index(cx+1,cy+0,nx,ny)] = true;
325  b[index(cx+1,cy+1,nx,ny)] = true;
326  break;
327  case 1:
328  b[index(cx+0,cy-1,nx,ny)] = true;
329  b[index(cx-1,cy+0,nx,ny)] = true;
330  b[index(cx-1,cy+1,nx,ny)] = true;
331  b[index(cx+0,cy+1,nx,ny)] = true;
332  b[index(cx+1,cy+1,nx,ny)] = true;
333  break;
334  case 2:
335  b[index(cx+1,cy+0,nx,ny)] = true;
336  b[index(cx+0,cy-1,nx,ny)] = true;
337  b[index(cx-1,cy-1,nx,ny)] = true;
338  b[index(cx-1,cy+0,nx,ny)] = true;
339  b[index(cx-1,cy+1,nx,ny)] = true;
340  break;
341  case 3:
342  b[index(cx+0,cy+1,nx,ny)] = true;
343  b[index(cx+1,cy+0,nx,ny)] = true;
344  b[index(cx-1,cy-1,nx,ny)] = true;
345  b[index(cx+0,cy-1,nx,ny)] = true;
346  b[index(cx+1,cy-1,nx,ny)] = true;
347  break;
348  }
349  }
350  return true;
351 }
352 
353 bool InitSketchPad(vector<bool> & b, int nx, int ny, const Array<int> & params)
354 {
355  int cx = params[0];
356  int cy = params[1];
357 
358  int ox = 0;
359  int oy = 0;
360 
361  for (int i=2; i<params.Size(); i++)
362  {
363  if ( params[i]/2 == 1 )
364  {
365  ox = 0;
366  oy--;
367  }
368  else
369  {
370  b[index(cx+ox,cy+oy,nx,ny)] = (bool)params[i];
371  ox++;
372  }
373  }
374  return true;
375 }
376 
377 bool InitMFEM(vector<bool> & b, int nx, int ny)
378 {
379  int ox = 0;
380  int oy = 0;
381  int wx = (nx >= 23) ? 23 : 5;
382  int hy = (ny >= 7) ? 7 : 5;
383 
384  if (wx == 23)
385  {
386  // Write out "MFEM"
387  ox = (nx - 23) / 2;
388  oy = (ny - hy) / 2;
389 
390  for (int j=0; j<hy; j++)
391  {
392  b[index(ox + 0, oy+j,nx,ny)] = true;
393  b[index(ox + 4, oy+j,nx,ny)] = true;
394  b[index(ox + 6, oy+j,nx,ny)] = true;
395  b[index(ox + 12, oy+j,nx,ny)] = true;
396  b[index(ox + 18, oy+j,nx,ny)] = true;
397  b[index(ox + 22, oy+j,nx,ny)] = true;
398  }
399  for (int i=1; i<5; i++)
400  {
401  b[index(ox + 6 + i, oy + hy - 1,nx,ny)] = true;
402  b[index(ox + 12 + i, oy + 0,nx,ny)] = true;
403  b[index(ox + 12 + i, oy + hy - 1,nx,ny)] = true;
404  }
405  for (int i=1; i<4; i++)
406  {
407  b[index(ox + 6 + i, oy + hy/2,nx,ny)] = true;
408  b[index(ox + 12 + i, oy + hy/2,nx,ny)] = true;
409  }
410  b[index(ox + 1, oy + hy - 2,nx,ny)] = true;
411  b[index(ox + 2, oy + hy - 3,nx,ny)] = true;
412  b[index(ox + 3, oy + hy - 2,nx,ny)] = true;
413 
414  b[index(ox + 19, oy + hy - 2,nx,ny)] = true;
415  b[index(ox + 20, oy + hy - 3,nx,ny)] = true;
416  b[index(ox + 21, oy + hy - 2,nx,ny)] = true;
417  }
418  else if (wx == 5)
419  {
420  // Create a single 'M'
421  ox = (nx - 5) / 2;
422  oy = (ny - hy) / 2;
423 
424  for (int j=0; j<hy; j++)
425  {
426  b[index(ox + 0, oy+j,nx,ny)] = true;
427  b[index(ox + 4, oy+j,nx,ny)] = true;
428  }
429  b[index(ox + 1, oy + hy - 2,nx,ny)] = true;
430  b[index(ox + 2, oy + hy - 3,nx,ny)] = true;
431  b[index(ox + 3, oy + hy - 2,nx,ny)] = true;
432  }
433  else
434  {
435  // Set a single pixel
436  b[index(nx/2,ny/2,nx,ny)] = true;
437  }
438 
439  return true;
440 }
int Size() const
Return the logical size of the array.
Definition: array.hpp:134
virtual void Print(std::ostream &out=mfem::out) const
Definition: mesh.hpp:1387
Class for grid function - Vector with associated FE space.
Definition: gridfunc.hpp:30
virtual void Save(std::ostream &out) const
Save the GridFunction to an output stream.
Definition: gridfunc.cpp:3484
bool InitSketchPad(vector< bool > &b, int nx, int ny, const Array< int > &params)
Definition: life.cpp:353
bool GameStep(vector< bool > *b[], int nx, int ny)
Definition: life.cpp:242
void Parse()
Parse the command-line options. Note that this function expects all the options provided through the ...
Definition: optparser.cpp:150
constexpr char vishost[]
double b
Definition: lissajous.cpp:42
constexpr int visport
bool InitMFEM(vector< bool > &b, int nx, int ny)
Definition: life.cpp:377
void PrintUsage(std::ostream &out) const
Print the usage message.
Definition: optparser.cpp:457
bool InitBlinker(vector< bool > &b, int nx, int ny, const Array< int > &params)
Definition: life.cpp:285
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
Definition: fespace.hpp:87
bool InitGlider(vector< bool > &b, int nx, int ny, const Array< int > &params)
Definition: life.cpp:310
void AddOption(bool *var, const char *enable_short_name, const char *enable_long_name, const char *disable_short_name, const char *disable_long_name, const char *description, bool required=false)
Add a boolean option and set &#39;var&#39; to receive the value. Enable/disable tags are used to set the bool...
Definition: optparser.hpp:82
void ProjectStep(const vector< bool > &b, GridFunction &x, int ns, int s)
Definition: automata.cpp:195
int index(int i, int j, int nx, int ny)
Definition: life.cpp:237
void PrintOptions(std::ostream &out) const
Print the options.
Definition: optparser.cpp:327
int open(const char hostname[], int port)
Open the socket stream on &#39;port&#39; at &#39;hostname&#39;.
int main()
Arbitrary order &quot;L2-conforming&quot; discontinuous finite elements.
Definition: fe_coll.hpp:285
bool Good() const
Return true if the command line options were parsed successfully.
Definition: optparser.hpp:150