MFEM v4.7.0
Finite element discretization library
Loading...
Searching...
No Matches
life.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2024, 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 bool visualization = 1;
63
64 OptionsParser args(argc, argv);
65 args.AddOption(&nx, "-nx", "--num-elems-x",
66 "Number of elements in the x direction.");
67 args.AddOption(&ny, "-ny", "--num-elems-y",
68 "Number of elements in the y direction.");
69 args.AddOption(&r, "-r", "--random-fraction",
70 "Fraction of randomly chosen live cells.");
71 args.AddOption(&rs, "-rs", "--random-seed",
72 "Seed for the random number generator.");
73 args.AddOption(&sketch_pad_params, "-sp", "--sketch-pad",
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.");
78 args.AddOption(&blinker_params, "-b", "--blinker",
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 ...'.");
82 args.AddOption(&glider_params, "-g", "--glider",
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 ...'.");
87 args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
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 real_t rv = real_t(rand()) / real_t(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 {
154 init = InitSketchPad(vb0, nx, ny, sketch_pad_params);
155 }
156 if ( blinker_params.Size() > 0 && (blinker_params.Size() % 3 == 0 ) )
157 {
158 init = InitBlinker(vb0, nx, ny, blinker_params);
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
235inline int index(int i, int j, int nx, int ny)
236{
237 return ((j + ny) % ny) * nx + ((i + nx) % nx);
238}
239
240bool 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
275void ProjectStep(const vector<bool> & b, GridFunction & x, int n)
276{
277 for (int i=0; i<n; i++)
278 {
279 x[i] = (real_t)b[i];
280 }
281}
282
283bool 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
308bool 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
351bool 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
375bool 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:144
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
Definition fespace.hpp:220
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:330
Mesh data type.
Definition mesh.hpp:56
virtual void Print(std::ostream &os=mfem::out, const std::string &comments="") const
Definition mesh.hpp:2288
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:4243
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:375
void ProjectStep(const vector< bool > &b, GridFunction &x, int n)
Definition life.cpp:275
int index(int i, int j, int nx, int ny)
Definition life.cpp:235
bool InitBlinker(vector< bool > &b, int nx, int ny, const Array< int > &params)
Definition life.cpp:283
bool GameStep(vector< bool > *b[], int nx, int ny)
Definition life.cpp:240
bool InitGlider(vector< bool > &b, int nx, int ny, const Array< int > &params)
Definition life.cpp:308
bool InitSketchPad(vector< bool > &b, int nx, int ny, const Array< int > &params)
Definition life.cpp:351
real_t b
Definition lissajous.cpp:42
const int visport
float real_t
Definition config.hpp:43
const char vishost[]