MFEM  v4.2.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-2020, 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 = new Mesh(nx, ny, Element::QUADRILATERAL, 0, nx, ny, false);
102 
103  // 3. Define a finite element space on the mesh. Here we use discontinuous
104  // Lagrange finite elements of order zero i.e. piecewise constant basis
105  // functions.
106  FiniteElementCollection *fec = new L2_FECollection(0, 2);
107  FiniteElementSpace *fespace = new FiniteElementSpace(mesh, fec);
108 
109  // 4. Initialize a pair of bit arrays to store two copies of the
110  // playing field.
111  int len = nx * ny;
112 
113  vector<bool> * vbp[2];
114  vector<bool> vb0(len);
115  vector<bool> vb1(len);
116 
117  vbp[0] = &vb0;
118  vbp[1] = &vb1;
119 
120  if ( r > 0.0 )
121  {
122  unsigned int seed;
123  if ( rs < 0 )
124  {
125  srand(time(NULL));
126  seed = (unsigned int)rand();
127  }
128  else
129  {
130  seed = (unsigned int)rs;
131  }
132  cout << "Using random seed: " << seed << endl;
133  srand(seed);
134  }
135 
136  bool init = false;
137  if (r > 0)
138  {
139  for (int i=0; i<len; i++)
140  {
141  double rv = double(rand()) / RAND_MAX;
142  vb0[i] = (rv <= r);
143  vb1[i] = false;
144  }
145  }
146  else
147  {
148  for (int i=0; i<len; i++)
149  {
150  vb0[i] = false;
151  }
152  }
153  if ( sketch_pad_params.Size() > 2 )
154  {
155  init = InitSketchPad(vb0, nx, ny, sketch_pad_params);
156  }
157  if ( blinker_params.Size() > 0 && (blinker_params.Size() % 3 == 0 ) )
158  {
159  init = InitBlinker(vb0, nx, ny, blinker_params);
160  }
161  if ( glider_params.Size() > 0 && (glider_params.Size() % 3 == 0 ) )
162  {
163  init = InitGlider(vb0, nx, ny, glider_params);
164  }
165  if (!init)
166  {
167  init = InitMFEM(vb0, nx, ny);
168  }
169 
170  // 5. Define the vector x as a finite element grid function corresponding
171  // to fespace which will be used to visualize the playing field.
172  // Initialize x with the starting layout set above.
173  GridFunction x(fespace);
174 
175  ProjectStep(*vbp[0], x, len);
176 
177  // 6. Open a socket to GLVis
178  socketstream sol_sock;
179  if (visualization)
180  {
181  char vishost[] = "localhost";
182  int visport = 19916;
183  sol_sock.open(vishost, visport);
184  }
185 
186  // 7. Apply the rule iteratively
187  cout << endl << "Running the Game of Life..." << flush;
188 
189  bool is_good = true;
190  bool is_stable = false;
191  while ( is_good && visualization && !is_stable )
192  {
193  is_stable = GameStep(vbp, nx, ny);
194  ProjectStep(*vbp[1], x, len);
195 
196  // Swap bit arrays
197  std::swap(vbp[0], vbp[1]);
198 
199  // 8. Send the solution by socket to a GLVis server.
200  is_good = sol_sock.good();
201 
202  if (visualization && is_good )
203  {
204  sol_sock << "solution\n" << *mesh << x << flush;
205  {
206  static int once = 1;
207  if (once)
208  {
209  sol_sock << "keys Ajlm\n";
210  sol_sock << "view 0 0\n";
211  sol_sock << "zoom 1.9\n";
212  sol_sock << "palette 24\n";
213  once = 0;
214  }
215  if (is_stable)
216  {
217  sol_sock << "valuerange 0 1\n";
218  }
219  }
220  }
221  }
222  cout << "done." << endl;
223 
224  // 9. Save the mesh and the final state of the game. This output can be
225  // viewed later using GLVis: "glvis -m life.mesh -g life.gf".
226  ofstream mesh_ofs("life.mesh");
227  mesh_ofs.precision(8);
228  mesh->Print(mesh_ofs);
229  ofstream sol_ofs("life.gf");
230  sol_ofs.precision(8);
231  x.Save(sol_ofs);
232 
233  // 10. Free the used memory.
234  delete fespace;
235  delete fec;
236  delete mesh;
237 
238  return 0;
239 }
240 
241 inline int index(int i, int j, int nx, int ny)
242 {
243  return ((j + ny) % ny) * nx + ((i + nx) % nx);
244 }
245 
246 bool GameStep(vector<bool> * b[], int nx, int ny)
247 {
248  bool is_stable = true;
249  for (int j=0; j<ny; j++)
250  {
251  for (int i=0; i<nx; i++)
252  {
253  int c =
254  (int)(*b[0])[index(i+0,j+0,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  (int)(*b[0])[index(i-1,j+0,nx,ny)] +
260  (int)(*b[0])[index(i-1,j-1,nx,ny)] +
261  (int)(*b[0])[index(i+0,j-1,nx,ny)] +
262  (int)(*b[0])[index(i+1,j-1,nx,ny)];
263  switch (c)
264  {
265  case 3:
266  (*b[1])[index(i,j,nx,ny)] = true;
267  break;
268  case 4:
269  (*b[1])[index(i,j,nx,ny)] = (*b[0])[index(i,j,nx,ny)];
270  break;
271  default:
272  (*b[1])[index(i,j,nx,ny)] = false;
273  break;
274  }
275  is_stable &= (*b[1])[index(i,j,nx,ny)] == (*b[0])[index(i,j,nx,ny)];
276  }
277  }
278  return is_stable;
279 }
280 
281 void ProjectStep(const vector<bool> & b, GridFunction & x, int n)
282 {
283  for (int i=0; i<n; i++)
284  {
285  x[i] = (double)b[i];
286  }
287 }
288 
289 bool InitBlinker(vector<bool> & b, int nx, int ny, const Array<int> & params)
290 {
291  for (int i=0; i<params.Size()/3; i++)
292  {
293  int cx = params[3 * i + 0];
294  int cy = params[3 * i + 1];
295  int ornt = params[3 * i + 2];
296 
297  switch (ornt % 2)
298  {
299  case 0:
300  b[index(cx+0,cy+1,nx,ny)] = true;
301  b[index(cx+0,cy+0,nx,ny)] = true;
302  b[index(cx+0,cy-1,nx,ny)] = true;
303  break;
304  case 1:
305  b[index(cx+1,cy+0,nx,ny)] = true;
306  b[index(cx+0,cy+0,nx,ny)] = true;
307  b[index(cx-1,cy+0,nx,ny)] = true;
308  break;
309  }
310  }
311  return true;
312 }
313 
314 bool InitGlider(vector<bool> & b, int nx, int ny, const Array<int> & params)
315 {
316  for (int i=0; i<params.Size()/3; i++)
317  {
318  int cx = params[3 * i + 0];
319  int cy = params[3 * i + 1];
320  int ornt = params[3 * i + 2];
321 
322  switch (ornt % 4)
323  {
324  case 0:
325  b[index(cx-1,cy+0,nx,ny)] = true;
326  b[index(cx+0,cy+1,nx,ny)] = true;
327  b[index(cx+1,cy-1,nx,ny)] = true;
328  b[index(cx+1,cy+0,nx,ny)] = true;
329  b[index(cx+1,cy+1,nx,ny)] = true;
330  break;
331  case 1:
332  b[index(cx+0,cy-1,nx,ny)] = true;
333  b[index(cx-1,cy+0,nx,ny)] = true;
334  b[index(cx-1,cy+1,nx,ny)] = true;
335  b[index(cx+0,cy+1,nx,ny)] = true;
336  b[index(cx+1,cy+1,nx,ny)] = true;
337  break;
338  case 2:
339  b[index(cx+1,cy+0,nx,ny)] = true;
340  b[index(cx+0,cy-1,nx,ny)] = true;
341  b[index(cx-1,cy-1,nx,ny)] = true;
342  b[index(cx-1,cy+0,nx,ny)] = true;
343  b[index(cx-1,cy+1,nx,ny)] = true;
344  break;
345  case 3:
346  b[index(cx+0,cy+1,nx,ny)] = true;
347  b[index(cx+1,cy+0,nx,ny)] = true;
348  b[index(cx-1,cy-1,nx,ny)] = true;
349  b[index(cx+0,cy-1,nx,ny)] = true;
350  b[index(cx+1,cy-1,nx,ny)] = true;
351  break;
352  }
353  }
354  return true;
355 }
356 
357 bool InitSketchPad(vector<bool> & b, int nx, int ny, const Array<int> & params)
358 {
359  int cx = params[0];
360  int cy = params[1];
361 
362  int ox = 0;
363  int oy = 0;
364 
365  for (int i=2; i<params.Size(); i++)
366  {
367  if ( params[i]/2 == 1 )
368  {
369  ox = 0;
370  oy--;
371  }
372  else
373  {
374  b[index(cx+ox,cy+oy,nx,ny)] = (bool)params[i];
375  ox++;
376  }
377  }
378  return true;
379 }
380 
381 bool InitMFEM(vector<bool> & b, int nx, int ny)
382 {
383  int ox = 0;
384  int oy = 0;
385  int wx = (nx >= 23) ? 23 : 5;
386  int hy = (ny >= 7) ? 7 : 5;
387 
388  if (wx == 23)
389  {
390  // Write out "MFEM"
391  ox = (nx - 23) / 2;
392  oy = (ny - hy) / 2;
393 
394  for (int j=0; j<hy; j++)
395  {
396  b[index(ox + 0, oy+j,nx,ny)] = true;
397  b[index(ox + 4, oy+j,nx,ny)] = true;
398  b[index(ox + 6, oy+j,nx,ny)] = true;
399  b[index(ox + 12, oy+j,nx,ny)] = true;
400  b[index(ox + 18, oy+j,nx,ny)] = true;
401  b[index(ox + 22, oy+j,nx,ny)] = true;
402  }
403  for (int i=1; i<5; i++)
404  {
405  b[index(ox + 6 + i, oy + hy - 1,nx,ny)] = true;
406  b[index(ox + 12 + i, oy + 0,nx,ny)] = true;
407  b[index(ox + 12 + i, oy + hy - 1,nx,ny)] = true;
408  }
409  for (int i=1; i<4; i++)
410  {
411  b[index(ox + 6 + i, oy + hy/2,nx,ny)] = true;
412  b[index(ox + 12 + i, oy + hy/2,nx,ny)] = true;
413  }
414  b[index(ox + 1, oy + hy - 2,nx,ny)] = true;
415  b[index(ox + 2, oy + hy - 3,nx,ny)] = true;
416  b[index(ox + 3, oy + hy - 2,nx,ny)] = true;
417 
418  b[index(ox + 19, oy + hy - 2,nx,ny)] = true;
419  b[index(ox + 20, oy + hy - 3,nx,ny)] = true;
420  b[index(ox + 21, oy + hy - 2,nx,ny)] = true;
421  }
422  else if (wx == 5)
423  {
424  // Create a single 'M'
425  ox = (nx - 5) / 2;
426  oy = (ny - hy) / 2;
427 
428  for (int j=0; j<hy; j++)
429  {
430  b[index(ox + 0, oy+j,nx,ny)] = true;
431  b[index(ox + 4, oy+j,nx,ny)] = true;
432  }
433  b[index(ox + 1, oy + hy - 2,nx,ny)] = true;
434  b[index(ox + 2, oy + hy - 3,nx,ny)] = true;
435  b[index(ox + 3, oy + hy - 2,nx,ny)] = true;
436  }
437  else
438  {
439  // Set a single pixel
440  b[index(nx/2,ny/2,nx,ny)] = true;
441  }
442 
443  return true;
444 }
int Size() const
Return the logical size of the array.
Definition: array.hpp:124
virtual void Print(std::ostream &out=mfem::out) const
Definition: mesh.hpp:1234
Class for grid function - Vector with associated FE space.
Definition: gridfunc.hpp:30
int main(int argc, char *argv[])
Definition: ex1.cpp:66
virtual void Save(std::ostream &out) const
Save the GridFunction to an output stream.
Definition: gridfunc.cpp:3417
bool InitSketchPad(vector< bool > &b, int nx, int ny, const Array< int > &params)
Definition: life.cpp:357
bool GameStep(vector< bool > *b[], int nx, int ny)
Definition: life.cpp:246
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:381
void PrintUsage(std::ostream &out) const
Print the usage message.
Definition: optparser.cpp:434
bool InitBlinker(vector< bool > &b, int nx, int ny, const Array< int > &params)
Definition: life.cpp:289
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:314
Collection of finite elements from the same family in multiple dimensions. This class is used to matc...
Definition: fe_coll.hpp:26
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:196
int index(int i, int j, int nx, int ny)
Definition: life.cpp:241
void PrintOptions(std::ostream &out) const
Print the options.
Definition: optparser.cpp:304
int open(const char hostname[], int port)
Open the socket stream on &#39;port&#39; at &#39;hostname&#39;.
Arbitrary order &quot;L2-conforming&quot; discontinuous finite elements.
Definition: fe_coll.hpp:221
bool Good() const
Return true if the command line options were parsed successfully.
Definition: optparser.hpp:145