From e432776ccf58af5bc5f76eb54b786af739920b79 Mon Sep 17 00:00:00 2001 From: Nicholas Pease Date: Thu, 13 Mar 2025 23:17:46 -0400 Subject: [PATCH] Initial Working --- ActionAgent.py | 38 +++++ SchoolActionAgent.py | 138 ++++++++++++++++++ __pycache__/ActionAgent.cpython-313.pyc | Bin 0 -> 1992 bytes __pycache__/SchoolActionAgent.cpython-313.pyc | Bin 0 -> 4897 bytes __pycache__/action.cpython-313.pyc | Bin 0 -> 1093 bytes __pycache__/agent.cpython-313.pyc | Bin 0 -> 2813 bytes __pycache__/agent_stat.cpython-313.pyc | Bin 0 -> 1573 bytes __pycache__/decision.cpython-313.pyc | Bin 0 -> 846 bytes __pycache__/defs.cpython-313.pyc | Bin 0 -> 1153 bytes __pycache__/ztime.cpython-313.pyc | Bin 0 -> 2982 bytes agent.py | 5 + decision.py | 11 +- defs.py | 26 ++-- 13 files changed, 203 insertions(+), 15 deletions(-) create mode 100644 ActionAgent.py create mode 100644 SchoolActionAgent.py create mode 100644 __pycache__/ActionAgent.cpython-313.pyc create mode 100644 __pycache__/SchoolActionAgent.cpython-313.pyc create mode 100644 __pycache__/action.cpython-313.pyc create mode 100644 __pycache__/agent.cpython-313.pyc create mode 100644 __pycache__/agent_stat.cpython-313.pyc create mode 100644 __pycache__/decision.cpython-313.pyc create mode 100644 __pycache__/defs.cpython-313.pyc create mode 100644 __pycache__/ztime.cpython-313.pyc diff --git a/ActionAgent.py b/ActionAgent.py new file mode 100644 index 0000000..746c613 --- /dev/null +++ b/ActionAgent.py @@ -0,0 +1,38 @@ +import random +import action +import ztime + +# Parent Class for all Action Agents +# This class is used to create an Action Agent that can be used to perform actions on a target agent. + +class ActionAgent: + def __init__(self, sourceAgent, STATS): + self.target = sourceAgent + self.stats = STATS + self.name = sourceAgent.get_name() + + # Abstract Method to Update Target Agent On Each Run + def update(self, curtime): + pass + + # Abstract Method to Initialize Stats of Target Agent + # This method is set by the child class as some agents might have + # logic behind instantiating starting stats + def stat_init(self): + pass + + + +class ActionAgent_Action: + def __init__(self, name, effects, duration): + self.name = name + self.effects = effects + self.duration = duration + + # Performs Bulk Actions on the Target Agent + def act(self, targetAgent, cur_time): + for stat in self.effects: + targetAgent.change_stat(stat, self.effects[stat]) + + actionTime = ztime.Time(cur_time.minute + self.duration) + targetAgent.set_action(action.Action(self.name, 0, actionTime)) \ No newline at end of file diff --git a/SchoolActionAgent.py b/SchoolActionAgent.py new file mode 100644 index 0000000..706c22e --- /dev/null +++ b/SchoolActionAgent.py @@ -0,0 +1,138 @@ +import ActionAgent +import random + +# SchoolActionAgent class inherits from ActionAgent class and is used to perform actions on a target agent. + +class SchoolActionAgent(ActionAgent.ActionAgent): + + location_enums = { + "home": 0, + "school": 1, + "work": 2, + "OHOP": 3, + } + + actions = { + 'study': ActionAgent.ActionAgent_Action('study', {'purpose': 2, 'bored': 2, 'tired': 1, 'accomplished': 2, 'lazy': -2}, 10), + 'work': ActionAgent.ActionAgent_Action('work', {'money': 5, 'bored': 2, 'tired': 1, 'accomplished': 2, 'lazy': -2}, 15), + 'socialize': ActionAgent.ActionAgent_Action('socialize', {'lonely': -2, 'bored': -1, 'sad': -1}, 10), + 'exercise': ActionAgent.ActionAgent_Action('exercise', {'tired': 3, 'hungry': 2, 'accomplished': 2, 'lazy': -4}, 15), + 'relax': ActionAgent.ActionAgent_Action('relax', {'bored': 2, 'tired': -1, 'sad': -1, 'accomplished': -1}, 25), + 'eat': ActionAgent.ActionAgent_Action('eat', {'hungry': -5, 'tired': 1, 'sad': -1}, 10), + 'sleep': ActionAgent.ActionAgent_Action('sleep', {'tired': -5, 'accomplished': -1, 'sad': -1}, 60), + 'party': ActionAgent.ActionAgent_Action('party', {'bored': -2, 'lonely': -2, 'tired': 2, 'hungry': 2, 'money': -35}, 30), + } + + actionsAvailableAtLocation = { + "home": ['eat', 'sleep', 'relax'], + "school": ['study', 'socialize', 'exercise'], + "work": ['socialize', 'work'], + "OHOP": ['socialize', 'party'] + } + + statBasedActions = { + "hungry": ['eat'], + "tired": ['sleep', 'relax'], + "bored": ['study', 'work', 'socialize', 'exercise', 'relax', 'party'], + "lonely": ['socialize', 'party'], + "sad": ['socialize', 'party', 'relax'], + "money": ['work', 'study'], + "accomplished": ['study', 'work', 'exercise'], + "lazy": ['study', 'work', 'exercise'], + "frustrated": ['study', 'work', 'exercise'], + "purpose": ['study', 'work', 'exercise'], + } + + # List order is priority order for the stats + # cutoff is the max value for the stat which it will be triggered + statLimitsAndPriorities = { + "hungry": 10, + "tired": 12, + "bored": 8, + "money": 100, + "lonely": 10, + "sad": 10, + "frustrated": 10, + "lazy": 10, + "accomplished": 10, + "purpose": 10, + } + + # 24 entries, one for each hour of the day + # 0000, 0100, 0200, 0300, 0400, 0500, + # 0600, 0700, 0800, 0900, 1000, 1100, + # 1200, 1300, 1400, 1500, 1600, 1700, + # 1800, 1900, 2000, 2100, 2200, 2300 + schedule = { + "Sunday": ['home','home','home','home','home','home', + 'home','home','work','work','work','work', + 'work','work','work','work','work','work', + 'work','home','home','home','home','home'], + "Monday": ['home','home','home','home','home','home', + 'home','home','school','school','school','school', + 'school','school','school','school','school','school', + 'school','home','home','home','home','home'], + "Tuesday": ['home','home','home','home','home','home', + 'home','home','school','school','school','school', + 'school','school','school','school','school','school', + 'school','home','home','home','home','home'], + "Wednesday": ['home','home','home','home','home','home', + 'home','home','school','school','school','school', + 'school','school','school','school','school','school', + 'school','home','home','home','home','home'], + "Thursday": ['home','home','home','home','home','home', + 'home','home','school','school','school','school', + 'school','school','school','school','school','school', + 'school','home','OHOP','OHOP','OHOP','OHOP'], + "Friday": ['home','home','home','home','home','home', + 'home','home','school','school','school','school', + 'school','school','school','school','school','school', + 'school','home','OHOP','OHOP','OHOP','OHOP'], + "Saturday": ['home','home','home','home','home','home', + 'home','home','home','home','home','home', + 'home','home','home','home','home','home', + 'home','home','home','home','home','home'] + } + + # Update Target Agent on Each Run + def update(self, curtime): + self.target.update_action(curtime) + if self.target.is_idle(): + # Evaluate according to current location + currentLocation = self.schedule[curtime.day_of_week()][curtime.hour()] + + self.target.get_stat('location').index = self.location_enums[currentLocation] + + locationActions = self.actionsAvailableAtLocation[currentLocation] + + # Evaluate according to current stats + for statName in self.statLimitsAndPriorities: + # If the stat is above the threshold, perform an action based on the stat name + # and the current location of the target agent + if self.target.get_stat(statName).get_value() >= self.statLimitsAndPriorities[statName]: + # Get the list of actions that can be performed based on the stat name + actions = self.statBasedActions[statName] + + # Find the intersection of the two lists to get the available actions + availableActions = list(set(actions) & set(locationActions)) + + # If there are available actions, choose one at random and perform it + if len(availableActions) > 0: + action = self.actions[random.choice(availableActions)] + action.act(self.target, curtime) + return + # If no stat-based action is performed, perform a location-based action + if len(locationActions) > 0: + action = self.actions[random.choice(locationActions)] + action.act(self.target, curtime) + return + + # Initializes the Target Agent's Stats + # This particular agent can start at any random point + # as any day for a college student could be like any other + def stat_init(self): + for stat in self.stats: + randomStart = random.randint(0, len(self.stats[stat]) - 1) + self.target.change_stat(stat, randomStart) + + \ No newline at end of file diff --git a/__pycache__/ActionAgent.cpython-313.pyc b/__pycache__/ActionAgent.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5eca94a0c1adc020de894a1034acc44a7427f73e GIT binary patch literal 1992 zcmaJ>&2Jk;6rc5ecw?`de%NW0K(hG&Wz#yUL?b>_J|ZG$p;*WpOKBTvwQM$9OZK|V zu8Tmb#0lk)UJzC#1frZE;?Qzx|BWbuSi_N1ZZxLqmG@?Ljbjy_?BC41nR##az2AGi zLLo;$e))bs*jEVo6Bnt#noDmLh7PfbEqzF+7$qw4b%tif$tt-YMNNQ45P4)kd%gMAx8hx7q*_A4m!nD|> zfgdw9irqM3I?SEWZTd_>v}C44eq)Q}qgI=GK7Sok?X{IPJ3?EgX|i+gzOnH!Z3WbA zHtS)q;kBZRufJBl?UrwT_R7YPqjd`v}1vHL|_i_p&nXJrO*5Y(EK;^*Do~sgh*JcwOQ6KCW(@NOe_Y-H?DkQbleL zi7nNX<>Xsa4iHm4G!q_4sl0ueYi;W3`{BlYO&_m`(X_}R+eJxq%R^&AxtCiE^rZNCc;p;&!P?iD=)&#igKTMYN+ZJNt8P7TPoA28*a9m zbP1Mp5dm4oZx5Me`X$k%-lDI<5hPDE5Ac^PNzxyrz<4}l5kcfG F{{a@dhMfQa literal 0 HcmV?d00001 diff --git a/__pycache__/SchoolActionAgent.cpython-313.pyc b/__pycache__/SchoolActionAgent.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1e8a5d76e6a882fcc4bee3ffd96f99250d805b35 GIT binary patch literal 4897 zcmc&1TW}NCb@g6La`ZOB4sOmmASium^|Po z9nznWnPdX}fpj_(x6Mr5uVy-#jywG*pSy@u>PU=?hFJ#Hn| zDciVvrC=2Y>kitZ$;H>eKDJVYJHgJbxP!Qa-K`vP3;h6_g#q$ZW-#^~ z^$Dq&d_J2wjkWF$qa*ddJwWFA*BAz&D$Y|KinGx?lE?JAR@)=PQvI<=0CjGd-7 zRV$?DD*t};=uyl@9kT^8n^z@jJD(>~n%XoOKW;IV%I9XYvN{7ZYgW8GSHUtPE;NK? z1+unWUXkVq76I`g@FNHSsCWR-CN-au#jJc;s(`UaF$=Y4K_Xifp%zt4Q(k&QA}Lvw zD*r+jD^o=6GX*6>=9U#w8b!J*^JgKlBiYw$?T8_l9)@#1Y6EZ(rnDK z!b@DQ>}SMTk!W-B-%-+5RS#{XCe9SKATMGuo-`u~gFwOv+7WC)(1~Cxf^7i6*75?< zVS$M@q+<4sb=5R5Uc{M&x&~$(?Z%ud_Q}G+{AOW6msdp3IZXoZ!4vFxBj+FI zq}Gv*IjCJIC~0wy+E3-#*YQ$8QgNZ_Wht#xQU0Zw0%42x7l@2RUJ$hc0W5`msXiQl zV2gVAGk_m)6YJi)^6&4$_8l02D@rhH7gdN23L__8sJ9$ z$Z?7lsB4Mb_&Sy+to1mLZ(1u--|PC;Cak#2eP3`~8qWOGAr@)uVyi}#Pq9s6uVQ1e zqmE73>h@QxS?X)OkOOsGu}@-WWn;o#Ulq*zKCIcl!t6(DEGs^fUG>TM({*h5+r=$w z!~H0Z3CB2gz*S@Ym_nY`@;wTfn&DgkSNPf3N!3sB@PF-NL(~7}V+MT8!1Xjr`D{KV z;^TrnDPlot*F=($G}>I4O~ciZ6xq{*I%G8|r?V2};dLRU3xK(x%#-=)1YT()E-g*s{$sGW#PI-jFIQf;IV$EPatv~Tdc}t8EHWy!*DPYq zMr(uaI=XM_G|6X(n9IpZW-66e2Zr|cUKD$0F7;2XdDX>dq0?3)PQz=dRmyoVpw2)s zT;*N=Z|=XUgbc67ngmdhY)Zs1p#jiJY#l~EdsL9 zO$PmU6gr4$vepwgZrs|?&_LjKplK3sMm9F;D_G~tU9K0NHT6!zZk@(GQBwn-^=_*l z`|DCs?$l|H^(R*Utm+Bgi!nR?NDwuZJP$MK!0M3|jU0s;8Nel1YDQEt602u;myY0? z6-B74T51Bc4o$`IKoyWNq&FcwDJ!x@+t#LagZM7+s%RH78#3y}4_~}??CaL(t@zL5 z^FBxs>+hYvbACQx>^uxfyqGY0o-ek(aP36d?X70)R>QsZ_OZXZpL<~ALR$!4d%xmF zV;;&UlM0k>GD)4uWG;_=3vhQbd7&U?YZOm1IV}@a%gTzR!1DRqW%?= zWEE%KfE)l;au~ru1bYxbd^vI!01YKsu;f%y)HEWWFKChq`X`YAePl5rm>Ue>F{ocd zy8V=c%JTWk{y^F5ga7+Ywif%<(IqeE?nc zT*7?R3_0sRW-fc%O5XTgZ+!mZqVLWb{p>4xHm6^jGxl88-+HI$y;2TEOQHU|q5g#v zcaG?1C-pQ|sV=`>3}wr~Ev4Y@yTRQH`|kMkAHA&0uj$%Fx zM1N&US7wc&3%Xh;`Yx6|ttC(IT~F`4v}gv)FP}9AU(u(|6+Ow7g1dpBpHCY@lAe)^ zzSq7_J0s}llKSge{nBM)=uQ3YE4ZtK`M$5oW50U*L74MBL;V9Fb7NZfZo_Xz_il$U zb=h@m1mYCje7(Eu-+W__?%PUzr^vP*4upVj3-Gl)*lr0Ne85`*hfM&334|?2 z5r&Q+3@{ivYP#Rw;k92Kd2rYiv2FV91(U_Lr+V;s=1@aF!K25#uO`9w6NB>ms~LNc qq<{t6kcw}gC5y>q`ik>^#WjD+A2FNu+;Ek`eMY$NI}T^ez<&UDVC=5| literal 0 HcmV?d00001 diff --git a/__pycache__/action.cpython-313.pyc b/__pycache__/action.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..224e27df7c634611ba2a43caa28f832ae179a483 GIT binary patch literal 1093 zcmbVK&ubG=5PmPa+icUuZLMuX50Xl0vtWv#f)*=?M{6RE#k?6d~aqlJM+ztQMFoODBqXA1z$O1Kgbx1 zUMQmmDt+cKhd*MFzZ`gtU1LsZg*n>oQXUDVfZO^R87(h&<%pS;jIQI;XB`IIVNh~7 zXp{kTM?*9m9dXJr5X;UKm_gY#<s^k@K?}l&qd@_(ymA2lB|6r zD(Ep5umNw`n$+Snkd-uZArn~``_lB29i`d2)Kfp&1rrm&Gyx4LrNVU-x&Cmg^BB4T zxN#gNLC5Q+x9;9)zI2;=uWoi+#dY)fYiSA*CSfLowDJZv#}0$?%_F>JCksml=E=(1 zfpt==f2@ws$-D_$G$uV>MWfH!b5AJbFWJJVH5?hb2VBt|lwbxeX?i_SZKaf`UWZxo zR>D+3FG-NToG-|oyQt^XLO^=f^5^DZ^Vr%rGB<#H|3U7{gd9-QaGCO7q*KP)JKe;W zm(ID6O&weyvVFLHY&DL|Mowh1UyLGMUBU_~efE_7Yf8n0Y_3*|kIGM<7Go&~ekSNJ z#ykqUiXkZl(?AwJ?SI&RZz&G8riOifDlL1O_MW)0k9)5O5qEcb5$Ur+yy&@6@sB0M zZU|`>g^8bZg#ao6R|u8~s13O&1RiJ>dcw^z2%q&bKNVuYRB5$HprcekO1gybjtxti zu{t!l(HQF7uzw-c@=quTZ(onyY)=|*=^ RHgn2mf9n-q8!`m-*&m`|+R*?2 literal 0 HcmV?d00001 diff --git a/__pycache__/agent.cpython-313.pyc b/__pycache__/agent.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7e56fabff3718c22dff682512fea3ea04ed1335f GIT binary patch literal 2813 zcmbtW-D}%c6u+{iSn)^X)XRrm*OeV7#3LzXTOez)u#c`VHo}MqSZmH5|#Zb=z{HxlL(jq76Gu3t%}20{OsQT*Q{82B`ryO$)$jDx$KSud z&v&|WaOB98mTP63QfG>LeaE+vr0p!JXl?umZTY) z7<05Suoed1<#p6JYRdEfj|F@lRm^VT)P@WfM~TTR2-3tM9x1~W6r0d)+qm`(o}8UK7=bqDx9Y{E!As=Uu$ylkWsrlQfjfogOY9=e$*3g)GtlW z!EV-j0N`{)IoW7zv?4hyKS)7a$+eW+uj_4PrKPNNGP$3`)5t}T#8&~L3*$2B9~r3h z>%^*&ulxgpCu>Y7=o_A(PR!-&nKzN?;CKLjXQif+Ys$HCB5zqW{OBFkYaG>i=_H>W zsCIZB7xu8gc@4D{wWX*{SsiE956=@nJjioota)}{4p5ySr{OB_-pJ#;IIyfi4NbXsRzPiam zqF$GE#4n&qw&A}H^nX0eq5p>|t=3D2@b`l{QA)o()GOW-h0;f5X4#c;&8XEWWeV=} z4g$6oh9%2#2#W~sB5;k|#XHmwyNB=*fHOmR2QL??ZriN<@X$6aO3z)=ydg3aCI>t; zt@{8!kk^@fXFmOx6q2&txF9Wd#ej6XJ0VEPt|&-15pH$GptR7H1pqL!T~U+@-LN3# z{zj8Wg>MU~=WT|6=X3lRo(KPTkHYMVfL&W02e_)5Pm1If5g0y?GgaK=3j6< B6G8w0 literal 0 HcmV?d00001 diff --git a/__pycache__/agent_stat.cpython-313.pyc b/__pycache__/agent_stat.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..829f149d4ac359a1ea900d8bf997c986646c47dd GIT binary patch literal 1573 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