From 2f6b751bec1a9ebbd66ecadf24d2e7e1f157aaa7 Mon Sep 17 00:00:00 2001 From: tpc9000 Date: Mon, 16 May 2022 00:48:48 +0100 Subject: [PATCH] Better place file --- TestPlace.rbxl | Bin 47844 -> 0 bytes build.rbxlx | 1861 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1861 insertions(+) delete mode 100644 TestPlace.rbxl create mode 100644 build.rbxlx diff --git a/TestPlace.rbxl b/TestPlace.rbxl deleted file mode 100644 index d8f94bf50363e5b8b7933b7552530bf2ad3d867b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47844 zcmb4s2Yg(`@$l@vcKw~YtL{m1k!+oGy}0VCyDco)*nqK5(#bw7>BQa11!FK^NFW6W zCA8360t84x2}x)P1PCEOLdZXbIH7$30-}TjlHhOVohADuVTb+co$lSu?C#9Y%+Aiv z+gp&!cJ*a<)t!IPYMNpK2m!z@yhch#N9WoyyqR|BgLeAQBrtK%-@>xh9i21qv-(2- z!xi+#j#p$L)wjEl>CShgb2~EKX~v2mPcp*M@TRu-0Sv43l?AUzKA$d(fqz_wZ^UnB zjlkD%d{>EAtYb+$(_P492U5A+WAH3G-eiQT8No9M_)aFgRDU{`feWaKms1Ob zM`TbLo=n}KjuGzhnSq|{&IvIuCCr%!*MJw5;YtNxX-s0-zP_|h{20pboPc8nM9?{w z9Vn!C6(SSiJ*?x=NNRXd87?#8s|~MMHoHBO9y^u~=@?V+`$>3F88)I6={%07yKTw?A&H1O0>d&~ZpK#?uO&Dd?Wz3O%J0`vNb;xwD#K+E5ojE|cu_ehQrD?3 zh)_rkpn%Ib-0ab(M7xc1rWYE#QkqFK2ytaKB#a>T-M~4P2S+PW5F@(}i$M6SwPVrK3zJME}ZcPdrsfjluZ6j#0`j zk|pBS%AwTp>8>#dztIs&39mw6B79Y0Ey%pGTb^E5=vom6!6H1e*BWzHN7Y zyoLO(j?skQ)8JAWo-`F-sdMdpsom*ZqJO9_RmkSXF!3rKkwB?iREABYKV&hAh@<*g zu7kSpi&ng-w2|hB*i?Hi+nvtmN5p#KKK@q6FEwdo&uPrCf?b*ZOkp>^=^q@cN`KWc zONE29kc8v#a3wRj{HX1%)KN;UpoZY28HZ6ikRhm1BkJ*@j#MhObVpKKl%+A;dsIgt zE)WN)3|nZ}$OLwz51HDL+Mm&ZNft(xPa>K~lc?TwuGoeJos^Bf@8tt&`t8K(_QRVQ9efyuGJAr zZ5vTVW@n^+T@%I-as7BA<|0b5TRAkI>r;J0=@F8DFdmvF_#(+eJ((;S!;vmsqj#y) zpKv0ygjZ)e)jz6QujzoL0^Wi^B;cJWM}ygM)5~{sl+t;W^pX^}Go8z&GNZ19Onok8 zJZUje*qP3cRkk-K=!+Xci+$NuAn#+02z^6G(Gcjc$a(ONM6NS~e$yCuyAEHfoMekh zIXkmM-P?4}XbjTNCTPSr(#XzqdSDFv*LC<(-6W$=y19Ps7~mi4z@@?yKtR&X^_g5@ zDAlL;X;o?f?L}^6fdAhFZ3GXsaeOwnJwKQ_?8O3WU%RHAEHc$fWte6Q-SdeIW>%zk z>(dwkm70D6B9us>kjr!p6?C5p=flit=~0YQ*&z(7do{XAY$DH^8qpBo@Oa@(QMa-~ zx$d;?RWIuqv1)qHsS7khRE9adQWKr2t@)$$g1nG*1DR8Y(#v`r!0tQ1Pr0oATk~e$ zHQo8zf5wwaH64r8Qb{I^QBq0Zj$9CoCzHT+6Y@WjIa2*m$Rv=lWd8B!WD@s|MP{im z62>T*x>r8II%Yh%g!V`zEA6)u_g&+O;+K106EW7ZWOU`~}e_huW z;za2(fNCU86xVSbLtUrfLWssdm+ev;O|WFqi?E%$2h-%Q0aaRRa0!siXfl)T>siyg zK9k-lo{MfEL6**o1W6Y32uSo%57%32rwE!RiDb5WC~xQI96$5!b1ryt-~L$}^{UBZ zA{n4EERFPbX*$(|PW<+C57%iTca$Kh3@ZdhrjMXH(9=(gXW|`obUf{&f~F6FzVIbL z4G|6-465*ph!(*g!(b4|7}2_a#nDBJYCM)EmGYJ_$c#mYy1M$(G@B&`QuwY1;}b|^ zsVhvtG;Axgsh)VcH#O9!&woV*lPRZ*N*h6n@Vstu={;3Eo@w;W(V0eF8?my7D!u3^ z+DH|SVcC7R9i3$)SjS=+eLvERVNMw2SBx-97cX*q$iJdejB8L|boxo6i2ex!9OIcn z)gPTH?-5(^vgqEZa;jIxnDaAM;Gv7Yfvq{_bt6A6AC6HA#wAY++w#nG8U z;K!nMHp0xr zi^^~{VT{tMPvR4V*2a@dXkUcfk!DKBC615Cds`7=*mR_4_J2pGmgbIQQA_occSdE{ zP8g%qQs|;IAUK{*Lii$djxFdKCl)g-Q03DS)2Jp!t@5>k6z*192DCFts zC`I68E}cFSp;Q$J;aL3E5N3om!zy8nGLon&Hgg0k$HNoY|H{je`j5g(0vW^0`<@xg zOPbLr>_X);8)^CH(#Z~O_>mkq5%4R7gXLfap^XwtlYTPOhvCIE#wHI1O{?Izc74N% zkZJsqcu_fMAmj-sXHdPRxP?L}G{7Sh@4F+-6=^@RTPGvfsd!O&Um&Tyc+R(6tDSs-HO*jytd=@I$moSJbJHQ&ejWcs#HcO zF7vs(!|x`>7-buEcFdL#VQBdAVX2}vCg2r*ibeN>sRuLyRE9ahQwv9t5&{{+r2E&5 zl@jW~;h-?pPYzb3zl*fZaz<5E^~g1zN`gKXUrJ|I!k`&0nxp-$0o;iY_u(y7P2xHW zFDh*Whuak@x|p~TvMtkF*c{Ck3fX?LsH26@NOVZ*s0>#U9x2BWc%9k7aY)q847{ie zR}m6PVParMCYK%PN5bQo-rk`+ZcEYfo1jK|I3lUT)r3V0_am@62GiZRcZ0$ABhgB2 z8ljOxnb|Z#=b|5y#xVc`g_J5&U=|8>{(K#%g}4jiZ*2 zrOTyiLl~nYNT?I+OOGdou)YWzM_MUiV=1)+GKSjw9vMq5$+dt&vOT*R#OeDs#6avi` zvIFVSYDo!b(xmba6G4)raRj7Zxw#VBbv&xPbPgaCVmam}b?4HB(wd3YREEn5jAqag zuu+RW@&VOc8p0wRQ_opShAHRuzDNTYG~Inse#X- z7I(WbjJ`6pBQ;8`en%*!8azTqJd^6r4)oxb^br)#zz#IwMdf2Nfs*rdB&c3JcD7wd zr0NNZ27}75NO$_Io5IYf?$JS-Se%kNkY43s9IBJ>h#6FdIn_xTnapMmb8kFVG=>DC z*Ea%BwN0!pOZrmXnRHHvCUrSAkV_3@j$qd3R9l2PTy625s-0L#mgF!k zY?M(GD~S%HS4m2u-%*7Vb9G6Mk|uO$;%f1hz)3-AJgE#bB1^z)S@&UyFP)c12uBFL zwnNZV6&VzgUn(CN36LC!mFZOP?!$dGRY3b_Ux`~Jyi`6k5C&<+N}S1cwbUChmLH*{ z1TtQ(R;6-i0Y=$1F$ao3qZ}ySe*OrQs^!EZpu^}j(g=*Ib{TXjJI#L5Y zqdlFtVFDR#m~N=HiA9#a{Tz{8jAs}@e zKb@4o>J89zq7U9tcsr2Yd6*k`L!cVge|Q-%qQL)aG-pKOOI)AvydW!bC>iLGJ2N_P z6Z7H_aM2SPg&jZ0G;@!@mUt!OC6~;9dFf&%QHPm>&W>+L&{5Kg0$}tFAmQJqZN-g*EAoE zz>mxYqu@vF2O8EDP=w-=2lCe|N`Fq;GHB@Y72KOBTG>M`gs5J~}A zSIG3ygb_i_+)d72EIYW{9XFexy(N!PxO&(U8!EWtCXCP-fz`mv#Ij=S+8wuAfT6jW z3-=_meLXAFy$Go~K(YbD-u96Dan4!_P`cyxdca^aLpex6S256^0rW596~9`yq;1jrsjR zwFE%jfjxT{*u@O41{i`aJ+n4{4kOA>f-=F$?1j@|OX5^ax5Ol4!qNa1LR%8E!Y>54 zNQaybY><2KleP5b5PPp?u4My-zp&z_!PU%AB&{3TR;Rb7kpJBy%=!mv?J zHPDWGqS=9(L;Qvnuy1n*ri@_H7s+QW3dh?dAXwgSA+aIbT}&ayT@}R5r5L@3&(uUP&6EJCR|=mI1&%WJigf8Iq*T1KN*d9lR>vL9*%pQK94`*jKsZh zrzhYI#be2UD;e?-T+-_fhmvlWGZBp^oW8i%>x={v0cRu@^d!RGfGZO6>h;B9-cZ8p zcShZDuhSQZC7jVnJnD>l1HPy`7)it<(K&EWB<4v3JU)*z;l3R{ltNzYUohfxM*Q(; zDCF}-yx!Zf)kHAt_a(hyr`wyr+I)VWGa3v=o&JzFko1RwL03H7iEyq^A{oJUoJn8M zhj1>JGa8DDlfe+ymkhg{VNWvYbh-SIP&nd^dn0jz zLjn*z>2`+VQNPpY4SSrSa0r289&bG04*LRLzs{AgKkiRN6V8YiS?CKSg3gF9k#vS) z(WobY40i=wbKt{Dk3a4WMIy-ma5U`nMMDS@Pk5YhcibHcgnaS1cNQDA;5fu%9#7mE zb4LPBpUWF{hP{a354j^O7IlZ+|G~=QzGx)rawnYeSR#gvVmG59f6VFk#gjp=*X{C# z+nFmC_SWzZts!T~=ZZUhIOfi9#OKGZd3=FzEas0UUqnjXNMYO^NjQViWC$w@#gGD5 z$mvhUVu^$&7;yP~FTzI^u5cpm@%aMIcrc8Oy0N`*ARclCym1$D&*KXHi+xz>4o5vm z7k0>tGUSUU+|Ec6--UwyXdoE&gaQ#9?n+lM5cIj-J{+z94wpL`K{{heXTtCGC8G(C z-{T=deTitq9rJmeu2>|B>~MLV$Wf2ekMEF%U@{SV4jT;ylgU^lgpDFPif|Oc;>ozv zg`(ySM#8>O;y>_FrN`rnyFECZE)+7SFX#$8!~SR-De$0-1#rB5XTS%QF}KGb^@g0m zU;^6=M1n{Q4qP-GLpDV{F`vhcL#6nViC`pvBOgn6P?AEfm^12fpCw!pc88PUNYIIC2BkHT*TWIT*p_V%|tJKr9P|lkrdjMaCUSqM!u> zQB*0P&*=>z@vcZB>2h^q#KjZDM%~y(%pXDdjzpejue#%?E&;44gd&u9hJR4y^T$y* z-6;Fs0E%NGh%_c*US}MIBjgJNLlI8`c_GC@-dG$}!x`}SQHGMqs55i|dpweicu}$4 zuBZo1!-thougC57qVl=iF|00G6h>w}X~& z{y$-h6V*K&UCtYl-YMd+5KDTKzE~jSj6@RHV9Xz@mVWJaqH@P$v9J$y=t<#GZy*v1 z#&Cp?{-pwO-x)%ocLtM@h~E|PCEQWuSVbrh^~Ze?XDkv&Lm5Jg5b}9qXf1<*m^T^^ z1cEPu=5;lRAKKigiGfH2d7lg*6xuQ`TsVpL z7YPh|e5gtBHt8O>%N>n+lPIL#Y6%?Thb$C{qn@A`Vnul)#wJ{-n`Ki~}beBrqGS{Ti2G=iGt3;EpPo7o3dF;65C z3x%Rimj|`W7fO1Zk!Z~A^rMM(MG!C$2t0>yZg0$h=Zx{}^#EbK(x!1|)01k&fi4g>;8>_pV#e_TXkm-PE%IHaf=*r3lJMx*12 zx}B)jNuMhiNAszxZ88u=yX|ti0s&-}JAh^)>_$yPvlj4p1EHXMB?@5>RVfxlt;hON zYT{@*BLN>Ou-EUxAq#lW%HEEB3kDJqcQg^ffx()5;V2HI-;cr;jYe_i@gdWcdpyao z*N@8QOrm-|8jGS$jQT=Ol)cL#<_$!X^}IdqMQVQ_eP~Vw6R1KSoKV6(BscCsiA|uK z`XbNpVUs@@jwR72a(~i;HVxIn74o3u;5>=l zkNcc{TbY{T_evIW{by=Agw0#GKw3s-iFI z3+&*(PN13xeQ0A`=r+uON29Jp0Ii4{ZA92)_&^FHD}rbh0|`$Ib=~Xp#BmOMK8W6i z+mEfGWCnePVF}*_aWv6w@cDuqnpYarB6=ydNZQiM;4T{`d>{7B>Wj zas%2Pt4YJ6``Ow;EtfuZD3eP!=Jn?_=N+HQ6p~qd>MYaO(^x+fqV-Kp^L?;}p4;|< zHGuB18y4fM3#PPoq_=?0gC5%!Sh;t8RCo#gT;lki23DgJcqcyK$aoF3u1v+ChBt#8 zg$v0~Cy#tT*p<0pYJc7Wj86D` zDCdnJ&x34n8E`MCqJFRpH5*0RJ`Fne7bb+d|LfDolXrA@VRO&HK3)YikcNSX+ErO>q!sh(%tBq z_h`5()C#noY3c66tsO0cPa9*_ztyx+RHnhq72;LGjlDwydU{P`(`KQ)FWa5!>j$-r zv9nw7=~WDfwwRcw$!hX})|0+gZ0Jf~ES9%Az**nC34p@~hD@QcKEEwH)YqeRBLP}U z>!L;7kKiEJc4i9Ow1F&sflrPP6!HmNYjNP;ffv_chDHXT^2b5nv-d&}@zXkBA?EGT z7XAh*8{Hs1OrPe`yKuc#Xmr7rrj|msWgshTYe6YV^#wt6Qx60`_y^Lv=7BOFI=j*> z+3hDV{5YH*=-CApD{C)oOKZIj={^lpiYUaZWqUtls)bEijBcvR=5Y&aEo4k4Ha@_oE{24BGQXc)td}Low(^Ip|7j z1Gvm7U`3i!>&19vI;UlNHB1J{R6$$1Woyg%@G*188#gp-ZX~2h!}VjX5^_Gg$8r9f zDBdV?2SgThE^6#Feq@-Z?djaxtkJ@2o;EPl-<8faYn@pOlr?Fjp1ok+eeF zR$NZL3yKH$*&z6UZH2l83%>&9Q=qFWy&HGb!t$9|#lY6Wwj5fM1t1TBIu9g%8u0(X zy_8XdUFgheP7gCogSNH!ko;tJQGcqittHi!Z*+nrNHJtZV?FQ(p&i-dZu%!HA4upz z^oT5=*p!10N9b(aJw;;$E44+MD-03Tv}7IcXx8wNJKVAQli`5YlTGIbW*4;l(BNS9 zB9t*a5~A=`{*5l&FWi~R_2m8BmC|yL0pF=t|22?T0(WsfmqP<*1>Ue{@2{Bh9pKHl zhnvn1^(D+a3T55DVO(U$8bc$LEt?|v7HaPIAl{qJX&4b5W;_S$oo;OYX5c+gexd@b z1w5Oyg~wUSPgKFu46GXJw7D68H$Zs+g!h530`{%|Uj?BW*fyc{W_gvtim6c6cg@mD z1BMx;a&BEgZ`4m@QCX-67a{$kX|ck0iR=jm`vix3g|4^;d@!-q0H?uOjeBs8^zCh4 z0KyFn)l|qzdgWBG42#1uJmN ziy;3-@AE~hM(9TMzZ4&|+^B!zX9guCR%Vx3cr`;c{F2NMN^M#uuYIY^wIkJciMg(j zoeHcG?6Y$iuT#`EX5bq|7e0;qFUhb^kZ+=TFjB+Uh|)e00vIadRlWv^Sngxo4NAt0 zrxz&gT>T6Tf$pKg`?C5N*mwq{tHAIys24GTO9BsqNt3L#+&CAE%S5#xnqiqJ?~tq? zW1xy5qt>F}|BT&g;##AkP)f$_yZ(J?UIjfD1vMBdGs^`ZDju z28a6~vQekCe1Ws;Ns;}i$nr8<0NtXkc` z`P5K12_r9s zPm#_SSuHDX?vc%ZAw1!Sz}}LSOBD+2wc_44J_YO-&6q5^t{m3t^7~aTn;?b)_Vu4D z$FEKI40V^)xU@ocOL-h;xxN@@=ag%i!cE3}Z^7B0%@g$+P{HVpW352-7!=NMtb^%m3H3P2}WIHII zfbcCA#c4Bh8W%p`;)cm}*}=EUTeAZvOjh|lB3x;Zb~7_ZY3~x$r@+7?pgaYzvr1gX z)SrQY{gA2O6YcyS#-8Bzbi{}Tv5L&H;k!)zttiAcH*1@Xf|8^_@4 ze|vR#_K!C9m`$|{26Yjun!GtC8nB{20ZWO{TE=TDtRKnjaS%gbS&fmNm<3GR#)sKeD+(QU5J-VP>8BANfqQ z0PNB#^QYBt0TVkF6_n$II_5Z)DP=-4y6B2Ym;tI#{PzZqe~X}s%5vd6Fqg^v9*z|9 zXJz=QUHvYGz!%B}8rbQADk+2NKQKMyQBi#w47N7}Wx2>-7vvyQ9$~7i%$I%y>i-i3 zRZ+ga*snSFSh#9dHmEUHu3D7usDH)XjguwSs_d*6_^+z?f?2RjV-*G$`u0YfFl(lu z8QRd~4F1CuMd!NyIb&2y73%#{te?vKII#UwX1}nVi8_LIxo}^Vf!$sUH^|DvRw$SF z=?3rW^v-D@<9sytz;w9*xLX9A#D6)HH`EGY5Eh8`$cjmbDpaFJh)Gef;^M9Fw7tx| zwXQQ#&=xFg1ay(>{{s9o@Mzhgo*B?Ni@!SwzM}DqfFCr0$HC`Tv%G?Mw>X%YRs+H% zz^&jqF;eKSWz9@YfaHr5n!k>-Gqho?b31^?K>AD;HXAv5F&MAEk_WO+CkK<$hE?#v0M& z*9$upSZ(Fqqz1+fCh?t0>*nbwTk0I({8}>{uP55z{dKZIcpKomwLCOgyw@t7CE&Q! z!_Tbf86mFi-yn&M;|7g^Y0cWKDW))(_gWpEy;dV%4u;dMsvl=uTr7kc`Q&*tJ?*CvGe;W)d zI4=i=$ESUBDozz9hd;?~WDA+9Y9`BKwuaU$VODK-pTpHKvpDeP!o@wA{Mp!v1&qI1 z+d9{CHa7^91@T-}wOE++kif3v(5T@8$#pA-`U+b>VE;9v`;FpT<501VXS@fIC zhO+j3jl#c``q}w^mxEH%hG|k;XWMTmV1ia29oPO;mmuB+IJNhu1_uw$1W!#{cBnA8 z4EQob43{fAYn4u?HjwFC2W>kueSKeV{6>CzW^fP}u(L2{V90tsIuf{^*@g~90+=96 z39t?r*gH;3ms+;w)(UY`HQ(3B7S*va85z*I~=t6upO~(uQ`5f%HKgpyBb0 zg3BWP(8VS(m@LBV2KJ0FBcIw^X>UGYRBxI#S)Rqza}mMCl%J6>3v(HI59V^l=2e+0 z!TgCq-BE4g4>RE-uB>G2O>Vvb*xOvZ1k}yUknPD1VZ328GoyW!_etVHlRC(2)#Yq< zQk$cB(A(!=d5K=t)#xx}L0!vcq%vCvvbpr{MQ-Nh`Ax0tE_CQ+!!KpDdn@4^61#w} z(H3al^Cf+Xx&z>TF!d7IY$dnV0IrwVW-uXpuVzfh8HAs4W1k0}Ho-Qc!88}tvm7<- zcXjGMN4+bb9=N1Q+~*LyjnWxYrIe_C7Z(j{r7};wacQ7g3r%WZGno37DLR|q2OPaM zTC3*yBUi46w#+UZ&r;#3KwV?rKkuAITtwifO}7I-LuR*&u&s!K02u=&_F@Q3@e4wez&{3d1A~9+5_>a4gF64UpjBuDb7g@!7p{7u5*Y3;cD?PPVAG8BXSxaZzRB3!+-d zma~gz33fJ!!2V_}Fr7UT#4&Ex!Wi&y&1SYHx9eV!`Z_Y0T6}hk0Y1*QHxy$P@8qzA7Yk|Y7FmUU^B=)=&8sI zLuspJemdBl$(pI|HL}eG+zBjS!*D%w2T8B82>2ifos8cEY#PS}l7vxO$0Qv7Re!YU z^J!M5`BWV&{<}~Vl?P~jBz-cIJulva9$Iz)1GX6Ye8DO{!qr==CyVnJskc{y`R7c$ z6AZycd^PE-@O{C0i>&_AM#ChyKs*POZC@&EY#{e!rq#TElQg zo-V#FZPp5y{RFcoNxx+5PasU0tDHjJFg*yyuUXVrL6si&z|SY)kW&roF?+UILUZvd zrdT|+&=zLsmh%gl;Q-(atNtE()VtwSbw4C+8$o)Nv2H86y|&u~c(w^(p0J{t&z`4V zEzA@i=G-$?+)swiw4JMu@tNx7+-!V|8{|f$n0*PD%1Fd!=WLskeVM`h>T*FHGJxP% z0=+i&dztMps9OypoM2Vb1~>!EzZLmBMYwq(!|<;Uq#I=OPfb!C7;iH2PC>ZTD9x&o zaSivF1~;~P=W1?GjdGtscv|LhT-h*~%W;H-cfpZ7bCy+_1L|JIAgl!GPhgQ8Qaz}r zanbl7XHPFTpQ*qt5l9*BJ^fYc4rMa>a%i4}Tfosc+ghOx2{ZUlz+7+Txe7})U?^4h zN>*^?$KY@~lo@=cz`so%j*??-`JJi3A1ka$gkM_KUnp}VG~Ao2)VpAk`mk(UL&(zhfxR?=M#`U=6}Itm+4Hje0;d8*$uh%xpAE z_^+}&cNJs7>HICGevA*=m0RWc)VBFykpjFY*)aYg@%QS=8^0=tdUIEfpvHq)etooJd?V{flGX{UXePN*_|8e)a{eX)cbLb@K{Qc=F3{OQo&y??F9DbV|a%ayb1iDxN$MiW5nK{BVL4|=$>rMvxSBe*wy{||r&hd}mP_}buim*ctKmU-g}*gy(eOdnq1-$N_HWWE zbO=9SeY0tO0}yVX}KB6E6pLrd{bGN6&y;Zk_=8*?3ywg&jlv zE_AzH)6gyX5~$Naf_`wg)}vOO(h1kDw-?%g-MK{F0!+fZujS}GfTL;k1BvHA* zBwPxrL7C4^$9Y+)l)lVWxwY2(wk)leO$R68vV;`RaJvMzNx}o59+V?+$HwDQ`R??< z(-;s^Y6dDmeA0xIv_p8&!XNDvmq11170X=B+L~A92;Zc*jPMCV=eAy3&;&B?{#8tz%e$)4g7vpko5bVXxVz$0j{WIoR>U+Y`z4N_d zrmZY?{$emS4MY7P)^bgWA63g1)7tQc``3DMa24?m1?$B}T9?Io$o3L+p%hjs8 zb6e&a?KpgvS{qtvJ`UIEeY<}umiMIAH>CQmoCEDp*V7XU9oI|8+qHu5e2Cw;UfY?j z^vui_ez-yF+uiTR!1tCVm^6$vti?yK((|;PsRuWp39HKtYy{kv?oEB|WH|*lq%`am z9z;Al|C7!^-0RE@br;?u9Du2A zxQUnh_+(hJk^OT~+bnHo?t8WSvxB%fh}7I<=Mg5JDeYUVb>fLx+RktKdvVh#m+4=} zgxM^z6A$AIchH_!FR-hC;cK^{A>Vw*RCZn1-u1K-{$oh=;}d)ut89_W{?fUzJ+Zkn z5?z@=1t1*q%%8$Yh7|xItRSg|N(%!1EG( z*(%+=65S-(7=*3U`1Qw2xF&zt4iyOuM6?S&ta%5lIuVEWZ&RZM%s0)1?M=bC%9~(M z-R8lKIb%DRaejZ(%lOZM4FFV}z@`amIg^Yh;o6}b-fm&I#WsaZfO<>V2KP@htp>*# z<@02971&Ha1>EnFHc7bi3ztKA4kIIT!Lr|^8n|g@i}6ZH40E`(0@eXH(t5{a{j^_d zO`+#)RB;i#1~V*LPeIezY!ua3Od#Ch#;q+cbIl zOkufH*Vl*3`MNTuG(>w*2)_^N-_V$%wq4Bl>r8!)+u$c$*sh>P+BeKzSU&$R2EMaL5MJZHOy|=aGTLEiR_qZ4%BQ!AGY6Mn%#239DqO9oAv>2 zLAJ$kZInw+b78>$A+dV%y&}I2OlwafJ&%Lj(}Qo6ewqe^UjXxf`C7n8J_93#ukYq? z&SLmrE8Hemr*X+riLt@Sy?g;*jI(t;56D-L*9f(|+2eYTsmpjT#-SfGyvZ#EtK#7N zT?W(E!zQCr%`x2q_uhM&#r0|o^UD#dRaB?y9Dsm0)xCr)k7F+qXu;)kmuPEh*;pw-CjLSqk9Lu}o^Qfn{momX&5Q*SH_sQp5dv8k6L`1pW3yitB}eJI9uQZCY(5re<)fPl=J&5<%?utCx$OY;anMA8Vmw4Up-hv zG0S0#2yIN7Q?6cu+lGG>#9OA@7k*_K-$3p0^&nqtWjka{4H52Td@(TeboE_T{x~=` zMOm>^Guy zX%w5n$xc}(tDU%(eR@yR_VhQWGECvez0H^LNV&%ZnBj(ZjDT`OQVnc9tZV7T^q9-0 zdyr4hI<3RND+GBd*sfsG>?9g0@d{=@*JCDaj?L!3#yH>7c6)4~c5!Yf{YP*UH!GRJ z5v|nqNDbj;VJw7+B^cergqF=>MZsofFSWO&Q{CJC#mbz*B=!#hc9fN;+L-#+b{G4U zS?>~=VX}OuC|=nv-{dea669W=aHA-^Sp|23`fbs~mx3ENSNhP|*#HMbadx};I5VF^ z>dYS$Do#WX46{-(`(`;{(wX{uv5LJ1jzEWIrQq@JL_K)?vM91?prTW3TO^3{%`ERx zZ(j3vaiPff%Xa5^YXplRmRZydMd$fs&YL+bTnhWo5-gGs5e*y6>>O@BNJ7r{;DpjG zN+0O5h69G_G@Hyd>Pcpq*NgmlgaF0G+3$?Fd7_E%@mzb)d~;oPXTLRtXVKzWIa5H@ z*nIVEqlLv8n*lxZaNi=G`*aeHjO96!8k2{WrP7KzGismR(uHK%OeQ!FnPw!FFr5RA zD6YU!fS4cHXL>lq_dSVyrxwmwZ_g>y_)QW#I$fTz3SODsr47;J8QZ4A^;4yDr-?6c z>GkFC&y{>Tn4jh*%s+YF$o|>PSNPR8R*LG?k|bcIOMt(=(saO11pkVQA5Ud(Fn$&F zjct_5iDZ(OrE)k>mO9kO_ z5O+yv@K{oSn@+V4RBlk?x`dmrX2K9y|Hb*!7Tkjg+U~-WicP$86@LuaLzE8VP&o!K zegw;;DJJWqpx$k(f>$JN<;+>m?wAS}RH>CFQT-*j_}V&{yqaa1`faFVmzm}BW&56| zP3mQf%fd#>FxBvy_;zl? zZC{MwF{SF2JAF!5y>VHCY3WQjYpQA6jEkDltHj*HeEO~v`GuRLr89*O8!*}K`*S#M zqV{#M&dby>G(RuN+ZA{m*RmH# zKbgTXn7Uq(SO*v%ZQvF~jVr*~6uuhZxs$~2O_B;T#b2MK;xkK<2LpQwTrmk!27bvT zd(+9S@^wtTW)d!fK|P_t#uqZ?pNsUf!7fa_`3U%5bnAv|IDEFL6X%kY#=i|RZrGha zgS}#s@d3ysmB!m;(}|4jR~Sb8&ISHwhC(O2&M>;A{;k0zEr$x_Riojf3U-OAeq3Q; zX8~+1mv3g=K-pZFZKnRKqQbGcQ?26bK>lw9+YfvPHC(o=C9EE-aG;#7$-Co2tWuvT zA28}O<%33jru?x%RBo1pA2XB1D4Z8imC6PY7f5He%6t=JQzh%<3amgixLV3tE1dkkf(pZgkm zo`=>wW$W5h^*($aP|ttW)(l0zruy_-%z$<8o&UfG(#>@P_tg& zZ|I`DN93@>VA@l$8kW%B(V zHitv}G<#&P*%8HD7(#doYK2P|nVtfcUa0(@`lFtJe)ArN|17+Lu`M8;qBw&8kp=*B ziwX*SCZWXHJGYem1~X1i?A$aDlV5l5ZQ?Tj5n#ZUf6FASp@@g|Itim8rkf=`AK0~$ zux2{PSj>kK$8@h<63(5L(~h zi?>Aa#13_3(Qa>K;u!!P5~k~sd+{x_)fwhID( zfeD6L)|V`}b0oh5wiGjc8>HWu;k%e4A{*5ca7*ZJTxpp3D-N})svNhykQKt^On4P| z)eNj6`7C$_5bUijL%5F~@(WfK$XkCx#+Z$OdGFc_UwJ;*Ws&bSA%E4rqxp zc4Zxk4@NpQfHNI%vckJS4T+OcCtLIC2D=qb7jT`YE+70qy83sN7%+)q= z5#I-DRJ;`T=HW6STTcm{^`GGSp<5_FaR%_K*Qsr|=BOh_z}C;ym15BJLnyCr7d`c! z`n6(fz4s}wsZU^R3>~=R#lbMQ zPaD-6ZCtw6#{OLeJAqweQ17%c;WS(!%{Tstq>X(^JhX&(%3w0Q4-Vf_o7I5P+e<~+ zFcXBc6n>_pJ}wc2B=CV|N_{z?s8c`GxN3#U!TC^QY{ zTtS^=bg_rQa*O5Eu5^KZD`e^v;gyBLx3{UdU5ITXIY#+%mTJ`Y)JN(+o;_!;7E#41 z&=#xTtNp$VSIATm##b9~PBIeZCd@n;s;8xosTCYC@@H0v@;5=*Cb0yQZ#GJgu$co} z_g;gk7vVNcjoE;E%ZnK0f(uk6E+bBt+ftG=IYT8RGPLY%3K)orBe`f$zyvC`z| zdq5fx{Z;Dsr5?w^YZlA=*9;eM>YXShZ-Arkc1fQ+Zr0^p_%i33D5DP%0vQdCI*7}z zji7d->vAQ!F0%OSl!kozlzzBM;x!HUur+(BL0xa1B<{n!q{bM;Rk*ScT9-~tOIaa;~ zUA8OLA6P_mOZZz7#r^+%KpJ4*nWFwlIsrUW;0|-S=f@IUR3~3%RxdZZ;WWEtl0{uQ zMO42G%Y`qaDs%Q8gXKmHKeAoS@$dt4beGzP%i&rOaHrq}QBKOLzsRPHEaH+vU4l!O zUU@a!3u?E#o$Xi-9cYoi!Fwd}?JC?bf<+44&&8C&v`f}ttiFKjQyeqg(Zm!6^-U0X z*Q8qSmnHQqNk&IS%=BBc1Rz`^3v)o&z8aH??7}Ilt#g3?P~=b0cQ!jdwZM28gR>QR z9^<fdflSrXSMvKh;&R~n4Ua*6;o-f}xO;3oqK+y-_29cLD`5I+p03K2F z0k{BD(eIYpyR&#wsN{s^@3OVWvAuPCqfh+xH1#TJ3fqm-RmCddOjZ`zT~`rd{)?iR zsf>?CXwlUtvjg?5_1c>;h2t@-=S@Q1wKeXk-*tV3eYXVDPkW(;dofSJWx&@*Ch=1# z7$a8mA~S19g)AHBXc*v>0}7hmq4%@7(Vo& z{uN9FFa>N=;fh%>&q(Xz>+Q;nyWk?OKBdo!Xuj2-Gne`SSgfnWDUlyeqh_bDvgmIu zY;h&~TYY_FqzueU7+)od=reIB@7=RN{>J{OeIq{Mc)~KFQmb!o0sC=yx7Lep`{Gt9EwU2@^=kA>Q4Zg1 zw?0ayOZYa!#3=scY8BHyI7$irD{KFh(SOHrm@?&9+lVs#3&x(qNvb;K0)X_cuikH} zox|U15Vmfv-21rPj$zgz{aN`&{2D`zdMW-CpB3MaInsu~!HXS?K|?#ELQjohGz|V* zM1%J*9RVk0+{w^?SE%F&hwJg<7X*C4auD?zGwtv?;z)C7bl}mvjgDLJ<)G7ER$ftA zRb4Y_@|3C5rq?>Oy84D0GiS|iY??E-+1b+SqN&f1&l85{&0nx^(c-p9G!{=Jmn>bj ze8u;nv6Wv!b>SJmjgCj@1yA{HblgfWc+PL510}4{@jAWWQNN9jd+FtDqU&>x`>nyg zq6L8o&$htNi4!Q7n7C6c2MB>Up&y!2XT)=^j`ib&RE35ZxO~yeuu64IFbKu3b>>r- z&^y-4Fb0N>OTAfJs4QVgz#b+wh^QquOlG!A3M=$CZ6Fw_YuFSNZ=L%*H0TB)7fK!9`5cm ztb@8tW!Ox$(%=?rJ)*-R^?*iBn#LlQ8o`1)bU-XTmK4o&Ia+(m$-03@wc+K5y<<<%V1 zqhZCLx=i)q;|YU!ggic%Ir%7pg^$w>6vhny#3FbY^6=G+YLrDH#y6*QW3hRvAX9s}@jXD23$Y8>0wiAU`S$kMKo6qD*v8O8`kQB05g;j4*- zZWW#jNzWn{4@e>_hL6_irTELOWS~Y4Qo=Uylz1F@j1b{7bH(z$ks2cgjDW4jKvrs? z&|!s(@KMN~QQa7?rlWS;i0F8_8ve#C?$D0N7O5(g;c{w$gs?rcBU@P0+lwwHx`#&; zLYbJgYty}Sc0T>AFtv6RDbx=k8Z)k+Qzso)q(dGa*omc3uXF6kYS@kOQg|e2>Ck>o z)p$H#doGj3vxat$>hAc-gV?qjn)2#jTV06SLXe+SJa`Np!p-O9+ec}+G~sLD8gciB>23$~O3LUTh_E%IVv5eOh<+bw-b+bz4BE+GcF z-8;9v_U@hK-d*U5&{k^|K_RhzBhhF~42g!Q+Zcr}67dT$elyXS7>zIS(`bnC8~%T1 zX3or+nLE?VNv7R<@AI5x9;I6p@18L}L zz3^7+$(3cF3m&5zC0yCX(7N0!wyGM`9nI}LGz+q10!!0xN*>ov-4!WpX+ja!`PWeRZY&x0Xd9>;;xrS2#~CV z-YAz4v!*HuDKoKlmmbRRK(HLVx)yAR-u<;V2OMARw-@>YuSNoY7_#H`O2`HzOZI=u z$wuj_$maKC@(l(G6AxG8-|G4z8FTyv0@|p3wx#9%k#@hWSFcdUIvT*xM$IUJc0g0C zG?t;~pKLEGJ_^wX@({#fco;WS!b3n@9*)j_yqt%)qbeS{#u*H&I|M~D3boq{3bvc( zB+UFE0b>|S;}%LN1;nLvWbMc0l-7k(Md`R;pq3JBs_S1%DLRo*I;%KJ*OcdJ1!ovq zfv1fUS^>$@`j2v2F|bv%;t7azD8UtASXyb#nu46o19TV?O;7TwoiOdr1RcLuJ0y_+iaor^}0+OZiujMonlvdG*-9(g>;3_cCRGjUs_VBy9 zN-7m*5&w+qp#+-&whW;iY?Y^5uzKyK<$-n!tYKIaw^G6yK(eg)TRCf>qKY+j!C(?8 z!8Kr5Y7GG)wSz`7@Wap<*Iz;>AXz&9o~6@OT4EV`5nY~T&PO{+Y*jGd0=9}LuDXE1 z%wtL~aj>irP7udVzwwzR#)NAk0A`7G%2yUUYSKtuagITzZc4BLP|Is@v_;Wvrc?@v zGw3P7M!;Dv(s@msNfw^-meSpVX=vP;8Lfr-t(M!Fy6Q><59lz0j5}WJ<}mSUDiBZ_qlG(@5>=ouxr7GNfBQW|eLjOztA-JK<54_B+}`Z>`95U`D6 zoAMKMPM(ge)6q+Qm*F%@1Hdx;D8bdh^V95c|G6n%f$*0&#v7;)J<~~}pt1#JZ>jXOqKbn%dgB64-wz$R-41!^1y8Bla z?S;O^bdBo=8lVK5;@<4i8bz<6dB&5zzZhHKv2%ifkEJ;6u7mSpghM!op`Zkp(N4Hy zWZD_51TnSNut30GkCo}MGw;1;Zi~z_6peB)4wIxjcJ{Tf|CmUuJSDh}CYT&q1d%Pc55u9$#H##SDxu@40`?4B`t;OBa@D8Oh zy|B<(VJFKOGj|9X+e`^Q3N5%gkTgN$r29yeI`I(;;oxF8ktHSA3@#E%;T|l)0*>KN z32xvMajdY+j$KJ}u44s}CEggZu&q1~VR{%h9@I-$ETo;7UShC>On2Q>OcG0OO0?v7 z&HiLxHhJHa!U->qO%3-i8gsALXgZj8s!Ob^QG%Oeh7`2PuGVC4@xvn@@Qyji?rI(_ zv!Ybc@?mXM^&M2!ThJX!a0{sXT!3meY_h!`4*NrQTetM~mOos#=R7ZuebM}y^W~hJ zhVz=pXm*dG1oqltbt12S9dU^*|q*@!33Q*-O6BGR%wOWWw%Tz!Kw$eH=M{(<+72eWnwRW^x z=SR?_|Xc60f-`afa&U+kGgW@q`h+}M&9 z(ycf9O5_dlW{D0lOSK+hQU_Bhu+=Oiv@&@;f=v#}0p51i_CQC6GRW*$`PK!P#6+-Y zOpn=AGQkL^0DHYKW<;FD&L8+e^yFJG75gi_1uryy)_fU7C72-gqy$Pm40}`()ri$?r*x_5{V_Qig4V~1%_oKP9+Zf%*M&Uxd>>(@Fs4i zgg1a>cyr;EEN@(LQKl+#>w-zN1`JCs1envt8A^K_{o`m&9)1`)2Y}1f!5zH7n>u?7hK>xw>iRj+ORT@I5gicU`^IqNa0RW3gcU0=RPKx%9>Lme2=?OW)|> zedYAwC9CLj{TdjSJ~F80bTU2;xSm@2s_Xkil%NblYTQH#serhoj;y_tCDrW(AqlQy zazWQb^mXK~CO|r9&u?0n>L!W+PZMS7rodFO&z-P>!5ARrGn3y_0hlECU=~n1N^m(f zUa2RQL~1E=S1f@pY$$@pdjFu(K zSs+}hC!KDpkEUS17^X;>5^&zpMWmYog&vfpo2Fm}rkf_Mm8F{kg^X-KU!HCXIKoe- zn|}X$KY-S8eT^}r1e*XvXUg+Ui}^F<=@=igkQBO%V*N{UO_L1jD#%1hm?I@Sb@Ue1 zOQ)JzrLOCT#JB*;uug-zDpEYIDzj3ln2~A$lg<{CmC7}>eJvhQh2|S`X!Yy?+*Nlf+r6Mx**8Iy1KxZ2$bJ9Xn2) zIMv$m^&^kG`r_waUYu@qeDBSFz4Mzty!7Or2k#o+y7gnN&p!0e*Z23IKXLZ?C(r!q z&;7Ie-+g=Sw?8_6{`@PbBXJrJ<=@6=A(yzdK{>E_PFvrzUg&d5c+p7})W1z1d;r` z`-_j?*Pqz_*iwH^x8W==>tOUKf5NPx+^*l5ic*4`XaTYA>qn+^clX)0-alavpwWpd zh~CA@lL6EB^=CUep~GxU#cyr7VH=HDyAI{L!X}-Zk%wjs9WwVxO}D}|uD-GCh{)ak zpl$}^Y+J`{OLb=z%;@~%1C3)$&P>VcB1prSoR|Q|)k@B(nGg9V8CfDP3mO_vz}nxhCA+oG&@x0%I0VVfmI0|Od4%0x*P`q z^SCRGFsEZd)mFFbbqaSQL9!}>1e?IHBz{C6%%sS8x`B%X z;0!}++(rqlfMjX?Q#q~o3T2a?ve#=dhqxC?umKE9D|41PPKFprCg8)68CPFICLmcd z-z+Ed(?Y2t)AhDsSTYHnj?4_C5!hj9jO#9;5s)m6zb~ipKA}|6$bs!9<7IUqYpa$&LE4AmRb9imUFP3K(YPp+hoK*q+~u zB8T*j)+Yr>Cn>>=py3DHg^>OTLo9MaPi=qd&b#rSuBw8GK$IAy*V2~5L<0J3@}FFh z$KyiX6D#ZccYQt4e9GZ0;+&Oetd?&iYuAQk7@h7FA+iVp;MPS1Cp< z5NWYs4v=Uz-J0@r$|TDXp`PW%kKa7#X(LK3V~4pv*=f9y~XMEEoPqGC=MAa z9nc3Y1{QbDqOs=zh#3SJiNwFbLp037GWEzwCttrcE*Q{x9VV<-Zx&2~z3Wq`AB8=n zte(DkiT7apSb7C6-<{0@kKe6Z_Li13urjOLZv00R#D97vcCpc0g~haJW&n5Tk2~~G zf?I&3d-lbEI9`dAILe2y(kO0cM#AptVw?1Ucjsud0}p3)pHHK+(>pFL_Dvc@!NdpP za-&-pL}H|YClFD-xVB@aSC(`2=cRS6g;%HJDU#_+exO2qST~GscV`;S_KKO3oMs4C zARKNe1Ifv#Di*M9Vb1W`EnqRs1g?!GL4JV$e* zdg{Yrm{88^YXjbz46{TXc^NmL1}WpnUjI+lkwGg%nDYLWpr8gC9-XGqeFoxIy}zzi zOMOq`#G$#<`s>$b##gKDsGZbrA3X82Zev?_nT~E5y;SGW)Gpa}|HC)bw%xdOL+zc> z(Oc$6bVbSNC70?)omsPeY-DUbQ0uPw%g6^$+*%vyjBLEgd^5UjAP@2iT@w|RL?)!@lQVTf8l$E<^TWy diff --git a/build.rbxlx b/build.rbxlx new file mode 100644 index 0000000..654db0b --- /dev/null +++ b/build.rbxlx @@ -0,0 +1,1861 @@ + + + + ReplicatedFirst + + + + TableUtil + return { + Array = require(script:WaitForChild("Array")); + Dict = require(script:WaitForChild("Map")); + Map = require(script:WaitForChild("Map")); + Set = require(script:WaitForChild("Set")); +}; + + + + Array + return { + SelectFirst1D = require(script:WaitForChild("SelectFirst1D")); + SelectLast1D = require(script:WaitForChild("SelectLast1D")); + FoldRight1D = require(script:WaitForChild("FoldRight1D")); + FoldLeft1D = require(script:WaitForChild("FoldLeft1D")); + Shuffle1D = require(script:WaitForChild("Shuffle1D")); + Reverse1D = require(script:WaitForChild("Reverse1D")); + Remove1D = require(script:WaitForChild("Remove1D")); + Filter1D = require(script:WaitForChild("Filter1D")); + Insert1D = require(script:WaitForChild("Insert1D")); + Merge1D = require(script:WaitForChild("Merge1D")); + Sort1D = require(script:WaitForChild("Sort1D")); + Copy1D = require(script:WaitForChild("Copy1D")); + Cut1D = require(script:WaitForChild("Cut1D")); + Map1D = require(script:WaitForChild("Map1D")); +}; + + + + Copy1D + --- Copies an array +--- @deprecated Use table.clone instead +return table.clone + + + + + Copy1D.spec + return function() + local Copy1D = require(script.Parent.Copy1D) + + describe("Array/Copy1D", function() + it("should copy a blank table with no contents", function() + local Original = {} + local Copied = Copy1D(Original) + + expect(Copied).to.never.equal(Original) + expect(next(Copied)).to.never.be.ok() + end) + + it("should copy the first element of an array", function() + local Original = {100} + local Copied = Copy1D(Original) + + expect(Copied).never.to.equal(Original) + expect(Copied[1]).to.equal(Original[1]) + end) + + it("should copy all elements of an array", function() + local Original = {1, 2, 3, 4} + local Copied = Copy1D(Original) + + expect(Copied).never.to.equal(Original) + expect(Copied[1]).to.equal(Original[1]) + expect(Copied[2]).to.equal(Original[2]) + expect(Copied[3]).to.equal(Original[3]) + expect(Copied[4]).to.equal(Original[4]) + end) + end) +end + + + + + Cut1D + --- Cuts a chunk from an array given a starting and ending index - the difference in these indexes can be negative - faster if positive e.g. Cut1D(X, 1, 4) over Cut1D(X, 4, 1) +local function Cut1D<T>(Array: {T}, From: number, To: number): {T} + local Size = #Array + + assert(From >= 1, "Start index less than 1!") + assert(To >= 1, "End index greater than 1!") + + assert(From <= Size, "Start index beyond array length!") + assert(To <= Size, "End index beyond array length!") + + local Diff = To - From + local Range = math.abs(Diff) + + if (Range == Size - 1) then + return Array + end + + if (Diff > 0) then + -- Faster, but table.move doesn't support iterating backwards over a range + return table.move(Array, From, To, 1, {}) + end + + local Result = table.create(Range) + local ResultIndex = 1 + + for Index = From, To, -1 do + Result[ResultIndex] = Array[Index] + ResultIndex += 1 + end + + return Result +end + +return Cut1D + + + + + Cut1D.spec + return function() + local Cut1D = require(script.Parent.Cut1D) + + describe("Array/Cut1D", function() + it("should return the first element given range 1, 1", function() + local Result = Cut1D({1234}, 1, 1) + expect(Result[1]).to.equal(1234) + end) + + it("should return the middle two elements of a 4-item array given range 2, 3", function() + local Result = Cut1D({1, 2, 3, 4}, 2, 3) + expect(Result[1]).to.equal(2) + expect(Result[2]).to.equal(3) + expect(Result[3]).never.to.be.ok() + end) + + it("should throw an error if either index is less than 1", function() + expect(function() + Cut1D({}, 0, 1) + end).to.throw() + + expect(function() + Cut1D({}, 1, 0) + end).to.throw() + end) + + it("should throw an error if either index is greater than the array length", function() + expect(function() + Cut1D({1, 2}, 1, 3) + end).to.throw() + + expect(function() + Cut1D({1, 2}, 3, 1) + end).to.throw() + end) + + it("should return the original array if the range is equivalent to the array's length", function() + local Test = {1, 2, 3, 4} + local Result = Cut1D(Test, 1, 4) + + expect(Result).to.equal(Test) + end) + + it("should cut backwards", function() + local Result = Cut1D({1, 2, 3, 4}, 3, 1) + expect(Result[1]).to.equal(3) + expect(Result[2]).to.equal(2) + expect(Result[3]).to.equal(1) + end) + end) +end + + + + + Filter1D + --- Filters an array for all items which satisfy some condition +local function Filter1D<T>(Array: {T}, Condition: (T, number) -> boolean, Allocate: number?): {T} + local Result = table.create(Allocate or 0) + local Index = 1 + + for ItemIndex, Value in ipairs(Array) do + if (Condition(Value, ItemIndex)) then + Result[Index] = Value + Index += 1 + end + end + + return Result +end + +return Filter1D + + + + + Filter1D.spec + return function() + local Filter1D = require(script.Parent.Filter1D) + + describe("Array/Filter1D", function() + it("should return a blank table for no data", function() + local Results = Filter1D({}, function() + return true + end) + + expect(next(Results)).never.to.be.ok() + end) + + it("should return all items in order for true condition", function() + local Results = Filter1D({3, 2, 1}, function() + return true + end) + + expect(Results[1]).to.equal(3) + expect(Results[2]).to.equal(2) + expect(Results[3]).to.equal(1) + end) + + it("should return no items for false condition", function() + local Results = Filter1D({3, 2, 1}, function() + return false + end) + + expect(next(Results)).never.to.be.ok() + end) + + it("should filter all items larger than some value in order", function() + local Results = Filter1D({8, 4, 2, 1}, function(Value) + return Value >= 4 + end) + + expect(Results[1]).to.equal(8) + expect(Results[2]).to.equal(4) + expect(Results[3]).never.to.be.ok() + end) + + it("should pass the index in order", function() + Filter1D({1, 2, 3, 4}, function(Value, Index) + expect(Index).to.equal(Value) + return true + end) + end) + end) +end + + + + + FoldLeft1D + --- Reduces an array to a single value from its left-most value to its right-most value +local function FoldLeft1D<T>(Array: {T}, Processor: (T, T, number, number) -> T, Initial: T): T + local Aggregate = Initial + local Size = #Array + + for Index = 1, Size do + Aggregate = Processor(Aggregate, Array[Index], Index, Size) + end + + return Aggregate +end + +return FoldLeft1D + + + + + FoldLeft1D.spec + return function() + local FoldLeft1D = require(script.Parent.FoldLeft1D) + + describe("Array/FoldLeft1D", function() + it("should not call on an empty table", function() + local Called = false + + FoldLeft1D({}, function() + Called = true + end, 1) + + expect(Called).to.equal(false) + end) + + it("should return an initial value with no operations", function() + local Result = FoldLeft1D({}, function() end, 1) + + expect(Result).to.equal(1) + end) + + it("should call in order", function() + local Indexes = {} + + FoldLeft1D({1, 2, 3, 4}, function(_, _, Index) + table.insert(Indexes, Index) + end) + + for Index = 1, 4 do + expect(Indexes[Index]).to.equal(Index) + end + end) + + it("should correctly give the size of the array", function() + FoldLeft1D({1, 2, 3, 4}, function(_, _, _, Size) + expect(Size).to.equal(4) + end) + end) + + it("should sum up some values with a sum function", function() + local Result = FoldLeft1D({1, 2, 3, 4}, function(Aggr, Value) + return Aggr + Value + end, 0) + + expect(Result).to.equal(10) + end) + end) +end + + + + + FoldRight1D + --- Reduces an array to a single value from its right-most value to its left-most value +local function FoldRight1D<T>(Array: {T}, Processor: (T, T, number, number) -> T, Initial: T): T + local Aggregate = Initial + local Size = #Array + + for Index = Size, 1, -1 do + Aggregate = Processor(Aggregate, Array[Index], Index, Size) + end + + return Aggregate +end + +return FoldRight1D + + + + + FoldRight1D.spec + return function() + local FoldRight1D = require(script.Parent.FoldRight1D) + + describe("Array/FoldRight1D", function() + it("should not call on an empty table", function() + local Called = false + + FoldRight1D({}, function() + Called = true + end, 1) + + expect(Called).to.equal(false) + end) + + it("should return an initial value with no operations", function() + local Result = FoldRight1D({}, function() end, 1) + + expect(Result).to.equal(1) + end) + + it("should call in order", function() + local Indexes = {} + + FoldRight1D({1, 2, 3, 4}, function(_, _, Index) + table.insert(Indexes, Index) + end) + + for Index = 1, 4 do + expect(Indexes[Index]).to.equal(5 - Index) + end + end) + + it("should correctly give the size of the array", function() + FoldRight1D({1, 2, 3, 4}, function(_, _, _, Size) + expect(Size).to.equal(4) + end) + end) + + it("should sum up some values with a sum function", function() + local Result = FoldRight1D({1, 2, 3, 4}, function(Aggr, Value) + return Aggr + Value + end, 0) + + expect(Result).to.equal(10) + end) + end) +end + + + + + Insert1D + --- Inserts a value into an array with an optional "insert at" index +local function Insert1D<T>(Array: {T}, Value: T, At: number?): {T} + local ArraySize = #Array + local NewSize = ArraySize + 1 + local Result = table.create(NewSize) + At = At or NewSize + + assert(At >= 1 and At <= NewSize, "Insert index out of array range") + + table.move(Array, 1, At - 1, 1, Result) + Result[At] = Value + table.move(Array, At, NewSize - 1, At + 1, Result) + + return Result +end + +return Insert1D + + + + + Insert1D.spec + return function() + local Insert1D = require(script.Parent.Insert1D) + + describe("Array/Insert1D", function() + it("should insert an item in the first position in an empty array", function() + local Result = Insert1D({}, 1) + + expect(Result[1]).to.be.ok() + expect(Result[1]).to.equal(1) + end) + + it("should insert two items in order into an empty array", function() + local Result = {} + Result = Insert1D(Insert1D(Result, 1), 2) + + for Index = 1, 2 do + expect(Result[Index]).to.be.ok() + expect(Result[Index]).to.equal(Index) + end + end) + + it("should allow insertion at an inner index", function() + local Result = {1, 2, 4, 5} + Result = Insert1D(Result, 3, 3) + + for Index = 1, 5 do + expect(Result[Index]).to.be.ok() + expect(Result[Index]).to.equal(Index) + end + end) + + it("should allow insertion at index 1", function() + local Result = {2, 3} + Result = Insert1D(Result, 1, 1) + + for Index = 1, 3 do + expect(Result[Index]).to.be.ok() + expect(Result[Index]).to.equal(Index) + end + end) + + it("should disallow insertion at index length+2", function() + expect(function() + Insert1D({1, 2}, 1000, 4, true) + end).to.throw() + end) + end) +end + + + + + Map1D + --- Puts an array's values through a transformation function, mapping the outputs into a new array - nil values will be skipped & will not leave holes in the new array +local function Map1D<T>(Array: {T}, Operator: (T, number) -> T?, Allocate: number?): {T} + local Result = table.create(Allocate or 0) + local Index = 1 + + for ItemIndex = 1, #Array do + local Value = Array[ItemIndex] + local Transformed = Operator(Value, ItemIndex) + + if (Transformed == nil) then + -- Skip nil values + continue + end + + Result[Index] = Transformed + Index += 1 + end + + return Result +end + +return Map1D + + + + + Map1D.spec + return function() + local Map1D = require(script.Parent.Map1D) + + describe("Array/Map1D", function() + it("should return a blank array if passed in a blank array", function() + local Result = Map1D({}, function(Value, Key) + return Key, Value + end) + + expect(next(Result)).to.equal(nil) + end) + + it("should return all items in an array with a function passing back the same value, in order", function() + local Result = Map1D({1, 2, 3, 4}, function(Value) + return Value + end) + + for Index = 1, 4 do + expect(Result[Index]).to.equal(Index) + end + end) + + it("should double all items in an array with a double function, in order", function() + local Result = Map1D({1, 2, 3, 4}, function(Value) + return Value * 2 + end) + + for Index = 1, 4 do + expect(Result[Index]).to.equal(Index * 2) + end + end) + + it("should ignore nil returns from the operation function", function() + local Result = Map1D({1, 2, 3, 4}, function(Value) + if (Value < 3) then + return nil + end + + return Value + end) + + expect(Result[1]).to.equal(3) + expect(Result[2]).to.equal(4) + expect(Result[3]).never.to.be.ok() + end) + + it("should send the index to the operation function", function() + Map1D({2, 4, 6, 8}, function(Value, Index) + expect(Value / 2).to.equal(Index) + end) + end) + end) +end + + + + + Merge1D + --- Merges multiple arrays together, in order +local function Merge1D<T>(...: {T}): {T} + local Result = table.clone(select(1, ...)) + local Index = #Result + 1 + + for SubArrayIndex = 2, select("#", ...) do + local SubArray = select(SubArrayIndex, ...) + local Size = #SubArray + table.move(SubArray, 1, Size, Index, Result) + Index += Size + end + + return Result +end + +return Merge1D + + + + + Merge1D.spec + return function() + local Merge1D = require(script.Parent.Merge1D) + + describe("Array/Merge1D", function() + it("should merge two blank arrays into a blank array", function() + local Result = Merge1D({}, {}) + expect(next(Result)).to.never.be.ok() + end) + + it("should merge more than two blank arrays into a blank array", function() + local Result = Merge1D({}, {}, {}, {}) + expect(next(Result)).to.never.be.ok() + end) + + it("should merge several one-item arrays into a final array, in order", function() + local Result = Merge1D({1}, {2}, {3}, {4}) + + for Index = 1, 4 do + expect(Result[Index]).to.equal(Index) + end + end) + + it("should merge several multiple-item arrays into a final array, in order", function() + local Result = Merge1D({1, 2, 3}, {4}, {5, 6}, {7, 8, 9, 10}) + + for Index = 1, 10 do + expect(Result[Index]).to.equal(Index) + end + end) + end) +end + + + + + Remove1D + --- Removes a single element from an array +local function Remove1D<T>(Array: {T}, Index: number): {T} + local ArrayLength = #Array + + if (ArrayLength == 0) then + return {} + end + + Index = Index or ArrayLength + + assert(Index > 0, "Index must be greater than 0!") + assert(Index <= ArrayLength, "Index out of bounds!") + + local Result = table.create(ArrayLength - 1) + table.move(Array, 1, Index - 1, 1, Result) + table.move(Array, Index + 1, ArrayLength, Index, Result) + + return Result +end + +return Remove1D + + + + + Remove1D.spec + return function() + local Remove1D = require(script.Parent.Remove1D) + + describe("Array/Remove1D", function() + it("should return an empty array upon removing from an empty array", function() + local Original = {} + local Result = Remove1D(Original) + + expect(next(Result)).to.equal(nil) + expect(Original).never.to.equal(Result) + end) + + it("should remove the last item in a two-item array", function() + local Result = Remove1D({1, 2}) + expect(Result[1]).to.equal(1) + expect(Result[2]).never.to.be.ok() + end) + + it("should remove the last item multiple times", function() + local Result = Remove1D({1, 2, 3, 4}) + expect(Result[1]).to.equal(1) + expect(Result[2]).to.equal(2) + expect(Result[3]).to.equal(3) + expect(Result[4]).never.to.be.ok() + + Result = Remove1D(Result) + expect(Result[1]).to.equal(1) + expect(Result[2]).to.equal(2) + expect(Result[3]).never.to.be.ok() + + Result = Remove1D(Result) + expect(Result[1]).to.equal(1) + expect(Result[2]).never.to.be.ok() + + Result = Remove1D(Result) + expect(Result[1]).never.to.be.ok() + end) + + it("should remove an item in the middle", function() + local Result = Remove1D({1, 2, 3, 4}, 2) + expect(Result[1]).to.equal(1) + expect(Result[2]).to.equal(3) + expect(Result[3]).to.equal(4) + end) + end) +end + + + + + Reverse1D + --- Flips all items in an array +local function Reverse1D<T>(Array: {T}): {T} + local ArraySize = #Array + local Result = table.create(ArraySize) + + for Index = 1, ArraySize do + Result[Index] = Array[ArraySize - Index + 1] + end + + return Result +end + +return Reverse1D + + + + + Reverse1D.spec + return function() + local Reverse1D = require(script.Parent.Reverse1D) + + describe("Array/Reverse1D", function() + it("should return an empty array if passed an empty array", function() + local Result = Reverse1D({}) + expect(next(Result)).never.to.be.ok() + end) + + it("should return a one-item array from a one-item array", function() + local Result = Reverse1D({1}) + expect(Result[1]).to.equal(1) + end) + + it("should swap two items in a two-item array", function() + local Result = Reverse1D({1, 2}) + expect(Result[1]).to.equal(2) + expect(Result[2]).to.equal(1) + end) + + it("should swap items in an odd-number-of-items array", function() + local Result = Reverse1D({1, 2, 3}) + expect(Result[1]).to.equal(3) + expect(Result[2]).to.equal(2) + expect(Result[3]).to.equal(1) + end) + + it("should reverse 1000 items", function() + local Result = {} + + for Index = 1, 1000 do + table.insert(Result, Index) + end + + Result = Reverse1D(Result) + + for Index = 1, 1000 do + expect(Result[Index]).to.equal(1000 - Index + 1) + end + end) + end) +end + + + + + SelectFirst1D + --- Selects the first item in an array which satisfies some condition +local function SelectFirst1D<T>(Array: {T}, Condition: (T, number) -> boolean): T? + for Index = 1, #Array do + local Value = Array[Index] + + if (Condition(Value, Index)) then + return Value + end + end + + return nil +end + +return SelectFirst1D + + + + + SelectFirst1D.spec + return function() + local SelectFirst1D = require(script.Parent.SelectFirst1D) + + describe("Array/SelectFirst1D", function() + it("should select nothing on an empty array", function() + expect(SelectFirst1D({}, function() end)).never.to.be.ok() + end) + + it("should select the first item in an array for a return-true function", function() + expect(SelectFirst1D({1}, function() + return true + end)).to.equal(1) + end) + + it("should select the first item greater than some number", function() + expect(SelectFirst1D({1, 2, 4, 8, 16, 32}, function(Value) + return Value >= 8 + end)).to.equal(8) + end) + + it("should select the first index greater than some number", function() + expect(SelectFirst1D({1, 2, 4, 8, 16, 32}, function(_, Index) + return Index >= 3 + end)).to.equal(4) + end) + end) +end + + + + + SelectLast1D + --- Selects the last item in an array which satisfies some condition +local function SelectLast1D<T>(Array: {T}, Condition: (T, number) -> boolean): T? + for Index = #Array, 1, -1 do + local Value = Array[Index] + + if (Condition(Value, Index)) then + return Value + end + end + + return nil +end + +return SelectLast1D + + + + + SelectLast1D.spec + return function() + local SelectLast1D = require(script.Parent.SelectLast1D) + + describe("Array/SelectLast1D", function() + it("should select nothing on an empty array", function() + expect(SelectLast1D({}, function() end)).never.to.be.ok() + end) + + it("should select the last item in an array for a return-true function", function() + expect(SelectLast1D({1}, function() + return true + end)).to.equal(1) + end) + + it("should select the last item greater than some number", function() + expect(SelectLast1D({1, 2, 4, 8, 16, 32}, function(Value) + return Value >= 8 + end)).to.equal(32) + end) + + it("should select the last index greater than some number", function() + expect(SelectLast1D({1, 2, 4, 8, 16, 32}, function(_, Index) + return Index >= 3 + end)).to.equal(32) + end) + end) +end + + + + + Shuffle1D + local RandomGenerator = Random.new() + +--- Scrambles an array with an optional random seed +local function Shuffle1D<T>(Array: {T}, Seed: number?): {T} + local Generator = Seed and Random.new(Seed) or RandomGenerator + + local ArraySize = #Array + local Result = table.clone(Array) + + for Index = 1, ArraySize do + local Generated = Generator:NextInteger(1, ArraySize) + Result[Index], Result[Generated] = Result[Generated], Result[Index] + end + + return Result +end + +return Shuffle1D + + + + + Shuffle1D.spec + return function() + local Shuffle1D = require(script.Parent.Shuffle1D) + + describe("Array/Shuffle1D", function() + it("should return an empty array given an empty array", function() + local Result = Shuffle1D({}) + expect(next(Result)).never.to.be.ok() + end) + + it("should return a one-item array given a one-item array", function() + local Result = Shuffle1D({1}) + expect(Result[1]).to.equal(1) + end) + + it("should shuffle items given a seed", function() + local Original = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + + -- Two tests to ensure seed is locally deterministic + local Result1 = Shuffle1D(Original, 100) + local Result2 = Shuffle1D(Original, 100) + + local Sum0 = 0 + local Sum1 = 0 + local Sum2 = 0 + + local Different1 = false + local Different2 = false + + for Index = 1, #Result1 do + Sum0 += Original[Index] + Sum1 += Result1[Index] + Sum2 += Result2[Index] + + Different1 = Different1 or Result1[Index] ~= Original[Index] + Different2 = Different2 or Result2[Index] ~= Original[Index] + expect(Result1[Index]).to.equal(Result2[Index]) + end + + expect(Sum0).to.equal(Sum1) + expect(Sum1).to.equal(Sum2) + expect(Different1).to.equal(true) + expect(Different2).to.equal(true) + end) + end) +end + + + + + Sort1D + --- Copies & sorts an array according to some condition +local function Sort1D<T>(Array: {T}, Condition: (T, T) -> boolean): {T} + local Result = table.clone(Array) + table.sort(Result, Condition) + return Result +end + +return Sort1D + + + + + Sort1D.spec + return function() + local Sort1D = require(script.Parent.Sort1D) + + describe("Array/Sort1D", function() + it("should return an empty array given an empty array", function() + local Result = Sort1D({}) + expect(next(Result)).to.equal(nil) + end) + + it("should return a one-item array given a one-item array", function() + local Result = Sort1D({1}) + expect(Result[1]).to.equal(1) + end) + + it("should sort ascending given an ascending function", function() + local Result = Sort1D({4, 8, 1, 2}, function(Initial, Other) + return Initial < Other + end) + + expect(Result[1]).to.equal(1) + expect(Result[2]).to.equal(2) + expect(Result[3]).to.equal(4) + expect(Result[4]).to.equal(8) + end) + + it("should sort descending given an ascending function", function() + local Result = Sort1D({4, 8, 1, 2}, function(Initial, Other) + return Initial > Other + end) + + expect(Result[1]).to.equal(8) + expect(Result[2]).to.equal(4) + expect(Result[3]).to.equal(2) + expect(Result[4]).to.equal(1) + end) + end) +end + + + + + + Map + return { + MergeDeep = require(script:WaitForChild("MergeDeep")); + Filter1D = require(script:WaitForChild("Filter1D")); + Values1D = require(script:WaitForChild("Values1D")); + Merge1D = require(script:WaitForChild("Merge1D")); + Keys1D = require(script:WaitForChild("Keys1D")); + Map1D = require(script:WaitForChild("Map1D")); +}; + + + + CreateNonOverwritingPatchDeep + --- Creates a "patch template" into another object recursively. +--- This allows us to apply an additional merge to add new fields to values which were not originally nil. +--- Good use case: want to merge in new default fields to a player's data structure without overwriting existing fields. +--- @todo Return nil if result is empty & wrap top level with another function? That way we trim recursive merge work for the resulting empty tables, which will be the common use case. +local TYPE_TABLE = "table" + +local function CreateNonOverwritingPatchDeep(Previous, Template) + local Result = {} + + for Key, Value in pairs(Template) do + local ExistingValue = Previous[Key] + + if (type(Value) == TYPE_TABLE and type(ExistingValue) == TYPE_TABLE) then + Result[Key] = CreateNonOverwritingPatchDeep(ExistingValue or {}, Value) + continue + end + + if (ExistingValue ~= nil) then + continue + end + + Result[Key] = Value + end + + return Result +end + +return CreateNonOverwritingPatchDeep + + + + + CreateNonOverwritingPatchDeep.spec + return function() + local CreateNonOverwritingPatchDeep = require(script.Parent.CreateNonOverwritingPatchDeep) + + describe("Map/CreateNonOverwritingPatchDeep", function() + it("should return a blank table for no data", function() + expect(next(CreateNonOverwritingPatchDeep({}, {}))).never.to.be.ok() + end) + + it("should apply all new items in a flat table", function() + local Result = CreateNonOverwritingPatchDeep({}, { + X = 1; + Y = 2; + Z = 3; + }) + + expect(Result.X).to.equal(1) + expect(Result.Y).to.equal(2) + expect(Result.Z).to.equal(3) + + local Count = 0 + + for _ in pairs(Result) do + Count += 1 + end + + expect(Count).to.equal(3) + end) + + it("should not overwrite existing items in a flat table but apply new", function() + local Result = CreateNonOverwritingPatchDeep({ + X = 20; + }, { + X = 1; + Y = 2; + Z = 3; + }) + + expect(Result.Y).to.equal(2) + expect(Result.Z).to.equal(3) + + local Count = 0 + + for _ in pairs(Result) do + Count += 1 + end + + expect(Count).to.equal(2) + end) + + it("should apply all new items in a nested table", function() + local Result = CreateNonOverwritingPatchDeep({}, { + X = { + Y = { + Z = 1; + }; + }; + }) + + expect(Result.X.Y.Z).to.equal(1) + end) + + it("should not overwrite existing items in a nested table but apply new", function() + local Result = CreateNonOverwritingPatchDeep({ + X = { + Y = { + Z = 20; + }; + }; + }, { + X = { + Y = { + Z = 1; + H = 200; + }; + }; + }) + + expect(Result.X.Y.Z).to.equal(nil) + expect(Result.X.Y.H).to.equal(200) + end) + end) +end + + + + + Filter1D + --- Filters a table for all items which satisfy some condition +local function Filter1D(Structure, Condition) + local Result = {} + + for Key, Value in pairs(Structure) do + if (Condition(Value, Key)) then + Result[Key] = Value + end + end + + return Result +end + +return Filter1D + + + + + Filter1D.spec + return function() + local Filter1D = require(script.Parent.Filter1D) + + describe("Map/Filter1D", function() + it("should return a blank table for no data", function() + local Results = Filter1D({}, function() + return true + end) + + expect(next(Results)).never.to.be.ok() + end) + + it("should return all items for true condition", function() + local Results = Filter1D({A = 1, B = 2, C =3}, function() + return true + end) + + expect(Results.A).to.equal(1) + expect(Results.B).to.equal(2) + expect(Results.C).to.equal(3) + end) + + it("should return all items greater than 3", function() + local Results = Filter1D({A = 2, B = 4, C =8}, function(Value) + return Value > 3 + end) + + expect(Results.A).never.to.be.ok() + expect(Results.B).to.equal(4) + expect(Results.C).to.equal(8) + end) + + it("should pass the keys", function() + local Results = Filter1D({A = 2, B = 4, C =8}, function(_, Key) + return Key == "A" or Key == "B" + end) + + expect(Results.A).to.equal(2) + expect(Results.B).to.equal(4) + expect(Results.C).never.to.be.ok() + end) + end) +end + + + + + Keys1D + --- Obtains the keys from a table +local function Keys1D(Structure) + local Result = {} + local Index = 1 + + for Key in pairs(Structure) do + Result[Index] = Key + Index += 1 + end + + return Result +end + +return Keys1D + + + + + Keys1D.spec + return function() + local Keys1D = require(script.Parent.Keys1D) + + describe("Map/Keys1D", function() + it("should return a blank table given a blank table", function() + local Result = Keys1D({}) + expect(Result).to.be.ok() + expect(next(Result)).never.to.be.ok() + end) + + it("should return one key given a one key table", function() + local Result = Keys1D({A = 1000}) + expect(table.find(Result, "A")).to.be.ok() + end) + + it("should return multiple keys given a multiple key table", function() + local Result = Keys1D({A = 1000, B = 2000, C = true}) + expect(table.find(Result, "A")).to.be.ok() + expect(table.find(Result, "B")).to.be.ok() + expect(table.find(Result, "C")).to.be.ok() + end) + end) +end + + + + + Map1D + --- Puts each key-value pair in a table through a transformation function, mapping the outputs into a new table +local function Map1D(Structure, Operation) + local Result = {} + + for Key, Value in pairs(Structure) do + local NewValue, NewKey = Operation(Value, Key) + Result[NewKey or Key] = NewValue + end + + return Result +end + +return Map1D + + + + + Map1D.spec + return function() + local Map1D = require(script.Parent.Map1D) + + describe("Map/Map1D", function() + it("should return a blank array if passed in a blank array", function() + local Result = Map1D({}, function(Value, Key) + return Key, Value + end) + + expect(next(Result)).to.equal(nil) + end) + + it("should return all items given a return-same-value function", function() + local Result = Map1D({A = 1, B = 2}, function(Value) + return Value + end) + + expect(Result.A).to.equal(1) + expect(Result.B).to.equal(2) + end) + + it("should pass in keys and allow for custom keys", function() + local Result = Map1D({A = 1, B = 2}, function(Value, Key) + return Value, Key:lower() + end) + + expect(Result.a).to.equal(1) + expect(Result.b).to.equal(2) + expect(Result.A).never.to.be.ok() + expect(Result.B).never.to.be.ok() + end) + end) +end + + + + + Merge1D + --- Merges various tables together, into a union data type. +local function Merge1D(...) + local Result = {} + + for Index = 1, select("#", ...) do + for Key, Value in pairs(select(Index, ...)) do + Result[Key] = Value + end + end + + return Result +end + +return Merge1D + + + + + Merge1D.spec + return function() + local Merge1D = require(script.Parent.Merge1D) + + describe("Map/Merge1D", function() + it("should return a blank table for no inputs", function() + local Result = Merge1D() + expect(next(Result)).never.to.be.ok() + end) + + it("should return a blank table for one blank table input", function() + local Result = Merge1D({}) + expect(next(Result)).never.to.be.ok() + end) + + it("should return a blank table for multiple blank table inputs", function() + local Result = Merge1D({}, {}, {}, {}) + expect(next(Result)).never.to.be.ok() + end) + + it("should merge two tables", function() + local Result = Merge1D({A = 1, B = 2}, {C = 3}) + expect(Result.A).to.equal(1) + expect(Result.B).to.equal(2) + expect(Result.C).to.equal(3) + end) + + it("should overwrite former tables", function() + local Result = Merge1D({A = 1, B = 2}, {B = 3}, {B = 4}) + expect(Result.A).to.equal(1) + expect(Result.B).to.equal(4) + end) + + it("should merge several tables", function() + local Result = Merge1D({A = 1, B = 2}, {C = 3}, {D = 4}) + expect(Result.A).to.equal(1) + expect(Result.B).to.equal(2) + expect(Result.C).to.equal(3) + expect(Result.D).to.equal(4) + end) + end) +end + + + + + MergeDeep + local TYPE_TABLE = "table" + +--- Creates a new data structure, representing the recursive merge of one table into another. Ensures structural sharing. +local function MergeDeep(Structure, Into) + local Result = {} + + -- Copy top level + for Key, Value in pairs(Into) do + Result[Key] = Value + end + + -- Structure overwrites + for Key, Value in pairs(Structure) do + if (type(Value) ~= TYPE_TABLE) then + Result[Key] = Value + continue + end + + local OtherValue = Into[Key] + local IntoTarget = (type(OtherValue) == TYPE_TABLE and OtherValue or {}) + Result[Key] = MergeDeep(Value, IntoTarget) + end + + return Result +end + +return MergeDeep + + + + + Values1D + --- Obtains the values from a table +local function Values1D(Structure) + local Result = {} + local Index = 1 + + for _, Value in pairs(Structure) do + Result[Index] = Value + Index += 1 + end + + return Result +end + +return Values1D + + + + + Values1D.spec + return function() + local Values1D = require(script.Parent.Values1D) + + describe("Map/Values1D", function() + it("should return a blank table given a blank table", function() + local Result = Values1D({}) + expect(Result).to.be.ok() + expect(next(Result)).never.to.be.ok() + end) + + it("should return one value given a one value table", function() + local Result = Values1D({A = 1000}) + expect(table.find(Result, 1000)).to.be.ok() + end) + + it("should return multiple values given a multiple value table", function() + local Result = Values1D({A = 1000, B = 2000, C = true}) + expect(table.find(Result, 1000)).to.be.ok() + expect(table.find(Result, 2000)).to.be.ok() + expect(table.find(Result, true)).to.be.ok() + end) + end) +end + + + + + + Set + return { + FromValues = require(script:WaitForChild("FromValues")); + FromKeys = require(script:WaitForChild("FromKeys")); + + Intersection = require(script:WaitForChild("Intersection")); + Negation = require(script:WaitForChild("Negation")); + Union = require(script:WaitForChild("Union")); + Outer = require(script:WaitForChild("Outer")); +}; + + + + Equals + --!nonstrict +local SetType = require(script.Parent:WaitForChild("_SetType")) +type Set<T> = SetType.Set<T> + +local function Equals<T>(Set1: Set<T>, Set2: Set<T>): boolean + for Key in pairs(Set1) do + if (not Set2[Key]) then + return false + end + end + + for Key in pairs(Set2) do + if (not Set1[Key]) then + return false + end + end + + return true +end + +return Equals + + + + + Equals.spec + return function() + local FromValues = require(script.Parent.FromValues) + local Equals = require(script.Parent.Equals) + + describe("Set/Equals", function() + it("should return true for two empty sets", function() + expect(Equals(FromValues({}), FromValues({}))).to.equal(true) + end) + + it("should return false for two sets with different values", function() + expect(Equals(FromValues({1, 2, 3}), FromValues({4, 5, 6}))).to.equal(false) + end) + + it("should return true for two sets with the same values", function() + expect(Equals(FromValues({1, 2, 3}), FromValues({1, 2, 3}))).to.equal(true) + expect(Equals(FromValues({1, 2, 3}), FromValues({3, 2, 1}))).to.equal(true) + end) + + it("should return false for an intersection which is not equal to A or B", function() + expect(Equals(FromValues({1, 2, 3}), FromValues({2, 3, 4}))).to.equal(false) + expect(Equals(FromValues({1, 2, 3}), FromValues({1, 2, 4}))).to.equal(false) + end) + end) +end + + + + + FromKeys + --!nonstrict +local SetType = require(script.Parent:WaitForChild("_SetType")) +type Set<T> = SetType.Set<T> + +local function FromKeys<T>(KeysTable: {[T]: any}): Set<T> + local Result = {} + + for Key in pairs(KeysTable) do + Result[Key] = true + end + + return Result +end + +return FromKeys + + + + + FromKeys.spec + return function() + local FromKeys = require(script.Parent.FromKeys) + + describe("Set/FromKeys", function() + it("should return an empty table given an empty table", function() + local Result = FromKeys({}) + expect(next(Result)).never.to.be.ok() + end) + + it("should return correctly for one item", function() + local Result = FromKeys({A = 1234}) + expect(Result.A).to.be.ok() + end) + + it("should return correctly for multiple items", function() + local Result = FromKeys({A = 1, B = 2, C = 3}) + expect(Result.A).to.be.ok() + expect(Result.B).to.be.ok() + expect(Result.C).to.be.ok() + end) + end) +end + + + + + FromValues + --!nonstrict +local SetType = require(script.Parent:WaitForChild("_SetType")) +type Set<T> = SetType.Set<T> + +local function FromValues<T>(ValuesTable: {[any]: T}): Set<T> + local Result = {} + + for _, Value in pairs(ValuesTable) do + Result[Value] = true + end + + return Result +end + +return FromValues + + + + + FromValues.spec + return function() + local FromValues = require(script.Parent.FromValues) + + describe("Set/FromValues", function() + it("should return an empty table given an empty table", function() + local Result = FromValues({}) + expect(next(Result)).never.to.be.ok() + end) + + it("should return correctly for one item", function() + local Result = FromValues({A = "1234"}) + expect(Result["1234"]).to.be.ok() + end) + + it("should return correctly for multiple items", function() + local Result = FromValues({A = "1", B = "2", C = "3"}) + expect(Result["1"]).to.be.ok() + expect(Result["2"]).to.be.ok() + expect(Result["3"]).to.be.ok() + end) + end) +end + + + + + Intersection + --!nonstrict +local SetType = require(script.Parent:WaitForChild("_SetType")) +type Set<T> = SetType.Set<T> + +local function Intersection<T>(Set1: Set<T>, Set2: Set<T>): Set<T> + local Result = {} + + for Key in pairs(Set1) do + if (Set2[Key] and Set1[Key]) then + Result[Key] = true + end + end + + return Result +end + +return Intersection + + + + + Intersection.spec + return function() + local Intersection = require(script.Parent.Intersection) + local FromValues = require(script.Parent.FromValues) + + describe("Set/Intersection", function() + it("should find no intersection with two empty sets", function() + local Result = Intersection({}, {}) + expect(next(Result)).never.to.be.ok() + end) + + it("should find an intersection between one common element", function() + local Result = Intersection(FromValues( {"A", "B"} ), FromValues( {"A", "C"} )) + expect(Result.A).to.be.ok() + expect(Result.B).never.to.be.ok() + expect(Result.C).never.to.be.ok() + end) + + it("should find multiple intersecting elements", function() + local Result = Intersection(FromValues( {"A", "B", "X"} ), FromValues( {"A", "B", "Y"} )) + expect(Result.A).to.be.ok() + expect(Result.B).to.be.ok() + expect(Result.X).never.to.be.ok() + expect(Result.Y).never.to.be.ok() + end) + end) +end + + + + + Negation + --!nonstrict +local SetType = require(script.Parent:WaitForChild("_SetType")) +type Set<T> = SetType.Set<T> + +local function Negation<T>(Set1: Set<T>, Set2: Set<T>): Set<T> + local Result = {} + + for Key in pairs(Set1) do + if (Set2[Key] == nil) then + Result[Key] = true + end + end + + return Result +end + +return Negation + + + + + Negation.spec + return function() + local Negation = require(script.Parent.Negation) + local FromValues = require(script.Parent.FromValues) + + describe("Set/Negation", function() + it("should return a blank set from two blank set inputs", function() + local Result = Negation(FromValues( {} ), FromValues( {} )) + expect(next(Result)).never.to.be.ok() + end) + + it("should remove the latter from the former with one item", function() + local Result = Negation(FromValues( {1} ), FromValues( {1} )) + expect(next(Result)).never.to.be.ok() + end) + + it("should remove the latter from the former with multiple items", function() + local Result = Negation(FromValues( {1, 4, 8} ), FromValues( {4, 8, 1} )) + expect(next(Result)).never.to.be.ok() + end) + + it("should remove the latter from the former with multiple items and leave non-negated present", function() + local Result = Negation(FromValues( {1, 4, 8, 2} ), FromValues( {4, 8, 1} )) + expect(Result[2]).to.be.ok() + end) + end) +end + + + + + Outer + --!nonstrict +local SetType = require(script.Parent:WaitForChild("_SetType")) +type Set<T> = SetType.Set<T> + +local function Outer<T>(Set1: Set<T>, Set2: Set<T>): Set<T> + local Result = {} + + for Key in pairs(Set1) do + if (not (Set2[Key] and Set1[Key])) then + Result[Key] = true + end + end + + for Key in pairs(Set2) do + if (not (Set2[Key] and Set1[Key])) then + Result[Key] = true + end + end + + return Result +end + +return Outer + + + + + Outer.spec + return function() + local Outer = require(script.Parent.Outer) + local FromValues = require(script.Parent.FromValues) + + describe("Set/Outer", function() + it("should return a blank set from two blank set inputs", function() + local Result = Outer(FromValues( {} ), FromValues( {} )) + expect(next(Result)).never.to.be.ok() + end) + + it("should return the left items given a left set and a blank right set", function() + local Result = Outer(FromValues( {1, 2, 3} ), FromValues( {} )) + expect(Result[1]).to.be.ok() + expect(Result[2]).to.be.ok() + expect(Result[3]).to.be.ok() + end) + + it("should return the right items given a blank left set and a right set", function() + local Result = Outer(FromValues( {} ), FromValues( {1, 2, 3} )) + expect(Result[1]).to.be.ok() + expect(Result[2]).to.be.ok() + expect(Result[3]).to.be.ok() + end) + + it("should return the outer items without the intersections", function() + local Result = Outer(FromValues( {1, 2, 3, 4} ), FromValues( {3, 4, 5, 6} )) + expect(Result[1]).to.be.ok() + expect(Result[2]).to.be.ok() + expect(Result[3]).never.to.be.ok() + expect(Result[4]).never.to.be.ok() + expect(Result[5]).to.be.ok() + expect(Result[6]).to.be.ok() + end) + end) +end + + + + + Union + --!nonstrict +local SetType = require(script.Parent:WaitForChild("_SetType")) +type Set<T> = SetType.Set<T> + +local function Union<T>(Set1: Set<T>, Set2: Set<T>): Set<T> + if (next(Set1) == nil) then + return Set2 + end + + if (next(Set2) == nil) then + return Set1 + end + + local Result = {} + + for Key in pairs(Set1) do + Result[Key] = true + end + + for Key in pairs(Set2) do + Result[Key] = true + end + + return Result +end + +return Union + + + + + Union.spec + return function() + local Union = require(script.Parent.Union) + local FromValues = require(script.Parent.FromValues) + + describe("Set/Union", function() + it("should combine two empty sets into an empty set", function() + local Result = Union({}, {}) + expect(next(Result)).never.to.be.ok() + end) + + it("should return A for A union B where B is empty" , function() + local A = FromValues({"x", "y", "z"}) + local B = FromValues({}) + + expect(Union(A, B)).to.equal(A) + end) + + it("should return B for A union B where A is empty" , function() + local A = FromValues({}) + local B = FromValues({"x", "y", "z"}) + + expect(Union(A, B)).to.equal(B) + end) + + it("should return an equal set for two equivalent sets", function() + local A = FromValues({"x", "y", "z"}) + local B = FromValues({"x", "y", "z"}) + + for Key in pairs(A) do + expect(B[Key]).to.equal(A[Key]) + end + + for Key in pairs(B) do + expect(A[Key]).to.equal(B[Key]) + end + end) + + it("should return a union of two sets", function() + local A = FromValues({"X", "Y", "Z"}) + local B = FromValues({"P", "Q", "R"}) + local Merge = Union(A, B) + + expect(Merge.X).to.be.ok() + expect(Merge.Y).to.be.ok() + expect(Merge.Z).to.be.ok() + expect(Merge.P).to.be.ok() + expect(Merge.Q).to.be.ok() + expect(Merge.R).to.be.ok() + end) + end) +end + + + + + _SetType + export type Set<T> = {[T]: boolean} +return true + + + + + + \ No newline at end of file