Game Of Life

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Message
Author
T3RRY
Posts: 243
Joined: 06 May 2020 10:14

Game Of Life

#1 Post by T3RRY » 02 Jun 2020 13:37

Just my take on an age old favorite.
Utilises Ascii escape codes to display Living Cells by coordinate in a Semi Random Color
Option to save randomly Generated patterns in self loading batch files
Option to download or use BG.exe version 3.9 to enhance animation using Hide cursor option and Font size Selection
- Uses 'Where' to search your User Profile for any copy of BG.exe to take advantage of these functions, If you have an older version of BG.exe in your user profile, it may not work as intended.
Can be called with the command line with Custom Parameters for width, Height and Density

Code: Select all

@ECHO OFF & Setlocal EnableDelayedExpansion
	TITLE Game of Life
	Set "SaveDir=%TEMP%\CGOL_Data"
	IF not exist "%SaveDir%" MD "%SaveDir%"
::: { Optional utility to improve animation: BG.exe Ver 3.9 SE by Carlos Montiers Aguilera
::: - Download Link - https://github.com/carlos-montiers/consolesoft-mirror/releases
	For /F "Delims=" %%0 in ('Where /R "%USERPROFILE%" BG.exe') Do Set "BG.Utility=%%~0"
	IF Exist "%BG.Utility%" ("%BG.Utility%" Cursor 0)
::: }
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: Build concept:
::: Nested array Pos[y,x] containing _O / _I (Alive / Dead) values
::: Calculate offset values for adjacent Cells. EG: Pos[1,3] (using For /L %%Y %%X values as starting point, and N E S W NE NW SE SW as offset Variables.)
::: If statements to test value of neighbour cells N,E,S,W,NE,NW,SE,SW and counter to indicate how many cells are _O / _I and update status accordingly
::: Use intermediary array variables - vPos[x,y] for the above to prevent values changing before all cells in current generation iterated over
::: Transfer value to real cell, then destroy value of virtual cell.
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::: { Creates variable /AE = Ascii-27 escape code.
::: - http://www.dostips.com/forum/viewtopic.php?t=1733
::: - https://stackoverflow.com/a/34923514/12343998
:::  /AE can be used  with and without DelayedExpansion.
    Setlocal
    For /F "tokens=2 delims=#" %%a in ('"prompt #$H#$E# & echo on & for %%b in (1) do rem"') do (
        Endlocal
        Set "/AE=%%a"
    )
::: }
::: { Custom Font. Recommended Size: 9
	IF Exist "%BG.Utility%" (
		If Not Defined F.Size (
			Echo(%/AE%[32mSelect Font Size [0-9]:%/AE%[0m
			For /F "Delims=" %%F in ('%__AppDir__%\Choice.exe /N /C:0123456789') Do Set "F.Size=%%F" & %BG.Utility% Font %%F) Else (%BG.Utility% Font !F.Size!)
		)
	)
::: }
If /I "%~1"=="load" Goto :Load_In
REM Time suffix for saving initial state
	Set DateString=%Date:~4,10%
	Set DateString=%DateString:/=%
	Set Filetime=%Time::=_%
	Set Filetime=%Filetime: =0%
	Set Filetime=%DateString%_at%Filetime:~,-3%
REM Folder to save initial state in.
	Set "SaveDir=%~dp0CGOL_Data"
	IF not exist "%SaveDir%" (
		MD "%SaveDir%"
		PUSHD "%SaveDir%"
		Goto :Dimensions
	)
	PUSHD "%SaveDir%"
	IF Not "%~2"=="" (Goto :Dimensions)

		IF Exist "%SaveDir%\*.bat" (
			ECHO(%/AE%[33m && CLS
			CHOICE /N /C yn /M "Load previous? N/Y"
			If Errorlevel 2 (
				CLS
				Goto :Dimensions
			)
		) Else (Goto :Dimensions)
	Explorer.exe "%SaveDir%"
:selectFile
	Set /P "source=%/AE%[35m{%/AE%[36mSelect file: }%/AE%[33m<%/AE%[37m"
	IF exist "!source!" (
		FOR /F "delims=" %%L in ("!source!") DO (Start "" "%%~L" & Exit)
	) Else (Goto :selectFile)
:Dimensions
REM define grid dimensions. Batch can be called or started with parameters for Width and Hieght, and Density.
	IF Not "%~2"=="" (
		Set /A width=%~1,hieght=%~2
		Goto :Populate
	) Else (
		Set /P width=%/AE%[35m{%/AE%[36mWidth %/AE%[34m[%/AE%[33m30 %/AE%[34m~ %/AE%[33m125%/AE%[34m]%/AE%[37m
		Set /P hieght=%/AE%[35m{%/AE%[36mHieght %/AE%[34m[%/AE%[33m15 %/AE%[34m~ %/AE%[33m40%/AE%[34m]%/AE%[37m
	)
	For /L %%A IN (30,1,125) Do (IF "!width!"=="%%A" (For /L %%B IN (15,1,40) Do (IF "!hieght!"=="%%B" (Goto :Populate))))
	ECHO(%/AE%[31mInvalid Dimensions.
Goto :Dimensions
:Populate
REM populate grid array with _I cells
	For /L %%Y In (1,1,!hieght!) Do (
		For /L %%X In (1,1,!width!) Do (	
			Set "Pos[%%Y_%%X]=_I"
		)
	)
REM randomly populate grid array with living cells. Density can be selected using a 3rd parameter when calling the batch.
	IF Not "%~3"=="" (
		For /L %%Y In (1,1,!hieght!) Do (
			For /L %%X In (1,1,!width!) Do (
				Set /A "density=!random! %%%~3 + 1"
				If "!density!"=="2" (Set "Pos[%%Y_%%X]=_O")
			)
		)
	) Else (
		For /L %%Y In (1,1,!hieght!) Do (
			For /L %%X In (1,1,!width!) Do (
				Set /A "density=!random! %%9 + 1"
				If "!density!"=="2" (Set "Pos[%%Y_%%X]=_O")
			)
		)
	)
REM Option to store the initial state.
	CHOICE /N /C ny /M "Save Initial State? N/Y"
	If Errorlevel 2 (
		ECHO(%/AE%[36mSaving...%/AE%[0m
		>"CGOLfile%Filetime%.bat" (
			ECHO(@Echo OFF
			ECHO(Set "width=!width!"
			ECHO(Set "hieght=!hieght!"
			ECHO(Set "F.Size=!F.Size!"
			For /L %%Y in (1,1,!hieght!) DO For /L %%X in (1,1,!width!) Do (ECHO.Set "Pos[%%Y_%%X]=!Pos[%%Y_%%X]!")
			Echo(CD ..
			Echo(%~n0 Load
		)
	)
:Load_In
REM define display characteristics of _O (living) or _I (dead) cells
	Set /A Color=!random! %%7 + 31
	Set "_I=%/AE%[31m%/AE%[7m+%/AE%[0m"
	REM Set "_O=%/AE%[!Color!m%/AE%[7m %/AE%[0m"
	Set tick=0
REM Adjust display size, extra line for cursor overflow.
	Set /A hieght+=1
	Mode Con: cols=%width% lines=%hieght%
	Set /A hieght-=1
	(For /L %%Y In (1,1,!hieght!) Do (
		For /L %%X In (1,1,!width!) Do (
			For %%A in (!Pos[%%Y_%%X]!) DO (Echo(%/AE%[%%Y;%%XH!_I!)				
		)
	))>"%SaveDir%\BGScreen.txt"
	Set /A Color=!random! %%4 + 34
	(For /F "USEBACKQ Tokens=1,2 Delims==" %%A in (`"Set Pos["`) Do (
		Set ".Y=%%~A"
		Set ".Y=!.Y:Pos[=!"
		Set ".X=!.Y:*_=!"
		Set ".X=!.X:]=!"
		For %%V in (_!.X!]) Do Set ".Y=!.Y:%%V=!"
		If /I "%%~B" == "_O" (Set /A Color=!random! %%4 + 34 & Echo.%/AE%[!.Y!;!.X!H%/AE%[!Color!m%/AE%[7m@%/AE%[0m)
	))>"%SaveDir%\Screen.txt"
	TYPE "%SaveDir%\BGScreen.txt"
	TYPE "%SaveDir%\Screen.txt"
REM loop to cycle through generations (otherwise known as ticks) 
For /L %%. in () Do (
	Set /A tick+=1
	TITLE !TIME:~3,8! !tick!
REM determine _O / _I status for each cells next generation and assign to virtual cells ready for transfer.
	For /L %%Y In (1,1,!hieght!) Do (
		For /L %%X In (1,1,!width!) Do (
	REM calculate positions of nieghbour cells and reset living neighbour count
			Set /A N=%%Y,E=%%X,S=%%Y,W=%%X,B_North=0,B_South=!hieght!,B_East=!width!,B_West=0
			Set /A N-=1,E+=1,S+=1,W-=1,B_East+=1,B_South+=1,Count=0
	REM horizontal, vertical and diagonal Screen Wrapping. REM prior to below If conditions Disables wrapping.
		REM	IF "!N!"=="!B_North!" Set N=!hieght!
		REM	IF "!E!"=="!B_East!" Set E=1
		REM	IF "!S!"=="!B_South!" Set S=1
		REM	IF "!W!"=="!B_West!" Set W=!width!
			Set "NE=!N!_!E!"
			Set "NW=!N!_!W!"
			Set "SE=!S!_!E!"
			Set "SW=!S!_!W!"
			Set "N=!N!_%%X"
			Set "E=%%Y_!E!"
			Set "S=!S!_%%X"
			Set "W=%%Y_!W!"
		REM iterate over each neighbour cell and increment count for each live neighbour
			For %%A in (Pos[!N!],Pos[!E!],Pos[!S!],Pos[!W!],Pos[!NE!],Pos[!NW!],Pos[!SE!],Pos[!SW!]) Do IF /I "!%%~A!"=="_O" (Set /A count+=1)
REM Game rule implementation
		REM Any cell not meeting the following two critea dies.
			Set "vPos[%%Y_%%X]=_I"
		REM A living Cell with exactly two living neighbours survives
			IF "!count!"=="2" (IF /I "!Pos[%%Y_%%X]!"=="_O" (Set vPos[%%Y_%%X]=_O))
		REM Any cell with exactly three living neighbours is concieved / survives.
			IF "!count!"=="3" (Set vPos[%%Y_%%X]=_O)
		)
	)
REM Build Foreground screen to display current living generation, Transfer array values and 'Undefine' Calculation array variables
	(For /F "USEBACKQ Tokens=1,2 Delims==" %%A in (`"Set vPos["`) Do (
		Set ".Y=%%~A" & Set %%~A=
		Set ".Y=!.Y:vPos[=!"
		Set ".X=!.Y:*_=!"
		Set ".X=!.X:]=!"
		For %%V in (_!.X!]) Do Set ".Y=!.Y:%%V=!"
		Set "Pos[!.Y!_!.X!]=%%~B"
		If /I "%%~B" == "_O" (Set /A Color=!random! %%4 + 34 & Echo.%/AE%[!.Y!;!.X!H%/AE%[!Color!m%/AE%[7m@%/AE%[0m)
	))>"%SaveDir%\Screen.txt"
	TYPE "%SaveDir%\BGScreen.txt"
	TYPE "%SaveDir%\Screen.txt"
)

An example of a pattern File:

Code: Select all

@Echo OFF
Set "width=30"
Set "hieght=20"
Set "F.Size=9"
Set "Pos[1_1]=_I"
Set "Pos[1_2]=_I"
Set "Pos[1_3]=_I"
Set "Pos[1_4]=_I"
Set "Pos[1_5]=_I"
Set "Pos[1_6]=_I"
Set "Pos[1_7]=_I"
Set "Pos[1_8]=_I"
Set "Pos[1_9]=_I"
Set "Pos[1_10]=_I"
Set "Pos[1_11]=_I"
Set "Pos[1_12]=_I"
Set "Pos[1_13]=_I"
Set "Pos[1_14]=_I"
Set "Pos[1_15]=_I"
Set "Pos[1_16]=_I"
Set "Pos[1_17]=_I"
Set "Pos[1_18]=_I"
Set "Pos[1_19]=_I"
Set "Pos[1_20]=_I"
Set "Pos[1_21]=_I"
Set "Pos[1_22]=_I"
Set "Pos[1_23]=_I"
Set "Pos[1_24]=_I"
Set "Pos[1_25]=_I"
Set "Pos[1_26]=_I"
Set "Pos[1_27]=_I"
Set "Pos[1_28]=_I"
Set "Pos[1_29]=_I"
Set "Pos[1_30]=_I"
Set "Pos[2_1]=_I"
Set "Pos[2_2]=_I"
Set "Pos[2_3]=_I"
Set "Pos[2_4]=_I"
Set "Pos[2_5]=_I"
Set "Pos[2_6]=_I"
Set "Pos[2_7]=_I"
Set "Pos[2_8]=_I"
Set "Pos[2_9]=_I"
Set "Pos[2_10]=_I"
Set "Pos[2_11]=_I"
Set "Pos[2_12]=_I"
Set "Pos[2_13]=_I"
Set "Pos[2_14]=_I"
Set "Pos[2_15]=_I"
Set "Pos[2_16]=_I"
Set "Pos[2_17]=_I"
Set "Pos[2_18]=_I"
Set "Pos[2_19]=_I"
Set "Pos[2_20]=_I"
Set "Pos[2_21]=_I"
Set "Pos[2_22]=_I"
Set "Pos[2_23]=_I"
Set "Pos[2_24]=_I"
Set "Pos[2_25]=_I"
Set "Pos[2_26]=_I"
Set "Pos[2_27]=_I"
Set "Pos[2_28]=_I"
Set "Pos[2_29]=_I"
Set "Pos[2_30]=_I"
Set "Pos[3_1]=_I"
Set "Pos[3_2]=_I"
Set "Pos[3_3]=_I"
Set "Pos[3_4]=_I"
Set "Pos[3_5]=_I"
Set "Pos[3_6]=_I"
Set "Pos[3_7]=_I"
Set "Pos[3_8]=_I"
Set "Pos[3_9]=_I"
Set "Pos[3_10]=_I"
Set "Pos[3_11]=_I"
Set "Pos[3_12]=_I"
Set "Pos[3_13]=_I"
Set "Pos[3_14]=_I"
Set "Pos[3_15]=_I"
Set "Pos[3_16]=_I"
Set "Pos[3_17]=_I"
Set "Pos[3_18]=_I"
Set "Pos[3_19]=_I"
Set "Pos[3_20]=_I"
Set "Pos[3_21]=_I"
Set "Pos[3_22]=_I"
Set "Pos[3_23]=_I"
Set "Pos[3_24]=_I"
Set "Pos[3_25]=_I"
Set "Pos[3_26]=_I"
Set "Pos[3_27]=_I"
Set "Pos[3_28]=_I"
Set "Pos[3_29]=_I"
Set "Pos[3_30]=_I"
Set "Pos[4_1]=_I"
Set "Pos[4_2]=_I"
Set "Pos[4_3]=_I"
Set "Pos[4_4]=_I"
Set "Pos[4_5]=_I"
Set "Pos[4_6]=_I"
Set "Pos[4_7]=_I"
Set "Pos[4_8]=_I"
Set "Pos[4_9]=_I"
Set "Pos[4_10]=_I"
Set "Pos[4_11]=_I"
Set "Pos[4_12]=_I"
Set "Pos[4_13]=_I"
Set "Pos[4_14]=_I"
Set "Pos[4_15]=_I"
Set "Pos[4_16]=_I"
Set "Pos[4_17]=_I"
Set "Pos[4_18]=_I"
Set "Pos[4_19]=_I"
Set "Pos[4_20]=_I"
Set "Pos[4_21]=_I"
Set "Pos[4_22]=_I"
Set "Pos[4_23]=_I"
Set "Pos[4_24]=_I"
Set "Pos[4_25]=_I"
Set "Pos[4_26]=_I"
Set "Pos[4_27]=_I"
Set "Pos[4_28]=_I"
Set "Pos[4_29]=_I"
Set "Pos[4_30]=_I"
Set "Pos[5_1]=_I"
Set "Pos[5_2]=_I"
Set "Pos[5_3]=_I"
Set "Pos[5_4]=_I"
Set "Pos[5_5]=_I"
Set "Pos[5_6]=_I"
Set "Pos[5_7]=_I"
Set "Pos[5_8]=_I"
Set "Pos[5_9]=_I"
Set "Pos[5_10]=_I"
Set "Pos[5_11]=_I"
Set "Pos[5_12]=_I"
Set "Pos[5_13]=_I"
Set "Pos[5_14]=_I"
Set "Pos[5_15]=_I"
Set "Pos[5_16]=_I"
Set "Pos[5_17]=_I"
Set "Pos[5_18]=_I"
Set "Pos[5_19]=_I"
Set "Pos[5_20]=_I"
Set "Pos[5_21]=_I"
Set "Pos[5_22]=_I"
Set "Pos[5_23]=_I"
Set "Pos[5_24]=_I"
Set "Pos[5_25]=_I"
Set "Pos[5_26]=_I"
Set "Pos[5_27]=_I"
Set "Pos[5_28]=_I"
Set "Pos[5_29]=_I"
Set "Pos[5_30]=_I"
Set "Pos[6_1]=_I"
Set "Pos[6_2]=_I"
Set "Pos[6_3]=_I"
Set "Pos[6_4]=_I"
Set "Pos[6_5]=_I"
Set "Pos[6_6]=_I"
Set "Pos[6_7]=_I"
Set "Pos[6_8]=_I"
Set "Pos[6_9]=_I"
Set "Pos[6_10]=_I"
Set "Pos[6_11]=_I"
Set "Pos[6_12]=_I"
Set "Pos[6_13]=_I"
Set "Pos[6_14]=_I"
Set "Pos[6_15]=_I"
Set "Pos[6_16]=_I"
Set "Pos[6_17]=_I"
Set "Pos[6_18]=_I"
Set "Pos[6_19]=_I"
Set "Pos[6_20]=_I"
Set "Pos[6_21]=_I"
Set "Pos[6_22]=_I"
Set "Pos[6_23]=_I"
Set "Pos[6_24]=_I"
Set "Pos[6_25]=_I"
Set "Pos[6_26]=_I"
Set "Pos[6_27]=_I"
Set "Pos[6_28]=_I"
Set "Pos[6_29]=_I"
Set "Pos[6_30]=_I"
Set "Pos[7_1]=_I"
Set "Pos[7_2]=_I"
Set "Pos[7_3]=_I"
Set "Pos[7_4]=_I"
Set "Pos[7_5]=_I"
Set "Pos[7_6]=_I"
Set "Pos[7_7]=_I"
Set "Pos[7_8]=_I"
Set "Pos[7_9]=_I"
Set "Pos[7_10]=_I"
Set "Pos[7_11]=_I"
Set "Pos[7_12]=_I"
Set "Pos[7_13]=_I"
Set "Pos[7_14]=_I"
Set "Pos[7_15]=_I"
Set "Pos[7_16]=_I"
Set "Pos[7_17]=_I"
Set "Pos[7_18]=_I"
Set "Pos[7_19]=_I"
Set "Pos[7_20]=_I"
Set "Pos[7_21]=_I"
Set "Pos[7_22]=_I"
Set "Pos[7_23]=_I"
Set "Pos[7_24]=_I"
Set "Pos[7_25]=_I"
Set "Pos[7_26]=_I"
Set "Pos[7_27]=_I"
Set "Pos[7_28]=_I"
Set "Pos[7_29]=_I"
Set "Pos[7_30]=_I"
Set "Pos[8_1]=_I"
Set "Pos[8_2]=_I"
Set "Pos[8_3]=_I"
Set "Pos[8_4]=_I"
Set "Pos[8_5]=_I"
Set "Pos[8_6]=_I"
Set "Pos[8_7]=_I"
Set "Pos[8_8]=_I"
Set "Pos[8_9]=_I"
Set "Pos[8_10]=_I"
Set "Pos[8_11]=_I"
Set "Pos[8_12]=_I"
Set "Pos[8_13]=_I"
Set "Pos[8_14]=_I"
Set "Pos[8_15]=_I"
Set "Pos[8_16]=_I"
Set "Pos[8_17]=_I"
Set "Pos[8_18]=_I"
Set "Pos[8_19]=_I"
Set "Pos[8_20]=_I"
Set "Pos[8_21]=_I"
Set "Pos[8_22]=_I"
Set "Pos[8_23]=_I"
Set "Pos[8_24]=_I"
Set "Pos[8_25]=_I"
Set "Pos[8_26]=_I"
Set "Pos[8_27]=_I"
Set "Pos[8_28]=_I"
Set "Pos[8_29]=_I"
Set "Pos[8_30]=_I"
Set "Pos[9_1]=_I"
Set "Pos[9_2]=_I"
Set "Pos[9_3]=_I"
Set "Pos[9_4]=_I"
Set "Pos[9_5]=_I"
Set "Pos[9_6]=_I"
Set "Pos[9_7]=_I"
Set "Pos[9_8]=_I"
Set "Pos[9_9]=_I"
Set "Pos[9_10]=_I"
Set "Pos[9_11]=_I"
Set "Pos[9_12]=_I"
Set "Pos[9_13]=_I"
Set "Pos[9_14]=_I"
Set "Pos[9_15]=_O"
Set "Pos[9_16]=_I"
Set "Pos[9_17]=_I"
Set "Pos[9_18]=_I"
Set "Pos[9_19]=_I"
Set "Pos[9_20]=_I"
Set "Pos[9_21]=_I"
Set "Pos[9_22]=_I"
Set "Pos[9_23]=_I"
Set "Pos[9_24]=_I"
Set "Pos[9_25]=_I"
Set "Pos[9_26]=_I"
Set "Pos[9_27]=_I"
Set "Pos[9_28]=_I"
Set "Pos[9_29]=_I"
Set "Pos[9_30]=_I"
Set "Pos[10_1]=_I"
Set "Pos[10_2]=_I"
Set "Pos[10_3]=_I"
Set "Pos[10_4]=_I"
Set "Pos[10_5]=_O"
Set "Pos[10_6]=_O"
Set "Pos[10_7]=_O"
Set "Pos[10_8]=_I"
Set "Pos[10_9]=_I"
Set "Pos[10_10]=_I"
Set "Pos[10_11]=_I"
Set "Pos[10_12]=_I"
Set "Pos[10_13]=_I"
Set "Pos[10_14]=_O"
Set "Pos[10_15]=_O"
Set "Pos[10_16]=_O"
Set "Pos[10_17]=_I"
Set "Pos[10_18]=_I"
Set "Pos[10_19]=_I"
Set "Pos[10_20]=_I"
Set "Pos[10_21]=_I"
Set "Pos[10_22]=_I"
Set "Pos[10_23]=_O"
Set "Pos[10_24]=_O"
Set "Pos[10_25]=_O"
Set "Pos[10_26]=_I"
Set "Pos[10_27]=_I"
Set "Pos[10_28]=_I"
Set "Pos[10_29]=_I"
Set "Pos[10_30]=_I"
Set "Pos[11_1]=_I"
Set "Pos[11_2]=_I"
Set "Pos[11_3]=_I"
Set "Pos[11_4]=_I"
Set "Pos[11_5]=_I"
Set "Pos[11_6]=_O"
Set "Pos[11_7]=_I"
Set "Pos[11_8]=_I"
Set "Pos[11_9]=_I"
Set "Pos[11_10]=_I"
Set "Pos[11_11]=_I"
Set "Pos[11_12]=_I"
Set "Pos[11_13]=_I"
Set "Pos[11_14]=_I"
Set "Pos[11_15]=_I"
Set "Pos[11_16]=_I"
Set "Pos[11_17]=_I"
Set "Pos[11_18]=_I"
Set "Pos[11_19]=_I"
Set "Pos[11_20]=_I"
Set "Pos[11_21]=_I"
Set "Pos[11_22]=_I"
Set "Pos[11_23]=_I"
Set "Pos[11_24]=_O"
Set "Pos[11_25]=_I"
Set "Pos[11_26]=_I"
Set "Pos[11_27]=_I"
Set "Pos[11_28]=_I"
Set "Pos[11_29]=_I"
Set "Pos[11_30]=_I"
Set "Pos[12_1]=_I"
Set "Pos[12_2]=_I"
Set "Pos[12_3]=_I"
Set "Pos[12_4]=_I"
Set "Pos[12_5]=_I"
Set "Pos[12_6]=_I"
Set "Pos[12_7]=_I"
Set "Pos[12_8]=_I"
Set "Pos[12_9]=_I"
Set "Pos[12_10]=_I"
Set "Pos[12_11]=_I"
Set "Pos[12_12]=_I"
Set "Pos[12_13]=_I"
Set "Pos[12_14]=_I"
Set "Pos[12_15]=_I"
Set "Pos[12_16]=_I"
Set "Pos[12_17]=_I"
Set "Pos[12_18]=_I"
Set "Pos[12_19]=_I"
Set "Pos[12_20]=_I"
Set "Pos[12_21]=_I"
Set "Pos[12_22]=_I"
Set "Pos[12_23]=_I"
Set "Pos[12_24]=_I"
Set "Pos[12_25]=_I"
Set "Pos[12_26]=_I"
Set "Pos[12_27]=_I"
Set "Pos[12_28]=_I"
Set "Pos[12_29]=_I"
Set "Pos[12_30]=_I"
Set "Pos[13_1]=_I"
Set "Pos[13_2]=_I"
Set "Pos[13_3]=_I"
Set "Pos[13_4]=_I"
Set "Pos[13_5]=_I"
Set "Pos[13_6]=_I"
Set "Pos[13_7]=_I"
Set "Pos[13_8]=_I"
Set "Pos[13_9]=_I"
Set "Pos[13_10]=_I"
Set "Pos[13_11]=_I"
Set "Pos[13_12]=_I"
Set "Pos[13_13]=_I"
Set "Pos[13_14]=_I"
Set "Pos[13_15]=_I"
Set "Pos[13_16]=_I"
Set "Pos[13_17]=_I"
Set "Pos[13_18]=_I"
Set "Pos[13_19]=_I"
Set "Pos[13_20]=_I"
Set "Pos[13_21]=_I"
Set "Pos[13_22]=_I"
Set "Pos[13_23]=_I"
Set "Pos[13_24]=_I"
Set "Pos[13_25]=_I"
Set "Pos[13_26]=_I"
Set "Pos[13_27]=_I"
Set "Pos[13_28]=_I"
Set "Pos[13_29]=_I"
Set "Pos[13_30]=_I"
Set "Pos[14_1]=_I"
Set "Pos[14_2]=_I"
Set "Pos[14_3]=_I"
Set "Pos[14_4]=_I"
Set "Pos[14_5]=_I"
Set "Pos[14_6]=_I"
Set "Pos[14_7]=_I"
Set "Pos[14_8]=_I"
Set "Pos[14_9]=_I"
Set "Pos[14_10]=_I"
Set "Pos[14_11]=_I"
Set "Pos[14_12]=_I"
Set "Pos[14_13]=_I"
Set "Pos[14_14]=_I"
Set "Pos[14_15]=_I"
Set "Pos[14_16]=_I"
Set "Pos[14_17]=_I"
Set "Pos[14_18]=_I"
Set "Pos[14_19]=_I"
Set "Pos[14_20]=_I"
Set "Pos[14_21]=_I"
Set "Pos[14_22]=_I"
Set "Pos[14_23]=_I"
Set "Pos[14_24]=_I"
Set "Pos[14_25]=_I"
Set "Pos[14_26]=_I"
Set "Pos[14_27]=_I"
Set "Pos[14_28]=_I"
Set "Pos[14_29]=_I"
Set "Pos[14_30]=_I"
Set "Pos[15_1]=_I"
Set "Pos[15_2]=_I"
Set "Pos[15_3]=_I"
Set "Pos[15_4]=_I"
Set "Pos[15_5]=_I"
Set "Pos[15_6]=_I"
Set "Pos[15_7]=_I"
Set "Pos[15_8]=_I"
Set "Pos[15_9]=_I"
Set "Pos[15_10]=_I"
Set "Pos[15_11]=_I"
Set "Pos[15_12]=_I"
Set "Pos[15_13]=_I"
Set "Pos[15_14]=_I"
Set "Pos[15_15]=_I"
Set "Pos[15_16]=_I"
Set "Pos[15_17]=_I"
Set "Pos[15_18]=_I"
Set "Pos[15_19]=_I"
Set "Pos[15_20]=_I"
Set "Pos[15_21]=_I"
Set "Pos[15_22]=_I"
Set "Pos[15_23]=_I"
Set "Pos[15_24]=_I"
Set "Pos[15_25]=_I"
Set "Pos[15_26]=_I"
Set "Pos[15_27]=_I"
Set "Pos[15_28]=_I"
Set "Pos[15_29]=_I"
Set "Pos[15_30]=_I"
Set "Pos[16_1]=_I"
Set "Pos[16_2]=_I"
Set "Pos[16_3]=_I"
Set "Pos[16_4]=_I"
Set "Pos[16_5]=_I"
Set "Pos[16_6]=_I"
Set "Pos[16_7]=_I"
Set "Pos[16_8]=_I"
Set "Pos[16_9]=_I"
Set "Pos[16_10]=_I"
Set "Pos[16_11]=_I"
Set "Pos[16_12]=_I"
Set "Pos[16_13]=_I"
Set "Pos[16_14]=_I"
Set "Pos[16_15]=_I"
Set "Pos[16_16]=_I"
Set "Pos[16_17]=_I"
Set "Pos[16_18]=_I"
Set "Pos[16_19]=_I"
Set "Pos[16_20]=_I"
Set "Pos[16_21]=_I"
Set "Pos[16_22]=_I"
Set "Pos[16_23]=_I"
Set "Pos[16_24]=_I"
Set "Pos[16_25]=_I"
Set "Pos[16_26]=_I"
Set "Pos[16_27]=_I"
Set "Pos[16_28]=_I"
Set "Pos[16_29]=_I"
Set "Pos[16_30]=_I"
Set "Pos[17_1]=_I"
Set "Pos[17_2]=_I"
Set "Pos[17_3]=_I"
Set "Pos[17_4]=_I"
Set "Pos[17_5]=_I"
Set "Pos[17_6]=_I"
Set "Pos[17_7]=_I"
Set "Pos[17_8]=_I"
Set "Pos[17_9]=_I"
Set "Pos[17_10]=_I"
Set "Pos[17_11]=_I"
Set "Pos[17_12]=_I"
Set "Pos[17_13]=_I"
Set "Pos[17_14]=_I"
Set "Pos[17_15]=_I"
Set "Pos[17_16]=_I"
Set "Pos[17_17]=_I"
Set "Pos[17_18]=_I"
Set "Pos[17_19]=_I"
Set "Pos[17_20]=_I"
Set "Pos[17_21]=_I"
Set "Pos[17_22]=_I"
Set "Pos[17_23]=_I"
Set "Pos[17_24]=_I"
Set "Pos[17_25]=_I"
Set "Pos[17_26]=_I"
Set "Pos[17_27]=_I"
Set "Pos[17_28]=_I"
Set "Pos[17_29]=_I"
Set "Pos[17_30]=_I"
Set "Pos[18_1]=_I"
Set "Pos[18_2]=_I"
Set "Pos[18_3]=_I"
Set "Pos[18_4]=_I"
Set "Pos[18_5]=_I"
Set "Pos[18_6]=_I"
Set "Pos[18_7]=_I"
Set "Pos[18_8]=_I"
Set "Pos[18_9]=_I"
Set "Pos[18_10]=_I"
Set "Pos[18_11]=_I"
Set "Pos[18_12]=_I"
Set "Pos[18_13]=_I"
Set "Pos[18_14]=_I"
Set "Pos[18_15]=_I"
Set "Pos[18_16]=_I"
Set "Pos[18_17]=_I"
Set "Pos[18_18]=_I"
Set "Pos[18_19]=_I"
Set "Pos[18_20]=_I"
Set "Pos[18_21]=_I"
Set "Pos[18_22]=_I"
Set "Pos[18_23]=_I"
Set "Pos[18_24]=_I"
Set "Pos[18_25]=_I"
Set "Pos[18_26]=_I"
Set "Pos[18_27]=_I"
Set "Pos[18_28]=_I"
Set "Pos[18_29]=_I"
Set "Pos[18_30]=_I"
Set "Pos[19_1]=_I"
Set "Pos[19_2]=_I"
Set "Pos[19_3]=_I"
Set "Pos[19_4]=_I"
Set "Pos[19_5]=_I"
Set "Pos[19_6]=_I"
Set "Pos[19_7]=_I"
Set "Pos[19_8]=_I"
Set "Pos[19_9]=_I"
Set "Pos[19_10]=_I"
Set "Pos[19_11]=_I"
Set "Pos[19_12]=_I"
Set "Pos[19_13]=_I"
Set "Pos[19_14]=_I"
Set "Pos[19_15]=_I"
Set "Pos[19_16]=_I"
Set "Pos[19_17]=_I"
Set "Pos[19_18]=_I"
Set "Pos[19_19]=_I"
Set "Pos[19_20]=_I"
Set "Pos[19_21]=_I"
Set "Pos[19_22]=_I"
Set "Pos[19_23]=_I"
Set "Pos[19_24]=_I"
Set "Pos[19_25]=_I"
Set "Pos[19_26]=_I"
Set "Pos[19_27]=_I"
Set "Pos[19_28]=_I"
Set "Pos[19_29]=_I"
Set "Pos[19_30]=_I"
Set "Pos[20_1]=_I"
Set "Pos[20_2]=_I"
Set "Pos[20_3]=_I"
Set "Pos[20_4]=_I"
Set "Pos[20_5]=_I"
Set "Pos[20_6]=_I"
Set "Pos[20_7]=_I"
Set "Pos[20_8]=_I"
Set "Pos[20_9]=_I"
Set "Pos[20_10]=_I"
Set "Pos[20_11]=_I"
Set "Pos[20_12]=_I"
Set "Pos[20_13]=_I"
Set "Pos[20_14]=_I"
Set "Pos[20_15]=_I"
Set "Pos[20_16]=_I"
Set "Pos[20_17]=_I"
Set "Pos[20_18]=_I"
Set "Pos[20_19]=_I"
Set "Pos[20_20]=_I"
Set "Pos[20_21]=_I"
Set "Pos[20_22]=_I"
Set "Pos[20_23]=_I"
Set "Pos[20_24]=_I"
Set "Pos[20_25]=_I"
Set "Pos[20_26]=_I"
Set "Pos[20_27]=_I"
Set "Pos[20_28]=_I"
Set "Pos[20_29]=_I"
Set "Pos[20_30]=_I"
CD ..
GameOfLife Load

misol101
Posts: 475
Joined: 02 May 2016 18:20

Re: Game Of Life

#2 Post by misol101 » 02 Jun 2020 15:47

Nice :) Here is a rather minimal version using cmdgfx.exe:

Code: Select all

@echo off
set /a w=160, h=80, density=10, col=1
mode %w%,%h%
if not defined _ set _=. & call %0 | cmdgfx.exe "fbox 0 0 db & block 0 0,0,%w%,%h% 0,0 -1 0 0 - random()*100/(101-%density%)" S
:rep
	echo "cmdgfx: block 0 0,0,%w%,%h% 0,0 -1 0 0 - store(gtr(col(x-1,y-1),0)+gtr(col(x,y-1),0)+gtr(col(x+1,y-1),0)+gtr(col(x-1,y),0)+gtr(col(x+1,y),0)+gtr(col(x-1,y+1),0)+gtr(col(x,y+1),0)+gtr(col(x+1,y+1),0),0)+((1-(lss(s0,2)+gtr(s0,3)))*gtr(col(x,y),0)+eq(col(x,y)*10+s0,3))*%col%"
goto rep

T3RRY
Posts: 243
Joined: 06 May 2020 10:14

Re: Game Of Life

#3 Post by T3RRY » 02 Jun 2020 22:13

I may just have to start learning how to utilise cmdgfx. That's much more efficient.
Hope you don't mind, I removed the goto, added in the Cursor Hide, adjusted the font to a smaller size, and played around with the color and character choices

Code: Select all

@echo off & TITLE Game of Life & SETLOCAL EnableDelayedExpansion
For /F "Delims=" %%E in ('Where /R "%USERPROFILE%" cmdgfx.exe') Do CD "%%~dpE" || Echo cmdgfx.exe is not installed in your user profile && Exit /B 0
(For /F "Delims=" %%P in ('Where /R "%USERPROFILE%" BG.exe') Do %%P Cursor 0 && %%P font 2) || Title Game of Life can be enhanced with BG.exe
cmdwiz.exe SetWindowTransparency 25 1> Nul  2>Nul
set /a w=160, h=80, density=35, col=25
mode %w%,%h%
if not defined _ set _=. & call %0 | cmdgfx.exe "fbox 0 0 fe & block 0 0,0,%w%,%h% 0,0 -1 0 0 - random()*100/(101-%density%)" S -n
For /L %%. in () Do (Set /A Tk+=1 & Title Game Of Life Gen: !Tk!& echo "cmdgfx: block 0 0,0,%w%,%h% 0,0 -1 0 0 - store(gtr(col(x-1,y-1),0)+gtr(col(x,y-1),0)+gtr(col(x+1,y-1),0)+gtr(col(x-1,y),0)+gtr(col(x+1,y),0)+gtr(col(x-1,y+1),0)+gtr(col(x,y+1),0)+gtr(col(x+1,y+1),0),0)+((1-(lss(s0,2)+gtr(s0,3)))*gtr(col(x,y),0)+eq(col(x,y)*10+s0,3))*%col%")
Last edited by T3RRY on 04 Jun 2020 12:21, edited 1 time in total.

misol101
Posts: 475
Joined: 02 May 2016 18:20

Re: Game Of Life

#4 Post by misol101 » 03 Jun 2020 11:50

T3RRY wrote:
02 Jun 2020 22:13
I may just have to start learning how to utilise cmdgfx
Please do if you feel like it, would be fun to see what you could do with it.

It is fairly well documented, both from command line and at
https://github.com/misol1/cmdgfx/blob/master/README.md

Then again, it is quite a feature-rich program (some might say bloated) so some parts are definitely not covered all that extensively. But the archive also contains loads of examples that should be helpful too.
Let me know if you run into issues, I won’t pretend that there won’t be a few hickups.

The block expression in the script might look a bit daunting, but at its essence a block color expression is just a math expression which produces a (color) value between 0-15 (or 0-255 if you want to make a background color as well), which runs once per character with input x and y for each position (actually pretty similar to a fragment shader...)

To save screen buffer (as in original script), use cmdgfx c flag, e.g to capture the initial buffer after randomization, add this to the end of the line so it becomes: Sc:0,0,%w%,%h%,1,1

This will save capture-1.gxy, which could be drawn again at some point with cmdgfx ”image”operation to restore a specific seed.

By the way, note that this version of Game of Life doesn’t wrap around the edges. This can be accomplished too of course but the expression will grow. So when counting the neighbours, each position checked (previously e.g: gtr(col(x+1,y-1),0) will look like: gtr(col(mod(%w%+x+1),%w%),mod(%h%+y-1,%h%)),0)
Last edited by misol101 on 03 Jun 2020 12:12, edited 1 time in total.

T3RRY
Posts: 243
Joined: 06 May 2020 10:14

Re: Game Of Life

#5 Post by T3RRY » 03 Jun 2020 12:04

Thanks for the extra information. I must say I'm very impressed with the performance that can be achieved. ~ 12 hundredths of a second per generation on my PC. Can't yet claim to understand how the rules of Game of life are being implemented in the script, but it does give me alot to explore. I've previously downloaded the archive, but haven't invested much time studying the scripts within to develop an understanding of what can be achieved with your programs (and how). Time for that to change.

misol101
Posts: 475
Joined: 02 May 2016 18:20

Re: Game Of Life

#6 Post by misol101 » 03 Jun 2020 12:58

T3RRY wrote:
03 Jun 2020 12:04
Can't yet claim to understand how the rules of Game of life are being implemented in the script
Things do tend to get cryptic when having to compose everything in a single line :mrgreen: (not true, it could be broken into several lines, but i digress)

Actually, it's worth a break-down.

Entire expression (I changed the "second part" slightly to make it simpler):

Code: Select all

store(gtr(col(x-1,y-1),0)+gtr(col(x,y-1),0)+gtr(col(x+1,y-1),0)+gtr(col(x-1,y),0)+gtr(col(x+1,y),0)+gtr(col(x-1,y+1),0)+gtr(col(x,y+1),0)+gtr(col(x+1,y+1),0),0)+((eq(s0,2)+eq(s0,3))*gtr(col(x,y),0)+eq(col(x,y)*10+s0,3))*!col!
The store function takes a value and puts it into 1 of 5 storage variables, s0-s4. The result of the store function is always 0, so it can be safely added to the rest of the expression, e.g:
store(1+5,2) will store 6 into s2,and return 0.

This entire part counts the current position's neighbours and stores it in s0:

Code: Select all

store(gtr(col(x-1,y-1),0)+gtr(col(x,y-1),0)+gtr(col(x+1,y-1),0)+gtr(col(x-1,y),0)+gtr(col(x+1,y),0)+gtr(col(x-1,y+1),0)+gtr(col(x,y+1),0)+gtr(col(x+1,y+1),0),0) 
The col function returns the color value at the given position, so: col(x-1,y-1) returns our top left neighbour.

If we only used color 1, then we could just sum up like col(x+1,y+1) etc, but since color might be bigger than 1, we use "gtr" function like: gtr(col(x-1,y-1),0) , which returns 1 if value1 is bigger than value2, otherwise 0.

So now we have summed up all neighbours and have a value 0-8 stored in s0.

Second "part" is:

Code: Select all

(eq(s0,2)+eq(s0,3))*gtr(col(x,y),0)
This corresponds to the rule saying that if the cell is alive, then it stays alive if it has 2 or 3 neighbours.
eq returns 1 if values are equal, otherwise 0, and s0 holds number of neighbours, so eq(s0,2) means "return 1 if there are 2 neighbours, otherwise 0".
Then we multiply it by the color of our current cell(clamped to max 1 as before), so the end result is either 1(if cell was alive and we had 2 or 3 neigbours) or otherwise 0.

Last part is:

Code: Select all

eq(col(x,y)*10+s0,3)
This corresponds to the rule saying that if the cell is dead, it lives if we have 3 neighbours.
Nothing new here, just worth pointing out that 10 is arbitrary (could be any value 4 or higher), but the point is that if the cell is NOT dead, then we want to make sure that its color value+neighbours does not end up in result 3.

The entire thing adds up to a value that is either 0 or 1. Then in the final step, it is multiplied by !col!, producing the final value as either 0 or col.

Phew :) It is worth noting that color expressions this complex are not that common.

T3RRY
Posts: 243
Joined: 06 May 2020 10:14

Re: Game Of Life

#7 Post by T3RRY » 04 Jun 2020 09:54

Thanks for the breakdown. The section that counts neighbour cells was clear enough, however I hadn't been able to pick out on what basis the values were stored and compared against the current cell. With the aid of your breakdown, I now see how it's all working together. Love how efficient it is compared to working with nested for loops in batch.

Aacini
Expert
Posts: 1885
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: Game Of Life

#8 Post by Aacini » 05 Jun 2020 17:09

A different approach based on pure Batch file commands:

Code: Select all

@echo off
setlocal EnableDelayedExpansion

rem Density is a value between 0 (minimum) and 9 (maximum)
set /A width=60, height=30, density=3,  wP1=width+2, hP1=height+2
mode %wP1%,%hP1%
title Game of Life - %width% X %height% @ %density%

rem Empty environment (faster execution)
(
for /F "delims==" %%v in ('set') do set "%%v="
set /A "width=%width%, height=%height%, density=%density%,  wP1=%wP1%-1, hP1=%hP1%-1"
set "ComSpec=%ComSpec%"
)

rem Populate the initial seed:
for /L %%x in (0,1,%wP1%) do (
   set "grid[0]=!grid[0]!0"
   set "grid[%hP1%]=!grid[%hP1%]!0"
)
for /L %%y in (1,1,%height%) do (
   for /L %%x in (1,1,%width%) do (
      rem The grid is comprised of "0" or "1" values that are directly used in the formulae
      rem the "right shift" of 31 bits produce -1 or 0; plus 1 = 0 or 1
      set /A "cell=((density-!random:~-1!)>>31)+1"
      set "newGrid[%%y]=!newGrid[%%y]!!cell!"
   )
)

:loop
for /L %%. in ( ) do (

rem Show new grid and transfer it to current grid
cls
echo/
for /L %%i in (1,1,%height%) do (
   echo  !newGrid[%%i]!
   set "grid[%%i]=0!newGrid[%%i]!0"
   set "newGrid[%%i]="
)
if defined wP1 set "wP1=" & pause

rem Pass to next tick
for /L %%y in (1,1,%height%) do (
   set /A "yM1=%%y-1, yP1=%%y+1"
   rem Below: %%A = y-1, %%B = y, %%C = y+1
   for /F "tokens=1-3" %%A in ("!yM1! %%y !yP1!") do for /L %%x in (1,1,%width%) do (
      set /A "xM1=%%x-1, xP1=%%x+1"
      rem Below: %%a = x-1, %%b = x, %%c = x+1
      for /F "tokens=1-3" %%a in ("!xM1! %%x !xP1!") do (
         set /A ^"neighbours=!grid[%%A]:~%%a,1!+!grid[%%A]:~%%b,1!+!grid[%%A]:~%%c,1! + ^
                             !grid[%%B]:~%%a,1!+                   !grid[%%B]:~%%c,1! + ^
                             !grid[%%C]:~%%a,1!+!grid[%%C]:~%%b,1!+!grid[%%C]:~%%c,1! ^"
      )
      rem          cell live      AND (   neigs  ==  2 OR  neigs  ==  3 ) OR NOT cell live     AND (neigs  ==  3)
      set /A "cell=!grid[%%y]:~%%x,1!*(^!(neighbours-2)+^!(neighbours-3)) + ^!!grid[%%y]:~%%x,1!*^!(neighbours-3)"
      set "newGrid[%%y]=!newGrid[%%y]!!cell!"
   )
)

)
goto loop
This method is simple but slow, because it redraws the entire grid in each tick. This method could be modified in order to just refresh the cells that change state and redraw they via my Read arrow keys and show color text in an efficient way method. Perhaps in this way the program would run faster, and in color...

Antonio

Aacini
Expert
Posts: 1885
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: Game Of Life

#9 Post by Aacini » 05 Jun 2020 19:43

misol101 wrote:
03 Jun 2020 12:58

. . . . .

The store function takes a value and puts it into 1 of 5 storage variables, s0-s4 . . . . . .

This entire part counts the current position's neighbours and stores it in s0:

Code: Select all

store(gtr(col(x-1,y-1),0)+gtr(col(x,y-1),0)+gtr(col(x+1,y-1),0)+gtr(col(x-1,y),0)+gtr(col(x+1,y),0)+gtr(col(x-1,y+1),0)+gtr(col(x,y+1),0)+gtr(col(x+1,y+1),0),0) 
. . . . .

So now we have summed up all neighbours and have a value 0-8 stored in s0.

Second "part" is:

Code: Select all

(eq(s0,2)+eq(s0,3))*gtr(col(x,y),0)
. . . . . so the end result is either 1(if cell was alive and we had 2 or 3 neigbours) or otherwise 0.

Last part is:

Code: Select all

eq(col(x,y)*10+s0,3)
This corresponds to the rule saying that if the cell is dead, it lives if we have 3 neighbours.

. . . . .

The entire thing adds up to a value that is either 0 or 1. Then in the final step, it is multiplied by !col!, producing the final value as either 0 or col.

Mmmm... I am confused... If I correctly understood, your long expression count the neighbours of one pixel, store it in s0 storage variable (there are just 5 storage variables) and this variable is used to know the final value of the pixel. However, your description did not explain what to do with this final value. I assume that this value is used to set the new color of the pixel in the screen immediately after its final value was calculated.

If this is true, then your method does not follow the Game of Life rules. The article at Wikipedia indicates:

"The initial pattern constitutes the seed of the system. The first generation is created by applying the above rules simultaneously to every cell in the seed; births and deaths occur simultaneously, and the discrete moment at which this happens is sometimes called a tick. Each generation is a pure function of the preceding one (its evaluation has not side effects). The rules continue to be applied repeatedly to create further generations."

In other words: you should calculate the new state of all cells in the grid using their original values, and pass to next tick (change the values of all cells) after that. It seems that your method change the state of each cell immediately, altering the new state of posterior cells.

Perhaps I misunderstood something?

Antonio

T3RRY
Posts: 243
Joined: 06 May 2020 10:14

Re: Game Of Life

#10 Post by T3RRY » 06 Jun 2020 01:47

If my understanding is correct, the color expression is applied to the whole 'block'. what's missing from the description is how
block 0 0,0,%w%,%h% 0,0 -1 0 0 -
relates to the entire grid. However, Block operations are described in the documentation Mikael linked to. Still in the process of getting my head around it all though. From what I believe I've pieced together so far, the parameters for the block operation essentially clone the grid

On another note, I'm impressed by the efficiency of your pure batch script, even if it is rather visually dull. Have been meening to have a run at building a version that calculates each new generation using a list containing the living cells to minimise number of operationd required to update each cycle my current method types a background screen populated with 'dead' cells and a second file containing the living cells. The concept I'm thinking of would iterate over each X.Y value in the Live string, and use a macro to fetch neighbour cell values and search the string for living neighbours to increment live count before implementing as a replacement string. It should drastically reduce the number of operations required compared to my original build concept

misol101
Posts: 475
Joined: 02 May 2016 18:20

Re: Game Of Life

#11 Post by misol101 » 06 Jun 2020 02:49

Aacini wrote:
05 Jun 2020 19:43
Mmmm... I am confused... If I correctly understood, your long expression count the neighbours of one pixel, store it in s0 storage variable (there are just 5 storage variables) and this variable is used to know the final value of the pixel. However, your description did not explain what to do with this final value. I assume that this value is used to set the new color of the pixel in the screen immediately after its final value was calculated.
Hi Antonio, the block operation does a whole lot of copying behind the scenes (a bit too much for optimal speed actually, but I digress).

The operation: "block 0 0,0,%w%,%h% 0,0 -1 0 0 - ..." first makes a copy of the block in the buffer (A) at position 0,0 with width %w% and height %h% to a temporary buffer B of size %w%,%h%, and it also creates a second temp buffer C of size %w%,%h%. When the expression runs, the result per character in buffer B is written to buffer C, so the result does not affect buffer B (or the original buffer) at all. Only in the final step is buffer C copied back into the original buffer at position 0,0.

Most other color expressions do not rely on previous values in the buffer to compute, in which case all this copying is kind of unnecessary, but it does help in cases such as Game of Life.

There is one issue with my block solution, the fact that it does not wrap around the edges (I believe Game of Life usually does). I adressed that in my second post in this thread.



For other scripts in the Cmdgfx archive that uses "block" AND relies on previous values to compute its own, see:
RGB-Fire-js.bat, RGB-Fire-js-2.bat, server-cubeincube.bat, server-life.bat (Duh!)

For prominent examples of color expressions where previous values are NOT used, see e.g.:
RGB-Fractal.bat, RGB-Plasma.bat, RGB-Bounce-js.bat, server-checkerbox-transitions.bat, ScreenSaver-PlasmaHulk.bat, servH-plasma.bat, ScreenSaver-Static.bat

Also a quite interesting example to look at for actual image (BMP files) manipulation: bmp_convert.bat

Prominent examples that use purely the transform argument of block, using no math expressions:
server-matrix.bat, server-matrix01.bat, server-fire-js.bat, server-trails.bat

Prominent examples that use the x and y MOVE expressions rather than the color expressions:
RGB-Wave-Face.bat, servH-bob-flag.bat, server-wave-face.bat, server-rotate-expr.bat, hearts.bat

The majority of examples in the archive don't use the block operation. In fact, using block with expressions is pretty slow due to running every position through a recursive parse tree. Compared to batch though, even block with expressions is a speed demon :) For an example where this is noticable, see: servH-loader-pixel.bat. Here a simple color expression ( random()*!RANDVAL!+fgcol(y,y) ) is run *per pixel*. Try running this in full-screen (Ctrl-Enter or Alt-Enter while running), and the FPS is likely to go down.This is both due to all the excess copying but mostly just because expressions are comparatively slow. If you run server-life.bat in full-screen, it still runs at an ok speed on my machine, but try doing the same in pixel mode and it wouldn't be so pretty...
Last edited by misol101 on 08 Jun 2020 10:16, edited 3 times in total.

misol101
Posts: 475
Joined: 02 May 2016 18:20

Re: Game Of Life

#12 Post by misol101 » 06 Jun 2020 05:10

try doing the same in pixel mode and it wouldn't be so pretty...
Actually let's try it:

Code: Select all

@echo off
setlocal EnableDelayedExpansion
cmdwiz fullscreen 1 & cmdwiz showcursor 0
cmdwiz getdisplaydim w & set /a w=!errorlevel!
cmdwiz getdisplaydim h & set /a h=!errorlevel!
set /a density=10, col=10
if not defined _ set _=. & call %0 | cmdgfx_gdi.exe "fbox 0 0 db & block 0 0,0,%w%,%h% 0,0 -1 0 0 - random()*100/(101-%density%)" Sfa:0,0,%w%,%h%n
for /l %%a in () do (
	echo "cmdgfx: block 0 0,0,%w%,%h% 0,0 -1 0 0 - store(gtr(col(x-1,y-1),0)+gtr(col(x,y-1),0)+gtr(col(x+1,y-1),0)+gtr(col(x-1,y),0)+gtr(col(x+1,y),0)+gtr(col(x-1,y+1),0)+gtr(col(x,y+1),0)+gtr(col(x+1,y+1),0),0)+((1-(lss(s0,2)+gtr(s0,3)))*gtr(col(x,y),0)+eq(col(x,y)*10+s0,3))*%col%"
)
Ouch... that's 1920*1080 (~2 million) expressions/pixels updating per frame on my display. Around 1 FPS on my machine :) (whereas in pure C this would likely be 100 Fps+. Good thing cmdgfx wasn't primarily made for pixel modes...)

Having said that, my new (unreleased) version of cmdgfx allows running the block operation using threads/multi-core CPU, so I could get it up to ~3 Fps for the above by simply adding a flag. Still not great.

Anyway, speed is more acceptable (on my machine) running pixel mode c instead (3x3 size pixels):

Code: Select all

@echo off
setlocal EnableDelayedExpansion
cmdwiz fullscreen 1 & cmdwiz showcursor 0
cmdwiz getdisplaydim w & set /a w=!errorlevel!/3+1
cmdwiz getdisplaydim h & set /a h=!errorlevel!/3+1
set /a density=10, col=12
if not defined _ set _=. & call %0 | cmdgfx_gdi.exe "fbox 0 0 db & block 0 0,0,%w%,%h% 0,0 -1 0 0 - random()*100/(101-%density%)" Sfc:0,0,%w%,%h%n
for /l %%a in () do (
	echo "cmdgfx: block 0 0,0,%w%,%h% 0,0 -1 0 0 - store(gtr(col(x-1,y-1),0)+gtr(col(x,y-1),0)+gtr(col(x+1,y-1),0)+gtr(col(x-1,y),0)+gtr(col(x+1,y),0)+gtr(col(x-1,y+1),0)+gtr(col(x,y+1),0)+gtr(col(x+1,y+1),0),0)+((1-(lss(s0,2)+gtr(s0,3)))*gtr(col(x,y),0)+eq(col(x,y)*10+s0,3))*(%col%+eq(s0,3)*3)"
)
EDIT: added a little twist for fun in the last example, using different color depending on state

EDIT2: confirmed FPS of "pure C" (actually, same example as here with cmdgfx plus an external C dll) at 130 FPS at 1920x1080
Last edited by misol101 on 05 Jul 2020 17:40, edited 2 times in total.

misol101
Posts: 475
Joined: 02 May 2016 18:20

Re: Game Of Life

#13 Post by misol101 » 06 Jun 2020 07:38

Optimization attempt:

The general rule for expressions is simple: the longer they are, the slower they run. At a granularity of ~2 million pixels, it might be worth trying to cut it a bit.

By only allowing using color 1, we can get rid of 9 calls to gtr, plus the final multiplication. But isn't that boring, then the color will always be dark blue? No, because cmdgfx_gdi.exe allows changing the palette (per frame if we would like), so we just set it to whatever RGB color we want (regular cmdgfx.exe can rearrange the order of the palette, but not set it explicitly).

This version runs 50-80% faster, so that's approaching 2 FPS at least on my machine :P (and ~5 FPS with new version threading)

Code: Select all

@echo off
setlocal EnableDelayedExpansion
cmdwiz fullscreen 1 & cmdwiz showcursor 0
cmdwiz getdisplaydim w & set /a w=!errorlevel!
cmdwiz getdisplaydim h & set /a h=!errorlevel!
set /a density=10
set col=ff6655
if not defined _ set _=. & call %0 | cmdgfx_gdi.exe "fbox 0 0 db & block 0 0,0,%w%,%h% 0,0 -1 0 0 - min(random()*100/(101-%density%),1)" Sfa:0,0,%w%,%h%n 000000,%col%
for /l %%a in () do (
	echo "cmdgfx: block 0 0,0,%w%,%h% 0,0 -1 0 0 - store(col(x-1,y-1)+col(x,y-1)+col(x+1,y-1)+col(x-1,y)+col(x+1,y)+col(x-1,y+1)+col(x,y+1)+col(x+1,y+1),0)+(eq(s0,2)+eq(s0,3))*col(x,y)+eq(col(x,y)*10+s0,3)"
)

T3RRY
Posts: 243
Joined: 06 May 2020 10:14

Re: Game Of Life

#14 Post by T3RRY » 07 Jun 2020 02:18

Liking the color by state expession. I'm getting approximately 18 frames per second for the latest version compared to the ~110 from your original version. I actually prefer that pace as it's easier to see the evolution between generations.

Did have to lower my screen res to scale it to full screen however.

misol101
Posts: 475
Joined: 02 May 2016 18:20

Re: Game Of Life

#15 Post by misol101 » 07 Jun 2020 13:31

T3RRY wrote:
07 Jun 2020 02:18
Liking the color by state expession. I'm getting approximately 18 frames per second for the latest version compared to the ~110 from your original version. I actually prefer that pace as it's easier to see the evolution between generations.

Did have to lower my screen res to scale it to full screen however.
110 to 18 fps by just using 2 state colors, or you mean by doing that AND running fullscreen? If it is the former that sounds like a huge slowdown, I would then guess you are still running with cmdgfx.exe instead of cmdgfx_gdi.exe? Cmdgfx_gdi is much faster when not using monochrome colors.

To limit FPS, it's a good idea to use the 'W' flag (in the first call to cmdgfx). The number following W is the minimum number of ms that cmdgfx waits per frame. In other words, if we set e.g. W100, then the target FPS is 1000/100=10 fps, W20 is 1000/20=50 fps etc. You can add the 'z' flag as well to be nice to the CPU and let it sleep if not busy.

I'm not sure how you measure Fps, but just wanted to point out that cmdgfx can print it out for you. Just add this as the last line of the echo to cmdgfx: & text e 0 0 [FRAMECOUNT] 2,2 The first number is the number of frames run so far, the second number is the average FPS (wait a few seconds to let it stabilize). To use this in a pixel mode screen with cmdgfx_gdi, add a font number to the end of that line (if not, the "text" will just show up as single pixels).

Post Reply