Neuron Core Wars code…

Written by

in

So you want to see some crazy shit…?

This is a neuron-like corewars set of code I wrote back in 2006…

From the readme.txt:

This is a collection of "neuron-like" corewars code.
Whilst the signals being passed (at least up to v4) are basically pre-programmed actions,
one could alter this and instead use the neurons to process some input,  
like a neural network.


Although there is no real advantage in corewars to do so.

Perhaps there is a better way to create "artificial intelligence" in corewars.

--

Another way, is using an "evolver" (see simple-evolver) where it takes code from 
a random location and tries to store and execute it. If it fails, the code is discarded.

--

Another way, is the typical parasite method, which is by forking into random code. But
this can be easily defeated by the forkbomb.cw code. 

--

The only other way i can think of is, some piece of code like this, with lots of jumps...

addr:		data 0		# this gets randomised (within the boundaries of the code)
r:		data 0		# just a var

line1: 		some instruction here, e.g. move 0, [r]
		some instruction here to alter addr, e.g. add 1, addr
		jump [addr]

line2: 		some instruction here
		some instruction here to alter addr
		jump [addr]

line3: 		some instruction here
		...
		...
...

and so on. 

It then boils down to the "altering addr" method, which can be stored into a table or 
based on "learning" or "statistics" -- thus improving its code over time, without
actually changing anything (except the order it's run). 

E.g. you could use an sbox to hold all the addrs of code. Each "jump instruction"
jumps into a function which then permutates the sbox and returns a new addr accordingly.

The permutation function can then be optimised for a particular scenario.

This is similar to "neuron" with the signal handling/actions. 

--

Here’s version 4 of the code:

# NB: This is complete and working, but there is a race condition with "sendsignal()"
# and "clone()" which can prevent signals being sent to newly moved neurons (moveneuron)
# because sendsignal() may be polling on the old location and doesn't know that it has moved.
# 
# The only way to fix this is to implement a communications protocol similar to
# process_protect and process_module.
#
# But this code has already reached the limit of 200 instructions, so there is no way
# to fix this problem.
# 
# 

title "AAA - Neuron v4"
author "---------- DS -----------"

bootthreads: 	data 1				# initial case: minimum 1 more threads
						# after "first neuron" and its clone...
						# i.e. total = 3 threads

						# self-signal to create initial neuron

boot:		move 2, signalstatus
		move 1, signal
		add 256, signal
# 		move &bootstrap, jumpback
 		move &bootstrap2, jumpback
		jump checksignal


#bootstrap:	move 1, sendsignaldata			# issue "create neuron" command to last neuron
#		add 256, sendsignaldata
#		move nextneuron, sendsignalneuron	# &neuronmarker can be used instead of nextneuron
#		move &bootstrap1, jumpback		# in non-bootstrap "first neuron" type code.. i.e. with a poll loop
#		jump sendsignal				# otherwise, in bootstrap code, you need to checksignal()
							# to get it to work. 
							# Since we have a cloned neuron already, due to this bootstrap,
							# we just use it instead.

#bootstrap1:	loop bootthreads, bootstrap

bootstrap2:	move 4, sendsignaldata			# issue new command action, for all neurons
		add 256, sendsignaldata
		add 512, sendsignaldata		
		move nextneuron, sendsignalneuron
		move &bootstrap3, jumpback
		jump sendsignal

bootstrap3:	move &bootstrap5, jumpback
		jump delay

bootstrap5:	move 2, sendsignaldata			# move neurons
		add 256, sendsignaldata
		add 512, sendsignaldata
		move nextneuron, sendsignalneuron
		move &bootstrap6, jumpback		
		jump sendsignal

bootstrap6: 	move &bootstrap2, jumpback
		jump delay

delay:		move 1000, i
delay2:		loop i, delay2
		jump [jumpback]
		
# ----------- begin neuron here --------------------------------------

	# signals (sent as bit flags):
	#
	# (action signals) - you'd want one of these (you can combine these):
	# 0 = do nothing. 
	# 1 = create more neurons
	#
	# (action signals with special conditions) - or these (don't combine these):
	# 2 = move this neuron into a new cell
		# since this moves the current neuron into a new place, you can't combine this.
	# 4 = move 0, [random] (safe version, does not kill other neurons of same process) 
		# aborts on receiving another signal
	# 8 = move 0, [random] (unsafe version)
		# aborts on receiving another signal
	# 32 = convert into an imp
		# neuron transforms into an imp
	# 64 = terminates
		# neuron terminates
	#
	# (signal handling signals) - and you'd want one of these (at least one of these):
	# 128 = signal prev neuron with this same signal
	# 256 = signal next neuron with this same signal
	# 512 = do action (even if you're not the last neuron to get this signal)
	#
	# NB: When you combine the flags, they will be handled in numerical order. 
	# Special conditions may prevent further flags from being "actioned".
	# Also, signal propagation occurs FIRST before actions, so you cannot
	# expect "newly created" neurons to receive a signal until the next signal is sent.
	# (e.g. This is the only way to prevent signal 1 creating an infinite number of
	# neurons. Similar problems occur with signal 2.)

# ------------- global variables: the values will not be copied on clone()->linkneuron() -----------------------------------

neuronmarker:		data 1337

# --- public vars ---

signalstatus:		data 0
signal:			data 0

nextneuron:		data 0
prevneuron:		data 0

# --- private vars ---

jumpback:		data 0			# jumpback address for various code
jumpbackclone:		data 0			# jumpback address for clone code (only)
recvsignal:		data 0			# received signal's value
sendsignaldata:		data 0			# signal value to be sent
sendsignalneuron:	data 0			# target address of neuron for sendsignal

s:			data 0			# generic vars -- may be clobbered when entering a new function call.
r:			data 0
i:			data 0

# --- constants ---

sizeofvars:		data 12			# size of all variables
entrypointoffset:	data 15			# entry point offset from &neuronmarker

# ------------ main polling loop --------------------------------
# Clobbered: jumpback

main:		jump main2
#		data 0				# if your entrypointoffset is incorrect, it will terminate
#		data 0				# as soon as it forks into the wrong offset. 
#		data 0				# (this is only useful for debugging)
#		data 0				# but this exceeds the "max limit" of 200 instructions.
#		data 0				# Uncomment as many as you want.

main2: 		move 0, signalstatus		# clear signalstatus

poll:		move &poll, jumpback
poll1:		equal 2, signalstatus	# poll signalstatus
		jump checksignal
		jump poll1

# ------------- Function: checksignal() ---------------------------------
# Clobbered but restored: jumpback
# Clobbered: signalstatus

checksignalret:	data 0					# caller's return address

checksignal:	move jumpback, checksignalret		# save caller's return address
		move signal, recvsignal			# save it into recv signal, and reset signalstatus

checksignal0:	equal 0, recvsignal			# do nothing?
		jump checksignalend

	# similar to doaction()'s switch-case, but optimised for speed (not code size).
	# i.e. does not do multiple evaluations but requires more opcodes for control.

checksignal128:	move 128, i				# check 'signal prev' flag
		and recvsignal, i
		equal 128, i
		jump sendsignal128
		jump checksignal256			# ->--+
							#     |
							#     |
sendsignal128: 	equal prevneuron, 0			# <= if we're the last neuron in this signal chain
		jump checksignaldo			# <= do the action.
		move recvsignal, sendsignaldata		#     |
		move prevneuron, sendsignalneuron	#     |
		move &checksignal256, jumpback		# ->--|--+
		jump sendsignal				#     |  |
							#     |  |
							#     |  |
checksignal256:	move 256, i				# <---+--+
		and recvsignal, i
		equal 256, i
		jump sendsignal256
		jump checksignal512			# ->--+
							#     |
sendsignal256: 	equal nextneuron, 0			# <= if we're the last neuron in this signal chain
		jump checksignaldo			# <= do the action.
		move recvsignal, sendsignaldata		#     |
		move nextneuron, sendsignalneuron	#     |
		move &checksignal512, jumpback		# ->--|--+
		jump sendsignal				#     |  |
							#     |  |
							#     |  |
checksignal512:	move 512, i				# <---+--+
		and recvsignal, i
		equal 512, i
		jump checksignaldo			# 512 -- do the action anyway.
		jump checksignalend

checksignaldo:	move &checksignalend2, jumpback
		jump doaction				# on return, doaction is expected to clear (or set) signalstatus

checksignalend: move 0, signalstatus			# reset signalstatus -- we only passed a signal
checksignalend2:	move checksignalret, jumpback		# return to caller. 
		jump [jumpback]


# ----------- Function: doaction() --------------------
# Clobbered but restored: jumpback
# Clobbered: recvsignal, signalstatus (by action 4 and 8)

doactionret:	data 0					# caller's return address

	# think of the following code like a switch-case statement that operates on a bitflag
	# it is optimised for code size, not speed. (it saves 1 or 2 jump instructions on each case in the switch-case)
	# at the expense of re-evaluating the entire switch-case from the beginning.

doaction:	move jumpback, doactionret
		and 127, recvsignal			# mask only the bits we want...

doactionstart:	move &doactionstart, jumpback		# set this as the jumpback value
							# because "doaction" will change "recvsignal"
							# by substracting from recvsignal the action being done.
							# (this saves some code -- but you cannot do this for "signal handling actions"

		equal 0, recvsignal			# are we done yet?
		jump doactiondone

		move 1, i			
		and recvsignal, i
		equal 1, i
		jump doaction1

		move 2, i
		and recvsignal, i
		equal 2, i
		jump doaction2

		move 4, i
		and recvsignal, i
		equal 4, i
		jump doaction4

		move 8, i
		and recvsignal, i
		equal 8, i
		jump doaction8

		move 32, i
		and recvsignal, i
		equal 32, i
		jump doaction32

		move 64, i
		and recvsignal, i
		equal 64, i
		jump doaction64

		data 0					# buggy code? undefined action signal

	# -- NB these subroutines of doaction() will return to doaction()'s switch-case statement 
	# via "jump [jumpback]" and not "return to caller" since that only occurs once (see above).

# --- Action 1: Create more neurons

doaction1:	add -1, recvsignal			# substract from recvsignal, so this action isn't repeated again.
		move &linkneurons, jumpbackclone	# make clone() fork and link the neurons
		jump clone				# jumpback is already set by doaction()

# --- Action 2: Move neuron.

doaction2:	add -2, recvsignal
		move &moveneuron, jumpbackclone		# no need to set jumpback
		jump clone

# --- Action 4: move 0, [random] (safe version)

doaction4:	move 0, signalstatus			# reset signalstatus here.
doaction4p1: 	equal 2, signalstatus			# check signalstatus -- if this changes, we stop action4
		jump stopaction4
doaction4p2:	info 2, r
		own [r]
		jump doaction4p2
		move 0, [r]
		jump doaction4p1
stopaction4:	jump doactiondone2			# return, but don't reset signalstatus

# --- Action 8: move 0, [random] (unsafe, faster)

doaction8:	move 0, signalstatus			# reset signalstatus here.
doaction8p1: 	equal 2, signalstatus			# check signalstatus -- if this changes, we stop action8
		jump stopaction8
		info 2, r
		move 0, [r]
		jump doaction8p1
stopaction8:	jump doactiondone2			# return, but don't reset signalstatus

# --- Action 32: Convert into an imp

doaction32:	move doaction32, imp			# no need for jump [jumpback]
imp:		data 0

# --- Action 64: Terminate neuron
 
doaction64:	data 0					# no need for jump [jumpback]

# --- Done?

doactiondone:	move 0, signalstatus			# reset signalstatus.
doactiondone2:	move doactionret, jumpback
		jump [jumpback]				# return to caller

# ----------- Function: sendsignal() -------------------------------------------
# Clobbered: None.

sendsignal:	equal 1337, [sendsignalneuron]		# check target is a valid neuron by testing beginmarker
		jump sendsignal2

		data 0					# buggy code? terminate

sendsignal2:	move sendsignalneuron, r
		add 1, r				# set r to point to "signalstatus"

sendsignal3:	equal 0, [r]				# simplified version.
		jump sendsignal4
		jump sendsignal3	

sendsignal4:	move 1, [r]				# lock signalstatus
		move r, i
		add 1, i				# set i to point to "signal"
		move sendsignaldata, [i]		# send signal
		move 2, [r]				# update signalstatus to completed

sendsignalend:	jump [jumpback]				# return to caller.

# ----------- Function: clone() ------------------------------------------
# Clobbered: None. (Also see polymorphic functions.)

# NB: "jumpbackclone" is a polymorphic function descriptor (i.e. function pointer or subclass method)
# which changes the behaviour of the clone code

clone:		info 2, r
		own [r]					# check if 'r' overwrites into another neuron.
		jump clone

		move &neuronmarker, s			# grab length of neuron (i could hardcode this, but i dont want to)
		neg s, i
		add &endvar, i
		add 1, i
		move r, endvar				# save 'r'

		add i, r				# check if 'r + len' overwrites into another neuron.
		own [r]
		jump clone

		move endvar, r				# restore 'r'

copy:		movei s, r
		loop i, copy

		move 1337, [endvar]			# set neuron marker

		jump [jumpbackclone]			# jump to the polymorphic function -- either moveneuron() or linkneurons()

# --- Polymorphic jumpbackclone function: linkneurons() ---
# Clobbered: nextneuron

linkneurons:	equal 0, nextneuron
		jump linkneurons1

		data 0					# buggy code? terminate

linkneurons1:	move endvar, r				# zero the bss
		add 1, r				# (1st var location)
		move sizeofvars, i			# (number of vars)
zerobss:	move 0, [r]				
		add 1, r
		loop i, zerobss

		move endvar, nextneuron			# set our "nextneuron" to point to the clone

		move endvar, r
		add 4, r
		move &neuronmarker, [r]			# set "prevneuron" in the cloned neuron to us.

		add entrypointoffset, endvar		# readjust endvar to entry point of cloned neuron
		fork [endvar]				# fork

		jump [jumpback]				# return to caller

# --- Polymorphic jumpbackclone function: moveneuron() ---

moveneuron:	move prevneuron, r
		add 3, r
		move endvar, [r]			# set previous neuron's "next neuron" to new address

		move nextneuron, r
		add 4, r
		move endvar, [r]			# set next neuron's "prev neuron" to new address

		add entrypointoffset, endvar		# this is just a clone_move() operation, so re-adjust entrypoint
		jump [endvar]				# and only jump (don't fork)

# ------------------------------------------------------------------------------------

endvar:			data 0			# end variable -- used to mark end of neuron -- only to be used by clone().

	# data/code below is NOT used -- just to look nice in the gui -- only first neuron has this.

neuronendmarker:	move &neuronendmarker, neuronendmarker1		
neuronendmarker1:	data 0						

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *