Yet Another BASIC Evolver

Here we go again, it's been a few years since playing with this stuff, but recently I found a neat programming language called yabasic, for "yet another BASIC". I've been using it for simple scripts but got the itch again and learned the language better and used it to make yet another redcode evolving program... YabEvolver. This one is similar to most of my previous evolvers - a two dimensional soup where randomly selected warriors battle an immediately surrounding warrior with the loser being replaced with an evolved copy of the winner.

Here's an animated gif showing the early moments of a run...


The numbers show the number of instructions in each warrior, the colors represent the warrior's "species" which is a random number which is changed whenever a mutation can potentially change the base instruction sequence. The warriors start out random, optionally the minimum length and the initial instruction sequence can be specified, in this run the warriors start out with random instructions and length. Very quickly natural selection kicks in and favors the stronger warriors which multiply and form islands of similar warriors within the soup. Within these islands the warriors strengthen by competing against their own kind, at the borders between islands the warriors battle dissimilar warriors and learn new skills. Usually one particular sequence will mostly or completely take over but the species will keep changing as a new random species number is selected whenever an operation occurs that might result in a sequence change, regardless if the sequence actually changed. Besides helping to keep the display interesting, it highlights the warrior forms with more successful strategies as they multiply more.

After a bit the length 5 warriors have almost completely taken over except for a small patch of 4's...


The user interface permits cursoring around the soup then pressing L or B to list or benchmark warriors...

 

Pressing V selects alternate soup views that use characters to represent warrior species and instruction sequence...

 

When mapping the instruction sequence to a limited set of characters collisions are inevitable so the program has two different sequence methods, the one above is mod 52 to map instruction sequences to A-Z and a-z, the next view mode is mod 94 to map the sequences to ascii 33-126. The above screenshots are using 16-color ANSI to color the species, the program also supports using 256 color ANSI and 24-bit RGB ANSI.

Pressing numpad keys 1-9 battles the warrior against a surrounding warrior in pmarsv(sdl), pressing 0 battles the warrior against a randomly selected warrior from the test set...




67_03 beat Free the Fish! This time.. the score in this case is from 10 rounds randomly positioned.

In addition to on-demand benchmarking, the evolver periodically benchmarks a randomly selected soup warrior and saves it to a separate directory if it scores above a specified threshold and within a specified percentage of the top score. Pressing O brings up an "other" menu with options to change the benchmark set, change parameters or load another INI file (ignores any settings such as soup/top directories and size parameters that would invalidate the current run), show soup instruction sequences and population numbers, and show the sequences along with a full benchmark of the soup...

 

This program is for Linux-type system, it uses sh/nix-type shell commands ("[" ls echo mkdir read chmod etc), assumes /dev/shm is a user-writable ram disk for temp files, and outputs lots of ANSI screen codes. Requires yabasic, pmars and pmarsv, links in the source code. Yabasic is in the Ubuntu repositories, here's my patched pmars092N3 source/binaries compiled for 64-bit Linux. Here's my slightly modified pmars-0.9.2-5sdlN source and 64-bit Linux binaries for the pmarsv SDL port (added a delay to slow it down), requires the SDL 1.2 libraries. For newer Ubuntu etc look for the libsdl1.2debian package. For nano warriors the old pmarsv looks about right with view options "-v 564", for my patched SDL version I use view options "-v 784".

Here's the latest YabEvolver program code...

#!/usr/bin/env yabasic
REM YabEvolver - Evolves Redcode Warriors for Core War
REM usage: yabevolver [inifile]
REM Requires pmars/pmarsv and yabasic...
REM yabasic githib: https://github.com/marcIhm/yabasic (or repository)
REM Various versions of pmars/v are here: https://corewar.co.uk/pmars.htm
REM Recommend version 0.9.2 or 0.9.2-5 (SDL). By convention the nongraphical
REM pmars is named "pmars" and the graphical version is named "pmarsv",
REM both are compiled from the same source code. The SDL version of pmarsv
REM uses different view options so I name it "pmarsvsdl" to avoid conflict.
REM Note - the pmars package in Ubuntu etc repositories is really pmarsv
REM and won't work for evolving.
REM This program uses ANSI codes and Unix/Linux-specific commands paths etc
REM Assumes sh syntax for system commmand, embedded inkey.sh script uses bash
REM Must run in a terminal large enough for soup display (xdim+2 by ydim+3)
REM ANSI soup dump player Fast/Slow feature requires awk (scripted FP math) 
REM For best results tempdir$ should be on a ram disk (default /dev/shm).
REM DO NOT set tempdir$ to a solid state drive! This program really messes
REM with automatic file indexers (like baloo) or anything that monitors or
REM intercepts disk writes. Changes/License/Disclaimer after program code.

e=system("[ -t 1 ]"):if e end rem exit if not in terminal or OS not supported
print "*** YabEvolver *** Version 230303-08"

REM --- yabevolver ini file ------------------------------------------------
REM copy this section to a file and specify filename on command line to load
header$   = ";redcode-nano"        rem first line of program (start with ;)
author$   = ";author yabevolver"   rem author line (start with ;)
comment$  = ";strategy evolved"    rem comment line (start with ;)
tempdir$  = "/dev/shm/yabevoltmp"  rem temp directory for battling warriors
soupdir$  = "soup"        rem where soup warriors are stored
topdir$   = "top"         rem where top-scoring warriors are saved
benchdir$ = "nano2301"    rem benchmark dir for testing warriors
REM soup/pmars specs
xdim      = 77      rem soup x dimension
ydim      = 20      rem soup y dimension
xwrap     = 0       rem enable wraparound in x dimension
ywrap     = 0       rem enable wraparound in y dimension
length    = 5       rem max warrior length
mincrelen = 1       rem minimum warrior length at creation
coresize  = 80      rem core size
processes = 80      rem max processes
cycles    = 800     rem instruction cycles per match
rounds    = 100     rem matches per battle
REM mutation rates, 0-1
instrate  = 0.015   rem chance of changing an instruction
modrate   = 0.02    rem chance of changing a modifier
addrrate  = 0.03    rem chance of changing an address mode
numrate   = 0.04    rem chance of changing a number
reprate   = 0.005   rem chance of replacing with new random line
insrate   = 0.005   rem chance of inserting a new line (pushing rest down)
duprate   = 0.005   rem chance of duplicating previous or following line
swaprate  = 0.005   rem chance of swapping two lines (only 1 chance per evo)
delrate   = 0.005   rem chance of deleting a line (pulling rest up)
orgrate   = 0.0     rem chance of a random ORG line (ORG starts at 0)
REM mutation options
mutadjust = 0       rem 0-1, reduce mutation rates by up to rate*mutadjust
mutadjgen = 2000    rem by mutadjgenerations. Make either 0 to disable
adjrate   = 0.4     rem if number changed, chance of increment or decrement
localrate = 0.5     rem if new number, chance of being within warrior
org0rate  = 0.5     rem if ORG changed, chance of ORG 0
crossrate = 0.02    rem crossover rate
adjlocref = 1       rem if not 0 adjust local refs when inserting and deleting
REM evaluate and benchmark options
evopseq   = 0       rem evolving pmars sequence 0=random 1=fixed 2=permutate
evalmode  = 3       rem 0=unguided 1=rnd bench 2=all 3=rnd 0/1 4=rnd 0/1/2
evalrate  = 0.5     rem for evalmode 4 and 5, chance of using bench warriors
evalgen   = 200     rem apply evalmode after both reach this many generations
benchfreq = 1000    rem warrior benchmark interval (0 to disable)
savethr   = 135     rem only save if score is >= this
savepc    = 2       rem save if within this percentage of top score
bmrounds  = 142     rem number of rounds when benchmarking
benchpseq = 2       rem benchmark pmars sequence 0=random 1=fixed 2=permutate
reinsfreq = 0       rem warrior reinsertion interval (0 to disable)
REM instructions, modifiers and address modes...
REM instruction$ length must be divisible by 4 (start with space)
REM modifiers$ length must be divisible by 3
REM bias encoded using duplicates
REM addr1mode$ and addr2mode$ separated as they have different mixes
instructions$ = " MOV MOV MOV MOV MOV MOV MOV MOV MOV MOV SPL SPL"
instructions$ = instructions$+" SPL SPL SPL SPL DJN DJN DJN DJN DJN"
instructions$ = instructions$+" ADD SUB MOD MUL DIV JMP JMZ JMN SEQ SNE"
modifiers$ = ".I .I .I .I .I .I .I .I .B .B .B .B .F .F .F .X .A .AB.BA"
addr1modes$ = "######$$$$$>>>><<<{{}}*@"
addr2modes$ = "<<<<<<{{{{{>>>>$$$$}}}@#"
initialinstr$ = " SPL MOV MOV MOV DJN"  rem initial warrior instructions
enablepreseed = 0 rem if not zero use initialinstr$ to pre-seed the soup
REM other options
ssfreq   = 1000000 rem automatic soup save interval, 0 to disable
superror = 1       rem if not 0 redirect pmars stderr to dev/null
unibox   = 1    rem enable unicode chars for box surrounding soup display
viewmode = 0    rem view mode 0=length 1=species 2=sequence52 3=sequence94 
viewshift = 0   rem raw display sequence hash xord by this to help avoid dups
ansimode = 256  rem ANSI mode 16=16colors 256=256colors 24=RGB else no color
enableC  = 1    rem if not 0 allow color scheme cycling by pressing C
REM binaries and CL options
lister$ = "less"       rem [path/]name of file lister program
listeropts$ = "-~"     rem lister opts for warriors.. no ~ at end
listeroptsb$ = "-~ +G" rem lister opts for benchmarks.. no ~, go to eof
pmars$  = "pmars"      rem [path/]name of pmars binary
pmarsv$ = "pmarsv"     rem [path/]name of pmarsv binary
pmarsvopts$ = "-v 564" rem pmarsv options (-v digits speed dispmode detail)
rem pmarsv$ = "./bigpmarsv.sh" rem for my pmarsv magnifying script
rem pmarsv$ = "pmarsvsdl"  rem for the SDL version of pmarsv
rem pmarsvopts$ = "-v 784" rem opts for my speed-patched SDL version
pmarsvrounds = 10      rem rounds to run in pmarsv
REM --- end yabevolver ini file --------------------------------------------

REM Warrior file format...
REM
REM ;redcode-nano                  whatever is in header$
REM ;name 1_1.red                  current filename
REM ;author yabevolver             whatever is in author$
REM ;strategy evolved              whatever is in comment$
REM ;origin 1_1                    this maintained throughout evolution
REM ;species 12345                 random string changed if inst seq changes
REM ;generation 0                  increments with each mutated copy
REM ;created 01-26-2023 11-01-49   creation time/date
REM ;assert CORESIZE==80
REM ORG 0
REM SPL.AB $   0,<  60             must be in this format
REM MOV.I  #  64,{  42             coresize<9999, 4 character numbers
REM ...                            (evolver only uses - for small numbers)
REM END

REM load ini file if specified on the command line
args=peek("argument")
if args=1 then fname$=peek$("argument"):loadinifile(fname$)
else print "Using internal defaults"
endif
validateparms() rem make sure parameters are valid

REM dimension arrays
maxbw=1000                  REM maximum number of benchmark warriors
dim soup$(xdim,ydim,length) REM just the warrior code, first empty is end
dim origin$(xdim,ydim)      REM the string after origin
dim species$(xdim,ydim)     REM the string after species
dim tdstamp$(xdim,ydim)     REM creation time/date
dim wlength(xdim,ydim)      REM warrior lengths
dim orgnum(xdim,ydim)       REM warrior ORG numbers
dim gennum(xdim,ydim)       REM warrior generation counts
dim benchwarriors$(maxbw)   REM benchmark warrior names
REM used by the soup analyze functions...
maxseq=2000            REM maximum number of unique sequences
dim seq$(maxseq,2)     REM sequence code character, instruction sequence
dim seqcounts(maxseq)  REM population count per sequence
dim seqtops(maxseq)    REM score of best scoring warrior for this sequence
dim seqnames$(maxseq)  REM name of best scoring warrior for this sequence
dim benchscores(xdim,ydim) REM benchmark scores for each warrior
maxrepsave=10          REM how many categories to save to report

esc$=chr$(27)+"[" rem ANSI escape sequence
stat=ydim+3 rem location of status line
initdirs()  rem initialize directories
initbenchwarlist()  rem initialize bench warrior list

REM yabasic's inkey$ function requires clear screen which
REM prevents casual use of ANSI codes so use a script instead...
REM usage: key$=system$(tempdir$+"/inkey.sh[ -t [seconds]]")
REM if -t specified returns immediately (~1ms), -t 1 for 1 second etc
REM if -t specified if key detected (no timeout) but can't tell what
REM key was pressed (enter space etc) then returns "[extra]"
e=open(2,tempdir$+"/inkey.sh","w")
if e=0 myerror("Can't write inkey script")
print #2 "#!/bin/bash"
print #2 "to=\"\";key=\"\";if [ \"$1\" = \"-t\" ];then"
print #2 "to=\"-t .001\";if [ \"$2\" != \"\" ];then"
print #2 "to=\"-t $2\";fi;fi;read $to -sn 1 key"
print #2 "if [ $? = 0 -a \"$to\" != \"\" ]&&[ \"$key\" = \"\" ];then"
print #2 "echo -n \"[extra]\";else echo -n \"$key\";fi"
close #2
e=system("chmod +x "+tempdir$+"/inkey.sh")
if e myerror("Can't set permissions on inkey script")

print "(press esc within 2 seconds to pause) ";
a$=system$(tempdir$+"/inkey.sh -t 2")
if a$=chr$(27) then
 print:print esc$,"A";:cleartoend()
 input "(press enter to continue) " a$
endif
print

print "Initializing..."
soupchanged=0 rem flag to prevent needless writes
for y=1 to ydim
 for x=1 to xdim
  for i=1 to length:soup$(x,y,i)="":next i
  fname$=soupdir$+"/"+warriorname$(x,y)+".red"
  if (open(1,fname$,"r")) then
   rem file exists, read into arrays
   close #1
   loadwarrior(fname$,x,y)
  else rem file doesn't exist so create it
   origin$(x,y)=warriorname$(x,y)
   species$(x,y)=str$(int(ran(100000)))
   wl=length-int(ran(1)*(length-mincrelen))
   wlength(x,y)=wl
   gennum(x,y)=0
   tdstamp$(x,y)=mid$(date$,3,10)+" "+left$(time$,8)
   for ln=1 to wl
    soup$(x,y,ln)=rndwline$(ln)
    if enablepreseed and ln<=niniti then
     left$(soup$(x,y,ln),3)=mid$(initialinstr$,(ln-1)*4+2,3)
    endif
   next ln
   soupchanged=1  rem soup changed, enable save option
  endif
  normalizenumbers(x,y) rem make sure numbers are consistent
 next x
next y
print

REM init globals counters constants and stuff
w2x=0  rem global vars set by picksurrounding
w2y=0
sc1=0  rem global vars set by battlewarriors
sc2=0
lastscore=0:lastbenchwar$=""  rem for ANSI soup dump
vm1chars$="~!@#$%^&*?.+';" rem warrior chars for viewmode 1
c1=1:c2=1  rem constants used for viewmode 2/3 hash (1,1 for orig)
varsubcount=0  rem used to compile subs for printing variables
anavalid=0  rem global set when benchmark analysis is valid
benchtrigger=0:sstrigger=0:reinstrigger=0 rem init cycle counters
viewshiftsave=viewshift rem save viewshift to toggle back to it
cursoring=0 rem set if cursoring around
showsoup()  rem hides cursor
cwx=1:cwy=1 rem cursor warrior x,y

REM global vars for ANSI dump feature
ansitrigger=0  rem cycle counter for ANSI soup dump
ansifreq=0     rem ANSI dump frequency (if 0 disabled)
ansidir$="ansidumps" rem directory for ANSI dumps (setup can change)
ansifbase$=""  rem base name for dumps (setup will change)
ansifnumber=0  rem sequence number - dump0000.ans dump0001.ans etc
ansifmax=9999  rem max sequence number

REM main loop
exitprogram=0
repeat
 if soupchanged anavalid=0  rem bench no longer valid
 hidecursor():position(stat,1):cleartoend()
 print " [E]Evolve [L]List [B]Bench ";
 if pmarsv$<>"" print "[0-9]Battle ";
 print "[V]View [O]Other ";
 if soupchanged then print "[S]SaveSoup ";
 elseif enableC then print "[C]Colors   ";
                else print "            "; endif
 if cursoring then print " ",warriorname$(cwx,cwy);
 else print "[X]Exit";
 endif
 position(cwy+1,cwx+1):showcursor():cursoring=0
 uikey$=system$(tempdir$+"/inkey.sh") rem wait for a keypress
 curscode$="" rem code after esc sequence
 if uikey$=chr$(27) then
  b$=system$(tempdir$+"/inkey.sh -t")
  if b$="[" curscode$=system$(tempdir$+"/inkey.sh -t"):cursoring=1
 endif
 hidecursor()
 REM Shift-V - randomize seqhashxor to change chars (only viewmode 2+)
 REM Toggles xor mask between random number and viewshift setting
 if uikey$="V" and viewmode >=2 then
  if viewshift=viewshiftsave then viewshift=int(ran(100000))
  else viewshift=viewshiftsave endif
  showsoup()
 endif
 REM menu for other functions
 if uikey$="o" THEN
  showcursor():otherstuff()
  hidecursor():showsoup()
 endif
 REM toggle view
 if uikey$="v" viewmode=mod(viewmode+1,4):showsoup()
 REM if color cycling enabled change ANSI color scheme
 if uikey$="c" and enableC cycleansimode()
 REM cursor control 
 if curscode$="A" and cwy>1    cwy=cwy-1 rem up
 if curscode$="B" and cwy<ydim cwy=cwy+1 rem down
 if curscode$="C" and cwx<xdim cwx=cwx+1 rem right
 if curscode$="D" and cwx>1    cwx=cwx-1 rem left
 REM continue evolving
 if uikey$="e" then
  rem showsoup()
  position(stat,1):cleartoend()
  position(stat,61):print "(esc to stop)";
  continue_evolving():showsoup()
  soupchanged=1
 endif
 REM list a warrior
 if uikey$="l" then
  fname$=tempdir$+"/temp.red"
  writewarrior(fname$,cwx,cwy)
  clearscreen()
  system(lister$+" "+listeropts$+" "+fname$)
  if lister$="cat" input "(press enter to continue)" a$
  showsoup()
 endif
 REM Benchmark a warrior
 if uikey$="b" and nbenchwars>0 then
  position(stat,1):cleartoend()
  print "Generating benchmark report..."
  fname$=tempdir$+"/temp.red"
  writewarrior(fname$,cwx,cwy) rem write warrior to tempdir/temp.red
  makebenchreport(fname$,wname$) rem write report to tempdir/results.txt
  clearscreen()
  system(lister$+" "+listeroptsb$+" "+tempdir$+"/results.txt")
  if lister$="cat" input "(press enter to continue)" a$
  showsoup()
 endif
 REM battle warrior against surrounding warrior in pmarsv
 REM using numpad keys.. if 5 then run just the selected warrior
 REM fun bonus.. if 0 then battle against random bench warrior
 if pmarsv$<>"" and uikey$>="0" and uikey$<="9" then
  bwx=0:bwy=0
  if uikey$="0" and nbenchwars bwx=-1:bwy=-1
  if uikey$="1" and cwx>1    and cwy<ydim bwx=cwx-1:bwy=cwy+1
  if uikey$="2"              and cwy<ydim bwx=cwx  :bwy=cwy+1
  if uikey$="3" and cwx<xdim and cwy<ydim bwx=cwx+1:bwy=cwy+1
  if uikey$="4" and cwx>1                 bwx=cwx-1:bwy=cwy
  if uikey$="5"                           bwx=cwx  :bwy=cwy
  if uikey$="6" and cwx<xdim              bwx=cwx+1:bwy=cwy
  if uikey$="7" and cwx>1    and cwy>1    bwx=cwx-1:bwy=cwy-1
  if uikey$="8"              and cwy>1    bwx=cwx  :bwy=cwy-1
  if uikey$="9" and cwx<xdim and cwy>1    bwx=cwx-1:bwy=cwy+1
  if bwx and bwy then
   cl$=pmarsv$+" "+pmarsvopts$+" -bk -l "+str$(length)+" -d "+str$(length)
   cl$=cl$+" -s "+str$(coresize)+" -p "+str$(processes)+" -c "+str$(cycles)
   cl$=cl$+" -r "+str$(pmarsvrounds)
   w1$=tempdir$+"/a.red" : w2$=tempdir$+"/b.red"
   writewarrior(w1$,cwx,cwy):cl$=cl$+" "+w1$
   opname$="unknown warrior"
   if uikey$<>"5" and uikey$<>"0" then rem if battling another soup war
    writewarrior(w2$,bwx,bwy)     rem write that one to temp too
    opname$=warriorname$(bwx,bwy) rem name for battle text
    cl$=cl$+" "+w2$               rem add filename to command line
   endif
   if uikey$="0" then rem battle against a random bench warrior
    w2$=benchwarriors$(int(ran(nbenchwars))+1) rem select bench warrior
    cl$=cl$+" "+w2$   rem add filename to command line
    e=open(1,w2$,"r"):if e=0 myerror("Can't open bench warrior")
    while not eof(1)
     line input #1 a$:a$=trim$(a$)
     if left$(a$,6)=";name " opname$=left$(trim$(mid$(a$,7)),20)
    wend
    close #1
   endif
   position(stat,2):cleartoend()
   if uikey$="5" then
    print "Running ",warriorname$(cwx,cwy);
    runpmarscommandline(cl$,pmarsvrounds)
   else
    print "Battling ",warriorname$(cwx,cwy)," vs ",opname$;
    runpmarscommandline(cl$,pmarsvrounds)
    if sc1 or sc2 then
     print "  Results: ";
     print left$(str$(sc1)+"    ",5)," ";
     print left$(str$(sc2)+"    ",5)," ";
     print "(press a key) ";
     a$=system$(tempdir$+"/inkey.sh")
    endif
   endif
  endif
  showsoup()
 endif
 REM save soup and/or exit
 if (uikey$="s" or uikey$="x") and soupchanged then
  position(stat,1):cleartoend():print " Saving soup...";
  savesoup():soupchanged=0
 endif
 if uikey$="x" exitprogram=1  rem make x because q quits less
until exitprogram
position(stat,1):showcursor():print
end

REM ----------------- subs ------------------

REM error handling sub.. pause before exiting
sub myerror(a$)
if a$<>"" print a$
input "----- press enter to exit program ----- " a$
end
end sub

REM write benchmark report to tempdir/results.txt
REM uses file handles 1 and 2
sub makebenchreport(fname$,wname$)
local e,bscore,wn,w$,bname$,a$,j
e=open(2,tempdir$+"/results.txt","w")
if e=0 myerror("Can't write to result file")
print #2 "Battling ",wname$,".red against..."
bscore=0
for wn=1 to nbenchwars
 rem get actual benchmark warrior name
 w$=benchwarriors$(wn)
 e=open(1,w$,"r")
 if e=0 myerror("Can't read bench warrior")
 bname$=""
 while not eof(1)
  line input #1 a$
  if left$(a$,6)=";name " bname$=trim$(mid$(a$,7))
 wend
 close #1
 print #2 left$(bname$+"                         ",25)," ";
 battlewarriors(fname$,benchwarriors$(wn),bmrounds,benchpseq) rem into sc1
 bscore=bscore+sc1
 print #2 left$(str$(sc1)+"     ",6),"  ";
 rem make a line of * showing strength
 for j=1 to int(sc1*(40/300)) rem max 40 chars with perfect score
  print #2 "*";
 next j
 print #2
next wn
bscore=bscore/nbenchwars
print #2 "Benchmark score: ",left$(str$(bscore)+"     ",6)
close #2
end sub

REM cycle through available ANSI modes
sub cycleansimode()
if ansimode=0 then ansimode=16
elseif ansimode=16 then ansimode=256
elseif ansimode=256 then ansimode=24
elseif ansimode=24 then ansimode=0 endif
showsoup()
end sub

REM load INI file
sub loadinifile(fname$)
local iflag,mflag,a1flag,a2flag,tflag,e,a$,z,ln,i
e=open(1,fname$,"r")
if e=0 myerror("Can't open ini file")
rem make sure it's a valid INI file.. every line must either be
rem empty, start with rem, or contain a line with "=" and no binary data
rem stop loading at first error and print the line number with the error
ln=0:repeat
 ln=ln+1:e=1  rem start out with error flag set
 line input #1 a$:a$=trim$(a$)  rem get next line of ini file
 if a$="" e=0                   rem empty line so ok
 if upper$(left$(a$,4))="REM " e=0  rem starts with REM so ok
 z=instr(a$,"="):if z>1 and z<len(a$) e=0  rem has = so ok
 for i=1 to len(a$)  rem check for binary/invalid characters...
  if asc(mid$(a$,i,1))<32 or asc(mid$(a$,i,1))>126 e=1 rem not ok
 next i
 rem if e is still 0 then loading it probably won't bug out the program
until e or eof(1)
close #1
if e then rem something's wrong with the ini file
 print "Invalid data on line ",ln
 print "Not loading ini file":return
endif
e=open(1,fname$,"r")
if e=0 myerror("Can't open ini file") rem shouldn't need this
print "Loading ini file ",fname$
while not eof(1)
 line input #1 a$:a$=trim$(a$):parseiniline(a$)
wend:close #1
end sub

REM validate code entries in INI file
sub validatecode(entry$,nentries,entrytype)
local allowed$,fw,i,a$,z,retval
retval=0:entry$=upper$(entry$)
if entry$="" or nentries=0 return
if entrytype=1 then
 fw=4
 allowed$=" DAT MOV ADD SUB MUL DIV MOD JMP JMZ JMN DJN"
 allowed$=allowed$+" SPL SLT CMP SEQ SNE NOP LDP STP"
elseif entrytype=2 then
 fw=3
 allowed$=".A .B .AB.BA.F .X .I "
elseif entrytype=3 then
 fw=1
 allowed$="#$@<>*{}"
endif
for i=1 to nentries
 a$=mid$(entry$,(i-1)*fw+1,fw)
 z=instr(allowed$,a$)
 if z=0 then print "Invalid ";
  if entrytype=1 print "instruction ";
  if entrytype=2 print "modifier ";
  if entrytype=3 print "address mode ";
  print a$:retval=1
 endif
next i
return retval
end sub

REM parse a line from ini file
sub parseiniline(a$)
local eqs,varname$,varnum,varstr$,q1p,q2p,v,apflag
if upper$(left$(a$,4))="REM " return
eqs=instr(a$,"="):if eqs<2 return
varname$=trim$(left$(a$,eqs-1))
varnum=val(mid$(a$,eqs+1))
q1p=instr(a$,"\"")
q2p=instr(a$,"\"",q1p+1)
varstr$=mid$(a$,q1p+1,q2p-q1p-1)
apflag=instr(trim$(mid$(a$,eqs+1)),varname$) rem check for append
if apflag<>1 apflag=0 rem apflag set if varname is right after =
v=0 rem recognized entry flag 
if varname$="xdim"      v=1:xdim=varnum
if varname$="ydim"      v=1:ydim=varnum
if varname$="xwrap"     v=1:xwrap=varnum
if varname$="ywrap"     v=1:ywrap=varnum
if varname$="length"    v=1:length=varnum
if varname$="coresize"  v=1:coresize=varnum
if varname$="processes" v=1:processes=varnum
if varname$="cycles"    v=1:cycles=varnum
if varname$="rounds"    v=1:rounds=varnum
if varname$="mincrelen" v=1:mincrelen=varnum
if varname$="instrate"  v=1:instrate=varnum
if varname$="modrate"   v=1:modrate=varnum
if varname$="addrrate"  v=1:addrrate=varnum
if varname$="numrate"   v=1:numrate=varnum
if varname$="reprate"   v=1:reprate=varnum
if varname$="insrate"   v=1:insrate=varnum
if varname$="duprate"   v=1:duprate=varnum
if varname$="swaprate"  v=1:swaprate=varnum
if varname$="delrate"   v=1:delrate=varnum
if varname$="mutadjust" v=1:mutadjust=varnum
if varname$="mutadjgen" v=1:mutadjgen=varnum
if varname$="adjrate"   v=1:adjrate=varnum
if varname$="localrate" v=1:localrate=varnum
if varname$="orgrate"   v=1:orgrate=varnum
if varname$="org0rate"  v=1:org0rate=varnum
if varname$="crossrate" v=1:crossrate=varnum
if varname$="adjlocref" v=1:adjlocref=varnum
if varname$="evopseq"   v=1:evopseq=varnum
if varname$="evalmode"  v=1:evalmode=varnum
if varname$="evalrate"  v=1:evalrate=varnum
if varname$="evalgen"   v=1:evalgen=varnum
if varname$="ssfreq"    v=1:ssfreq=varnum
if varname$="superror"  v=1:superror=varnum
if varname$="benchfreq" v=1:benchfreq=varnum
if varname$="savethr"   v=1:savethr=varnum
if varname$="savepc"    v=1:savepc=varnum
if varname$="bmrounds"  v=1:bmrounds=varnum
if varname$="benchpseq" v=1:benchpseq=varnum
if varname$="reinsfreq" v=1:reinsfreq=varnum
if varname$="enablepreseed" v=1:enablepreseed=varnum
if varname$="unibox"    v=1:unibox=varnum
if varname$="viewmode"  v=1:viewmode=varnum
if varname$="viewshift" v=1:viewshift=varnum
if varname$="ansimode"  v=1:ansimode=varnum
if varname$="enableC"   v=1:enableC=varnum
if varname$="pmarsvrounds" v=1:pmarsvrounds=varnum
rem these strings have to be a single line
if varname$="header$"   v=1:header$=varstr$
if varname$="author$"   v=1:author$=varstr$
if varname$="comment$"  v=1:comment$=varstr$
if varname$="tempdir$"  v=1:tempdir$=varstr$
if varname$="soupdir$"  v=1:soupdir$=varstr$
if varname$="benchdir$" v=1:benchdir$=varstr$
if varname$="topdir$"   v=1:topdir$=varstr$
if varname$="lister$"   v=1:lister$=varstr$
if varname$="listeropts$"  v=1:listeropts$=varstr$
if varname$="listeroptsb$" v=1:listeroptsb$=varstr$
if varname$="pmars$"       v=1:pmars$=varstr$
if varname$="pmarsv$"      v=1:pmarsv$=varstr$
if varname$="pmarsvopts$"  v=1:pmarsvopts$=varstr$
if apflag then rem these strings can be appended to
 if varname$="instructions$" v=1:instructions$=instructions$+varstr$
 if varname$="modifiers$"    v=1:modifiers$=modifiers$+varstr$
 if varname$="addr1modes$"   v=1:addr1modes$=addr1modes$+varstr$
 if varname$="addr2modes$"   v=1:addr2modes$=addr2modes$+varstr$
 if varname$="initialinstr$" v=1:initialinstr$=initialinstr$+varstr$
else rem overwrite if apflag not set
 if varname$="instructions$" v=1:instructions$=varstr$
 if varname$="modifiers$"    v=1:modifiers$=varstr$
 if varname$="addr1modes$"   v=1:addr1modes$=varstr$
 if varname$="addr2modes$"   v=1:addr2modes$=varstr$
 if varname$="initialinstr$" v=1:initialinstr$=varstr$
endif
if v then
 print varname$;
 if apflag then print " + "; else print " = "; endif
 if right$(varname$,1)="$" then print "\"",varstr$,"\""
 else print varnum endif
else print varname$," not recognized" endif
end sub

REM validate parms
sub validateparms(noexit) rem if non-0 parm supplied don't exit just warn
local e                   rem (defaults to error exit if called with no parms)
pe=0 rem parm error flag
if left$(header$,1)<>";" print "header$ must start with ;":pe=1
if left$(author$,1)<>";" print "author$ must start with ;":pe=1
if left$(comment$,1)<>";" print "comment$ must start with ;":pe=1
if tempdir$="" print "tempdir$ must be specified":pe=1
if soupdir$="" print "soupdir$ must be specified":pe=1
if xdim<2 or xdim>316 print "xdim must be >=2 and <=316":pe=1
if ydim<2 or ydim>74  print "ydim must be >=2 and <=74":pe=1
if coresize<=0 or coresize<length*3 or coresize>9999 print "bad coresize":pe=1
rem this evolver does not support huge cores, udb wasting cycles even trying
if length=0 or length*3>coresize print "bad pmars length value":pe=1
if processes<=0 or processes>coresize print "bad pmars processes value":pe=1
if cycles<length or cycles<coresize print "bad pmars cycles value":pe=1
if rounds<=1 print "rounds must be >= 1":pe=1
if mincrelen<1 or mincrelen>length print "bad mincrelen":pe=1
if instrate<0 or instrate>1 print "instrate must between 0 and 1":pe=1
if modrate<0 or modrate>1 print "modrate must between 0 and 1":pe=1
if addrrate<0 or addrrate>1 print "addrrate must between 0 and 1":pe=1
if numrate<0 or numrate>1 print "numrate must between 0 and 1":pe=1
if mutadjust<0 or mutadjust>1 print "mutadjust must be between 0 and 1":pe=1
if adjrate<0 or adjrate>1 print "adjrate must between 0 and 1":pe=1
if localrate<0 or localrate>1 print "localrate must between 0 and 1":pe=1
if reprate<0 or reprate>1 print "reprate must between 0 and 1":pe=1
if insrate<0 or insrate>1 print "insrate must between 0 and 1":pe=1
if duprate<0 or duprate>1 print "duprate must between 0 and 1":pe=1
if swaprate<0 or swaprate>1 print "swaprate must between 0 and 1":pe=1
if delrate<0 or delrate>1 print "delrate must between 0 and 1":pe=1
if orgrate<0 or orgrate>1 print "orgrate must between 0 and 1":pe=1
if org0rate<0 or org0rate>1 print "org0rate must between 0 and 1":pe=1
e=evopseq:if e<0 or e>2 or e<>int(e) print "evopseq not int 0-2":pe=1
e=evalmode:if e<0 or e>4 or e<>int(e) print "evalmode not int 0-4":pe=1
if evalrate<0 or evalrate>1 print "evalrate must be between 0 and 1":pe=1
if ssfreq>0 and ssfreq<10000 print "ssfreq set too low":pe=1
if savethr<0 or savethr>300 print "savethr must be 0-300":pe=1
e=benchpseq:if e<0 or e>2 or e<>int(e) print "benchpseq not int 0-2":pe=1
ninst=len(instructions$)/4
if ninst<>int(ninst) print "instructions$ not divisible by 4":pe=1
if ninst=0 print "instructions$ empty":pe=1
nmods=len(modifiers$)/3
if nmods<>int(nmods) print "modifiers$ not divisible by 3":pe=1
if nmods=0 print "modifiers$ empty":pe=1
naddr1=len(addr1modes$):if naddr1=0 print "addr1modes$ empty":pe=1
naddr2=len(addr2modes$):if naddr2=0 print "addr2modes$ empty":pe=1
niniti=len(initialinstr$)/4
if niniti<>int(niniti) print "initialinstr$ not divisible by 4":pe=1
rem make sure instructions$ initialinstr$ modifiers$ addr1mode$ addr2modes$
rem all contain valid entries
pe=pe+validatecode(instructions$,ninst,1)
pe=pe+validatecode(initialinsts$,niniti,1)
pe=pe+validatecode(modifiers$,nmods,2)
pe=pe+validatecode(addr1modes$,naddr1,3)
pe=pe+validatecode(addr2modes$,naddr2,3)
rem disable out of bounds stuff
if mutadjgen<2 print "MutRate adjust disabled":mutadjust=0
if viewmode<0 or viewmode>3 or int(viewmode)<>viewmode then
 print "bad viewmode, making 0":viewmode=0 endif
if viewshift<0 or int(viewshift)<>viewshift then
 print "bad viewshift, making 0":viewshift=0 endif
rem disable functions if empty strings
if benchdir$="" print "benchdir$ empty":benchfreq=0
if topdir$="" print "topdir$ empty":benchfreq=0
if benchfreq=0 print "Benchmarking disabled":evalmode=0
if niniti=0 print "$initialinstr$ empty, preseed disabled":enablepreseed=0
rem ensure binaries are available
e=system(">/dev/null which "+pmars$)
if e print "pmars$ "+pmars$+" not found":pe=1
e=system(">/dev/null which "+pmarsv$)
if e print "$pmarsv not found, disabled":pmarsv$=""
e=system(">/dev/null which "+lister$):if e then
 print "$lister not found"
 lister$="cat"
endif
if lister$="cat" then
 print "Using cat, clearing listeropts$ listeroptsb$"
 listeropts$="":listeroptsb$=""
endif
if pe then
 if noexit then return pe rem return error code
 else myerror("") endif rem halt if parm error
endif
end sub

REM initialize/verify directories
sub initdirs()
topscore=0:topwarrior$=""
REM make/check directories and get top score
e=system("mkdir -p "+soupdir$)
if e myerror("Can't make soup directory")
e=system("mkdir -p "+tempdir$)
if e myerror("Can't make temp directory")
if benchfreq>0 then
 e=system("mkdir -p "+topdir$)
 if e myerror("Can't make top directory")
 e=open(1,topdir$+"/topscore.file","r")
 if e=1 then
  input #1 topwarrior$,topscore  rem space separated
  print "Top score = ",topscore," by ",topwarrior$
  close #1
 endif
endif
end sub

REM initialize benchmark warrior list
sub initbenchwarlist()
nbenchwars=0
if benchfreq>0 then
 nbenchwars = getbenchwarriors() rem get list of benchmark warriors
 if nbenchwars=0 then
  print "No benchmark warriors, benchmarking disabled"
  evalmode=0:benchfreq=0
 else
  print "Testing with ",nbenchwars," warriors from ",benchdir$
 endif
endif
end sub

sub continue_evolving()
rem all vars here are global
stopevolving=0
while stopevolving=0
 if ansifreq>0 then  rem if ANSI dumps enabled
  ansitrigger=ansitrigger+1
  if ansitrigger>ansifreq ansitrigger=0:doanotheransidump()
 endif
 w1x=int(ran(xdim))+1      rem choose a warrior from the soup
 w1y=int(ran(ydim))+1
 picksurrounding(w1x,w1y)  rem choose opponent, into w2x and w2y
 position(stat,2)
 print warriorname$(w1x,w1y)," vs ",warriorname$(w2x,w2y)," ",chr$(8);
 w1name$=tempdir$+"/a.red"
 w2name$=tempdir$+"/b.red"
 battlemode = 0
 if gennum(w1x,w1y)>=evalgen and gennum(w2x,w2y)>=evalgen then
  battlemode=evalmode
  if evalmode=3 if ran(1)<evalrate battlemode=1
  if evalmode=4 if ran(1)<evalrate battlemode=int(ran(2))+1
 endif
 if battlemode<>2 then  rem evalmode 2 writes its own files
  writewarrior(w1name$,w1x,w1y)
  writewarrior(w2name$,w2x,w2y)
 endif
 if battlemode = 1 then  rem guide with random bench warrior
  print "$",chr$(8);
  bwar$=benchwarriors$(int(ran(nbenchwars))+1)
  battlewarriors(w1name$,bwar$,rounds,evopseq)
  s1t=sc1  rem save since battlewarriors overwrites it
  battlewarriors(w2name$,bwar$,rounds,evopseq) rem use evolving rounds/seq
  sc2=sc1 : sc1=s1t  rem sc1=1st warrior score sc2=2nd warrior score
 else
  if battlemode = 2 then  rem guide with all bench warriors (slow!)
   print "#",chr$(8);
   s1t=benchwarrior(w1x,w1y)
   s2t=benchwarrior(w2x,w2y)
   sc1=s1t : sc2=s2t
  else
   print "-",chr$(8);
   battlewarriors(w1name$,w2name$,rounds,evopseq)
  endif
 endif
 position(stat,21) : print left$(str$(int(sc1))+"  ",3);
 position(stat,25) : print left$(str$(int(sc2))+"  ",3)," ",chr$(8);
 if sc1>sc2 then
  tx=w2x:ty=w2y  rem evolve overwrites w2x w2y if crossover enabled
  evolve(w1x,w1y,w2x,w2y)
  plotwarrior(tx,ty)
 else
  evolve(w2x,w2y,w1x,w1y)
  plotwarrior(w1x,w1y)
 endif
 position(stat,24)
 if crflag then print "*"; else print " "; endif
 if benchfreq>0 then
  benchtrigger=benchtrigger+1
  if benchtrigger>=benchfreq doperiodicbenchtest():benchtrigger=0
  if reinsfreq>0 then
   reinstrigger=reinstrigger+1
   if reinstrigger>reinsfreq reinsert():reinstrigger=0
  endif
 endif
 if ssfreq>0 then
  sstrigger=sstrigger+1
  if sstrigger>=ssfreq savesoup():sstrigger=0
 endif
 a$=system$(tempdir$+"/inkey.sh -t")
 if a$=chr$(27) stopevolving=1 rem stop if escape pressed
 if a$="[extra]" showsoup() rem redisplay soup if space or enter pressed
 if a$="v" viewmode=mod(viewmode+1,4):showsoup() rem adjust view if v pressed
 if a$="c" and enableC cycleansimode() rem adjust colors if c pressed
 if a$="V" and viewmode >=2 then  rem randomize viewshift if shift V pressed
  if viewshift=viewshiftsave then viewshift=int(ran(100000))
  else viewshift=viewshiftsave endif
  showsoup()
 endif
wend
end sub

REM save soup
sub savesoup()
local x,y,fname$
for y=1 to ydim
 for x=1 to xdim
  fname$=soupdir$+"/"+warriorname$(x,y)+".red"
  writewarrior(fname$,x,y)
 next x
next y
end sub

REM get list of benchmark warriors into benchwarriors$ array
REM return actual number of benchmark warriors in array
sub getbenchwarriors()
local a$,i,w,t$,c$
for i=1 to maxbw:benchwarriors$(i)="":next i
a$=system$("2>/dev/null ls "+benchdir$+"/*.red")
if a$="" return 0  rem no benchmark warriors found
w=0:t$=""
for i=1 to len(a$)
 c$=mid$(a$,i,1)
 if c$=chr$(10) then
  if w<maxbw w=w+1:benchwarriors$(w)=t$
  t$=""
 else t$=t$+c$ endif
next i:return w
end sub

REM select a warrior and benchmark it
sub doperiodicbenchtest()
local x,y,wscore,t,e,wn$,saveflag,h$,a$,sh$,z1,z2,sn$,ss,hasentry,rflag,ts$
if nbenchwars=0 return
x=int(ran(xdim))+1
y=int(ran(ydim))+1
wscore=benchwarrior(x,y)
wn$=warriorname$(x,y)
lastscore=wscore:lastbenchwar$=wn$  rem save globally for ANSI dump
position(stat,30):print wn$;
print " scores ",left$(str$(wscore)+"    ",5)," ",chr$(8);
if wscore>topscore topscore=wscore:topwarrior$=wn$
print " top=",left$(str$(topscore)+"    ",5)," ",chr$(8);
t=topscore-(topscore*(savepc/100))
if wscore>=t and wscore>=savethr then
 saveflag=1:position(stat,61):cleartoend()
 if wscore=topscore then rem save topscore warriorname$ to topscore.file
  e=open(2,topdir$+"/topscore.file","w")
  if e=0 myerror("Can't open topscore.file for write")
  print #2 wn$," ",topscore
  close #2
 else rem don't overwrite the top scoring warrior
  if wn$=topwarrior$ saveflag=0:print "(cancelled)";
 endif
 if saveflag then
  rem need a way to keep from saving duplicates so keep a file
  rem containing warrior names, hashes, testset and score
  rem also, avoid overwriting stronger warriors.. which leads
  rem to another thing.. if it does overwrite a warrior then
  rem it needs to remove its entry from the warinfo file
  h$=hashwarrior$(x,y)
  hasentry=0 rem set if warrior is present in warinfo file
  rflag=0    rem reason for rejection
  e=open(1,topdir$+"/warinfo.file","r")
  if e then rem if file exists
   while not eof(1)
    line input #1 a$
    z1=instr(a$," ") rem get the 1st space
    if z1 then rem if multiple fields
     sn$=left$(a$,z1-1) rem get name field
     if wn$=sn$ hasentry=1 rem flag to indicate name match
     z2=instr(a$," ",z1+1) rem get the space after that
     if z2 then rem if there's a field
      sh$=mid$(a$,z1+1,z2-z1-1) rem get hash field
      if h$=sh$ saveflag=0:rflag=1 rem cancel save if dup
      z1=instr(a$," ",z2+1) rem get space after next field
      if z1 then rem if fields exist
       ts$=mid$(a$,z2+1,z1-z2-1) rem get testset field
       ss=val(mid$(a$,z1+1)) rem get saved score
       if sn$=wn$ then rem if warrior name matches
        if wscore<=ss saveflag=0:rflag=2 rem cancel if weaker
       endif
       if str$(wscore)=str$(ss) then rem if score is the same as saved
        if ts$=benchdir$ and benchpseq>0 rem and same testset, using fixed 
         saveflag=0:rflag=3 rem cancel presumed functional duplicate
       endif
      endif
     endif
    endif
   wend
   close #1
  endif
  if saveflag then
   if hasentry then print "Replacing ",wn$;
   else print "Saving ",wn$;
   endif
   writewarrior(topdir$+"/"+wn$+".red",x,y)
   if hasentry then rem if there's already an entry
    e=open(1,topdir$+"/warinfo.file","r")
    if e=0 myerror("Has entry but can't open warinfo file")
    e=open(2,tempdir$+"/warfile.tmp","w")
    if e=0 myerror("Can't open temp war file for write")
    while not eof(1):line input #1 a$:print #2 a$:wend
    close #2:close #1
    e=open(1,tempdir$+"/warfile.tmp","r")
    if e=0 myerror("Can't open temp war file for read")
    e=open(2,topdir$+"/warinfo.file","w")
    if e=0 myerror("Can't open warinfo file for replacing")
    while not eof(1)
     line input #1 a$
     z1=instr(a$,wn$) rem check line for warrior name
     if z1=0 print #2 a$ rem if not write the line
    wend:close #1:close #2
   endif
   rem update warinfo file
   e=open(2,topdir$+"/warinfo.file","a") rem append
   if e=0 myerror("Can't open warinfo file for append")
   print #2 wn$," ",h$," ",benchdir$," ",wscore
   close #2
  else
   cleartoend()
   if rflag=1 print "(",wn$," dup)";
   if rflag=2 print "(",wn$," weak)";
   if rflag=3 print "(",wn$," fdup)";
  endif
 endif
endif
end sub

REM return hash of the warrior code
REM this is not a good hash.. just has to catch dupes
sub hashwarrior$(x,y)
local h,i,j,a$,lc,c1,c2,c3
c1=377:c2=5911:c3=225
h=0:for i=1 to wlength(x,y)
 a$=upper$(soup$(x,y,i))
 lc=0:for j=1 to len(a$)
  lc=lc+asc(mid$(a$,j,1))+j*c1
 next j
 h=h+lc+(lc+c3)*i*c2
next i
return right$("00000"+str$(mod(h,1000000)),6)
end sub

REM benchmark specified warrior, return score
REM changes global vars sc1 sc2
sub benchwarrior(x,y)
local w$,s,i
if nbenchwars=0 return
w$=tempdir$+"/a.red"
writewarrior(w$,x,y)
s=0
for i=1 to nbenchwars
 battlewarriors(w$,benchwarriors$(i),bmrounds,benchpseq)
 s=s+sc1
next i
return s/nbenchwars
end sub

REM return fixed-length warrior name
sub warriorname$(x,y)
local xlen,ylen,x$,y$
xlen=len(str$(xdim))
ylen=len(str$(ydim))
x$=right$("0000"+str$(x),xlen)
y$=right$("0000"+str$(y),ylen)
return x$+"_"+y$
end sub

REM evolve winning warrior on top of losing warrior
sub evolve(x1,y1,x2,y2)
local ln,wl,dp,a$,b$,n,ch,docross,iline,dline,i,an,bn rem ,rmod global
rem derive mutation rate modifier rmod from gennum mutadjust and mutadjgen
rem affects instrate modrate addrrate numrate reprate insrate duprate
rem swaprate and orgrate by gradually reducing mutation as gen increases
if mutadjgen<2 mutadjgen=1:mutadjust=0 rem disable if mutadjgen<2
rmod=1-(mutadjust*(gennum(x1,y1)/mutadjgen)) rem calculate multiplier
if rmod<mutadjust rmod=mutadjust rem rmod between mutadjust and 1
origin$(x2,y2)=origin$(x1,y1)
species$(x2,y2)=species$(x1,y1)
orgnum(x2,y2)=orgnum(x1,y1)
gennum(x2,y2)=gennum(x1,y1)+1
tdstamp$(x2,y2)=mid$(date$,3,10)+" "+left$(time$,8)
dp=0 rem destination line pointer
wl=wlength(x1,y1)
ch=0 rem species change flag
if ran(1)<swaprate*rmod and wl>1 then rem swaps lines in the parent warrior
 ln=int(ran(1)*(wl-1))+1         rem usually fatal but sometimes not
 a$=soup$(x1,y1,ln):soup$(x1,y1,ln)=soup$(x1,y1,ln+1)
 soup$(x1,y1,ln+1)=a$:ch=1
endif
docross=0:crflag=0:dline=0:iline=0
if crossrate>0 then
 picksurrounding(x1,y1) rem into w2x w2y
 if species$(w2x,w2y)=species$(x1,y1) docross=1 rem must be same length
 if w2x=x2 and w2y=y2 docross=0 rem probably best to not cross with loser..
 if wlength(w2x,w2y)<>wlength(x1,y1) docross=0 rem in case dup species number
endif
for ln=1 to wl
 a$=soup$(x1,y1,ln)
 if docross if ran(1)<crossrate crflag=1:a$=soup$(w2x,w2y,ln) rem crossover
 if ran(1)<duprate then
  if ran(1)<0.5 then
   if ln>1 a$=soup$(x1,y1,ln-1):ch=1
  else
   if ln<wl a$=soup$(x1,y1,ln+1):ch=1
  endif
 endif
 if ran(1)<instrate*rmod then
  b$=mid$(a$,1,3)
  mid$(a$,1,3)=rndinst$()
  if b$<>mid$(a$,1,3) ch=1
 endif
 if ran(1)<modrate*rmod  mid$(a$,4,3)=rndmod$()
 if ran(1)<addrrate*rmod mid$(a$,8,1)=rndaddr1$()
 if ran(1)<numrate*rmod then
  if ran(1)<adjrate then rem adjust number
   n=val(mid$(a$,9,4))
   n=n+(int(ran(2))-1)
   mid$(a$,9,4)=right$("   "+str$(n),4)
  else
   mid$(a$,9,4)=rndnum$(ln)
  endif
 end if
 if ran(1)<addrrate*rmod mid$(a$,14,1)=rndaddr2$()
 if ran(1)<numrate*rmod then
  if ran(1)<adjrate then rem adjust number
   n=val(mid$(a$,15,4))
   n=n+(int(ran(2))-1)
   mid$(a$,15,4)=right$("   "+str$(n),4)
  else
   mid$(a$,15,4)=rndnum$(ln)
  endif
 end if
 if ran(1)<reprate a$=rndwline$(ln) : ch=1
 if ran(1)>delrate*rmod or wl=1 then
  if dp<length dp=dp+1
  if ran(1)<insrate*rmod then
   ch=1:iline=dp
   soup$(x2,y2,dp)=rndwline$(dp)
   if dp<length dp=dp+1
  endif
  soup$(x2,y2,dp)=a$
 else
  ch=1:dline=dp
 endif
next ln
wlength(x2,y2)=dp
if orgnum(x2,y2)>=dp orgnum(x2,y2)=dp-1 rem in case length changed
n=orgnum(x2,y2)
if ran(1)<orgrate*rmod then
 if ran(1)<org0rate then orgnum(x2,y2)=0
 else orgnum(x2,y2)=int(ran(dp))
 endif
endif
if n<>orgnum(x2,y2) ch=1
if ch species$(x2,y2)=str$(int(ran(100000)))
if adjlocref and (iline or dline) then rem adjust local references
 for i=1 to dp                         rem (this probably isn't right)
  a$=soup$(x2,y2,i):an=val(mid$(a$,9,4)):bn=val(mid$(a$,15,4))
  rem when inserting, increment local refs before the iline
  rem if they point to iline or after and decrement local
  rem refs after iline if they point to iline or before
  if iline then
   if i<iline and an+i>=iline and an+i<=dp an=an+1
   if i>iline and an+i<=iline and an+i>=1 an=an-1
   if i<iline and bn+i>=iline and bn+i<=dp bn=bn+1
   if i>iline and bn+i<=iline and bn+i>=1 bn=bn-1
  endif
  rem when deleting, decrement local refs before the dline
  rem if they point to dline or after and increment local
  rem refs after dline if they point to dline or before
  if dline then
   if i<dline and an+i>=dline and an+i<=dp an=an-1
   if i>dline and an+i<=dline and an+i>=1 an=an+1
   if i<dline and bn+i>=dline and bn+i<=dp bn=bn-1
   if i>dline and bn+i<=dline and bn+i>=1 bn=bn+1
  endif
  mid$(a$,9,4)=right$("   "+str$(an),4)
  mid$(a$,15,4)=right$("   "+str$(bn),4)
  soup$(x2,y2,i)=a$
 next i
endif
normalizenumbers(x2,y2)
end sub

REM normalize numbers so they range from 0 to coresize-1
REM and only backrefs within the warrior code are negative
sub normalizenumbers(x,y)
local i,n1,n2,a$
for i=1 to wlength(x,y)
 a$=soup$(x,y,i)
 n1=val(mid$(a$,9,4)):n2=val(mid$(a$,15,4))
 n1=mod(n1,coresize):n2=mod(n2,coresize)
 if n1<0 n1=n1+coresize
 if n2<0 n2=n2+coresize
 if coresize-n1<i n1=n1-coresize
 if coresize-n2<i n2=n2-coresize
 mid$(a$,9,4)=right$("   "+str$(n1),4)
 mid$(a$,15,4)=right$("   "+str$(n2),4)
 soup$(x,y,i)=a$
next i
end sub

REM pick surrounding warrior
sub picksurrounding(x,y)
label pickagain
d=int(ran(8))
if d=0  w2x=x-1 : w2y=y
if d=1  w2x=x-1 : w2y=y-1
if d=2  w2x=x   : w2y=y-1
if d=3  w2x=x+1 : w2y=y-1
if d=4  w2x=x+1 : w2y=y
if d=5  w2x=x+1 : w2y=y+1
if d=6  w2x=x   : w2y=y+1
if d=7  w2x=x-1 : w2y=y+1
if xwrap then
 if w2x<1    w2x=w2x+xdim
 if w2x>xdim w2x=w2x-xdim
else
 if w2x<1 or w2x>xdim goto pickagain
endif
if ywrap then
 if w2y<1    w2y=w2y+ydim
 if w2y>ydim w2y=w2y-ydim
else
 if w2y<1 or w2y>ydim goto pickagain
endif
end sub

REM battle warriors - file1$ file2$ rounds mode
REM where mode = 0 for random, 1 for fixed, 2 for permutate 
REM return scores in global vars sc1 sc2
sub battlewarriors(f1$,f2$,battlerounds,mode)
local cl$,t$
t$=""  rem default random positioning
if mode=1 t$="f"  rem fixed sequence positioning
if mode=2 t$="P"  rem permutate every position (rounds 142 for nano)
cl$="":if superror cl$="2>/dev/null "
cl$=cl$+pmars$+" -"+t$+"bkl "+str$(length)+" -d "+str$(length)
cl$=cl$+" -s "+str$(coresize)+" -p "+str$(processes)+" -c "+str$(cycles)
if mode<>2 cl$=cl$+" -r "+str$(battlerounds)
cl$=cl$+" "+f1$+" "+f2$
runpmarscommandline(cl$,battlerounds)  rem leave scores in global vars sc1 sc2
if sc1=0 and sc2=0 myerror("Both scores 0, check PMARS")
end sub

sub runpmarscommandline(cl$,battlerounds)
local s$,t$,p,a,b,c,d,i
s$=system$(cl$)
t$="" : p=0 : a=0 : b=0 : c=0 : d=0
for i=1 to len(s$)  rem parse KOTH score
 c$=mid$(s$,i,1) : t$=t$+c$
 if p=0 and c$=" "      p=1:a=val(t$):t$=""
 if p=1 and c$=chr$(10) p=2:b=val(t$):t$=""
 if p=2 and c$=" "      p=3:c=val(t$):t$=""
 if p=3 and c$=chr$(10) p=4:d=val(t$)
next i
rem return normalized scores, 0-300
sc1=(a*3+b)*(100/battlerounds)
sc2=(c*3+d)*(100/battlerounds)
end sub

REM return a random warrior line
sub rndwline$(ln)
a$=rndinst$()+rndmod$()+" "+rndaddr1$()+rndnum$(ln)
a$=a$+","+rndaddr2$()+rndnum$(ln)
return a$
end sub

REM return a random instruction
sub rndinst$()
local i
i=int(ran(ninst))*4+2
return mid$(instructions$,i,3)
end sub

REM return a random modifier
sub rndmod$()
local i
i=int(ran(nmods))*3+1
return mid$(modifiers$,i,3)
end sub

REM return a random addressing mode 1
sub rndaddr1$()
local i
i=int(ran(naddr1))+1
return mid$(addr1modes$,i,1)
end sub

REM return a random addressing mode 2
sub rndaddr2$()
local i
i=int(ran(naddr2))+1
return mid$(addr2modes$,i,1)
end sub

REM return a random number
REM small numbers refer to location within warrior length (used or not)
REM large numbers are 0 to coresize-1
sub rndnum$(ln)
local i
if ran(1)<localrate then
 i=int(ran(length))-(ln-1)
else
 i=int(ran(coresize))
end if
return right$("   "+str$(i),4)
end sub

REM write a warrior to disk
sub writewarrior(fname$,x,y)
local e,i,a$
e=open(2,fname$,"w")
if e=0 print "Can't write warrior",fname$:error "hosed"
print #2 header$
print #2 ";name ",warriorname$(x,y),".red"
print #2 author$
print #2 comment$
print #2 ";origin ",origin$(x,y)
print #2 ";species ",species$(x,y)
print #2 ";generation ",gennum(x,y)
print #2 ";created ",tdstamp$(x,y)
print #2 ";assert CORESIZE==",str$(coresize)
print #2 "ORG ",orgnum(x,y)
for i=1 to wlength(x,y)
 a$=soup$(x,y,i)
 print #2 a$
next i
print #2 "END"
close #2
end sub

REM show the soup
sub showsoup()
local x,y rem tl$,tr$,bl$,br$,hl$,vl$ global to reuse for ANSI dumps
clearscreen():hidecursor()
if unibox then
 tl$=chr$(226)+chr$(148)+chr$(140)
 tr$=chr$(226)+chr$(148)+chr$(144)
 bl$=chr$(226)+chr$(148)+chr$(148)
 br$=chr$(226)+chr$(148)+chr$(152)
 hl$=chr$(226)+chr$(148)+chr$(128)
 vl$=chr$(226)+chr$(148)+chr$(130)
else
 tl$=".":tr$=".":bl$="`":br$="'":hl$="-":vl$="|"
endif
print esc$,"0m",tl$;:for x=1 to xdim:print hl$;:next x:print tr$
for y=1 to ydim
 print vl$;
 for x=1 to xdim
  printwarrior(x,y) rem print the warrior character
 next x
 print esc$,"0m",vl$
next y
print bl$;:for x=1 to xdim:print hl$;:next x:print br$
end sub

REM hide cursor
sub hidecursor()
print esc$,"?25l";
end sub

REM show cursor
sub showcursor()
print esc$,"?25h";
end sub

REM position cursor line,column
sub position(l,c)
print esc$,l,";",c,"H";
end sub

REM clear to end of line
sub cleartoend()
print esc$,"0K";
end sub

REM clear screen
sub clearscreen()
position(1,1)
print esc$,"0m";     rem reset colors
print esc$,"2J";     rem clear screen
end sub

REM plot warrior on screen
sub plotwarrior(x,y)
position(y+1,x+1)
printwarrior(x,y)
print esc$,"0m";
end sub

REM print warrior character to screen or file
sub printwarrior(x,y,fnum)  rem if fnum specified write to file #
local r,g,b,c,d,l,w$,a$
rem if ansimode specified color by species
if ansimode=16 then rem ANSI-16 colors (14 colors 1-7,9-15)
 c=mod(val(species$(x,y)),14)+1:b=0:if c>7 b=1:c=c-7
 a$=esc$+str$(b)+";"+str$(c+30)+"m"
elseif ansimode=256 then rem ANSI-256 colors (214 colors 17-230)
 c=mod(val(species$(x,y)),214)+17
 a$=esc$+"38;5;"+str$(c)+"m"
elseif ansimode=24 then rem ANSI 24 bit RGB color
 c=val(species$(x,y)):r=mod(c,30)*7+40
 g=mod(int(c/46),30)*7+40:b=mod(int(c/2116),30)*7+40
 a$=esc$+"38;2;"+str$(r)+";"+str$(g)+";"+str$(b)+"m"
else a$="" rem no color
endif
if viewmode=1 then rem use characters for species
 d=mod(val(species$(x,y)),len(vm1chars$))+1
 w$=mid$(vm1chars$,d,1)
elseif viewmode>=2 then rem use characters for instruction sequence
 w$=getwarriorbasechar$(x,y) rem reduce base instr.seq. to a character
else rem use numbers and letters for length
 w$="*":l=wlength(x,y)
 if l<62 w$=chr$(l+87)
 if l<36 w$=chr$(l+55)
 if l<10 w$=str$(l)
endif
if fnum=0 then print a$,w$; else print #fnum a$,w$; endif
end sub

REM simple hash of the instruction sequence for display purposes
REM messing with this again, still too many dups. Dups are unavoidable
REM (the birthday paradox) but added mechanisms to adjust for a particular
REM soup - added constants C1 C2 initialized at the beginning of the program
REM (right now they're 1,1 for the original characters but can be changed)
REM and added a viewmode setting that is xor'd against the raw hash, changing
REM that shifts everything to find a combo with no dups in the dominant types
sub getwarriorbasechar$(x,y)
local d,i,j,m,e,a$,w$
d=0:for i=1 to wlength(x,y) rem do a simple hash on the first
 a$=upper$(soup$(x,y,i))    rem 3 letters of each line
 e=0:for j=1 to 3:e=e+asc(mid$(a$,j,1))*(1+(j-1)*c1):next j
 d=d+e*(1+(i-1)*c2)
next i
d=xor(d,viewshift)  rem change all the characters
if viewmode=3 then m=mod(d,94):w$=chr$(m+33) rem 94 chars ! to ~
else m=mod(d,52):w$=chr$(m+65):if m>25 w$=chr$(m+71) rem 52 chars A-Z a-z
endif:return w$
end sub

REM dump soup display as an ANSI file
REM write as a linear file with no positioning and no background color
sub dumpansifile(fname$)
local e,x,y,z
e=open(2,fname$,"w"):if e=0 return 1 rem if error exit sub with error
rem tl$ tr$ bl$= br$= hl$ vl$ set by showsoup(), always called before this
print #2 esc$,"0m",tl$;:for x=1 to xdim:print #2 hl$;:next x:print #2 tr$
for y=1 to ydim
 print #2 vl$;
 for x=1 to xdim
  printwarrior(x,y,2) rem write warrior char to file #2
 next x
 print #2 esc$,"0m",vl$
next y
print #2 bl$;:for x=1 to xdim:print #2 hl$;:next x:print #2 br$
if benchfreq>0 then
 if topwarrior$="" topwarrior$=left$("------",len(warriorname$(1,1)))
 if lastbenchwar$="" lastbenchwar$=topwarrior$:lastscore=topscore
 z=instr(fname$,"/"):print #2 " ",mid$(fname$,z+1)," "; rem base name
 print #2 "Top=",left$(str$(topscore)+"    ",5)," by ",topwarrior$," ";
 print #2 "Last=",left$(str$(lastscore)+"    ",5)," by ",lastbenchwar$," ";
endif
close #2
end sub

REM do another ANSI dump
REM directory in ansidir$, base filename in ansifbase$
REM last sequence number in ansifnumber, max # dumps in ansifmax
sub doanotheransidump()
local e,dump$,a$
e=system("mkdir -p "+ansidir$) rem setup should have already made this
if e error("Can't make ANSI dump dir") rem but in case removed etc
ansifnumber=ansifnumber+1
if ansifnumber>ansifmax ansifreq=0:sleep 5:return rem stop dumping
dump$=ansidir$+"/"+ansifbase$+right$("000"+str$(ansifnumber),4)+".ans"
e=dumpansifile(dump$)
if e then rem if error occurs
 position(stat,1):cleartoend():print "Error dumping ANSI file ",dump$;
 print " (press a key)"; : system(tempdir$+"/inkey.sh")
 ansifreq=0:showsoup()
endif
end sub

REM load a warrior into the soup
sub loadwarrior(fname$,x,y)
local ln,done
e=open(1,fname$,"r")
if e=0 myerror("Can't load warrior")
origin$(x,y)=""
species$(x,y)=""
orgnum(x,y)=0
gennum(x,y)=0
ln=0 : done=0
while not eof(1) and not done
 line input #1 wline$
 wline$=trim$(wline$)
 if upper$(left$(wline$,3))="END" or wline$="" then
  done=1
 else
  if left$(wline$,8)=";origin " origin$(x,y)=trim$(mid$(wline$,9))
  if left$(wline$,9)=";species " species$(x,y)=trim$(mid$(wline$,10))
  if left$(wline$,12)=";generation " gennum(x,y)=val(mid$(wline$,13))
  if left$(wline$,9)=";created " tdstamp$(x,y)=trim$(mid$(wline$,10))
  if upper$(left$(wline$,4))="ORG " then
   orgnum(x,y)=val(trim$(mid$(wline$,5)))
  else
   if left$(wline$,1)<>";" and ln<length then
    ln=ln+1
    soup$(x,y,ln)=wline$
   endif
  endif
 endif
wend
close 1
wlength(x,y)=ln
end sub

REM Reinsert a saved warrior back into the soup
sub reinsert()
local x,y,i,n,p,q,w,war$,warname$,a$
if nbenchwars=0 return
rem select random soup warrior to replace
x=int(ran(xdim)+1)
y=int(ran(ydim)+1)
rem select random warrior from top
a$=system$("2>/dev/null ls "+topdir$+"/*.red")
if a$="" return  rem no warriors in top
n=0
for i=1 to len(a$)
 if mid$(a$,i,1)=chr$(10) n=n+1
next i
w=int(ran(n))
rem find position in dir listing string
p=0:for i=1 to w:p=instr(a$,chr$(10),p+1):next i
q=instr(a$,chr$(10),p+1):war$=mid$(a$,p+1,q-p-1)
rem get name of reinserted warrior
warname$=mid$(war$,len(topdir$)+2,len(str$(xdim))+len(str$(ydim))+1)
position(stat,61)
print warname$,">",warriorname$(x,y); : cleartoend()
loadwarrior(war$,x,y)
end sub

REM Other functions that won't fit on the main UI menu
REM File dir/naming note - report/INI file writes from here go to the
REM topdir$ and add the .txt/.ini extension (because almost always saving
REM the parms for that particular run and also want to make it harder to
REM accidently overwrite a file), INI file load comes from the program
REM directory and does not add the extension (because most of the time
REM loading a file from there and might not end with .ini)
sub otherstuff()
local selection,nseq,fname$,a$,b$,c$,t$,e,x,y,z,i,seqtmp$,sc$,sm,score,tflg
local f$,avg,again,v1,v2,v3,v4,v5,v6,v7,s1$,s2$,s3$,s4$,subname$,code$,vss,wc
local addlistings,addsoupdisplay,addfullbench,savefname$
label otherstuffmenu
clearscreen()
print
print "   Other functions..."
print "   1 - Change the benchmark set"
print "   2 - Load settings file (some parms ignored)"
print "   3 - Show soup sequences and population numbers"
print "   4 - Sequence and benchmark the entire soup"
print "   5 - Change settings variable(s)"
print "   6 - Display variable(s)"
print "   7 - Display all settings"
print "   8 - Save settings to file"
if soupchanged print "   9 - Save soup (not saved yet)"
print "   10 - Start/stop ANSI dumps";
if ansifreq>0 then print " (enabled, select for info)" else print endif
print "   11 - Play back ANSI dumps"
input "   Select function or none to exit: " t$ : selection=val(t$)
print
if selection=9 then
 if soupchanged then
  print "Saving soup..."
  savesoup():soupchanged=0
  sleep 1:goto otherstuffmenu
 endif
elseif selection=10 then rem ANSI dump control
 if ansifreq>0 then
  print "ANSI dumps enabled, saving to ",ansidir$,"/",ansifbase$,"*.ans"
  print "Dump interval = ",ansifreq," Last sequence = ",ansifnumber;
  print " Max sequence = ",ansifmax
  input "Stop ANSI dumps? " t$:t$=left$(upper$(trim$(t$)),1)
  if t$="Y" ansifreq=0:print "ANSI dumps disabled"
 else
  input "Base name for ANSI dump files: " a$
  ansifbase$=trim$(a$):if ansifbase$="" goto otherstuffmenu
  print "Directory (enter to use ",ansidir$,")";
  input ": " a$:a$=trim$(a$):if a$<>"" ansidir$=a$
  e=system("mkdir -p "+ansidir$)
  if e print "Can't make that directory":sleep 2:goto otherstuffmenu
  e=open(1,ansidir$+"/"+ansifbase$+"0001.ans","r")
  if e then
   close #1:print "ANSI files exist, Remove or Continue? (R/C/any)";
   input ": " t$:t$=left$(upper$(trim$(t$)),1)
   if t$="R" then
    print "Removing existing ANSI files"
    system("rm "+ansidir$+"/"+ansifbase$+"*.ans"):ansifnumber=0
   elseif t$="C" then
    c$="for f in "+ansidir$+"/"+ansifbase$+"*.ans;do a=\"$f\";done;echo -n $a"
    a$=system$(c$) rem get filename for last dump file
    print "Last dump = ",a$
    ansifnumber=val(mid$(a$,len(a$)-7,4)) rem extract sequence #
    print "Starting with sequence #",ansifnumber+1
   else print "(cancelled)":sleep 1:goto otherstuffmenu
   endif
  else ansifnumber=0 endif
  input "Cycles between ANSI dumps: " ansifreq
  if ansifreq<=0 ansifreq=0:print "(cancelled)":sleep 1:goto otherstuffmenu
  if ansifreq<1000 print "(warning excessive writes, enter -1 to cancel)"
  input "Max number of dumps (enter for max 9999): " ansifmax
  if ansifmax<0 or ansifmax>9999 then
   ansifreq=0:ansifmax=9999:print "(cancelled)":sleep 1:goto otherstuffmenu
  endif
  ansitrigger=ansifreq:if ansifmax=0 ansifmax=9999
  print "ANSI dumps enabled"
 endif
 sleep 1:goto otherstuffmenu
elseif selection=11 then rem play back ANSI dumps
 t$="":if ansifnumber>0 then rem if previous ANSI dumps performed
  print "Play back ",ansifbase$;:input "? " t$
  t$=left$(upper$(trim$(t$)),1)
 endif
 if t$="Y" then
  a$=ansifbase$:c$=ansidir$ rem play back previous dumps
 else
  input "ANSI dump base name: " a$:a$=trim$(a$)
  if a$="" goto otherstuffmenu
  print "ANSI dump dir (enter for ",ansidir$,")";:input ": " c$
  if c$="" c$=ansidir$
  b$=c$+"/"+a$+"0001.ans":e=open(1,b$,"r")
  if e=0 print "Not found":sleep 2:goto otherstuffmenu
  close #1 rem ANSI files exist just don't delete sequence #1
  rem if ansi2txt installed then make sure term is big enough for dump
  z=system("which ansi2txt>/dev/null"):if z=0 then
   t$=system$("cat "+b$+"|ansi2txt|wc -L"):x=val(t$)-2
   t$=system$("cat "+b$+"|ansi2txt|wc -l"):y=val(t$)-2
   if x>max(xdim,78) or y>max(ydim,21) then
    print "Too big":sleep 2:goto otherstuffmenu
   endif
  endif
 endif
 input "Seconds between frames (enter for 0.1): " z
 if z<0 then goto otherstuffmenu else if z=0 z=0.1
 endif
 print
 print "While playing press P-pause S-slower F-faster L-loop on/off,"
 input "any other key exits. Press enter to start playback... " a$
 rem one-line sh script for playing back ANSI files...
 b$=str$(z):t$="clear;td="+b$+";loop=0;"
 t$=t$+"while [ 1 ];do for f in "+c$+"/"+a$+"*.ans;do "
 t$=t$+"echo -n \"\\033[H\";cat $f;echo -n \"\\033[0K\";"
 rem use inkey.sh for delay to allow exiting (can't read -n etc under sh)
 rem while playing press L-loop P-pause F-faster S-slower, any other exits
 t$=t$+"x=$("+tempdir$+"/inkey.sh -t $td);"
 t$=t$+"if [ \"$x\" = \"f\" ];then td=$(echo $td|awk "
 t$=t$+"'{if ($1>0.01) print $1*0.8;else print $1}');"
 t$=t$+"elif [ \"$x\" = \"s\" ];then td=$(echo $td|awk "
 t$=t$+"'{if ($1<2) print $1*1.2;else print $1}');"
 t$=t$+"elif [ \"$x\" = \"p\" ];then "+tempdir$+"/inkey.sh;"
 t$=t$+"elif [ \"$x\" = \"l\" ];then loop=$((1-loop));"
 t$=t$+"elif [ \"$x\" != \"\" ];then exit;fi;done;"
 t$=t$+"if [ $loop = 0 ];then exit;fi;done"
 repeat
  hidecursor():system(t$):showcursor()
  print "  Play again? ";:a$=system$(tempdir$+"/inkey.sh")
 until a$<>"y":goto otherstuffmenu
elseif selection=1 then rem change/disable bench directory
 label enterbenchdirname
 input "Enter new benchmark directory: " a$:a$=trim$(a$)
 if a$="" then
  input "Disable benchmarking? " t$
  t$=left$(trim$(upper$(t$)),1)
  if t$="Y" benchdir$="":nbenchwars=0
  return
 endif
 e=system("[ -d "+a$+" ]")
 if e print "That is not a directory":goto enterbenchdirname
 benchdir$=a$:resetbenchstuff():goto otherstuffmenu
elseif selection=2 then rem load an INI file
rem have to be very careful here, the new INI must be compatible
rem with existing soup..same dimensions, coresize, warriorsize etc.
rem do not change tempdir$ soupdir$ topdir$ apply settings to current soup
 e=0
 label enterinifilename
 input "Enter settings filename to load: " a$:a$=trim$(a$)
 if e=0 and a$="" goto otherstuffmenu rem don't go back to menu if error
 if a$="" print "Must load good INI file":goto enterinifilename
 e=system("[ -f "+a$+" ]")
 if e print "That is not a file":goto enterinifilename
 rem save variables that should not be changed while running
 v1=xdim:v2=ydim:v3=length:v4=mincrelen
 v5=coresize:v6=processes:v7=cycles
 s1$=tempdir$:s2$=soupdir$:s3$=topdir$
 s4$=benchdir$ rem don't restore but use to detect if bench set changed
 vss=viewshift rem to see if new viewshift specified
 loadinifile(a$)  rem load the INI file (prints items)
 rem restore variables
 if xdim<>v1      print "Restoring xdim from ",xdim," to ",v1
 if ydim<>v2      print "Restoring ydim from ",ydim," to ",v2
 if length<>v3    print "Restoring length from ",length," to ",v3
 if mincrelen<>v4 print "Restoring mincrelen from ",mincrelen," to ",v4
 if coresize<>v5  print "Restoring coresize from ",coresize," to ",v5
 if processes<>v6 print "Restoring processes from ",processes," to ",v6
 if cycles<>v7    print "Restoring cycles from ",cycles," to ",v7
 if tempdir$<>s1$ print "Restoring tempdir$ from ",tempdir$," to ",s1$
 if soupdir$<>s2$ print "Restoring soupdir$ from ",soupdir$," to ",s2$
 if topdir$<>s3$  print "Restoring topdir$ from ",topdir$," to ",s3$
 if vss<>viewshift viewshiftsave=viewshift rem if viewshift changed save new
 xdim=v1:ydim=v2:length=v3:mincrelen=v4
 coresize=v5:processes=v6:cycles=v7
 tempdir$=s1$:soupdir$=s2$:topdir$=s3$
 e=validateparms(1)   rem make sure parms are valid
 if e print "Error - must load valid settings":goto enterinifilename
 input "(press enter to continue)" a$
 if benchdir$<>s4$ resetbenchstuff()
 goto otherstuffmenu rem back to other stuff menu to inspect/change
elseif selection=3 or selection=4 then rem Analyze the soup, if 4 bench too
 addlistings=0:addsoupdisplay=0:addfullbench=0:savefname$=""
 again=1  rem determine if to analyze the soup again
 if anavalid and selection=4 and lastselection=4 then rem if no change
  e=open(1,tempdir$+"/anareport.txt","r") rem check if report exists
  if e then
   close #1 rem prompt to redisplay existing
   input "Redisplay existing anareport.txt file? " t$
   t$=left$(trim$(upper$(t$)),1)
   if t$="Y" again=0
  endif
 endif
 if again then
  if selection=4 then
   if nbenchwars=0 print "(no benchmarking)":sleep 2:goto otherstuffmenu
   print "Report setup... answer C to questions to cancel"
   input "Include listings and benchmarks? " t$
   t$=left$(trim$(upper$(t$)),1):if t$="C" goto otherstuffmenu
   if t$="Y" then
    rem soup must be saved first to include listings
    if soupchanged print "(save soup first)":sleep 2:goto otherstuffmenu
    addlistings=1
    print "How many sequences to list and benchmark? ";
    print "(enter for ",maxrepsave,")";: input " " t$
    t$=trim$(t$):if left$(upper$(t$),1)="C" goto otherstuffmenu
    if t$<>"" maxrepsave=val(t$)
   endif
   input "Include soup display dump? " t$
   t$=left$(trim$(upper$(t$)),1):if t$="C" goto otherstuffmenu
   if t$="Y" addsoupdisplay=1
   input "Include full benchmark listing? " t$
   t$=left$(trim$(upper$(t$)),1):if t$="C" goto otherstuffmenu
   if t$="Y" addfullbench=1
   label enterreportfname
   savefname$=""
   input "Save report to a txt file? " t$
   t$=left$(trim$(upper$(t$)),1):if t$="C" goto otherstuffmenu
   if t$="Y" then
    print "Report file [path/]basename (default ",topdir$,"/report)";
    input ": " t$:t$=trim$(t$):if t$="" t$="report"
    savefname$=t$+".txt"    rem always save to txt to help avoid accidents
    z=instr(savefname$,"/") rem check to see if path specified
    if z=0 savefname$=topdir$+"/"+savefname$ rem if not default to topdir$/
    e=open(1,savefname$,"r") rem check to see if it exists
    if e then
     close #1:input "File exists. Overwrite? " t$
     t$=left$(trim$(upper$(t$)),1):if t$="C" goto otherstuffmenu
     if t$<>"Y" goto enterreportfname rem reenter filename
    endif
    print "Will save report to file ",savefname$
   endif
  endif rem selection 4 setup options
  print "Analyzing... ";
  if selection=4 print "(this will take awhile, esc to cancel)";
  print:tflg=0:nseq=0:score=0:avg=0:hidecursor()
  for x=1 to xdim
   for y=1 to ydim
    if selection=4 and nbenchwars>0 then rem if doing full benchmark
     t$=system$(tempdir$+"/inkey.sh -t"):if t$=chr$(27) goto otherstuffmenu
     print "Benchmarking warrior ",warriorname$(x,y)
     print esc$,"A"; rem move cursor up
     f$=tempdir$+"/temp.red"
     writewarrior(f$,x,y):score=0
     for i=1 to nbenchwars
      battlewarriors(f$,benchwarriors$(i),bmrounds,benchpseq)
      score=score+sc1
     next i
     score=score/nbenchwars:benchscores(x,y)=score
     avg=avg+score
    endif
    seqtmp$="":sc$=getwarriorbasechar$(x,y)  rem get soup seq display char
    for i=1 to wlength(x,y)     rem get the actual instruction sequence
     seqtmp$=seqtmp$+upper$(left$(soup$(x,y,i),3))+" "
    next i
    if nseq=0 then rem first entry
     nseq=1:seq$(nseq,1)=sc$:seq$(nseq,2)=seqtmp$:seqcounts(1)=1
     seqtops(1)=score:seqnames$(1)=warriorname$(x,y)
    else rem search to see if already an entry
     sm=0 rem match flag
     for i=1 to nseq
      if seq$(i,2)=seqtmp$ sm=i
     next i
     if sm then rem if a match
      seqcounts(sm)=seqcounts(sm)+1 rem increment seq pop count
      if score>seqtops(sm) then  rem update top score for sequence
       seqtops(sm)=score:seqnames$(sm)=warriorname$(x,y)
      endif
     else
      if nseq<maxseq then
       nseq=nseq+1  rem new entry
       seq$(nseq,1)=sc$:seq$(nseq,2)=seqtmp$:seqcounts(nseq)=1
       seqtops(nseq)=score:seqnames$(nseq)=warriorname$(x,y)
      endif
     endif
    endif
   next y
  next x
  avg=avg/(xdim*ydim) rem adjust average score
  print:showcursor()
  rem output temp list
  rem don't use file numbers 1 or 2
  e=open(12,tempdir$+"/anatmp.txt","w")
  if e=0 myerror("Can't open anatmp.txt file")
  for i=1 to nseq
   print #12 left$(str$(seqcounts(i))+"    ",5);
   if selection=4 then
    print #12 " best ",left$(str$(seqtops(i))+"    ",5)," by ",seqnames$(i);
   endif
   print #12 "  \"",seq$(i,1),"\"  ",left$(seq$(i,2),40);
   if len(seq$(i,2))>40 print #12 "...";
   print #12
  next i
  close #12
  rem use system commands create sorted report...
  a$="Total of "+str$(xdim*ydim)+" warriors":z=viewmode:if z<2 z=2
  if selection=4 a$=a$+", average score = "+left$(str$(avg)+"    ",5)
  a$=a$+chr$(10)+"Using characters for viewmode="+str$(z)
  a$=a$+" with viewshift="+str$(viewshift)
  a$=a$+chr$(10)+"Found "+str$(nseq)+" unique instruction sequences..."
  e=system("echo \""+a$+"\">"+tempdir$+"/anareport.txt")
  if e myerror("Can't write to anareport.txt file")
  e=system("sort -gr "+tempdir$+"/anatmp.txt>>"+tempdir$+"/anareport.txt")
  if e myerror("Can't sort anareport.txt file")
  if selection=4 then
   e=open(5,tempdir$+"/anareport.txt","a")
   if e=0 myerror("Can't open anareport.txt for append")
   print #5 ""
   if addlistings then
    print #5 "------------------------------------------------------"
    print #5 ""
    print #5 "Top warriors from the top ",maxrepsave," sequences..."
    print #5 ""
    rem open beginning part of the file for read
    e=open(3,tempdir$+"/anareport.txt","r")
    if e=0 myerror("Can't reopen anareport.txt for read")
    line input #3 a$:line input #3 a$:line input #3 a$
    for wc=1 to maxrepsave rem loop through top categories
     if eof(3) break rem shouldn't happen but if it does
     line input #3 a$:if mid$(a$,7,5)<>"best " break rem no more
     b$=mid$(a$,21):b$=left$(b$,instr(b$," ")-1) rem base name
     c$=soupdir$+"/"+b$+".red" rem soup filename
     print #5 "------- Pop.Cnt. ",a$ rem append the analyze line
     e=open(4,c$,"r"):if e=0 myerror("Can't read soup warrior")
     while not eof(4) rem append warrior
      line input #4 t$:print #5,t$
     wend:close #4
     print #5 "------------------------"
     rem append benchmark report
     makebenchreport(c$,b$) rem write to tempdir/results.txt
     e=open(4,tempdir$+"/results.txt","r")
     if e=0 myerror("Can't read bench report")
     while not eof(4) rem append benchpark report
      line input #4 t$:print #5,t$
     wend:close #4
     print #5 ""
    next wc:close #3
   endif rem add listings
   if addsoupdisplay then
    for x=1 to xdim:print #5 "-";:next x:print #5 ""
    rem dump soup display, no color don't bother with border
    v1=viewmode:v2=ansimode rem save to restore after dump
    ansimode=0:if v1<2 v1=2 rem force modes
    for y=1 to ydim:for x=1 to xdim:printwarrior(x,y,5)
    next x:print #5 "":next y
    viewmode=v1:ansimode=v2 rem restore modes
    for x=1 to xdim:print #5 "-";:next x:print #5 ""
   endif rem add soup display
   if addfullbench then
    print #5 ""
    print #5 "Full benchmark report..."
    for x=1 to xdim
     for y=1 to ydim
      print #5 warriorname$(x,y),"  ";
      print #5 left$(str$(benchscores(x,y))+"    ",5),"  ";
      seqtmp$="":sc$=getwarriorbasechar$(x,y)
      for i=1 to wlength(x,y)
       seqtmp$=seqtmp$+upper$(left$(soup$(x,y,i),3))+" "
      next i
      print #5 "\"",sc$,"\"  ",left$(seqtmp$,40);
      if len(seqtmp$)>40 print #5 "...";
      print #5
     next y
    next x
   endif rem add full bench
   close #5
  endif rem selection 4
 endif rem again
 rem list the report...
 e=system(lister$+" "+listeropts$+" "+tempdir$+"/anareport.txt")
 if e print "List failed, check lister settings"
 if lister$="cat" input "(press return to continue) " t$
 lastselection=selection  rem global to persist between runs
 if selection=4 then rem save full report
  anavalid=1 rem set analysis valid flag
  if savefname$<>"" then
   print "Saving to ",savefname$
   e=system("cp "+tempdir$+"/anareport.txt "+savefname$)
   if e print "Error saving "+savefname$+" file"
   sleep 2
  endif
 endif
 goto otherstuffmenu
elseif selection=5 then rem directly change parameters
 print "Enter settings as variable = value (quote strings, will echo back)"
 print "Soup/core/temp vars will be ignored. Enter an empty line when done."
 label reenterparms
 rem save variables that should not be changed...
 v1=xdim:v2=ydim:v3=length:v4=mincrelen:v5=coresize:v6=processes
 v7=cycles:s1$=tempdir$:s2$=soupdir$:s3$=topdir$:s4$=benchdir$
 iflag=1:mflag=1:a1flag=1:a2flag=1 rem for handling vars that append
 repeat
  line input a$:a$=trim$(a$):if a$<>"" parseiniline(a$)
 until a$=""
 e=0:if xdim<>v1 or ydim<>v2 or length<>v3 e=1
 if coresize<>v5 or processes<>v6 or cycles<>v7 e=1
 if tempdir$<>s1$ or soupdir$<>s2$ or topdir$<>s3$ e=2
 if e=1 print "(can't change soup or core parm)":sleep 2
 if e=2 print "(can't change soup/top/temp dir)":sleep 2
 rem restore variables
 xdim=v1:ydim=v2:length=v3:mincrelen=v4:coresize=v5:processes=v6
 cycles=v7:tempdir$=s1$:soupdir$=s2$:topdir$=s3$
 print "Checking settings...":e=validateparms(1):sleep 2
 if e print "Errors detected, correct them...":goto reenterparms
 if benchdir$<>s4$ resetbenchstuff()
 goto otherstuffmenu
elseif selection=6 then rem display the contents of a variable
 repeat
  line input "Enter variable name: " a$:a$=trim$(a$)
  if a$<> "" then  rem error-chech name to avoid program error
   e=0:if instr(a$," ") e=1 rem can contain no spaces
   t$=upper$(left$(a$,1)):if t$<"A" or t$>"Z" e=1  rem must start with letter
   t$=upper$(right$(a$,1))   rem must end with letter number $ or _
   if (t$<"A" or t$>"Z")and(t$<"0" or t$>"9")and(t$<>"$")and(t$<>"_") e=1
   for i=2 to len(a$)-1      rem check all other chars
    t$=upper$(mid$(a$,i,1))  rem must be letter, number or _
    if (t$<"A" or t$>"Z")and(t$<"0" or t$>"9")and(t$<>"_") e=1
   next i
   if not e then
    varsubcount=varsubcount+1 rem increment sub count
    subname$="printvar"+str$(varsubcount)
    code$="sub "+subname$+"():print "+a$+":end sub"
    compile(code$):execute(subname$) rem that's just scary :-)
   else print "Invalid variable name" endif
  endif
 until a$=""
 goto otherstuffmenu
elseif selection=7 or selection=8 then rem dump all parameters
 print "Generating parameters file..."
 e=open(2,tempdir$+"/parms.ini","w")
 if e=0 myerror("Can't open parms.ini for write")
 if selection=8 then
  print #2 "REM ----- yabevolver ini file -----"
  print #2 "REM created ",mid$(date$,3,10)+" "+left$(time$,8)
 endif
 print #2 "header$    = \"",header$,"\""
 print #2 "author$    = \"",author$,"\""
 print #2 "comment$   = \"",comment$,"\""
 print #2 "tempdir$   = \"",tempdir$,"\""
 print #2 "soupdir$   = \"",soupdir$,"\""
 print #2 "benchdir$  = \"",benchdir$,"\""
 print #2 "topdir$    = \"",topdir$,"\""
 print #2 "xdim       = ",xdim
 print #2 "ydim       = ",ydim
 print #2 "xwrap      = ",xwrap
 print #2 "ywrap      = ",ywrap
 print #2 "length     = ",length
 print #2 "coresize   = ",coresize
 print #2 "processes  = ",processes
 print #2 "cycles     = ",cycles
 print #2 "rounds     = ",rounds
 print #2 "mincrelen  = ",mincrelen
 print #2 "instrate   = ",instrate
 print #2 "modrate    = ",modrate
 print #2 "addrrate   = ",addrrate
 print #2 "numrate    = ",numrate
 print #2 "adjrate    = ",adjrate
 print #2 "localrate  = ",localrate
 print #2 "reprate    = ",reprate
 print #2 "insrate    = ",insrate
 print #2 "duprate    = ",duprate
 print #2 "swaprate   = ",swaprate
 print #2 "delrate    = ",delrate
 print #2 "mutadjust  = ",mutadjust
 print #2 "mutadjgen  = ",mutadjgen
 print #2 "orgrate    = ",orgrate
 print #2 "org0rate   = ",org0rate
 print #2 "crossrate  = ",crossrate
 print #2 "adjlocref  = ",adjlocref
 print #2 "evopseq    = ",evopseq
 print #2 "evalmode   = ",evalmode
 print #2 "evalrate   = ",evalrate
 print #2 "evalgen    = ",evalgen
 print #2 "ssfreq     = ",ssfreq
 print #2 "superror   = ",superror
 print #2 "benchfreq  = ",benchfreq
 print #2 "savethr    = ",savethr
 print #2 "savepc     = ",savepc
 print #2 "bmrounds   = ",bmrounds
 print #2 "benchpseq  = ",benchpseq
 print #2 "reinsfreq  = ",reinsfreq
 print #2 "unibox     = ",unibox
 print #2 "viewmode   = ",viewmode
 print #2 "viewshift  = ",viewshift
 print #2 "ansimode   = ",ansimode
 print #2 "enableC    = ",enableC
 print #2 "lister$       = \"",lister$,"\""
 print #2 "listeropts$   = \"",listeropts$,"\""
 print #2 "listeroptsb$  = \"",listeroptsb$,"\""
 print #2 "pmars$        = \"",pmars$,"\""
 print #2 "pmarsv$       = \"",pmarsv$,"\""
 print #2 "pmarsvopts$   = \"",pmarsvopts$,"\""
 print #2 "pmarsvrounds  = ",pmarsvrounds
 print #2 "instructions$ = \"",instructions$,"\""
 print #2 "modifiers$    = \"",modifiers$,"\""
 print #2 "addr1modes$   = \"",addr1modes$,"\""
 print #2 "addr2modes$   = \"",addr2modes$,"\""
 print #2 "enablepreseed = ",enablepreseed
 print #2 "initialinstr$ = \"",initialinstr$,"\""
 close #2
 if selection=7 then
  e=system(lister$+" "+listeropts$+" "+tempdir$+"/parms.ini")
  if e print "List failed, check lister settings":sleep 2
  if lister$="cat" input "(press enter to continue) " a$
 elseif selection=8 then
  print "Name for .ini file (default ",topdir$," directory)";
  input ": " a$:a$=trim$(a$)
  if a$<>"" then
   a$=a$+".ini"
   if instr(a$,"/")=0 a$=topdir$+"/"+a$ rem if path not spec'd use topdir$/
   t$="Y":e=open(1,a$,"r")
   if e close #1:print "File ",a$," exists. Overwrite";:input "? " t$
   t$=left$(trim$(upper$(t$)),1):if t$="Y" then
    print "Saving to ",a$:e=system("cp "+tempdir$+"/parms.ini "+a$)
    if e print "Error saving "+a$+" file":sleep 1
    sleep 1
   endif
  endif
 endif
 goto otherstuffmenu
endif rem any other selection returns to main menu
end sub

sub resetbenchstuff()
local e,a$,t$
initbenchwarlist()
e=open(1,topdir$+"/topscore.file","r")
if e then rem topscore file exists, prompt to remove it
 line input #1 a$:close #1
 print "Existing top score: ",a$
 input "Reset the top score? " t$
 t$=left$(trim$(upper$(t$)),1)
 if t$="Y" then
  print "Removing topscore file and resetting top score"
  system("rm "+topdir$+"/topscore.file")
  topscore=0:topwarrior$=""
 endif
 sleep 1
else print "No top score":sleep 1
endif
end sub

REM Changes...
REM (230124+) initial experiments with no UI, evolved until stopped
REM (230129+) added/refined UI for listing, benchmarking, battling warriors
REM (230205+) added/refined "other" menu with tools settings and stuff
REM 230209 first fairly stable version with UI
REM 230212 fixed anareport redisplay logic, loading bad INI no longer
REM        exits but reprompts, added mutadjust and mutadjgen options
REM 230213 added viewmode and viewshift options for tweaking display
REM        added shift-V to toggle viewshift between spec'd and random
REM 230214 added ANSI dump and playback, on other menu
REM 230217 tweaked soup display and ANSI dump code, added F S P L keys
REM        to ANSI playback for faster slower pause loop (requires awk)
REM        uses unicode soup box in ANSI dumps if unibox option set
REM 230225 added normalizenumbers sub to ensure that only backrefs within
REM        the warrior are negative, all other numbers mod coresize
REM 230303 added warrior listings, benchmarks and soup dump to other opt.4
REM        soup bench report with prompts to control what's included
REM        added detection for identical scores to avoid functional dups
REM        ini/report saves now only add topdir$/ if no path/ specified
REM
REM License...
REM YabEvolver is Copyright (c) 2023 Terry Newton (WTN)
REM Do whatever you want to want to do with this code provided
REM that whatever you do with this code does not prevent me or
REM anyone else from doing whatever we want to do with this code.
REM THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
REM EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
REM MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
REM IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
REM CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
REM TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
REM SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

About YaBasic

I like this language! FreeBasic is ok too but with YaBasic I can just edit and run without a compile step, # is a comment character so it can be treated like a scripting language, and I like being able to just write code without having to declare variables subs etc but it does have LOCAL for declaring local variables in subs. No need for global etc, if not declared local it's global. Although it claims to be an interpreter it seems to be quite fast and does compile the code to an internal format. When a program is run it reads it completely into memory, so unlike shell scripts the program can be safely edited while it is running.

The syntax is a bit different - IF THEN ELSE constructs must have the ENDIF, for simple single-line conditions THEN is omitted and ELSE is not supported. Some functions are a bit different, it's RAN(maxval) instead of RND(1)*maxval, UPPER$ and LOWER$ instead of UCASE$ and LCASE$, MOD(x,y) instead of x MOD y (same with XOR and some others) and the print and file open commands are a bit different. Thankfully there is no ON ERROR GOTO for error handling like the old QBasic, instead file operations return error codes for error checking without disturbing the program flow. It still has GOTO which comes in handy for redisplaying a menu after doing something or if the operation is cancelled, or repeating an entry if an error is detected. YaBasic only allows branching within the same sub level, can't just go anywhere, but unlike some basics branching out of a loop early is fine and a recommended usage.

This is not a bug, just a quirk with reasons - the screen handling commands require that the CLEAR SCREEN command be run first to initialize certain ncurses-related things, usually in programs like this I do my own screen handling using ANSI codes however one of the functions disabled by not using clear screen is INKEY$, kinda need that, and after using clear screen to enable keystroke input, ANSI codes no longer work and all screen handling must be done using YaBasic's commands for those purposes. Not complaining since this helps maintain compatibility between the Windows and Linux versions. I'm used to doing my own screen stuff using ANSI and some of the stuff I need to do doesn't look that easy to do with YaBasic's commands (cursor on/off, clear to end etc), so instead using a shell script solution for getting keystrokes...

#!/bin/bash
to="";key="";if [ "$1" = "-t" ];then
to="-t .001";if [ "$2" != "" ];then
to="-t $2";fi;fi;read $to -sn 1 key
if [ $? = 0 -a "$to" != "" ]&&[ "$key" = "" ];then
echo -n "[extra]";else echo -n "$key";fi

When called using code like a$=system$("pathto/inkey.sh") it waits for the user to press a key then prints it so that YaBasic can pick it up. Keyboard echo is suppressed. When called using code like a$=system$("pathto/inkey.sh -t") it returns immediately (after 1 second), if no key is in the key buffer then it returns an empty string. Add a number after the -t option to specify the number of seconds to wait. Works like a charm except for two things.. 1) it can wait for space to be pressed for press-any-key situations but due to the way shell script works the script cannot return a space character back to YaBasic, and 2) keyboard echo is suppressed only when actually running, if a key is pressed while yabasic is running code the terminal will echo it, possibly disrupting the display. The only way I've found to fix that is to do system("stty -echo") to turn off echo and system("stty echo") to turn it back on but if doing that then best to also do on interrupt continue to disable control-C handling or you may find yourself at a terminal prompt that doesn't echo until blindly entering stty echo to turn it back on. YabEvolver presently doesn't turn off echo because any echoed keys get immediately overwritten without much effect.. except for enter which can disrupt the display. To fix that added code to the inkey.sh script so that if -t is specified but it can't figure out what was pressed (space enter tab etc) then it returns "[extra]" which YabEvolver detects and repaints the display.

About the YabEvolver program

The evolving operations are a hodgepodge of whatever came to mind as I was writing the code, not necessarily proper or useful. All the operations have variables to disable or control how frequently the operation is performed. The swap operation is brutal - it swaps two lines in the parent warrior before evolving, usually killing the winning warrior in the process. Did it that way because it was easy (harder to swap when streaming forward), tempted to revisit that but oddball random operations like that can be part of the magic so probably should leave it be. The way it does crossover is goofy.. as it's evolving the winner to the loser position it randomly swaps the source line with a surrounding warrior of the same species if one's around. The dup line operation doesn't change the length, it just swaps the current line with a copy of the previous or following line - in my previous evolvers usually dup was part of the insert operation but in this evolver the insert operation always adds a new random line when triggered. The delete operation simply doesn't write the current source line, in effect deleting that line. When inserting or deleting lines, if the adjlocref setting is set then it attempts to adjust the number fields to preserve references and loops but the way it's implemented is probably not that useful, just something else to scramble the code.

When evolving number fields the program randomly selects between a new random number and incrementing or decrementing the existing number according to the adjrate variable. If a random number is selected it is weighted towards small +/- number according to the localrate variable. In corewar the core wraps around so in the case of nano warriors, -1 is the same as 79. The program makes some attempts to limit the magnitude of negative numbers to no more than the max warrior length. When saving top-scoring warriors the program does a simple hash to avoid saving duplicates but it presently doesn't attempt to distinguish between modulo locations so a few dupes might slip through.

There are two hashing functions in the program, a simple but fast hash that reduces the base instruction sequence to a single character for the soup display (collisions are inevitable), and a more elaborate 6-digit hash for detecting duplicates. Writing hashing code was surprisingly difficult, spent way too much time on such a seemingly trivial thing. What I come up with is ugly, fiddled with it until it returned mostly random numbers for variations, but works well enough for what I need it to do.

Instructions, instruction modifiers and the A and B address modes are weighted by including duplicate entries in the strings, a lot easier to do it that way than to (somehow) specify individual probabilities, and more intuitive. Weighting only determines the probability of selecting an item, if the soup favors another kind of code then weighting won't make as much difference.

YabEvolver's INI file format is the same as the default settings portion of the program code.. rem for comments, lines containing variable = value or variable$ = "value". Only instruction specifier lines can span multiple lines using the form variable$ = variable$ + "value", for most string settings anything between '=' and the first quote is ignored.

Originally the program had to be restarted to reload different INI file parameters but added a feature that lets an INI be loaded without restarting (assuming the INI is valid otherwise it exits with an error). That was a bit tricky.. pmars and soup parms and the directory names (other than the bench dir) cannot change or undesirable things happen. Then I rigged it so that individual settings can be changed on the fly (because that's handy to do) but that part was even trickier - it reuses the INI verify code which normally exits if there's an error but that would be unfortunate for a simple typo, so added a "noexit" flag to the verify sub so if an error occurs it complains then prompts to fix the error and won't continue until it passes the parms check. Good enough, would be too much code to try to automatically recover from bad parms.

Running the program

To run YabEvolver you will need to have pmars and pmarsv binaries in a path directory (or bin64 directory if using my run script), and yabasic needs to be installed. YaBasic is available from the Ubuntu repository or get from the link in the program code. Just about any pmars/pmarsv binaries will work so long as the Permutate option is available, by default it uses that for benchmarks for consistent scores. The program defaults are for the old X11 pmarsv 0.9.2 version, settings for the SDL version are commented out. Binaries and source for my patched versions of pmars/pmarsv and the SDL version are linked before the program listing.

To perform benchmarking and use benchmark-guided evaluation modes it needs a set of test warriors. As written the program looks in a directory named "nano2301" for the nano2301 test set of 30 warriors from the recent Koenigstuhl recursive nano hill. Another test set I've been using is nano106 consisting of 106 older and newer nano warriors. Settings can be changed by editing the INI section of the program code, or copying the INI section to an .ini file and editing that and specifying it on the command line when running the program.

To run YabEvolver, save the program code to a file named say "yabevolver.yab" in its own directory, copy the nano2301 directory there (or whatever test set you are using) and at a terminal in the program's directory enter: yabasic yabevolver.yab

Add a filename to the command to specify an INI file to use instead of the default settings.

To make things easier I use run scripts based on the script listed below in the helper scripts section (requires xterm and awk), each copy is edited for a unique INI file and window title, the script checks the INI file for xdim and ydim and automatically sizes the xterm window to fit the soup display. Note - if running multiple experiments at once, each INI file must specify different directories for tempdir$ soupdir$ and topdir$ or there will be problems.

The program creates the following directories and files when it runs...

/dev/shm/yabevoltmp - default temp file directory, change tempdir$ to use another directory.
The following files are created in the temp directory...
inkey.sh - a script for getting keystrokes
a.red and b.red - the two warriors doing battle, these files are overwritten constantly
temp.red - the warrior being listed or benchmarked
results.txt - benchmark results
anareport.txt - sequence or benchmark report
anatmp.txt - unsorted sequence/benchmark report
parms.ini - used when listing all settings
warfile.tmp - used when removing lines from the top/warinfo.fil file.
Temp files are not removed when the program exits.

soup - default soup directory, change soupdir$ to change. Soup warriors are saved here.

top - default top-scoring warrior directory, change topdir$ to change.
In addition to warriors it also writes the following files to the top directory...
warinfo.fil - contains a list of warriors saved to the top directory with their benchmark scores
topscore.fil - contains the top score and warrior
Settings .ini files and sequence/benchmark reports are saved to the top directory by default.

The User Interface

When the program starts it reads the settings file if one specified on the command line, checks the parameters, creates/verifies directories and initializes the benchmark warrior list, if you need to read the status messages press esc right after running, otherwise it loads or creates the soup and displays the main menu (using plain ascii screendumps in this section, normally it's in color with neat unicode borders)...

.-----------------------------------------------------------------------------.
|23543222422322254532355554435522535433245343354534235533343323423242242435234|
|52423352225542325322324453245442354532234322432545232234355424525433533225325|
|25343232355343423235352345254555233354443232253252355235552442422253322434322|
|33422554224533542433223333243454325353533232433243253444524422422235355353252|
|44554323445235542244555552344343355453543444444433345553552235353535354325443|
|33522524435435453554355555553234424343452245352424524245344235543445523353555|
|35354354235333443443442242423355524553322534223553453444445543323525432353543|
|54334345453543324424433322535455554343555453522452345244354352554442332325445|
|25552543523525523353434234525232552234353535523344355244355335433534543234343|
|52225552232425243343322455242542342435523232252434244254255333332434353224433|
|52444532233344243332525345553432354423444354322344452554324452355553242433422|
|53252532422232455242522332233342222525443355244325322253554535453352542535225|
|54245334444243344523255254425333523444255343243324522545544424422235355324433|
|43432325233454532242452424532342235523454535443445522354434534452423434444534|
|24252542244243544422525535442543224424424234253442445443523522243253554433533|
|52554352442352444553423433555243333333345352432543242532524555252242554355342|
|42325252325424422543452432452354422225322453545523453225353542243235434432433|
|32433424452344254244255244455452355354452525342222222524425334345555353233435|
|44424232434222325532543534524324552522245242535255524454455324245343245432425|
|43342345435553552445422534224425225222532445554554234223245344345432523232343|
`-----------------------------------------------------------------------------'
[E]Evolve [L]List [B]Bench [0-9]Battle [V]View [O]Other [S]SaveSoup [X]Exit

Press E to start evolving. After a short time the warriors start "clumping"...

.-----------------------------------------------------------------------------.
|34433333333333333332244444222444444443442332222555554444554444555555555555555|
|34333333333533333332222224222244444443443232222555545445555445455555555555555|
|34333333353453335333222225424444444444444432222525554445555545555554555555555|
|33333333555455353332222444444444444444243433322222544444555555555555555555555|
|33333332255553533332224244333444444444422333222222244445555555555555555555555|
|33333333455555553323225443343444344424222222222225444445555555555555555555555|
|33333335522555233232255543333453444422422222222252545455555555555445554445445|
|33333335352255523355555555334333333442222222222222555555555555555444554445555|
|33333333532555555455555555333333333442222222222552555555555555555545455445455|
|33355533333555554555555555333333334444422222255225255555555555555555555555555|
|55455353333555555455555555533333334442222252555225555555555555555555555544555|
|55545543333555555555555555333333333442252525555555555555555555555555455545455|
|55444553333345554555555555353333333432445555555555555555555555555555445554455|
|55444545333333555445555555553333333345255554555555555555555555555554545454444|
|55555553553333555555555553532333333454555555455555555555555555555545454444444|
|55545555333333355555544555532233333335555555555555555555555555555555544444443|
|55555555333333535555554545553323333335555555555555555555555555555554544444445|
|55555535535535555554445555553333333355555555555555555555555555555555555554455|
|55555555333555555544445554533353332555555544554455555555555555555555555555555|
|35555555543555555544455555553332233535555555444555555555555545535555555555555|
`-----------------------------------------------------------------------------'
28_02 vs 28_03- 134 128 12_06 scores 40.56 top=61.57 (esc to stop)

Press esc to stop evolving and return to the menu...

.-----------------------------------------------------------------------------.
|33333335554455555555555533343333343344445555544545555555444554444444444554554|
|43343353544555555545545353333333434344555555554455555553445544454444444445544|
|34443355535555555555555555333333334334355555555555555533345554544444445455555|
|45444535555555553335555555333335335333555555555555455333344454444454455555555|
|44444455555455355355555555335353533533553555544544554333444444445444444545555|
|44444345555435534455555555333353535555555454444445443333333444455544455555555|
|44444543355543434555555553555333355553443544444444553333443445555554455455555|
|44435445555554545455555555553334455553333344444455553334444444455555455555555|
|44445555455553454555555555553333355555353334445555553334444444455555555555455|
|45543455555553545555555544555554535553533544445555535344444455555555555555555|
|55544455555555555555555445455444555533355444544555555355444444445555555555555|
|55444445555454555555555455555545554533355445555555555544454444554455555555555|
|44444555555444555555555555555443555333355555555555555544554445554555555555555|
|44445555554445555555555555555554555333555555555555545554454454444455554555555|
|44445555555544555555555555555555353333555555555555555544444444444455545555555|
|44545555555555555555555555555455533355555555555555555544444444444445554555555|
|55555545555555555545555555555554555355555555554555555444444444544445555555555|
|55555555555555555455555555555555555555555555555455555555445545554445555555555|
|55555555553555545555555555555554554555555555555455555555555554455545555555455|
|55555555533355455555555555555555554455555555554555455555555554455555555555555|
`-----------------------------------------------------------------------------'
[E]Evolve [L]List [B]Bench [0-9]Battle [V]View [O]Other [S]SaveSoup [X]Exit

The default is to display warriors as numbers (or numbers and letters) to represent the warrior's length, press the V key to cycle the soup view mode to use symbols for species (mod 14), letters for instruction sequence (mod 52), or letters, numbers and symbols for instruction sequence (mod 94, fewer collisions)...

.-----------------------------------------------------------------------------.
|!!!!!!!...##$$$&&$&&&&&&!!!~#####~##~~~~~~?**^#;#;...&&&+++?+..........?$@$$;|
|!!!!!!*!.$$$$$$$&&^&&%&!&!######~#~#~~~~~~&*~;##;;..&&&@++??...?......@@@$$@@|
|!!!!!!***!$$$$$$&&&&^^&&@&########~####~~~*~~~;;;&&&&&@@@.??..?...%...^..$;..|
|!#!!!*!***$$*$$$!!!&%^&&@&###~#$##~####~~*~@?&?;;;#&&@@@?...?....*+**??##....|
|!!!!!!**+$$&$'!$$!&&&&&&&&##&#$#~##~##~~#~&@@#~;'';;'@@@........?******#?.&.~|
|!!!!!!@+++!&!?$!&&&#&&&&&&####$#~#~~!~~~~#&~~~~~~;''@@@@@@@....???***?.??..~~|
|!!!!!!@!!!!!&!&!&?&#&&&&&#$&&####~~!~#!!#&###~~~?~;;@^@@..@..!?..??**??*...~~|
|!^!!!@@!!!!*?&?&?&#?$&&&&&&$##.;;~!~~#########~~%;;;@@@.........????*???..%$.|
|^^^^!!!!.!&!.!&?&?&;@$$&&&#$#####~~~~~#$##@#.#++;;;;@@@.......*@?????&??.#!..|
|^$$^$^$!!!&??!!????@@$$$$$$$$$$#~#.~~#$##~@###+;;;;@&^......&&&???'??????....|
|!#$^^^%!!!??&?!&?????@;$$$.;$###.~.~###~~@@@*##?;;%;&^~&........????????.....|
|!!^^^^^%!!!%!;?????&?$;$;;;&;!#~~.!.###~&@@@??@@;&;;%%...$....??.&??????.&...|
|~^^^^!'!!!!;;;??&????&@&;;$;&###~..####~&??&&???;;;;;%.!$$...???;????????....|
|^^^^!!%!!!@;;&&?????$&&&;&;&&&&#~~.###~&.&??&&&?@@+*%%%..$..#!!.;.????!??....|
|^^^^!!!@!!&!;;&&??&?&&&;&@@&&&&&#~####~~&+&&&$&@@?@@%@.......!!...%~?$;;.....|
|^^!^!!!!!&!!!&@&&$&&?&&&&&&&&*&&^###&~~&&&&&&&^@@@@@@@......~.!.+~^?~.'......|
|!!!!!!@!.!!+%&@&&?~*&;&&&&&&&&&#&^&#~&&&&&&&&&#;@@@@@........!...^'?...;.....|
|!!+!!!!!!!!%+@@@&#&&;;;&&&&^&&'&;&&&;~&;&&%&#&&#+++@+%++..$$!....^^..;...+...|
|!!!!!!!!!.!%;;;!&&&;;;&.&&&&&&&!&&;&&&&*&&^&&&&;+++@++%$$$++.;;...^$..;;?.*..|
|!!!@!!!!.!!!;;;;#&&;!;;%&&&?&&~&&&;;&&&&&&&&&&#...%+++++$$+++;;.......;;.....|
`-----------------------------------------------------------------------------'
.-----------------------------------------------------------------------------.
|YYYYYYYqqqUUjijiijiiiiiiYYYyYYYYYEYYEEEEZZdjjycFcFqqqvvvEEEiyUUUUUUUUUUiUoUUw|
|gYYgYYqYqUUiijijiiaiiEiYiYYYYYYYECEYEEZZZZvjZFccFFqqvvvYEEiiUUUiUUUUUUoooUUoo|
|YgggYYqqqYiiqiijiiiiDBiiiiYYYYYYYYEYYcYZZZjZZZFFFvvvvvYYYUiiyUiUUUEUUUjUjUjjj|
|gxgggqYqqqiimijjYYYiMDiiiiYYYqYTYYZYYYkZZjZxbvJFFFcvZYYYgUUUiUUUUUTUUiiUUjjjj|
|ggggggqqxiicirYjjYiiiiiiiiYYiYiYZYYZYYZZYZjxxcyFccxxcYYYUUUUUUUUiUUUUUUUUjjjr|
|gggggYyxxxVcYrjYcciUiiiiiiYYYYiYZYZZjZZZZcjcyyyyyFccYYYYYYYUUUUiiiUUUijiijjrr|
|gggggVyYYVVVcYcYcriNiiiiiYiiiYYYYZZjZYEEYccccyyyyyFFYqYYUUYUUjiiiiiUUiiUjjjrr|
|gcgYVyyVVVVqrcrcrcNriiiiiiiiYYuIIZjZZYYYYYccccyyOFFFYYYUUUUUUUUiiiiiUiiijjrUj|
|ccccVVVVWVxVdYcrcriiUiiiiipiYYYYYZZZZZYxYYgcUcxxFFFFYYYUUUUUUUUJiiiiiiiijikjj|
|cGGcgcGrVVxrrYVsrrriiiiikkiiiiicZYTZZYxYYZecccxFFFFYOYUUUUUUTTiiiiqiiiiiijjjj|
|VxGcccrVVVrrorVUrrrrrirkkisricccTZTZYYYvZeeeCccnFFxxOYTvUUUUUUUUiiiiiiiijjjjj|
|VVcccccFVVVOVGrrrrririrkrrrirbcZZTLTYYYZveeHrnFFxOxxeeUUURUUUUiiUAiiiiiiUojjO|
|OccccVrVVVVGGGrrirrrriiirrgriccYZTTYYYYvvnnvvnnnxxxxxeUqRRUUUiiiUiiiiiiiijjjj|
|cccczVLVVViGGiirrrrrviiiririiiicZZTYYYvvpvnnvvvnwwbIeeeUURUUBEEUUUiiiiUiijjjj|
|ccccVVVhVVUVGGiirririiiricMiiiiiYZYYYYvvvqvvvAcwwvwwawUUUUUUUEEUUUjpisnnjjjjj|
|ccVcVVVVVUVVVuUuiviiriiiiiiiiEiivYYYvvvvvvvvvviwowwwwwUUUUUUqUEUGaEipjNjjjjjj|
|VVVVVVsVbVVbVpUiivEairiiiiiiiiiciviYRvvvvvvvvvkmwowwwUUUUUUUUEcUUEMijjjnjjjjj|
|VVdVVVVVVVVVbUUUiEiirrriiiiEiijitivvTRvRvvRvLvvkbbbwbSbbUURREjjjUEEjjnjjjjjjj|
|VVVVVVVVVVYVrrrsiiirrriAiiiiiiiHiiIvvvvnvvsvvvRUbbRwbbSRRRbbjUUjjjEjjjnnzjUjj|
|VVVVVVVVVYYYrrertiirprriiiiqiiiiiiIIvvvvvvvvvvkbbbGRbbbbRRxbbUUjjjjjjjnnjjjjj|
`-----------------------------------------------------------------------------'
.-----------------------------------------------------------------------------.
|7777777aaa55:/://://////777}77777E77EEEEZZ*::}gPgPaaafffEEE/55555555555/+?++q|
|k77k77a7a55//:/://[//E/7/7777777E!E7EEZZZZf:ZPggPPaafff7EE//555/555555???++??|
|7kkk77aaa7//a//:////Dl////77777777E77g7ZZZ:ZZZPPPfffff7775//i5/555E555:5:+:::|
|krkkka7aaa//3/::777/wD////777?7J77Z777[ZZ:Z|ffJPPPgfP777i555/55555455//++::::|
|kkkkkkaa|//g/l7::7////////77/7/7Z77Z77ZZ7Z:||g}Pgg||g77755555555/555555+5:::l|
|kkkkk7}|||Vg7l:7gg/K//////7777/7Z7ZZdZZZZg:g}}}}}Pgg77777775555///555/://::ll|
|kkkkkV}77VVVg7g7gl/$/////7///7777ZZdZ7EE7gggg}}}}}PP7?77557550/////55//5:::ll|
|kgk7V}}VVVValglglg$l////////77MSSZdZZ77777gggg}}%PPP77755555555/////5///::l!:|
|ggggVVVVWV|V*7glgl//K/////J/77777ZZZZZ7|77ig5g||PPPP77755555555j////////:/;::|
|gGGgigGlVV|ll7Vmlll/////;;/////gZ7JZZ7|77Z?ggg|PPPP7%755555544////a//////::::|
|V|GggglVVVllilVKlllll/l;;/ml/gggJZJZ777fZ???9gg4PP4|%74f55555555////////:::::|
|VVgggggfVVV%VQlllll/l/l;lll/lfgZZJvJ777Zf??^b4PP|%||!!555(5555//5u//////K?::%|
|%ggggVlVVVVQQQll/llll///ll-l/gg7ZJJ7777ff44ff444|||||!5K((555///5////////::::|
|gggg@VvVVV/QQ//lllllp///l/l////gZZJ777ff,f44fff4qq\S!!!55(55bEE555////5//::::|
|ggggVVVnVVKVQQ//ll/l///l/Iw/////7Z7777fffafff7}qqfqq[q5555555EE555:J/m44:::::|
|ggVgVVVVVKVVVGKe/p//l////////E//f777ffffffffff/q?qqqqq555555K5E5Q[E/J:N::::::|
|VVVVVVmVfVVfVjK//pEq/l/////////g/f/7|fffffffff;]q?qqq55555555Eg55Ew/:::4:::::|
|VV*VVVVVVVVVfKKK/E//lll////E//:/0/ff4|f|ff|flff;\\\q\)\\55((E:::5EE::4:::::::|
|VVVVVVVVVV7Vlllm///lll/7///////R//Iffff4ffcfff|+\\(q\\)(((\\:55:::E:::44@:5::|
|VVVVVVVVV777ll+lD//l6ll////a//////IIffffffffff;\\\G(\\\\((|\\55:::::::44:::::|
`-----------------------------------------------------------------------------'
[E]Evolve [L]List [B]Bench [0-9]Battle [V]View [O]Other [S]SaveSoup [X]Exit

Press the S key to save the soup without exiting. To avoid unneeded disk writes the save option is available only if the soup has changed since the last save, otherwise (if enableC is set) the menu changes to...

 [E]Evolve [L]List [B]Bench [0-9]Battle [V]View [O]Other [C]Colors   [X]Exit

The C key is still available when SaveSoup is displayed, there just wasn't enough room to list the option. Press C to cycle between different color schemes - none, 16-color ANSI, 256-color ANSI or 24-bit RGB ANSI. The default can be specified in the program or INI file in the 'ansimode' setting.. 0 16 256 or 24. Make the 'enableC' setting 0 to disable color cycling, not all terminals support RGB ANSI and that might be jarring.

The V and C keys can also be pressed while evolving to change the view and colors without going back to the menu.

Use the cursor arrow keys to move around the soup display, the Exit menu text will be temporarily replaced with the warrior coordinates under the cursor. Press L to list the selected warrior...

;redcode-nano
;name 23_06.red
;author yabevolver
;strategy evolved
;origin 15_09
;species 74277
;generation 51
;created 02-09-2023 18-55-54
;assert CORESIZE==80
ORG 0
SPL.F # 1,$ 71
MOV.I # 19,> 3
MOV.I { 59,> 2
MOV.B @ 1,{ -1
DJN.I $ -3,} 1
END
/dev/shm/yabevoltmp/temp.red (END)

By default text files are listed using the 'less' utility, press Q to exit back to the program. Edit the INI settings lister$ listeropts$ and listeroptsb$ to use another lister, or replace lister$ with "cat" for something simple.

Press B to benchmark the selected warrior...

Crisis                    59.154  *******
[RS] C-Strobia 108.45 **************
eerie glow 82.394 **********
Free the Fish! 106.33 **************
Hot Soup 46.478 ******
Just a MEVO Thing 100.70 *************
Little Bang 96.478 ************
Little Red Rat 136.62 ******************
7_3 49.295 ******
NanoCracker 44.366 *****
02_08 134.50 *****************
[RS] Nextratulated Sturvi 67.605 *********
Nonlocality 2 84.507 ***********
Nonlocality 95.070 ************
Quantum Foam 76.760 **********
Reflow Oven 52.112 ******
Sleepy Lepus 80.985 **********
Steaming Pebbles 31.690 ****
SWT2-21 90.140 ************
SWT2-28 84.507 ***********
Transfer Function 73.943 *********
Unholy Fire 102.11 *************
Benchmark score: 76.690
/dev/shm/yabevoltmp/results.txt (END)

...not very strong yet, this soup has only been running for a few minutes. Generally to be competitive on an actual hill, a redcode warrior needs to score 145 or more against a test set that represents the kind of warriors on that hill.

Press a number key to run the selected warrior in pmarsv, press 1,4,7,8,9,6,3 or 2 on the number keypad to battle a surrounding soup warrior, press 5 to run the warrior by itself, or press 0 to battle a randomly selected benchmark warrior. The default pmarsv$ and pmarsvopts$ settings are for the old 0.9.2 version of pmarsv, add -font fixed to pmarsvopts$ if it complains about the font (my patched 0.9.2N3 version is patched to default to fixed). Settings for the SDL version are in the comments, I had to slightly modify it to slow it down, for nano warriors the stock SDL version was way too fast. The pmarsvrounds setting (default 10) determines how many rounds to go in the core, after battling the average scores are printed on the status line and it waits for enter to be pressed.

Press the O key for the "Other" menu where I add various bits I need...

   Other functions...
1 - Change the benchmark set
2 - Load settings file (some parms ignored)
3 - Show soup sequences and population numbers
4 - Sequence and benchmark the entire soup
5 - Change settings variable(s)
6 - Display variable(s)
7 - Display all settings
8 - Save settings to file
9 - Save soup (not saved yet)
10 - Start/stop ANSI dumps
11 - Play back ANSI dumps
Select function or none to exit:

(my menu style may be showing my age...)

Option 1 prompts for a new benchmark directory, if a valid directory containing benchmark warriors is entered it reloads the benchmark warrior names and if there's a topscore.fil file in the top directory, offers to delete it and reset the scores. If nothing is entered then offers to disable benchmarking (might take that out now that settings can be changed directly).

Option 2 prompts for a settings filename, include the extension. If the file exists and looks valid then it loads it. Settings for xdim ydim length mincrelen coresize processes cycles tempdir$ soupdir$ and topdir$ are restored if different in the settings file, changing those settings would require a restart. After loading it verifies the settings, if there is an error in the settings the program will terminate so if option 9 - Save soup is showing use it before loading an experimental INI file.

Option 3 sifts through the soup and lists unique instruction sequences the number of members in each sequence...

Total of 1540 warriors
Found 107 unique instruction sequences...
285 "i" SPL MOV MOV MOV DJN
169 "U" SPL MOV MOV MOV
163 "Y" SPL MOV MOV
110 "j" SPL MOV MOV MOV SPL
97 "V" SPL MOV SPL MOV SPL
84 "r" SPL SPL MOV MOV SPL
75 "c" SPL SPL MOV MOV
74 "v" SPL SPL MOV MOV SUB
62 "Z" SPL MOV MOV DJN MOV
32 "F" MOV SPL SPL MOV MOV
28 "E" SPL MOV MOV DAT
27 "g" MUL SPL MOV MOV
...snip...

Option 4 does the same thing except it performs a full benchmark of the soup. That can take awhile, if you get impatient press esc to cancel. When complete it produces a listing like this...

Total of 1540 warriors, average score = 82.51
Found 107 unique instruction sequences...
285 best 108.5 by 72_10 "i" SPL MOV MOV MOV DJN
169 best 109.6 by 62_19 "U" SPL MOV MOV MOV
163 best 95.19 by 55_06 "Y" SPL MOV MOV
110 best 117.6 by 71_18 "j" SPL MOV MOV MOV SPL
97 best 98.97 by 05_15 "V" SPL MOV SPL MOV SPL
84 best 110.5 by 17_10 "r" SPL SPL MOV MOV SPL
75 best 94.29 by 49_05 "c" SPL SPL MOV MOV
74 best 104.1 by 34_17 "v" SPL SPL MOV MOV SUB
62 best 87.42 by 39_04 "Z" SPL MOV MOV DJN MOV
32 best 90.11 by 49_11 "F" MOV SPL SPL MOV MOV
28 best 105.9 by 68_17 "E" SPL MOV MOV DAT
27 best 82.44 by 05_06 "g" MUL SPL MOV MOV
...snip...

...followed by the benchmark results of all soup warriors. After displaying the report it prompts for a name to save the file to, just press enter to not save, or enter a name without an extension. For file safety reasons the directory is set to topdir$ and the file is saved with a .txt extension. To save to another directory enter ../path/name instead to back out of the top directory. If the soup has been saved and hasn't changed and the report already exists then option 4 redisplays the existing report instead of doing the benchmarks again.

Option 5 permits changing one or more settings without having to load a whole INI file. The entries must follow the same INI format, variable = number or variable$ = "string". The instructions$ setting and other code element strings can span multiple lines by entering instructions$ = instructions$ + " ADD SUB" etc. Press just enter to exit the entry loop and check the settings, if there are any errors it prompts again to fix the errors. It won't exit the entry loop until it passes the checks.

Option 6 displays the contents of a variable. Not only settings but any program variable. Doing that was a bit tricky, generally a BASIC program can't be made to dump its own variables unless specifically coded, but YaBasic has compile and execute, so it actually adds a sub to itself that prints the requested variable. To avoid calamity the entered variable name cannot contain spaces, must start with a letter, and after the first letter only letters, numbers and "_" is allowed besides the "$" at the end to indicate a string.

Option 7 lists all settings variables.

Option 8 runs the same code as option 7 except instead of listing it prompts for an .ini name to save to. Don't add the extension, for file safety the file is saved to the topdir$ directory and it adds .ini to the filename. To save to the program directory instead add ../ in front of the name. As with saving the sequence report this is to help avoid accidently overwriting something important and almost always when saving parms or the report it's specific to a particular run.

Option 9 shows only if the soup has changed since the last save as a reminder, once the soup is saved option 9 goes away.

Option 10 and 11 permit making and playing back ANSI "movies" of the soup progress, handy for overnight runs to see what happened and eye candy in general. When ANSI dumps are enabled, while evolving it will periodically save a snapshot of the soup to a numbered file, up to a hard-coded maximum of 9999 files (4 digit sequence number) to keep things relatively sane. Each snapshot consists of the colorized soup display using the current view mode and colors in a text-mode box (to avoid unicode incompatibilities), followed by the filename, top score and last benchmark result. Separate files are used so the sequence can be played back with a controlled frame delay.

If ANSI dumps is currently enabled, option 10 will show the current dump parameters and prompt to disable, otherwise it  prompts for a base name for the dump files and the directory to put them in (default ansidumps), if ANSI files with the same base name exist (checks for basename0001.ans) then it prompts to Remove or Continue - enter R to remove existing files and start with sequence 0001, enter C to continue dumping at the next consecutive file, or anything else to return to the menu. Next it prompts for how many cycles between dumps, typically a few thousand (if under 1000 it warns about excessive writes), then prompts for the maximum number of dumps, just enter for the max of 9999, -1 to go back to the menu without enabling, or enter the number of dumps to perform before automatically disabling. Once enabled exit the other menu, make sure the view mode and colors are as desired, and start evolving to record - the first frame will be the current soup. You can stop evolving, check the soup etc without disturbing the dumps or interval but if the program is exited then to continue dumping you'll have to set it up again using the continue option, which will probably result in a score or soup glitch in the playback.

Option 11 plays back an existing ANSI dump sequence. If the sequence number is non-zero it assumes a dump has been performed and offers to play back from the existing basename and directory, otherwise or if declined it prompts for the basename and directory to play back - if installed it uses the ansi2txt utility to make sure the current display can handle the dump, if not prints an error and goes back to the menu. Finally it prompts for the delay between frames (default 0.1 seconds) then plays the sequence. While playing press F to go faster, S to go slower, P to pause, L to enable/disable looping, H to redisplay the key choices, or any other key to stop. After stopping or after the last frame if looping is disabled (default) it prompts to play again - press Y to play from the beginning or any other key to exit back to the menu.

Some early results

This is from evolving a 138x37 soup for a couple days...


The larger soup size has helped to maintain more diversity, even after thousands of generations there are still several unique sequences with sizable populations...

Total of 4366 warriors, average score = 127.3
Found 85 unique instruction sequences...
1826 best 142.0 by 008_32 "i" SPL MOV MOV MOV DJN
1660 best 147.2 by 057_36 "q" SPL SPL MOV MOV DJN
894 best 155.3 by 022_19 "x" SPL SPL MOV MOV MOV
467 best 139.2 by 086_35 "U" SPL MOV MOV MOV JMP
203 best 144.9 by 067_14 "r" SPL SPL MOV MOV SPL
33 best 133.9 by 027_08 "c" SPL SPL MOV MOV
15 best 136.9 by 004_19 "Z" SPL SPL MOV MOV ADD
13 best 104.7 by 055_17 "N" SPL SPL MOV SPL MOV
12 best 115.9 by 023_07 "O" DJN SPL SPL MOV MOV
9 best 135.4 by 002_02 "W" SPL SPL MOV MOV JMZ
7 best 106.6 by 055_11 "M" MOV SPL MOV MOV DJN
6 best 138.1 by 021_08 "v" SPL SPL MOV MOV SUB
6 best 127.2 by 004_18 "W" SPL SPL MOV MOV SNE
5 best 136.2 by 090_06 "c" SPL SPL MOV MOV JMP
5 best 125.2 by 002_31 "p" SPL MOV MOV MOV MOV
5 best 124.3 by 003_30 "U" SPL MOV MOV MOV
.....

A look at a few of these and how they perform against current nano hill warriors and the old nanoBM04 test set...

;redcode-nano
;name 008_32.red
;author yabevolver
;strategy evolved
;origin 012_23
;species 74794
;generation 4077
;created 02-05-2023 13-20-02
;assert CORESIZE==80
ORG 0
SPL.BA # 41,< 63
MOV.I } 75,{ 0
MOV.I > 8,{ -2
MOV.I { -3,< 60
DJN.B $ -2,} 14
END

Testing 008_32.red...
Anefam Vesyes 152.11 70 6 ***************
Another MEVO Thing 164.08 77 2 ****************
b69e6908-a5be0bba-ab4445 133.09 60 9 *************
Clone 148.59 68 7 **************
Cold Plasma 140.84 64 8 **************
Combat Arithmetic 136.61 61 11 *************
Creamed Corn 135.91 61 10 *************
Crimson Climber 126.05 57 8 ************
Crisis 150.70 70 4 ***************
[RS] C-Strobia 146.47 66 10 **************
eerie glow 129.57 54 22 ************
Free the Fish! 130.98 58 12 *************
Hot Soup 139.43 63 9 *************
Just a MEVO Thing 171.12 79 6 *****************
Little Bang 154.22 71 6 ***************
Little Red Rat 161.26 76 1 ****************
7_3 139.43 65 3 *************
NanoCracker 121.12 55 7 ************
02_08 147.18 64 17 **************
[RS] Nextratulated Sturv 139.43 65 3 *************
Nonlocality 2 133.09 57 18 *************
Nonlocality 128.16 55 17 ************
Quantum Foam 157.74 73 5 ***************
Reflow Oven 140.14 65 4 **************
Sleepy Lepus 116.90 47 25 ***********
Steaming Pebbles 139.43 63 9 *************
SWT2-21 146.47 65 13 **************
SWT2-28 155.63 69 14 ***************
Transfer Function 152.81 69 10 ***************
Unholy Fire 121.83 51 20 ************
Score = 142.01

Testing 008_32.red...
8c09fc1a-4799259f-174724 164.78 78 0 ****************
rdrc: Alcoholism Malt 138.73 64 5 *************
b1f3ad11-a6073e-f711ff69 155.63 72 5 ***************
Bacillus anthracis 142.95 65 8 **************
Black Sun III 154.22 69 12 ***************
rdrc: Breakaway Carte 132.39 49 41 *************
c82f15b5-85011fd8-5a969d 142.95 65 8 **************
Clear-2 167.60 78 4 ****************
Cosmic Horror 129.57 52 28 ************
Different Day 147.18 68 5 **************
written in a dream 119.01 49 22 ***********
early morning view 105.63 50 0 **********
Escherichia coli 146.47 69 1 **************
e6843-5724-xt430-4-nano- 132.39 57 17 *************
Foggy Maus (beta) 135.21 62 6 *************
girl from the underworld 138.02 62 10 *************
human resistance 147.88 66 12 **************
the kraken awakes 138.73 51 44 *************
Leishmania deanei 145.77 66 9 **************
looking glass 155.63 73 2 ***************
lord of the digital swam 119.01 49 22 ***********
Man&Machine 162.67 74 9 ****************
Military Grade Nano 168.30 76 11 ****************
New Vera City 134.50 62 5 *************
Obsidian peasoup 145.77 68 3 **************
Plasmodium vivax 159.15 73 7 ***************
qEvo[[3]] 173.23 77 15 *****************
realm of the fire flower 106.33 43 22 **********
Red Moon 154.92 72 4 ***************
rdrc: Repent Linemen 160.56 76 0 ****************
Rocket propelled monkey 155.63 70 11 ***************
rumpelstiltskin 104.22 40 28 **********
Salmonella enterica 150 70 3 **************
Science Abuse 178.16 82 7 *****************
scythe 102.81 37 35 **********
rdrc: Snapback Sprite 168.30 78 5 ****************
spawn of the dark core 166.19 68 32 ****************
Staphylococcus aureus 173.23 76 18 *****************
Stegodon Aurorae 183.80 83 12 ******************
rdrc: Strychnine Banshee 169.01 79 3 ****************
Subterranean Homesick Al 130.98 48 42 *************
tinderbox 111.26 43 29 ***********
toy soldier 173.23 79 9 *****************
Trypanosoma brucei 147.18 56 41 **************
victim of the night 155.63 70 11 ***************
war of the snowflakes 92.253 34 29 *********
wisdom of the grasshoppe 178.87 79 17 *****************
wreath of thistles 126.76 60 0 ************
written in the dust 183.80 85 6 ******************
Xinyl 110.56 46 19 ***********
Score = 145.74

--------------------------------------------------
;redcode-nano
;name 057_36.red
;author yabevolver
;strategy evolved
;origin 114_15
;species 87799
;generation 2018
;created 02-05-2023 13-21-58
;assert CORESIZE==80
ORG 0
SPL.I # 19,> 56
SPL.BA # 17,} 0
MOV.I } 13,} -1
MOV.I # 37,} -1
DJN.X { 14,< 63
END

Testing 057_36.red...
Anefam Vesyes 123.94 54 14 ************
Another MEVO Thing 136.61 62 8 *************
b69e6908-a5be0bba-ab4445 132.39 54 26 *************
Clone 147.88 63 21 **************
Cold Plasma 128.16 39 65 ************
Combat Arithmetic 157.04 71 10 ***************
Creamed Corn 163.38 73 13 ****************
Crimson Climber 135.21 56 24 *************
Crisis 156.33 63 33 ***************
[RS] C-Strobia 165.49 72 19 ****************
eerie glow 123.23 33 76 ************
Free the Fish! 133.09 39 72 *************
Hot Soup 146.47 62 22 **************
Just a MEVO Thing 161.26 72 13 ****************
Little Bang 141.54 61 18 **************
Little Red Rat 150.70 66 16 ***************
7_3 180.28 83 7 ******************
NanoCracker 156.33 69 15 ***************
02_08 189.43 84 17 ******************
[RS] Nextratulated Sturv 125.35 51 25 ************
Nonlocality 2 116.90 32 70 ***********
Nonlocality 118.30 36 60 ***********
Quantum Foam 166.19 75 11 ****************
Reflow Oven 162.67 74 9 ****************
Sleepy Lepus 145.77 47 66 **************
Steaming Pebbles 172.53 73 26 *****************
SWT2-21 171.12 69 36 *****************
SWT2-28 130.98 53 27 *************
Transfer Function 141.54 56 33 **************
Unholy Fire 138.02 45 61 *************
Score = 147.27

Testing 057_36.red...
8c09fc1a-4799259f-174724 129.57 57 13 ************
rdrc: Alcoholism Malt 138.73 61 14 *************
b1f3ad11-a6073e-f711ff69 154.22 66 21 ***************
Bacillus anthracis 180.28 79 19 ******************
Black Sun III 145.77 59 30 **************
rdrc: Breakaway Carte 142.25 49 55 **************
c82f15b5-85011fd8-5a969d 196.47 85 24 *******************
Clear-2 185.91 81 21 ******************
Cosmic Horror 151.40 51 62 ***************
Different Day 219.01 99 14 *********************
written in a dream 125.35 31 85 ************
early morning view 224.64 106 1 **********************
Escherichia coli 178.87 82 8 *****************
e6843-5724-xt430-4-nano- 108.45 17 103 **********
Foggy Maus (beta) 166.90 74 15 ****************
girl from the underworld 88.732 39 9 ********
human resistance 167.60 67 37 ****************
the kraken awakes 114.78 35 58 ***********
Leishmania deanei 195.07 85 22 *******************
looking glass 154.92 71 7 ***************
lord of the digital swam 83.098 16 70 ********
Man&Machine 123.23 48 31 ************
Military Grade Nano 141.54 62 15 **************
New Vera City 149.29 56 44 **************
Obsidian peasoup 207.04 95 9 ********************
Plasmodium vivax 170.42 75 17 *****************
qEvo[[3]] 150 57 42 **************
realm of the fire flower 106.33 13 112 **********
Red Moon 171.83 71 31 *****************
rdrc: Repent Linemen 131.69 57 16 *************
Rocket propelled monkey 121.12 56 4 ************
rumpelstiltskin 109.85 12 120 **********
Salmonella enterica 176.76 82 5 *****************
Science Abuse 124.64 47 36 ************
scythe 121.83 27 92 ************
rdrc: Snapback Sprite 166.19 76 8 ****************
spawn of the dark core 154.92 48 76 ***************
Staphylococcus aureus 165.49 66 37 ****************
Stegodon Aurorae 142.95 56 35 **************
rdrc: Strychnine Banshee 130.98 47 45 *************
Subterranean Homesick Al 129.57 38 70 ************
tinderbox 121.12 27 91 ************
toy soldier 133.09 53 30 *************
Trypanosoma brucei 137.32 38 81 *************
victim of the night 89.436 35 22 ********
war of the snowflakes 121.83 28 89 ************
wisdom of the grasshoppe 139.43 60 18 *************
wreath of thistles 169.01 80 0 ****************
written in the dust 75.352 32 11 *******
Xinyl 72.535 16 55 *******
Score = 144.14

--------------------------------------------------
;redcode-nano
;name 022_19.red
;author yabevolver
;strategy evolved
;origin 114_15
;species 28964
;generation 1913
;created 02-05-2023 13-21-47
;assert CORESIZE==80
ORG 0
SPL.I # 22,> 13
SPL.I # 48,} 0
MOV.I } 3,} -1
MOV.I # 32,} -1
MOV.I } 39,} -4
END

Testing 022_19.red...
Anefam Vesyes 171.83 78 10 *****************
Another MEVO Thing 171.12 78 9 *****************
b69e6908-a5be0bba-ab4445 164.78 69 27 ****************
Clone 167.60 71 25 ****************
Cold Plasma 107.04 28 68 **********
Combat Arithmetic 210.56 93 20 *********************
Creamed Corn 180.28 81 13 ******************
Crimson Climber 147.88 66 12 **************
Crisis 136.61 63 5 *************
[RS] C-Strobia 159.15 68 22 ***************
eerie glow 117.60 37 56 ***********
Free the Fish! 145.77 49 60 **************
Hot Soup 146.47 61 25 **************
Just a MEVO Thing 157.74 73 5 ***************
Little Bang 165.49 76 7 ****************
Little Red Rat 158.45 72 9 ***************
7_3 173.94 78 13 *****************
NanoCracker 133.09 60 9 *************
02_08 198.59 88 18 *******************
[RS] Nextratulated Sturv 128.87 57 12 ************
Nonlocality 2 122.53 40 54 ************
Nonlocality 106.33 34 49 **********
Quantum Foam 135.21 60 12 *************
Reflow Oven 165.49 72 19 ****************
Sleepy Lepus 148.59 48 67 **************
Steaming Pebbles 178.87 81 11 *****************
SWT2-21 207.04 94 12 ********************
SWT2-28 177.46 81 9 *****************
Transfer Function 171.12 74 21 *****************
Unholy Fire 103.52 37 36 **********
Score = 155.30

Testing 022_19.red...
8c09fc1a-4799259f-174724 134.50 62 5 *************
rdrc: Alcoholism Malt 164.78 77 3 ****************
b1f3ad11-a6073e-f711ff69 140.84 62 14 **************
Bacillus anthracis 137.32 62 9 *************
Black Sun III 164.78 71 21 ****************
rdrc: Breakaway Carte 72.535 14 61 *******
c82f15b5-85011fd8-5a969d 161.26 72 13 ****************
Clear-2 185.91 85 9 ******************
Cosmic Horror 116.90 35 61 ***********
Different Day 155.63 71 8 ***************
written in a dream 114.78 30 73 ***********
early morning view 114.08 54 0 ***********
Escherichia coli 132.39 58 14 *************
e6843-5724-xt430-4-nano- 114.78 24 91 ***********
Foggy Maus (beta) 173.94 79 10 *****************
girl from the underworld 113.38 50 11 ***********
human resistance 147.18 63 20 **************
the kraken awakes 94.366 24 62 *********
Leishmania deanei 180.28 77 25 ******************
looking glass 130.28 57 14 *************
lord of the digital swam 98.591 29 53 *********
Man&Machine 123.94 52 20 ************
Military Grade Nano 154.22 70 9 ***************
New Vera City 161.26 68 25 ****************
Obsidian peasoup 204.22 95 5 ********************
Plasmodium vivax 160.56 73 9 ****************
qEvo[[3]] 170.42 68 38 *****************
realm of the fire flower 116.19 21 102 ***********
Red Moon 160.56 72 12 ****************
rdrc: Repent Linemen 118.30 52 12 ***********
Rocket propelled monkey 127.46 58 7 ************
rumpelstiltskin 100.70 13 104 **********
Salmonella enterica 126.05 56 11 ************
Science Abuse 121.12 50 22 ************
scythe 116.19 24 93 ***********
rdrc: Snapback Sprite 178.16 83 4 *****************
spawn of the dark core 100.70 29 56 **********
Staphylococcus aureus 164.08 67 32 ****************
Stegodon Aurorae 174.64 75 23 *****************
rdrc: Strychnine Banshee 104.22 32 52 **********
Subterranean Homesick Al 108.45 27 73 **********
tinderbox 129.57 34 82 ************
toy soldier 141.54 64 9 **************
Trypanosoma brucei 106.33 21 88 **********
victim of the night 128.87 60 3 ************
war of the snowflakes 107.04 17 101 **********
wisdom of the grasshoppe 119.71 52 14 ***********
wreath of thistles 118.30 56 0 ***********
written in the dust 156.33 72 6 ***************
Xinyl 97.183 31 45 *********
Score = 134.90

--------------------------------------------------
;redcode-nano
;name 086_35.red
;author yabevolver
;strategy evolved
;origin 012_23
;species 7434
;generation 4101
;created 02-05-2023 13-22-06
;assert CORESIZE==80
ORG 0
SPL.A # 41,< 63
MOV.I } 75,{ 0
MOV.I > 48,{ -2
MOV.I { -3,< 60
JMP.I $ -2,} 22
END

Testing 086_35.red...
Anefam Vesyes 140.84 65 5 **************
Another MEVO Thing 136.61 64 2 *************
b69e6908-a5be0bba-ab4445 141.54 65 6 **************
Clone 157.04 72 7 ***************
Cold Plasma 144.36 65 10 **************
Combat Arithmetic 145.77 66 9 **************
Creamed Corn 157.74 72 8 ***************
Crimson Climber 133.80 62 4 *************
Crisis 156.33 73 3 ***************
[RS] C-Strobia 152.81 69 10 ***************
eerie glow 91.549 40 10 *********
Free the Fish! 126.76 53 21 ************
Hot Soup 142.95 65 8 **************
Just a MEVO Thing 155.63 72 5 ***************
Little Bang 134.50 63 2 *************
Little Red Rat 147.88 70 0 **************
7_3 149.29 69 5 **************
NanoCracker 131.69 60 7 *************
02_08 130.98 58 12 *************
[RS] Nextratulated Sturv 135.21 62 6 *************
Nonlocality 2 135.21 58 18 *************
Nonlocality 114.78 45 28 ***********
Quantum Foam 152.81 71 4 ***************
Reflow Oven 147.18 69 2 **************
Sleepy Lepus 120.42 52 15 ************
Steaming Pebbles 149.29 68 8 **************
SWT2-21 154.22 69 12 ***************
SWT2-28 145.07 65 11 **************
Transfer Function 128.87 58 9 ************
Unholy Fire 115.49 50 14 ***********
Score = 139.22

Testing 086_35.red...
8c09fc1a-4799259f-174724 167.60 79 1 ****************
rdrc: Alcoholism Malt 137.32 63 6 *************
b1f3ad11-a6073e-f711ff69 186.61 87 4 ******************
Bacillus anthracis 157.04 71 10 ***************
Black Sun III 157.74 71 11 ***************
rdrc: Breakaway Carte 127.46 47 40 ************
c82f15b5-85011fd8-5a969d 158.45 73 6 ***************
Clear-2 144.36 66 7 **************
Cosmic Horror 123.94 51 23 ************
Different Day 146.47 69 1 **************
written in a dream 145.77 61 24 **************
early morning view 105.63 50 0 **********
Escherichia coli 147.18 69 2 **************
e6843-5724-xt430-4-nano- 163.38 74 10 ****************
Foggy Maus (beta) 140.84 66 2 **************
girl from the underworld 140.84 63 11 **************
human resistance 171.83 76 16 *****************
the kraken awakes 163.38 61 49 ****************
Leishmania deanei 152.81 69 10 ***************
looking glass 167.60 78 4 ****************
lord of the digital swam 115.49 47 23 ***********
Man&Machine 139.43 64 6 *************
Military Grade Nano 163.38 75 7 ****************
New Vera City 121.83 55 8 ************
Obsidian peasoup 143.66 63 15 **************
Plasmodium vivax 184.50 86 4 ******************
qEvo[[3]] 166.90 75 12 ****************
realm of the fire flower 102.81 37 35 **********
Red Moon 152.11 72 0 ***************
rdrc: Repent Linemen 149.29 70 2 **************
Rocket propelled monkey 139.43 63 9 *************
rumpelstiltskin 104.22 40 28 **********
Salmonella enterica 155.63 72 5 ***************
Science Abuse 166.19 77 5 ****************
scythe 117.60 47 26 ***********
rdrc: Snapback Sprite 158.45 74 3 ***************
spawn of the dark core 155.63 63 32 ***************
Staphylococcus aureus 154.92 69 13 ***************
Stegodon Aurorae 181.69 80 18 ******************
rdrc: Strychnine Banshee 168.30 76 11 ****************
Subterranean Homesick Al 130.98 49 39 *************
tinderbox 150 59 36 **************
toy soldier 164.78 74 12 ****************
Trypanosoma brucei 137.32 48 51 *************
victim of the night 164.78 76 6 ****************
war of the snowflakes 117.60 43 38 ***********
wisdom of the grasshoppe 192.95 88 10 *******************
wreath of thistles 148.59 70 1 **************
written in the dust 194.36 89 9 *******************
Xinyl 111.97 47 18 ***********
Score = 149.22

--------------------------------------------------

;redcode-nano
;name 067_14.red
;author yabevolver
;strategy evolved
;origin 114_15
;species 782
;generation 1969
;created 02-05-2023 13-21-45
;assert CORESIZE==80
ORG 0
SPL.BA # 34,} 36
SPL.F # 24,} 0
MOV.I } 13,} -1
MOV.I # 54,} 79
SPL.I { 14,> 61
END

Testing 067_14.red...
Anefam Vesyes 183.09 83 11 ******************
Another MEVO Thing 165.49 76 7 ****************
b69e6908-a5be0bba-ab4445 96.478 38 23 *********
Clone 108.45 44 22 **********
Cold Plasma 111.97 25 84 ***********
Combat Arithmetic 172.53 81 2 *****************
Creamed Corn 180.28 82 10 ******************
Crimson Climber 108.45 45 19 **********
Crisis 155.63 71 8 ***************
[RS] C-Strobia 124.64 54 15 ************
eerie glow 120.42 40 51 ************
Free the Fish! 129.57 43 55 ************
Hot Soup 147.88 61 27 **************
Just a MEVO Thing 154.22 72 3 ***************
Little Bang 149.29 68 8 **************
Little Red Rat 150.70 66 16 ***************
7_3 141.54 65 6 **************
NanoCracker 132.39 57 17 *************
02_08 183.09 83 11 ******************
[RS] Nextratulated Sturv 138.73 57 26 *************
Nonlocality 2 128.16 43 53 ************
Nonlocality 144.36 54 43 **************
Quantum Foam 166.19 73 17 ****************
Reflow Oven 128.87 57 12 ************
Sleepy Lepus 135.91 41 70 *************
Steaming Pebbles 169.01 77 9 ****************
SWT2-21 159.85 72 11 ***************
SWT2-28 169.01 75 15 ****************
Transfer Function 149.29 59 35 **************
Unholy Fire 142.95 53 44 **************
Score = 144.95

Testing 067_14.red...
8c09fc1a-4799259f-174724 119.71 53 11 ***********
rdrc: Alcoholism Malt 126.05 55 14 ************
b1f3ad11-a6073e-f711ff69 119.71 51 17 ***********
Bacillus anthracis 152.11 67 15 ***************
Black Sun III 160.56 71 15 ****************
rdrc: Breakaway Carte 123.94 33 77 ************
c82f15b5-85011fd8-5a969d 158.45 72 9 ***************
Clear-2 171.12 75 18 *****************
Cosmic Horror 129.57 44 52 ************
Different Day 186.61 82 19 ******************
written in a dream 127.46 27 100 ************
early morning view 216.90 101 5 *********************
Escherichia coli 157.04 71 10 ***************
e6843-5724-xt430-4-nano- 125.35 30 88 ************
Foggy Maus (beta) 152.11 68 12 ***************
girl from the underworld 76.056 30 18 *******
human resistance 135.91 56 25 *************
the kraken awakes 88.732 20 66 ********
Leishmania deanei 161.97 72 14 ****************
looking glass 115.49 52 8 ***********
lord of the digital swam 90.140 17 77 *********
Man&Machine 185.91 81 21 ******************
Military Grade Nano 121.83 50 23 ************
New Vera City 145.07 52 50 **************
Obsidian peasoup 190.84 88 7 *******************
Plasmodium vivax 157.04 71 10 ***************
qEvo[[3]] 142.95 57 32 **************
realm of the fire flower 113.38 17 110 ***********
Red Moon 163.38 72 16 ****************
rdrc: Repent Linemen 145.07 64 14 **************
Rocket propelled monkey 126.76 59 3 ************
rumpelstiltskin 119.01 22 103 ***********
Salmonella enterica 175.35 78 15 *****************
Science Abuse 187.32 82 20 ******************
scythe 114.78 22 97 ***********
rdrc: Snapback Sprite 199.29 91 10 *******************
spawn of the dark core 145.07 42 80 **************
Staphylococcus aureus 148.59 61 28 **************
Stegodon Aurorae 148.59 61 28 **************
rdrc: Strychnine Banshee 133.80 48 46 *************
Subterranean Homesick Al 109.15 22 89 **********
tinderbox 108.45 20 94 **********
toy soldier 155.63 69 14 ***************
Trypanosoma brucei 109.15 23 86 **********
victim of the night 119.71 53 11 ***********
war of the snowflakes 111.26 19 101 ***********
wisdom of the grasshoppe 112.67 46 22 ***********
wreath of thistles 185.91 88 0 ******************
written in the dust 129.57 57 13 ************
Xinyl 75.352 14 65 *******
Score = 139.52

I didn't save the exact settings at the moment I fished these warriors out of the soup but did save it later in the run, it should be pretty close to this...

REM --- yabevolver ini file ------------------------------------------------
REM copy this section to a file and specify filename on command line to load
header$ = ";redcode-nano" rem first line of program (start with ;)
author$ = ";author yabevolver" rem author line (start with ;)
comment$ = ";strategy evolved" rem comment line (start with ;)
tempdir$ = "/dev/shm/yabevolbig" rem temp directory for battling warriors
soupdir$ = "bigsoup" rem where soup warriors are stored
benchdir$ = "nano2301" rem benchmark dir for testing warriors
topdir$ = "bigtop" rem where top-scoring warriors are saved
xdim = 118 rem soup x dimension
ydim = 37 rem soup y dimension
xwrap = 0 rem enable wraparound in x dimension
ywrap = 0 rem enable wraparound in y dimension
length = 5 rem max warrior length
mincrelen = 5 rem minimum warrior length at creation
coresize = 80 rem core size
processes = 80 rem max processes
cycles = 800 rem instruction cycles per match
rounds = 100 rem matches per battle
instrate = 0.007 rem chance of changing an instruction
modrate = 0.015 rem chance of changing a modifier
addrrate = 0.015 rem chance of changing an address mode
numrate = 0.03 rem chance of changing a number
adjrate = 0.4 rem if number changed, chance of increment or decrement
localrate = 0.5 rem if new number, chance of being within warrior
reprate = 0.002 rem chance of replacing with new random line
insrate = 0.002 rem chance of inserting a new line (pushing rest down)
duprate = 0.002 rem chance of duplicating previous or following line
swaprate = 0.002 rem chance of swapping two lines (only 1 chance per evo)
delrate = 0.002 rem chance of deleting a line (pulling rest up)
orgrate = 0.0 rem chance of a random ORG line (ORG starts at 0)
org0rate = 0.5 rem if ORG changed, chance of ORG 0
crossrate = 0.05 rem crossover rate
evopseq = 0 rem evolving pmars sequence 0=random 1=fixed 2=permutate
evalmode = 3 rem 0=unguided 1=rnd bench 2=all 3=rnd 0/1 4=rnd 0/1/2
evalrate = 0.3 rem for evalmode 4 and 5, chance of using bench warriors
evalgen = 50 rem apply evalmode after both reach this many generations
ssfreq = 1000000 rem automatic soup save interval, 0 to disable
superror = 1 rem if not 0 redirect pmars stderr to dev/null
benchfreq = 1000 rem warrior benchmark interval (0 to disable)
savethr = 140 rem only save if score is >= this
savepc = 4 rem save if within this percentage of top score
bmrounds = 142 rem number of rounds when benchmarking
benchpseq = 2 rem benchmark pmars sequence 0=random 1=fixed 2=permutate
reinsfreq = 25100 rem warrior reinsertion interval (0 to disable)
REM instructions, modifiers and address modes...
REM instruction$ length must be divisible by 4 (start with space)
REM modifiers$ length must be divisible by 3
REM bias encoded using duplicates
REM addr1mode$ and addr2mode$ separated as they have different mixes
instructions$ = " MOV MOV MOV MOV MOV MOV MOV MOV MOV MOV SPL SPL"
instructions$ = instructions$+" SPL SPL SPL SPL DJN DJN DJN DJN DJN"
instructions$ = instructions$+" ADD SUB MOD MUL DIV JMP JMZ JMN SEQ SNE"
modifiers$ = ".I .I .I .I .I .I .I .I .B .B .B .B .F .F .F .X .A .AB.BA"
addr1modes$ = "######$$$$$>>>><<<{{}}*@"
addr2modes$ = "<<<<<<{{{{{>>>>$$$$}}}@#"
initialinstr$ = " SPL MOV MOV MOV DJN" rem initial warrior instructions
enablepreseed = 0 rem if not zero use initialinstr$ to pre-seed the soup
REM binaries and options
unibox = 1 rem enable unicode chars for box surrounding soup display
lister$ = "less" rem [path/]name of file lister program
listeropts$ = "-~" rem lister opts for warriors.. no ~ at end
listeroptsb$ = "-~ +G" rem lister opts for benchmarks.. no ~, go to eof
pmars$ = "pmars" rem [path/]name of pmars binary
pmarsv$ = "pmarsv" rem [path/]name of pmarsv binary
pmarsvopts$ = "-v 564" rem pmarsv options (-v digits speed dispmode detail)
rem pmarsv$ = "./bigpmarsv.sh" rem for my pmarsv magnifying script
pmarsv$ = "pmarsvsdl" rem for the SDL version of pmarsv
pmarsvopts$ = "-v 784" rem opts for my speed-patched SDL version
pmarsvrounds = 10 rem rounds to run in pmarsv
REM --- end yabevolver ini file --------------------------------------------


Helper Scripts

The program can be run directly, but usually I use copies of this script that launches the program in a xterm window sized large enough to accommodate the soup display to minimize fiddling with xterm parameters...

#!/bin/bash
PATH=bin64:$PATH       # add bin64 dir to PATH
inifile="bigsoup.ini"  # name of ini file (no spaces)
xtopts="-bg black -fg green"  # xterm options
title="YabEvolver (bigsoup)"  # xterm window title
if [ -f "$inifile" ];then # if ini file exists...
 # use awk to get xdim ydim from ini file.. long live awk!
 xdim=$(grep "^xdim " "$inifile"|awk '{print $3}')
 ydim=$(grep "^ydim " "$inifile"|awk '{print $3}')
 gx=$(($xdim+2));gy=$(($ydim+3)) # terminal must be at least xdim+2 by ydim+3
 if [ "$gx" -lt 80 ];then gx=80;fi  # minimum 80 chars wide
 if [ "$gy" -lt 24 ];then gy=24;fi  # minimum 24 rows
else
 gx=80;gy=24 # default terminal size
fi
if [ -t 1 -a "$gx$gy" = "8024" ];then # if can run in existing terminal
 yabasic yabevolver.yab $inifile      # just run the evolver
else  # otherwise launch the evolver in xterm sized appropriately
 xterm -geometry $gx"x"$gy $xtopts -title "$title" \
       -e yabasic yabevolver.yab $inifile &
 sleep 1  # this is needed if "run in terminal" selected
fi

The bin64 directory it adds to the path is where I put my pmars/pmarsv/pmarsvsdl binaries. The script uses grep and awk to read the xdim and ydim settings from the INI file, if not present assumes an 80x24 terminal.  Because it uses awk's field print, the "=" must be surrounded by spaces.

Here's the "bigpmarsv.sh" script I made to magnify the original pmarsv window so I could see what was going on...

#!/bin/bash
# magnify the pmarsv window - too tiny!
# requires xzoom and xdotool
# call this script instead of pmarsv, chains to
# pmarsv with parms after launching xzoom
if [ "$1" = "bk" ];then
 xzoomx=800  # move xzoom window here
 xzoomy=600  # after launching
 sleep 0.2   # enough time to launch pmarsv
 pmvw=$(xdotool search --name "pMARS 9") # get pmarsv window id
 if [ "$pmvw" = "" ];then exit;fi        # not found, exit
 # get info about the pmarsv window
 windata=$(xdotool getwindowgeometry --shell "$pmvw")
 if [ ! $? -eq 0 ];then exit;fi # exit if error
 for d in $windata;do eval $d;done # "run" $windata to set vars
 X1=$(($X-10));Y1=$(($Y-46))  # this might be system dependent
 # launch the xzoom tool telling it where to grab from
 xzoom -source "$WIDTH"x"$HEIGHT"+"$X1"+"$Y1" &
 sleep 0.2 # wait for it to open
 xzw=$(xdotool search --name "xzoom") # get xzoom window id
 xdotool windowmove "$xzw" $xzoomx $xzoomy # tell it where to go
 xdotool windowfocus "$xzw"  # focus it (probably already)
 xdotool key --delay 30 d d d  # send it d keys to decrease delay
 xdotool windowfocus "$pmvw" # refocus pmarsv window
 # wait for pmarsv window to close
 while [ 1 = 1 ];do
  sleep 0.2
  pmvw=$(xdotool search --name "pMARS 9") # get pmarsv window id
  if [ "$pmvw" = "" ];then # window closed
   killall "xzoom"  # kill the xzoom process
   exit
  fi
 done
else
 "$0" "bk" 2>/dev/null & # run self in background (suppress errors)
 pmarsv "$@" # run the pmars command line in forground
fi

I don't use the magnifying script that much now that I figured out how to slow down the SDL version of pmarsv - computers are very different now than they were when pmars/pmarsv was written, back then 800x600 monitors were common and processors were usually a few hundred (or dozen) MHz. But the old code still compiles. They're trying to depreciate SDL 1.2, at first I thought it was removed and replaced with a compat thing that didn't work but then I found the libsdl1.2debian package which works fine on Ubuntu 22.04 in a VM, my main system is Ubuntu 20.04 with a GUI hacked from bits of Gnome and MATE.


Dev Notes

2/11/23 - It's been about a day and a half since I had to make any changes the program code, so yay.. but noticed a gruesome bug in this page - had the wrong INI posted for the big soup.. oops. When I started this program I was really just testing out YaBasic to see what I could make it do, but then got the evolving bug again and ended up adding way more things to it than I had planned - originally the program had no user interface at all, it just evolved until stopped, now it includes most of the stuff I do when evolving warriors. If some things seem like they were bolted on it's because they were.. and most of the code updates involved the various interface things and not the actual evolving code.

Now that the interface is doing approximately what I want I can focus more on the actual evolving code. I'm not particularly happy with how I implemented line swap - it swaps lines in the parent warrior before evolving. Probably should fix that so that it only swaps lines in the offspring. Also I don't think the adjust local references option is working correctly. Funny thing about corewar evolvers is fixing these things would likely make little to no difference or might even make it worse - the defective swap could be viewed as a culling operation that might help to increase diversity - winning is not always winning - and incorrect or not I haven't noticed much difference whether references are adjusted or not, at least with nano warriors, as inserts and deletes tend to kill mature warriors anyway. Inserts and deletes are mainly only useful at the start of evolution while the warriors are weak, but occasionally it might get lucky and result in a viable warrior.

Usually one warrior form will completely take over the soup but I have noticed with this evolver it tends to take longer for that to happen and sometimes multiple forms can coexist for quite a while. This "huge" soup of 8000 warriors has been evolving for over 24 hours and around 1500 generations but there are still several forms present...


Total of 8000 warriors, average score = 120.0
Found 123 unique instruction sequences...
5804 best 148.1 by 032_42 "M" MOV SPL MOV MOV DJN
1203 best 127.3 by 093_28 "F" SPL MOV MOV SPL MOV
451 best 133.4 by 073_45 "p" SPL MOV MOV MOV MOV
31 best 109.5 by 131_20 "Z" SPL MOV MOV DJN MOV
31 best 106.9 by 120_24 "G" SPL MOV SPL MOV
26 best 93.75 by 030_48 "i" MOV SPL MOV DJN
26 best 129.8 by 002_25 "q" SPL SPL MOV MOV DJN
26 best 122.9 by 102_47 "b" SPL MOV SPL MOV MOV
22 best 124.9 by 159_43 "N" MOV SPL MOV MOV SPL
18 best 126.8 by 016_47 "T" MOV SPL MOV MOV MOV
18 best 102.1 by 057_32 "d" SPL MOV MOV SEQ MOV
17 best 139.5 by 043_38 "u" MUL SPL MOV MOV DJN
16 best 132.7 by 076_02 "c" ADD SPL MOV MOV DJN
...snip...

;redcode-nano
;name 032_42.red
;author yabevolver
;strategy evolved
;origin 027_14
;species 20045
;generation 1597
;created 02-11-2023 13-05-51
;assert CORESIZE==80
ORG 0
MOV.I > 50,$ 15
SPL.AB # 73,> 20
MOV.I { -2,{ -1
MOV.I { -2,{ -3
DJN.F $ 78,{ -4
END

It's not particularly strong on benchmarks but scores over 142 on all my test sets, sometimes when evolving against a particular test set the warriors will learn how to beat those warriors but perform poorly against warriors it has not encountered. I ran this soup for a few hundred more generations, it maintained diversity but didn't get any stronger.

Here's the INI file I used for this soup...

REM --- yabevolver ini file ------------------------------------------------
REM copy this section to a file and specify filename on command line to load
header$ = ";redcode-nano" rem first line of program (start with ;)
author$ = ";author yabevolver" rem author line (start with ;)
comment$ = ";strategy evolved" rem comment line (start with ;)
tempdir$ = "/dev/shm/yabevolhuge" rem temp directory for battling warriors
soupdir$ = "hugesoup" rem where soup warriors are stored
benchdir$ = "nano2301" rem benchmark dir for testing warriors
topdir$ = "hugetop" rem where top-scoring warriors are saved
xdim = 160 rem soup x dimension
ydim = 50 rem soup y dimension
xwrap = 0 rem enable wraparound in x dimension
ywrap = 0 rem enable wraparound in y dimension
length = 5 rem max warrior length
mincrelen = 1 rem minimum warrior length at creation
coresize = 80 rem core size
processes = 80 rem max processes
cycles = 800 rem instruction cycles per match
rounds = 100 rem matches per battle
instrate = 0.01 rem chance of changing an instruction
modrate = 0.02 rem chance of changing a modifier
addrrate = 0.03 rem chance of changing an address mode
numrate = 0.04 rem chance of changing a number
adjrate = 0.5 rem if number changed, chance of increment or decrement
localrate = 0.5 rem if new number, chance of being within warrior
reprate = 0.002 rem chance of replacing with new random line
insrate = 0.002 rem chance of inserting a new line (pushing rest down)
duprate = 0.002 rem chance of duplicating previous or following line
swaprate = 0.002 rem chance of swapping two lines (only 1 chance per evo)
delrate = 0.002 rem chance of deleting a line (pulling rest up)
orgrate = 0 rem chance of a random ORG line (ORG starts at 0)
org0rate = 0.5 rem if ORG changed, chance of ORG 0
crossrate = 0.01 rem crossover rate
adjlocref = 0 rem if not 0 adjust local refs when inserting and deleting
evopseq = 0 rem evolving pmars sequence 0=random 1=fixed 2=permutate
evalmode = 3 rem 0=unguided 1=rnd bench 2=all 3=rnd 0/1 4=rnd 0/1/2
evalrate = 0.2 rem for evalmode 4 and 5, chance of using bench warriors
evalgen = 100 rem apply evalmode after both reach this many generations
ssfreq = 1000000 rem automatic soup save interval, 0 to disable
superror = 0 rem if not 0 redirect pmars stderr to dev/null
benchfreq = 1000 rem warrior benchmark interval (0 to disable)
savethr = 140 rem only save if score is >= this
savepc = 3 rem save if within this percentage of top score
bmrounds = 142 rem number of rounds when benchmarking
benchpseq = 2 rem benchmark pmars sequence 0=random 1=fixed 2=permutate
reinsfreq = 0 rem warrior reinsertion interval (0 to disable)
REM instructions, modifiers and address modes...
REM instruction$ length must be divisible by 4 (start with space)
REM modifiers$ length must be divisible by 3
REM bias encoded using duplicates
REM addr1mode$ and addr2mode$ separated as they have different mixes
instructions$ = " MOV MOV MOV MOV MOV MOV MOV MOV SPL SPL"
instructions$ = instructions$+" SPL SPL SPL SPL DJN DJN DJN DJN DJN"
instructions$ = instructions$+" ADD SUB MOD MUL DIV JMP JMZ JMN SEQ SNE"
modifiers$ = ".I .I .I .I .I .I .I .I .B .B .B .B .F .F .F .X .A .AB.BA"
addr1modes$ = "######$$$$$>>>><<<{{}}*@"
addr2modes$ = "<<<<<<{{{{{>>>>$$$$}}}@#"
initialinstr$ = " SPL MOV MOV MOV DJN" rem initial warrior instructions
enablepreseed = 0 rem if not zero use initialinstr$ to pre-seed the soup
REM binaries and options
unibox = 1 rem enable unicode chars for box surrounding soup display
lister$ = "less" rem [path/]name of file lister program
listeropts$ = "-~" rem lister opts for warriors.. no ~ at end
listeroptsb$ = "-~ +G" rem lister opts for benchmarks.. no ~, go to eof
pmars$ = "pmars" rem [path/]name of pmars binary
pmarsv$ = "pmarsv" rem [path/]name of pmarsv binary
pmarsvopts$ = "-v 564" rem pmarsv options (-v digits speed dispmode detail)
rem pmarsv$ = "./bigpmarsv.sh" rem for my pmarsv magnifying script
pmarsv$ = "pmarsvsdl" rem for the SDL version of pmarsv
pmarsvopts$ = "-v 784" rem opts for my speed-patched SDL version
pmarsvrounds = 10 rem rounds to run in pmarsv
REM --- end yabevolver ini file --------------------------------------------

2/12/23 - fixed a bug in the part that redisplays the existing benchmark report if it's still valid, while at it fixed the load settings file option so that it doesn't terminate the program if a bad INI file is loaded (it won't exit the filename prompt until a valid settings file is loaded but that's better than crashing), and trying something new - gradually reducing the mutation rates as warriors mature. Been occasionally doing that manually but added two settings, mutadjust and mutadjgen, to automate the process. The mutadjust setting ranges from 0 to 1 and sets the maximum amount to reduce the mutation rates, for example if mutadjust is 0.5 then if instrate is 0.02 then the instruction mutation rate will eventually be reduced to 0.01 but no lower. The mutadjgen setting sets the warrior generation where full mutation reduction takes place, after which there is no further reduction.

The code bit that perform the adjustment is...

rem derive mutation rate modifier rmod from gennum mutadjust and mutadjgen
rem affects instrate modrate addrrate numrate reprate insrate duprate
rem swaprate and orgrate by gradually reducing mutation as gen increases
if mutadjgen<2 mutadjgen=1:mutadjust=0 rem disable if mutadjgen<2
rmod=1-(mutadjust*(gennum(x1,y1)/mutadjgen)) rem calculate multiplier
if rmod<mutadjust rmod=mutadjust rem rmod between mutadjust and 1

...then the individual mutation rates are multiplied by rmul. Fairly simple mod that shouldn't change anything else. Whether it helps or not ???. By default mutadjust is set to 0 to disable for compatibility with previous INI files. Tempted as it is, broken or not, trying to leave the core evolving operations alone - changing it would likely change the entire character of the evolver and I like what it's doing, particularly maintaining diversity for much longer than previous experiments. The warrior code it's producing so far hasn't been super-strong against current hill nanos (does better on older benchmarks) but it's more interesting.. doesn't always end up only boring SPL MOV MOV MOV DJN warriors, other forms have a chance and in larger soups can coexist with each other for long durations.

I have ideas but when it comes down to it I don't have that much of a clue what operations really make a difference in the output, that's why I like playing this stuff and trying to figure it out - I throw stuff at my wall and see what sticks. Fundamentally though, mix together the combination of a system with a random starting population, mutation, and a scoring function then something is going to happen. I remember years ago there was some fuss about Artificial (simulated) Life, programs like Tierra etc but two-dimensional redcode evolvers seem to meet at least some of the definitions of AL - "executable DNA" (love that term), a world in which creatures evolve, simple open-ended evaluation (warriors either survive or they don't), and nearest-neighbor interactions that mimick how real creatures interact (or at least how they used to, transportation changed all that - sure enough if I simulate that via reinsertion then almost inevitably one form will dominate). One difference is the creatures it produces have silly but actual real-world applications - they can compete on hills with other warriors and take on a life of their own. Well sort of...

2/13/23 - Ok just a bit more monkeying... one of the things that bugged me a bit was often different instruction sequences would be assigned duplicate characters when in viewmode 2 or 3. Partially because my hashing formula isn't exactly great but mainly because of the birthday problem (aka birthday paradox) - when there are a given number of choices it takes suprisingly few members with the same choice. To fix added viewshift (and the existing viewmode) to the settings (XOR's the instruction sequence hash with viewshift before picking a soup view character), and added (hidden) shift-V keystrokes to toggle between the viewshift from the settings and a random integer. Saving a settings file will save the new viewshift value. The viewmode/viewshift settings are also displayed in the sequence report.

2/14/23 - Wow... this is cool, ANSI soup evolution "movies"! Totally worth the 11K or so it added to the program size. Described in the user interface docs section. Here's a script I use to play back the ANSI files outside of the evolver program, put this in the ANSI dump directory (requires xterm zenity and ansi2txt from colorized-logs)...

#!/bin/bash
# "play" ANSI dump files
# requires ansi2txt and zenity
if [ "$1" = "doit" ];then
 while [ 1 ];do # loop until ctrl-C or window closed
  clear;for f in $2*.ans;do
   echo -ne "\033[H";cat "$f";sleep 0.2
  done
 done
else
 bn=$(zenity --title "Play ANSI" --entry --text "Enter base name")
 if [ $? != 0 ];then exit;fi # exit if cancel clicked
 for f in $bn*.ans;do fn="$f";done;fi # get last fn
 if [ -f "$fn" ];then # if ANSI files exist
  columns=$(cat "$fn"|ansi2txt|wc -L) # get width
  lines=$(cat "$fn"|ansi2txt|wc -l)   # get # lines
  lines=$((lines+1))  # bump lines, last line has no lf
  xterm -geometry $columns"x"$lines -e $0 doit "$bn"  # play in xterm
 fi
fi

...hmm, VIM convert messed up the color on the last fi, not a bug for today. But a bug I might have to watch out for is the difference between sh's echo and bash's echo - in the sh shell 'echo -ne' isn't recognized but it prints ANSI codes just fine with just 'echo -n'. In bash you need 'echo -ne' to print ANSI sequences. YaBasic uses sh so in YabEvolver used the 'echo -n' version to print the home in between frames.

2/17/23 - The ANSI thing is addictive.. when sped up it really looks like a colony of living organisms - various warrior forms compete for territory and numbers then another form arises and puts stress on the dominant form which either adapt or be consumed. Despite being an extremely limited simulation of... something. Core war is a convenient "critter" base because (besides being old and cool) the language basically has no invalid combinations, just various degrees of it does something and a clear-cut evaluation scheme - fight two warriors in a simulated core with 3 points per win and 1 point per tie. Pretty much the same behavior can result when using any non-brittle creature format with a simple way to evaluate fitness, and if done on a grid similar to YabEvolver and other grid-based nearest-neighbor evolvers with similar color/character schemes it would probably look about the same when played back at high speed.

Here's the enhanced ANSI dump player bash script... (will update as needed)

#!/bin/bash
# 0_playansi.sh 230221
# play YabEvolver ANSI dump files
# requires xterm ansi2txt zenity awk
xtermopts="-bg black -fg green"
function processkeys {
[ "$key" = "q" ] && exit
if [ "$key" = "f" ];then
  fd=$(echo $fd|awk '{if ($1>0.01) print $1*0.8;else print $1}')
  echo -ne "\033[0K (fd=$fd)"
elif [ "$key" = "s" ];then
  fd=$(echo $fd|awk '{if ($1<2) print $1*1.2;else print $1}')
  echo -ne "\033[0K (fd=$fd)"
elif [ "$key" = "n" ];then fd="0.2";echo -ne "\033[0K (fd=$fd)"
elif [ "$key" = "l" ];then loop=$((1-loop));echo -ne "\033[0K"
 if [ $loop = 1 ];then echo -n " (loop on)";else echo -n " (loop off)";fi
fi
}
helptxt=" Q-quit P-pause F-faster S-slower N-normal B-begining L-loop on/off "
if [ "$1" = "doit" ];then
 echo;echo "$helptxt H-Show again ";read -sn 1 -t 2
 fd=0.2;loop=1;clear
 echo -ne "\033[?25l" # hide cursor
 while [ 1 ];do # loop until stopped
  for f in $2*.ans;do
   echo -ne "\033[H";cat "$f"
   read -sn 1 -t $fd key
   if [ "$key" = "p" ];then
    echo -ne "\033[0K (press a key)"
    read -sn 1 key;echo -ne "\033[14D\033[0K"
   fi
   if [ "$key" = "h" ];then
    echo -ne "\033[1;4H$helptxt"
    read -sn 1 key
   fi
   processkeys
   [ "$key" = "b" ] && break
  done
  if [ $loop = 0 -a "$key" != "b" ];then
   echo -ne "\033[0K (press a key)"
   read -sn 1 key;echo -ne "\033[14D\033[0K"
   processkeys
  fi
 done
else
 bn=$(zenity --title "Play ANSI" --entry \
 --text "Enter base name (default all)")||exit
 for f in $bn*.ans;do fn="$f";done    # get last fn
 if [ -f "$fn" ];then # if ANSI files exist
  columns=$(cat "$fn"|ansi2txt|wc -L) # get width
  lines=$(cat "$fn"|ansi2txt|wc -l)   # get # lines
  lines=$((lines+1))  # bump lines, last line has no lf
  [ $columns -lt 79 ] && columns=79   # minimum 79 columns
  xterm $xtermopts -geometry $columns"x"$lines -e $0 doit "$bn" #play in xterm
 fi
fi

This version uses keys to Pause Faster Slower Normal and Loop on/off - default is loop on, press L to toggle to pause after the last frame. Uses awk for faster/slower to do floating point math - each press of F multiplies the frame delay by 0.8 (min 0.1) and each press of F multiplies the delay by 1.2 (max 2). N resets the delay to the default, edit fd=0.2 for the default speed.

Similar techniques have been added to YabEvolver's ANSI play system command, also requiring awk (on my system awk points to gawk but there are other versions that should work the same). The ANSI player is implemented using sh and cat because that's faster and easier than trying to write the same thing in basic.. hard to beat cat for display speed. The ANSI soup dumps now use unicode box characters if the unibox setting is enabled, was using only ascii chars for compatibility but the crude box bugged me and if I need ascii-only I can just useuni=0. The soup display code has been restructured to make showsoup() faster when exiting listers, the other menu etc.. was pretty fast but was calling unnecessary positioning code, separated the actual warrior print from plotwarrior to just print without positioning first. Less code, noticibly faster with large soup displays (been evolving 160x50 "huge" soups), and now rather than duplicating the display code, the ANSI dump code can just call the position-free warrior print with a parm to tell it to write to a file instead of the screen. Much better.

2/20/23 - Playing back ANSI snapshots is neat, but making self contained animated GIFs and MP4s is a bit more convenient than a bash script and hundreds of ANSI files. Before adding ANSI dumps to make an animated GIF I used a crude script that periodically ran the xwd utility to make a bunch of screen snapshots then used convert to turn them into an animated GIF. This process rendered the computer unusable for the duration of the run as covering up the window would spoil the snapshots. ANSI dumps make the process a lot more usable, the script can just play back the ANSI frames to make each GIF, still have to avoid covering the window but it only ties up the computer for a couple minutes. Making GIFs longer than a few hundred frames failed with out of memory warnings, had to edit a file in /etc to fix that, see the comments. Once the animated gif was made, turning it into a MP4 video was fairly easy with ffmpeg (at least after a bit of googling).

Here's the script... (will update as needed)

#!/bin/bash
# mkansigif.sh 230220
# This script makes an animated GIF from a set of YabEvolver ANSI dumps
# Requires xterm zenity ansi2txt (from colorized-logs) awk xdotool convert
# (from imagemagick). Optionally uses ffmpeg to convert the GIF to a mp4 video
# Note - on my Ubuntu 20.04 system convert would fail after a few hundred
# frames, to fix I had to edit /etc/ImageMagick-6/policy.xml and change
# <policy domain="resource" name="memory" value="256MiB"/> value to "8GiB"
xtermopts="-bg black -fg green" # xterm options
tempdirprefix="/dev/shm"  # path to put tempdir in, "." to put under current
if [ "$1" = "doit" ];then
 shift;bname=$1;oname=$2;tempdir=$3;title=$4;
 fdelay=$5;fskip=$6;makemp4=$7;quality=$8;scale=$9
 wid=$(xdotool search "$title") # get window ID (getactive window fails)
 mkdir -p $tempdir # make temp dir
 >/dev/null 2>&1 rm $tempdir/*.gif # make sure there are no GIFs in tempdir
 [ "$wid" = "" -o "$wid" = "0" ] && exit # bail if ID command failed
 fcnt=$fskip  # frame counter for frame skip (output 1st frame then skip)
 clear;echo -ne "\033[?25l" # hide cursor
 for f in $bname*.ans;do
  echo -ne "\033[H";cat $f # show frame
  if [ $fcnt -ge $fskip ];then # time to dump
   xwd -id $wid -out "$tempdir/$(basename $f).xwd" # dump frame to xwd file
   convert $tempdir/$(basename $f).xwd $tempdir/$(basename $f).gif
   rm $tempdir/$(basename $f).xwd # convert/remove each to minimize disk/ram
   fcnt=0 # reset frame counter
  else fcnt=$((fcnt+1)) # not time yet, increment frame counter
  fi
 done
 echo -ne "\033[?25h\033[79D\033[0K Converting... " #show cursor, clear line
 convert -delay $fdelay -loop 0 $tempdir/*.gif $oname
 rm $tempdir/*.gif
 rm -d $tempdir
 if [ "$makemp4" = "1" ];then
  framerate=$(echo $fdelay|awk '{print 100/$1}') # calculate framerate
  mp4name="${oname%.*}.mp4" # replace .gif extension with .mp4
  echo;echo Running: ffmpeg -r $framerate -f gif -i $oname -f mp4 \
   -crf $quality -vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" "$mp4name"
  ffmpeg -r $framerate -f gif -i $oname -f mp4 -pix_fmt yuv420p \
   -crf $quality -vf "scale=trunc(iw/2)*$scale:trunc(ih/2)*$scale" \
   "$mp4name" || read -n 1
 fi
else
 timestr=$(date +%H%M%S) # get 6-digit string with current time
 tempdir="$tempdirprefix/mkansitmp$timestr"  # temp directory - no spaces
 title="MkANSIgif $timestr"  # for identifying window to dump, must be unique
 bname=$(zenity --width 300 --title "Make ANSI GIF" --entry \
  --text "ANSI path/basename (default all)")||exit
 echo "$bname"|grep " " && exit # no spaces allowed
 >/dev/null 2>&1 ls $bname*.ans || exit # exit if .ans files not found
 oname=$(zenity --width 300 --title "Make ANSI GIF" --entry \
  --text "Output GIF path/name")||exit
 echo "$oname"|grep " " && exit
 echo "$oname"|grep ".gif$" || oname="$oname.gif" # add .gif if not present
 fdelay=$(zenity --width 300 --title "Make ANSI GIF" --entry \
  --text "Frame delay (x10ms, def 10)")||exit
 [ "$fdelay" = "" ] && fdelay=10
 fdelay=$((fdelay+0))
 fskip=$(zenity --width 300 --title "Make ANSI GIF" --entry \
  --text "Frame skip (def 0)")||exit
 [ "$fskip" = "" ] && fskip=0
 fskip=$((fskip+0))
 makemp4=0;zenity --width 200 --title "Make ANSI GIF" --question \
  --text "Make MP4 video too?" && makemp4=1
 quality="";scale=2
 if [ $makemp4 = 1 ];then
  quality=$(zenity --width 350 --title "Make ANSI GIF" --entry \
   --text "Video quality 0-63 (0=best, none for 25)") || exit
  sel=$(zenity --width 250 --height 250 --title "Make ANSI GIF" \
   --column "" --hide-header --text "Select video scaling..." \
   --list "Actual size" "Upscale 1.5X" "Upscale 2.0X" "Upscale 3.0X") || exit
  [ "$sel" = "Upscale 1.5X" ] && scale=3
  [ "$sel" = "Upscale 2.0X" ] && scale=4
  [ "$sel" = "Upscale 3.0X" ] && scale=6
 fi
 [ "$quality" = "" ] && quality=25
 quality=$((quality+0))
 [ $quality -lt 0 -o $quality -gt 63 ] && exit
 for f in $bname*.ans;do fn="$f";done # get last fn
 columns=$(cat "$fn"|ansi2txt|wc -L) # get width
 lines=$(cat "$fn"|ansi2txt|wc -l)   # get # lines
 lines=$((lines+1))  # bump lines, last line has no lf
 xterm $xtermopts -title "$title" -geometry $columns"x"$lines -e $0 doit \
  "$bname" "$oname" $tempdir "$title" $fdelay $fskip $makemp4 $quality $scale
fi

It's a little crazy but seems to be working fairly well now. The upscale option is useful for magifying smaller soups but added mainly because anything less than 480p tends to look fuzzy on YouTube no matter how high quality the source.

Here's a few YT soup movies I made...

Run5a - stock parms, 77x20 grid, 1600 frames, about 800 generations
Run5b - later in the same run, 1055 frames, 1000 battles per frame, about 500 generations
Hugerun5 - 160x50 grid, 559 frames, 10000 battles per frame, about 600 generations
Hugerun6 - 160x50 grid, 1365 frames, 10000 battles per frame, about 1500 generations

At the end of Run5 a strong type "M" suddenly evolved and wiped out everything else, top warrior...

;redcode-nano
;name 14_12.red
;author yabevolver
;strategy evolved
;origin 38_11
;species 96444
;generation 1318
;created 02-18-2023 00-44-12
;assert CORESIZE==80
ORG 0
MOV.I > 36,$ 51
SPL.I # 73,> 47
MOV.I > 26,{ -1
MOV.I { -1,< -2
DJN.I $ -2,@ -2
END

...scores 152.25 against the nano2301 test set, 152.09 against the nano106 test set, and 154.39 against the old nanoBM07 test set. Not bad. Other high-scoring warriors from that run...

;redcode-nano
;name 59_13.red
;author yabevolver
;strategy evolved
;origin 38_11
;species 60045
;generation 1342
;created 02-18-2023 00-43-48
;assert CORESIZE==80
ORG 0
MOV.I > 31,$ 51
SPL.I # 73,> 47
MOV.I > 26,{ -1
MOV.I { -1,< -2
DJN.F $ -2,@ -2
END

...scores 152.08 against nano2301, 152.23 against nano106 and 154.11 against nanoBM07

;redcode-nano
;name 01_19.red
;author yabevolver
;strategy evolved
;origin 38_11
;species 60045
;generation 1262
;created 02-18-2023 00-33-36
;assert CORESIZE==80
ORG 0
MOV.I > 41,$ 52
SPL.B # 73,} 47
MOV.I > 26,{ -1
MOV.I { -1,< -2
DJN.F $ -2,@ -2
END

...scores 148.12 against nano2301, 151.48 against nano106 and 155.15 against nanoBM07.

Hugerun5 didn't produce anything particularly strong, at the end of the run this was the top warrior with a score of about 140-143 points...

;redcode-nano
;name 116_10.red
;author yabevolver
;strategy evolved
;origin 121_10
;species 26534
;generation 325
;created 02-18-2023 09-26-28
;assert CORESIZE==80
ORG 0
SPL.BA # 3,< 64
SPL.I # 32,< 0
MOV.I $ 76,< -2
MOV.I # 6,} 1
DJN.I < 15,{ -6
END

At the end of Hugerun6 this was the top warrior...

;redcode-nano
;name 135_11.red
;author yabevolver
;strategy evolved
;origin 041_18
;species 3435
;generation 1280
;created 02-19-2023 08-59-58
;assert CORESIZE==80
ORG 0
SPL.BA # 42,> 48
MOV.I # 9,} 1
SPL.A { 12,{ 73
MOV.I @ 0,{ 72
DJN.X $ -1,{ 54
END

...scores ~148 against nano2301, ~142 against nano106 and ~138 against nanoBM07, not so great. The large soups are visually interesting but don't necessarily produce stronger code and take longer to run for a given number of generations.

2/21/23 - Sometimes big soups produce something strong. Still using the same "hugesoup.ini" settings as the previous runs that didn't amount to much...

REM --- yabevolver ini file ------------------------------------------------
REM copy this section to a file and specify filename on command line to load
header$ = ";redcode-nano" rem first line of program (start with ;)
author$ = ";author yabevolver" rem author line (start with ;)
comment$ = ";strategy evolved" rem comment line (start with ;)
tempdir$ = "/dev/shm/yabevolhuge" rem temp directory for battling warriors
soupdir$ = "hugesoup" rem where soup warriors are stored
benchdir$ = "nano2301" rem benchmark dir for testing warriors
topdir$ = "hugetop" rem where top-scoring warriors are saved
xdim = 160 rem soup x dimension
ydim = 50 rem soup y dimension
xwrap = 0 rem enable wraparound in x dimension
ywrap = 0 rem enable wraparound in y dimension
length = 5 rem max warrior length
mincrelen = 5 rem minimum warrior length at creation
coresize = 80 rem core size
processes = 80 rem max processes
cycles = 800 rem instruction cycles per match
rounds = 100 rem matches per battle
mutadjust = 0.5 rem gradually reduce mutation rates by up to rate*mutadjust
mutadjgen = 1000 rem by this warrior generation then stop changing rates
instrate = 0.015 rem chance of changing an instruction
modrate = 0.03 rem chance of changing a modifier
addrrate = 0.04 rem chance of changing an address mode
numrate = 0.05 rem chance of changing a number
reprate = 0.003 rem chance of replacing with new random line
insrate = 0.003 rem chance of inserting a new line (pushing rest down)
duprate = 0.003 rem chance of duplicating previous or following line
swaprate = 0.003 rem chance of swapping two lines (only 1 chance per evo)
delrate = 0.003 rem chance of deleting a line (pulling rest up)
orgrate = 0 rem chance of a random ORG line (ORG starts at 0)
org0rate = 0.5 rem if ORG changed, chance of ORG 0
adjrate = 0.5 rem if number changed, chance of increment or decrement
localrate = 0.5 rem if new number, chance of being within warrior
crossrate = 0.01 rem crossover rate
adjlocref = 0 rem if not 0 adjust local refs when inserting and deleting
evopseq = 0 rem evolving pmars sequence 0=random 1=fixed 2=permutate
evalmode = 3 rem 0=unguided 1=rnd bench 2=all 3=rnd 0/1 4=rnd 0/1/2
evalrate = 0.15 rem for evalmode 4 and 5, chance of using bench warriors
evalgen = 100 rem apply evalmode after both reach this many generations
ssfreq = 1000000 rem automatic soup save interval, 0 to disable
superror = 0 rem if not 0 redirect pmars stderr to dev/null
benchfreq = 1000 rem warrior benchmark interval (0 to disable)
savethr = 135 rem only save if score is >= this
savepc = 4 rem save if within this percentage of top score
bmrounds = 142 rem number of rounds when benchmarking
benchpseq = 2 rem benchmark pmars sequence 0=random 1=fixed 2=permutate
reinsfreq = 0 rem warrior reinsertion interval (0 to disable)
REM instructions, modifiers and address modes...
REM instruction$ length must be divisible by 4 (start with space)
REM modifiers$ length must be divisible by 3
REM bias encoded using duplicates
REM addr1mode$ and addr2mode$ separated as they have different mixes
instructions$ = " MOV MOV MOV MOV MOV MOV MOV MOV SPL SPL"
instructions$ = instructions$+" SPL SPL SPL SPL DJN DJN DJN DJN DJN"
instructions$ = instructions$+" ADD SUB MOD MUL DIV JMP JMZ JMN SEQ SNE"
modifiers$ = ".I .I .I .I .I .I .I .I .B .B .B .B .F .F .F .X .A .AB.BA"
addr1modes$ = "######$$$$$>>>><<<{{}}*@"
addr2modes$ = "<<<<<<{{{{{>>>>$$$$}}}@#"
initialinstr$ = " SPL MOV MOV MOV DJN" rem initial warrior instructions
enablepreseed = 0 rem if not zero use initialinstr$ to pre-seed the soup
REM binaries and options
unibox = 1 rem enable unicode chars for box surrounding soup display
viewmode = 2 rem view mode 0=length 1=species 2=sequence52 3=sequence94
viewshift = 0 rem raw display sequence hash xord by this to help avoid dups
ansimode = 256 rem ANSI mode 16=16colors 256=256colors 24=RGB else no color
enableC = 1 rem if not 0 allow color scheme cycling by pressing C
lister$ = "less" rem [path/]name of file lister program
listeropts$ = "-~" rem lister opts for warriors.. no ~ at end
listeroptsb$ = "-~ +G" rem lister opts for benchmarks.. no ~, go to eof
pmars$ = "pmars" rem [path/]name of pmars binary
pmarsv$ = "pmarsv" rem [path/]name of pmarsv binary
pmarsvopts$ = "-v 564" rem pmarsv options (-v digits speed dispmode detail)
rem pmarsv$ = "./bigpmarsv.sh" rem for my pmarsv magnifying script
pmarsv$ = "pmarsvsdl" rem for the SDL version of pmarsv
pmarsvopts$ = "-v 784" rem opts for my speed-patched SDL version
pmarsvrounds = 10 rem rounds to run in pmarsv
REM --- end yabevolver ini file --------------------------------------------

...but this time after this...

┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│iiiSiSSScSSSfSiiiiiiiPiSSSSSiiSSiiiiiiiiiiiiiiiiiiiiiiiiiiiiTiiiiiiTTSSSSSSSSSSSSSiiiiiiiiiSSSSSiSSLiiiiiiiiiiiiiiiiiiiiTTTTTSSSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii│
│SSSiiSiSSSSSiiiiiiiiiiPSiSSSSSSSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiTiiiiiiTTSvSiSSSSSSSSiSiiiiiiSSSSSSSSiiiiiiiiiiiiEiiiiiiiiiTTTTTSSiiiiiiiiiiiiioyiiiiCiiiiiiiOiiiii│
│iSSSSSSSSoSSSiiiiiiiiPiPSiEiiSSSSiiiiiiiiiiiiiiiiiiiiiiiiiiiiTiiiiiTTiiSSSiiSiSSSiSSiiiiSiiSTTiSSiiiiiiiiiiiiEiiiiiiiiijiTZTSSiiYiiiiiiiiEiiiiiiiiiiiiiiiiiiiiii│
│iiSSSiiSSSSSiiiiiiiiiiUPPEEiiSSiSiiSiiiiiiEiiiiiiiiEiiiiiiiiiiiiiiiiiiiSSSiSiSSSiSSiiiiSSSTBSTTSSiSSiaiiriiiiiiiiiiiZiiiiiTZSiYYYYiiiiiiiiiiiqiiiiiiiiiiiiiiiiii│
│iSSSSSSSSEiiYiiiiiiiiiiUPPPSiSSSSWiSiiiiiiiiiiiiiiiiiiiEiiiEiiiiiiiiiSSSSiijSiSSYiiiiiiiiTTTTTTTSSSiiiiiiiiiiiiiiiiiiiiiiiTTTYYYYYYiiiiJciiiiiiiiiiiiiiiiiiSiiii│
│iSSSSSSSSSSiiiiiiiiiiiiiiiSSSSSSSiSLiiiiiiiimmiiiiiiiiiiiiiiiiiiiiiiiiSSSSjiSSiiYiiiiiiiiTTTTTTTSSiiiiiiiiiiiiiiiiiiiiiiiTiTTTYYYYnYiNiiiiiiiiiiiiiiiiiiiiiiiiii│
│SSSSSSSSSSSiSiiiiiiiiiiiiiSSSSSSSUiiiiiiiiiimmiiSiSiiiiiiisiiiiiiiiiiStSnnSSEYyiiSiiiiiiiTTTTTTTTSiiiiiiiiiiiiiiiiiiiiiiiiiTTZSSYYriYYiiiiiiiiiiiiiiiiiiiiiiiiii│
│SSiSStSSSiiiSiiSiiiiiiiiAiiSSiSSSSiiiiiiiqiimmiiiiiiiiiiiiiiiiiiDiiiSSttSnSiSiiiiiiiiiiiiTTTTTSSSSSiiiiiiiiiiiJiiiiiiiiZZZiZTYZSirYYYYiiiiiiSiiiiiiiiiJiiiiiiiii│
│SiiSSSSSiSitiiiiiiziiiiiiiSSiSiSiSikiiSSimmmmtmiiiiAiiiiiiiiiiiiiiiiiitHHSHSiiiSSiiiiiiSSSTTTSSSSSSiiiiiiiiiiiiiiiiiiiiZZSSSYYSZrrYYSYiiiiiSiiiiiiiiiiiiMiiiiiNi│
│iSiSSSSSSSStSSiiSiiiiiiiSiSSSiiiShiiiiSSnnmmmmtyiiiiiqiiiiiiiiiiiiiiSSSSSHSwSiiSiSiiiiiSSSTTSSSiSiiiiiiiiiiiiiiiiiMZiZZZZSSSSYiiSYYSiiiiiiiiiiiiiiiiiiiMiiSiiiii│
│iittiSttSSttSSiiiiiiiiiSSSSSiiiiiSSsiimmmmmmmmiiiiiiiiiiiiiiiiiiiiiSSSSSSSHHHiiSSSiiSiTjSSTSSSiiiiiiiiiippiiiiiiTiZiZZZZGhSSSSSSSYYSiiiiiSiiqiiiiiiiiiiiiiiiiiii│
│tttttttSttStSiSiiiiiiiiiSSSySUiiSSiSSiimmmmimiiiiiiiiiiifiiiiiiiiiiiiSSiSSSHHttiSiSiiSSSFSTiSiiiiiiiiiiiipiiiiZiZTiTTZZZihSSSoSSSSYYiiiiiiiiiiiiiOfiiiiiiiiioiii│
│ttttttttmtttpSiiiiiiiiiSSSSSSSSiiiSiSSiiimmiiiiiiiiiiiiiiiiiiiiiiiiiiiSiSiSSSHtiitSiiiSSSSSSiiiiiiiiiiiiiiiiiqiiTZTTTiZZiZSSSSSSSSSSiiiiiiiiiiiiiiEiJiiiiiiiiiii│
│tttttTttttSStSSSiiiiiiSSSSSSSSSiiiiSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiSSSSittFFFdSSSvSiiiiiiiiiiiiiiiiiiJiTTTTZTiZSSSSSSSSSSSSSiiiiiiiiiiiiuiiiiiiiiiiiiiii│
│tttttStttStSSSSkSiiSiiiSSSSSSSiiiiAiiiiiiiiiiiiiiiiiiiNiiiiiiiiiiiiiiiiiUSiSStSSHtFFtFSSSSSSiEiiiiiiiiiiiiiiiiTTTTZTTZSZSSSiSSSSSSSSSSSiiiiiUiiiiiiiiiiqiiiiiiii│
│ttttttStttSSSSSSSiSSiiiiSSSSSSiiiiiiiiaiiiiiiiiipOiiiiiiiiiiiiiiiiiiiiiiiiSStSSSttFFiSSiSSSiiiiiiiiiiiiiiiiiiiiTTiTZiTiSSiiiiiiSSXXSSXiiiiiiiiiiiiiiiiiiiiiiiiii│
│ttttttSStSSSSSSiSiSVqiiSSEiSiiiiiiiiiiiiiiiiiiiiiiiiiiqiiiiiiiiiiiniiiiiiiiSttSHittiFiiiSZiiiiiiiiqiiiiiiiiiiiiTTTiiTiSiiiiiiiiSSXXXXXXiyiiiiiiiiiiiiiiiiiiiiiii│
│ttStttSttSSSSSiSiiiiiiiiiwiiiiEiqiiiiiiiiiiiiiiiiiiipiiiiiiiiiiiiiiViSiiiiqSttSiitiiiicyiiiiiiiiiiiqiiiiiiiiiijTTTiiiiiiiiiiiiiiiiiXXXXiiiiiiiZiZZiiZiAiiijiiiii│
│tSttttttttSSiiiiiiSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiitDiitiiiiiiViiiiiiittttiiiitTiiiiiiiiiiiiiiiiiiiiiiiiiiiTTiTTiTiiiiiiiiiiiiiiXiiiiiiiiZZZZZZZZAiiiiiiiiii│
│SSttttttSSSSiiiiSiSSSSSiiiiiiiJiEiiiiiiiiiiiiiiiiiJiiitittiitiiiiVnViiiiitttittiiiTiTiiiiiiiiiiiiiiiiiiiiiTiiiTTiTiiiiiiiiiiiiiiiiiiiiiiiiiiiZpxZZZZZiiiiiiiiiii│
│SSStttttiSiiSiiiiSSSSSSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiitttttiiiiinZiiiiiittittttiiCiiiiifiiiiiiiiTiiiiiiiiiiiTTTTTiiiiiiiiiiiiiiiiiiiiiiiZiZZZpZZZiiiOiJiiiiiiii│
│SStStStiiiSyyiiSSSSSSSSSiiiiiiiiiiiiiiiiiiiiiiiiSiiiiiiittttBininniiiiitittiiiiiiiiiTTiiiTiiiiiiiiiiiiTiiiiiiiiTiTTiiiiiiiiiiiiiiiiiiniiiZZZZZZZZiZiiiiipiiiiEii│
│SSiiSYSiiiiSSSSSSSiSiiSSiiiiiiiiiiiiiEiiSiiiiiiiiiiiiiiitttttiininniiiiiitttitiiiiipiTiiiTiiiiiiiiiiTTiiiEiiiiiTiTiiiiTiiiiiiiiiiinnniiiZZZZZZZZZiiiiiiiiiiiiiii│
│SSSSiSYiiiiiSSSSSiSSiiiiiiUUUiiiiiiiiSiSiiiiiiiiiiiiiiiitittiiijinniiiiitiiitiiiiipiiiiTTiiiTiiiiiiiTTiiiiiiiiTTiiiTifiTiiiiiiiiinnnTiiZivZZZZZZZqiiiiiiiiiiiiii│
│SSSSiiSSiPPPPSSPPiiSiiiUyiiUiiiiiiiiSiSiiiiiiiiiiiiYtttiiitiiiiaiiiUiiimmiiiiiiiiiiiiiiiiTiiiTUiTiTiTiiiiiiiiiiiiifffiiiiEUiiiiiiininZiiiiZAZZZZZiiiiiiiiiJiiiii│
│SSSSSSiSPPiPPSMPPiiSiiiiUUiiiiiiiiiiSSSSiiAiiiiiiiYtttiiiittiiiiiiiiqiiimmiiiitiiiiiiiiiiiiiTTiTiZTTiiiiiTTiRiiiiiiiiiiiiiiiiiiiiinzZiiZZZhZZZZZiiiiiiitiiEiiiii│
│SSSSSiiiPPPPiPPPPiiiYYiUUiiiiiiiiiSYiSSiiiiiiiiiiYittttiiintiiiiiiiiiiiiSSiiEiiiiiiiiiiiSiiTpTTTTTTiiiiiiiiTiiiiiiTiiiiiiiiiiiiiiiiniiZZZZZZZZiiiiiiiiiiiiiiiiii│
│SSSSSiiiiPPPPPZPPSiiiYYUmymmmJiiSSSSoSiiSiiiiiiiYYYttttittttiiiiiiiiiiiiSSijiSyiipNiiSSSSSTTTTiTTlTTTiiiiiTTiiiiiiiTTTiiiiiiiEiiiiiniiiiiZZaZZiiiiiiiiNiiiiiiiii│
│SSSSxiiiiPPiiiiSPiPSSSYYmmmmJmmSiSSSSSiSSiiiiiiiiYttiYYiiiniiiiiiiiiiiiiiSiiSSSiiiiiiSSiSisSTTiiTTTTTiiiiiiTiiiiiiiiTkiiiiiiiiiiiinniiiiZZrZiiiiiMiiiiiiZiiiiiii│
│ESSSPiPiimiiiiSiSPPSSSSmPmmmmmiiSSSSSSSiSiiiiiiiiittiYiiiniiiiisiiiiiiiiiSSSSSSiSiiiiSSiTTSiSSiiiTiTTiiiiiiiTMiiiiiTjTiiiiiqiiiiiiiiiiiiZZZZZiiiMiiiiiMZiiiiiiii│
│SESSPjiimmiiiiSyPPXXSSSmmmYmmimSiSSSSSiSwSUiiUiiiiiiiiiiiiMiiisiiiiiiiiiiiSSiiiiiiiiiSiTiTTiSSiiiiTiiTTiiiiiiiiiiSiVTVTViiiiiiiiiiiiiiiviZiZZZiiMiiiiiiZiiiiiiii│
│EiSESPPimiiiiiPPPPXPPSSmmammmmmSiiSSSSSSSiSSiiiiUiiiYYEiipiMiiiititiiiiiiiisiiiiiiiiiSiiiiiZTZZgiiiiiiiiiiiNiiSiiSSTViViiiiiiiiiiiiiiiiiZiZZZJZZZiiiiZZZZZiiiiii│
│PPiPSSPPmmmiiiPPPXPPSSSmmmmmmmiiiiiSSSSSiiSiiiiiiiOiYYntiiijiiiiittiiiiiiiiiiiiiiiiiiipiiiiZZZZiiiiiiiiiiiiiiSSSSSVVViiZiViiiiiciiiiiiiiiiinZZZiZZZZOZZZZiiiiiii│
│PPiiSPPPPmmmiPPPPSPPPPmPmmmmmiiiiiiiSSSSiUiiiiiiiiiiYYtttiiitiitttiiiiiiiiiiiiiRiiiiiiiiiZiZZZnmiiiiiiiiiiiiiSSVSSSVEZZEEiiiiiiiiiiiiiiiiinniZiiqZZZZZZZZiiiiiif│
│sPsimSPPPPmmmmPmSPSSPPPPPmmmSiiiiiiSASSiiiiSiiiiiiiYiYtttpiiiiiiLtiiiiiiiiiiiiiiiiiiiiiAZZVZZZnmJiiiiiiiiiiiSiSVVSVVVZEEEiiiiiiiiipiiiiiMnniiimqZZZiZZZZJiiiiiii│
│PsPmmmmPPmmmEmmmZmSPSSPPmmnmmSSaSiSSSSSiiiiiSiiiiiiiiYYtttitiiiaYiiiiiiiiiiiSiiiiiiiiiiiZZZZiZmniiiiiiiiiiiSiiSVESSVZZSSEipikiSiiiiniiiiiiiiiiiiZZZZinZniiiqiiii│
│PPmmmPmxPmrSSmmZZZBSSSSSSnSmmSSSSSSSSSSiiiiiiiiiiiiiYYttPitiiiiiiYYiiiiqiiiSiiiSiiiiiiiiZZZZZmmrVViiiiiiiiiSiiiSSSVSZTSSESiiiiiiSnXiiyiiiniiiiZZZZiZniiiiiPUiiii│
│mSmmmmSSmSSSmmmZZBPSSSSSSSmSiSSSSSiSiSSSiiiiiiiiiiiiitSYPiYYiYYYYYYiiiiiiiiSSSSSSiiiiicTZZZZZZTViViiiiiiiiiiiiiSSVVVSSSSSiiiiiiiiiipiisnsnsiiZZnZinnZninnYUYUUii│
│mmmmmSmmiSSSSiSBBPPiiUYYSSttSSSSSSiiSSSSiiiiiiiiiiiiiiiSYiYYYYYYiiiiiiiiiiiiiSSSiSiiiiiTTTTZVTNiViiiEiiiiiiiiiEiSSYSSSSSSSiiiiiiiiniiissniiiiZiiiiiiininnUYUUyUi│
│gmmmmSmiSSSSSiiBBBSiSiYYVVStSSSiSiiSSSSiiiiiiiiiiiiiiYiSYYYYYYYiiiiiiiiiiiiiiiSSiiiiiiiTTTTjTVVTViViiiiiiiiiiiiiXSSSSSSSSSiiiiiiiiiiiiisssiiiiiiiniiiinnUnEUUUii│
│mmmmmmSmiPiiiiiiBiiiiiEYYYSSSSSSiiiiiSiiSiiiiiiiiiiiiYStrrYYYYiiiiiiiiiiiiiiiiiiijiiiiiiTTTjTTTVVViViiiiZiiiXiiXiSSSSSSSSiiiiiiiiniiiiiiiiiiiiiinniinnnnUEEnUiUU│
│mmmmmmmiiiiiiiiiiPPiiiiYYYYYYYYYiiiiiiSiiiiiiiiiiiiiiiYrirSYYYYYiiiiiiiiiitiiipiiiiiiiiiinTTTVVTViSiiiiiZZSiiXmXXXSSSSSSSiiiiiiiiiiiiiiiiiiiiiiiiinnnnnEEEUWiiii│
│mmmmmmisiiJiEiiiPriiiiiYmmYYYYYYiiSSSiiiiiiiiiiiiiisiioiirSYYYiiiiiiiiiiiiitiiiiiiUiinSiiiiiVVVVTSiiiijSXZSSXXXXYSSSSSSSSiiiiiiiiiiiiiiiiiiiiiiiiiiinnnnnEiiiiiN│
│SSmmmiiiiiiiiiiiiirimiimmYIYYYYiYSSSiiiiiiiiiiiiiSiiiiiiSSSSYYiiiiiiOiiiiiitiaiiiiiiinnniiVVdVVTVViiitSSXXSSXXSXXXXSSSSSSiiiiiiiXiiiiiiiiiiiiiiiiUiinnnnEEiniiii│
│SmmmimmiijjiiiigiirrmiimmummYSkSSSSiiiiiiiiiiiiiiiiiiiiiaSSSSSiiiiiiiiiiiiqiiiiipinniiEnniiiVVVTTVTiitSSkpXXiYXXttSSSSSSiiiiiiXXXiijjOiiiiUiiiiiiiinnnYEEinniiii│
│SimiimiiiUiiiiiiririiiiimuumzYYSSSSiiiiiiiiiiiiiiiiiiiiJiSSvSiSiiiiiiiiiiiiiitiinnniiiiiniiVVVtVTiiiiSSSXSXXSXXXXtSSmStiiiiiiiiXiiiiiiiiiiiiiyiEiiiinninnnniiiii│
│SmiiiiiiiiSiiiiirrUfmmiiEuumYEEESiiiiiiiiiiiiiiiiiiiiSiSSStSiiJiiiqiiiiiiiiiitiiiniiiiiiiiVVVtVtTTiitTSSSSSYYSXXXXtttmtttiiiiiiXiiiiiiiiiiiiiiiiiiininnUnnnnniii│
│SSiiiiiSSiiUiiiirrEErEEEmEEYYEEiSiiiiiiiiiiiiiiiiiiiSSStSSSSSiiSiiiiiiiiiiiiiiOiiiiiiiiiiiiitVzMVVTiiiTSSSSSSSSXXXnptttttxiiiiiiiiiiiiiiiiiiiiiiipiitZUUnniiiiii│
│SSiiiiiSiSSiiiiirrrEEEEEEmEmEEESiiiiiiiiiiiiiiiiiiSSiiStSSSSSSiiiiiiiiiiiiiiiiOiiiiiiiiiiiiiVtttnnniiTSSSSSSSSSSSSSttttttiiiiiiiiiiiiiiiiiiiiiiiiiiinnZinniiiiEi│
│iSiiSiiSiSEiiiiiirrrEMEEEEmEEEEiiiiiiiiiiiiiiiiiqSiiEttPSSSSSSiiiiiiiOiiiiiiiiiiiiiiiiiiiiiAittntnnniiiiSSnSSSSSSSSStttEtiiiqiiiiiiiiiiiiiiiiUiiiiiinZiiiiiiiiUi│
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
Total of 8000 warriors, average score = 126.6
Using characters for viewmode=2 with viewshift=0
Found 140 unique instruction sequences...
4934 best 151.3 by 063_43 "i" SPL MOV MOV MOV DJN
583 best 139.6 by 007_03 "S" SPL MOV MOV DJN DJN
522 best 139.0 by 077_16 "S" SPL MOV MOV JMP SNE
278 best 138.8 by 071_09 "t" SPL MOV MOV JMP MOV
234 best 133.0 by 003_27 "Z" SPL MOV MOV DJN MOV
234 best 127.3 by 092_08 "T" SPL MOV MOV DJN SPL
223 best 137.1 by 117_46 "m" SPL MOV MOV JMP DJN
146 best 138.1 by 040_09 "n" SPL MOV MOV JMP SPL
111 best 139.3 by 020_36 "P" SPL MOV MOV DJN MOD
.....

There was this in the top dir...

;redcode-nano
;name 097_12.red
;author yabevolver
;strategy evolved
;origin 045_26
;species 1216
;generation 907
;created 02-20-2023 11-02-58
;assert CORESIZE==80
ORG 0
SPL.I # 39,< 55
MOV.I * 71,{ 0
MOV.I > 55,{ -2
MOV.I { -3,< 52
DJN.I $ -2,{ -4
END

Back to the boring SPL MOV MOV MOV DJN form but it scores 151.90 against the nano2301 test set, 151.24 against nano106, and 150.04 against nanoBM07. Scores 150-156 against all of my test sets, consistent.

Here's another strong one...

;redcode-nano
;name 085_31.red
;author yabevolver
;strategy evolved
;origin 045_26
;species 7916
;generation 869
;created 02-20-2023 10-20-57
;assert CORESIZE==80
ORG 0
SPL.I # 39,< 55
MOV.I * 70,{ 0
MOV.I > 57,{ -2
MOV.I { -3,< 52
DJN.I $ -2,{ -4
END

...very similar. Scores 151.43 against nano2301, 152.04 against nano106 and 151.78 against nanoBM07.

High score against just one test set doesn't always translate, this warrior gets 156.64 against nanoBM07...

;redcode-nano
;name 145_13.red
;author yabevolver
;strategy evolved
;origin 045_26
;species 58402
;generation 758
;created 02-20-2023 08-13-36
;assert CORESIZE==80
ORG 0
SPL.B # 34,< 56
MOV.I } 75,{ 0
MOV.I > 61,{ -2
MOV.I { -3,< 53
DJN.I $ -3,< 37
END

...but only 144.03 against nano2301 and 150.12 against nano106. This is why the evolver saves warriors within a certain percentage of the top score as the top-scoring warrior itself is often over-specialized and adapted to the current test set.

The next run also produced fairly strong but more specialized warriors...

┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│MExxxxxxxxxlxxxxxxxxxxMMMMMMMMMMxMxMxMMMMMMMMMMMMMMxMMMMxxxRxxxxMMMMMMSMxxxxxxxxqMMMMMMMMMMMMMMEMMMMMMMKMMMMMMMMMMMMMMMxxxxxxxxMiMMMMMMMMMMMMMMEEEEEEEMMMMMMMMMM│
│MEEMxxxjxxxxxxLxxxxxxqMMMMMMMMMMMMMMxMMMMMMMMMMMMMMMMMMMxxxxxxxxxxMMMMMxMxxxxxxxMMMMMMMMMMOMMMMMMMMMMMMMMMMMMMMMMMMMMMxxxxxxxxxMMMMMMMMMMMMMMMMMMEEEMEEMMMMMMMMM│
│MMMxxxxxxxxxqxxxxxxxxxMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMxxxxxxxxxxMMMxMMMxxxxxxxMqMMMMMMMMMMMMMMMMMMMMMMMMnMMMMMMMMMMMMMxxxxxMxxxMMMMMMMMMMMMMMMEEEEEMEuMMMMMMMM│
│MMxxxxxxxxxxxxxxxxxxxxMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMcxxxxxxqxxxMxMxMxMxxxxMMMMMMMMMMMMiMMMMMMMMMMMMMMMMMMMMMMMgMMMMMxxxxxxMxxxMMMMMMMMMMMMMMEEEEEMMMMMMMMMMM│
│MMxxMxMMxxxxxxMMMxxxxxMxEEMMMxMMMMMMxMMMMMRMMMMMMwMMxMMcxxxxxxxxxxxxxxMxxMMxxxxMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMzMMMMMMMxxxxxMMxxxxMxMMMMMMMxMMMEEEEEEMMyMMMMMMN│
│MMMxxMMxxxxxxhMMxxxxxxxxxMMwwMxxMMMMMMMMMMMMMMMMMMMxxMxxxxxxMxxxxxxxTMxxxxxxxxxMMMMxMMMMMwMMMMMMMMMMMMMyMMMMMMMMMMMMMMMMMRxxxxxxxxxMMMMMMMxxxxMEEEExEEEMyyMMMMNN│
│xxxhMxxMxxxxxMMxxMxxxxxxxMMMMMMMMxxMMMMxxMMMMMMMMMMMxxxxxxxMxMxxxxxxxxMxxxxxxMMMMMxWxxMMMMMMxMMMMMMMMMMMMMMMMMMMMMMMMMMMxMMMMxxxxxxxxMxMMMxxxxxMEExExxMMxMMMMNNN│
│MxMMxxMMxxxxxMMxDMxxxxxxxMxMMxxMxxxMMxiMMMMMMMMMMMMMMMxxxxMMMMxwxxxxxxxxxxxxMMMMMxxxxMMMcMMxMMMMMMMMMMMzMMMMMMMMMEMMMMMMxMxMxMxxMxxMMxMMMxMxxxMzxxExxxxMxxMMNxxM│
│BxxUxxxMxxMMMMxxMMMxxxxMxMMxxxxxxxxxxMMMMMMMMMMMMMMMMxxxMxxMMMMxxxMxxxxxxhxxxMMMMxxxMMMMMxMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMxxxxMNxxMMMMMMMqxMzxqqxxxxxxxMxxxxMx│
│xxxxjxMxMMxxMxxxxxMxxxMxMMMMxxxxxxxxMMMMMMMMMMMMMMMMMxxMMjxxxxMMMxxxxxxxxxxxxMMMxcccMMxMxMxMMMMMMMMMMMMMMGMMqMMMMMMMMyyUMMMMMxxxMMqMMMMMMMMqxqqqqqExxxxxxxxxxxMM│
│xxxxxMxxxxxxxxxxxMxMxxMMMMxxxxxxxxMxxMMMMMMMMMMMMMMMMMMxMMMpxxMMMMxxxxxxxxxMxMMMMMxxMYMxxxMxxMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMxMMMMMMMMMMMMMMxqqqqqqxxxxxxxxxxMMMM│
│xxxxxxxxxxxxxxxxxxxMMMMxMxxxxxxxxxxxMMMMMMxMMMMMMMMMMMMMMMMxxxMMMxxxxxxxxxxMxMMMMMxwNNMMxxxpxMMMMMMMMMMMMMMMMMMMMMMMMRMMMMMMxMMMMMMMMMMMMxxqqqiqqqxxxxxxxxxxMMMM│
│xYxxxxNxxxxxxxxxxxxxMMMxMxxxxxxUMUMxMMMMMxMxMMMxxxxMMMMMMMMMMMMMqMxxxxxxxMxxMxMMMMyMNMMxxMMxxxxMMMMaaMxMMMMMMMMMMMMMMMMMMMMMMMxMMMbMMTMMMMxqqqqiqqqqqxxxEExMMMME│
│xxxxxxxxxxxxxxxxxxxMxxxxMxxxxxxMxMxMMMMMMMMMMMMxjxMMMMMMMMMMMMMMMMxxxxxxxxxMMMMMMMMMMMMMMxMxxxxMMMMMMxxxxMMMMxMMMMMMMMMMMxxMMxxxMMMMMMMMMMqMqqqiMqqqMxxxiEMMEEEE│
│xxxxxxxxxxxxxxxxxxxxxxMMMxxxxxxMMMMMMMMxMMMMEMMxxMMMMMMMMMMMMMMxMMxxxxxxxxxxMMMMMMMMMMMMMMxxxxxMMMMMMxxxxxxxxMMMMMMMMMMMxMMMMMMMMMMMMMMMxxxMMqMMMqqqMMxxEEMMEEEE│
│MxRJxxxxxxxxxxxxxxxxxMMMMMMxxxxxxMMMxMMxMMMBMMMMMxMMMMMMMMMMMMMMxMxxxMMTxxxxMxxMxMMMMMMxxMxxxxxMMMMxxxxxxxxxxqMMMMMMMMMMMMMaMMMMMMMMMMMMMcxcxMcMMMxxMMMMxMEMEEEE│
│xxJxxxxxxxxxxxxMxxxxdxMMMxMxxxxxxxMMxMxxxMcMMMMMExMMMMMMMMuMMMMxxMxxxxMMuuxxxxxMMMMMMMMMMMxxxxxMMqxMxxxxMMuMMMMMMMMMMMMMMMxxMMMMMMMMMyMcMccxccxMMqyyMMMMMMEEEEEE│
│xxxxxxxxxxxxxxMMMxxxxxxxxxxxxxxxxxMMMMMxMMMMMMMMxxMMMMMMMMMMMMMxxxxxxMxMuxxxxxMxMMqqMMMMMMMxxxxMMMMxMxxMMMMMMMMMMEMMMMMMMMMMbMMMMMMMMMMcMccxxcxxMqMMMMMMMMEMMMEx│
│xxxxxxxxxxxxxxMMMEMxxMxxxxxMxxxxxxMxxMMMMMMxMMMMMxMhMwMMMMMMMMMMxxxxMxxxMxxxxxRxxxxqMMMMMMWMMMMMMMMxxcMMiMMMMMMMMMMMMMMMMMMMxMMMMyqMMMMMMcccxxxxMxxxMMMMMxxxxMxx│
│xxxxUxxxxxxxxxMMMxxxxxxxxxMxxxxxxxMMxMMMxMMMcAMcMMMMMMMMVMMMMMMMxxxNNxxxxxxxxxxxxxMMxxxMxMMMiMMMMMxxxxxMMMMMMMMMMMMMMMMxMMMMMMMMMMMMMMRMMcMxxxxxMMMxMxxMMxxxxMxx│
│xxxxxxxxxpxxxxMMMxxxxxxxxxMxxxxxxxxxxxMxxxxMxcxxMMMMMMMMMNMMMMMMMMMxMxxBxxxxxxxxxxMxxxxxxxMMMMMMMMMxxxMMMMMMMMMxMMMMMMxMxMxMMMMMMMMMMMMMMxxMMxxxMMxMMMxMMMxQMxxx│
│MMMmxxxxxxxxxxxMMxxxxxxxxxxxxxxxxxxxxxjxxxMxxMxxpMMMOMMTMMxxMMMMMMMhxMMxxxxxxxxxxxxxxxWxxxMMMMxxxxMxxMMMMMMxYMxxMxxMMMMMMxxMMMMMMMMMMMMMMMMMxxMMMxxMMMMMMxxxxMMx│
│MMMxxxxxxxxxxxxxxxxMxxxxxxxUxxxxxxxxxxxxxxxxxxxxxMxxMMMyMMMMMMxMGxxMMxxxxxxxxxxxxxxxxxxxxxxxMMxxxxxxMMMVMxxxxxxxxxxMxxxxMMxMMMMMMMMMMMMMMMMMxxMMxMMMMMMMMxxxxxxx│
│MMMxxxTxxxxxxxxMxxxMxxxxxxxxxxxxxxxxxxxxxxRxxxxxxxxxMMxMMMMMMxxMMMxMWMxtxxxxxxxxxxxxxxbxxxMxMqMxMxxxMMMMMxMxMxxMRxxxxxMMMMMxMMMMMMMMMMMMMMMMMMMxMqMMMMxxMxxxxxxx│
│MMxxxxMxxxxMMxxxMMMxxxxxxUxxxxxxxxxxxxxxxxxxxxxxxxxMMMMMMMMMxMMMMxxxMMMxxxxxxxxxxxxxxxxMMMMMOOMxxxMxxwMMMxMMxxxMMxxMxUMMMMnMMMMMMMMMMTMMMMMMxxyxxMMxMMMxxxxxxxxx│
│MxxMMMMNxMMxxMMMMMMMRxxxxxxxxxxxxxxxxxxxxxxxxxxxxMxMxMMMMMMMxxxxMMMxMMxxxxxxxxxxMxMxxMMMMMMMxOxxMMxxxMxMMMMMxxxxxxxMxMDMMMMMMMMMMuMMMMEMMMMMxxMMMqxMxMMMMxxxxxxx│
│xMMMMxMxxxrxxxMMxMMMMMxxMIxxxxxxxxxHxxxxxxxxBbxxxxhxMxxMxMMMMxxxxxMMMxxxxxxxxxxxxMMsxiMMMMMMMxxxxMxxxxxxMMMMMMxMxxpMMMDMMMMMMMMMMMMMMMMMMMMxMMMMMMxxMxMMxxxxxxxx│
│MMMMMMxxxxMMMMMMxMMMMMMxMMxxxxxxxxxxqxxxxxxMxxxxxxxMxxxMMMMxxMMMxxMMxxxxxxxxxxxxxxMMMxMMMMMMMxxMMxxxxxxxMMMxMMMMMMMMMMMMMMMMMMMMMEMMMMMMMyMMxxMMxxxMxxxxxxxxxxxx│
│MMMMMxMxxMEiMMMxMxMMMMMMMxxxxxxxxxDxxxxxxMxxxxxxxxxxxxxxxxxxxxMMMMMxxMMMxxxxxxxxxxMMxwMMMMMMMMMMMxxxxxxxxMMMMxMxMMiwMMMMxxMMMMMMMMMMMMMMMMMxxxxxxxxxxxMMxxxxxxxx│
│MMMMMMxxMMMMMMMMMxMMMMxxxEExxTxxxxxxxxxxxxxxxxxxxxxxxUxxxxxxxxxMMMuMMMMMxMxMMxxxxxxMMMMMMMMMMxxMxxxxxxxxxxMMMMMMMxMMMMMxxxMMMMMEMMMMxxxxMMMxxxMxxxxxxxMxxlxxxxxx│
│MMMxMMMxxMMMMMMMMMMMMEMWxMMxMxxxxVxxxxxxxxxxxxxxxxxxxxxxxxxxxxHMxMxMMMMMMxxMMxxxxxMMMMMMMMMxxxxxxxxxxxxxxMMMMMMMMMMMxxMxxMMMMMMMMMMMMMxxxxxMxxMMxxxaMxxpxxxxxxxx│
│MMxMMyMMMMMMMMMEMMMMMMMxMMMMxxxxxxxxxxxxpxxxxxMxxcxxxxMxMxxxxxxxxxxxMMMMMxMMxxxMMxxMMMMMMMMMMxxxxxxxxxxxxMMMMxxMxxxxxxxxTMMMMMMMMMMMMyMMxxMxxxxxMMMMMMMxxxxxxxxx│
│MxxxMMMMxxxxMMEMMMMMMMMMxxxMMxxxxxxxxxxxxYxxxxxxxxxxxxMxMxxxxxxxxxxxMxMxxMMMxxrMMxxMxMMMMMMMMxxxxxxxxxxxxMMxxxxxxxxxxMxxMMMMMMMMVMxMMMMMMMMxxxxMxMMMMMMxxxMxjxxx│
│xxxxMRBxxxxxxMMMMMMMMpMMMxMxxxxxxbxxxxxxxxxxxxxxxxxxxxxxMMxxxxxxxxxMxxxxxxxMMxMMwxMMMxMMMMMMMxxxxxxxxxxxxxxxxMxxMxxxxMMxMMMMMWMxMMuMMMMMMMxMMxxxxMMMMMMxxxMMxxxx│
│MMxxxxRxxMxxxxMMMMMMMMxMxMMxxxxxxxxxxxxxxxxxxxxxxxxxBIxxxxYxjxxxxxxxxxxxxxxMMMMMxxxMMMMMMMMMMMxxxxxxxxxxxxMxxxxMMMxMEMMxMMMMMxMxxMuuMMMxMMMMMxxxxMMMMMMxMxMMMxMM│
│MMxxxxxxxMxxxMqMMMMMxxMxMMMxMMMMxxxMMxMMxxxxxxxxxxxxxxxxxxxExxxxxxxxxxxxxxxMMMMMMMMMMMMMMMMxxcxxxxxxxxxxxxxxMxMMMMMxxxMxxxxMxxxxxxxMMxMMxMMMMMxxEMMMMMMgMxxxMMMM│
│MMMxxxxxMMxxMMMMMxMxxxxxxMMxxMMMMEMMMxMMMxxxxxxxxxxxxxxxxxExxxxxxxxxxxxxxxxxMMMMMMMMMMMMMMMMxxxMMxxxxxxxxxxxMMMMMMMMMMxxMxxxxxxxxMMMxxpxMxxxMxxMUfEMNMMMxxxMMMMM│
│MMMvMxxxMxMMMMrMxxMMMxMxxxxMxMMMMMMxxMMMMMxxxxxxxxxxxxMMMMMxxxxxxxxxxxxxxxxMMMMMMEccMMMMMMMMMixMMxxxxxxxxMMMMMMMEMMxMMMxxxxxxxxxxxxxcxxxxqqMMxxxMxEEMEMMxxxMMxMx│
│MMMxxxxqMMMMMMrxxxxMMsMxxxxMMMMMMMMMEMMMMMxxxxxxxxxxxxxMNxxxxcxxxxRxxxxxYxxMMxMMxMcwccMMMxxMMxxMMxxxxbxxxOMyMWMMMMMMMMMxhWxxxxxxxxxxxxxxxNMMMMMMMMNEMMMMxxMxxxxx│
│MMMxMMMMMMMxMxxxxxxxMMMxxMMxxxMMMMMMMMnMMMMxMxxcxxxxxxxUMMMMMxxxxxxxxxxxxxxxxxxxxxcccciMMxxxxMMMMMxxxxxxxMMMMMMMMMMMMMMMxxxxxxMxxxxxxxxxxxxMMMMMMMMMMMMMMMMxxxxx│
│MMMMMMMMxMMMxxxxxxxxxMxxxxxxxxxMMMMMMMMMRMMMMxxxxxxxxxxMUMMMMMMxxxxxxxxxxxxxxxxxxxccccMEMxxcxxxMMxxxMMxxxxMMMMMMMMMMMMMMMxxxxxMMrMMMMxxxxxMMMMMMMMMMMxxMMxxMxxxx│
│MMMMMxMMMxxxxpxxxxxfxxxxxxMvxxMMMMMxxMUMMMsxxxxxxxxxxxxMMMMxsxMMxxxxxxxxxxxxxxxxxczUxccExMxxxxxxxwxxMMMUxMMMMMMMMMVMMMMMxxxxxxxMMMMMxxxxxxxMMMxMMMMMMxxMMMxxxxxx│
│MMiMxxMMMMMxMxxxxxxxfbxxxxvxMxxMMMMMxxxxxMMxxxxxxxxxxxxMMMMMMxxxxxxxxxxxxxxxxxxxFxxxxcEcxxxxxxxxxxxxxxcxMqMMMMMMMMMMMcMxxxxxMxMMMMMMxxxxxxMxMxMMMMMMMMxMMMMxxxxx│
│MMxxxxMMMMwMMMxxxxxxxxxxxxxxxMxMMMMMnxMxxMxxxxxxxxxxxxxqMMMMMxxxxxxxxxxxxxxxjxxxxxxxcExxxMxxMMMxxxxxxcMMMMMMMMMMMMMMMxMMMxxMMxxMMMMMxMxxxxxxxMMMMMxMMxxxMMMxxxxr│
│MMxxxMxMXxxMMxxMxxxxxxxxxxxxxMMMMMMMxMxxMxcxxxxxrxxcxxxqMMMMMxxxxxxxxMMMxxxxxxxxxxxxxxxxMxxMKMMMxxxxXQMMMMMMMiMMMMMMxxxMxxMMMMxMMMMMMMMMMxxxxMMMxxxxxMMxxMMxxxrx│
│MxxxxMxMxxxMxxxMMMxxMMxxxxxxMMMMwMMMxxxxxxxxxxxxxxxxxxxxxMMxMMMxxxMMMMMMMxxxxxxxxxxxxxxxxxxxMMKxxxxMMMMMMMMMMMMMMMMRMRRMMxbMxMMMMMMMMMMMMxxxxoMWxxhxxxxxxxxxxxxx│
│MMMMMUMMMxxMxxxMMxxxxMMxxxMMMMMMMMMMMMxxxxxxxxxxxxMMMxxxxMxxMMxxxMwMMMMMxxxxxxxxxxxxxrxxxxxxMMMxpxxMMMMMMMMMMMMMRMMMRRRxMMxxxMMMMMMMMMMMxxxxxxxxxxxxxxxxxxxxxxxx│
│MMMMMMMKMxxxxxxMxMxxMMxMxMxMMMMMMMxxxxxxxxxxxxxxxxMxMMxxxxxMMxMMMMMMMMMxxxxxxxxxxxxxHxxMxxxxMMMMrxxxMMMxNMMMMMMMMMRRRRxxxxxxxxMxxMMMMMMMxxxMxxxxxxxxxxxxxxxxxxxx│
│MMMMMMMMMxxMxxxxxxxxMMxxxxxMMMMMMMMxxxxxxxxxxxxxxxxxhxxxxxxxMMxMMMxMMMxxxxxxxxxxxxxxxxxxxxRxExMxMxxxxMMMxxMMMMMMyMRRRRRMxxxxxxxxMMMMMMMMxqxxMxxxxxxxxxxxxxxxxxxx│
│MMMMMMMMMxxMxxxxxxxxxMMMxxMMMMMMMMNMRxxxxxxxxxxxxxxxxxxxxxMMMMxxxMxxMxxMMxxxxxxxxxxcxxxxxxxxxxxxMMxxxMxxxMxMMMMMMMRWRRMRMxxxxWMMMMMMMMMMxMMMMxxxxxxxxxxxxxxxxxxx│
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
Total of 8000 warriors, average score = 121.7
Using characters for viewmode=2 with viewshift=0
Found 104 unique instruction sequences...
3729 best 148.9 by 077_09 "M" MOV SPL MOV MOV DJN
3716 best 146.6 by 078_06 "x" SPL SPL MOV MOV MOV
99 best 134.6 by 144_01 "E" SPL MOV MOV DJN
78 best 125.2 by 061_17 "q" SPL SPL MOV MOV DJN
47 best 129.5 by 083_42 "c" ADD SPL MOV MOV DJN
.....

Top scoring warrior against nano2301...

;redcode-nano
;name 006_12.red
;author yabevolver
;strategy evolved
;origin 083_42
;species 72236
;generation 701
;created 02-21-2023 05-55-31
;assert CORESIZE==80
ORG 0
SPL.I # 4,> 75
SPL.I # 18,} 0
MOV.I } 48,} -1
MOV.I { 2,} -1
MOV.I # 12,} -1
END

...153.59 against nano2301 but only 141.12 against nano106 and 134.73 against nanoBM07 - very specialized.

Top scoring warrior against nano106 and nanoBM07...

;redcode-nano
;name 119_34.red
;author yabevolver
;strategy evolved
;origin 061_10
;species 68564
;generation 611
;created 02-21-2023 04-32-57
;assert CORESIZE==80
ORG 0
MOV.I > 39,$ 61
SPL.I # 54,< 75
MOV.I < 26,{ 79
MOV.I { -1,< -2
DJN.I $ 78,$ -3
END

...152.82 against nano106 and 158.97 against nanoBM07 but only 143.19 against nano2301.

This one is more balanced...

;redcode-nano
;name 079_07.red
;author yabevolver
;strategy evolved
;origin 061_10
;species 40400
;generation 832
;created 02-21-2023 08-01-23
;assert CORESIZE==80
ORG 0
MOV.I > 40,$ 60
SPL.B # 50,{ 75
MOV.I > 24,{ 79
MOV.I { -1,< -2
DJN.I $ -2,$ -3
END

...148.99 against nano2301, 150.19 against nano106 and 151.43 against nanoBM07. Note that the last lines of the two preceding warriors are the same - "78" and "-2" refer to the same location, it's all mod 80, sometimes dupes slip through because of this. Probably should fix the warrior write code so that any back reference to within the warrior code is negative, otherwise positive so the identical code will always be written the same way.

To find the strongest overall warrior I made a script that cycles through the warriors and grabs and accumulates the scores from my various benchmarks to produce a cross-referenced report...

Cross-referenced benchmarks...
Acc.Score Warrior nano106 nano2301 nanob2 nanobm15 nanoBM07 nanoht1 nanoht2 nanoht3
1222.53 138_16.red 152.58 143.96 153.30 152.69 158.54 155.07 153.89 152.50
1217.37 119_34.red 152.82 143.19 153.41 151.70 158.97 153.16 151.66 152.46
1216.29 089_38.red 151.90 143.35 152.46 152.21 156.97 154.17 152.81 152.42
1213.8 028_40.red 151.49 143.09 152.25 152.34 156.33 153.45 152.66 152.19
1210.32 092_15.red 150.95 143.12 153.38 151.28 155.80 152.93 151.85 151.01
1210.32 039_50.red 150.95 143.12 153.38 151.28 155.80 152.93 151.85 151.01
1207.77 082_30.red 150.65 142.72 151.47 151.47 155.49 152.32 152.18 151.47
1207.57 074_19.red 150.74 143.84 151.97 151.17 155.05 152.27 151.44 151.09
1206.68 153_39.red 151.58 143.00 152.60 149.68 157.85 151.43 150.09 150.45
1206.68 127_19.red 151.58 143.00 152.60 149.68 157.85 151.43 150.09 150.45
1195.07 082_02.red 150.19 148.99 154.43 145.59 151.43 148.73 148.79 146.92
1195.07 079_07.red 150.19 148.99 154.43 145.59 151.43 148.73 148.79 146.92
....

(LOL) and the winner is...

;redcode-nano
;name 138_16.red
;author yabevolver
;strategy evolved
;origin 061_10
;species 31211
;generation 671
;created 02-21-2023 05-26-26
;assert CORESIZE==80
ORG 0
MOV.I > 39,$ 58
SPL.F # 54,< 74
MOV.I < 26,{ 79
MOV.I { -1,< -2
DJN.I $ 78,$ -3
END

2/25/23 - Modified YabEvolver so that only back-references within the warrior code are negative, all other numbers are mod coresize. To implement this added a normalizenumbers(x,y) sub that is called when warriors are created or mutated, hopefully this doesn't slow it down too much but tired of seeing "78" when it should be "-2". This also prevents saving equivalent dups.

Here's a strong nano warrior from a huge run...

;redcode-nano
;name 128_48.red
;author yabevolver
;strategy evolved
;origin 030_43
;species 81224
;generation 820
;created 02-24-2023 01-56-15
;assert CORESIZE==80
ORG 0
SPL.I # 76,> 67
MOV.I { 0,> 23
MOV.I > 30,{ -2
MOV.I { -1,{ 39
DJN.I $ -2,{ 53
END

...scores over 152 against all my test sets...

Acc.Score 	Warrior          	   nano106  nano2301 nanob2   nanobm15 nanoBM07 nanoht1  nanoht2  nanoht3
1241.66 HR10c_128_48.red 156.21 155.91 156.51 153.96 155.59 153.94 156.67 152.87

It's yet another sort of boring SPL MOV MOV MOV DJN but it puts up a pretty good fight...

Testing HR10c_128_48.red...
Anefam Vesyes 142.25 66 4 **************
Another MEVO Thing 168.30 76 11 ****************
b69e6908-a5be0bba-ab4445 178.16 82 7 *****************
Clone 166.19 77 5 ****************
Cold Plasma 97.887 40 19 *********
Combat Arithmetic 111.26 49 11 ***********
Creamed Corn 163.38 76 4 ****************
Crimson Climber 142.95 66 5 **************
Crisis 133.80 61 7 *************
[RS] C-Strobia 156.33 73 3 ***************
eerie glow 166.90 75 12 ****************
Free the Fish! 165.49 72 19 ****************
Hot Soup 142.25 65 7 **************
Just a MEVO Thing 159.85 75 2 ***************
Little Bang 154.22 71 6 ***************
Little Red Rat 180.98 85 2 ******************
7_3 135.21 63 3 *************
NanoCracker 147.88 66 12 **************
02_08 133.09 61 6 *************
[RS] Nextratulated Sturv 147.18 68 5 **************
Nonlocality 2 161.97 72 14 ****************
Nonlocality 178.16 80 13 *****************
Quantum Foam 180.98 83 8 ******************
Reflow Oven 147.18 67 8 **************
Sleepy Lepus 176.05 80 10 *****************
Steaming Pebbles 156.33 71 9 ***************
SWT2-21 172.53 81 2 *****************
SWT2-28 156.33 73 3 ***************
Transfer Function 140.14 64 7 **************
Unholy Fire 214.08 99 7 *********************
Score = 155.91

In the performance chart the numbers after the score represent wins and ties, the opponent wins aren't noted but can be inferred by subracting the numbers from 142, the number of rounds in a -P nano match.

Here are the scripts and test sets I'm currently using for testing warriors, at least for now. The scripts require Blassic which is still available but is no longer maintained as far as I can tell, need to rewrite them to use something else.. one day maybe.

3/3/23 - The option 4 analysis/benchmark report now can include listings of the top-scoring warriors from the top sequences and how they perform against the test warriors (same format as soup display benchmark option), a snapshot of the soup display, the full benchmark list is now optional, and prompts for all the options up front. It now works like this...

   Other functions...
1 - Change the benchmark set
2 - Load settings file (some parms ignored)
3 - Show soup sequences and population numbers
4 - Sequence and benchmark the entire soup
5 - Change settings variable(s)
6 - Display variable(s)
7 - Display all settings
8 - Save settings to file
10 - Start/stop ANSI dumps (enabled, select for info)
11 - Play back ANSI dumps
Select function or none to exit: 4

Report setup... answer C to questions to cancel
Include listings and benchmarks? y
How many sequences to list and benchmark? (enter for 10) 5
Include soup display dump? y
Include full benchmark listing? n
Save report to a txt file? y
Report file [path/]basename (default top/report): report1
Will save report to file top/report1.txt
Analyzing... (this will take awhile, esc to cancel)
Benchmarking warrior 03_15 [etc till done]

Total of 1540 warriors, average score = 102.0
Using characters for viewmode=2 with viewshift=0
Found 86 unique instruction sequences...
633 best 121.2 by 72_18 "E" SPL MOV MOV DJN
270 best 127.3 by 07_10 "M" MOV SPL MOV MOV DJN
249 best 114.2 by 64_09 "U" SPL MOV MOV MOV
87 best 127.6 by 24_06 "u" MUL SPL MOV MOV DJN
76 best 115.9 by 14_11 "q" SPL SPL MOV MOV DJN
...etc...
------------------------------------------------------

Top warriors from the top 5 sequences...

------- Pop.Cnt. 633 best 121.2 by 72_18 "E" SPL MOV MOV DJN
;redcode-nano
;name 72_18.red
;author yabevolver
;strategy evolved
;origin 60_18
;species 16479
;generation 191
;created 03-03-2023 06-58-24
;assert CORESIZE==80
ORG 0
SPL.B # 49,< 60
MOV.I } 0,{ 78
MOV.I > 21,{ -2
DJN.I $ -2,$ -3
END
------------------------
Battling 72_18.red against...
Anefam Vesyes 132.39 *****************
Another MEVO Thing 123.94 ****************
b69e6908-a5be0bba-ab4445f 126.76 ****************
Clone 139.43 ******************
Cold Plasma 94.366 ************
Combat Arithmetic 164.78 *********************
Creamed Corn 138.73 ******************
Crimson Climber 119.01 ***************
Crisis 134.50 *****************
[RS] C-Strobia 128.16 *****************
eerie glow 94.366 ************
Free the Fish! 100 *************
Hot Soup 99.295 *************
Just a MEVO Thing 128.87 *****************
Little Bang 136.62 ******************
Little Red Rat 111.97 **************
7_3 135.21 ******************
NanoCracker 140.14 ******************
02_08 110.56 **************
[RS] Nextratulated Sturvi 119.71 ***************
Nonlocality 2 112.67 ***************
Nonlocality 92.957 ************
Quantum Foam 150.70 ********************
Reflow Oven 129.57 *****************
Sleepy Lepus 106.33 **************
Steaming Pebbles 131.69 *****************
SWT2-21 132.39 *****************
SWT2-28 113.38 ***************
Transfer Function 95.070 ************
Unholy Fire 94.366 ************
Benchmark score: 121.26

...etc listings/benchmarks for specified # sequences

-----------------------------------------------------------------------------
MhhuuuuuuqqZuhhMMCMvMMUUUUUWgUUUUEEEEEYUUUEEUUUUUUEEEEEEEEEEEEEEEUUUUEEEEEEEE
MuhhuuuuhhhhhMMMNMvyubMUUUWWuuUUUUEEEUUUUUUeUUUUUUEEEEEEEEEEEIEEEUUUEEOYWWEEE
hhuhuuuuuhBhhhhMMMdvuUUUUuWWWcEYUEEEEUUuUlUUUUUUUEEEEEEEMEEEEEEEEUUEEEWWWWWEE
ouhuVuuuuuuuMOMMUCCCUuUUuueWEWUUMEEEEUUUUUUUnUUUUYEEUEEEEEMEUEUEEUEEEUEWEWEEE
hMhRuuuuhMuuqMUUUCCKCUUUUcucEWMMEEEEEEUUUUUUUUUUEEUUEvMMUMMUMMMMEUEUEEESEEWEE
hMMuuhhuMMMuMMMMMUUuCuuuJuccEUUEqEEUEEEEUxUUUUEUEEUUUUMUUMyUMMMMUEUUEEEWEWEaE
MMMyMMhhMBMMMMMMMqkEuuuKuqEcUEEMEEEUEEEEUUUUUUUEEUEUUUUUUUUMMUMMMUEEEEEzEWEWW
jMMMhMhThMMMMMMMqqqqueuuuEVEUEEiEEEEEUEEUUUUUUUEEEUUENUUUUMUUUUMUUEEEEEWEEEEE
MjMMqhhMMMMMyMqqqqqqRUuJuuEEEEEEEEUUUUUEUUUUUUUUEEEUEUUUAUjUUUUUUUUEEEEWEEEEU
MMMMhMMMMMMMMMMqqqqcUUEuuyEEEEEEEEAUUoEUUUUUUUUUUEEEEUUUUEBBBMMUUuUEUEAEEEEWE
MMEMMMMMMMMqIqMMqqMqqquuEEEEEEEEEEEMUEoUUUUUUUUYEEMEMEEUEEEEMMEEuuuUEEEEEEEEk
MgMMMWMMMMMMMiMMcMWiqqEEEEEEEEEEGEUEEEEoUEEEEUUEEEEMEEEEEEfEMEMEuEuUUuEEEEWEE
MMMMMMMMMMMMMMMMMqqqqqqMMMEEEEEEEEEiEEEEEUUVUUUUUEEEEEkEEppEEMMMuuuiupMMEEEEE
hMhhMMMMMMMqMMMMqcqUVqEEIEEEEEEEEEEEEEEEEaMEEqUEEEEEEEEaEEEEEMMqEEEuMMMEEEEEE
hhhhMMMMMMMSMaMMqqUqEEEEEEEEEEqEEEEEEYEEEEUUEAEEEEEEEEEEEEEEEMMuMMMKuEEEEEEEE
hhhMMMwwMMyWWqqMMqqGqEEEEEEEEEEwEEEEEEEEEUEEEEEEEEEEqEEEEEEEEEIEEMMMuEEEEEEEE
VhhMMMMMMMMMMWqMMqqEEEEEEEEEEEEEwEoEEEEEEEEEEEEEIUUEEEEEEqEEEEEEMMriREEEEEzEE
MhXlMMMMMMMMMqqqqrrqEEqqqEEEEEEwEoEEEVEEEEEEEEEEEiUEEEEEEEEMEMEMMMMMEEEEEEEEE
hMXMMMaMMMMMMqqqqqqEEqEEqEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEiEEEMMEMMMuMMEEEEEEEEE
MMMMMMMMMMMMMMqqqqxiqqqqqEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEaEaMMMMMuuuuuuEEEEEEE
-----------------------------------------------------------------------------

Basically it's now doing something that's similar to what I was doing manually, and saving a minimal snapshot of the soup state to avoid missing something without having to save everything every time. The current version lists the top sequences by population count, that was fairly easy as the report file was (at that point) already sorted that way but still involved a bit of trickery - it reopens the same file it's writing to in read mode to grab the initial entries, was kind of surprised that it let me do that but so long as the beginning part of the file is no longer changing there's no inconsistency. It's temping to code it so that it lists the top sequences by top score rather than population count but that would involve a lot more trickery (sorting on another field) for not much gain since the top scoring warriors are almost always found in the most populous sequences.

Things that save files - presently the analysis/benchmark report and the settings file - always add the extension (.txt or .ini) and default to the specified top directory for file safety reasons. Modified the code so that it only adds the "top/" part if no path is specified, previous code added it all the time and required contortions like "../soup/name" to back out of top to save to soup dir, now can just say "soup/name" for soup dir or "./name" for current dir.

One thing that was bugging me was it saved functional duplicates - warriors with slightly different code (usually a different instruction modifier) that did exactly the same thing. When fixed-sequence or permutate benchmarking is used (benchpseq 1 or 2), these can be detected when two warriors get exactly the same benchmark score against a given test set. This was a bit tricky to implement since YaBasic's floating point representation is not necessarily the same as what comes back after printing it to a file then reading it back, as it is with just about every programming language that uses binary FP - one of the first lessons of programming is don't directly compare FP numbers for equality if they've passed through any process or computation. A=2 ... IF A=2 THEN ... is safe but pretty much anything else has a chance of failing an equality test even when the numbers look exactly the same. As far as I can tell YaBasic has only one number type - float - which is fine by me (in other languages I waste a lot of time with number types), so long as you treat it like integers with up to 9 digits it acts like integers but once you get into 10 digit integers or any kind of floating point representation never assume equality. In this case, 'if wscore=ss' (wscore is the warrior score, ss is the saved score extracted from the warinfo file) failed to detect duplicate scores but 'if str$(wscore)=str$(ss)' worked as str$ (I'm assuming) replicates what gets printed. In this case anyway, for anything critical the code should specify an "equality range" - instead of 'if a=b' use something like 'if abs(a-b)<0.00001'.


Terry Newton (wtn90125@yahoo.com)