CPC Wiki Forum Die Amstrad-CPC-Community

Conway’s Game of Life Challenge: Hat jemand Lust mitzuspielen?

Begonnen von Eratosthenes · 13.01.2026 09:32 · Antworten: 30 · Aufrufe: 201
#21
Super... Danke!

Ich habe das Repo etwas erweitert und werde [member=5]TFM[/member]s Version die Tage einfügen.

Falls Du möchtest, lege ich Deine Version dort auch mit ab.

[member=5]TFM[/member] : Leider ist dieser Thread nur für eingeloggte Mitglieder sichtbar. Kannst Du ihn nicht irgendwo ablegen, wo er zu sehen ist?

Dann kann ich die Referenz hier hin ins README packen...
👍 0👎 0
#22
— Eratosthenes
Super... Danke!

Ich habe das Repo etwas erweitert und werde [member=5]TFM[/member]s Version die Tage einfügen.

Falls Du möchtest, lege ich Deine Version dort auch mit ab.

[member=5]TFM[/member] : Leider ist dieser Thread nur für eingeloggte Mitglieder sichtbar. Kannst Du ihn nicht irgendwo ablegen, wo er zu sehen ist?

Dann kann ich die Referenz hier hin ins README packen...


Da müsstest Du bitte den Markus fragen, der ist Chef hier.

Ansonsten kann ich gerne jedermann hier registrieren, einfach Email an mich (man findet mich via www.FutureOS.de bei Kontakten)

Mitglieder können mir auch via PM die Email Adressen von Freunden schicken, die ich dann auch gerne eintragen kann.
TFM of FutureSoft
http://www.FutureOS.de --> Das Betriebssystem FutureOS (Update: 30.05.2026)
http://futureos.cpc-live.com/files/LambdaSpeak_RSX_by_TFM.zip --> RSX ROM für LambdaSpeak (Update: 29.01.2025)
👍 0👎 0
#23
... das gleiche Program im Model III Modus auf dem Model 4 (Z80A, 4 MHz):

https://youtu.be/aNDbH-e_P_c

Ach so - das ist immer noch nicht in der 4 MHz-Geschwindigkeit... da muss ich noch mal ran.
Zuletzt bearbeitet von LambdaMikel am 23.01.2026 05:20
👍 0👎 0
#24
— LambdaMikel
... das gleiche Program im Model III Modus auf dem Model 4 (Z80A, 4 MHz):

https://youtu.be/aNDbH-e_P_c

Ach so - das ist immer noch nicht in der 4 MHz-Geschwindigkeit... da muss ich noch mal ran.


Sieht super aus!

Auch Prodatron hat eine Alternative abgeliefert, die ohne Zweitregister auskommt, auch eine sehr schlaue Herangehensweise. Läuft in etwa so schnell wie mein Programm.
TFM of FutureSoft
http://www.FutureOS.de --> Das Betriebssystem FutureOS (Update: 30.05.2026)
http://futureos.cpc-live.com/files/LambdaSpeak_RSX_by_TFM.zip --> RSX ROM für LambdaSpeak (Update: 29.01.2025)
👍 0👎 0
#25
Wenn man das Game of Life startet, dann ist es nach den ersten paar Zyklen so, daß die Mehrheit der Zell-Positionen pro Zyklus nicht geändert werden.

Schaut man da wie Prodatron und ich das gelöst haben (siehe angehängte Datei), dann zeigt sich folgendes:

PDT's Algorithmus benötigt pro Zelle: 51 us

PDT's Algorithmus benötigt pro Zelle: 48 us

Beide Algorithmen haben ihren Reiz 🙂
📎 PDT_TFM_Comparison.txt
TFM of FutureSoft
http://www.FutureOS.de --> Das Betriebssystem FutureOS (Update: 30.05.2026)
http://futureos.cpc-live.com/files/LambdaSpeak_RSX_by_TFM.zip --> RSX ROM für LambdaSpeak (Update: 29.01.2025)
👍 0👎 0
#26
Hier ist noch mal das Model 4-Monster - geht ab:

www.youtube.com/shorts/RzlfEHB0Yls?feature=share

M3 = Model III Modus, nur 1.7 MHz.
M4 = Model 4 Modus, 80x24, und 4 MHz.
M4M3 = Model 4 mit schneller CPU, aber zum Vergleich zum Model III = nur 64x16.
Zuletzt bearbeitet von LambdaMikel am 23.01.2026 17:55
👍 0👎 0
#27
Habe nicht viel Zeit investiert (3o Minuten) - nur angepasst. Anbei.
📎 game_of_life_m3.asm
📎 game_of_life_m4.asm
📎 game_of_life_m4m3.asm
👍 0👎 0
#28
Original ist von hier:
https://github.com/bchiha/Ready-Z80/blob/main/03-Game_of_life/game_of_life.z80
👍 0👎 0
#29
OK Ihr Lieben, jetzt habe ich mal meinen Kumpel George Phillips angefixt dass er uns mal zeigt wie man das macht.

Jetzt können wir leider einpacken - so gut ist keiner von uns
🙂)  😎  ;D 

WTF, how is this even possible... pure magic and witch craft:

https://www.youtube.com/watch?v=hyLK1FfeYeg

Da haben [member=5]TFM[/member] und [member=138]prodatron[/member] wohl doch Ihren Meister gefunden 😉

https://www.youtube.com/watch?v=DxpGjBk3iSk

http://48k.ca/

Mehr Details auf dem TRS-80 Discord.

George Phillips ist wahrscheinlich der beste Z80-Programmierer, den die Welt momentan hat... dann kommt Ihr beiden ([member=138]prodatron[/member] und [member=5]TFM[/member]).
Zuletzt bearbeitet von LambdaMikel am 28.01.2026 16:18
👍 0👎 0
#30
George hat es so erklärt - ich verstehe nur wenig davon :rofl muss ich mir genauer ansehen.


It uses what you might call return-oriented programming.

It operates in two passes.  The first pass, the census, goes over each cell and if the cell is live it increments the population of all its neighbours.  That increment happens on a second array I call "nature" because it makes the life and death decisions.

In the second pass, nature, it knows if a cell was alive or dead and how many neighbours it has.  If a cell was dead and has no neighbours it does nothing.  If a cell was dead and is now alive then it draws it on screen and makes a note in the census.  If a cell was alive and should be dead it erases it on sceen and updates the census.  And, in any case, if the cell has any neighbours it sets the count back to zero.  So when we're done we're all cleared out and ready for the next pass.
Major advantages here is that it does nothing with dead cells in the census pass and mostly nothing with unchanged cells in the nature pass.
The census is just 1024 addresses.  To process it, you set the stack pointer to it and do a RET.  This will go through all 1024 cells.  For dead cells the address points to a RET.  Which means do nothing and keep stepping through the census.
For live cells the address jumps to a subroutine which increments the population count for all the neighbours in the nature array.
The nature array is similar but instead of 2 states it has 18.  That accounts for a cell having 0 .. 8 live neighbours and it being currently alive or dead.  Initially it is cleared out to all $C000 or something like that.  Bit 12 is set if the cell is alive.  So that's $E000  and bits 8 through 11 are the number of live neighbours.  So $C300 would be a dead cell with 3 neighbours.  That means the $C300 is a subroutine that will draw a cell on the screen and put the live cell marker into the census.
G Phillips
—
1/25/26, 12:08 AM
When it has done that it sets itself to $E000 indicating that the cell is alive and has no neighbours (i.e., ready for the census pass).
What about, say, $E300.  Well, that's just like $C300 except that the cell remains alive.  So it does what $C300 does but doesn't draw a on the screen and doesn't update the census since nothing has changed in either place.
Since the $Cx and $Ex are the high byte of an address, all the census needs to do is increment the high byte of the right addresses in the nature array.
The census array in the program has some complications.  It has special handlers for the corners and edges so they don't try to increment neighbours which don't exist.
And both arrays use 256 bytes for an entire row.  Was more than the 128 (or 130) they need but it means it is easy to compute the next or previous row address. If HL points to a cell then INC H will go to the next row and DEC H to to the previous.
This also means that at the end of each row there are special addresses to skip the stack pointer to the start of the next row.  Or, at the very end, it just goes to a routine that moves to the next phase.


Hier ist sein code für splife; die schnelleren Versionen sind noch abgefahrener, das spare ich mir hier:


;
; splife.z - abuse the stack to do Conway's Game of Life quickly.
;
; Starts with a random pattern.  Key functions:
;	R		load a new random pattern
;	G		load a glider gun
;	space-bar	pause while held
;	break		return to TRS-DOS
;
; TODO:
;	Torus geometry option.
;	Measurements.
;	Population count option.
;	Generation count option.
;	64 x 48 resolution
;	Self-generate code instead of leaning on zmac macros.
;
; Quick measurement:
;	zmac splife.z
;	trs80gp -m3 -fa do_census zout\splife.bds

	org	$6000
start:	di
	in	a,($ff)
	and	~$20		; turn off video waits
	out	($ec),a	
	ld	(stack),sp

new_rand:
	call	clear
	call	randfill

	jp	do_nature

stack:	defw	0

; HL = pattern
load_pattern:
	ld	de,nature+1
	ld	a,e
	add	(hl)
	add	(hl)
	inc	hl
	ld	e,a
	ld	a,d
	add	(hl)
	inc	hl
	ld	d,a

	ld	c,e
llp:	ld	a,(hl)
	inc	hl
	cp	'$'
	ret	z
	or	a
	jr	z,newline
	cp	'#'
	jr	nz,nocell
	ld	a,high(ntdead)+$3
	ld	(de),a
nocell:	inc	e
	inc	e
	jr	llp

newline:
	ld	e,c
	inc	d
	jr	llp

clear:	ld	hl,$3c00
	ld	de,$3c00+1
	ld	bc,1024-1
	ld	(hl),128
	ldir

	ld	hl,census
	ld	c,16
clrow:	ld	b,64
clcol:	res	1,(hl)
	inc	l
	inc	l
	djnz	clcol
	inc	h
	ld	l,0
	dec	c
	jr	nz,clrow

	ret

glider_gun:
	defb	1,0
	ascii	'........................#',0
	ascii	'......................#.#',0
	ascii	'............##......##............##',0
	ascii	'...........#...#....##............##',0
	ascii	'##........#.....#...##',0
	ascii	'##........#...#.##....#.#',0
	ascii	'..........#.....#.......#',0
	ascii	'...........#...#',0
	ascii	'............##'
	ascii	'$'

next_nature_row:
	ld	hl,256-(64+1)*2
	add	hl,sp
	ld	sp,hl
	ret

end_nature:
	ld	a,($38ff)
	or	a
	jr	z,do_census	; no keys to check

	ld	sp,(stack)

	ld	hl,$0404	; 'R'
	call	key
	jp	c,new_rand

	ld	hl,$0440	; break
	call	key
	ret	c

	ld	hl,$8040	; space
	call	key		; pause while held

	ld	hl,$8001	; 'G'
	call	key
	jr	nc,do_census

	call	clear
 	ld	hl,glider_gun
	call	load_pattern
	jp	do_nature

key:	ld	a,h
	ld	b,h
	ld	h,$38
	and	(hl)
	ret	z
wtkey:	ld	a,b
	and	(hl)
	jr	nz,wtkey
	ccf
	ret

; map from census to nature
c2n	macro	dx,dy
	c2n_off = nature-census-2+256*dy+2*dx+1
	endm

do_census:
	ld	sp,census
	c2n	-1,-1
	ld	de,c2n_off	; mapping from census to nature
	ld	bc,256-64*2	; skip to next row of census
	ret

; Handlers for census bytes.  There are a pair of handlers dead/alive
; cells for each position on the grid.  The pair must start on an four
; byte boundary so clearing bit 1 will kill it and setting bit 1 will
; bring it alive.  The corners, sides and interior all have their own
; handler because of their different neighbourhoods.
;
;    top_left			     top			     top_right
;	+--------------------------------------------------------------+
;	|							       |
;	|							       |
;  left |			    middle			       | right
;	|							       |
;	|							       |
;	+--------------------------------------------------------------+
; bottom_left			    bottom			  bottom_right
;
; Right right-hand side handlers must also bring the stack to the next line.
; And the bottom-right finishes the census pass.
;
; The macro generating each grid case uses these numbers as the positions
; around a cell:
;
;	0  1  2
;	7 xxx 3
;	6  5  4

count	macro	name,pos,len,special
	local	special_op
	org	($+3)/4*4

name:
; cell dead
	if	special
		jr	special_op	; TODO - small speedup by wasting space
	else
		ret
		nop
	endif
; cell alive
	ldhloff	pos
	add	hl,sp

	inc	(hl)
	npos = pos
	rept	len-1
		if	npos == 0 || npos == 1
		inc	l
		inc	l
		endif

		if	npos == 2 || npos == 3
		inc	h
		endif

		if	npos == 4 || npos == 5
		dec	l
		dec	l
		endif

		if	npos == 6 || npos == 7
		dec	h
		endif

		inc	(hl)

		npos++
		npos %= 8
	endm
special_op:
	endm

ldhloff	macro	pos
	if	pos == 0
	ld	h,d		; offset for middle case is pre-loaded
	ld	l,e
	endif

	if	pos == 1	; .#.
	c2n	0,-1		; .+.
	endif			; ...

	if	pos == 2	; ..#
	c2n	1,-1		; .+.
	endif			; ...

	if	pos == 3	; ...
	c2n	1,0		; .+#
	endif			; ...

	if	pos == 4	; ...
	c2n	1,1		; .+.
	endif			; ..#

	if	pos == 5	; ...
	c2n	0,1		; .+.
	endif			; .#.

	if	pos == 6	; ...
	c2n	-1,1		; .+.
	endif			; #..

	if	pos == 7	; ...
	c2n	-1,0		; #+.
	endif			; ...

	if	pos > 0
	ld	hl,c2n_off
	endif

	endm

census_next_row macro
	ld	h,b
	ld	l,c
	add	hl,sp
	ld	sp,hl
	endm

	count	top_left,3,3,0
	ret

	count	top,3,5,0
	ret

	count	top_right,5,3,1
	census_next_row
	ret

	count	left,1,5,0
	ret

	count	middle,0,8,0
	ret

	count	right,5,5,1
	census_next_row
	ret

	count	bottom_left,1,3,0
	ret

	count	bottom,7,5,0
	ret

	count	bottom_right,7,3,1
do_nature:
	ld	sp,nature
	ld	de,ntdead
	ld	bc,ntlive
	ret

census_row macro c_left,c_middle,c_right
	defw	c_left
	rept	64-2
	defw	c_middle
	endm
	defw	c_right
	org	($+255) & 0xff00
	endm

	org	$8000		; start of census processor
census:
	census_row	top_left,top,top_right
	rept	16-2
	census_row	left,middle,right
	endm
	census_row	bottom_left,bottom,bottom_right

	org	$9200		; start of nature processor
nature:
	rept	16
		base = $
		rept	64
			defw	ntdead
		endm
		defw	next_nature_row
		org	base+256
	endm
	base = $
	defw	end_nature

	org	base+256	; safety zone (unnecessary?)

screen:
	scr_addr = $3c00
	rept	16
		base = $
		rept	64
			defw	scr_addr
			scr_addr++
		endm
		org	base+256
	endm

change	macro	is_alive
	if	is_alive
		do_change bc,$bf,set
	else
		do_change de,$80,res
	endif
	endm

do_change macro	tmp,ch,setres
	push	tmp

	; update screen
	ld	hl,screen-nature
	add	hl,sp
	ld	a,(hl)
	inc	hl
	ld	h,(hl)
	ld	l,a
	ld	(hl),ch

	; update census
	ld	hl,census-nature
	add	hl,sp
	setres	1,(hl)

	pop	tmp

	endm

	org	$c000		; start of nature handlers
ntdead:	
	st = 0
	rept	18		; 0 .. 8 for dead, 9 .. 17 for live
		if st == 9
			ntlive:
		endif
		neigh = st % 9
		live = st / 9

		new_live = neigh == 3 || (live && neigh == 2)

		if new_live != live
			change new_live
		else
			if neigh > 0
				if new_live
					push	bc
					pop	bc
				else
					push	de
					pop	de
				endif
			endif
		endif

		ret

		st++
		org	($+255) & $ff00
	endm

; Borrowed and tweaked from game_of_life_m3.asm
randfill: 
	ld	hl,nature+1
	ld	c,16
r0:	ld	b,64
r1:	push	hl
	call	xrnd
	rr	l
	pop	hl
	jr	nc,not_live
	ld	(hl),high(ntdead)+3
not_live:
	inc	l
	inc	l
	djnz	r1
	inc	h
	ld	l,low(nature+1)
	dec	c
	jr	nz,r0
	ret

; 16-bit xorshift pseudorandom number generator by John Metcalf
; 20 bytes, 86 cycles (excluding ret)

; returns   hl = pseudorandom number
; corrupts   a

; generates 16-bit pseudorandom numbers with a period of 65535
; using the xorshift method:

; hl ^= hl << 7
; hl ^= hl >> 9
; hl ^= hl << 8

; some alternative shift triplets which also perform well are:
; 6, 7, 13; 7, 9, 13; 9, 7, 13.

xrnd:
	ld	hl,1
	org	$-2
	defw	1	; seed must not be 0

	ld	a,h
	rra
	ld	a,l
	rra
	xor	h
	ld	h,a
	ld	a,l
	rra
	ld	a,h
	rra
	xor	l
	ld	l,a
	xor	h
	ld	h,a

	ld	(xrnd+1),hl

	ret

	end	start

👍 0👎 0
#31
Lol... jetzt sagt keiner mehr was, war schon klar  ;D
👍 0👎 0

Anmelden, um zu schreiben