|
|
Эксперименты с «Жизнью»
Материал из Blitz Et Cetera
Модификации
Используется программа "жизни" из предыдущего выпуска.
Можно легко сделать полезные cчетчики поколений и клеток.
Ограничение поля: для того, чтобы планеры не прилетали с другой стороны и не вносили изменений в организм, можно сделать "мертвый прямоугольник", то есть зону по краям поля, где клетки не будут рождаться. Тогда планеры будут превращаться в блоки, достигнув границы. Определим границей линии x=0 и y=0.
Детектор стабилизации: пульсирующие конфигурации, возникающие в процессе развития случайно заполненной области, как правило, имеют период не выше трех. Поэтому, стабилизацию можно зафиксировать, если счетчик количества клеток принимает одно и то же значение через каждые два, три или шесть ходов несколько раз. В этом случае, очищаем буфера и задаем новую прямоугольную область.
Поиск долгоживущих конфигураций: можно провести небольшое исследование: поиск небольших по размерам конфигураций с продолжительностью жизни более 2000 поколений. Будем заполнять случайным образом квадрат 7х7 клеток и смотреть, сколько он продержится. Рекордсменов будем записывать в текстовый файл.
Редактирование организма: вместо того, чтобы писать сложный многофункциональный редактор, удобнее поступить следующим образом: редактировать организм, как растровую картинку, где белый пиксел - клетка, а черный - пустая ячейка, и обрабатывать этот файл в программе. Поместим организм в центре поля (используя размеры рисунка). Вот примеры:
Изменение правил: это просто - меняем содержимое "массива правил". Для этого лучше написать более удобную процедуру. Попробуйте следующие варианты (взяты из программы MCell). В таблице даны количества соседей при рождении и смерти:
|
| 2х2
| Коралл
| Лабиринт
| Движение
| Стены городов
|
| Рождение
| 36
| 3
| 3
| 013678
| 45678
|
| Смерть
| 034678
| 0123
| 0678
| 368
| 01678
|
Периодический "взрыв": очень интересно наблюдать за развитием, если через определенное количество ходов изменять правила на один ход. В этом случае, уже остановившийся в развитии организм получает толчок и жизнь опять вспыхивает (неплохой вариант - сделать правила в этот ход "помягче"). Детектор стабилизации лучше отключить.
Программа
Вот программа, в которой сочетаются все вышеперечисленные добавки. Пробуйте изменять константы:
SeedRnd MilliSecs()
Const xres=800, yres=600, fsiz=10, cell=30
;dst - флаг детектора стабилизации, dstperiod - наибольший распознаваемый период
; конфигураций, dstpasses - кол-во проверок, rep - флаг повторений, bnd - флаг
; ограничения поля, visible - отображение организма на экране, explo - режим
; периодического "взрыва", xper - частота "взрыва" в поколениях, loadorg$ -
; - загрузка организма из файла, search - режим поиска долгожителей (0 - откл,
; любое положительное число - минимальное кол-во поколений для записи в файл)
Const dstperiod=3, dstpasses=5, xper=256, bnd=1, visible=1
Const explo=1, xrect=150, yrect=100
;Const dst=1, search=2000, xrect=7, yrect=7
;Const dst=1, loadorg$="Life-Locomotive.png", xrect=0, yrect=0
;Const dst=1, loadorg$="Life-Virus.png", xrect=0, yrect=0
Const fsiz0=1 Shl fsiz,fsiz1=fsiz0-1,fsiz2=(fsiz0 Shl fsiz),fsiz3=fsiz2-1
Const xc=(fsiz0-xres) Shr 1,yc=(fsiz0-yres) Shr 1
Const x1=(xres-xrect) Shr 1,x2=((xres+xrect) Shr 1)-1
Const y1=(yres-yrect) Shr 1,y2=((yres+yrect) Shr 1)-1
Global ib, bnk, dbnk, dend, cellq
Graphics xres,yres,32
SetFont LoadFont ("Arial cyr",14)
buf=CreateImage(xres,yres)
ib=ImageBuffer(buf)
bnk=CreateBank(fsiz2)
dbnk=CreateBank(fsiz2 Shl 2)
Dim neig(8)
k=-fsiz0-1
For n=0 To 7
If n=3 Then k=-1
If n=5 Then k=fsiz0-1
neig(n)=k
k=k+1+(n=3)
Next
Dim change(64)
For n=0 To 3
Read m$
For nn=0 To 8
change(n*16+nn)=Sgn(Instr(m$,nn))
Next
Next
Data "3","0145678";основные правила
Data "23","0245678";правила для "взрыва"
;Массив для хранения сгенерированного организма
Dim org(xrect,yrect)
If search Then f=WriteFile("longlife.txt")
Repeat
LockBuffer ib
dend=-4
If loadorg$="" Then
For x=x1 To x2
For y=y1 To y2
If Rand(0,99)<cell Then putcell x,y: org(x-x1,y-y1)=1 Else org(x-x1,y-y1)=0
Next
Next
Else
i=LoadImage(loadorg$)
ii=ImageBuffer(i)
xsiz=ImageWidth(i)
ysiz=ImageHeight(i)
xx=(xres-xsiz) Shr 1
yy=(yres-ysiz) Shr 1
For x=0 To xsiz-1
For y=0 To ysiz-1
If ReadPixel(x,y,ii)=-1 Then putcell x+xx,y+yy
Next
Next
FreeImage i
End If
;Вспомогательный массив детектора стабилизации: первое значение - кол-во клеток
; на предыдущем этапе, второе - счетчик совпадений кол-ва клеток
Dim dstcq(dstperiod,1)
Repeat
UnlockBuffer ib
If visible Then
;Чтобы исключить мерцание, печатаем текст не на экран, а в буфер
SetBuffer ib
Color 0,0,0
Rect 0,0,100,36
Color 255,255,255
Text 0,0,"FPS: "+1000.0/(MilliSecs()-fps)
;Вывод кол-ва клеток и поколений на экран
Text 0,12,"Клеток: "+cellq
Text 0,24,"Поколение: "+gen
SetBuffer FrontBuffer()
DrawBlock buf,0,0
End If
;Счетчик поколений
gen=gen+1
;"Взрыв"
If explo And (gen Mod xper)=0 Then add=32 Else add=0
;Детектор стабилизации
If dst Then
;Цикл по всем периодам
For n=2 To dstperiod
If gen Mod n=0 Then
If cellq=dstcq(n,0) Then
;Если кол-во клеток совпадает с прежним - увеличивается счетчик проверок
dstcq(n,1)=dstcq(n,1)+1
;Если кол-во проверок достигло предела - организм стабилизировался,
; включается флаг выхода
If dstcq(n,1)=dstpasses Then ex=1
Else
;Если кол-во клеток отличается - счетчик обнуляется
dstcq(n,0)=cellq
dstcq(n,1)=0
End If
End If
Next
End If
;Если флаг выхода включен или нажата клавиша пробела - обнуляем его и выходим
; из цикла развития
If ex Or KeyHit(57) Then ex=0:Exit
fps=MilliSecs()
LockBuffer ib
n=0
While n<=dend
pos=PeekInt(dbnk,n)
k=PeekByte(bnk,pos)
If change((k And 31)+add) Then PokeByte bnk,pos,k Or 32
If (k And 31)=0 Then
PokeInt dbnk,n,PeekInt(dbnk,dend)
PokeByte bnk,pos,0
dend=dend-4
Else
n=n+4
End If
Wend
n=0
dend2=dend
While n<=dend2
pos=PeekInt(dbnk,n)
k=PeekByte(bnk,pos)
If k And 32 Then
If bnd=0 Or (pos>fsiz1 And (pos And fsiz1)>0) Then
v=(k And 16) Shr 4
If visible Then
x=(pos And fsiz1)-xc
y=(pos Shr fsiz)-yc
If x>=0 And x<xres And y>=0 And y<yres Then WritePixelFast x,y,v-1,ib
End If
v=1-(v Shl 1)
;Если ячейка очистилась, счетчик клеток уменьшается на 1, если заполнилась -
; увеличивается на 1
cellq=cellq+v
For nn=0 To 7
addr=(neig(nn)+pos) And fsiz3
p=PeekByte(bnk,addr)
If p=0 Then
dend=dend+4
PokeInt dbnk,dend,addr
PokeByte bnk,addr,65
Else
PokeByte bnk,addr,p+v
End If
Next
PokeByte bnk,pos,k Xor 48
End If
End If
n=n+4
Wend
If KeyHit(1) Then End
Forever
;Запись долгожителя в файл
If search>0 And search<=gen Then
WriteLine f,gen
For x=0 To xrect-1
m$=""
For y=0 To yrect-1
If org(x,y) Then m$=m$+"0" Else m$=m$+"-"
Next
WriteLine f,m$
Next
WriteLine f,""
End If
;Перед генерацией нового организма очищается экран и буфер атрибутов, а также
; обнуляются счетчики поколений, клеток и указатель массива "интересных"
; ячеек
SetBuffer ib
Cls
SetBuffer FrontBuffer()
FreeBank bnk
bnk=CreateBank(fsiz2)
gen=0
cellq=0
Forever
Function putcell(x,y)
pos=x+xc+((y+yc) Shl fsiz)
For nn=0 To 8
addr=(neig(nn)+pos) And fsiz3
p=PeekByte(bnk,addr)
If p=0 Then
dend=dend+4
PokeInt dbnk,dend,addr
PokeByte bnk,addr,65
Else
PokeByte bnk,addr,p+1
End If
Next
PokeByte bnk,pos,PeekByte(bnk,pos) + 15
If visible Then WritePixel x,y,-1,ib
;Увеличение счетчика клеток на единицу
cellq=cellq+1
End Function
Автор: Матвей Меркулов (E-mail: MattMerkulov gmail.com, ICQ: 392-274-050)
|
|