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