From 3c0767b9324e57b881da365deb20d543fe769d39 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 27 Aug 2025 01:06:59 +0000 Subject: [PATCH 1/5] Initial plan From a70176fa149d798e747254c2fb1d42fc7e3f96e9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 27 Aug 2025 01:10:05 +0000 Subject: [PATCH 2/5] Initial assessment - identify test infrastructure issues Co-authored-by: SimplyAISolution <124332391+SimplyAISolution@users.noreply.github.com> --- ...est_determinism.cpython-312-pytest-8.2.2.pyc | Bin 0 -> 24379 bytes .../test_evolution.cpython-312-pytest-8.2.2.pyc | Bin 0 -> 32221 bytes ...est_integration.cpython-312-pytest-8.2.2.pyc | Bin 0 -> 37225 bytes ...est_performance.cpython-312-pytest-8.2.2.pyc | Bin 0 -> 24227 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/__pycache__/test_determinism.cpython-312-pytest-8.2.2.pyc create mode 100644 tests/__pycache__/test_evolution.cpython-312-pytest-8.2.2.pyc create mode 100644 tests/__pycache__/test_integration.cpython-312-pytest-8.2.2.pyc create mode 100644 tests/__pycache__/test_performance.cpython-312-pytest-8.2.2.pyc diff --git a/tests/__pycache__/test_determinism.cpython-312-pytest-8.2.2.pyc b/tests/__pycache__/test_determinism.cpython-312-pytest-8.2.2.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1a894544ad9c2f65f0e78fe0b2b7aa221d1a4882 GIT binary patch literal 24379 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%0DN8ZV55?%$0puY00~&5M?l#Zq2z&R!?@1RL zx=(@1Nx!ZWT~&i*6zobPG392NlXRskbzNyY6RDqE)QIQ8x{n;zuye$H&BN3+T(sAy z9F!Ehf?wXsfY4pO2 z6C3t^FMhFR->4Ief);*NRx=r_yI9{k9&F16H;xB4Ua71)UUIbLeAT;=?dP_P`A=4! zs61KwgW5}x?Gu$dZkkQyA*9mOU#x#^t_)sY@4WSyA*kJqH>o$xZ4xa$pkB2>-dX~D~p$$ z{Njl(UfA~5V{bn8?#jLApFb6Uqwh@LrImXp7VjI~JsFJVf~&`atIyS54nF*WH!#}z zTF0v$695eCx$JF3_@D!F;I~_W1p?jRRr7s9+D6yrmTnwhx^ZG@$92Z+c$yt|EgIQ% z#B~LPYoG8&##$!4i?7r*oD7`^opMdotr_)9`S$1T#raVQhWGLH%}ni+?8j2gzK^fl znYtbD#KzR^fgdMRw@bdmhfbzxA1kbdkKoOP!98}{n{5^_e{K)%aoc`gV*&FQwwgTw z&wusS?5XhlLsiY5YR^A5)a5i1DQhs1EBI(8|}~(gRW}U0&cMi%sXg<4an0$m|J9}uM32PFacJtc?apbLmcxD zpk0`H{rOL)!3xIy**Pn8bL^r6JM}<6 z>$d59isXp~w;FANmL6#6;e1;=Ht`|N4JkI+JIxRGgZ#Mb{4nU?+;M(*9^}Ve=ZAqN z&boCPRHewk*91f^qQN|(hfoL<=rq>LX%r)M zmR!LYsWu@-elDO;8C+Z$f@g|e_ZF?!)zu4V-YRvWLX_qzlt!|p0M3go)JR2igE27A2Zpt2S$hcjG2npG6Y-D2xj4R)zi>Q-OTsjk9W zG$yn*g;X;cL7YhVG+KiMAXY07ES*44!xoQU^b2q5;%BTa{Gctu=tie4{#aT(YOd%~ z!9YW4a3xoz0H62z3nDEe_=^0HKc-#-XgTW^-MuSlnbv-@4qdIF>KgbK7XXOq5d$Qr z%fvFM8ESR0G|uZX$m?o!rOWFFH3KB}{MHP*e3`TK6|kogIJuf*Hqw3%a6P z@}f14N`A}JI7H7wDfPgASvD}_ibjT)Yf!5;u@v#uL?(ihT?Lpv%=3xPOy5vbvK)q< z`4=ETqb40#x*i_LSS2FKQFgcmLC7MfM50v$0+E#h{Grw87Y-f}WN_NdMWEsEe+F-0 zJ+} z%r+rnO~_u;a*obkC1n(Yk@E&ets%ex3=r~HmKQGaAn z--iwnGa>Xf$)TnqPNAhyB6CK81UH?O#-B!KuVA2_{v@aSD&%`@3a;eD_ww9X85Act zdKbEX9t|89kx}+<;Z+YB&!NGAp{Jp(=l>l({x&|=bJ1D_WJef+>oT8FlF{|Ki~cO= z`rMA*f`A1JeAa}Zc4XhFZI}J4M{HL-mCBq>z}#!=#>!6CoT!3?+e(BKz&^XVUq48cK zyVjc&0tU{E3ZjEe91hJpXoHKhV#6KQA6cQy+B1f*Ex^(-g2b)5aMXE+S(v{GU^(#B z2*&|BiO?x<>K5|^C_n!|up2rBc66od6gVDV?V_U zU#C=NKf;Vpt$eF+cD?kze)01)sFs1NMI+=`19fFiTbn{6rb-Gv><++*y1?*{1 z;@ss;1)J1`QN3hgRIiffIxdthM58=6f|M}NagYW(c^=5GQZ9&;m)Y`E&GQmno~xx2 zz$Lfn*TK?_I*E*Oyqv(&A$0X=%5m-!r%84BWK^cr94=r_<)m>H3K-$!DiKIj%4(o! z{iMPgdHP8$t59A`puClGRS(K5YG;~GST7dHrQ6m}56Ua>`86$Vq+5$Gukymnv(%Xn z^sCm=pjR5PK$izbT1u@^AC%X7R$eW=4KyFLU$qSgLkthvfLglHtJ^QJ1xofGwhdUu z$%~`UrlV+{KWGEe4A+sL3gqMi9d*y!fLsl10b@P@d65l5pA~`VyhDT`F_jWoE|#l0 zYYDX|x-x*g$cJhZW*7>; z3YOIwF79iNFM5#UnANnc_Q#&%qvpeASm(~tyPOp8X$DP$Y=yRdk64jWi8ZRm2f7WQ z zEmdlh%A#u%qxG9SK*rUOZZ&PPRxSzzE~~Qs8@3vLqm#@C_k{5nK)RQqn@=+CdH>6t zE**_}{OTxI0ejkdfy}mtgw=$ zh8h`_gH@sx%E4-Mp_K!j8>2W^(U9+d?r_b@Y~lr63)dyRhfrqIlA zH9e*PF3K+_Xq6q_eP>LqJcfKBudMTgf(o;6vv6)K|L16Y1B~4;Efx3008z+X zTm$wIC5_4CC1qtVB!!(tPgItdWF<^CULn;zF$I@FAzwxEGDGBVYd#DxDL-u?&;Y-{t_DCCja3RE#l-R2{*Pu;{dvX^)Wd%2pDDadMd2L_m%j`@V^ zhndI@2Qn#ZEBd(&jA_KHo%oc{tv28lnehB$yxNFHu``uJ-N3lh7=-F_UKRLUM`=@E zl;I0`axu=RCtH~C{{@5mdoU!g@+l^82LVVm{!Mrqtysm50pkx`39SL!%Fk zBpSE?nSk_LW6qZRRGt^Tkj#rl?{K>Vh+zzL5itEIJ8gKCA9$y`)QSaU!p15m}-m=YSu zfp>N}Y#9Dj=$t@x)Ao_a8FB{QFtUR@v=(tzZLdmkq&|Vqepm{eHKJU?Q=ev!UeVpb zSy&_GPd7M4J z%?9~xPSz;sJH>;kN|ZeSPS2G|s#D~{i+pw zyd__0yZ_#{@7_6l|1jV8Xjs`>xCbzkzGSL9lJ1Hmxb8%xGcF_qg6cjC8=i8o+8oRr zPH`~76&7l2x{83%Rh(f;HVYt=h{+;{fI_%K5P!=vUOty*akL=9y zUqK%&GHi%op$?G!bG-cqzBlpX@H4!!D4*pmmjUYy>gIdyM862^h*TTNCT}td`;tOp zY6(x^NXa`m0Jv8I@I`^-q-3X8I(%|JdiX^&o>gCThma=OD_ zuBonMHvoXjJ>;|GqJ;^!J(lcrLbZ(f5zLXGj|6z^LN8U|QSig<3@}he0F_MypxDB$ zszM@03-fH72+Xip37sav4Ly^uGwKS6)islF!oHmH!iVk>0{#UI!9Sa0Cp)s`n#t#7fwEN;+b<>#uu+Sckn0KA7?K-KfZ3~WJC01`-%2*A>0J@rkQm=%zij;5rrh2 z*SL9_A0j+uz?@ zq1b#(v-!x|>;>Us^=EUw&t39;j@W#Ry(O%IPws4B-$v7$O@SwunI<+mo?K$RY^i** z-g>#-ir32=pAT6tw^n}MZ+*vah3nl>r(}sw80tj*U`>{izLG<$ugX`!{{`c}5dy6(nT*p1c>*&FmB>gBO|k?eG{Wuok)a7@;m zP4eg$)N@UI;t&rD$4U>n5+o4iQ=coK#((@k2XCT~9NR-VgB zQP$ohHq&Ex%ib<0*e3peg4dvy7-10@kasNm9`o>r7~?;6{|giNS7zzIGLhd}K5J!R KflV|?1phy>fN_oh literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_performance.cpython-312-pytest-8.2.2.pyc b/tests/__pycache__/test_performance.cpython-312-pytest-8.2.2.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d017f97c9eb8ce5854a87b95895f81de4b698887 GIT binary patch literal 24227 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 Date: Wed, 27 Aug 2025 01:14:57 +0000 Subject: [PATCH 3/5] Fix module structure and basic class implementations Co-authored-by: SimplyAISolution <124332391+SimplyAISolution@users.noreply.github.com> --- Makefile | 8 +- ai_evo/__init__.py | 3 + ai_evo/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 268 bytes ai_evo/__pycache__/brain.cpython-312.pyc | Bin 0 -> 2590 bytes ai_evo/__pycache__/config.cpython-312.pyc | Bin 0 -> 4602 bytes ai_evo/__pycache__/creature.cpython-312.pyc | Bin 0 -> 3066 bytes ai_evo/__pycache__/evolution.cpython-312.pyc | Bin 0 -> 3407 bytes ai_evo/__pycache__/genome.cpython-312.pyc | Bin 0 -> 2026 bytes ai_evo/__pycache__/rng.cpython-312.pyc | Bin 0 -> 1967 bytes ai_evo/__pycache__/simulation.cpython-312.pyc | Bin 0 -> 20278 bytes ai_evo/__pycache__/spatial.cpython-312.pyc | Bin 0 -> 2019 bytes ai_evo/__pycache__/stats.cpython-312.pyc | Bin 0 -> 11382 bytes ai_evo/__pycache__/world.cpython-312.pyc | Bin 0 -> 1813 bytes ai_evo/_init_.py | 4 - ai_evo/brain.py | 54 +++++++++----- ai_evo/config.py | 70 ++++++++++++++++++ ai_evo/creature.py | 43 +++++++++++ ai_evo/creatures.py | 23 ------ ai_evo/genome.py | 57 +++++++++----- tests/{_init_.py => __init__.py} | 0 tests/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 263 bytes .../test_energy.cpython-312-pytest-8.2.2.pyc | Bin 0 -> 27281 bytes tests/test_energy.py | 2 +- 23 files changed, 192 insertions(+), 72 deletions(-) create mode 100644 ai_evo/__init__.py create mode 100644 ai_evo/__pycache__/__init__.cpython-312.pyc create mode 100644 ai_evo/__pycache__/brain.cpython-312.pyc create mode 100644 ai_evo/__pycache__/config.cpython-312.pyc create mode 100644 ai_evo/__pycache__/creature.cpython-312.pyc create mode 100644 ai_evo/__pycache__/evolution.cpython-312.pyc create mode 100644 ai_evo/__pycache__/genome.cpython-312.pyc create mode 100644 ai_evo/__pycache__/rng.cpython-312.pyc create mode 100644 ai_evo/__pycache__/simulation.cpython-312.pyc create mode 100644 ai_evo/__pycache__/spatial.cpython-312.pyc create mode 100644 ai_evo/__pycache__/stats.cpython-312.pyc create mode 100644 ai_evo/__pycache__/world.cpython-312.pyc delete mode 100644 ai_evo/_init_.py create mode 100644 ai_evo/creature.py delete mode 100644 ai_evo/creatures.py rename tests/{_init_.py => __init__.py} (100%) create mode 100644 tests/__pycache__/__init__.cpython-312.pyc create mode 100644 tests/__pycache__/test_energy.cpython-312-pytest-8.2.2.pyc diff --git a/Makefile b/Makefile index 7f5356a..5588354 100644 --- a/Makefile +++ b/Makefile @@ -28,16 +28,16 @@ install-dev: # Testing test: - pytest tests/ -v + PYTHONPATH=. pytest tests/ -v test-cov: - pytest tests/ --cov=ai_evo --cov-report=html --cov-report=term + PYTHONPATH=. pytest tests/ --cov=ai_evo --cov-report=html --cov-report=term test-determinism: - pytest tests/test_determinism.py -v + PYTHONPATH=. pytest tests/test_determinism.py -v test-performance: - pytest tests/test_performance.py -v + PYTHONPATH=. pytest tests/test_performance.py -v # Running run: diff --git a/ai_evo/__init__.py b/ai_evo/__init__.py new file mode 100644 index 0000000..b9a49f9 --- /dev/null +++ b/ai_evo/__init__.py @@ -0,0 +1,3 @@ +"""AI Evolution Environment Package.""" + +__version__ = "1.0.0" diff --git a/ai_evo/__pycache__/__init__.cpython-312.pyc b/ai_evo/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..57b1dfe181476ffc84309c85a513fecd9ceea617 GIT binary patch literal 268 zcmX@j%ge<81YFVUGVFo$V-N=h7@>^MJV3^Dh7^VLhGvias^7Z56 zGxIV_;^XxSDt~d<-9G`Kg!}NPJ*sWMsU_p#FeQw2`}r6(|A#t8P!K literal 0 HcmV?d00001 diff --git a/ai_evo/__pycache__/brain.cpython-312.pyc b/ai_evo/__pycache__/brain.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c124f87dce8ec050b7da43cdebde9e83c580f091 GIT binary patch literal 2590 zcmai0O-vg{6rTOF4cMiDU{WB_Hc+)D5mpl_8Z~Vi(gal{Bs9gUoK~9+JBBs;5*fk_SPQkSe3~tRV(Gh4V6fha_XC1n_|UDN9*_AyqS6P z-kWdU_}5UV6+!#$>W{NNA3{%Pr{4IQu=^nh1*D)jQW%BRm^cfZ(>R@v^STfh7^Luu zp!sxv+|M8ujUmN%3n~8ltY;w}$gq(>wtoVriL8kRwiiq?CtfGAYKRGwh|>hicA8*o zXq&E>h_H_ESvsX*CvYictEM4q+YAgu7{@n;4O7R{o@I}M<8Isi8dw2g6lcI$a5By* zEanre!rkKIg2H28@|D`$eM}LW>k~YL!N&uNzxg<1Ze>S7WGCHpIwd4!f8fH+AuJQ zQdmUK8mca9()oE)OVhKL&KvV8F%2CX_R9xlRl@UT)b(p9m2m=+q(WShlnpnW$5370 zMm;$KZ2{SPna?rzYjO@H_phKPq0NmTM9$lS*Fz{-$8uc(JjW=EO_Sy}dX~*BHQO@_ zm%Dxm6Kq|g=ikgi4_adOSwTs+G}QO?_{*aKyTQi!w3mhG=DvV9(=gt|=8OD!v9%tQ zY=3^A=211bQqRQ_yD#^zHnGX%_VTvG?VH=tWG+|F&J|?WiziARe12xK`+409{H&qp z3W{*qW0#3JjV(*Guwj`bBYKDwZIj+qOk^92@-)3O))1j!n}`G*PQwPF*>YGTNPk`J+PkkuZo}d5bz2d;#=t}fqTjX(9cOkYM zD_&Tg{DE6LyJm0PSikX0=Y!bKz2)e5<*kdQfr(1jrPAeVrOB!4`=d%6(I%SiBm;&MxPwu?$blB`4R^`uBPQFceQ+u%fSN(@DZ|uW7rmTa~{)5 zDk>_$E>^?@G4)1BdFUauDs597ZBzFm4C$v#@USodGC2ywVUSR%rQ>+szZE)oXYzKu_~z>CD}&|GK%Q-)X+Lo1 zM1DRWa!BoSI}Nhc*VWscuaWJ2VO z8!4etJ3>M;0qKJvuOoL87vO}xg)X6jH4fw!dc>dG;d$Y3aefy8+X;q*cZ%2VCRdU> z2*kBdH{$E@T`KMz5rpts$41Y3&kh1{Q`p20rZ%9eJ zO`n}f!M_97K|=4H`=)xqxPHA&brg24ug?Ofy*zwChCxTsW%UBt;TVQ_gd$H+$Diov RAE@Ua-(e>FHv-}={uf}AMt=YR literal 0 HcmV?d00001 diff --git a/ai_evo/__pycache__/config.cpython-312.pyc b/ai_evo/__pycache__/config.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4ec47ac3944109564dc80424176b35b39e01599f GIT binary patch literal 4602 zcmd5=OKcn06@9}$sShnmvh^4B_sB9OTej=C{-|~gBW*#nanquz+rfBdD2_~W*f> z5{SeLEo`7okd@W0yy(J)(ISg(y2~;^fkJ^(bUN+AKo_YN1(?V{fUJ7%8*(fOb$}Eo z(5Jcgo%8*_ltqKG@P;JEcBw&L^PBnlQj_fc3E&|!FrvLOO8Wjn&9WV(z*WubEL&?Bhxl#PHoT-=g~bZy44`_~ zOW5Y7AqtxaWu5BE3Wm1fM^STtQ+?Iono%V``}O-d>U(|rXuYOp8Wn9rH2Y7*Xg$!f zNQ@U4CAz$_4*l9oX2Gzls=tPPOueg(?u&nVWx|lX#}b8qU8*xBsIG^=Q0}LC7_9k< zF;TTs^l)*i4< zu#SMG!Eku^seS}39k4F2BLT~Rbp@;&EEBLEu_$@`p~&dJxDT+GR{oE*x@kDT1c$!naP#mPsUT*JvDoE*W) zeokg{vX+yPoNVJ{5+_SI8Ng}Xe1sIm9nX>)BQ*{skJ3?7D*S)0&ZZS-zdC(+Yrbfe zjd@j;Z&l2)T2ijBS*4nIZ?9L@Om0=mM#cH+O4U@1HEZ7A(Oh-IODKvs zYL(5je}v~+1++_TP{EBI))HAWOMNhmf_o{o}|w-O=3r$R~-RARnG3 zKH!d>rhFvG2PV-x?KZRVGgLe3w(^N{#ItTIADJUQ*od)#2{BD`w!SP_huh4Wt=w1T zQx|BIakrJvzD|6`ZRJD9iTAsd|2fgW2ZI^Hd3P51t`5azDD$5J302Or*EGGE|aHFx581>&^H6?}RX#Y-tl8Q-rG;dVv&c}*=fYvPKc zTN;YPX2tQMg_5N@d>UgvVSEM|pCUy^uuya|+rPyeTkNlq^Vp-2tUEI1j*h$I)9&$E z@tkny=H0mkcWTz1I_{oapeMKJ=1#ft-1Es97th2rJ@VYkXJOIBbM`g&>;>vIC4Y8h zNs7wuv4KXI!1KPrMw9@>#c(4oV1f;28%Y6EY+}06CSW_k4gova*mNT;;1M=8+vpN7 zL$F)G9yT)3=oRoN!D9mUvFQ_yegOvv4hlHLX672h0*(+I6)?-jXBuMyPO{M)Po+h{`;29?88fOJONAMd0o@cqm#%lsz zAoxuIFS6`J;MRw$k7+xmmPJ)|ovT&c zTXN!D)>`w^f+(;^_LiXzUl^RA%3j->BEN~NrQwCREr<9dh7=8Lnmn8JUj%wHHrruJa9(}~CE`H8$K0Mi98t-? zXL=sW@5_J5%zT`gc^r9`Ie9xPB8&Wf-vhVl zgA04o-S4eGSby*B2XAj{kIp`xe3qWyNz4mNw5EH}&^LlMq4^Uk^eR(=Z4|wPs!Q*( zLk2`>Q<^(~l^4~jabOQ_pyOtOR!j|Vg4uLcp!E*kcf^Y859-@*?W8Am5)=Mv|MzZ> z(_(Oj?(_S)7b)Tw4!=%IJm~drlMQVx8_?~C)aIbu-+A!Pw*Bb!gKnpoX(jqpi}<+= z@b%-T9(mey%b^X3X+_J_47@0l7}%3kVSyLNZ-$bp8J<+NX&Dql%^=Ey`a$G({V}*r zceR5-q(16-wEltgEPZ+>aoXRQ{m~uRa~C}yvab_HPT-e%$f0pWYW+&*v3lrT{0O;& z0KCcSp?fhMVfT_3_Axn>=jYKI`?!RRQ?z+0(>5!%qgG&j)TvcVh8z~o;oqk=VuQ(& zmP5QYiUzB!+_tRJX1cY{&4OEKX;TCo3fbSRe$ey#+9%VOw)LHKVaHtkIK8@)Sp8`7 zqqz^S?l0N-sk(DZ+g~D_i8ax+fuDlP!;ZWh&i?>FTp03l8$V68i=^m(9sDe*bEN24 z<8%@Tb2+Vz)1LCTNRd0l1pbL}fhegrAod9;x(v)ULZN7G`{o`)wwIEk3!lfN=;R*M z0;$CpPCz2 YM6OAa{23$t#dkwJ(jDn@Mq14DZ^Y{m0ssI2 literal 0 HcmV?d00001 diff --git a/ai_evo/__pycache__/creature.cpython-312.pyc b/ai_evo/__pycache__/creature.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3f3dfdfc1bc55a8395cb3b6dc1bba4f81a076cde GIT binary patch literal 3066 zcma)8O>7fK6rTOFH+GC6fj|fiOZlr&aG+Jy3L+H#+e!qf1ywSrt7T^rFJAAuv+EF> z95`?w5(o~J2Ro2 zd+*Kc&$(QNK>Oj`cjfO>ggnJg($pnkaTtW#gpv}W5|v%4Bm-AmrK*y~R+o>R5l z08~a;W=7yt>#h|#zE^yP%ZwD{NIzXeh2=)tnR?A-&m06DuPXF%?x zS*FuY_~*bjMc05XCv@0XX&(A(=vo+G2mdaXr`;?+s?+tLU)$JF=!x_Mk>>GFS&g%R zd0_xRtc1l*K)d9GWn+waEOhK(z_`H5*0|&I0kEe=5LzKCMzTYr6xaY5Q97tG+hIYZ z*Zjai+@ciB=3@;TBHeBnBL)78w}9LxlhPzH+k=xb^blmxCKc!{#gvdq^8E_utbq`hk2HJK)EDv35hkjXC zIZw!EP^``UN6XBv7|!T&Fw0h8K+@L7%D^x~jnFqZRD@6KHY*ks5i}9bs3*}2e8VzS zP1$$pJ&AY2W&wI&@by5WoNak#V$GU8Z4h73_u#BPAb@1P`{w1T%hU93w?U?V6zyC z%^bnc8(3!zL}8`@#e{7-1Xay4ihQbBI+J81zBaXeo{ ziux?@c83O=uoFu#@Hz;uD?lpY#*lhQ zl_uQXtKVW&!ZLo%m@L+i#Tx1x){t=x@nr2iTchwC8_ByXlF<;Z#0hEfhH;5owHl;s zT-+Re8!1TQr737J=DR`1-$7D9vIj_jc7T48p!vGk%1pLuZfeKir4d{wI0gi6fi1?} zQ+H0yZkyjSFw5t+?0;}>zHj)NdNVhbyJgMmn-*R%I6vQaI5BvvZxSIJDXhac%CoA> zG^31ZR^hsKQSLO&FX5AJX(Vl$)VD#c1R>}6fJU-ci_#voIJX*+HtPCT$T#4CdcpTy zj@L1N3(0mQyKqEtypRuobdQMLMS*hsRuBY*U zB>$56DI>(W^VT97w0dOiurwVm667si){4_Ruv2X5sNPUs0sGy{YC7xI%REW zW@ihmh8~SIRzMk>=H_@Nx1k%Biq{ zNx?C-xr#)5!yss}uOFa<&yn!|xjqzweuy6c9p0${;?`0mNqRyKJtbX#khgy)oBvI1 MmySq(6QpALfAzN2CIA2c literal 0 HcmV?d00001 diff --git a/ai_evo/__pycache__/evolution.cpython-312.pyc b/ai_evo/__pycache__/evolution.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8adae6c78810a7089dd507e5784bc51871db882e GIT binary patch literal 3407 zcmb7HO>7&-6`uX$pGf_!{^Qt`Wm}?cN%2pdG!QKLzcvB^aRS&N3aseeCABiS%gnB1 zNg!bz1elBqSV9qq3JRD&d!XT>PSHctoP5v4Di4yT4p5+{+|;T_3!nOCe@IG|TXcYZ z^WMBSv+vEk_s#Bikw^$ZljFagj`>jl}%P~1tW^+iYnE+2Cwc7#&JQ-tg5F||ZJjM~;RMW}& zh)EZVMx$?}(P`M0Q9P^bDjt1c;QOPObS;xeOSLGZYxqVH6Q2MSNJb zX0a~2M@)xN3q1Cw@G$xaJ>nk;Yp953Y~@}u(>TvneZjxQ)=PtiCB6^}d_gFnB&RUT zfjkQ{{S9iD%`r1HmVK?r7lr54PiT40YH~OPiJN0#M%a_0KWWd2+p1 znv}3-|J+UX9QT{Zb9c`_MX~0|Jyk;sK!j)aTOeO@kQ~B+$>DBK{Z$a8D-^l=kUIEC z?rn30##s)pFh$|W55&Q z(+v!DN`z$ENLYl+XgZL@e9T9620`AhXUs_ti;1~jhwRm)Y zUD`0Ej*O)ZIgvImj$|xaY36kx|AG>25ueN#=bz+@`Nt2-?SE<+*lHD5thM>o`L&0u z56hodj#j&eH(G~RxYuIOFU8XBUyJ4274cVxs=Xssar9Mq6w~!mU#-*j$MF;h&;k?g z$hJ4aPq->Dc5HS$)n7d3re~u}Xs2Pvv?%J#A4Xp+qVL5NiVTl2BH~ zvYMqZOxjccz0`uFCX<@1sk*#Cy0DtXhB7Nt1Ie0(Y12kpA%V1(RLyKcCy{$Nq3O~C zRZC4g?^s!3NL)YhjZgx>{XpZXLW`^1EE>ORMl#jb-- zo+6Z41@ZUyS-!OleIgmcS>^kT(y00yG>lVw03>+PjWPj9W?g7rSD_MWYFp4;j>{*Sg`YiI`rgCRTh0kL24U`8_Gg+XXGV z)eY}NsD@J^82$fns>tQgOk+6qf`DHXFreiDSq{3YJbcTu^b(q9=hO2h|h-Wlmrmc=5qD@eb~JDDcu_CSm~x37aSq z8X4)HVE_=C(}}D~c-hD<;7)i8;mp|#Vf8GGrL`;t#stQM&m_$Iga`I4uGPrF@ECSW z7Hks>i|_9*mwgr8fSB|{PnGOxv}VmIAQU^r?zInBKim`tH^jjTTeD`4 zgVOnON7<~3r(T6m*{S^u#e#M$#P1?hJx0RbIYZcc@Z32I{aO4I>KZ-Vspu5NhiKHs z2ONW;#UuYC+9;iT69`3#ruM=(1h>^2EjB})TwY;UF8|2#!P9>WqeyS5y&68W9qu`> z-w6t|HU+mfC*cbkz8UpIMwKKIlB5ivahmdBN&0*?k#;qHNm2}1k}$1bOmAUKQ_2C< zVam}c@JE!;>&A(Uekx!}=q&R+Aj@c*e}9K%gcGIfI|#apxJ$bopD2V%z8wTz`NA&k zc27174uIH8#DYZ7XETs&=V2YRN_X&~aP&3^ZK9C^(Ksz)aD@o)s1dF51)d`jvPv|5hiJln zB-l|98WB0xTv(;1Ri#qVP;Facw8p4SO-DD&Qjt;BsWU2-sYxBZXy;zw)-{>)#E)x^ zZkei441xu8I8=@d5ZZ*20tfj(9))Ovoc?HOElPQfhbh2}!HmNcVJ4`cC25StHwI4z z59A48r9myVBNP%^zt*?I7m`rNfENquXfEFBmS9UxrI)*;nKA6zj;GF)h_pD}bywUX@b1kwT=5;!f z9Bs~iXzGrx8qh_A45DgFcPf$tJ#eH=)u>ZDxAp>mUQRL0i^Y+MNU${wT`Yy94~Q2r zYhH}0re;+=0lCT2V3!kcRMaS)BZR-+#@JGKy!tF(fzZo+})w=DK${(V!gs)llN z(=zJ*S>7}^b!M4WxC#I7RCR@JTKS;dT&>}WilRds6s0*3ATrlAU~%>xAlsxfLdGU{ z`EQf%$T+Zso0$bR&}GwCclqDMtb64e@)>tr0&m*QEkDYwez(5M|1M^Jz4h?UgF6qE z2g;*$WA6Cy{NQy<{FMJ2f6R;_`T-Kpqy zh{&8Qo)Zju+KUhau`19;6pHHI*^7 z!A8-9-MCNu9E0}wJw}3Wf&C4T9rA=e%pC+>?PiV0s#lDYX#$`|{{e7=+R#eQ<}QfI&y2g$U2Z}`W| z{mg;1bmRwrc_k$TeGK{#V_C?{6U=(G)(EPKIgJ|pNd|TLHC%=z$926DuLbQ|VRPU> fJlI%!C(3c$6LR$#8UBMz{u`U-lK&7O{?7jZ*-5$_ literal 0 HcmV?d00001 diff --git a/ai_evo/__pycache__/rng.cpython-312.pyc b/ai_evo/__pycache__/rng.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9ae2c2869895092ca52f390685710b870b794163 GIT binary patch literal 1967 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 literal 0 HcmV?d00001 diff --git a/ai_evo/__pycache__/spatial.cpython-312.pyc b/ai_evo/__pycache__/spatial.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ebbc057e0ebaf7e5bc9ad16fce355938e0988165 GIT binary patch literal 2019 zcmah~T}<0n6u#GXoP>lCR$!vjL|EyMs{*Tps)AOeZeyjZ##A9~Q`E{bwgFSerq>Q5 zQIPt;Ly$TFTBU$Asi0MAGNiG`J?>#|djVzBI*;vTPkos-c$n0uo#VtwTCwd&IsSa^ zxxV+F?>jf&x3;zrFg5kn?3WxNzvD|_LJb^U08k_v(Wpt%G({<5&g4=YN3Ic#UnZKc z1GCXme3p|!PZ3Jgi)s3_k}+M)P~8a?gMrZ+I64QQNOY2-8cA^)P4ODHOi}`VHWktY z@S+w1AI^$W*o#fhE3ToK#rm&IUiR*`2|w797!(#};fe;~KUlk68-_v#qplx&J+=7_z=#57ufo zFrW1zvTRs}E6cf#rb-V4Q4#mj2kstu(s!=ZTe`Uy=`9HNqiaJCE^Q2be(9j2=aEq9 zI9(7vjUGWBQSpR<)r&j@ks@&$<)n>*&D9GVOd})*v8HV9|FfsLNy3mAiSu~Fa{Yoz zJZpuJM;ASsb#P};QuY#t1o@7h%_W{YG(`O3pM`{b2Y6$XkD6(74!UDU8aqwM!VtVU&c5b3tAlUT&yuOK>0c=l-&P7 zARu>sf?+A1BF~b@;_&c7s;Mi?i|SS;tuts%)`{WByzLmMUQdL!)h+0RDp#U113Gqq zL=9L|>YY%fEzI<(jA3fI6V1%kg2=UV3mm955?_t3L|5BZ+J1<1A9VF>iIuLihlECN z(3N)!Z?CmKi6>UCuUuc#ipILJMIX0px0Eh!x0dei=E}Kmddo|dt}FZT(VyaD`|+`F z)gR;U{l(+#D&wf^z_3Yt#=isLGhWwukOGzFj?;OKh2zJbW5H)$SPC_?(YS_=<2R2Q zK{dZ%IbbsArr^0Gx=5986nJE>0F=T$QS4Q~>}7DC02k{l-e!jBcbCW885h#9?Cdpk zJ(wmkMKdyv$El0(%X*YKs7mNOv?`6!;Cgm)oy|nn!?>U`1`eof?<{t&cW+#LII=mi zrEFd)_3oZ8pa1%1xxdnWc||O6g|`mkZ4ZQEbUnIpW^3s2@b>Wj$qSW4eMlpw8xTWXIoLjb@|4nF$*EsR>~Eo+#t#WNf7(A4a-2i} literal 0 HcmV?d00001 diff --git a/ai_evo/__pycache__/stats.cpython-312.pyc b/ai_evo/__pycache__/stats.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..724be9aa6ce02132e9e54e6ccc116013be8df403 GIT binary patch literal 11382 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 XjQ>DW&(VSBDEF5#feSAYkjV3I`*L+w literal 0 HcmV?d00001 diff --git a/ai_evo/_init_.py b/ai_evo/_init_.py deleted file mode 100644 index ebc0d8c..0000000 --- a/ai_evo/_init_.py +++ /dev/null @@ -1,4 +0,0 @@ -numpy==2.0.1 -matplotlib==3.9.0 -streamlit==1.37.0 -pytest==8.2.2 diff --git a/ai_evo/brain.py b/ai_evo/brain.py index fb45f89..ca99114 100644 --- a/ai_evo/brain.py +++ b/ai_evo/brain.py @@ -1,23 +1,37 @@ -import numpy as np - -class RNG: - """Central deterministic RNG wrapper.""" - def __init__(self, seed: int): - self.seed = seed - self.rs = np.random.RandomState(seed) - - # Basic distributions - def normal(self, loc=0.0, scale=1.0, size=None): - return self.rs.normal(loc, scale, size) +"""Neural network brain for creatures.""" - def rand(self, *shape): - return self.rs.rand(*shape) - - def randint(self, low, high=None, size=None): - return self.rs.randint(low, high, size) +import numpy as np +from typing import Tuple, Optional +from .genome import Genome - def choice(self, a, size=None, replace=True, p=None): - return self.rs.choice(a, size, replace, p) - def random_bool(self, p=0.5, size=None): - return self.rs.rand(*(size if isinstance(size, tuple) else (() if size is None else (size,)))) < p +class CreatureBrain: + """Simple neural network brain for creature decision making.""" + + def __init__(self, genome: Genome): + """Initialize brain with genome weights.""" + self.weights = genome.brain_weights + self.input_size = 8 # Basic sensory inputs + self.output_size = 4 # movement directions + + def process(self, inputs: np.ndarray) -> np.ndarray: + """Process sensory inputs to generate actions.""" + # Simple feedforward network + if len(inputs) != self.input_size: + inputs = np.pad(inputs, (0, max(0, self.input_size - len(inputs))))[:self.input_size] + + # Simple linear transformation + if len(self.weights) >= self.input_size * self.output_size: + weight_matrix = self.weights[:self.input_size * self.output_size].reshape(self.input_size, self.output_size) + outputs = np.dot(inputs, weight_matrix) + return np.tanh(outputs) # Activation function + else: + # Fallback for insufficient weights + return np.random.randn(self.output_size) + + def get_movement(self, sensory_data: np.ndarray) -> Tuple[float, float]: + """Get movement direction from sensory input.""" + outputs = self.process(sensory_data) + dx = outputs[0] - outputs[1] # left-right + dy = outputs[2] - outputs[3] # up-down + return dx, dy diff --git a/ai_evo/config.py b/ai_evo/config.py index fb45f89..582ff12 100644 --- a/ai_evo/config.py +++ b/ai_evo/config.py @@ -1,5 +1,75 @@ import numpy as np +class Config: + """Configuration class for simulation parameters.""" + + def __init__(self, + seed: int = 42, + width: int = 100, + height: int = 100, + max_steps: int = 1000, + init_herbivores: int = 20, + init_carnivores: int = 10, + mutation_rate: float = 0.1, + mutation_strength: float = 0.1, + plant_growth_rate: float = 0.05, + max_energy: float = 200.0, + min_energy: float = 0.0, + herbivore_bite_cap: float = 3.0, + carnivore_gain_eff: float = 0.8, + reproduce_threshold: float = 120.0, + reproduce_cost_frac: float = 0.5, + move_cost_base: float = 0.1, + # Trait bounds + speed_min: float = 0.1, + speed_max: float = 3.0, + size_min: float = 0.5, + size_max: float = 2.0, + aggression_min: float = 0.0, + aggression_max: float = 1.0, + perception_min: float = 0.5, + perception_max: float = 5.0, + energy_efficiency_min: float = 0.5, + energy_efficiency_max: float = 2.0, + reproduction_threshold_min: float = 60.0, + reproduction_threshold_max: float = 150.0, + lifespan_min: int = 500, + lifespan_max: int = 2000): + """Initialize configuration with default values.""" + self.seed = seed + self.width = width + self.height = height + self.max_steps = max_steps + self.init_herbivores = init_herbivores + self.init_carnivores = init_carnivores + self.mutation_rate = mutation_rate + self.mutation_strength = mutation_strength + self.plant_growth_rate = plant_growth_rate + self.max_energy = max_energy + self.min_energy = min_energy + self.herbivore_bite_cap = herbivore_bite_cap + self.carnivore_gain_eff = carnivore_gain_eff + self.reproduce_threshold = reproduce_threshold + self.reproduce_cost_frac = reproduce_cost_frac + self.move_cost_base = move_cost_base + + # Trait bounds + self.speed_min = speed_min + self.speed_max = speed_max + self.size_min = size_min + self.size_max = size_max + self.aggression_min = aggression_min + self.aggression_max = aggression_max + self.perception_min = perception_min + self.perception_max = perception_max + self.energy_efficiency_min = energy_efficiency_min + self.energy_efficiency_max = energy_efficiency_max + self.reproduction_threshold_min = reproduction_threshold_min + self.reproduction_threshold_max = reproduction_threshold_max + self.lifespan_min = lifespan_min + self.lifespan_max = lifespan_max + + class RNG: """Central deterministic RNG wrapper.""" def __init__(self, seed: int): diff --git a/ai_evo/creature.py b/ai_evo/creature.py new file mode 100644 index 0000000..88efba6 --- /dev/null +++ b/ai_evo/creature.py @@ -0,0 +1,43 @@ +"""Creature class for individual animals in the simulation.""" + +import numpy as np +from typing import Optional, Tuple +from .genome import Genome + + +class Creature: + """Represents an individual creature with genetics, behavior, and state.""" + + def __init__(self, id: str, genome: Genome, species: str, position: np.ndarray, energy: float): + """Initialize a creature with basic properties.""" + self.id = id + self.genome = genome + self.species = species + self.position = position.copy() + self.energy = energy + self.age = 0 + self.generation = 0 + self.alive = True + + def can_reproduce(self) -> bool: + """Check if creature has enough energy to reproduce.""" + return self.energy >= self.genome.reproduction_threshold + + def is_alive(self) -> bool: + """Check if creature is still alive.""" + return self.alive and self.energy > 0 + + def consume_energy(self, amount: float, min_energy: float, max_energy: float): + """Consume energy, ensuring it stays within bounds.""" + self.energy = max(min_energy, self.energy - amount) + if self.energy <= min_energy: + self.alive = False + + def gain_energy(self, amount: float, max_energy: float): + """Gain energy, ensuring it doesn't exceed maximum.""" + self.energy = min(max_energy, self.energy + amount) + + def update_position(self, dx: float, dy: float, world_width: int, world_height: int): + """Update position with world boundary wrapping.""" + self.position[0] = (self.position[0] + dx) % world_width + self.position[1] = (self.position[1] + dy) % world_height diff --git a/ai_evo/creatures.py b/ai_evo/creatures.py deleted file mode 100644 index fb45f89..0000000 --- a/ai_evo/creatures.py +++ /dev/null @@ -1,23 +0,0 @@ -import numpy as np - -class RNG: - """Central deterministic RNG wrapper.""" - def __init__(self, seed: int): - self.seed = seed - self.rs = np.random.RandomState(seed) - - # Basic distributions - def normal(self, loc=0.0, scale=1.0, size=None): - return self.rs.normal(loc, scale, size) - - def rand(self, *shape): - return self.rs.rand(*shape) - - def randint(self, low, high=None, size=None): - return self.rs.randint(low, high, size) - - def choice(self, a, size=None, replace=True, p=None): - return self.rs.choice(a, size, replace, p) - - def random_bool(self, p=0.5, size=None): - return self.rs.rand(*(size if isinstance(size, tuple) else (() if size is None else (size,)))) < p diff --git a/ai_evo/genome.py b/ai_evo/genome.py index fb45f89..d39d872 100644 --- a/ai_evo/genome.py +++ b/ai_evo/genome.py @@ -1,23 +1,40 @@ -import numpy as np - -class RNG: - """Central deterministic RNG wrapper.""" - def __init__(self, seed: int): - self.seed = seed - self.rs = np.random.RandomState(seed) - - # Basic distributions - def normal(self, loc=0.0, scale=1.0, size=None): - return self.rs.normal(loc, scale, size) +"""Genome class representing creature genetics.""" - def rand(self, *shape): - return self.rs.rand(*shape) - - def randint(self, low, high=None, size=None): - return self.rs.randint(low, high, size) +import numpy as np +from typing import Optional - def choice(self, a, size=None, replace=True, p=None): - return self.rs.choice(a, size, replace, p) - def random_bool(self, p=0.5, size=None): - return self.rs.rand(*(size if isinstance(size, tuple) else (() if size is None else (size,)))) < p +class Genome: + """Represents the genetic information of a creature.""" + + def __init__(self, + speed: float = 1.0, + size: float = 1.0, + aggression: float = 0.5, + perception: float = 2.0, + energy_efficiency: float = 1.0, + reproduction_threshold: float = 90.0, + lifespan: int = 1000, + brain_weights: Optional[np.ndarray] = None): + """Initialize genome with trait values.""" + self.speed = speed + self.size = size + self.aggression = aggression + self.perception = perception + self.energy_efficiency = energy_efficiency + self.reproduction_threshold = reproduction_threshold + self.lifespan = lifespan + self.brain_weights = brain_weights if brain_weights is not None else np.random.randn(10) + + def copy(self) -> 'Genome': + """Create a copy of this genome.""" + return Genome( + speed=self.speed, + size=self.size, + aggression=self.aggression, + perception=self.perception, + energy_efficiency=self.energy_efficiency, + reproduction_threshold=self.reproduction_threshold, + lifespan=self.lifespan, + brain_weights=self.brain_weights.copy() + ) diff --git a/tests/_init_.py b/tests/__init__.py similarity index 100% rename from tests/_init_.py rename to tests/__init__.py diff --git a/tests/__pycache__/__init__.cpython-312.pyc b/tests/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7a250e877e36a28e384d2ca5b6548bbd71f00491 GIT binary patch literal 263 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 0 assert slow_energy_loss > 0 From b17e91352cc618b7fc448c61481de2b2b8b7aff6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 27 Aug 2025 01:19:56 +0000 Subject: [PATCH 4/5] Fix core simulation and creature classes Co-authored-by: SimplyAISolution <124332391+SimplyAISolution@users.noreply.github.com> --- ai_evo/__pycache__/brain.cpython-312.pyc | Bin 2590 -> 3183 bytes ai_evo/__pycache__/config.cpython-312.pyc | Bin 4602 -> 4823 bytes ai_evo/__pycache__/creature.cpython-312.pyc | Bin 3066 -> 3564 bytes ai_evo/__pycache__/evolution.cpython-312.pyc | Bin 3407 -> 9184 bytes ai_evo/__pycache__/genome.cpython-312.pyc | Bin 2026 -> 2786 bytes ai_evo/__pycache__/spatial.cpython-312.pyc | Bin 2019 -> 3142 bytes ai_evo/__pycache__/world.cpython-312.pyc | Bin 1813 -> 2700 bytes ai_evo/brain.py | 24 +++-- ai_evo/config.py | 6 ++ ai_evo/creature.py | 12 ++- ai_evo/evolution.py | 99 +++++++++++++++---- ai_evo/genome.py | 23 ++++- ai_evo/spatial.py | 30 +++++- ai_evo/world.py | 14 +++ 14 files changed, 176 insertions(+), 32 deletions(-) diff --git a/ai_evo/__pycache__/brain.cpython-312.pyc b/ai_evo/__pycache__/brain.cpython-312.pyc index c124f87dce8ec050b7da43cdebde9e83c580f091..d7695fce2ae23ff0113dff7cfb2c605263a42ead 100644 GIT binary patch delta 1408 zcmah|T}&KR6rMXXcV>TJL11UuZI^DTlv1VG6$(vBZD~pqV<^%DWP_50nf+mZb7xkH z-F2Ht8%#`Oa~~FxrqHB`OR7Snu`edlCrx~Cx7%dth_Q*jgJNQ$FW$SbpikaR&iU^7 z&UenacV_N3Y&BZGudX&RWZfP6Jn)s6u{>^`CA%!6FdiT?9##e(PBzJ$!uwd6pM@SM z!_1Zq2$?O(Ab3h zedT~IOva+VpkJjL7k;0;ZD}FsCi7f6JjZ`z-oR$ApZ%J{*SK$h8UM&PNt&hHiI@j_ zZP5oBAk>i>u*=9?=mf){5WdPwC&ze(NwFy|#iyVKE7Gaj3Oew(10FdGl5C}Hl6#Mk z5B7?tg#T9`6_WhE8vaKuCdF%vW~C>v$oom?{_f=1&AWhv@9~ll8Y818=;-(sf2bU^ z13ND8R-ET0r*trH1ywqx4D50^!pe(0mPrd zSJSA9&^lBD-ni0MqjO;;ifU+xjmC8@?v-^e;=Qh$f@(0TYToFig6O-^p_Yh;6`d!c zfX>M=O&0@R)vIX;(QDU5q?Bvw0`f-XSVV`^N860(1wtcwM4cTu-K(<`rwMQq@79Gt zP?nXbF2)jCJfW$2^|TW72ek2sS3|+;D*e1%m4)i@uy9PQBQezr@J(SL{j1qJFK5G< z@cqkA%_l!q*E%2dKI|=aTv)&Oq~r2)=h3CH#jy_u=KAK@`Mw2q(`w6(WJc}`Ee$UY zFZbvAiq4L;=7O`cXziNo-;^4&{*3?5#FB5(w>+F1DmJ~c<}NgK6{RzCS2pS$a|3?} zOs#ER%O*3)<&mc~>Gm&=t;vtV55q*os_s?g z+Xr*7RYmIFzKVrO=H+=tj|%oUh(=99}xU zczjvPIg5_&d2!Qb&rWBi7p9B0)_iR%8Ku$g$j)SDvgu5ESu3@67h1cEww`=#53*vv z_+4E)xv4g|d;g~hMla!u1_{*Tw+!vz1io$P7pyb|;Jo2Xx`AdKB1RXZvKJxm459@P z{fTwx3&+SS=_GiCK~#n6sWDUx1?n5b%rejTOTY0DIv3vEVTjwdG{Cb96H8NzQ`-!I zxuI3hif4zy?Ux~xa&@bY6~{KdWwhGMg=s1B+4K;n!)PKBpFxN47si(Ub0l)m`!7Rp rBqk@q%6W925cH2$>G<0m0N@$Z_6t+@Gt>MNl(0n+abF=g>Tn_z5w delta 975 zcmYjP&1(}u6rbIl*(95!u}PY?G1h3RkcywFmV#KQQbAIo76V45#O@|FHXlq9+e%7B zMCwVy98wS3dTMLIdJ-=lJP3jw5`#t;MWKIysTD*o`X;vOF!SE;{ocpC8Qzzcd(MWJ zR;z+wy*m4FM3xH;&%D>bDk2Sq1r>!wRR~L}sNtxnO4o6isJO7&T&OiGQLM^97*zuh ztwb|vil#)xR>%gI5nSd8lV4$HgcD(1j%mqMLK_zORqKmm3C>zCh$6}h@pZ@lDvySQ z4O=7^<5DzqpI?iaLQEGzt3=d;>#T$k;09VmxwqNQaAz+KFCN;6= z#Kq>Ng7t6hJc=u9i3HdK;%K-+5V~{@A)2exzsPP*vmV(X*w}G-7g$cydLxq}dy3oG z4{Q-9C3Z{R+AJ-tR~ox?Y{1#mr)soZ(0lJt> z>ry(R>QW*yP8)!tn>cTvT4p4Y)^tftWp$j5Bu8|aMv`hOp%bo?eoPyAcoPq|j%gQT zBiW%uBug)j>xNV=o6co38Qzo^7PZ%lrfF)J?K8R!JTtR`*nsim)HfMfY}46W`J4HQ z*)xadZTD@D+N%e8%dow@ug$&hJl@&honXlyTxeh3ySVqsvFhPq8Fojo*zf` zTEm!ZnYp}%K-DZ(a&YFtZ2V5Vh5#;{S`IIU*Ep=T5#m~~FS{4rHTKrr)LJjhOZiQ7 z4=9}^bBXi>ZDrW9qi;Vj|L*>2Je)|Wxl!#1?F0uOrp#s#HAxVJ_sGA3?4Qu~kI4O9 M-XgfZA`tcD-_#oS>i_@% diff --git a/ai_evo/__pycache__/config.cpython-312.pyc b/ai_evo/__pycache__/config.cpython-312.pyc index 4ec47ac3944109564dc80424176b35b39e01599f..5106ecce4bd82a606d0dd35691636e3274287b5f 100644 GIT binary patch delta 1563 zcmZ9M&rcgi6vt=mUE3IJAf^0D4D0nT)_?;cfsg=pew37^btwIrKxH|2gRL54jY%sI zDXo-4Rpn4LQmgdTLoY~G6)C6w1wFKwdat#V5_RGR*fQsvP0y|)mNj=W!g-oDxI zzM1ForT-2U%(K?pAU9!y|nYW$5n{!e0NFL3LUm4yLlp{ z7y50zV89i&wSsADH_jI#xAkkDji?ou^ZcH_sanog)^p4Gs{EI4^{j|eA-8Oo%Z*^I zlCRc^YwJ1t9b0V34cBj-rt>;d7pNQG{r&wd_bxZL`nOoF?_k1~0a5>xC=8JyL;5ri z%SHW@E)jv0h@!+mEVH52?D7rFwf`?yGv$uQl=nOz_}rYa_nCTLPI_lOJ?4knouGWr zJMOLT^OxRR>F{0>4EG9XJp@wv6^h*JzZ#GXHB>hv^+ZyB)HJyL9G~U7RqyImZiENjN0pACJghgQ zyo5d$GE@_xpGD%TnXm;hKp14PSzJuU4-4NZ#$xT zh92~B1uRD>{!rNs#2%#5i7VG>_ANH~NzOqgQF!s+Zf$GiFk@|9C3p1G~y)T8D^!_S;8sAX~G$n8dGV)3`agko2t%pW<=Bl zC%A~gU(R+rUBiam_ctfGmB7~N*f#w68-~+R?C@sGLiSQBmn&6D>$%*^@~xHu9b2B2 zJorXe2FP-Wi}2O(RMy05zHDhCA^&da@1)QOrSH z!vz~KoaJR9+H5}XgvXZT7eOrUo8Z!w3?!edY`M5rTDC3Sapie(Yj}kXe=^HhyVw1xR~`ElDT z+qmVHn~tV91)Q~9*{tQxz6wBxGHW%9akQ8~IgK)jas~w(DXU|c6#iaT3svLEZZ|;5$3_4 i9IJ&vl$&$@6Fc%qo8#R2ouNK@)78P>=6^6$D&{}!(rC^A delta 1331 zcmY+E?`u<67{|{^?rnb5+%{d-YHgC6q)B?xHui_DuGS_vT$w-OrgJhal&rZnVQE^< zO%WBXg0R=+fS|8^VP9+vx>sZS3kDkqvJk=T-H)qk%OLKmLVERP`L<;q@e<(Btxccw`ww;3F zfg=T+ktBJfLm5cm#`mkfN(#{jBe=z#Dl>4AmI?pj_D|2 z3^7h?;yZE8m&^pIEg`VASJYn0X>TVAv_o!znzSp*ErMg~QafI1% z400(#C-DUBV9l*H+-AA4;F?7K(v>0{*3j#G~VZ2{KZgTp7BKZ7yNIAO3C~W3;{Av diff --git a/ai_evo/__pycache__/creature.cpython-312.pyc b/ai_evo/__pycache__/creature.cpython-312.pyc index 3f3dfdfc1bc55a8395cb3b6dc1bba4f81a076cde..ca2bd68f4fed8446cdd16c5360b67c1da4d1eb5e 100644 GIT binary patch delta 573 zcmew*{zjVbG%qg~0}xoptjpNQK9TPlb!L`ZVeDGsnOa|!cge-@eg8m1biDxgs{%r(p)x(aB-YDORd1i=iN%vI{ni8;xo zIf*5y3MrYzC5d^-sR||e3W<4O7bzqcr6!h?7NzPHf!tOEa+D@>5f6~M#gtcYi>0`* zs6>;cNEjrk3nJ7(ga(i(W&slQ3P4aK0%YCdh8Z7Ul3yj_3^Nx=6r{g66i75MJm3}V zkL`?|VR(^Ob^+^hzJ+`%3NI=Ed4?TKH}J^a;TP;L?<}8CG(%}(>5QTU!b&qsC)Qr( zSG~ZYS|kQE+D}txvICbnYmq*XGr5|}lBvjW@#?6y2aLqOT%)rd*#Q1@Wg;i~V z)CUG2wSn~uhyrUa;s9#WR}Gj2sh^!hfQvNN@-52U6B<~f)R*|Wr4&8W=2NFn+$f38HDaLDBWd{`p%}z N=*0Mm0Yrke0{~!^E?EEo diff --git a/ai_evo/__pycache__/evolution.cpython-312.pyc b/ai_evo/__pycache__/evolution.cpython-312.pyc index 8adae6c78810a7089dd507e5784bc51871db882e..63bb5ca7e396b702c9a28cac2ed2e7c2d54731a6 100644 GIT binary patch literal 9184 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 diff --git a/ai_evo/__pycache__/genome.cpython-312.pyc b/ai_evo/__pycache__/genome.cpython-312.pyc index 3cb423295c600f0f3ad75c504f07ae4eba72280d..898646b99dcff3975448422a52f77de92adfb0ab 100644 GIT binary patch delta 1307 zcmZuwU1%It6ux(MW_M?1f2L~_&2}f*Y}&HZ%5KabRl-KJn$Tc;P(-K_V`iFc*vu|7 zv&0RvY!ty2L@Xo5#|T1FQZ>GnzV%HIq0_A-41HK7pe+;z(}(Jl=T3HE*LvWd`<-*o zIrseB`yl#zPvqxtm_tDIcfMc#JosbeC(Qdn^uapX!q#tiIkNGkOjTLgSLI~7%3GJI zk(kMv+%84@5R#d71|gyrK#BjWjG3ItKWRGx$}Zb0w29`uR7ivhHiO-=p z(L)d;nM_)*_&&imtzUdE)LcIb_1)%^&Axd3(n0)Geb!@#LTr=0%5FxlMz^?~*@kf8 z#;7gi?scg7hVbl;ZVMw_%ALy%;rSac+rm_rx^3&SOe#{p_-m-&ji6MvK1kpZDQ&UU zB!UrXk2jgOu=Of^x@M3bc+_sNCK@n|W0LX5$V4lxF}1a1zle5QVtWQvtj7P}b|uj1 zd5l}$460&-wZ5g_gW0XbtUbC1pCo*!_emcUU#E7-^nl(#lF!O81GTQ|NM)n2jwXT4 zB8)a6{dZ3yxW&XPoqd3FLhT~H!kZYQ&w6Q8LErh-u!QF%?9hwFQbnR1Dl_chHJ!8} znbhL2{liW!DXQ{8>5`UP@1IuG;;ND%)|tsdadDxb=Q1q=Etdg%os{z@dc1+|q-OT; z)^pnv-%M;@uv0UC>tq5S#S_?ijX6IQg7>E9Roz%n7ZoiGM-F3@O9e$60IZS8kY`$x zD2;@4B5-M5k>}Tx{L-?acjoAMtn(eood*y7Ltq=|knj0I-tSG@z3J;`?()O;#1tg6 z_vZfiv^_q3H#O4`XFi>4ii7)N))uq-;+QRtHN=Vf++nomAZ9h0@!|BN6Rg0wh-En~ z0a2x3>od7i+F6jb7_cQ+gkIFnT5tQ`AAJh26Hw&^Ow~@dbi!p|hac8IynM*si- delta 667 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 diff --git a/ai_evo/brain.py b/ai_evo/brain.py index ca99114..8308482 100644 --- a/ai_evo/brain.py +++ b/ai_evo/brain.py @@ -8,8 +8,9 @@ class CreatureBrain: """Simple neural network brain for creature decision making.""" - def __init__(self, genome: Genome): + def __init__(self, genome: Genome, config=None): """Initialize brain with genome weights.""" + self.genome = genome self.weights = genome.brain_weights self.input_size = 8 # Basic sensory inputs self.output_size = 4 # movement directions @@ -20,14 +21,21 @@ def process(self, inputs: np.ndarray) -> np.ndarray: if len(inputs) != self.input_size: inputs = np.pad(inputs, (0, max(0, self.input_size - len(inputs))))[:self.input_size] - # Simple linear transformation - if len(self.weights) >= self.input_size * self.output_size: - weight_matrix = self.weights[:self.input_size * self.output_size].reshape(self.input_size, self.output_size) - outputs = np.dot(inputs, weight_matrix) - return np.tanh(outputs) # Activation function + if isinstance(self.weights, tuple) and len(self.weights) == 4: + # Handle tuple format (W1, b1, W2, b2) + W1, b1, W2, b2 = self.weights + hidden = np.tanh(np.dot(inputs, W1) + b1) + outputs = np.tanh(np.dot(hidden, W2) + b2) + return outputs else: - # Fallback for insufficient weights - return np.random.randn(self.output_size) + # Handle simple array format - fallback + if hasattr(self.weights, 'shape') and len(self.weights) >= self.input_size * self.output_size: + weight_matrix = self.weights[:self.input_size * self.output_size].reshape(self.input_size, self.output_size) + outputs = np.dot(inputs, weight_matrix) + return np.tanh(outputs) # Activation function + else: + # Fallback for insufficient weights + return np.random.randn(self.output_size) def get_movement(self, sensory_data: np.ndarray) -> Tuple[float, float]: """Get movement direction from sensory input.""" diff --git a/ai_evo/config.py b/ai_evo/config.py index 582ff12..e08f28d 100644 --- a/ai_evo/config.py +++ b/ai_evo/config.py @@ -13,6 +13,7 @@ def __init__(self, mutation_rate: float = 0.1, mutation_strength: float = 0.1, plant_growth_rate: float = 0.05, + plant_cap: float = 10.0, max_energy: float = 200.0, min_energy: float = 0.0, herbivore_bite_cap: float = 3.0, @@ -20,6 +21,8 @@ def __init__(self, reproduce_threshold: float = 120.0, reproduce_cost_frac: float = 0.5, move_cost_base: float = 0.1, + grid_cell: int = 8, + snapshot_every: int = 100, # Trait bounds speed_min: float = 0.1, speed_max: float = 3.0, @@ -45,6 +48,7 @@ def __init__(self, self.mutation_rate = mutation_rate self.mutation_strength = mutation_strength self.plant_growth_rate = plant_growth_rate + self.plant_cap = plant_cap self.max_energy = max_energy self.min_energy = min_energy self.herbivore_bite_cap = herbivore_bite_cap @@ -52,6 +56,8 @@ def __init__(self, self.reproduce_threshold = reproduce_threshold self.reproduce_cost_frac = reproduce_cost_frac self.move_cost_base = move_cost_base + self.grid_cell = grid_cell + self.snapshot_every = snapshot_every # Trait bounds self.speed_min = speed_min diff --git a/ai_evo/creature.py b/ai_evo/creature.py index 88efba6..e9adda5 100644 --- a/ai_evo/creature.py +++ b/ai_evo/creature.py @@ -8,7 +8,8 @@ class Creature: """Represents an individual creature with genetics, behavior, and state.""" - def __init__(self, id: str, genome: Genome, species: str, position: np.ndarray, energy: float): + def __init__(self, id: str, genome: Genome, species: str, position: np.ndarray, energy: float, + generation: int = 0, parent_ids: Optional[list] = None): """Initialize a creature with basic properties.""" self.id = id self.genome = genome @@ -16,7 +17,8 @@ def __init__(self, id: str, genome: Genome, species: str, position: np.ndarray, self.position = position.copy() self.energy = energy self.age = 0 - self.generation = 0 + self.generation = generation + self.parent_ids = parent_ids or [] self.alive = True def can_reproduce(self) -> bool: @@ -41,3 +43,9 @@ def update_position(self, dx: float, dy: float, world_width: int, world_height: """Update position with world boundary wrapping.""" self.position[0] = (self.position[0] + dx) % world_width self.position[1] = (self.position[1] + dy) % world_height + + def distance_to(self, other: 'Creature') -> float: + """Calculate distance to another creature.""" + dx = self.position[0] - other.position[0] + dy = self.position[1] - other.position[1] + return np.sqrt(dx*dx + dy*dy) diff --git a/ai_evo/evolution.py b/ai_evo/evolution.py index bc99cb9..d4b91f9 100644 --- a/ai_evo/evolution.py +++ b/ai_evo/evolution.py @@ -8,6 +8,18 @@ class EvolutionEngine: def __init__(self, cfg: Config, rng: RNG): self.cfg, self.rng = cfg, rng + def create_random_genome(self, species: str) -> Genome: + """Create a random genome for a given species.""" + return Genome( + speed=self.rng.rand() * (self.cfg.speed_max - self.cfg.speed_min) + self.cfg.speed_min, + size=self.rng.rand() * (self.cfg.size_max - self.cfg.size_min) + self.cfg.size_min, + aggression=self.rng.rand() * (self.cfg.aggression_max - self.cfg.aggression_min) + self.cfg.aggression_min, + perception=self.rng.rand() * (self.cfg.perception_max - self.cfg.perception_min) + self.cfg.perception_min, + energy_efficiency=self.rng.rand() * (self.cfg.energy_efficiency_max - self.cfg.energy_efficiency_min) + self.cfg.energy_efficiency_min, + reproduction_threshold=self.rng.rand() * (self.cfg.reproduction_threshold_max - self.cfg.reproduction_threshold_min) + self.cfg.reproduction_threshold_min, + lifespan=int(self.rng.rand() * (self.cfg.lifespan_max - self.cfg.lifespan_min) + self.cfg.lifespan_min) + ) + def mutate(self, g: Genome) -> Genome: mr = self.cfg.mutation_rate ms = self.cfg.mutation_strength @@ -19,28 +31,81 @@ def mt(val, lo, hi): new_weights = self._mutate_weights(g.brain_weights, mr, ms) return Genome( - speed=mt(g.speed, 0.1, 3.0), - size=mt(g.size, 0.4, 2.5), - aggression=mt(g.aggression, 0.0, 1.0), - perception=mt(g.perception, 0.5, self.cfg.perception_max), - energy_efficiency=mt(g.energy_efficiency, 0.4, 2.5), - reproduction_threshold=mt(g.reproduction_threshold, 50.0, 160.0), - lifespan=int(mt(g.lifespan, 300, 2400)), + speed=mt(g.speed, self.cfg.speed_min, self.cfg.speed_max), + size=mt(g.size, self.cfg.size_min, self.cfg.size_max), + aggression=mt(g.aggression, self.cfg.aggression_min, self.cfg.aggression_max), + perception=mt(g.perception, self.cfg.perception_min, self.cfg.perception_max), + energy_efficiency=mt(g.energy_efficiency, self.cfg.energy_efficiency_min, self.cfg.energy_efficiency_max), + reproduction_threshold=mt(g.reproduction_threshold, self.cfg.reproduction_threshold_min, self.cfg.reproduction_threshold_max), + lifespan=int(mt(g.lifespan, self.cfg.lifespan_min, self.cfg.lifespan_max)), brain_weights=new_weights ) + def crossover(self, parent1: Genome, parent2: Genome) -> Genome: + """Create offspring through crossover of two parents.""" + # Simple average crossover for most traits + offspring = Genome( + speed=(parent1.speed + parent2.speed) / 2, + size=(parent1.size + parent2.size) / 2, + aggression=(parent1.aggression + parent2.aggression) / 2, + perception=(parent1.perception + parent2.perception) / 2, + energy_efficiency=(parent1.energy_efficiency + parent2.energy_efficiency) / 2, + reproduction_threshold=(parent1.reproduction_threshold + parent2.reproduction_threshold) / 2, + lifespan=int((parent1.lifespan + parent2.lifespan) / 2), + brain_weights=self._crossover_weights(parent1.brain_weights, parent2.brain_weights) + ) + return offspring + + def _crossover_weights(self, weights1, weights2): + """Crossover brain weights between two parents.""" + if weights1 is None or weights2 is None: + return weights1 if weights1 is not None else weights2 + + if isinstance(weights1, tuple) and isinstance(weights2, tuple): + # Handle tuple format (W1, b1, W2, b2) + W1_1, b1_1, W2_1, b2_1 = weights1 + W1_2, b1_2, W2_2, b2_2 = weights2 + + def crossover_array(arr1, arr2): + mask = self.rng.random_bool(0.5, arr1.shape) + result = arr1.copy() + result[mask] = arr2[mask] + return result + + return ( + crossover_array(W1_1, W1_2), + crossover_array(b1_1, b1_2), + crossover_array(W2_1, W2_2), + crossover_array(b2_1, b2_2) + ) + else: + # Handle simple array format + mask = self.rng.random_bool(0.5, weights1.shape) + result = weights1.copy() + result[mask] = weights2[mask] + return result + def _mutate_weights(self, weights, mr, ms): if weights is None: return None - W1, b1, W2, b2 = weights - def mutate_array(arr): - mask = self.rng.random_bool(mr, arr.shape) - arr2 = arr.copy() + + if isinstance(weights, tuple) and len(weights) == 4: + # Handle tuple format (W1, b1, W2, b2) + W1, b1, W2, b2 = weights + def mutate_array(arr): + mask = self.rng.random_bool(mr, arr.shape) + arr2 = arr.copy() + arr2[mask] += self.rng.normal(0, ms, mask.sum()) + return np.clip(arr2, -2.0, 2.0) + return ( + mutate_array(W1), + mutate_array(b1), + mutate_array(W2), + mutate_array(b2) + ) + else: + # Handle simple array format + mask = self.rng.random_bool(mr, weights.shape) + arr2 = weights.copy() arr2[mask] += self.rng.normal(0, ms, mask.sum()) return np.clip(arr2, -2.0, 2.0) - return ( - mutate_array(W1), - mutate_array(b1), - mutate_array(W2), - mutate_array(b2) - ) diff --git a/ai_evo/genome.py b/ai_evo/genome.py index d39d872..4c5b5f5 100644 --- a/ai_evo/genome.py +++ b/ai_evo/genome.py @@ -24,10 +24,29 @@ def __init__(self, self.energy_efficiency = energy_efficiency self.reproduction_threshold = reproduction_threshold self.lifespan = lifespan - self.brain_weights = brain_weights if brain_weights is not None else np.random.randn(10) + + # Initialize brain weights as tuple (W1, b1, W2, b2) or simple array + if brain_weights is None: + # Create default neural network weights + input_size = 8 + hidden_size = 6 + output_size = 4 + self.brain_weights = ( + np.random.randn(input_size, hidden_size) * 0.5, # W1 + np.random.randn(hidden_size) * 0.5, # b1 + np.random.randn(hidden_size, output_size) * 0.5, # W2 + np.random.randn(output_size) * 0.5 # b2 + ) + else: + self.brain_weights = brain_weights def copy(self) -> 'Genome': """Create a copy of this genome.""" + if isinstance(self.brain_weights, tuple): + copied_weights = tuple(w.copy() for w in self.brain_weights) + else: + copied_weights = self.brain_weights.copy() + return Genome( speed=self.speed, size=self.size, @@ -36,5 +55,5 @@ def copy(self) -> 'Genome': energy_efficiency=self.energy_efficiency, reproduction_threshold=self.reproduction_threshold, lifespan=self.lifespan, - brain_weights=self.brain_weights.copy() + brain_weights=copied_weights ) diff --git a/ai_evo/spatial.py b/ai_evo/spatial.py index 53ea600..cedf08a 100644 --- a/ai_evo/spatial.py +++ b/ai_evo/spatial.py @@ -3,8 +3,16 @@ class SpatialHash: """Grid-based spatial hashing for approximate neighbor retrieval.""" - def __init__(self, cell, W, H): - self.cell, self.W, self.H = cell, W, H + def __init__(self, cell_size=None, world_width=None, world_height=None, cell=None, W=None, H=None): + # Support both new and old parameter styles + if cell_size is not None: + self.cell = cell_size + self.W = world_width + self.H = world_height + else: + self.cell = cell + self.W = W + self.H = H self.grid = defaultdict(list) def _key(self, x, y): @@ -18,4 +26,20 @@ def rebuild(self, agents): def neighbors(self, agents, a, radius): cx, cy = self._key(a.position[0], a.position[1]) r = int(math.ceil(radius / self.cell)) - out + result = [] + + for dx in range(-r, r + 1): + for dy in range(-r, r + 1): + key = (cx + dx, cy + dy) + if key in self.grid: + for idx in self.grid[key]: + agent = agents[idx] + dist = math.sqrt((a.position[0] - agent.position[0])**2 + + (a.position[1] - agent.position[1])**2) + if dist <= radius and agent != a: + result.append(agent) + return result + + def get_neighbors(self, agents, query_agent, radius): + """Get neighbors within radius of query agent (alias for neighbors).""" + return self.neighbors(agents, query_agent, radius) diff --git a/ai_evo/world.py b/ai_evo/world.py index 8ae96ef..865be8e 100644 --- a/ai_evo/world.py +++ b/ai_evo/world.py @@ -17,3 +17,17 @@ def grow_food(self): def wrap(self, x, y): return x % self.width, y % self.height + + def step(self): + """Advance environment by one time step.""" + self.t += 1 + self.grow_food() + + def consume_food_at(self, x: int, y: int, amount: float) -> float: + """Consume food at given position, return amount actually consumed.""" + x, y = int(x), int(y) # Convert to integers + x, y = self.wrap(x, y) + available = self.food[y, x] + consumed = min(available, amount) + self.food[y, x] -= consumed + return consumed From 4d652adc13a44141c267040d0a57dc9d50119d85 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 27 Aug 2025 01:30:00 +0000 Subject: [PATCH 5/5] Complete test infrastructure and core functionality Co-authored-by: SimplyAISolution <124332391+SimplyAISolution@users.noreply.github.com> --- ai_evo/__pycache__/brain.cpython-312.pyc | Bin 3183 -> 6928 bytes ai_evo/__pycache__/creature.cpython-312.pyc | Bin 3564 -> 3698 bytes ai_evo/__pycache__/spatial.cpython-312.pyc | Bin 3142 -> 3867 bytes ai_evo/__pycache__/world.cpython-312.pyc | Bin 2700 -> 3224 bytes ai_evo/brain.py | 51 ++++++++++++++++++++ ai_evo/spatial.py | 12 +++++ ai_evo/world.py | 9 ++++ 7 files changed, 72 insertions(+) diff --git a/ai_evo/__pycache__/brain.cpython-312.pyc b/ai_evo/__pycache__/brain.cpython-312.pyc index d7695fce2ae23ff0113dff7cfb2c605263a42ead..b004c5cd9d945a58957fb60a4122ff77f6c64a1d 100644 GIT binary patch literal 6928 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 VlX&bOMAd&xuaKnmK7paS{C~4X2sQu! delta 206 zcmbPW_FjVTG%qg~0}#xMS(mYmeInmMkrp6tIzuW$6k`fQ6jKUg6mtsK#K%P}l}wu4 zo98kf<7Cxj^wZ>-+$-s?af>xACqJ>I$ONcoB}0)YkSY=e5gafA#3=TeEF|S6{Dpy) zRScpAB&x|-WHEWVl%bn7NG)9JFAkgB{FKt1RJ$TOpadgGzci5ez|6?Vc#}c%F@w;3 U2Bo_UQs3EB8O1&^012>W0DI9bAOHXW diff --git a/ai_evo/__pycache__/creature.cpython-312.pyc b/ai_evo/__pycache__/creature.cpython-312.pyc index ca2bd68f4fed8446cdd16c5360b67c1da4d1eb5e..728bdd4714f713f891122a2b962db3b7ace2a0e2 100644 GIT binary patch delta 974 zcmYk3OK1~87{_NeyV*ywN!z@dq>oh6)}*zqRYXb^Q4tYZ(VIbAqBE(HG%1r*EEHOy zhaP+&Bh;&xQZIrON{=2DsfwW1y?C`C5?k;f9{j$o*beOPd(8ho-|X)95 zR`0dvH~Wo8fsMlrSi;&_11u3va+Hyr!f4K85|&^dt}>NtOoQm<-dh3LVTR^(ZZLzJ z%;XmL#lcw!Fl#}}`I(Q~1%>$+)Lh_SZbKeq0dNH2Y2Ze|%Nn8A;P$XqSrgQaECl;u zcp`9el$!;E#h~BBni+w!P#1?fG@~e)q4+FW1J4 zta`(c9cq{^%T{GNJL{b#<2t@6u37yKttymE5a=4aJwliQl_{5%)6|k%O8Sflz?KLC z94%ie-sTy@QLDUE5GHQmrxZSfA7LZ@JF0kWDkaZ(7JZ|T&Xc;Agd*~@r{7L5s`rAM ziS&m=c14l8`pF*#MU|pMMC6v5rOh&|rPUa8WV;%bm$k`g2#Vby$^Gh&T8g&FU)lj0 zlX35lPLyR-fhUS~L<-RXsH%VzESS-&_tKQSq7Q-VvHrAqPwpx`yGjs_7+u~jG{@zb zo>1@mL~M1*XrgJ?aS(Py29ZVdpil$g6dbc*_R*~TV~)_+>IJKtwz;l8)c&{bzt%%v z7wwm8zOewldPWfngwae`CjH0f1|WCzGRq5*pK-K8sgkcbo?^Ks@Vbs#DpqSE>ZaE# zm6B+IRJ0+|h;GCo#30}q5ja9b5cscLU{ZYxSRh~2i}1(J()0AE$EPKp92u??h$~(< ztcTq2N_l6!w%qjn)w~-wTT^v3Yz_{sAAQlcgLyq6zxn6m?m!%oF~u k@~+*PI02o#JN-6>Co62a#7~M-@}=Ew_t36w0&pdN0kR~_tpET3 delta 824 zcmYk1%WD%+6vpSyJCn?$&8s!hBxss+Len53h?OFhiW}{!n^4rzd!vy&k4Z!?saOdW?%E_wHwUUg{Ne+Hin7~X%Ryv^D`` zc}y&5MUoH=EeCv{-AzP5=Qbj_*EXwXNm4B72f@bm-&1IFRgO_SjmRLffVK+A!h!HR z#tcb|H^y<;`ei(x+_n2o{M3KOXJ(${#Fh#1n*%dKa=x!6fu#Md5_7IE2XKi&ATL_zaGR0WTzufjovVcYsZM6R<9QRhI^8KubP4GLWD= z*ZuM~iY1YJCx5iwTfX#Z#T%hxkPuCKZJ(ctIc|U-h0@oW-NyAEFAKw&O3s0@`@?_C X6O9(_*4aFt7pI+!?UMXZ0{E7{!OE=z diff --git a/ai_evo/__pycache__/spatial.cpython-312.pyc b/ai_evo/__pycache__/spatial.cpython-312.pyc index 85d9bab89972f0a09f249c30227c15602405609b..4c9b55ed0e1c27d74baca390de2ac409372ae485 100644 GIT binary patch delta 832 zcmY*WO-vI(6rS1LmUg#A8bD;lx=|BZjg^SyAhnVxQbWR}UT8v^O?NGrrI48^LS0f~ zIB@VnHlY|vxEfIdCu2N$@#4jbiDaWU4<0LcP*1+y(yA|+@4YwgXTC4<_SoZS>$`AR zL}0mz7nAn`tF6V31Y{LtpbXYghSPC|*TvOJv`r5f!8@EDToy9o0;ftluhzsq$4?Aa zB!wJymmHc8JP+sKDL@6WX3awOo0blF>+oU`Gq=?<9WSGLxqP>!arxJ89fmhhBWf<; zERF>mojPaXEv}xGB!m`u3tODU>-+|vMygQi88MhbXEipZnaZR_Cl$&dp=>HgW1fm~ zW0wFyKb(V00HX+l6bnrpfu;B#Nw`5|4OcKm4hP&tQ-^^ucs zLtS=?LL%UP2NQXtV9d{wWT`h@CzUD#saW1%S(g}%CCr(eX3}J=+3M1v9RT0Z&n5Ih zPCXge=wF}P9IVQzU({v&ptB1T83L7eBsRk|Gn+F^lRBY#qQR?-I_=tAK3k8ovj*{c zR9~4~fk_eqaClmrcKmaiSv07_Piyn4K*DbIstZXMt^s(DDqx-}C)i|P>TIa`Pt-+s zCJnHJz8>vfKeMSmQtiTu^tnr}%44bR+b_p=#y`b}tMTF8_{c{&wcC|i3GGGCJxgy# zwnuj_3{|5;?*qqce$;bzucvqK^uYHPp-rqIAt;h|SgiWTJX0lmT)G)>1br&U$aQ-| zisJM3Yw1K}{}ksNtOZ1$xPJKu0%w1bRQVRndJF3R6G%+Y=ta{=l2Ka;b%*0v+(!Uj F%O8qV%YXm? delta 183 zcmbO&cT9rsG%qg~0}vdGS(kB&Z6aT(hyjo{ogtMWiZO*DiYbLLiaCX2;&eHdN+wOt zjYp?(acQ#ofq^FHWN|(>yOj(@!ayxWf*^twMz8=G#qvO+h2bWP$7d!kR<%zOjI36l z*qB+p89#CHvxJ1#ZfahMLUBoANoH|LW^%EfCd)0Z zlKhgyocOf-{FGZ9iDl_vR*?|Uq+6UNnYpR)#U-f)n(Vh&({l0?ON#hF63oS=xkZ8? z7E5kwVqOs&NO_U)0t$jmMcD~q5 zv5%ar0$d*$SUI_h#2Hu_82mK(CkJo_2o)KET%`jfZn30fCYKbMOkT+;#~3>K1gE>; zX9jjwi4Q{TtXvCNz5v-=lSQ~povlDR#6g58kod)6lbfGXnv-f*WDDeiVyIXGNPJ*s bWMsU}Aas{O>Mn!GcNSGfu1^d=0<04N8jFph delta 119 zcmbOs*(1t#nwOW00SGu^*JV_*Oyuie^qM%`o28OhlW*gvbPi@eP2S1w+yR`oSkrRy z6HAJ8Ca>g{WAvPSg4><-3j;f=#AF2?Qz;{$2}R-{LKH~+;;_lhPbtkwwJS26oX;b} OE5j)9i2+D}l>h*7!yV`V diff --git a/ai_evo/brain.py b/ai_evo/brain.py index 8308482..7f847ca 100644 --- a/ai_evo/brain.py +++ b/ai_evo/brain.py @@ -43,3 +43,54 @@ def get_movement(self, sensory_data: np.ndarray) -> Tuple[float, float]: dx = outputs[0] - outputs[1] # left-right dy = outputs[2] - outputs[3] # up-down return dx, dy + + def get_sensory_input(self, creature, environment, nearby_creatures) -> np.ndarray: + """Generate sensory input array for the creature.""" + # Basic sensory inputs + inputs = np.zeros(self.input_size) + + # Position relative to world center + inputs[0] = creature.position[0] / environment.width - 0.5 + inputs[1] = creature.position[1] / environment.height - 0.5 + + # Energy level (normalized) + inputs[2] = creature.energy / 200.0 # Assume max energy is ~200 + + # Food in current location + x, y = int(creature.position[0]), int(creature.position[1]) + x, y = environment.wrap(x, y) + inputs[3] = environment.food[y, x] / 10.0 # Assume max food is ~10 + + # Nearby creature information + if nearby_creatures: + # Count of nearby herbivores and carnivores + herbivore_count = sum(1 for c in nearby_creatures if c.species == "herbivore") + carnivore_count = sum(1 for c in nearby_creatures if c.species == "carnivore") + inputs[4] = min(herbivore_count / 5.0, 1.0) # Normalize + inputs[5] = min(carnivore_count / 5.0, 1.0) # Normalize + + # Distance to nearest threat/prey + if creature.species == "herbivore": + carnivores = [c for c in nearby_creatures if c.species == "carnivore"] + if carnivores: + nearest_distance = min(creature.distance_to(c) for c in carnivores) + inputs[6] = max(0, 1.0 - nearest_distance / creature.genome.perception) + else: # carnivore + herbivores = [c for c in nearby_creatures if c.species == "herbivore"] + if herbivores: + nearest_distance = min(creature.distance_to(c) for c in herbivores) + inputs[7] = max(0, 1.0 - nearest_distance / creature.genome.perception) + + return inputs + + def forward(self, sensory_input: np.ndarray) -> dict: + """Forward pass through the neural network, return action dictionary.""" + outputs = self.process(sensory_input) + + # Convert neural network outputs to action dictionary + return { + "move_x": float(outputs[0]) if len(outputs) > 0 else 0.0, + "move_y": float(outputs[1]) if len(outputs) > 1 else 0.0, + "eat": float(outputs[2]) if len(outputs) > 2 else 0.0, + "reproduce": float(outputs[3]) if len(outputs) > 3 else 0.0 + } diff --git a/ai_evo/spatial.py b/ai_evo/spatial.py index cedf08a..61ed32c 100644 --- a/ai_evo/spatial.py +++ b/ai_evo/spatial.py @@ -43,3 +43,15 @@ def neighbors(self, agents, a, radius): def get_neighbors(self, agents, query_agent, radius): """Get neighbors within radius of query agent (alias for neighbors).""" return self.neighbors(agents, query_agent, radius) + + def get_stats(self) -> dict: + """Get spatial hash statistics.""" + total_cells = len(self.grid) + total_agents = sum(len(agents) for agents in self.grid.values()) + avg_agents_per_cell = total_agents / max(1, total_cells) + + return { + "total_cells": total_cells, + "total_agents": total_agents, + "avg_agents_per_cell": avg_agents_per_cell + } diff --git a/ai_evo/world.py b/ai_evo/world.py index 865be8e..f1a960b 100644 --- a/ai_evo/world.py +++ b/ai_evo/world.py @@ -31,3 +31,12 @@ def consume_food_at(self, x: int, y: int, amount: float) -> float: consumed = min(available, amount) self.food[y, x] -= consumed return consumed + + def get_statistics(self) -> dict: + """Get environment statistics.""" + return { + "total_food": float(np.sum(self.food)), + "avg_food": float(np.mean(self.food)), + "temperature": self.temperature, + "time_step": self.t + }