MFEM v4.8.0
Finite element discretization library
Loading...
Searching...
No Matches
life.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2025, 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 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
41using namespace std;
42using namespace mfem;
43
44bool GameStep(vector<bool> * b[], int nx, int ny);
45void ProjectStep(const vector<bool> & b, GridFunction & x, int n);
46
47bool InitSketchPad(vector<bool> & b, int nx, int ny, const Array<int> & params);
48bool InitBlinker(vector<bool> & b, int nx, int ny, const Array<int> & params);
49bool InitGlider(vector<bool> & b, int nx, int ny, const Array<int> & params);
50bool InitMFEM(vector<bool> & b, int nx, int ny);
51
52int 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 real_t r = -1.0;
59 Array<int> sketch_pad_params(0);
60 Array<int> blinker_params(0);
61 Array<int> glider_params(0);
62 int visport = 19916;
63 bool visualization = 1;
64
65 OptionsParser args(argc, argv);
66 args.AddOption(&nx, "-nx", "--num-elems-x",
67 "Number of elements in the x direction.");
68 args.AddOption(&ny, "-ny", "--num-elems-y",
69 "Number of elements in the y direction.");
70 args.AddOption(&r, "-r", "--random-fraction",
71 "Fraction of randomly chosen live cells.");
72 args.AddOption(&rs, "-rs", "--random-seed",
73 "Seed for the random number generator.");
74 args.AddOption(&sketch_pad_params, "-sp", "--sketch-pad",
75 "Specify the starting coordinates and values on a grid"
76 " of cells. The values can be 0, 1, or 2. Where 0 and 1"
77 " indicate cells that are off or on and 2 represents a"
78 " newline character.");
79 args.AddOption(&blinker_params, "-b", "--blinker",
80 "Specify the starting coordinates and orientation (0 or 1)"
81 " of the blinker. Multiple blinkers can be specified as "
82 "'x0 y0 o0 x1 y1 o1 ...'.");
83 args.AddOption(&glider_params, "-g", "--glider",
84 "Specify the starting coordinates and "
85 "orientation (0,1,2, or 3) of the glider. "
86 "Multiple gliders can be specified as "
87 "'x0 y0 o0 x1 y1 o1 ...'.");
88 args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
89 "--no-visualization",
90 "Enable or disable GLVis visualization.");
91 args.AddOption(&visport, "-p", "--send-port", "Socket for GLVis.");
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 real_t rv = real_t(rand()) / real_t(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 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 return 0;
234}
235
236inline int index(int i, int j, int nx, int ny)
237{
238 return ((j + ny) % ny) * nx + ((i + nx) % nx);
239}
240
241bool GameStep(vector<bool> * b[], int nx, int ny)
242{
243 bool is_stable = true;
244 for (int j=0; j<ny; j++)
245 {
246 for (int i=0; i<nx; i++)
247 {
248 int c =
249 (int)(*b[0])[index(i+0,j+0,nx,ny)] +
250 (int)(*b[0])[index(i+1,j+0,nx,ny)] +
251 (int)(*b[0])[index(i+1,j+1,nx,ny)] +
252 (int)(*b[0])[index(i+0,j+1,nx,ny)] +
253 (int)(*b[0])[index(i-1,j+1,nx,ny)] +
254 (int)(*b[0])[index(i-1,j+0,nx,ny)] +
255 (int)(*b[0])[index(i-1,j-1,nx,ny)] +
256 (int)(*b[0])[index(i+0,j-1,nx,ny)] +
257 (int)(*b[0])[index(i+1,j-1,nx,ny)];
258 switch (c)
259 {
260 case 3:
261 (*b[1])[index(i,j,nx,ny)] = true;
262 break;
263 case 4:
264 (*b[1])[index(i,j,nx,ny)] = (*b[0])[index(i,j,nx,ny)];
265 break;
266 default:
267 (*b[1])[index(i,j,nx,ny)] = false;
268 break;
269 }
270 is_stable &= (*b[1])[index(i,j,nx,ny)] == (*b[0])[index(i,j,nx,ny)];
271 }
272 }
273 return is_stable;
274}
275
276void ProjectStep(const vector<bool> & b, GridFunction & x, int n)
277{
278 for (int i=0; i<n; i++)
279 {
280 x[i] = (real_t)b[i];
281 }
282}
283
284bool InitBlinker(vector<bool> & b, int nx, int ny, const Array<int> & params)
285{
286 for (int i=0; i<params.Size()/3; i++)
287 {
288 int cx = params[3 * i + 0];
289 int cy = params[3 * i + 1];
290 int ornt = params[3 * i + 2];
291
292 switch (ornt % 2)
293 {
294 case 0:
295 b[index(cx+0,cy+1,nx,ny)] = true;
296 b[index(cx+0,cy+0,nx,ny)] = true;
297 b[index(cx+0,cy-1,nx,ny)] = true;
298 break;
299 case 1:
300 b[index(cx+1,cy+0,nx,ny)] = true;
301 b[index(cx+0,cy+0,nx,ny)] = true;
302 b[index(cx-1,cy+0,nx,ny)] = true;
303 break;
304 }
305 }
306 return true;
307}
308
309bool InitGlider(vector<bool> & b, int nx, int ny, const Array<int> & params)
310{
311 for (int i=0; i<params.Size()/3; i++)
312 {
313 int cx = params[3 * i + 0];
314 int cy = params[3 * i + 1];
315 int ornt = params[3 * i + 2];
316
317 switch (ornt % 4)
318 {
319 case 0:
320 b[index(cx-1,cy+0,nx,ny)] = true;
321 b[index(cx+0,cy+1,nx,ny)] = true;
322 b[index(cx+1,cy-1,nx,ny)] = true;
323 b[index(cx+1,cy+0,nx,ny)] = true;
324 b[index(cx+1,cy+1,nx,ny)] = true;
325 break;
326 case 1:
327 b[index(cx+0,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 b[index(cx+0,cy+1,nx,ny)] = true;
331 b[index(cx+1,cy+1,nx,ny)] = true;
332 break;
333 case 2:
334 b[index(cx+1,cy+0,nx,ny)] = true;
335 b[index(cx+0,cy-1,nx,ny)] = true;
336 b[index(cx-1,cy-1,nx,ny)] = true;
337 b[index(cx-1,cy+0,nx,ny)] = true;
338 b[index(cx-1,cy+1,nx,ny)] = true;
339 break;
340 case 3:
341 b[index(cx+0,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 b[index(cx+0,cy-1,nx,ny)] = true;
345 b[index(cx+1,cy-1,nx,ny)] = true;
346 break;
347 }
348 }
349 return true;
350}
351
352bool InitSketchPad(vector<bool> & b, int nx, int ny, const Array<int> & params)
353{
354 int cx = params[0];
355 int cy = params[1];
356
357 int ox = 0;
358 int oy = 0;
359
360 for (int i=2; i<params.Size(); i++)
361 {
362 if ( params[i]/2 == 1 )
363 {
364 ox = 0;
365 oy--;
366 }
367 else
368 {
369 b[index(cx+ox,cy+oy,nx,ny)] = (bool)params[i];
370 ox++;
371 }
372 }
373 return true;
374}
375
376bool InitMFEM(vector<bool> & b, int nx, int ny)
377{
378 int ox = 0;
379 int oy = 0;
380 int wx = (nx >= 23) ? 23 : 5;
381 int hy = (ny >= 7) ? 7 : 5;
382
383 if (wx == 23)
384 {
385 // Write out "MFEM"
386 ox = (nx - 23) / 2;
387 oy = (ny - hy) / 2;
388
389 for (int j=0; j<hy; j++)
390 {
391 b[index(ox + 0, oy+j,nx,ny)] = true;
392 b[index(ox + 4, oy+j,nx,ny)] = true;
393 b[index(ox + 6, oy+j,nx,ny)] = true;
394 b[index(ox + 12, oy+j,nx,ny)] = true;
395 b[index(ox + 18, oy+j,nx,ny)] = true;
396 b[index(ox + 22, oy+j,nx,ny)] = true;
397 }
398 for (int i=1; i<5; i++)
399 {
400 b[index(ox + 6 + i, oy + hy - 1,nx,ny)] = true;
401 b[index(ox + 12 + i, oy + 0,nx,ny)] = true;
402 b[index(ox + 12 + i, oy + hy - 1,nx,ny)] = true;
403 }
404 for (int i=1; i<4; i++)
405 {
406 b[index(ox + 6 + i, oy + hy/2,nx,ny)] = true;
407 b[index(ox + 12 + i, oy + hy/2,nx,ny)] = true;
408 }
409 b[index(ox + 1, oy + hy - 2,nx,ny)] = true;
410 b[index(ox + 2, oy + hy - 3,nx,ny)] = true;
411 b[index(ox + 3, oy + hy - 2,nx,ny)] = true;
412
413 b[index(ox + 19, oy + hy - 2,nx,ny)] = true;
414 b[index(ox + 20, oy + hy - 3,nx,ny)] = true;
415 b[index(ox + 21, oy + hy - 2,nx,ny)] = true;
416 }
417 else if (wx == 5)
418 {
419 // Create a single 'M'
420 ox = (nx - 5) / 2;
421 oy = (ny - hy) / 2;
422
423 for (int j=0; j<hy; j++)
424 {
425 b[index(ox + 0, oy+j,nx,ny)] = true;
426 b[index(ox + 4, oy+j,nx,ny)] = true;
427 }
428 b[index(ox + 1, oy + hy - 2,nx,ny)] = true;
429 b[index(ox + 2, oy + hy - 3,nx,ny)] = true;
430 b[index(ox + 3, oy + hy - 2,nx,ny)] = true;
431 }
432 else
433 {
434 // Set a single pixel
435 b[index(nx/2,ny/2,nx,ny)] = true;
436 }
437
438 return true;
439}
int Size() const
Return the logical size of the array.
Definition array.hpp:147
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
Definition fespace.hpp:244
Class for grid function - Vector with associated FE space.
Definition gridfunc.hpp:31
virtual void Save(std::ostream &out) const
Save the GridFunction to an output stream.
Arbitrary order "L2-conforming" discontinuous finite elements.
Definition fe_coll.hpp:346
Mesh data type.
Definition mesh.hpp:64
virtual void Print(std::ostream &os=mfem::out, const std::string &comments="") const
Definition mesh.hpp:2433
static Mesh MakeCartesian2D(int nx, int ny, Element::Type type, bool generate_edges=false, real_t sx=1.0, real_t sy=1.0, bool sfc_ordering=true)
Creates mesh for the rectangle [0,sx]x[0,sy], divided into nx*ny quadrilaterals if type = QUADRILATER...
Definition mesh.cpp:4471
void Parse()
Parse the command-line options. Note that this function expects all the options provided through the ...
void PrintUsage(std::ostream &out) const
Print the usage message.
void PrintOptions(std::ostream &out) const
Print the options.
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 'var' to receive the value. Enable/disable tags are used to set the bool...
Definition optparser.hpp:82
bool Good() const
Return true if the command line options were parsed successfully.
int open(const char hostname[], int port)
Open the socket stream on 'port' at 'hostname'.
int main()
bool InitMFEM(vector< bool > &b, int nx, int ny)
Definition life.cpp:376
void ProjectStep(const vector< bool > &b, GridFunction &x, int n)
Definition life.cpp:276
int index(int i, int j, int nx, int ny)
Definition life.cpp:236
bool InitBlinker(vector< bool > &b, int nx, int ny, const Array< int > &params)
Definition life.cpp:284
bool GameStep(vector< bool > *b[], int nx, int ny)
Definition life.cpp:241
bool InitGlider(vector< bool > &b, int nx, int ny, const Array< int > &params)
Definition life.cpp:309
bool InitSketchPad(vector< bool > &b, int nx, int ny, const Array< int > &params)
Definition life.cpp:352
real_t b
Definition lissajous.cpp:42
float real_t
Definition config.hpp:43
const char vishost[]