1 
2 ///////////////////////////////////////////////////////////
3 //                                                       //
4 //                         SAGA                          //
5 //                                                       //
6 //      System for Automated Geoscientific Analyses      //
7 //                                                       //
8 //                     Tool Library                      //
9 //                sim_cellular_automata                  //
10 //                                                       //
11 //-------------------------------------------------------//
12 //                                                       //
13 //                       Wator.cpp                       //
14 //                                                       //
15 //                 Copyright (C) 2003 by                 //
16 //                      Olaf Conrad                      //
17 //                                                       //
18 //-------------------------------------------------------//
19 //                                                       //
20 // This file is part of 'SAGA - System for Automated     //
21 // Geoscientific Analyses'. SAGA is free software; you   //
22 // can redistribute it and/or modify it under the terms  //
23 // of the GNU General Public License as published by the //
24 // Free Software Foundation, either version 2 of the     //
25 // License, or (at your option) any later version.       //
26 //                                                       //
27 // SAGA is distributed in the hope that it will be       //
28 // useful, but WITHOUT ANY WARRANTY; without even the    //
29 // implied warranty of MERCHANTABILITY or FITNESS FOR A  //
30 // PARTICULAR PURPOSE. See the GNU General Public        //
31 // License for more details.                             //
32 //                                                       //
33 // You should have received a copy of the GNU General    //
34 // Public License along with this program; if not, see   //
35 // <http://www.gnu.org/licenses/>.                       //
36 //                                                       //
37 //-------------------------------------------------------//
38 //                                                       //
39 //    e-mail:     oconrad@saga-gis.org                   //
40 //                                                       //
41 //    contact:    Olaf Conrad                            //
42 //                Institute of Geography                 //
43 //                University of Goettingen               //
44 //                Goldschmidtstr. 5                      //
45 //                37077 Goettingen                       //
46 //                Germany                                //
47 //                                                       //
48 ///////////////////////////////////////////////////////////
49 
50 //---------------------------------------------------------
51 #include "Wator.h"
52 
53 //---------------------------------------------------------
54 #define FISH	1
55 #define SHARK	2
56 
57 
58 ///////////////////////////////////////////////////////////
59 //														 //
60 //														 //
61 //														 //
62 ///////////////////////////////////////////////////////////
63 
64 //---------------------------------------------------------
CWator(void)65 CWator::CWator(void)
66 {
67 	Set_Name		(_TL("Wa-Tor"));
68 
69 	Set_Author		("O.Conrad (c) 2003");
70 
71 	Set_Description	(_TW(
72 		"Wa-Tor - an ecological simulation of predator-prey populations - "
73 		"is based upon A. K. Dewdney's 'Computer Recreations' article "
74 		"in the December 1984 issue of Scientific American."
75 	));
76 
77 	Add_Reference("Dewdney, A.K.", "1984",
78 		"Sharks and fish Wage an ecological War on the toroidal planet Wa-Tor",
79 		"Scientific American. pp. I4�22."
80 	);
81 
82 	//-----------------------------------------------------
83 	m_Grid_Target.Create(&Parameters, false, "", "TARGET_");
84 
85 	m_Grid_Target.Add_Grid("GRID", _TL("Wa-Tor"), false);
86 
87 	//-----------------------------------------------------
88 	Parameters.Add_Bool("",
89 		"REFRESH"		, _TL("Refresh"),
90 		_TL(""),
91 		true
92 	);
93 
94 	Parameters.Add_Double("REFRESH",
95 		"INIT_FISH"		, _TL("Initial Number of Fishes [%]"),
96 		_TL(""),
97 		30., 0., true, 100., true
98 	);
99 
100 	Parameters.Add_Double("REFRESH",
101 		"INIT_SHARK"	, _TL("Initial Number of Sharks [%]"),
102 		_TL(""),
103 		7.5, 0., true, 100., true
104 	);
105 
106 	Parameters.Add_Table("",
107 		"TABLE"			, _TL("Cycles"),
108 		_TL(""),
109 		PARAMETER_OUTPUT
110 	);
111 
112 	Parameters.Add_Int("",
113 		"FISH_BIRTH"	, _TL("Birth Rate of Fishes"),
114 		_TL(""),
115 		3, 0, true
116 	);
117 
118 	Parameters.Add_Int("",
119 		"SHARK_BIRTH"	, _TL("Birth Rate of Sharks"),
120 		_TL(""),
121 		12, 0, true
122 	);
123 
124 	Parameters.Add_Int("",
125 		"SHARK_STARVE"	, _TL("Max. Starvation Time for Sharks"),
126 		_TL(""),
127 		4, 0, true
128 	);
129 }
130 
131 
132 ///////////////////////////////////////////////////////////
133 //														 //
134 ///////////////////////////////////////////////////////////
135 
136 //---------------------------------------------------------
On_Parameter_Changed(CSG_Parameters * pParameters,CSG_Parameter * pParameter)137 int CWator::On_Parameter_Changed(CSG_Parameters *pParameters, CSG_Parameter *pParameter)
138 {
139 	m_Grid_Target.On_Parameter_Changed(pParameters, pParameter);
140 
141 	return( CSG_Tool::On_Parameter_Changed(pParameters, pParameter) );
142 }
143 
144 //---------------------------------------------------------
On_Parameters_Enable(CSG_Parameters * pParameters,CSG_Parameter * pParameter)145 int CWator::On_Parameters_Enable(CSG_Parameters *pParameters, CSG_Parameter *pParameter)
146 {
147 	m_Grid_Target.On_Parameters_Enable(pParameters, pParameter);
148 
149 	return( CSG_Tool::On_Parameters_Enable(pParameters, pParameter) );
150 }
151 
152 
153 ///////////////////////////////////////////////////////////
154 //														 //
155 ///////////////////////////////////////////////////////////
156 
157 //---------------------------------------------------------
On_Execute(void)158 bool CWator::On_Execute(void)
159 {
160 	m_pWator	= m_Grid_Target.Get_Grid("GRID", SG_DATATYPE_Byte);
161 
162 	if( !m_pWator )
163 	{
164 		Error_Set(_TL("could not create target grid"));
165 
166 		return( false );
167 	}
168 
169 	//-----------------------------------------------------
170 	m_pWator->Set_Name(_TL("Wa-Tor"));
171 	m_pWator->Set_NoData_Value(-1);
172 
173 	CSG_Colors	Colors(3);
174 
175 	Colors.Set_Color(0, SG_COLOR_BLACK);
176 	Colors.Set_Color(1, SG_COLOR_GREEN);
177 	Colors.Set_Color(2, SG_COLOR_RED  );
178 
179 	DataObject_Add       (m_pWator);
180 	DataObject_Set_Colors(m_pWator, Colors);
181 	DataObject_Update    (m_pWator, 0, 2, SG_UI_DATAOBJECT_SHOW);
182 
183 	//-----------------------------------------------------
184 	if( Parameters("REFRESH")->asBool() )
185 	{
186 		double	Fish_perc	= Parameters("INIT_FISH" )->asDouble();
187 		double	Shark_perc	= Parameters("INIT_SHARK")->asDouble() + Fish_perc;
188 
189 		#pragma omp parallel for
190 		for(int y=0; y<m_pWator->Get_NY(); y++)
191 		{
192 			for(int x=0; x<m_pWator->Get_NX(); x++)
193 			{
194 				double	perc	= CSG_Random::Get_Uniform(0, 100);
195 
196 				if( perc <= Fish_perc )
197 				{
198 					m_pWator->Set_Value(x, y, FISH);
199 				}
200 				else if( perc <= Shark_perc )
201 				{
202 					m_pWator->Set_Value(x, y, SHARK);
203 				}
204 				else
205 				{
206 					m_pWator->Set_Value(x, y, 0);
207 				}
208 			}
209 		}
210 	}
211 
212 	//-----------------------------------------------------
213 	CSG_Table	*pTable	= Parameters("TABLE")->asTable();
214 
215 	pTable->Destroy();
216 	pTable->Set_Name(_TL("Wa-Tor"));
217 
218 	pTable->Add_Field("Cycle" , SG_DATATYPE_Int);
219 	pTable->Add_Field("Fishes", SG_DATATYPE_Int);
220 	pTable->Add_Field("Sharks", SG_DATATYPE_Int);
221 
222 	//-----------------------------------------------------
223 	m_Fish_Birth	= Parameters("FISH_BIRTH"  )->asInt();
224 	m_Shark_Birth	= Parameters("SHARK_BIRTH" )->asInt();
225 	m_Shark_Starve	= Parameters("SHARK_STARVE")->asInt();
226 
227 	m_Next  .Create(m_pWator, SG_DATATYPE_Byte);
228 	m_Age   .Create(m_pWator, SG_DATATYPE_Byte);
229 	m_Starve.Create(m_pWator, SG_DATATYPE_Byte);
230 
231 	#pragma omp parallel for
232 	for(int y=0; y<m_pWator->Get_NY(); y++)
233 	{
234 		for(int x=0; x<m_pWator->Get_NX(); x++)
235 		{
236 			switch( m_pWator->asByte(x, y) )
237 			{
238 			case FISH:
239 				m_Age   .Set_Value(x, y, CSG_Random::Get_Uniform(0., m_Fish_Birth  ));
240 				break;
241 
242 			case SHARK:
243 				m_Age   .Set_Value(x, y, CSG_Random::Get_Uniform(0., m_Shark_Birth ));
244 				m_Starve.Set_Value(x, y, CSG_Random::Get_Uniform(0., m_Shark_Starve));
245 				break;
246 			}
247 		}
248 	}
249 
250 	//-----------------------------------------------------
251 	int		i;
252 
253 	SG_UI_Progress_Lock(true);
254 
255 	for(i=1; Process_Get_Okay(true) && Next_Cycle(); i++)
256 	{
257 		Process_Set_Text("%s: %d", _TL("Life Cycle"), i);
258 
259 		CSG_Table_Record	*pRecord	= pTable->Add_Record();
260 
261 		pRecord->Set_Value(0, i);
262 		pRecord->Set_Value(1, m_nFishes);
263 		pRecord->Set_Value(2, m_nSharks);
264 
265 		DataObject_Update(m_pWator, 0, 2);
266 		DataObject_Update(pTable);
267 	}
268 
269 	SG_UI_Progress_Lock(false);
270 
271 	//-----------------------------------------------------
272 	m_Next  .Destroy();
273 	m_Age   .Destroy();
274 	m_Starve.Destroy();
275 
276 	if( is_Progress() )
277 	{
278 		Message_Fmt("\n%s %d %s", _TL("Dead after"), i, _TL("Life Cycles"));
279 	}
280 
281 	return( true );
282 }
283 
284 
285 ///////////////////////////////////////////////////////////
286 //														 //
287 ///////////////////////////////////////////////////////////
288 
289 //---------------------------------------------------------
290 #define GET_NEIGHBOR			{\
291 	ix = m_pWator->Get_System().Get_xTo(i, x); if( ix < 0 ) ix = m_pWator->Get_NX() - 1; else if( ix >= m_pWator->Get_NX() ) ix = 0;\
292 	iy = m_pWator->Get_System().Get_yTo(i, y); if( iy < 0 ) iy = m_pWator->Get_NY() - 1; else if( iy >= m_pWator->Get_NY() ) iy = 0;\
293 }
294 
295 #define GET_NEIGHBOR_RANDOMLY	{\
296 	i  = iNeighbor[(int)((double)rand() * nNeighbors / (double)RAND_MAX)];\
297 	ix = m_pWator->Get_System().Get_xTo(i, x); if( ix < 0 ) ix = m_pWator->Get_NX() - 1; else if( ix >= m_pWator->Get_NX() ) ix = 0;\
298 	iy = m_pWator->Get_System().Get_yTo(i, y); if( iy < 0 ) iy = m_pWator->Get_NY() - 1; else if( iy >= m_pWator->Get_NY() ) iy = 0;\
299 }
300 
301 //---------------------------------------------------------
Next_Cycle(void)302 bool CWator::Next_Cycle(void)
303 {
304 	static int	iDir	= 0;
305 
306 	int		x, y, i, ix, iy, xx, yy, ax, ay, dx, dy,
307 			iNeighbor[8], nNeighbors,
308 			Age, Starve;
309 
310 	//-----------------------------------------------------
311 	m_nFishes	= 0;
312 	m_nSharks	= 0;
313 
314 	m_Next.Assign(0.);
315 
316 	switch( iDir )
317 	{
318 	default: ay	= m_pWator->Get_NY() - 1; dy = -1; ax = m_pWator->Get_NX() - 1; dx = -1; iDir=0; break;
319 	case  2: ay	= 0;                      dy =  1; ax = m_pWator->Get_NX() - 1; dx = -1; iDir++; break;
320 	case  1: ay	= m_pWator->Get_NY() - 1; dy = -1; ax = 0;                      dx =  1; iDir++; break;
321 	case  0: ay	= 0;                      dy =  1; ax = 0;                      dx =  1; iDir++; break;
322 	}
323 
324 	//-----------------------------------------------------
325 	for(yy=0, y=ay; yy<m_pWator->Get_NY(); yy++, y+=dy)
326 	{
327 		for(xx=0, x=ax; xx<m_pWator->Get_NX(); xx++, x+=dx)
328 		{
329 			if( m_pWator->asByte(x, y) == FISH )
330 			{
331 				m_nFishes++;
332 
333 				Age		= m_Age.asInt(x, y) + 1;
334 
335 				m_Age.Set_Value(x, y, 0);
336 
337 				for(i=0, nNeighbors=0; i<8; i++)
338 				{
339 					GET_NEIGHBOR;
340 
341 					if( m_pWator->asByte(ix, iy) == 0 && m_Next.asByte(ix, iy) == 0 )
342 					{
343 						iNeighbor[nNeighbors++]	= i;
344 					}
345 				}
346 
347 				if( nNeighbors > 0 )
348 				{
349 					GET_NEIGHBOR_RANDOMLY;
350 
351 					m_Next.Set_Value(ix, iy, FISH);
352 					m_Age .Set_Value(ix, iy, Age >= m_Fish_Birth ? 0 : Age);
353 
354 					if( Age >= m_Fish_Birth )
355 					{
356 						m_Next.Set_Value(x, y, FISH);
357 						m_Age .Set_Value(x, y, CSG_Random::Get_Uniform(0, m_Fish_Birth));
358 					}
359 					else
360 					{
361 						m_pWator->Set_Value(x, y, 0);
362 					}
363 				}
364 				else
365 				{
366 					m_Next.Set_Value(x, y, FISH);
367 					m_Age .Set_Value(x, y, Age >= m_Fish_Birth ? 0 : m_Fish_Birth);
368 				}
369 			}
370 		}
371 	}
372 
373 	//-----------------------------------------------------
374 	for(yy=0, y=ay; yy<m_pWator->Get_NY(); yy++, y+=dy)
375 	{
376 		for(xx=0, x=ax; xx<m_pWator->Get_NX(); xx++, x+=dx)
377 		{
378 			if( m_pWator->asByte(x, y) == SHARK )
379 			{
380 				m_nSharks++;
381 
382 				Age		= m_Age.asInt(x, y) + 1;
383 
384 				m_Age.Set_Value(x, y, 0);
385 
386 				Starve	= m_Starve.asInt(x, y) + 1;
387 				m_Starve.Set_Value(x, y, 0);
388 
389 				for(i=0, nNeighbors=0; i<8; i++)
390 				{
391 					GET_NEIGHBOR;
392 
393 					if( m_Next.asByte(ix, iy) == FISH )
394 					{
395 						iNeighbor[nNeighbors++]	= i;
396 					}
397 				}
398 
399 				if( nNeighbors > 0 )
400 				{
401 					GET_NEIGHBOR_RANDOMLY;
402 
403 					m_nFishes--;
404 					m_pWator->Set_Value(ix, iy, 0);
405 					m_Next   .Set_Value(ix, iy, SHARK);
406 					m_Age    .Set_Value(ix, iy, Age >= m_Shark_Birth ? 0 : Age);
407 					m_Starve .Set_Value(ix, iy, 0);
408 
409 					if( Age >= m_Shark_Birth )
410 					{
411 						m_Next   .Set_Value(x, y, SHARK);
412 						m_Age    .Set_Value(x, y, CSG_Random::Get_Uniform(0, m_Shark_Birth));
413 						m_Starve .Set_Value(x, y, 0);
414 					}
415 					else
416 					{
417 						m_pWator->Set_Value(x, y, 0);
418 					}
419 				}
420 				else if( Starve <= m_Shark_Starve )
421 				{
422 					for(i=0, nNeighbors=0; i<8; i++)
423 					{
424 						GET_NEIGHBOR;
425 
426 						if( m_pWator->asByte(ix, iy) == 0 && m_Next.asByte(ix, iy) == 0 )
427 						{
428 							iNeighbor[nNeighbors++]	= i;
429 						}
430 					}
431 
432 					if( nNeighbors > 0 )
433 					{
434 						GET_NEIGHBOR_RANDOMLY;
435 
436 						m_Next  .Set_Value(ix, iy, SHARK);
437 						m_Age   .Set_Value(ix, iy, Age >= m_Shark_Birth ? 0 : Age);
438 						m_Starve.Set_Value(ix, iy, Starve);
439 
440 						if( Age >= m_Shark_Birth )
441 						{
442 							m_Next   .Set_Value(x, y, SHARK);
443 							m_Age    .Set_Value(x, y, CSG_Random::Get_Uniform(0, m_Shark_Birth));
444 							m_Starve .Set_Value(x, y, Starve);
445 						}
446 						else
447 						{
448 							m_pWator	->Set_Value( x,  y, 0);
449 						}
450 					}
451 					else
452 					{
453 						m_Next  .Set_Value(x, y, SHARK);
454 						m_Age   .Set_Value(x, y, Age >= m_Shark_Birth ? 0 : m_Shark_Birth);
455 						m_Starve.Set_Value(x, y, Starve);
456 					}
457 				}
458 				else
459 				{
460 					m_nSharks--;
461 				}
462 			}
463 		}
464 	}
465 
466 	//-----------------------------------------------------
467 	#pragma omp parallel for private(x, y)
468 	for(y=0; y<m_pWator->Get_NY(); y++)
469 	{
470 		for(x=0; x<m_pWator->Get_NX(); x++)
471 		{
472 			m_pWator->Set_Value(x, y, m_Next.asByte(x, y));
473 		}
474 	}
475 
476 	return( (m_nFishes > 0 && m_nFishes < m_pWator->Get_NCells()) || m_nSharks > 0 );
477 }
478 
479 
480 ///////////////////////////////////////////////////////////
481 //														 //
482 //														 //
483 //														 //
484 ///////////////////////////////////////////////////////////
485 
486 //---------------------------------------------------------
487