From 3651768e3eb21b217d6806248adf7b0fdea10d44 Mon Sep 17 00:00:00 2001 From: Vadzim Valchkovich Date: Thu, 1 Jun 2023 17:45:01 +0200 Subject: [PATCH] preparation to neural network implementation --- agent.py | 13 +++---- src/Engine.py | 84 +++++++++++++++++++++++++++-------------- src/StateController.py | 34 +++++++++++++---- src/UserController.py | 7 ---- src/img/goal.png | Bin 0 -> 46654 bytes src/obj/Goal.py | 9 +++++ src/obj/Kitchen.py | 4 +- src/obj/Object.py | 3 ++ src/obj/Table.py | 79 +++++++++++++++++++++++++------------- src/obj/Waiter.py | 31 ++++++++------- 10 files changed, 172 insertions(+), 92 deletions(-) create mode 100644 src/img/goal.png create mode 100644 src/obj/Goal.py diff --git a/agent.py b/agent.py index 9553054..80c234d 100644 --- a/agent.py +++ b/agent.py @@ -11,17 +11,16 @@ SCREEN_SIZE = [800, 800] SQUARE_SIZE = 40 waiter = Waiter([0, 0], 0, SQUARE_SIZE, SCREEN_SIZE) -objects = [ - Kitchen([0, 0], 0, SQUARE_SIZE, SCREEN_SIZE) -] +kitchen = Kitchen([0, 0], 0, SQUARE_SIZE, SCREEN_SIZE) +objects = [] for i in range(150): pos = [0, 0] - while any([o.compare_pos(pos) for o in objects]): - pos = [random.randint(1, SCREEN_SIZE[0]/SQUARE_SIZE), - random.randint(1, SCREEN_SIZE[0]/SQUARE_SIZE)] + while any([o.compare_pos(pos) for o in objects]) or pos == [0, 0]: + pos = [random.randint(1, SCREEN_SIZE[0]/SQUARE_SIZE - 1), + random.randint(1, SCREEN_SIZE[0]/SQUARE_SIZE - 1)] if (random.randint(0, 1)): objects.append(Block(pos, 0, SQUARE_SIZE, SCREEN_SIZE)) @@ -30,7 +29,7 @@ for i in range(150): user = UserController(waiter) state = StateController(waiter) -engine = Engine(SCREEN_SIZE, SQUARE_SIZE, user, state) +engine = Engine(SCREEN_SIZE, SQUARE_SIZE, kitchen, user, state) for o in objects: engine.subscribe(o) diff --git a/src/Engine.py b/src/Engine.py index a75a378..f648651 100644 --- a/src/Engine.py +++ b/src/Engine.py @@ -1,7 +1,9 @@ import time import pygame +from .obj.Goal import Goal from .obj.Object import Object from .obj.Waiter import Waiter +from .obj.Kitchen import Kitchen from .UserController import UserController from .StateController import StateController from .decisionTree.TreeConcept import TreeEngine @@ -10,12 +12,13 @@ from queue import PriorityQueue class Engine: - def __init__(self, screen_size, square_size, user: UserController, state: StateController): + def __init__(self, screen_size, square_size, kitchen: Kitchen, user: UserController, state: StateController): pygame.display.set_caption('Waiter Agent') self.action_clock = 0 self.tree = TreeEngine() + self.kitchen: Kitchen = kitchen self.user: Waiter = user self.state: StateController = state self.screen_size: list[int] = screen_size @@ -79,8 +82,13 @@ class Engine: # waiter interaction for o in self.objects: + if self.user.obj.chechNeighbor(o): + o.updateState(self.action_clock) if o.compare_pos(self.user.obj.position): - o.action(self.user.obj) + o.action(self.user.obj, self.action_clock) + + if self.kitchen.compare_pos(self.user.obj.position): + self.kitchen.action(self.user.obj, self.action_clock) time.sleep(0.5) @@ -97,6 +105,7 @@ class Engine: for o in self.objects: o.blit(self.screen) + self.kitchen.blit(self.screen) self.user.obj.blit(self.screen) for f in self.state.fringe.queue: @@ -105,10 +114,13 @@ class Engine: for s in self.state.path: s.blit(self.screen) + if self.goals: + self.goals[-1].blit(self.screen) + pygame.display.flip() def appendGoalPosition(self, position): - self.goals.append(position) + self.goals.append(Goal(position, self.square_size, self.screen_size)) def predict(self): @@ -117,6 +129,7 @@ class Engine: for o in self.objects: condition = o.agent_role in [ + "table", "order", "wait", "done" @@ -125,33 +138,48 @@ class Engine: if not condition or o.compare_pos(self.user.obj.position): continue - battery = self.user.obj.battary_status()[0] - distance = o.distance_to( - self.user.obj.position) // (self.num_squares // 2) - mood = o.get_mood(self.action_clock)[0] if condition else 0 - memory = self.user.obj.memory_size - dishes_held = self.user.obj.basket_size - customers = o.customers if condition else 0 - waiting_for_order = o.is_order() if condition else 0 - waiting_for_dish = o.is_done() if condition else 0 + medium_dist = (self.screen_size[0] // self.square_size) // 2 - p = self.tree.make_predict( - battery, - distance, - mood, - memory, - dishes_held, - customers, - waiting_for_order, - waiting_for_dish - ) + dataset = [ + # battery + self.user.obj.battery_status(), + # high | low | + # distance between kitchen and object + 0 if o.distance_to(self.kitchen.position) > medium_dist else 1, + # far | close | + + # mood  + o.get_mood(self.action_clock), + # undefined | good | bad | + + # basket is empty + 1 if self.user.obj.basket_is_empty() else 0, + # yes | no | + + # dish is ready + 1 if o.dish_is_ready(self.action_clock) else 0, + # yes | no | + + # dish in basket + 1 if self.user.obj.dish_in_basket(o) else 0, + # yes | no | + + # status + o.get_state_number(), + # empty | new order | waiting for dish | have a dish | + + # is actual + 1 if o.isActual() else 0, + # yes | no | + ] + + p = self.tree.make_predict(dataset) goal_queue.put((p, o.position)) - if len(goal_queue.queue): - priority, goal = goal_queue.queue.pop() - if priority: - print(goal, priority, end='\r') - self.appendGoalPosition(goal) + if goal_queue.queue: + priority, goal = goal_queue.queue[0] + if priority == 2: + self.appendGoalPosition(self.kitchen.position) else: - print(goal, priority, end='\r') + self.appendGoalPosition(goal) diff --git a/src/StateController.py b/src/StateController.py index 285cdd4..e82f23e 100644 --- a/src/StateController.py +++ b/src/StateController.py @@ -15,9 +15,10 @@ class StateController: self.explored.clear() self.fringe = PriorityQueue() - def build_path(self, goal_state): + def build_path(self, goal_state, engine): total_cost = goal_state.cost self.path.append(goal_state) + engine.goals.pop() while self.path[-1].parent.agent_role not in ["blank", "waiter"]: self.path.append(self.path[-1].parent) total_cost += self.path[-1].cost @@ -28,7 +29,7 @@ class StateController: def graphsearch(self, engine): # A* print("Search path") - self.goal = list(engine.goals.pop()) + self.goal = list(engine.goals[-1].position) self.reset() @@ -36,13 +37,10 @@ class StateController: self.fringe.put(start) - while self.fringe and not self.path: + while self.fringe.queue and not self.path: self.explored.append(self.fringe.get()) - - if self.explored[-1].position == self.goal: - goal_state = self.explored[-1] - self.reset() - return self.build_path(goal_state) + if self.goal_test(engine): + return self.succ(self.explored[-1].front(), engine) self.succ(self.explored[-1].left(), engine) @@ -51,6 +49,10 @@ class StateController: engine.redraw() self.reset() + for o in engine.objects: + o.compare_pos(engine.goals[-1].position) + o.agent_role = "block" + engine.goals.pop() print("Not found") @@ -90,3 +92,19 @@ class StateController: if state.cost_so_far < fringe.cost_so_far: fringe.replace(state) + + def goal_test(self, engine) -> bool: + if self.explored[-1].position == self.goal: + self.__is_goal__(self.explored[-1], engine) + return True + + for fringe in self.fringe.queue: + if fringe.position == self.goal: + self.__is_goal__(fringe, engine) + return True + + return False + + def __is_goal__(self, goal_state, engine): + self.reset() + self.build_path(goal_state, engine) diff --git a/src/UserController.py b/src/UserController.py index d9d6e70..35768a3 100644 --- a/src/UserController.py +++ b/src/UserController.py @@ -9,10 +9,3 @@ class UserController: for event in pygame.event.get(): if event.type == pygame.QUIT: engine.quit() - elif event.type == pygame.MOUSEBUTTONDOWN: - pos = pygame.mouse.get_pos() - pos = [pos[0] // engine.square_size, - pos[1] // engine.square_size] - for o in engine.objects: - if o.compare_pos(pos): - o.set_order(engine.action_clock) diff --git a/src/img/goal.png b/src/img/goal.png new file mode 100644 index 0000000000000000000000000000000000000000..24dbf98a93f4d1db52ad866db307276565069c0b GIT binary patch literal 46654 zcmeFYc{tQ-{69R13Z+w)K}9o#?1Le)hLIv7TWXBPQe&$@HFhc$gFYeqmOV5ggt6sd z>?%u^sEj2xu#Z!S69$9rGd!zj z27~P?-uZtI2l&hEvC?kvkD!;nwU@bvlh+-4q9aVl!Q+~vsG*y^v!j`#y#wi1tD`y$ z#wK>-lC_t$v5^|y!%fzHXN~M#x0_%$45qGi_ohAG#nDUjnxpd#cMZfsbsa+VhJyye z3T-TJd{fu)`i-+bL`QR<3zzUdE_hW3gqEhL`du|}fSaS2z35#xS9edfyBdh!=c<99 zcm6Dg5dFQx%S8k6hbU`fQ&C+HqN6BUR#^s*QamN9qAH6*tDaI&k``5vM=8t6tIDC2 zWaLq5@~Ud`DA9lZLx9_e4o+%jdYFIi0>5b>u6udiRFjjtbLWoi9Yt9WqO%-IRaI3^ zUO`SlK?bal@g%u>+256M_eB1Cf}W!%o_OP?*9{MM(VY|RuX%WTX&?Zle^_w4`QNeb zp8tpm2u$v-{Y^QPto#m3|1LB({{LR;=JwxePcJja|4Z-x>%pFvNH-nj%p5&Eyoq?A zI49)JRX5diiH`PO9>hx?9px;s)+5tXpI;qKsZ$MfXxJ;ugr zhVGtT_U?E`Lp==yI7Rlx4F@%4J$aPw8GRj91$`wHN?%14t$;qIs;hJcC67X>C@KEC zSI-0Q?dIt2_3vJX|GW3h|G0Mt4sJKW&3cZ+8@C-DFhmbG(cf&V-T0qrIrTrD_s?F3 z|Ctum|8cJzAVzLSx&IGk|NAF^pq(H8gD&{xKkz%c1H=;nw5u@jr7&2@OG7=KOLvFn zKC!=Xv>4p|sI;==5e(8spUX=MXjo2gUV9r8bzom9_x4d3nV%Pw-aJ{YT<*fBtfzJr|7 zJ3pMusb>ZMq+kE@YuNw(`oGfnUoiYOjsLZU|DSkJesl))l@=1)@H^gq=PH1?q1NI(?n8x~mF9zP6xSHN(90gV|v* zvJ}56H~Q`9fGZ!n2aGy7^E7Q=cFvS;jI|b25uxP@+KI&p3RI`Jb(m?s?xNz8&tksR zMnfX+C~>As0iTK+89>bH_1o&XM<>Qw7@5d!^%F`@4dmwZwmJ5TAO#Epv`z|S=ggJV z{B_rn&iB2$;_=4&vq3Lqg;-#ila$Dz<0ZG&@m&~b+1&i4^bI}zD~DbVPr7q4Js4px zuee@4pc-h#(At(o7t{n*)gU^hC?%WGtX$JSuCc>#<&2dYnfKHX!nmQ<=+NC4PprH? zWpp0p`nAfOD_TTM$%7-QJG0j*@g>vx!;Skw>jYG@JI5mS@=L~9QBoVWKTWnO zpZR@XPul7n^~lYEUS7-9vz^hb!TN!pj0)fTSQ5$wQd1HUv(;`}u0}uX6lEx4A2EB# z8^%Xq5+<69Q|{1uR?h1o)spA0>I>;b<%gVa7`vYCm~!$j=&V3)D~WMTpSW>*!oo@T zn7kQ~r$mbwq%l0lZ_{@cM_2Y(d`*kUA0*x-(zALCd#l+0i-@p z|9+l%xWFH=>J}Ivg%7?a6F14%;c>2$WHa{2PX?Xc?}*WhsyWr^Mv4VU^JUwmRV;|dw}F;hSf2CcA?Y8+aPDMHMB0`aBSj3S?L?;~}(Rq_6%nv=ID$1oEMN?IETM9SJ=YGvUdWmLrT6U~UBBIWP_UXJGLO zeyM$*kn3eC`NHtMJ(rP;H_A-WUw+vBDWu)wD+G12p}-$8?_^u?Ra6-X51zK{K3y}< zqx-v@CabM^Kc zo|D2YG1>~(b0pUTXZWf(of`*d_I4Jw!;fO zr0e79;&GD<$vA70qa`*YwtLj}?e+N3TX9witX%QtKnkJPV`=6Htw+fsstB}+5vxQi zAzu|adph4nFp=lWXos~mc|)gTmg|>4eYHpvRX(q0O}{OS!SYp#qD?k@E;*3!EIzJN zG*tfJwO{$P)K-37GoJv-0&1HpkQkYjD@bS}Wabs*k?j=zxg9AC=Q2&0Ktw%C{un^< zwQ;zL7t8|3zp5XLbGu)kd=bZD8eN1~IM?~&eiY{RdCL>9#$tQ=51KwSj^ueP?I?ZE zlt4l^NJ`@_NEE}St2h3B*`ik~qKahn(%PC?xqF{N>j0cgU3wq1OwPXCecthpfDQox ztr5moo^O7owIEjI4+bUKNHN~`CbRsCrEOl>wmK1KXk4P_73`tP8VJ_$cw<*% zi$MUCsJKfc_3+y0?nlc~SL>wQ#d;utirr$^Alb(LI zf$kyO;d`~^YVQPsU|N#;G*S1nV9^!q5sSez5UV=k0Z3kss4!A`{ld0C_a5%wuU$v? zd^LKgQiAcE)`n3y*nMF+)@rc7^&(3>_sL=yJo^MP%BT}RRCjc+?=0-a&0(4gvM%cI zUp61kf=Eb-eEF-}84!U4>|yNldtG@pjOo#kTg0yPa|L?Xr-^(v9)_LwRO2g$+6#;T z1&;s)l`-K3;#oOw+IGM8a6JyYSA2$UlTVU27`^tU7BU(>&{p7@-FR2PuuGT*o!8H@=-<%*e2=r}ywmfP+IsaMv+_`6ItvKw@GP4n)vYc?U$>IHaxM zZkQ*INt=g~IDRZgjAw-H-3@1vw*ViA6~4n-rwNj*9rhwwaFP4UsA@_l zDg1wf$lL}_r?Yvdi7?#=nn$ohXJPQ*9STP*Z3Vl@Y@2VVZrpevG!DtU1WCXdjS zp`@n9ScPZSv*zdpv&$PXO;^g3@m#0&&pyp8+6FOJ9ru9^A^=KfO3%@!Y2Tnv z5o|&G2bF0?M{v99p9ABZd8J4HO!Gi=M@l;$hk0Gko9Ht)H^<6+IUz3v_zVZBR9>TU zKB?sMZe)exewdmvogwKVd*GdzzUa!!ypy?3*o;R31)=tcxC=`!R$SDJdZZi z7*vO9QP?=Dmof7siz@V>8l$Yhfzts3*etz3_k<`IW%>QEW(V3tU-eT4;ZosSR{pbK zGi710`%&_d!R}Kqc;=224BHAW0$_602(qU84Pyd$NwyA-<1id~M+&D;QZR1oz#@ua zoYFxhGQuP`dM;EApo7n$KIZ~nu_C>mRbn6#N;AO59DcvQ{ufZT577+k4Lslo$aK=x z+YKVg$wGtgPr)SWfx1?YPgo3qu;C5>Y5MFcqK>RSuzDTFxx$-f0NeFQMla(tri)F5Wh2naa1joaZHk_>KvHW;rSsp-_j!vWXp-1WPF zo_|RTkU+4fC+~)}J*Ph31*E+Vzypt}&Y9eMubT*QYukrj#%iEWQqJLpkNwaBoVm*g zl5FS?AsI4T(A%9H+rtbS>-3Pu(F}6{AJ5Yp2kS&s)k|IjQs5JOCrUWEe#x=(YpbLV zjl$;~cwxLd3O@9d@xqH!`g0x7LS~sI-37X%Z>h8&#;HVS%=jCUEYyH%w}EQOU7JW} zy?JM_j{x?KM>AfSs7D+y1)tO_K$!@;h>>O>uHGGSiBDLhf%G&hn+(HUn7B6-72&$G z&xoX-HV3!SV)$kTeJKF~nOB!(EM`$z(_P!7)@aIS;HQ8r=02%0UeOE?Qb1$u!UU4{ zO*8Criranpn}Dl9kjEFiJcz&U1Ep}Z30lJx?_8&M=BvIwGF>5z+>X4$&orUG zSZQJ3+?b*5b9 z1u}hajAHJ_T&$M|rjjE!W;$u$q4icG{cI&}ZPu^yIHQ7=4F#5IUAQ3o(p~abcMKQB z!pz*Mh~EZT;*+$oxbgRg;R|a&Z$s#4K5-?Wv?DPwR%NZjd-$y7O#uhr1{D}g+9ZYr z51r4`%9=F16c`^9{()LDQ^L7DGN8XT>hyy6EjGNc3^}Xku4TutOkN(^j`Z1ANnO)6 z!}d9~tgQEokXb_wfMfBFiaZ>h_+6v^I^E5uE_~xQC=Bgl5}(cxkclO;LkU-@kG93e zMy7}^_h=X>0(s|pV24dR+}D_-?fBl^6Jsf(0DI2v>*y&Jtm?l-uD4#iPP)>*rm$5zA;sI=ZH-OUZ z)7ehKOHCM-M&L25->mCvLWykA!TRmPeMHse#g$3b?O*LQK8$%bl{kCHEi!3omf0Fs z**kQ*w(y!miXefs{06v`Fpv?%LRPs>zN+#E0AuXF7wLNh-WF>Q7<4__IEB`?=t_WG zBAk9OTs|);M|&u%Ja$b>>?<6uxRv^OM3_igKDID;k8xjFE5c6AL(}6eVep*JP|d)r|VUr z>~YRGVR=wlKxkj@j=MG+w^)yL2U&b7j-Xi$@iL_GM5+7-|@x%r(^ln6?^ zdB`^>=i}wkyc|6e`(j(63g*Ij@DCT6V+Q!G>vMlGy&U#s;&3lr2Fmg`MO8mJg3VupHE(B|`Pvdc#5op&5UqamrOcciRS zF9H2ZVb(7*H~?gTy3aiPMg>UZi>8%+P=FczZ(4;}>3jAyF)el6*TdxlWn+yCP$KJ; z=J$OwPr7=92&CAtQmteVkvjAeV$c8FfIWSzFnb5XHUWIPF7QE8`KnSZK*X@r%h}Q9iQOy#8)d*TRq5-ln!X+EZewzOK!Pd9-l%#3D) z%^StoP>egxN#bpVi)4=G^TF)ghMI+&#3tAfDHtqvcF* zfc%jKtMqPpAlJ;%YQbo?VX7FTUCARULxHOq^hvlUd_SE=nkaMnOt&Uji-tvxx>d(@}g#$`EGaz5g{zr3hiEa;16uTX0Q@rSO;)=_8k!w;dk;RC0z=D($E+xb9bnqp`bwW&s8N53NPlL zq&!7C`bNyq+nM3OL9$3ELV!9o+_`;I0h z-t)=O({l3&)k;Zh%nl}A1vSewk#WdX1SHW7m)Ci_AK zLCnePG&d^^(I@HOkbO*5B~H=yLlt5Zd`rdwkKVQ*eD(XoskmJ#nIMBZnh;~vD^<5z zCrR8X*5eGy<@w`}vh$lp`#qWTy|Q8U#7}$4%B=L-_zEV!pn$ zxL~v8u^+hX_*D;%uNQV$kQ!P*)?bq-4ich&?-2>3{w*P)hlPG_^u}27oa?lo8xZ8W zG}{8@=1fux*^pyN4cnm%&QS3<8;ViqLtE80X={)v^xR>pKc!&yD{wZ?4fQ71l)L!; zBstJ2&8+vHrU@lhG<=dm^ZeC19Gc7EZQQZ`vu#rGPbf#Q36rrl%&9vH{PDmQE!hMR zhVLMKh1v057YUZz6N@()=R(ZMyY@`wI1T@(f?Bd3P}+omJN%hN4PkEub$vu^1$?q7 zl+^wuCE`H_FjW7ti}c;lcX9~o4T@3LtNxR*9~#J+d~!R8c{MsQH*(U##x{4duuy6^ zFqg{8FD|l@%HzLnVdjJaaXdYWL7Z7IP*&ztyKMuKQ88eUPU|xf&8$WjYgsy-9g7aB z9^Y3=^|@j>4yk7L-d{WqFC#XVSR^?!j z;6-f1H2ools>Eo|>Ue$)&?k5FpsFU?T+8LJbW$kEYF(k2HO}OH; zDMr-uHD;^Ht!G{P{F&yAY!_lj#_(7KoF{Zyo|ZtLXRySlT*3M)055Zx!O7UjqJJ=7 zbm$$>D@XIMft)x-Hce%TMWB}4GiJV+H?s`-PFtY7@_gGSCDD=t3FJqvK%I9W z%Q7jDE{(^HP#a(;Xazb^kJaBv#Iw~!Q59_5*n9XU_AOZ9f^M~gM8Vus z`LB-VF|$S9lsg75QTu}~NKn$TZBk)hBRJr>SFjTn3Q@|OPG15kr=U)Fw{|X(hk6u$b}!Dgl=Z0G*Yuz3IPboR|jIzX9+P98^9C=5=7R5EY&498vkt5s}~sSNgr7y7Fd|BU*q6ZtXUh+;hA(c>G$QPa1CEmP825F8 zG)bWP&?$9>IYntE$K# zNeK$>AV9$}4}p$hjtb>4omLaU5zykBhvn%8uzoRahSh)w!342*FZ1y|9(1S<$%8%* zaRI1Hg7KUycaLqGlr@Z<|A~e?LlO@s7nm=`H00{-xEx7P&2Iu6JpjpKl7VKb`mLK1 zTL&tg3FYCerl7uFiehl`gFZoH`8lAV*obQfY6S*08H$;(yitAV1DVA~m&6{PQ6FL- zxEZKWl5mmxZ8g$YPZq@))BxH(Yd{6&)L{0|Z;xo35g9%MnAF3h%XGDI7@U`#KgI-n zng#X_)%llZxd%R;<0qwEgmJ%LK;#Ou@t!BKe@L$n`7(%L=-~54<&dZKE}%JsbV@n7 z+5pKn%y13Gg&(oBmkzuZr)AM5LL2wM=27PeSXd@CM9>s`OjRk#MqT=z2PA3*XNP?m zp(QlKnoO|`pudSbl`2GvL0y_%qaA~$Fz%pDAodCzolXt00ej^~WElMTy#8!x9TSP- zgz?v_TFVE3{Nn^Wf3L@KuOFj|eDTt^x3n%OI)XLm!6_+XIja66P}Y8l;&k`(dYb%$ z^%5_Z!h2(^roi3d_I&*0hYZe1ni&RW;d_g|)ohx52+0$E#hq8)`B`M)kW<-8i-prmy3O;n(aUH{gvom6} zhV{#hjc>%3K6}@38Jps}svLbaAu+diLdL1iHBemxZ7Y!9-i@5fb;?G_uL4n>;grs6 zSB6vs0YMQHy5^qOPxp$&SyX0K+0te6PZkkv$_n3k5t>#OKD$Q;$WUl)`Yqi<$^6V4 zjL(mR=x+DBRXE~3+oa$q01Iz6Vxe%|3%d9d4N-<}X8J)=_1FdI$gqg#jk4rdyeHFY z%5EjGfr+ba!eW1;NZD9iK+jg`_lKKLtrkJo!5z31(D%-383sD%_NJM@hou-&nd!Mi zmZuf0qf%%S{ZA**CM;QX?y-|8IxD|c2DTDFjqMA8D&X?0Pp0IM38K!V^WOfTL36B* z0YJe+1EZ>h^ljNka|wy!4|US$-vhx6t+=hJFA-x_(}(17jh=Ap9*goj%_!X6Bt4m5sNeY;;OU{POMUg1`hd+%+Y!D5O}v zAZJpCm&a|JIRd6Vc2RyR#E8El^fepjq9E@GRwvLgzyk*m;HyJF+HCs3F3V~7vlcnz zEj?tT7N}-k#HR6V{DaOR_0!-HL>BE97+%G}O`I7G>voUS8zpc#Et~|nyoa~{F{L=>V0S92f z0lAJykvrx#4>nG%QL84#Fb(mazyRLj0(Oujh0M@pF5m7}Ho#mkF>YWC_PU;Sn4f4| zSv=%1puib_aDtEb<}27zw~P7WYq_<+#V1zSlN5N|aE=o@Mw4f16{^s?eal~)g&{eP zMAR9AO!h4;v{@mS@uEUbh|TCRgOl?!E8m)P1-(@Ved+{SiXy85>Wo?1IAZK<=Lgv_ z{=t)~^iIvsk!ubX|we0!c-L>>pe3 zwD)nPQ=llll}PCuN=rrooRh=Iz(_wdQJYZSr>Mv@e3I(N#g$79c`JK8c#9_ReO6eP zvU!SF*Q@T&g>f72b#1JYU@X%bk?q1eY2IH}7!=jn5uMo=jOvds}>F;#>hcYY+^izeHhIBHm6j31D9s{CM z1#t1KCyUcX`X5n;;XX1VXQ<9Z-093x8+u-EvP*2mo>8em_l_T~U>yDzopNvE^{B5< z>otEVh6z4s8)T^&;9ef)lZjy=U5}K6M8=FANZZ?o^B6N7(TJ(6Nfw?0L9UI}D}@FI zwE|2F`VA=^tq%5M<A@*qHYjL|a585P9*VgvdD*xpv4V0XAu9<)6DpY~Z6 z%ioElDl%GnlTVNgkd^GidqB8rGRNr*S{j${Um095FJ;Ue%)~{y3WA8t0}bf)qYuWH z#3CPJx-N9;?qfu&^3_FnU}Vu897ZH|4S@BMBS&mn;65kgEJ+p}P2fhF%8)oF;0Vc# zaa<=p;=~XopHg9GZgQcsktcL+h;|Dr#2v%n^Z|vW<5=-g2kx2}9zwT?uzI&d{DTwb zrT)K`RxrLM$Y9k9eR@2Y@5X^{;=Jh~hIRlrn;j34L+$+H-`sQFN`J-B_!~?SvI?QI z<$AKLJ*FvRSdnoeF^m_zuA2ca4mB)YpYPKqu$OGiqnZNI7eAdci&)pM_2BK$ z4mdN!WnOylxX7s*2-2Rie59_3o^o(gDArm>LlmFA@&|x#$N+uxuu@w>*P9GG_b_!? zv~3Me%{?XYUUphiXpR3~P-NX}8@>~Gx>)Ourf|o@VeVjX9Vjs!ZIl^D@zdFjh!!EX z4^P4isPD&Bp}q*VFwi^OMA`{{OgkNC#oU|AvwAR{2yOx{IcA~>>Lh2}8BI=9G{;FY z4(;;1wH#=8i~WS44{2FXjztn6JQb)+bv{0E(PKJ}Ix_=mLEbUX#g)%-`u)oLCBhh- zGa%%glg$ZMptszW!*ka#zSDy9Rku0$z3=^^(Y` z(pleeHm*_wLaJm{(M4?tph{(e@6zAV7~Dobnc52XnfOZEL~bi{@?Q(@Rgc_C0*P%l zu$fO_C3w(4w$p_9?ZZz;v?F=W8b4-VtTv`!g%pPd_L_n&Pp>hFov-A%Y;)mvU*eu z`jt4Rcj{@tK3iseh`WYNNf~PC?IKcKxG}v*gk-dJ=VOo}Nb=XtlGwe?u?^aw0{TR{>R^GF7`*|3LvU`qq(Fx|nL*e;s*uN7 z;&fEXqtr*?VXFf)U4)fzljwv@ZQVHlGEnFJro@?@-)lT?g*BRZRe)qozm^G4_C|=1 z=iGRA((k+^1}CV4)Sg%^&0XVe^1VP`Z67!^Fw zK^Fmv6V`qAk~}9+`Bs8sF*UAEtS%bF^gDludBZMMjPt@iOFS4()KDcpLvg?TjueTS zBk=b4{#juy3RWm7U+7%XXSj$VGbP>(O{y@cgUtT+Fu;TH~nVm3(L_d{~} z08dwa-3&r;>j5z4&`f=%GEyf3G8d<`E$PB9d*DZ{vn&%1#A`Ky+85jqjU_5$S8 zRXY<}0$q1&&3=W~THd#tT?#OJjUphF|mI^IAs6=34JVAN?-7N!Ie$Nw7RYgOJiPo7 zjh^Up`e$8?)uOsYNlo0Y4)DmzN!G*B>}kwYGc6cuk9aP`6O|kfUWJtXv78F8`*G}l zmT9rLIpQU{l90J@T{J_o#YF!ZiZibbc-@TyoD+OyV5t6a6!P^U)ptk~B|jvz z9y;by)p}ZuNJq%B1_uZMZ@cTax4rz@%IEQ)g{enun&4qXe|kD-oWJvJ$3(i-bp=)Z z_9O*O!$=~J9ACU9JHL^g=Ic$HX47P<*_3m0kFS(|Ex&5LM7r8T8{CAkI+=@a-zB#U8X|&K%G4$F0?c-nw9XW~rwiX-5Z3i@6J}b{%YS#wcHEigx__gjbUb2b znGRG&&VUC-Q0w77F6*GmbP-~|WbhlPoi!U8p5}5g*6Lv4_gOlr*)*mR5htW8H<8O7 zpg0)Gh5qABGOGvfX2b~?eGg~1e*I8i8CXy90cH$p={wDlZG`P~_N9lgpqrinr$i=uaMwhqDPIST zK@duS6?sn?LKZc2+!tQ!;_EA!apN5{sJZ8~ZB`ETa^t@OeqmeTz8%QM1v(24s%;%q z@Dj}*kOP9c6xYM5szlrW1o!i8xUE%b?9%| zH{!cT)}0BQdADdP=9N8{<aqw=WM>FWgHTfbRzP9rvWz#o@1_Hi2K4ZM_BcbQb5fp|a z=l7%~W0g-1oa9dYg}YY_>r(s<{g4Wj!pB)@(qlQc6~exL7g9yVmVJ6`4<-p+TR-FOf_ zJMhaEg8NO{3N&d_+OzG-m$jpeowgT_%hX zmSKH~6bs(vUrS1C0|(a?_SMd7eml=!Sbwd9^Kx)3&->1)ROtl|$HK1jozLQD$)( z_J$KUG`X-oq*(_%Y6;l}52@Zy>rkD`;#P3&wiG4Qv3#O$W6jLzBxP+u5MuM+s|eoT zTz79S5)-8O9b;Yrsf;}&F2v?J{g&oDw9#>D`U!(@ce09_XqUgk&widLb@A9u-mCCR zFmKpc-52?@&Gx-?xa_b$!b5>$vc3t*G%r1IEtAFtrW&9_66)LwZWL=PG{CqYB3&nx zPh;QxOtU3fL^&dNubVkd^ICI%QiFC2HdE4xPM z60`jz0lpS+VEt!4^NFa|xA}y8Qb>B^M?ry!z8~ajYT}p$B$xFr&ZezE%?*rhf67~f zW27l!q^Bsd#nvT;1xVVW^vV^$&}0>|(o;6-9F$|`-&TQhf!AC@sqfngm=P~Jz`L03 zsrwBjjQhuYaRf_tc}?b-@oRXAey(9If^{;L@7J4h(No8WnULuLaB&P4+v)MVqs>MW zy}N!PEhi90Wf0)2a?Cw3T1*A!M*u1`zYr8QP9N*c-83XaypJk|$6-dFB=%V{pM0f= zPw+Wj#O^a01Gjg20i_V&4Az*Ibp_``u2sF+MN0Ze#-&|rw*O;7mSYCQlK!1OD zVO(OYPoUC4PViJEwKmRbF^Q0nO%-}3e*9sjqG|a7g-g5Fo3$(}->412!~E@N0xcDa zocy12^G~K}#U;Dw4>Ei=)}v!NbqdzJe~$sfV6KVLcJRv4<4awm@OLCF~T%WvHQAv+6pqvvFM|RZ9cRIXI#Xg z^mYq3Sljqpdeg@!5W@n13GeT z*VMOSLE5ui^LZQ;q}P4~92`829zI)#KfLq^z#Z&+POUm2(xymD;)g#q$eR3oz8NVp z5|Du6oyrEqqJ2-7VJH9lBS>+f-5TJEqZ56_5)}TqF6eti@G(G@lB|1+IHlI70L3d# zc}=QBaj(rX2B9KEi{NezApd_hd>2LH&H|;iK7)>s?OjKo0-`!!Kqh+6l)@S+k1<}- za`I0;qo^G8IZHyT1HoOy%8Vk-J&*Ephp>hpyA^y|)9SdhU95y1?@MRydyAd4awyke z%l`0E%)e|TCi(?@t8K0~l_-Y4ciFtyEZ-mA6)32CasLlA>Xbm%KJw{dyh8qCLeFg3*S( zG~;%*C&*cZPdxZ~Ao<{+7^8~*Eu>fHU!pokBOzmQ@X*O(IO}eUsP5T_^gQ8u9z9E? zFv3+nRVCsAt+H81f2{hqx_4kQa?63U&m4EL+5)+%68ef(pbe%mO?S_Md?v5|8&qAc z!s5OL%A9`?l4#2*{UW#i`R#S^<&Dr+r-t@~y$UGtF1BQfEp+A&s@*KVW%?H|cnM0V zJ_)_5Y}`2*XDY+aU$$uY_uXU>W*CZh+#doJj`tStpcI2jD+A>GhuP`7LdPrsF{R#! zYG6%N4PqupZ*JwERLyG_K-wDA-r&82`Ipy-eio_sJ?Lqnn0A+Nc3=f zUW+&6tant1`S6yh;D5yUFh4#@i3q-2S()p!F4$lVUI8h9BnyJj5g9^Y=v9MUkmr>A z52#g(dywgtoYI7{+w}S7o=U3Q54UF1`&9oGQG8zWsu`G1nMZ_h0&j0XLQA(5xNCIy zYnCzI=2-IlTss;*Zsr+udc4et4hO4kk<|uU*LW}egXl_6@bpzC@JU@b$`q;ctG8{X z;b-yM3vRhbBk$lo3tEtYe(I6R(NlRlaftCy3K)T^6fpz!Mj)gzz5Y3d*&$*gGB>IU zc?dRsbUQ$TP)nl==AL_IbIBp+072q$RwStefFH)sgA2-`T^dR@%a{j1Qqwx1zRU1j zK!qR<^Vy8oSAOcR`BH__GdTkRdbK(@Sglg*6^W1p}|JDTAbWDS%8hpYO41N7e@ag z-K3xUcR7k#rd`2neaRqL-|gtfQQ8L?XJPV~A~H!BB|(SgQ>*I#CA~vWvtv^YKW(H*xF&~B z=KzSdU&mE|CUP&l+36eY3)oTldPoC3ygS44w@y*!fl+>aaJ32A-WLFF92Jk}aNH?t zKH$-lyt2t7S6A+uC+7<^K(4b!5s%%88`sJU=?ndvW{Wy(OIsmqYx6AF_7u;%>%XK$ zKpa>)6EK96rML=bt+*KejvDb?n%v(lx9Yduo3-}|9O=39?C;xBa_gF-@aD+`s4ODm z?snK*!r_k>HU}W^{g;mt9J-{YE3GXT54&*BC*Z0Cj`ld8FIRc&HC5W$kTg!5rELr9 z{*;XxQ6Yw2G-aZ*e*4ajvM52FT!6M)KUw^rb=O{Qf~zeXBH>BYHG(%@V#wk<`&Sbn zRf@dBLIz?(}3$bghL*v=UDSYvHF+U zI-JB$cO*b|AZ=9FC<#UJ&c30QMg+G}1b);1{Jf^<_pZy}y=$9}NnixNNBcO+5;&dz5i1t@m}&nTO>W?Ci{M-5Ip3LmSfl?bjS2KiP*qC;NY^<(@U zZ?>rk`kugWzs~9bopF~@hg2BtG#@N0{+1!Vy17R%95%iDVDHj9j-66Xkrdnd0C>q0 z=AQDWCcs;K{KwQc6rJwdI>4rYFHy?-`n7&F2=YKRx=MyMNc7=C7bX zP0l!LLQb~lzU1eh2QsvbIIH&8+u#k?-U5$_0wOC_4>PkO*>hSRC#H-Z(z9Jjv^oa)iV&zJDqtoB3+T$dJh!SQ}apcCvx=0 z&X;?et$JtthDn9j^^zLG6+hfi%BP5+T5O*7nCr7dgZi)6o;!7Yk?2lC?A8N4V#yk@ z-)bZ@Fwm2Ds(SO+^N?^v#-Y=CmI@qecVDskd&(h!M=9{N6`P{QPFB$xpXv_hulkt& z4iEZ^$KKziuYNQKTh3SJKBovs_o$Tf+@4a|v3<{O<&dxRqy!Nmwpc9JZ)la>W!}rG zpUjWnO;o&*Y9X9|4Hszum5)Nj5onjCzN!txs}iK!d%v7J;`wL3b5qoRLLe?pgp zpn5981*Q)bP^vr)>Eg{j`F=+mK7eck#P)g`m}+uTwtwdhiM1yXCadoSxeAJ_UU@k< zk?-^!?Y3F7=TO(i0jld}0|Pvyq3Q1LSF?)^dY)`zthWUtk$ z*znU&0d~wI(&2SvDh;0ZqsrvBum){TFs$V=yHfa>r6{krUtvCXjGWop7~Rx`gtBXFOz`(&gsuccn1h z=|ueWrCM$5TX@)y4Vq1}w&Q>=ur`v38MZUFDB3fm`O978v|hl?o~z_w&8t1yc5@D3 z-;2Mz2$eJ)5c|y$3kn>P)%_rboCmLm*mOX*t3|SKJ|(8`{(Kt%kLJ~0Z99qLs?7SC zeio2r+t0m>RKF3V;@|6^0@6T$&+Bs{Hht{{=2lrw2Cz)$IIfzi;$TghZ)t^M=!Ilq z)iL0rb|EF+77Pz)^%5igY?`LKSw^iKoE_PV>Dg=R@U5?D-%^8e{-9ISbPAxbjYsUq zuK1trCP(}-QKs*fQ+~rbb5&2Sw9CJ7kJ>AD2R3}VoWQA`-2iYcA9hzyaCmOGU-vND zfpuj$U?-gMLU>rLnx$%GmL@{ZIAm%6E-C!*^t~ey>4%scTf@yg-&Q}f0e(h>R6F5$ zblOK9EAWqediVO2=L`IA+&aYxR>94BVc9}*|M~K zhgN%ISIWnRJ=5(?`(xZAXkyJhd#CKYXGM|l>PEyiibL{E>Tik&egj$SK1}Z!I{jPy z)i>vX`%C8$P&ZWr_#EFTm>*GNh~nYv`ak0U%3e)t0EAy-~FJ}m=zd9nGh#m{ub^hkQ2Z$4IbfGiBUr(AA%3KI$eSpZ;57Yfw{$}@` zJDfTp=Y#g>=$;8_@|>C*^1H9Z7<#|C9r2U=`pDRu3jb58&I^?TSgA(_1xY^lUmfDp z^M)qT1Z}pme(CgY`QNe=tUeS~JudU&S9wyNG;8xhYuj!)MGdnU;S@8;)#;oV)s#}` z=v!Q4ySd(c?XHCuH|2egWogZ%h~M#huh>HOXL2l9=D&;zazNFe*>9O1B6>781ULEK zReRq%a_`~u`{MTxd)^`$FG#kB314ASok|)NM-Sd{*l16TQ<5Xprgm*>G#0Gc-;I5i z@_@zitjVL(CzE`w>MWnLW<|=e&~Ijr-4hDAOXduDC0QWIXUv=2%^w~C zoiBP1bpG`ftE*k}(Iwa*{B$ef$z;e32Wh2a>yESpH)ZDj7mK0WpOHemwpVX1u!AXB zE1umF4hZQ|{+c?1piU{>G1YbZC^qZg^xdM7U6t7QLo&*xS6^x2=dFEjkCqQLz+D?Y z3)ID(6_P4`(%8hZF`jREYS>gqsl`<1r|T(d?WEc*%1}yI(mtK zrhejy{9S_hyRjm+MI{GBKH=`->$OflxVe|0{o)ODrtPl3JlQ!<6pj@kkK1=<_L|0A zNIZlVxgPn!EBBy7r1*e%`yZuFgP5O8^3%^-8H&BU)#6hJ3Nu>OXL2=H%PYDxb7Ctba4+dPDMud-^07Vb3*B$wyy3EdACUvQ8~q1}w}eL*--c3DOUHsd z|NcKreR({TZPfpa-7qMPWrR_R22l)Ivs1!g?6Q_+ERlp{&k)H{siw$o#=c}L`%zOHk==bZ0Z?g?IDRls$OFlT97H+xc;^9I+B zQfr|+1~u{mjnV`9W??J^6TrcC$PSfESamIVA&7F|P?D|5SVo{aRHGFs+)MDa7$i(TMk{sfaM+wNn%kT(tJNruljinb6TYgeh*f}aM0BkOE*YYVyt7C2q zN-;Mk`?P0&0YO??V&#lVr_|#IbC%6MJq8*H!3ufg_7V}IG8syCiHbW`b!aJ2+6HrD zM6)e&QRMK3)3OQ!o_sm5V_?bOwLvuJo@#fj&0z-c9|WNP*Gz~Ik1(fZ6ZkYuu8tMM z6vU1YU)40eNb$i%#{ef$wAt7SBd}IYJ<1{M#ONjRh?B1(^(?+p+P17X#QSsDhuM(niCkN zbtEDLQ{oBVdA)8??A#sP@5GhU;n**6E z%!bP+cn#UX?H!ANj7953s5~DxajB2xNo)d5mxN6S; zo`LZb4KD>7hvC=o&)kn;Lay`gNMUt&VigW^2>+;f?RvTA(Q$`BCXI}{G+knL3>yBZ z1vg{{I=iL?wlBgR$jEZK>NKXoa%+qeD~>uI>eIkk&SZ>!1b_qzy&;CyL<6~>;s9u3 zB)7eNLPk9kBB9>oDZDUaguPpXQ5pyA?ju2|2`DiQn!fAGY~Bv(1AgjYeb-q))O*)L z2e)XAlnLK5wT$Gn{-eedanm+gL4}hc{mi%dx~cgk7&G8I@2^jtWzt zGSYYb(c<)|OQwc!(I{|E^j1cI`* zGjm3yD9}Y|hxXA19zFCzA;T&G8*QD5*}%pIbC3Pjm#@pjl$bNZEWXb@3B8x1$zude zd&Wy6kEH@Ak{!S+v60mFHU~G0D5>ZONrc6`Fb+JA$(9Qeb$o8{M}X;Ed}XjRTP(S) zB$Ny}1LTgYWS}cFV*MTlSjwERbkjm|wOrH9=}&h%-ixt`-2C$%(kE;6-{vszpz#pr zzi<1q{ba!s&sO|s0gIhT zX>%Q{V$-57T#~7?!ao_Cz)fzGsteOP3T}V^XmMo>KIAK4SOs#IRz1I`iPsbp za5SELgyUhNJ1=ynEI=0nz(EX@@8Y7ecb;tmvIzu)(TI6PQI?zU*YLZJ!w+&@@I_a!cjb%2ht#%Ozjtqd zXKe#9-&10#YF;VlOT|pLm*5Xi_3!*)80qEQH2zqVNB7o6$$7K>sd1&6c z%a+EFkx%$z7Jm_3B*nHvl#$UoHra$8G^U0EyP35Fzg&QZJjy z5WsLoLQY~B$?b2maI>E!aI=>DXiXo01BR{EU#s%;wHc9&BotfNn>QE?%7U|kxB?(t zE&;Xx-~=L90n&s5mHFuO%~5AZ7;v*s%23ywlH`0gwr*3SIL|fVv-km~!2ZHxun54k zZ_&4R6#Lyrum-j+^p^JO1+c8mcK_Rj40N)#0n8y~Wa0q)2l&Q+PYysP3l8KR5l~du zF3Mu#GR|%*WzKlMbFKKg|4>P!u)|fFDi*-k?e2A5lP1>WUby0SeP6 zSRe>u5yh05ZgKzT(VraXwPIF8$TkZgTvBLFeF4W>H4(lY0NsisxY?1HxTtY(Rc0XQ z-E>%!>!H-41^@@&(Ca!tRI#%lk%F=se!4c%NC@+!Y-C^8WY%xSlr;B4o^@Mg)TjNa5&|nwidZqNDXGyh7iMaoa}{z&77m z@m~cxfn(9nY(@9nhEsPt{JZPV5yhypmchW$Lo)!_c!1UAJ6rwtcp!*0u|Q=b03bHH zQ#>3IaB#Xe;ErG;7w&%#QdK+jx`^&I{)~VS?Fu{(&}tF}Ty7g&j_$i4eWGtY+#;e# zJCGA~TtNFT8uo^*Y_$HFL;(PCBccY-Xu7;ktjx*mgJ8)&)|>#4x^q-|W(+rLECEQY z03xK8w04=}7d>iNHSe}=yNZgnw&XIbe8z4)m27+JOlv7%xIy<4LW(KbEEO3*(fRe2 z%TYIc=2x6NfO!d_XQ$!cT%(v!I6OZw`r2X^C~YmdGW2ow!0i;kcF_Iiww=o|8D1U&r+Cm8oO1=+B^%d~Z z;4hIn%#IXo=7jrDKG!b@?bh+O%z+#I^dm436{%4lrHz@VtAHWUz3tj)#cf>FGXToE zy8qq+1j`TT#^NRBo>bqc&&kN0m2VT!;r9Qm6wP9jP=-N-bX!Q$j|%?qjv4fPEgRrZ zG2r&I6a2#h&+j&)iCLl0O^tqJ(|Rk|RbsiGTDUS%U2MUc^^ zH3ZJlXqO6WLK-tWRz(&%eE}XBybx&9)eR^X$l${LZrQBdU4WHTvIr1>HLeYUas!v1 zU*o6mcb7dQ8Q3eQGl;qL1z+(Z{~x;GPc|6WpiGRAkWNSvkV#0~U`0PJ>Pk=!L&Z4L zq^!y87SP66i=+Uf^x7$#I(A&!cO^Hvj>2tHOH9D~1peFY5jfZa>ntPq+2}R!mEPTm ze?<|J%-l%Kgf-32LARZhU+6O5jc8}(afYNyuz*Jz?|1z6XM^r6x!Tz@Q~p}_3plU; zVHVYlf@&7h{})K$-+<+?4&!5lthBa$e>xz$1EK#+lQ}%rU=V&P8;{5E!3!pFX(~@2;JBvUV<-!eH z%&(lp0Wfxsel<8wKa(pP3H4F6t_|(*9C!)Qb*8b?F14Joi|DAtI zw|ku#m?;>Nj%RgH{raGuduKn_88{0ei@N|3f4ViKr*zLTK{|v?=x=G0b&UQt@ia{7 z-;2mbwit`Nm(tbApaqIxK`qQAvXjf(jTaSO97_p@@)?WV+3c`{3G|g?j0DOfF)m za4%put1bTyk|YL^lK~D4V32<7`1+~0IsYDe53^uJ@!1%^PVBh#nbM|)$u!j4+&zf1}5|F8a_R1FklLZJF<5G5u>TVYLPn7?nHjfnb zg7mRjpSkB_32+wN+~8;c6u?Qz(JyeF6hSlVc&Yo(fFXMJG&-;X3q9Wn9GZxT|8lCl z$n?$saXVMm|A-CHo&q7f1olpM9c$0KA5VBKWV4hDp9ePk--feBsiHT5A1k&Zo~6!? zK+u}*bY1jXAKFBB2gz>xY4Xyu;Fxb;E)@gMzW*Wx7$!;31m4r-LEI)suTDKM=G$uf z<$L>oTqC$~%0w%$5=zLNg4oO*;E_x%pgW~8t5Ya_jv~+9(dV$Hs(}?7yyg|4{1@u? zkvji!R&S7>jywXyBN3K*UxFO9>QFA&7IZ^FT21rc7@v{*ODC(w6xf^KHgSz5ds-63O5W*~5P0jmOB_?xv7 z2oWbi+ydNgo&^ngW!&Q3j?sDd#vBR)vJkjKlvj%rJ-X_E=sFEfwuy_<1$o2U3~v^N zuBrdL>%UR{m)pYy!NTWP;8QrntU85*8r7v%^d}0rQ!dk{>%z=8)-+WErGPZ^u3*N1 zqZmjcrXTQby`C*BK5lIWJsOBXFx4DKglty1Ghjlx0Tr1hYW=GdkUbn`)o=r9baeJ< zc*OSt0;-()pnyf(^~&b+dyyMnZvffWA-Ad4QD(E?n1(9mo%7P7ruhw$6ycJ}jvehl z4h-su5{K#jpNBH6+;1DEzh%KaPt!>tf6;z+pc%}sd??@?0(Imm7CKYqAQkoV>IyAt zm97;HkUHC#FOP-c+9;Mcnctp$qDN6MBN5DE( zK~>gy2}hA9L(?)%n6>`igEd(7I7qn+Sid?A(s6NmV&q)ZP$fQ6@dtQ`p^q|UKXl>k z4q|E!nrg4uBwPtp+nQq-|FTr&Fq=sNvPm%Kxgq>thPHkN&cGT>+5*2;+{gfVj*tW}QwPol9_(37HK$5O%K|Hu*swp&1t>zeL?#XP^K41#Ug(U=@$y_&kg{MLV`H9m zk=e$Rp!E7hl}6-P7*N;^koDEzt?~)k`d$YnaJH{F${3aBad=T(5li_ISR07<<15S` zrl<@6r`_CPD~?4VF-?|Jvp=#ShH0zOA_kJhE;x#QMdlm>w&@7Rfc)j|nO~g`Gunqo z&Mo%cXUHad>tP}?=B@sQ z0;cWJdj%yk9sjMXZ@4faO1KRKs6vD_QjFnKB{_G2niGM;|J(sQrm4-}pC|m}gYN}{ z>WG@%bk|}{+?v(!tz4FJIBd{pI1%~3vKO`HuUhjk14%RnLit!YNe%c3yLi zxeVn2@*X7>HqpuSl=CtHu)7TogX@85c7#{>ge1a&!e^)lI-RrGj@6fQxSz9!6VD*|VU{Of>JSchvem8!!t)xSw!eQoS z3d?H&x+h8#4S6l?h+uO&8PSGslHf>Mt=V*GPa^_TRCm%SOY27OF^ebXf7^DzeEp%>8T4>4r1>QlVfA{?f#(zr~9f0>1 z0NxKJo}=<~)$KpjQ?ebl9_Qwe_tHi3VtEpJhsn+YDlK=YOy;A8eea*vr|z2m(XZZ~;>m6s#0oq1XPd<@a7xE&Fy7 z&qlCz9lA+cK;lWwQkZxP9rKWH#Gg?^YuJo1c`7HJs(wgO-w$~Si}_PfsE!fu+-l#g zU9c)9l|{^(9DjKQ@u70Hr;Hr!9o6xF*4SQ@u3wn1RmkjySEX{PhW1&#`)q)yb=$j2 zBOLIOPn?;=%{*cIat_p!;;W}#vjwL zLzOauGn1aI{A=COTY_7@1pXpUQ^3em1AV)24X;DV zrKUEiVoGl2Jza(;|E+oZ0-au;;LeC=TZE!#RWicPw}HZi;iq`wCu$paq}WPPOtzgD zS0O=q{8FYAhs3dkE)%4K-o|OmYjdfKsz>JJ+F*G_pw$wB(cWOkpfKR1zmnnm^Wq(H zKFoZ3o%d>A6iQ*PwToEon{OCULKY1(GS7O3UG+V=vZ<9{QBoNi=2?sJ?u9Cudjkp;SRHIl;-mvIv=X{{hN#gY6?djO{ERhk@C54D`rhJOp1CHJ z-w13U6r`KJY-;_ReGHXWa1pLJG| z2*>{D(?t*9*^}5)#p!|S5eH-^SOUSM^t4bGbL~6$Zkd?eow*QqVVXY})uBPWK|ZM( zUb3pWX77|cN_;|USLNeHUHbRL&F9)+Uj!-sc(#Y|2OHPpMWsuWE#%WwVyenbhovQ6 zW)n-SMuJ5;X}g7Q zj?`PMX619OiVW}L0vjq9(H{1m8QFx{6+SK>_PF?pgJkO6D{y+hBmaTho4@wB3uQ8o zRY|DCJQ6Cb?r?}m6jsn7<#oe{e2Q!h7SRsNA7Of)s)rQT@K_8I_4rDF`Oq*5*meU@RMP)Z$#Mw zb)U~-3KYYpeoKSK0(v3 zQGD=&OtToTce5*fG=mfVYdi&$ejKbGsfN)#m$U0@Am9y(%eOOkVHngmQ8kq1)~yf} z-~lD>r|@j^7IO-ZQqWh76BL0k>7r$~B*aE`r%Zil^rb?cW;;+UTm$_A0m-*!ooOjvm9sO71CfopL z({-`p-#*5F03TK}f`1y1xKv}Vv)m-R@>_fI>m4o1^homee>e~8$0NT-5StY^5>)eN zaTL_Sxv_p9Vk=fhq3#rf)$m?tr`1X`#7l?Sa4HGMqiaAhRglH%@zh=9F1q;cTx-J! zIGzWb>cJc-s)cC0(sG$cCaMt_r-HIh4_agM)W45B1$eQZ>PrU#ST40Z$|TVcv)jIGG-zH*6^L7b}IgHR`1lL--Csi70p>?Z{-^v;fNrmo}tK0`ssQme6w{HNn zb$S%Elng-$_G^p;gG*`d4?}pnS~dv8W&SuL*;qk%NeeW(H(oraSd{RQn!W!qwCR1dqftKoaUN!wD2TmRnAC5SQt1it&p)ZlS zH5^}g$xzK0pP7<4lGq!Au4Zkv@=Hc;tQ^hy@E%b8|LPjEpa1s7QtvU+ zla;7@rNy*Y$eprsax!Sa9e~b#)Nw$vO>8ER{R=oAYVy3vq^`s5-cJSdTQQ7L;}cdh zmmDhID>($pW0*!9YoHka6y_`UMRnJ{C3?|fU@^#(OC@QE)5MErj8RO-^K3Ja>1(nS z8$28Ss>_YbD^*cs`u175G0vkyw?I1N6YHoLXc`Nzv;y}%QdX{LsxxW>K@xQ@*1vCS0Zd?HQ z_?ej_?Vro(-S4U-cLO|xXRx1`NfHy(!GKvAe>GirJe04xgK}mkz5DmAvHb_g7$R!r zgX)lr=`=rpB=q-JC6sdA(O}T{2F;)_DQ6-eFiNo{C{B};oCAu9e;*>#UBW&&d{{jN z85j?fRiE)ZTS%B)7%IIC*BYPh5Vc^u*#Smzy1ehr`RFjWC-W%DX{n4-kDJ0FYARa8 z@yPU>aqi+6rh3FSsl9R+Mw{z{-le(f^9@TIz}quVt&IESfAOSbP$6x@@Vzu@>ajhz z#tUTb?A)1cT-z;r)v0b)`-J-K{B=fZ>SL?@ylIsV6v=ah%zYqIaFQ5+Hh9RH<)c`V8nkYP$s~` z>^>>4TERrbG-BIMqGQrQo&)1A%lwgzUZ75>Y;-r2!ruW<;>AQ35yWfBemD7wV36d> z{DsrT-o=LL>Mx(^!p+ty6<%6nUSSz;f}}$E%LgqRRdcOlbq2jf)Kqa*nIC?}O2v>t zNTB76U?%y*rQ4c=->5us!s)*P@v|bZXMzA|w7KAeA##~N zmrF1uSiNeWf{i#Z=hY9`feqygHR@#~HrgS)AQWVeJ`lD1>X*AFF810N<4YEU*~bOJFya-Ng|FOwiy?R~b&B!6mHS*p$70E| z5J_alXL;j7unNj|*&k5U5z888jILXlOSmrl!%u9uQrTsMX4&sgQ{`T!hz~+1mckK_ zmdO)OkqsUUNDyQ%``5c4#W2;XB6Qs(-oMm@+djJ*nBpD^-sBRuNW7+4+|#j6tOAl& zAv%Jok$a0orXD1t^ke6Sg^N@kYT^7tb^YT{LFP}8zQq?jKah@t9`pUNxVrlimljvA z@DvP5gkiO?rtEtxc@WM@Y$Bkcl9Qx6*7$^XS-HYSaN0AYja0cVhRd~&7qkGFdfuIz z-!juXE{vH3B)>@`;RLJG)`bJ$ebm<4YmcJR*DiUDuUNnlxVUPxAIXta5yTjw&saT?&1YXRlz21v4ML z%L61a(pwUw0Dv_eWI8v(D5X@M5tsR`E^Unkp5UW{?{Ls-UK@*q)M1v5^NxWRFuLy- zOV&ch@4EtreaQ#Y3kqEO`gaZR35&jzIBKf!8_lXiGYw`aLP^32gl} zJy+C9=Bqaq#ex!}@M-{+=XgowysGsE3+7ti*7>)NzwHT06@ z)3<-YxQv*Jl~bIDL_zgMxkOMAKJ^C74b;HNk)WEQF70P}>T5~Lf*{2e2h|1mu$@Ev znYx-68Uvy_uj-yTA5Akf==Ki+yIv&;H58F)M%*m$W7+ApD&&k|x*S`lhD{0Lp` zE@jG_dL^hMmc+oe=pRsY zJ194CiAIhUHPbm7(j%C4RR?e#NjQ*a;uvZELKH0BpSWO*?{O5OJSTd0`-;QcUMUxQ z$6It|awRr3+l5D-Xu&8*0ZGt-3{Fb+`*bCjFsnEwtquP~yYzC;^R$KDk)>3o$s`Z6 zVn~yX`Nm6kp<>CHzgx>PJ`!n(v1E{|zMi$hYk~$_4(=5`3l#Nc&Xjgsj`g+Mlf_HE zyGi_xd11ypm>Y4%FQiu7^4OOX0XmUgV-_1=h_b)V95hcPH{E$95fw+?RmHoT;0Pu5%5!2Q@4&)D^1BpkgpJ|dRo1YIVTZF#49p5&SNm#5X88Sz`!)*Po$!W>B9GL~ z8n>ZMcN-z6YJ%I|o9i5Vsq*My&iq!V_VX4evtRPXkU}rx04cuU>_k`L;y($1$He)g zgE(grRqqQ+f|1(C{aacMNyuCqoe;f~r}(`^atW?70D-SAdQV_k*T8I6iI^U_$KO>ICN*J2;4G+10nJI!aW-J*aIcS~LH$4ffipwtkDF*W9__;)59 z>INH+-E9{or$s@!E_Oz(=|QJSR!ck*3+7fxjFXHdq8Qyzmj#~XFEGsEKvl~^llxOs zAuUw}%liWF$RB+4>{!FM8krM-N#*}8KM*<5^a7O9paoyv3kS8QPlVhalGcP@qUmff zW2oA)l)uDC?>AUh*huqkW~=@9&*HNL?+6|E2bWc;l^_Thm&iFQG71U{X5**d#A9{L zmsT0xCy(`2U~VJ|Io9QJ9lIs`_UQtg@{npKRku0<7{akI=W2C~?t0Eb0ABNI#7?xz zv;0OPGPg>I(<(B*JA>$?2d_Q4|@=`pVh%s z+{=%$0s>bn2@(V3HO5 zQ^AkEAn4I+72>pBE}=S+0x#23wVM)@Kx*o7`|$1-;?yKW%ReXaO{2+%FbL0#(tr$n zuOJ$bz|^~CM<1plY4YCF-fK$BiaqRE;+k@PrGXCCDotmhVMF!?#HO`M8i$%N1D(p+4s9i`CCh`wd}=u zPmlbl>eR9cN2I_jS_)aHphikPV&wa!+R{dtET$~#b^8ko^vN{QzPq`+hCK+CDua== zQo3$r!P=$nTs1dRgi8|#HxWsk8iurk7$!_BF^>i5dVdGH6!*D6`3X`k?g?f)VN(2d z;M|DzP9xvv%TtxV73Z#L?LYIKioA#s|6t|xHv_0XdGb{@3w1pgN)obn`E>(?KxZJ- z;)kPZ`tWDHi$v9~<+R!&cRsY1d=SYmVi^)`-vu{{KBF$V@)(!@gPTr#8lGy&0ISPv z{T*tF76GDQ!-#w3Hn{a(q*nvXiAD;s-I;8oahC@TKX+%|Rngtxc1e-CG8s?I1nFPG z?~f(46mXPQerqh_B4FB!y?!k==^|rdBgvdYZr~U4@B5vwbXHj!|ILfh!F>1PD?IwK zYo3_Y1uuU%ax3rXmG?V~O8*2F8;+jq_z-*Uvy5L42Ng^VQ)$dNo{{6kJd&7giuc2O zmXG(EHGYK!gau3$(e#Hi?66)EetJdCF!u`B3h33!JY$~}*ay@3v^aCJw12T)UAf5tG9pvle^{1dRmUtTNwdGcLr10WIPn55`h!iuorGh`$b|xRurJ?LYkdC z0iAyP=yr-3?ZE%38ROyuSrj$%Gw!oK+>Az90u%ACwTMpaosJ5bnN~YO0r4K=*_~S` zhxnhV(zQpn-;m(q%Wd&*yVMh&J;D#_ZcSEaR+i~3?t0c`6G*%Fg|--KyW>u_xv!k5 zOnzmZL&&F|AR)qN;>)F?iM3ipc?-uW!_E?%`CW(NIpx5 zvrxq3LYs9$7^bYXVJvC67O_S7HQ)$$FxpU}^brArw4+%K-@h{(UG`NKThYgE) zlg2Q!Dfc?hH@AsTy42MwlgGyRwyLx*R3r(Rq!cE8rAcLZ0ASqC?!QZvwb+pj9C>HG zX3F&Gbq>R9tA1iw&^IOAk&zR`nZTUKYHy<^95zG)O57ncJ{1LJCC^qB29ku_Y1p4X zaA%izSN%89im$8ZMx?1RY#J32Is%U0)|&&QffP@*21Eg#wbO3f0g+;UpP-r^&SWXR z+>C#=22pBHXtU}ox9lFj`Aey*d3URL;R(7EKQX{S{HD-cvadUp_`&M#EgqTxMHV1E zNv-^Y3Vr5!@3tZa0*q~zwc`Hdqz7YhLhsqfOI|ZtxHPrNkV(a<7L##{BLOsL_vdJf zUF`_9!D_KF*7>b*^jzeI8WH@K$;xWHWnVz&eO=?BCGi!+t^6D30!rqOjQyQg^k+qJ zRz{I&XizL!|7$Wu)?GE%`?YkhT4ZEK7=_tN{HbDDc&!4(#i#VKnv*y`z}*dRab2w% zDc@bav6uC`cqlM2vN^R-{V*XdjZvW?IfcAm&Wg1CdUU#tGV0=P2SNdE}tUS8L7#XYrg$jkOr-d3#@pxabglJZ2RJ8n@$w^%LSn@{*@GX<`f=N!ywbx?~ zAPKMjwkG7IZ^jHc)~SfESFdo)NtE_2q-7u*zC&&TGV`qQ1(bF04@rLhS5GP5{XP_6-_R;vL3F9S-Aeg&KE%<(Kj2(&! zc1oDsSX3_V84VzU(x~9=igVAAQG%c^pz!eXKn!GwU3LZ=XMkW7!6#vFX&Y##GR#xY z39R1vB}&XX74q>oqjdJ%S?Ys)!nM~!4yXONS-<`W3ef3E0eVeWD&6g0c-`Ja>n*Va zr`l3kIYosifR=OioIdyn*}NuUK^)&RF-gQItO5~<_i@$OUftlbyykn3!VXGz!x$$E z#z8q?#_yEvFKEeNSUq>KI-?pqN?muJpdlz_#^Tj~+Hq+Z`^E!PW7c9rLRpK-Q)Co@ ze(`=uKt>X0+-_@)W`VSEuXw!V%m5a=<;xkD)(-O}2|u3ux5lTfOIsL_pzcI6UrkKS3Xyq){0BT%PS9i!Mm^Qurn(`l?r@Ei zUV+p0^lKUy>;T?8%ZGf(e=^XC{$zJWykTNl%Wfhv>YWQXP%^yIChiHJD&MXXy)F>; zMM2g3zytx|nfgF%A+F=h?u9G-G}AG+31?OS4ZmzJa3C5r_2qSi$O6KNE2GB?0pq=| z0%=p-EC1WP(6gv2@W&oEbtc%b@AHL5L6*J6OoIreongA|Dk&r#ikenx$(}k zZe%|eo#-MpTye}%_zIh>i9>`P*d}-(n~;Q)5E2Op{c;9&{l?jf8xwd%)Q04z341dY}h|@?-6UX zJ6%ezn)P?ify(CziW(JrObB_SxRJi(1L%aGZtBJ5T40RKb#7`L+j_=G1XetF7jz@@ z3F1K``$khOHj?j|C^xz@u@|CMAlYU!ul6jT8wz*RY^E-y8xE|`a~VMfIf0Zi0>}+x zFH*CNyE3mN{Ca6|wX~*D74q?=?`TU)!-%!yz|=ff)m+ZiJJWpIng~sKP*b*h7vfU$ z@i8rn@gmTMda14L#(7If+pT7szg!w72)*i@C(B_NB}?%i0xsPr2TuTRC-dEoA_Z=L zoA^_q#zHTR#mlPVK#&ml#1l%tOimy2HM{=X|vS)E)Kqa8nVey6B5W+fZtB z_*kao%B|4>a$ZOn{x)!swAzIl>c0!4MC)MQ$1&o4Syx`^z`JXBBYjryQyQK#VlN0y z+&HnK`}GplEa~U7KtPJ!;zq2a_&F;f7g_NAMTGq2f;<9k<;N`$Zcb8(rjIx~rDGVe60DoWbj9)_BEed+&jJrWGi8-w79q@HVA?IPJ z8={`0gD=rb8`cu>UUi^AAY3G<2agg3Cu{|6zF@MLSCaSHJc3=Q{?t!JYbWK>!yp-REI0@DA4SJw6-`+r@ZzpQBFYOufJ|kA6$C%m!AqnF>c|9Mv3YQ zPG$yWAM+YbhKcBOcd4)Ag^)Fn!_3ae5Z{xVjf2=XoF=wc@4-q!f!Q%JMkzyv`I6Utss7}Ug7 znuT4>l=5qp&-XSDD&uaG2g()rivjhIuR;9vuH}rD)FHijD>rpb1mvhc_P5K4in-?7+}LB!Nt@+S$NzGHoQuTyObmiLLpRhQjf8knB^m;NG~A;E$Ws=*5jnH0IiR zaByURPy-{|Jt!Z~^p|;-lZH)jRGYx&ak=3>FQ2jf*U9ACfS0wi>y7I$z{-f^t=C#5(bElS!``U6{UX1`FQ z$sY?Fb%tZn)y_iot5T&|Ou4`dcWqWSochx9U3J*;aEIN}8GZX)Se^S3it@apJQLPw zOV~f7;gQb(Z!qkf+$N@bWL_i+#eGOsy9C#qk~M(@2u*!u=# zy9-i$!9TWVevvXnj0NRs#^Hd%1%A*37*L1@PyrtepjzhUO-^cS_>d3PiT$|fA0gH? zPuzvU*fEr<9Z9hx`YJGz8Reae%tQgRstU&n8ZxE<@DDi~rHkefO2>yc?J+f2X7{!O z$BI&#^Z%OMvV{@7bq2DCCyQUWKz{5+@OtdM^_Jj#Sa4KfkYRqS|0O7S|01ltk)VHZ zt7*678sdcS8JfYUS}-E#UMzMrJZ*?QE^rO%wTqi7oNO*$-Mv9(N*>gkTElA;_w-(K z3FAhA>9VNbYSJs1lV7{HYIf?~1B*3J(ojENFD{H*1%vq;&7&H8+izAoh{JL12;`01 z;6wN=OMT@D)={b>Eu!)w98e3FcHn6 zh^re57j(!uv-H?iwJgLEFmC67iXBEnK=)9_c_l(NUyEWFxG0IZ=+o8K_hm<=b7+nm zC!RpGe)9**y*Hn?(yn=rAQsabYrBB<3OR$$f#x0HD&y2+0x?`2le@1a>1$_$KSjJQ^6RtV2bW3%s z^Ml3qB7>(^u_j05z)&Hv0K*TfP=HddN5=c42-*AAf=Lmy7Dv^XNpK!P8qQmv%u1h% zXBbj6->}E`WC*|Tjg1c_G01!^TU2e=us48(eS;=raA4?dpE%<+U3m0$wp+LQUEM67 zy@zI0NIv)$C{<$;@>>PpGdfRX?JnzKuN|^VTRL_|VpAid~D^2u~MYg?dj#J|*c|3(nST zzS&lSx5=<{Ax(FRG3P#&ft!nO8w(AsURL4CXF-Jn&EGxS5orpy{V4;wAtpGeDGQ!! z_+t@GZzG`+&9UkB>Q6jIH3|HVfj=dT%8;_OyTpm_AvO~Sim#v;>C2}`jlj#*)OaQ;susHZ>8OVageHvouHg2)FmsB>PPEf0s9nTWBcy z_D`aTVSocMGwf(?cm(Vn_(=LyZo+G`d7QifVaA5POBv?OE#eN+3*fOD*jCh1v zQ*`#vo{-z)`dcK|)JqcwnaC#`^edoNsiJSO7vquD^@VFHV)K;)IdDX%Co6QWUlXuq{LU;2P;P{@bvaUBy5M zFO34^YdIkknxl3r_15sd!xZf494_|WarDY-ErRtQRedlf2NA^UFl&yQjQSMXCZnsz z>_HYBPGB;8kokMGBJp!mnY7=bw2&Wd?Mb|gC+FnAB6EOLnUDO|K?lo{;ba=I)m-O@ z&p^pA8QMd4-#6+E5;DpiJuit6??7cD_MEa>iHB`Ii{=%t6P5o)|uaw1^NlkCxKQ7te(+h-G!oFN(b(7Jlp8bQKt^LV3 zbYei&!f8CDcPEAg%c3e=WE#|&uunPVXId)L1+R%Z9)5A~P5$oohF655UPSQ^O=H_X zCQ~9-16HB>8%F@Fm%Ga(KgXaG*9i+KunlN;NWNDh3Kt6{-lm$}Q^suHSB0*KENVgt zUVi1S6Zi~%rJic|ob{lz^=I7^8bVghXungWZbb*t?B|*o$<*ajciy@S7fZeqplpo< z3q~;j%jk7g5{UQi&rNh4Uwk~-<*Rl_I4E20OE+9^*EGntI={hX)U-?3&-wYQH%-;C zCw&S!e*WrgTa*cikAoEaJiVgz=`zmjv5rE886%bpc*#(oVY5ly(7<9zLYaA$@$Osi ze(;+se%e8uE{!)651aDbf1niys~NfZMt&c)%2cG|Ah|sB>OWG?zIBB70T)9Ql)1R{R$+4nuDav=37v#H zjEG(w2rW5$p;9?wew!h?tPpAbO+A3IBzh#W|H!X4Zl8khpF8f83#wM%sFR_R-1MzG zyEC&koRj^g7U$&xD$JJ)1r3DTB3aMJ7OP#z^cJ2}1Dl$vqFh$c76(7i&TqAOJsCd) zqMcmxySE@uDFVO4J63mpH+9i;tB3*VelkE}n4*QytI|5?C#K<`kHH)i_N;3A$^{BL z&g`XdiO`A@Uf+yWPDgQ2(BZnf&+LtOiCd$$iVt2#UVPE;_aFC6bPaCIysYH)^Z9|* z!^5l#-LGa2y(ewP6%hxw6USQ*3wKq0l^mU{A}3lsMve_uN0c=_4$Cl^raZ?}sAjAO{y_0bg+nOH^@|Dw&iPwq@vo6Cm8$)W@BwsY& zajiJYm6LcvrYKauM%oerk<+PD`K1kh!lrn;yR6hL1)B{%IgI)ho7 zRI1%7`geAs@6Oib8v=AYJiF(L8V^h_O?f(DaXWJM%S#xIvoCT5!IVeFyl?YiFv>y{ zYsu7SSW?$8SxEK%fFoyep?@;7AdgJbp$H`KffNC$|XF9 z=A5ZYRoS;1SLmDqd4V;p#^G&cs0g7Dq46pJhl@Tl>>N#S6xG+txOWlH1`N|hKI%Q0 z$VLWy-EC%{e!^9>zh|j62`q4q*h)rLwXELTRJG>#4uBVJksyjvx?mdgiFiNCo&~1g z0G(0o3)%tCvOjAL28h75GGyOYxj957elwPC41EASYv9KoKNAp(y-UfN?J^T?ysDr* z(djY;ePFT|!u{9ArJnnnN;m$O(vlPeeq4s;s#TwM57l@>+;QZbd8p0FK}y3DK454( zMrki`Ekp*)3l1z0LY^^`wjq@dCuKv++i_1)Cebb%&wLV~pUK2W={eu1p4=KZG}0SI zP(hs`GLy3Q+vfOc#oC@xd0s7E#9)I`D)+6r)p8^1iuXUCKjnx-p4!@QV8R6KQ!v;# zs~u^+_i3ReQe!@5_PjDHkQIt`q{x(9J15-uQz3Lp0+9YfXB5UPT~LQ?tiR_0<18%T z?9p)cKgi(vtr%i)4ov&X0@SSGi0|L8&3Y)B=uar#RrO0aBMgAejer7*RNtOO(1gL? z4k8l?_k)EAFmjk*P2hYJ_iVO%;6f~*NcTMW;{ z$*&!sQzbqfYZ`AyN_q1Eztgd@VT}tn;CDn9E+Q%d->X8H-os6rVq3#)(<`gdwZJss zO4x#O=67pTtHv+#X^NXVFkxSA4k7edV?ZOT&I0!z`h)M*zbJ4is{N_TG_ zfdj>DYyKQwaa6^mAc2+;i!w;_w%p?{W`9Zpp zCtY|VTzuPc>cGP!)JyJIBD0?yWQ*Wg<)btp>5!;c^)%)JRS6OFj0FUE9}A7WB#o$Q zd&R#pVsXBnouUkvN^AV=fIRezB`SUzj*9hu<00)@0Nr@eOmz=#vJ$Fv|pLlj6I2O`zbWq#hz=N7X zXoQBbxcp#hO}KzUY5yzp;N{sFH7na8$ptntsmm|e&~)jL2Y{t3%(ayIpk|#oe!_jpuL=FOW5>IkJJ?*>ujJhuX%h2(Km+qZ0`H52 z;#LBZ!rIijYfq3waYM+o+$0AQ2#K>>t%Utp0%yOi>gsPm7JknNu0AYbG`1XJr3g)> zTtxkoUOS`CWOCz{F_T_EP|k45r_r{U@i%)i+n)j$DhSqS{HOfcPl!| z(HE%8Y5@DU@m^UM+yQ1s66M=0>H{NqIY2i&${rkM9oU-H&R`=1M27Z8b=9D)h7-?$ zFJ0FLNxfRzeoiHPTCW>t`Rt+ifUD5?BCGxKU|qJ$%DuY_-=Cr#shuYxT?{3WOt$v# z{DVcCeZRPkB>L3p*F?ha4}cuJ>X2n?66&Z~2!odG(vw!(ji#3PQ_v;wEYhy1TP zAMWqaprq$SRzeLr6-jvo1Ixbp-d2u%`d*gXu=+--u0Sx>=i-%r?&d@g z_7BMWpRobEh}st3*2J7K`|uYn_T2meQ7K0N4P!lUX_no2>cDNKhV}b z9YU+ujzur;a(J6h@`8!A4w5NTR?;p=am}Q=7iCa>D4K3QQ230=umZ~F*~h#hTcTSS ztij_-?yYV@IEwoMCzkb7w_v1kP0;&<{4)M*lHgRy=Pg3Qk)^Z8fZL}@!bPq10KSp;M#m0(v4pNxxT6}LfV{wPe?kPR5Zcn;OVMYj{mJ#H zR9jB0kDtQY=r2*NjDV)uHYuj~3RiP#HvpNu3CEdeWRKOlY<2iLPMqjZW_j~GtHpeS z+d43SiDIbDW6-*@*JfNVmn~as1aIP4;4T%sKhNkC%nll2%O+Rrxw;xLc5y$i-=QF1k&HiMLNB=vs*BMn9C}dR|cjN05 zydu&4$gf0L_m25-taP*~GRc5ur<*&KFUc*q@`#++cjTmDp*@D_HpYWiV~_*cz@eDJjpSmGcq#_ron2t$5C!)HY31rxAPb(NJ1pRn(b;B_e3_Hry(XjC1J+YogSmx zk=1Ba_L&3pg!}Cvp=ro4oG#qKAY~!~6l?K#O06sL(G3Qyw9THjKUajm*C2Vo%xG@? z^{&Z>WNKc&g?CL_Rgjk2*T{3FULV#Gt$qpSaJr@$bC0YnXA(9H77Zs?r?snPE4|oK z~*dJR6hb43~M1^r){G}D;H9AG_j%4gvT)$F$u zNs=7#vlhfhRe2k>w5JaJRoo9eDOVa+b8@nrE{b!+FInOd5B3U4ScvG9h#+m)H*-Ol9B9vnaCc#$o z0aXT?d^;33{Otg`z_+fmrR07_I3q;1&(}Vssu$T`}S*o)j3OS);cc||`Us3`s{$7_B zSCM5qp5@Zd4TqeUi`GN&*dg)Kd*I;DGW19&s)nZ)$Q85;^N(D2s{dRhY8lae~yazwmt$udMD>R@>kVx znI8C)58r{crw@NCcfW8X{n;UORWaoGISgWxqyypOYg#d zG*POa?^0F`W%9+s`f}mCU`+!4TweAAo6fr1cSrYPn}U$uqqdPhCWQXXu;Fz~)-<`W zuaa1?r2`h;|3yq|hT=j=e`DN`&P+Vg(U~E-zhV2a5kj2C*lV-52MWYvJO+YM!5nwY z-#K?DC}(f7r>3!BIN3x<#q-|8!^!T_XifT2L}DpziZ7}{oPHMB{L*UGV){9%x8>A! z)EwI1!ZrKBwu@pVhCzq5jk3Bo(wM&tLGls0I@y(0A5yP{NYzpYg>*;SpDYfBSj=xx z1C#VWpz1oeV7C9|z&RS>cicke%>NTBFE1Teae+!SNoe=|0#4Lvtq}U~2^J(!I@^N5RMq+m9Y_Hl$ zCJW<-5jI+fTvTEbw?$B|`K=y`GCbYv{CMZb9Qsw9gVU}PVev_Tq^+=yE4RwmQ8$g zRBP}gz&Zr$BskMX`@S9mmDY)DYBx?xPv~n590x_i)v4{Wbk|}aCJnV?D8kxnoSbLI z1Rh^EqcQL^5^SWU3m7I`Cx=`}s+3GC9IQxi!GteWD!ae06yW3_^Pd_ z=|Q&`k==1qU*hI~m;vPA-hU6s;fTxXTwsaY*F4h7`-(1@vw{Lz&o3+squk_w#}w| z>Fw$o?@j{w0%qbr?wAt^!$md~2Iu_t$(P{mo z6)%X`NMk87fSm0NHjq1s$~OxAVNc_=l=jmQX&rG6!<%qbf7aB?J&^O2WCOV4s{VhC z;|G6Ngo2+6Y$Ap?UnD6s_y*)jXTuN64O#@_K~X+^^%i4nCkRn)E~9=3+g(Ir7SoF=sVj-B zA=ltou<5@F$pu8Jo5AaofE|{m#r!jWQ9d&5_ocdR7HoI64NV_ecagT(!=&~QVs|^} z3eCW-HG39EmP0UMu+wv==k)oCB;ljanKnxAyB;SPA-QLmiVF8mW>?~7r1QL`z-=3d zt%@@gz2A;BvpG;S=zbWg>V2<4gkd=ZsO_6BURrM5!aM2!XjoZusBW56r|ZvwB^ii9 zluB|OvMz#l8RE%`?RlU2$Ze*Q-dSmmV=c;u)4spgu3C4ArU!==xSU~?A|<%5AZ<{? zNSOvuNnpLzS0}+k*<91DWT-q7CjlAu;)(M1sv#To-Es##HmCh)Sn_FnB}Bsm-g21H z_sAP^<|e@!e4vNiKn_FAhi2i-Z7VS!J~AYoNn|;|H0r z-cVd?<>*A4Ld$ufXWQmJ;+&vzseo)?+H@~Yy^eAgxIm78(%ciWKVAiKIE@;36jT*W z;4?3pxrOqcrK=I|cvP*|{^R zaY62JW$s?_RSEum&8aS1d=zO|7_X_5CCjJ$=P$Aa(|V*z@`~SrjTX zU)t}FXTi>xBmRus1B9=sh!`jE+#ey@O^D&-)lB|_PfcSj3UL>jP^dGf{U%?9sJ26) zczE34Ub}K#AZ;1Hztm{P_lg>`$aE>O*7s+@!vk;%`N>iIYx{`@5dmIJcwp5M_wf>56gmFS92y(R?$G0rr01d~|QxM_OIH5s&aSUw(5;DHZ3k5QvfIEZw1 z>Cq9efqhJ_<8(-b;r{l3Pr|Wbb08sp4DL~Wxts8m>s+{fmkZ-Ce_1T9y0d3t&&j|_ zq@IOx91gT6JM)p=j31C&w+>cib&(!7pSHhjKXc(GJGM-*7Az^mbnkYVHl?nJqII$b z>p=81Mk8GkA8*%cx@(U|_b?lFMYjt8O8E&`FO?QVbbr!#_XSX%UvP+Ydn}%JMGE{> za>g)G*4J>4geElT{p4*#DvoUl=HLAFnQR2h@0A~&7Y6YkgrRJw+{JST(*IizNoj9Q zh+$c}l`r>K7_A$Y9}3jY!lOS)mLP*xud_z(dUR&>`(FgNeS(ptqtlzHby`U~GX_A4 z#k?3Nf4#G7l~7vmKcx{?`U0?gNg-cE*wkd?N^*kRa6312rXFS(LtCJPOzd;yGQ7#K zNA6ZNrLAQ5XFG?dv0+cZR|Ulsp2JJR0=^~?g)Y{ovH4M|9L@{Gg~kt@(x&TrG8F|v zdA#m=nz(%5Az0;ovzW{wv<|&?2nZfuU)eQ7DpW3vmlewySkE{4fu*D62bm4k4Z;!z z9w2Jcr35An>dO8Do-25A78%w_Q)p&qCfUIOfB3~*9-Bbr@4qAo{U5}=Mbz9x%EBmb zbP$WCYZ`}ui#A*|y(V$|&6=11WV85!MxeoZF$w8%kP(V3b?ltokU=nE=Qp|~;YsH+ zQQ7{(gc`j99*=X)#x!IwtT8py8Fq@#PbBYas0$iOqYH{L@~S>yELAG&T;67#L!=+m z=g7;nBz%ZzXZ}h9Gj{YTi5WY@c)HU_P$&zd?scOsO7LIrSx1PL*}2Tyvxq{?!0|z{ zaMf{yuWwQ7_pTM@WVx!I%OSm%?Ni|Rqn!N;xd`PL`{D^4=|#k)fnJ2oJ*k5+rqIyP z){t4!jrn$C+BdtVq3!sDFB_OMPK1AaooJ5;y{3^C(2SYT%%0*;Yun$`NNRL zftn~I3c2o<*9j$XBW|gOThi*tSp;3!W^$iR?awmkm-4YrNK>ctopymrh+B@4_Q<=A z=R0407l9l!K?y4|pt@^1_ys`|&Xo~*U!XggZp794OafUI4EMPuRfy@5O>^ln?yvRM za;hmS9vi>Or+Q2{2CTiCsg5XTXv~R#r4ZOmj7L*$rn+wor>CQ$*bvr~R&~XJCo@js zOG86RRX-!h>&XxxVc^S_gz3NhrH05D-0_t(^KhxRq{ZL|(o#-`1ww=QJ6SbgL5v&l zz&hXHdhZI^6m>@XTq*gxb*J5OXwO?#x2*nbNBbo2oFQPSv(Lbkk6GCWkE>3dCo~>D z;QR2a2uU$fPg&8*n7*AxqPzOT)QZdIGFlPF-n3T z)K)+tCw*@#_KALxz3mqSZJ$QR8`oPKtA{pADA6z%u1IgPZ4&<+2Oc)K}Df>&b$+P`1hTtc5HOow@R|zItDjZae-rlHi4&mHhQ;zN4b64IQelSZfe@r zX!A?$B6{QZAUC-FSt3}NpW`qBVpuze7e(sO@zn}j@;WC|DU#k bze*%PpA5uZf0A~5Upg&J&zn>mV{ZK)T(Uzr literal 0 HcmV?d00001 diff --git a/src/obj/Goal.py b/src/obj/Goal.py new file mode 100644 index 0000000..183598f --- /dev/null +++ b/src/obj/Goal.py @@ -0,0 +1,9 @@ +from src.obj.Object import Object + + +class Goal(Object): + def __init__(self, position, square_size, screen_size): + super().__init__("goal", position, 0, square_size, screen_size) + + def collide_test(self, waiter: Object) -> bool: + return waiter.position == self.position diff --git a/src/obj/Kitchen.py b/src/obj/Kitchen.py index 971f6df..3dd04f7 100644 --- a/src/obj/Kitchen.py +++ b/src/obj/Kitchen.py @@ -5,6 +5,6 @@ class Kitchen(Object): def __init__(self, position, orientation, square_size, screen_size): super().__init__("kitchen", position, orientation, square_size, screen_size) - def action(self, waiter): - waiter.combine_orders() + def action(self, waiter, current_time): + waiter.combine_orders(current_time) waiter.recharge() diff --git a/src/obj/Object.py b/src/obj/Object.py index d4fdd30..24dca0d 100644 --- a/src/obj/Object.py +++ b/src/obj/Object.py @@ -57,3 +57,6 @@ class Object: def action(self, obj): pass + + def updateState(self, current_time): + pass diff --git a/src/obj/Table.py b/src/obj/Table.py index c254642..d9938a6 100644 --- a/src/obj/Table.py +++ b/src/obj/Table.py @@ -4,29 +4,59 @@ from src.obj.Object import Object class Table(Object): def __init__(self, position, orientation, square_size, screen_size): - super().__init__("order", position, orientation, square_size, screen_size) - self.order_time = 0 - self.customers = 0 - # roles = ["table", "order", "wait", "done"] + super().__init__("table", position, orientation, square_size, screen_size) + self.waiting_time = 0 + self.cooking_time = 0 + self.is_actual = False - def reset(self): - self.change_role("table") - self.order_time = 0 - self.customers = 0 + def isActual(self): + return self.is_actual + + def updateState(self, current_time): + if self.is_actual: + return + self.is_actual = True + # here must be neural network choise + new_role = random.choice(["table", "order", "wait", "done"]) + self.change_role(new_role, current_time) + + if self.agent_role == "table": + return + elif self.agent_role == "wait": + self.cooking_time = random.randint(0, 300) + + def dish_is_ready(self, current_time): + return current_time - self.waiting_time > self.cooking_time + + def get_state_number(self) -> int: + roles = { + "table": 0, + "order": 1, + "wait": 2, + "done": 3 + } + + return roles[self.agent_role] + + def change_role(self, new_role, current_time): + self.waiting_time = current_time + return super().change_role(new_role) + + def reset(self, current_time): + self.is_actual = False + self.change_role("table", current_time) def set_order(self, current_time): if self.agent_role == "table": - self.change_role("order") - self.customers = random.randint(1, 6) - self.order_time = current_time + self.change_role("order", current_time) - def set_wait(self): + def set_wait(self, current_time): if self.agent_role == "order": - self.change_role("wait") + self.change_role("wait", current_time) - def set_done(self): + def set_done(self, current_time): if self.agent_role == "wait": - self.change_role("done") + self.change_role("done", current_time) def is_order(self): return self.agent_role == "order" @@ -40,20 +70,15 @@ class Table(Object): def get_customers_count(self) -> int: return self.customers - def get_mood(self, current_time) -> str: + def get_mood(self, current_time) -> int: # перапісаць if self.agent_role == "table": - return None + return 2 # undefined - diff = current_time - self.order_time - if diff < 100: - return (0, "good") - elif diff < 200: - return (1, "medium") - else: - return (2, "bad") + diff = current_time - self.waiting_time + return 0 if diff >= 300 else 1 # 0 - bad; 1 - good - def action(self, waiter): + def action(self, waiter, current_time): if self.is_order(): - waiter.collect_order(self) + waiter.collect_order(self, current_time) elif self.is_done(): - waiter.deliver_dish(self) + waiter.deliver_dish(self, current_time) diff --git a/src/obj/Waiter.py b/src/obj/Waiter.py index fb52b48..47fab14 100644 --- a/src/obj/Waiter.py +++ b/src/obj/Waiter.py @@ -26,17 +26,20 @@ class Waiter(Object): def basket_is_full(self) -> bool: return self.basket_size == 0 - def combine_orders(self): + def basket_is_empty(self) -> bool: + return self.basket_size == 4 + + def combine_orders(self, current_time): while not self.basket_is_full() and not self.memory_is_empty(): dish = self.memory.pop() - dish.set_done() + dish.set_done(current_time) self.basket.append(dish) self.basket_size -= 1 self.memory_size += 1 - def deliver_dish(self, table): + def deliver_dish(self, table, current_time): if table in self.basket: - table.reset() + table.reset(current_time) self.basket.remove(table) self.basket_size += 1 @@ -49,21 +52,16 @@ class Waiter(Object): def memory_is_full(self) -> bool: return self.memory_size == 0 - def collect_order(self, table): + def collect_order(self, table, current_time): if self.memory_is_full(): return if table.agent_role == "order": - table.set_wait() + table.set_wait(current_time) self.memory.append(table) self.memory_size -= 1 - def battary_status(self) -> str: - if self.battery >= 200: - return (2, "hight") - elif self.battery >= 100: - return (1, "medium") - else: - return (0, "low") + def battery_status(self) -> int: + return 1 if self.battery >= 100 else 0 def recharge(self): self.battery = 300 @@ -79,3 +77,10 @@ class Waiter(Object): self.position[0] += self.orientation - 2 # x (-1 or +1) else: # y (0 or 2) self.position[1] += self.orientation - 1 # y (-1 or +1) + + def chechNeighbor(self, n, r=1): + + cond_x = abs(self.position[0] - n.position[0]) <= r + cond_y = abs(self.position[1] - n.position[1]) <= r + + return cond_x and cond_y