From 5e10b4c4617069c8e68f6f58f1caec9a55c669b7 Mon Sep 17 00:00:00 2001
From: ClemW <clement.wang@student-cs.fr>
Date: Mon, 7 Jun 2021 00:53:30 +0200
Subject: [PATCH] push MVP

---
 __pycache__/gen_algo.cpython-38.pyc | Bin 0 -> 9332 bytes
 __pycache__/graph.cpython-38.pyc    | Bin 0 -> 1490 bytes
 gen_algo.py                         | 325 ++++++++++++++++++++++++++++
 graph.py                            |  59 +++++
 main.py                             |  80 +++++++
 main_seq.py                         | 106 +++++++++
 6 files changed, 570 insertions(+)
 create mode 100644 __pycache__/gen_algo.cpython-38.pyc
 create mode 100644 __pycache__/graph.cpython-38.pyc
 create mode 100644 gen_algo.py
 create mode 100644 graph.py
 create mode 100644 main.py
 create mode 100644 main_seq.py

diff --git a/__pycache__/gen_algo.cpython-38.pyc b/__pycache__/gen_algo.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..405cda6c515797b0d78076361d961454e2fab437
GIT binary patch
literal 9332
zcmWIL<>g{vU|`4#+?$}Q&cN^(#6iX^3=9ko3=9m#J`4;DDGVu$ISf&ZV45k4DTOJ8
zxrHH$*_|PUC55$xA%!)SrI|U3)tw=QErq>>A%z{xXLDyr;Yi_ZVMyTw^Vw4vgBdir
zUV`lLOJ)L5Pz++TGB7YWgKX4bU|=X=s9{K9lw_!31d$R9&5U3XCa?&TBm+o9oFSM&
zli9C`k%58XC5X^uyv3ZDRIJGq#Zr)8YzQWeRx%WU{PHU_*(xTqIJKxaCOIcHH#Np3
zwK%&ZzaYlYF($FJBtJK?Br&ffrl2T4E48FJCMPkevM4jJBqKE@JvA@2Br{nTE(8*Z
zPs~Zr*DI*J#hII!ml2<mSzH2ge=*1-42(r=3=9m(P`|+`W>}DxF)%Qsf`c=PDMc6@
zn2b@(DIzJXDQqo_Q7oyfDeNg6Eey?!QEaJfS?npCa2{(4R|+>+h9i|Di#>%0&SOpC
zP2mH}aHeu*v8V9EcqsxYf-MYDTq#1q44R_1SlknHQ&%#*1i9uVJII}2H$z<s5&&Uv
zMA$JhFr+ZnFvK&YFx4=`Go~=tFvK&Zu+%WbGpDfDFvPQ@u%$4Uur6RrVXt9KVV7h`
zVUu83$Rxrb&XB^M2GYZhTSX0H3Y#QD3bP0USS3dZ=K?OMDa;}aDQx1P$cL%`@zR(U
zg51KL!dAnO#ht=#!vJM*z*!6_46RHco6$r#(wJ-*QaDq%S{Q1Xz`EH|I9i!fz+@Ve
zBttr5EprJ^4RbSN3VSy51jZtz8s-JODeMawYguZTYFL^XYgubpT^M2oYT0U-7x2}v
zEo5Y5n7~-bSNIAPDp}k$3|Sx-foKp0xe1g!dHi02(#1<qf_MoE=$A&I)SAM;z)%F@
zLK5%)|Ns9dgHkXw78pR01JV)<$`2Nd3=B04S>XJT!j#QeqyXkINislq=?uYed6-N#
zL(z^D<`TvQOrQwNVn_k0W|m?|VS+?Z3X?cPEhCBwMo?r(fupaM2^8Te;NYxb1P2~t
z4MPnRgk1wF4?qFUP{Y{D1PVTwNDU*%Tu>x{MAMmq8L-)a;sz!Z6;M}#l29!p$PG21
zh(oixhN+ba;x>>iAa}uRMTmm!21Ni1q<nw`HZKDMgC_GW*5ZuBg4A1VMTvPS`MI~)
zL3Czbi6-+cmfXau)F{@n)S{BiD3*-;qD*k%0U<yUd5f<oHMz8?II}D@9;6mjRu#*D
zqK#38k%dWwN#Q>evlNp6BM*}flM<s8BL^c7W0fi>&*{a-XXa&=#K-H|<m4wO<`moM
zp_*013349)EpAY05}%fsT#{dOODMIXASb^lu_QA;4<^Q2o|uypUyz!Yn3Gvjc}p-g
zucRn3FF6$<#GP7^SrT8ATAo;xQX~XQY{Co-47WI7DsQoX6x?FUD~MvPN-fGSE)oIB
zGv}n{-C{1tFSx~)SWu9fm!ipji={X<C#^^jWQHh64QpX(YH8{%=Hk+%TP!81xdpcv
zF#?GL=2d=B`qcsj4-*?B8wfM9F)1->F=?=IF>)~qF$%G8h)OW>F>*0-Fmf;oF!3-}
z@uCElTQVpwfJ^{k4h9AWP&9yxr!Hu|sAWz8WmD#KP$p%}Vsv4cz!<Aj%T~jd!kEoe
zWKqKmszYj+!Fhm5oFR>=mc528g(;h<C<G!0N<R>Js2p=PQ&9m#4k8XpvrsvfY^I_f
zh#W{1A`g{g&1NcE0+B-!X9wHNn9VwYvB;_h%&TF@V#)%$D4PK+%8<oW11dm4u6j_z
zQp1qNV!==gq9NiSpM0ueuVn`51gm7uW}3iQBmfozsbfV^&jOa!1DnKV4l)U(mvsSq
z4Kt|dDij0DfbD>?IZ8N7xN2CN8JihJ7{EF}rgDRd#Ttez-ex8d2CxkvTbpWFYM5(S
z)0l%9K&&*TU<OULstzs%C`c?yFU?KOD^ak53xQ;G6$)|^D^rVf6cQDJ6LSl4QWcVO
z5{ru!@{_VslS|-gi&9HUi}Elu=VTU_D3oWGWGECT=NF|aB<7_kq~@h46yz6Yg7ODK
zKd3B4WO;DLdI@R;Xz~?FgAy@YdTL&NZYns{L(;YcD1l3Y2q_Su03yUegd9jcb8%`(
zkr;>zDwT^s8D5j4NRfeoA&L#+rCV%8smb|8DN!6S*W6+SyE2L;H#IlENEM`61w??{
zfmEQdfb_eA@(HN9#lX(S$il?N$iu|JD8h(}1(;NrKskkvk%du!k%f_qQGkhyk%fte
ziHBK?S&Wg5k&Cg46P$ZMW};Lmpa213aK-`o0aQ^0GpuCvTge#384oKLRx*M;1#uqO
zp`hFb4zntd6G2rL0~0ih!ZcyD<Up#x7J-_WHK0nHA%!8Bp-30xeLV&ShLucUYi=>;
zrIur}s}5loF}?sBypr)2Ydok_E7Av919m)^0Q&$GV@(KCp}_=FPMYgMwRe#L4hz~4
z7LefVC{}Pyg4NleQn?FZD%9Daiib33n}A%5&DkIe`VbaiI2#_7e9%UD7GpCbBO*FA
znQpNbfEvn>tb`@#Z*hU;!G4?sw~4WeACgc(dcksjnv6xD;$4%`O_Tc;M|^x<Vs2`D
z{4K8d_}u)I(i{+*CqBNgG%*JvQ=|s+lny9+augKh7o--IRDx6LEw<v+l9JRSa5jaw
z%M7FxoEi`WI9-6+x}a*e7?jO9*tjgXjF}j@m{}OPn9=DXkXnpJJ;)SL#w`Zr4|t;<
z)SPEZVNM72k66-JVGVmovp$M7l?~DuZDx#OPi4>INZ|%GO~E|26rL1bcr%_Og%8eS
zOW{uu0LyTta%FL(2*P+NEWr$#Lbuo;C2FEFBLf4tB!rZ43RVi>ax*_oAv3QeH9fVc
zSRpC3q&zh>Pr(3Ogqna=8z@*Qloo)Qh6+{+MVaXtkOJ9A!Ac<|zdR2tWehSdH4P$T
zqF|*^T#{I+P+XFklcS((r%;lSS*(y*tWa8<npT>VqfnBskds<moSLUll98&ASDKrY
zS_C!+R2v}U{4|B!{Ib+yaPgjBTB4Agk(igBnU@aMn3I~9UXr1Zp9bM&7ME~=O$pA=
zO;so<Pfg4&Rwz#`N>xb7&r1banwXMUPy%9rtSv|^D#=VP%}Fd$fCLFxFSuFs5)|!z
z$)Exe)Vu&;P-*~0D!2gK2I~Am3NR4K2x{PFGZks2FsHDzFqAOWFf}u}Ff@bYnDaVn
z7*jwE0IM2MKZ!Z70@TCFW-3}$!;-?7%~2dw!wRB{qDoj&*qa%f7;9Lv7;0Fv7_wQ4
z3Tjwt*=ks8+4DGRSZmp8SZdk9{VI+U_8OKf=4Qr)Otl;}Y+1}Hj5Qpspl&dG7IO_p
z3L}Uu&H(C><#Cj-q;P`Fg><v4?t=XZ3gzUYRB!`Q!B#;D)Y#3>RRTpZxcRH2kd&&B
zoLB^pu;SDbNHBu~9c~8592izgF3Jbh+GVLlAcIo!6%rK?Lh$5eq)-g5<cqNy0y7Lo
zV{t}7Vo_>di6J<ZLfA%dqx`@Th*DsI3M)`lf_qJ%3O${n2GnI^EMZsx9=lk`G?A$g
zlv|j<{jMT)P=lKZY)}!Xfvm}Ni%HMm7Gn-trF4tS29$@A^K%RA)`7|}P$|O3z{F6c
z49Rp@+V%(?MW9Z?FD^s?Rfz|HJyxucSd^-emtUfglA4y8mzn~0Rh5iyeo87RvBRSc
zNr@&$kv6Ev<4Mg+iAOXwiW2kEQ^6M9;`B?*O?53Q$}d8AtO%6OH8~-*E(grqTU@z`
z74eYF1<}h631!1uP=-+yYf@@@X5KC4)V!1^Zm<{P3-UAbO3*?GTo^9~1&tCY6LGLH
zvN3Wnih*189E<{t985fnER1YSEQ~CSLW~N`Rs3iHhZ2#X{0G9|JXiy+c@{9HFqVM&
z?vT6yiBh;o4I`*qTgz0!Qo~roSi>aF(9Gn*5bIFO432un6sCntwJbF(Da_dnMN#0U
zFLMpE4MU+BsIi{SQe;-cki}fXoWdf(APMSBLuFG~!42Cec&ZD@NGwsvEiHj$r;?(~
z)M8LEla^SPUs|M4TvC(>DmN8!GV>r3>8T|OpyDh)O#$5LQ^?KC%gim!g&PQt@**Bk
z{lpC-Kxx=dlNBxMz_C*V3Jy(Ha3n@CRTxGwRT$l3%r3&#s$>JZG4&uQMg%}H!Xm^d
z#w5hZ1L}}6aWGc#Lc<@T4kh$K?L-g;Syv3oPms1Ew3QXb9uFx+u{KyhN&N!IQjkAD
zEk&p`5G|xNT0kR@Sle14ORgX+!EijtZm{D)WdO+WDGb>hMM5QvDU6`v5hN1KpvmM{
z#TyLnM`RW&_~qxNYBJqoiwC#ai(Eiafu%76&U-gNR)fNYfeF-Yt>S_D5v-LYFKRLt
z`GRc1)-nU-@_QhgK)zvMBHxoDu1H-ZP~{BuD{nk<5rW&xP7DkTk3bGV^0F}0%g82>
z>T^F_J_lL&0%Re?=bGF_<{+gOAi@$<_OZvur{pKc$Ab%<B5#nm6^H<(iy~_f3lw-o
zAajt4DNvbR1S(aEY#10ArhzN~Y2aXF;bmfg!v9=y9Q;g(1|Tc6A;`+Y2-bzW`3JHL
zrTHfeZtO8eu|S%7QLHK4DLgF<QEZTL+9=Qntw0My6sRF5)WQ(OnF8uWi`-&zcT7}b
zf;QnCOG=6|lS)fci=mz2g8YK`<iw&B1uIaUosyZCm;<UQ3i1m|bHHU1xL_&BF95aE
zGK)*VlBxNio_2XgW^#rCs1D07P6gNSXu48!GD}jinGR}1fhEC)!|H1o4VP6&&MzuT
zEiTB<O92;(1^EThR$zT#Lu0|ZQ&JO4GQfN^FMx~zOBO3+<|(8mCTD<JYpIZtFR)ga
zg&?{lBefFAK2RA9vMx8js02JH2DZTpVkNkU0}Y=QC6=UuO+r+AkalDUsM!h`iGUaB
zNvR5n#ZV{YfS8#j3dxCiAe)NwbIMW`5*5-Db0Ox1WTYzO=jBv_+P`_}3YGb#3K@xI
z;Fc)39h(aBQf3J#^MSmXn^*y<bQKB`ixP8FOQ4NnhzhVlrNyZ!3Mr*Uuof_A<S;Wa
zC$kt5$zWgjz3dQXV0d0Fvd;ZsRg3-eSu;W4CX8{?J{i<w0Tuor3~JYcvH)l}t+<1M
zfdSGnuVDj??1NjZOeM^q!5k4tdzPhyxrVid8OmdAXGmjAVN79a;V5APjgv!Lz3iZI
zZx@DGrCRnH#swTTpb>M98jciZNroEsc+MJ*6c#XxtI!9eGK(AB6=DG8jb0{5KT?y^
z52bJe<$6$218R^JgGz8jD=vkhgCT{n7tx9X4Q*;N6}f`)yc?)72g#<IOhtB}f`T!A
zCF3n#NZkW#=YYZhQVf8)ZM=*O48<TJHby3fDtUKD{4F_9wFn+pLUA!j6okR;PLR_f
zH4L=E1lJiW8E>&c8pB8>5~wh`#bJ|^n46T6XeYwR!0;IqQ&qA!oB>f6vXbc*b9QRw
zE%u_+vecsDR85{+ETE)#ix(xO-{OE37Ppu)^Ga@UrNZ;~EiQ0Q17{L&tD;CA6oR0>
zG}yJ^qVpC@aeh$=w9#0U08$VRB9cHM%L+E^7AHKz6(xe?qCf<qJrV}uf_w=sssDqD
zT+rwm11G3?$i~PA9{6SZ&%q2D4CVrjlyf6_9i>76nF7M#dL7i)hBWLzqoJTt*o92B
zOc)}}CCuQ-8I~GQhlwGJrG{COA%&$EG=|Dj!)OC)=&>#UO((I`FiJ9jCwo}HW5pn~
zjFJrT>@_T)v0Kn+EJ#H>2ed(00nUJ+f(SG^t_Lc5Af4M-P#!NzEyzhsPEAors#Hi+
z$V)AU_Jb7ilS&dn?R<y?xHYYiUk1(nAk$zSb0Y;vHw~&0(Sd?A7eQI1$Ptv@nIUN(
zG=5m*!N9=ar^$)bbOc2exZZ-qFE|cSBN^J9yu}7_ei7UttgT9Zu#@9KPL3}wNi7fq
z<sQ%w5(BFclMo{lBNGD$1IK?JW+6ra#wsCqM?EwRDA@+&3sBYrM=7W(22Js1F@mC0
z0u(ij;5Iy8En^MC0_GY<@UXQAxNpmpm<t=^0EHT;Oa-S$h4TD7^%8K07Bqa2nxc?c
zqL7%A1CK3`%JTf8;t~Z=6<H1%wE(ryks~ZWEv>i!RPMqxm8XKt$S()SBwQL)5fr5s
z6y>LsCZ~ez0hyhf4<7Xf*G{0m9k{2JngTZp<l4+Uh5VwF)FN=luOuH_Bj==6WTsUr
zl;r0t<d!C9fNSSW(D*pWI7pEX(h9;Q`3fodpo*qgN1;3wQO_jifQC3qAT>L<DGGB`
zaUy7xBr`WvAt^OEv9uUcXMxOuR5*~q8jyd$2B#u*<ssE0NU5JD3#6m~r>Rs>#?C^F
z<Q92>1VE7qDc(Ri^%kVZ3r;Pnpo9XFX5f+l&F~;643umRs+~aUz-b}~((_|j2u=;z
z3`KsRv;oS93z!!&FoHx`7O=v^7Bbc{)i6P`FB3>rJX;MDH2XsM>?zE_44N!edhiH=
zm6)J*5_(pKD?(&CKTQru_5zo7;6fAJ{KS^Cn86*jqD)Z8Ac{ng=fKquQfUp!N=Bd%
z0*#k5uz-q1a0U`^chrM95S-s|XCF|n8<BmmWE?(7#;GzdPgMY?d$7UalnqX8h_;|Y
z38;^oSqw>^8L0~3tObvM@YFRp5#)k=mJK4no&po#i01&Cotd9!1M(C|oPkM*u?keS
z!(>o=0;)Sfs=$#BDhO&A7BD~}cmX4X2G5p(njZ|Hp*MKbffcps05S-Ku~o!H5g?aF
zf(VdZh;&*M&A`Cm!pOi-6vM#4P$li|sF#ut>f<8jX<=1#NKq`P&}Avg0U47ABEV@N
z4aCCIUA)BxG071W4xoks19(8BiW6>!CaYgjAV@bTtrvmZT@(Vcf(xzBQ{)d)1Rn7Q
zWjlxiz!3m;MLtLt;Vm!=6ct4Q3=9l!LG>=EA;rPSBErB3g+IB(IruqvIk-4j!KySl
zZn5T-<`z^I$$|_6wXDF)SZ=Z9gD2;}!{3m~1Y{^A{J<8036O2KIBXz8^>(00C<YB`
Ra4>Q(bFc{1Gw=z70|42_*&YA@

literal 0
HcmV?d00001

diff --git a/__pycache__/graph.cpython-38.pyc b/__pycache__/graph.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..2a852878ebe6729f05b9e42ed9f5e00e27a15367
GIT binary patch
literal 1490
zcmWIL<>g{vU|=W++?$Za&cN^(#6iZ)3=9ko3=9m#W(*7rDGVu$ISf%Cnkk1dgdv4F
zg{6fdiYbLPg{_4liaCWng`<TbiY0|Rg)@b#g)xdXg)x{xljkMKL_bZ&TZ~%CEFd-%
zvoSC*fUI{0S&+uSz)-?a!<faG!YIkm%;>@p>ru-B6=8yknAEb?FxIftu%<CdGNdqN
zGZiV6Fl8|>V5woKVXk4VVG?1eVF8m&HB4zN3z-=i3Ka_ZN|>@(7qBg4U}Q*P2xeHx
z>{rCdz`*blM66`L#afh@m!5iywKyZOAoUhoL1lVkZt5+Tl%mA)TP#JX$tAbgQ!<MS
zauO?Vv6U92B$lLVvfW}U&&*57FTcf-n^={4i@Uh6G_fc(zBscg^%fUMC_XtqC%@<x
zW9BW!td$H!!VC-yzk-sjVnT~ki;82Cb5e6tV_Z^;vrF;|VhkN)5=%?+a}!Gv^Gae0
zit@8kONwK15|b*6GV@9@Qe)Cn^HNJPlXc-jG3iB#1sQq;mA5!S&Ws1yC;$pYAyCjV
z2{8&W3NW%UGBGkS3jO6^EfQs5V4!l)Gm{haMWFOl#LB?HpvhLm%fP@;#0Mhy85kI%
z1d|ezv*Xi?@=NnlAfZ<z2$BSO2^@Y9LWF^V;TBhNPHJKi*xRyjZ!=<eTMXoFkZqt)
z0bzCq1_n+B1_p473u0gZr8kBerWD2$re3BLW=V!x)*4n9hFG0iwi3oHrUlG3p!CF+
z!Xn8~!z{v3!<NDdWii(<L(`X731b$^0#;b+(q!`s(PX^ER+O5YUzGBa3FHM(q6a5A
zO}1MspoCP!0TKqeqzIG&inu{s9#F8bgM;N33pfq36({Bv<fImX!six$aYlZ5d_iJK
z1|)QDvE-)a=HFs2$S;P33@D8ifrEz=u2d5gI-r!nz$nDX0ZkH8f7w`z<UwHrau_IF
zKp4aZg%vn#V!*jEg|V5Dk)ebkg{g+2nTe61hB<{Ho3luzgdv5wh8ZMM!jQrO&a<qN
z3|UN25w;R$xEM<fQx<D8Oa^QoC=Y6~`&H@ak^rj2lT!1NGjbD)vK8{v6hJwmD6u3n
zKd*|bBr`WvL8Dkx!K#WUDYdvnp*T6eC{@7<9O6|XzKMAmi6te8c?v0+#U+V($*Brf
znrydN3qY9*6nq7@Siw4OaporGWyFJ2-Qr2jONlSYFU|y6af>CjBC|x3<rZ^#YThlD
z#GLf}A}LTZWC2-?9{3#Kz)w%jGXaG?D19?9axij%u@IvaV-YA$G#ULg*}$pb7Dsw&
wUOd=LP%bS3rCD&~fYl)xj)-0ko80`A(wtN~Pytp9N<bWp9DE$S96TJ{01gmMRsaA1

literal 0
HcmV?d00001

diff --git a/gen_algo.py b/gen_algo.py
new file mode 100644
index 0000000..5aba5f0
--- /dev/null
+++ b/gen_algo.py
@@ -0,0 +1,325 @@
+import numpy as np
+import random
+
+
+def manh_dist(pos1, pos2):
+    return abs(pos1[0] - pos2[0]) + abs(pos1[1] - pos2[1])
+
+
+class Game(object):
+    # nothing : 0
+    # wall : 1
+    # entrance : 2
+    # exit : 3
+
+    def __init__(self, shape=(3, 7)):
+
+        self.dist_factor = 5
+        self.exploration_factor = 6
+
+        self.wall_penality = 2
+        self.entrance_penality = 50
+        self.exit_reward = 100
+
+        self.entrance = (1 + 2 * random.randint(0, shape[0] - 1), 0)
+        self.exit = (1 + 2 * random.randint(0, shape[0] - 1), shape[1] * 2)
+
+        self.maze = np.zeros((shape[0] * 2 + 1, shape[1] * 2 + 1))
+
+        # recursive generation with queue
+
+        # delimitations :
+
+        self.maze[0] = 1
+        self.maze[-1] = 1
+        self.maze[:, 0] = 1
+        self.maze[:, -1] = 1
+
+        # generation :
+
+        def recursive_maze(maze):
+            if maze.shape[0] <= 1 and maze.shape[1] <= 1:
+                return []
+
+            if maze.shape[0] < maze.shape[1]:
+                verti = (
+                    2 * random.randint(0, maze.shape[1] // 2 - 1) + 1
+                )  # odd between 0 and shape[1]-1
+                hori = 2 * random.randint(0, maze.shape[0] // 2)
+
+                maze[:, verti] = 1
+                maze[hori, verti] = 0
+
+                return [maze[:, :verti], maze[:, verti + 1 :]]
+            else:
+                hori = 2 * random.randint(0, maze.shape[0] // 2 - 1) + 1
+                verti = 2 * random.randint(0, maze.shape[1] // 2)
+                maze[hori] = 1
+                maze[hori, verti] = 0
+                return [maze[:hori, :], maze[hori + 1 :, :]]
+
+        queue = [self.maze[1:-1, 1:-1]]
+        while len(queue) != 0:
+            sub = queue.pop(0)
+            temp = recursive_maze(sub)
+            for i in temp:
+                queue.append(i)
+
+        self.maze[self.entrance] = 2
+        self.maze[self.exit] = 3
+
+    def play(self, player, record=False):
+        """
+        argument :
+            - player, a Sample class object
+        return :
+            - list with score and end position
+        """
+        position = self.entrance
+        score = 0
+
+        memo = [position]
+        for i in player.genome:
+
+            if i == 0:
+                temp = (position[0], position[1] + 1)
+            if i == 1:
+                temp = (position[0] + 1, position[1])
+            if i == 2:
+                temp = (position[0], position[1] - 1)
+            if i == 3:
+                temp = (position[0] - 1, position[1])
+            if i == 4:
+                temp = (position[0], position[1])
+
+            if temp[1] >= 0 and temp[1] < self.maze.shape[1]:
+                # this can occur at the entrance and at the exit
+                if self.maze[temp] == 1:
+                    score -= self.wall_penality
+                if self.maze[temp] == 0:
+                    position = temp
+                if self.maze[temp] == 2:
+                    position = temp
+                    score -= self.entrance_penality
+                if self.maze[temp] == 3:
+                    # this makes it go for exit faster and staying there
+                    score += self.exit_reward
+                    position = temp
+
+                memo.append(position)
+            else:
+                score -= self.wall_penality
+
+        ### push the exploration :
+        score += self.exploration_factor * len(set(memo))
+
+        ### push the way toward the exit :
+        score -= self.dist_factor * manh_dist(position, self.exit)
+        if record:
+            return score, position, memo
+        return score, position
+
+    @property
+    def entrance(self):
+        return self._entrance
+
+    @entrance.setter
+    def entrance(self, new):
+        self._entrance = new
+
+    @property
+    def exit(self):
+        return self._exit
+
+    @exit.setter
+    def exit(self, new):
+        self._exit = new
+
+    @property
+    def maze(self):
+        return self._maze
+
+    @maze.setter
+    def maze(self, new):
+        self._maze = new
+
+    def print_maze(self):
+        print(self.maze)
+
+
+class Sample(object):
+    """
+    genome : list of integers between 0 and 4
+    0 : up
+    1 : right
+    2 : down
+    3 : left
+    4 : stay still -> this is usefull to lessen the number
+                    of moves without changing the length of the list
+
+    Some tweaks were done to adapt to the particular genome
+    """
+
+    def __init__(
+        self, creation="random", max_length=30, genome=[], parent1=None, parent2=None,
+    ):
+        """
+        creation = "random" : random, be carefull to set the length
+                    "cross over" : do a cross over between 2 samples
+                                    be carefull to set parent1 and parent2
+        """
+        self.score = None
+        self.end_position = None
+        if creation == "random":
+            self.genome = [random.randint(0, 4) for l in range(max_length)]
+        elif creation == "genome":
+            self.genome = genome
+        elif creation == "cross over":
+            if parent1 is None or parent2 is None:
+                raise NameError("Parents are not defined")
+            # we want to keep the beginning of the best parent
+            if parent1.score > parent2.score:
+                begin = parent1
+                end = parent2
+            else:
+                begin = parent2
+                end = parent1
+            cross_point = random.randint(0, len(parent1.genome))
+            self.genome = begin.genome[:cross_point] + end.genome[cross_point:]
+
+        else:
+            raise NameError("Mode of creation not defined")
+
+    def mutate(self):
+        """
+        That mutation tries to favour straight lines to get out of local minimum
+        """
+        x1 = random.randint(0, len(self.genome))
+        x2 = random.randint(0, (len(self.genome) - x1))
+
+        for k in range(x1, x1 + x2):
+            temp = random.randint(0, 6)
+            if temp < 5:
+                self.genome[k] = temp
+            else:
+                if k > 0:
+                    self.genome[k] = self.genome[k - 1]
+
+    @property
+    def genome(self):
+        return self._genome
+
+    @genome.setter
+    def genome(self, new):
+        self._genome = new
+
+    @property
+    def score(self):
+        if self._score is None:
+            raise NameError("Score is None")
+        return self._score
+
+    @score.setter
+    def score(self, new):
+        self._score = new
+
+    @property
+    def end_position(self):
+        if self._end_position is None:
+            raise NameError("End position is None")
+        return self._end_position
+
+    @end_position.setter
+    def end_position(self, new):
+        self._end_position = new
+
+
+class GA(object):
+    """
+    Attributes :
+    pop_card : cardinal of population
+    pop : list of people which compose the population
+    elite_card : cardinal of the elite
+                    the elite corresponds to pop[:elite_card]
+    death_card : cardinal of the deaths in each generation
+                they correspond to pop[mortality_card:]
+    mutation_rate
+    max_length
+
+    This class tries to be as general as it can to solve a game
+    The only thing you have to change in it is the maxlength parameter
+    it is only used during the initialisation
+
+    """
+
+    def __init__(
+        self,
+        game,
+        genome_length=None,
+        pop_card=5000,
+        elite=0.01,
+        mortality=0.4,
+        mutation_rate=0.2,
+    ):
+        self.game = game
+        self.mutation_rate = mutation_rate
+        self.pop_card = pop_card
+        self.elite_card = int(elite * pop_card)
+        self.death_card = int(mortality * pop_card)
+        self.pop = [
+            Sample(creation="random", max_length=genome_length) for _ in range(pop_card)
+        ]
+        for sample in self.pop:
+            temp = game.play(sample)
+            sample.score = temp[0]
+            sample.end_position = temp[1]
+        self.pop.sort(key=lambda sample: sample.score, reverse=True)
+
+    def cross_over_step(self, number):
+        """
+        self.pop[number] is replaced by a new sample obtained by a cross over
+        between 2 random samples
+        """
+        parent1 = random.randint(0, self.pop_card - 1)
+
+        parent2 = random.randint(0, self.pop_card - 1)
+        self.pop[number] = Sample(
+            creation="cross over", parent1=self.pop[parent1], parent2=self.pop[parent2],
+        )
+        temp = self.game.play(self.pop[number])
+        self.pop[number].score = temp[0]
+        self.pop[number].end_position = temp[1]
+
+    def cross_over(self):
+        """
+        the elite won't be changed at all
+        the worst pop will get replaced by offspring
+        we allow new offspring to reproduce at the moment they are created
+            in order not to complexify too much the implementation
+        to do this, we have to calculate the score at the same time because
+        it is useful to do the cross over
+
+        """
+        for k in range(self.pop_card - self.death_card, self.pop_card):
+            self.cross_over_step(k)
+
+    def mutation_step(self, number):
+        """
+        mutation of self.pop[number]
+        """
+        if random.random() < self.mutation_rate:
+            self.pop[number].mutate()
+            temp = self.game.play(self.pop[number])
+            self.pop[number].score = temp[0]
+            self.pop[number].end_position = temp[1]
+
+    def mutation(self):
+        """we will mutate all the population that is not the elite"""
+        for k in range(self.elite_card, self.pop_card):
+            self.mutation_step(k)
+
+    def do_gen(self):
+        self.cross_over()
+        self.mutation()
+        self.pop.sort(key=lambda sample: sample.score, reverse=True)
+
diff --git a/graph.py b/graph.py
new file mode 100644
index 0000000..fecfbc3
--- /dev/null
+++ b/graph.py
@@ -0,0 +1,59 @@
+import pygame
+from gen_algo import *
+
+
+########### What to show each generation
+
+### four ways to show :
+# 1) only the final position of all the elite
+# 2) the path taken by the best one with arrows to make it clear
+# 3) animation of the path taken by the best one
+# 4) leaderboard with 10 elites on another window
+
+# I implemented #2
+
+
+def draw_maze(window, maze, square_size, maze_color):
+
+    for i in range(maze.shape[0]):
+        for j in range(maze.shape[1]):
+            if maze[i, j] == 1:
+                pygame.draw.rect(
+                    window,
+                    maze_color,
+                    (j * square_size, i * square_size, square_size, square_size),
+                )
+    pygame.display.update()
+
+
+def clear_maze(window, maze, square_size, back_ground_color):
+    for i in range(maze.shape[0]):
+        for j in range(maze.shape[1]):
+            if maze[i, j] != 1:
+                pygame.draw.rect(
+                    window,
+                    back_ground_color,
+                    (j * square_size, i * square_size, square_size, square_size),
+                )
+    pygame.display.update()
+
+
+def show_path(window, game, sample, square_size, show_path_color):
+    memo = game.play(sample, record=True)[2]
+    for pos in memo:
+        pygame.draw.rect(
+            window,
+            show_path_color,
+            (pos[1] * square_size, pos[0] * square_size, square_size, square_size),
+        )
+    pygame.display.update()
+
+
+def show_gen(gen, algo, game, time=None):
+    print("--------------------------------------------")
+    print("benchmark of generation", gen)
+    if time is not None:
+        print("time (s) :", time)
+    print("best score :", algo.pop[0].score)
+    print("Manhattan distance :", manh_dist(algo.pop[0].end_position, game.exit))
+    print("--------------------------------------------")
diff --git a/main.py b/main.py
new file mode 100644
index 0000000..5aef399
--- /dev/null
+++ b/main.py
@@ -0,0 +1,80 @@
+import pygame
+from pygame.locals import *
+from gen_algo import *
+from graph import *
+import time
+
+###### Parameters
+
+#### Graphism
+window_length = 640
+window_width = 480
+maze_color = (255, 0, 0)
+backgroud_color = (255, 255, 255)
+entrance_color = (0, 255, 0)
+exit_color = (0, 0, 255)
+
+show_path_color = (160, 225, 55)
+
+#### Back
+shape = (10, 10)
+number_of_generations = 1000
+pop_card = 3000
+elite = 0.01
+mortality = 0.4
+mutation_rate = 0.4
+max_moves = 100
+
+
+################### Inititialisation of pygame
+pygame.init()
+
+
+# Init game
+t0 = time.time()
+
+game = Game(shape)
+algo = GA(
+    game,
+    genome_length=max_moves,
+    pop_card=pop_card,
+    elite=elite,
+    mortality=mortality,
+    mutation_rate=mutation_rate,
+)
+square_size = min(
+    window_length // game.maze.shape[1], window_width // game.maze.shape[0]
+)
+
+window = pygame.display.set_mode(
+    (square_size * game.maze.shape[1], square_size * game.maze.shape[0])
+)
+
+window.fill(backgroud_color)
+
+
+draw_maze(window, game.maze, square_size, maze_color)
+game.print_maze()
+
+
+print("Initialisation took ", time.time() - t0)
+t0 = time.time()
+
+
+show_gen(0, algo, game)
+show_path(window, game, algo.pop[0], square_size, show_path_color)
+
+for gen in range(1, number_of_generations):
+    print("starting generation", gen)
+    algo.do_gen()
+    clear_maze(window, game.maze, square_size, backgroud_color)
+    show_path(window, game, algo.pop[0], square_size, show_path_color)
+    show_gen(gen, algo, game, time=time.time() - t0)
+    t0 = time.time()
+
+
+flag = 1
+while flag:
+    for event in pygame.event.pump():
+        if event.type == QUIT:
+            flag = 0
diff --git a/main_seq.py b/main_seq.py
new file mode 100644
index 0000000..ad07dc4
--- /dev/null
+++ b/main_seq.py
@@ -0,0 +1,106 @@
+import pygame
+from pygame.locals import *
+from gen_algo import *
+from graph import *
+import time
+
+###### Parameters
+
+#### Graphism
+window_length = 640
+window_width = 480
+maze_color = (255, 0, 0)
+backgroud_color = (255, 255, 255)
+entrance_color = (0, 255, 0)
+exit_color = (0, 0, 255)
+
+show_path_color = (160, 225, 55)
+
+#### Back
+shape = (10, 20)
+number_of_generations = 1000
+pop_card = 3000
+elite = 0.01
+mortality = 0.4
+mutation_rate = 0.4
+max_moves = 300
+
+
+################### Inititialisation of pygame
+pygame.init()
+
+
+# Init game
+t0 = time.time()
+
+game = Game(shape)
+algo = GA(
+    game,
+    genome_length=max_moves,
+    pop_card=pop_card,
+    elite=elite,
+    mortality=mortality,
+    mutation_rate=mutation_rate,
+)
+square_size = min(
+    window_length // game.maze.shape[1], window_width // game.maze.shape[0]
+)
+
+window = pygame.display.set_mode(
+    (square_size * game.maze.shape[1], square_size * game.maze.shape[0])
+)
+
+window.fill(backgroud_color)
+
+
+draw_maze(window, game.maze, square_size, maze_color)
+game.print_maze()
+
+print("Initialisation took ", time.time() - t0)
+t0 = time.time()
+show_gen(0, algo, game)
+show_path(window, game, algo.pop[0], square_size, show_path_color)
+
+flag = 1
+state = (
+    "cross_over"  # this variable is either equal to "cross_over", "mutation" or "sort"
+)
+count_co = algo.pop_card - algo.death_card
+count_mutate = algo.elite_card
+gen = 1
+
+
+while flag:
+    for event in pygame.event.get():
+        if event.type == QUIT:
+            flag = 0
+
+    if state == "cross_over":
+        if count_co == 0:
+            print("starting generation", gen)
+        if count_co == algo.pop_card:
+            print("Cross over done")
+            count_co = algo.pop_card - algo.death_card
+            state = "mutation"
+        else:
+            algo.cross_over_step(count_co)
+            count_co += 1
+
+    elif state == "mutation":
+        if count_mutate == algo.pop_card:
+            print("Mutation done")
+            count_mutate = algo.elite_card
+            state = "sort"
+        else:
+            algo.mutation_step(count_mutate)
+            count_mutate += 1
+
+    elif state == "sort":
+        state = "cross_over"
+        algo.pop.sort(key=lambda sample: sample.score, reverse=True)
+        gen += 1
+        clear_maze(window, game.maze, square_size, backgroud_color)
+        show_path(window, game, algo.pop[0], square_size, show_path_color)
+        show_gen(gen, algo, game, time=time.time() - t0)
+        t0 = time.time()
+
-- 
GitLab